merge: with dev
2
.gitignore
vendored
@ -17,3 +17,5 @@ tests/**/coverage/
|
|||||||
*.sln
|
*.sln
|
||||||
.env.development
|
.env.development
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
|
helper.json
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"[vue]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"prettier.printWidth": 100,
|
||||||
|
"prettier.singleAttributePerLine": true,
|
||||||
|
"editor.wordWrap": "on"
|
||||||
|
}
|
@ -5,5 +5,6 @@
|
|||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
"@vue/cli-plugin-unit-jest": "3.6.3",
|
"@vue/cli-plugin-unit-jest": "3.6.3",
|
||||||
"@vue/cli-service": "3.6.0",
|
"@vue/cli-service": "3.6.0",
|
||||||
"@vue/test-utils": "1.0.0-beta.29",
|
"@vue/test-utils": "1.0.0-beta.29",
|
||||||
|
"@vue/runtime-dom": "3.5.13",
|
||||||
"autoprefixer": "^9.5.1",
|
"autoprefixer": "^9.5.1",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
"babel-eslint": "10.0.1",
|
"babel-eslint": "10.0.1",
|
||||||
@ -120,7 +121,7 @@
|
|||||||
"serve-static": "^1.16.0",
|
"serve-static": "^1.16.0",
|
||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
"svg-sprite-loader": "4.1.3",
|
"svg-sprite-loader": "4.1.3",
|
||||||
"svgo": "1.2.2",
|
"svgo": "1.2.4",
|
||||||
"vue-i18n-extract": "^1.1.1",
|
"vue-i18n-extract": "^1.1.1",
|
||||||
"vue-template-compiler": "2.6.10"
|
"vue-template-compiler": "2.6.10"
|
||||||
},
|
},
|
||||||
|
@ -2,37 +2,70 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
|
||||||
<meta http-equiv="Expires" content="0">
|
<meta content="0" http-equiv="Expires">
|
||||||
<meta http-equiv="Pragma" content="no-cache">
|
<meta content="no-cache" http-equiv="Pragma">
|
||||||
<meta http-equiv="Cache-control" content="no-cache">
|
<meta content="no-cache" http-equiv="Cache-control">
|
||||||
<meta http-equiv="Cache" content="no-cache">
|
<meta content="no-cache" http-equiv="Cache">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||||
<title><%= webpackConfig.name %></title>
|
<title><%= webpackConfig.name %></title>
|
||||||
<link rel="stylesheet" href="<%= BASE_URL %>theme/element-ui.css">
|
<link href="<%= BASE_URL %>theme/element-ui.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
#loading {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 5px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #3498db;
|
||||||
|
animation: spin 1s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function() {
|
window.onload = function () {
|
||||||
if (location.pathname === '/') {
|
if (location.pathname === '/') {
|
||||||
location.pathname = '/ui/'
|
location.pathname = '/ui/'
|
||||||
}
|
}
|
||||||
const pathname = window.location.pathname
|
const pathname = window.location.pathname
|
||||||
if (pathname.startsWith('/core')) {
|
if (pathname.startsWith('/core')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(pathname.indexOf('/ui') === -1) {
|
if (pathname.indexOf('/ui') === -1) {
|
||||||
window.location.href = window.location.origin + '/ui/#' + pathname
|
window.location.href = window.location.origin + '/ui/#' + pathname
|
||||||
}
|
}
|
||||||
if (pathname.startsWith('/ui/#/chat')) {
|
if (pathname.startsWith('/ui/#/chat')) {
|
||||||
window.location.href = window.location.origin + pathname
|
window.location.href = window.location.origin + pathname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<div id="app"></div>
|
<div id="app">
|
||||||
|
</div>
|
||||||
|
<div id="loading">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
</div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -5,6 +5,7 @@ import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSettin
|
|||||||
|
|
||||||
export const accountFieldsMeta = (vm) => {
|
export const accountFieldsMeta = (vm) => {
|
||||||
const defaultPrivilegedAccounts = ['root', 'administrator']
|
const defaultPrivilegedAccounts = ['root', 'administrator']
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assets: {
|
assets: {
|
||||||
component: Select2,
|
component: Select2,
|
||||||
@ -27,6 +28,9 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
component: Select2,
|
component: Select2,
|
||||||
rules: [Required],
|
rules: [Required],
|
||||||
el: {
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
},
|
||||||
multiple: false,
|
multiple: false,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/api/v1/accounts/account-templates/',
|
url: '/api/v1/accounts/account-templates/',
|
||||||
@ -43,6 +47,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
rules: [Required],
|
rules: [Required],
|
||||||
label: vm.$t('AccountPolicy'),
|
label: vm.$t('AccountPolicy'),
|
||||||
helpTip: vm.$t('AccountPolicyHelpText'),
|
helpTip: vm.$t('AccountPolicyHelpText'),
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: () => {
|
hidden: () => {
|
||||||
return vm.platform || vm.asset
|
return vm.platform || vm.asset
|
||||||
}
|
}
|
||||||
@ -50,6 +59,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
name: {
|
name: {
|
||||||
label: vm.$t('Name'),
|
label: vm.$t('Name'),
|
||||||
rules: [RequiredChange],
|
rules: [RequiredChange],
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
on: {
|
on: {
|
||||||
input: ([value], updateForm) => {
|
input: ([value], updateForm) => {
|
||||||
if (!vm.usernameChanged) {
|
if (!vm.usernameChanged) {
|
||||||
@ -69,7 +83,9 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
el: {
|
el: {
|
||||||
disabled: !!vm.account?.name
|
get disabled() {
|
||||||
|
return !!vm.account?.name || vm.isDisabled
|
||||||
|
}
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
input: ([value], updateForm) => {
|
input: ([value], updateForm) => {
|
||||||
@ -88,6 +104,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
},
|
},
|
||||||
privileged: {
|
privileged: {
|
||||||
label: vm.$t('Privileged'),
|
label: vm.$t('Privileged'),
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: () => {
|
hidden: () => {
|
||||||
return vm.addTemplate
|
return vm.addTemplate
|
||||||
}
|
}
|
||||||
@ -100,6 +121,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
el: {
|
el: {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
|
disabled: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${vm.account?.id || ''}&asset=${vm.asset?.id || ''}`,
|
url: `/api/v1/accounts/accounts/su-from-accounts/?account=${vm.account?.id || ''}&asset=${vm.asset?.id || ''}`,
|
||||||
transformOption: (item) => {
|
transformOption: (item) => {
|
||||||
@ -110,6 +136,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
},
|
},
|
||||||
su_from_username: {
|
su_from_username: {
|
||||||
label: vm.$t('UserSwitchFrom'),
|
label: vm.$t('UserSwitchFrom'),
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => {
|
hidden: (formValue) => {
|
||||||
return vm.platform || vm.asset || vm.addTemplate
|
return vm.platform || vm.asset || vm.addTemplate
|
||||||
}
|
}
|
||||||
@ -117,6 +148,11 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
password: {
|
password: {
|
||||||
label: vm.$t('Password'),
|
label: vm.$t('Password'),
|
||||||
component: UpdateToken,
|
component: UpdateToken,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => {
|
hidden: (formValue) => {
|
||||||
return formValue.secret_type !== 'password' || vm.addTemplate
|
return formValue.secret_type !== 'password' || vm.addTemplate
|
||||||
}
|
}
|
||||||
@ -124,33 +160,63 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
ssh_key: {
|
ssh_key: {
|
||||||
label: vm.$t('PrivateKey'),
|
label: vm.$t('PrivateKey'),
|
||||||
component: UploadSecret,
|
component: UploadSecret,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
||||||
},
|
},
|
||||||
passphrase: {
|
passphrase: {
|
||||||
label: vm.$t('Passphrase'),
|
label: vm.$t('Passphrase'),
|
||||||
component: UpdateToken,
|
component: UpdateToken,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
hidden: (formValue) => formValue.secret_type !== 'ssh_key' || vm.addTemplate
|
||||||
},
|
},
|
||||||
token: {
|
token: {
|
||||||
label: vm.$t('Token'),
|
label: vm.$t('Token'),
|
||||||
component: UploadSecret,
|
component: UploadSecret,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => formValue.secret_type !== 'token' || vm.addTemplate
|
hidden: (formValue) => formValue.secret_type !== 'token' || vm.addTemplate
|
||||||
},
|
},
|
||||||
access_key: {
|
access_key: {
|
||||||
id: 'access_key',
|
id: 'access_key',
|
||||||
label: vm.$t('AccessKey'),
|
label: vm.$t('AccessKey'),
|
||||||
component: UploadSecret,
|
component: UploadSecret,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => formValue.secret_type !== 'access_key' || vm.addTemplate
|
hidden: (formValue) => formValue.secret_type !== 'access_key' || vm.addTemplate
|
||||||
},
|
},
|
||||||
api_key: {
|
api_key: {
|
||||||
id: 'api_key',
|
id: 'api_key',
|
||||||
label: vm.$t('ApiKey'),
|
label: vm.$t('ApiKey'),
|
||||||
component: UploadSecret,
|
component: UploadSecret,
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: (formValue) => formValue.secret_type !== 'api_key' || vm.addTemplate
|
hidden: (formValue) => formValue.secret_type !== 'api_key' || vm.addTemplate
|
||||||
},
|
},
|
||||||
secret_type: {
|
secret_type: {
|
||||||
type: 'radio-group',
|
type: 'radio-group',
|
||||||
options: [],
|
options: [],
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
},
|
||||||
hidden: () => {
|
hidden: () => {
|
||||||
return vm.addTemplate
|
return vm.addTemplate
|
||||||
}
|
}
|
||||||
@ -163,7 +229,8 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
!automation.ansible_enabled ||
|
!automation.ansible_enabled ||
|
||||||
!vm.$hasPerm('accounts.push_account') ||
|
!vm.$hasPerm('accounts.push_account') ||
|
||||||
(formValue.secret_type === 'ssh_key' && vm.iPlatform.type.value === 'windows') ||
|
(formValue.secret_type === 'ssh_key' && vm.iPlatform.type.value === 'windows') ||
|
||||||
vm.addTemplate
|
vm.addTemplate ||
|
||||||
|
!formValue.secret_reset
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
@ -184,12 +251,27 @@ export const accountFieldsMeta = (vm) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
is_active: {
|
is_active: {
|
||||||
label: vm.$t('IsActive')
|
label: vm.$t('IsActive'),
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
comment: {
|
comment: {
|
||||||
label: vm.$t('Comment'),
|
label: vm.$t('Comment'),
|
||||||
hidden: () => {
|
el: {
|
||||||
return vm.addTemplate
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
secret_reset: {
|
||||||
|
label: vm.$t('SecretReset'),
|
||||||
|
el: {
|
||||||
|
get disabled() {
|
||||||
|
return vm.isDisabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
isDisabled: false,
|
||||||
usernameChanged: false,
|
usernameChanged: false,
|
||||||
submitBtnText: this.$t('Confirm'),
|
submitBtnText: this.$t('Confirm'),
|
||||||
iPlatform: {
|
iPlatform: {
|
||||||
@ -61,11 +62,12 @@ export default {
|
|||||||
form: Object.assign({ 'on_invalid': 'error' }, this.account || {}),
|
form: Object.assign({ 'on_invalid': 'error' }, this.account || {}),
|
||||||
encryptedFields: ['secret'],
|
encryptedFields: ['secret'],
|
||||||
fields: [
|
fields: [
|
||||||
[this.$t('AccountTemplate'), ['template']],
|
[this.$t('Basic'), ['name', 'username', 'privileged', 'su_from', 'su_from_username', 'template']],
|
||||||
[this.$t('Basic'), ['assets', 'name', 'username', 'privileged', 'su_from', 'su_from_username']],
|
[this.$t('Assets'), ['assets']],
|
||||||
[this.$t('Secret'), [
|
[this.$t('Secret'), [
|
||||||
'secret_type', 'password', 'ssh_key', 'token',
|
'secret_type', 'password', 'ssh_key', 'token',
|
||||||
'access_key', 'passphrase', 'api_key'
|
'access_key', 'passphrase', 'api_key',
|
||||||
|
'secret_reset'
|
||||||
]],
|
]],
|
||||||
[this.$t('Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
|
[this.$t('Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
|
||||||
],
|
],
|
||||||
@ -73,6 +75,16 @@ export default {
|
|||||||
hasSaveContinue: false
|
hasSaveContinue: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query': {
|
||||||
|
handler(nv, ov) {
|
||||||
|
if (nv && (nv.flag === 'move' || nv.flag === 'copy')) {
|
||||||
|
this.isDisabled = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
try {
|
||||||
await this.getPlatform()
|
await this.getPlatform()
|
||||||
@ -157,16 +169,16 @@ export default {
|
|||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
.account-add {
|
.account-add {
|
||||||
::v-deep .el-form-item {
|
::v-deep .el-form-item {
|
||||||
margin-bottom: 5px;
|
//margin-bottom: 5px;
|
||||||
|
|
||||||
.help-block {
|
.help-block {
|
||||||
margin-bottom: 5px;
|
//margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .form-group-header {
|
::v-deep .form-group-header {
|
||||||
.hr-line-dashed {
|
.hr-line-dashed {
|
||||||
margin: 5px 0;
|
//margin: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
|
@ -1,36 +1,38 @@
|
|||||||
<template>
|
<template v-if="iVisible">
|
||||||
<Dialog
|
<Drawer
|
||||||
v-if="iVisible"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
:destroy-on-close="true"
|
|
||||||
:show-cancel="false"
|
|
||||||
:show-confirm="false"
|
|
||||||
:title="title"
|
:title="title"
|
||||||
:visible.sync="iVisible"
|
:visible="iVisible"
|
||||||
v-bind="$attrs"
|
class="drawer"
|
||||||
width="900px"
|
@close-drawer="handleCloseDrawer"
|
||||||
v-on="$listeners"
|
|
||||||
>
|
>
|
||||||
<AccountCreateUpdateForm
|
<Page :title="'null'">
|
||||||
v-if="!loading"
|
<IBox class="content">
|
||||||
ref="form"
|
<AccountCreateUpdateForm
|
||||||
:account="account"
|
v-if="!loading"
|
||||||
:add-template="addTemplate"
|
ref="form"
|
||||||
:asset="asset"
|
:account="account"
|
||||||
@add="addAccount"
|
:add-template="addTemplate"
|
||||||
@edit="editAccount"
|
:asset="asset"
|
||||||
/>
|
@add="addAccount"
|
||||||
</Dialog>
|
@edit="editAccount"
|
||||||
|
/>
|
||||||
|
</IBox>
|
||||||
|
</Page>
|
||||||
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Drawer from '@/components/Drawer/index.vue'
|
||||||
import AccountCreateUpdateForm from '@/components/Apps/AccountCreateUpdateForm/index.vue'
|
import AccountCreateUpdateForm from '@/components/Apps/AccountCreateUpdateForm/index.vue'
|
||||||
|
import IBox from '@/components/IBox/index.vue'
|
||||||
|
import Page from '@/layout/components/Page/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateAccountDialog',
|
name: 'CreateAccountDialog',
|
||||||
components: {
|
components: {
|
||||||
Dialog,
|
IBox,
|
||||||
|
Drawer,
|
||||||
|
Page,
|
||||||
AccountCreateUpdateForm
|
AccountCreateUpdateForm
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@ -111,11 +113,22 @@ export default {
|
|||||||
},
|
},
|
||||||
editAccount(form) {
|
editAccount(form) {
|
||||||
const data = { ...form }
|
const data = { ...form }
|
||||||
this.$axios.patch(`/api/v1/accounts/accounts/${this.account.id}/`, data).then(() => {
|
const flag = this.$route.query.flag
|
||||||
this.iVisible = false
|
|
||||||
this.$emit('add', true)
|
switch (flag) {
|
||||||
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
case 'copy':
|
||||||
}).catch(error => this.setFieldError(error))
|
this.handleAccountOperation(this.account.id, 'copy-to-assets', data)
|
||||||
|
break
|
||||||
|
case 'move':
|
||||||
|
this.handleAccountOperation(this.account.id, 'move-to-assets', data)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.$axios.patch(`/api/v1/accounts/accounts/${this.account.id}/`, data).then(() => {
|
||||||
|
this.iVisible = false
|
||||||
|
this.$emit('add', true)
|
||||||
|
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
||||||
|
}).catch(error => this.setFieldError(error))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleResult(resp, error) {
|
handleResult(resp, error) {
|
||||||
let bulkCreate = !this.asset
|
let bulkCreate = !this.asset
|
||||||
@ -168,7 +181,29 @@ export default {
|
|||||||
refsAutoDataForm.setFieldError(current, err)
|
refsAutoDataForm.setFieldError(current, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
handleCloseDrawer() {
|
||||||
|
this.iVisible = false
|
||||||
|
Reflect.deleteProperty(this.$route.query, 'flag')
|
||||||
|
},
|
||||||
|
handleAccountOperation(id, path, data) {
|
||||||
|
this.$axios.post(`/api/v1/accounts/accounts/${id}/${path}/`, data).then((res) => {
|
||||||
|
this.iVisible = false
|
||||||
|
this.$emit('add', true)
|
||||||
|
this.handleResult(res, null)
|
||||||
|
}).catch(error => this.handleResult(null, error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.drawer {
|
||||||
|
::v-deep .el-drawer__body {
|
||||||
|
|
||||||
|
.el-form {
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ListTable ref="ListTable" :header-actions="headerActions" :table-config="tableConfig" />
|
<DrawerListTable
|
||||||
|
ref="ListTable"
|
||||||
|
:detail-drawer="detailDrawer"
|
||||||
|
:header-actions="headerActions"
|
||||||
|
:quick-filters="quickFilters"
|
||||||
|
:table-config="tableConfig"
|
||||||
|
/>
|
||||||
<ViewSecret
|
<ViewSecret
|
||||||
v-if="showViewSecretDialog"
|
v-if="showViewSecretDialog"
|
||||||
:account="account"
|
:account="account"
|
||||||
@ -16,22 +22,13 @@
|
|||||||
<AccountCreateUpdate
|
<AccountCreateUpdate
|
||||||
v-if="showAddDialog"
|
v-if="showAddDialog"
|
||||||
:account="account"
|
:account="account"
|
||||||
|
:add-template="addTemplate"
|
||||||
:asset="iAsset"
|
:asset="iAsset"
|
||||||
:title="accountCreateUpdateTitle"
|
:title="accountCreateUpdateTitle"
|
||||||
:visible.sync="showAddDialog"
|
:visible.sync="showAddDialog"
|
||||||
@add="addAccountSuccess"
|
@add="addAccountSuccess"
|
||||||
@bulk-create-done="showBulkCreateResult($event)"
|
@bulk-create-done="showBulkCreateResult($event)"
|
||||||
/>
|
/>
|
||||||
<AccountCreateUpdate
|
|
||||||
v-if="showAddTemplateDialog"
|
|
||||||
:account="account"
|
|
||||||
:add-template="true"
|
|
||||||
:asset="iAsset"
|
|
||||||
:title="accountCreateByTemplateTitle"
|
|
||||||
:visible.sync="showAddTemplateDialog"
|
|
||||||
@add="addAccountSuccess"
|
|
||||||
@bulk-create-done="showBulkCreateResult($event)"
|
|
||||||
/>
|
|
||||||
<ResultDialog
|
<ResultDialog
|
||||||
v-if="showResultDialog"
|
v-if="showResultDialog"
|
||||||
:result="createAccountResults"
|
:result="createAccountResults"
|
||||||
@ -44,29 +41,36 @@
|
|||||||
v-bind="updateSelectedDialogSetting"
|
v-bind="updateSelectedDialogSetting"
|
||||||
@update="handleAccountBulkUpdate"
|
@update="handleAccountBulkUpdate"
|
||||||
/>
|
/>
|
||||||
|
<PasswordHistoryDialog
|
||||||
|
v-if="showPasswordHistoryDialog"
|
||||||
|
:account="currentAccountColumn"
|
||||||
|
:visible.sync="showPasswordHistoryDialog"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import { accountOtherActions, accountQuickFilters, connectivityMeta } from './const'
|
||||||
import { ActionsFormatter } from '@/components/Table/TableFormatters'
|
import { openTaskPage } from '@/utils/jms'
|
||||||
|
import { ActionsFormatter, PlatformFormatter, SecretViewerFormatter, AccountConnectFormatter } from '@/components/Table/TableFormatters'
|
||||||
import ViewSecret from './ViewSecret.vue'
|
import ViewSecret from './ViewSecret.vue'
|
||||||
import UpdateSecretInfo from './UpdateSecretInfo.vue'
|
import UpdateSecretInfo from './UpdateSecretInfo.vue'
|
||||||
import AccountCreateUpdate from './AccountCreateUpdate.vue'
|
|
||||||
import { connectivityMeta } from './const'
|
|
||||||
import { openTaskPage } from '@/utils/jms'
|
|
||||||
import ResultDialog from './BulkCreateResultDialog.vue'
|
import ResultDialog from './BulkCreateResultDialog.vue'
|
||||||
|
import AccountCreateUpdate from './AccountCreateUpdate.vue'
|
||||||
|
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
|
||||||
|
import DrawerListTable from '@/components/Table/DrawerListTable/index.vue'
|
||||||
import AccountBulkUpdateDialog from '@/components/Apps/AccountListTable/AccountBulkUpdateDialog.vue'
|
import AccountBulkUpdateDialog from '@/components/Apps/AccountListTable/AccountBulkUpdateDialog.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AccountListTable',
|
name: 'AccountListTable',
|
||||||
components: {
|
components: {
|
||||||
AccountBulkUpdateDialog,
|
|
||||||
ResultDialog,
|
|
||||||
ListTable,
|
|
||||||
UpdateSecretInfo,
|
|
||||||
ViewSecret,
|
ViewSecret,
|
||||||
AccountCreateUpdate
|
ResultDialog,
|
||||||
|
DrawerListTable,
|
||||||
|
UpdateSecretInfo,
|
||||||
|
AccountCreateUpdate,
|
||||||
|
PasswordHistoryDialog,
|
||||||
|
AccountBulkUpdateDialog
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
url: {
|
url: {
|
||||||
@ -89,7 +93,7 @@ export default {
|
|||||||
},
|
},
|
||||||
hasClone: {
|
hasClone: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: true
|
||||||
},
|
},
|
||||||
asset: {
|
asset: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -119,7 +123,7 @@ export default {
|
|||||||
columnsDefault: {
|
columnsDefault: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => ([
|
default: () => ([
|
||||||
'name', 'username', 'asset', 'date_updated'
|
'name', 'username', 'secret', 'asset', 'platform', 'connect'
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
headerExtraActions: {
|
headerExtraActions: {
|
||||||
@ -129,22 +133,29 @@ export default {
|
|||||||
extraQuery: {
|
extraQuery: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
showQuickFilters: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const vm = this
|
const vm = this
|
||||||
return {
|
return {
|
||||||
|
addTemplate: false,
|
||||||
|
currentAccountColumn: {},
|
||||||
|
showPasswordHistoryDialog: false,
|
||||||
showViewSecretDialog: false,
|
showViewSecretDialog: false,
|
||||||
showUpdateSecretDialog: false,
|
showUpdateSecretDialog: false,
|
||||||
showResultDialog: false,
|
showResultDialog: false,
|
||||||
showAddDialog: false,
|
showAddDialog: false,
|
||||||
showAddTemplateDialog: false,
|
showAddTemplateDialog: false,
|
||||||
|
detailDrawer: () => import('@/views/accounts/Account/AccountDetail/index.vue'),
|
||||||
createAccountResults: [],
|
createAccountResults: [],
|
||||||
accountCreateUpdateTitle: this.$t('AddAccount'),
|
|
||||||
accountCreateByTemplateTitle: this.$t('AddAccountByTemplate'),
|
|
||||||
iAsset: this.asset,
|
iAsset: this.asset,
|
||||||
account: {},
|
account: {},
|
||||||
secretUrl: '',
|
secretUrl: '',
|
||||||
|
quickFilters: this.showQuickFilters ? accountQuickFilters(this) : [],
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
permissions: {
|
permissions: {
|
||||||
@ -153,6 +164,7 @@ export default {
|
|||||||
},
|
},
|
||||||
extraQuery: this.extraQuery,
|
extraQuery: this.extraQuery,
|
||||||
columnsExclude: ['spec_info'],
|
columnsExclude: ['spec_info'],
|
||||||
|
columnsAdd: ['secret', 'platform', 'connect'],
|
||||||
columnsShow: {
|
columnsShow: {
|
||||||
min: ['name', 'username', 'actions'],
|
min: ['name', 'username', 'actions'],
|
||||||
default: this.columnsDefault
|
default: this.columnsDefault
|
||||||
@ -160,29 +172,50 @@ export default {
|
|||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
width: '120px',
|
width: '120px',
|
||||||
formatter: function(row) {
|
formatterArgs: {
|
||||||
const to = {
|
can: () => vm.$hasPerm('accounts.view_account'),
|
||||||
name: 'AssetAccountDetail',
|
getDrawerTitle({ row }) {
|
||||||
params: { id: row.id }
|
return `${row.username}@${row.asset.name}`
|
||||||
}
|
}
|
||||||
if (vm.$hasPerm('accounts.view_account')) {
|
}
|
||||||
return <router-link to={to}>{row.name}</router-link>
|
},
|
||||||
} else {
|
secret: {
|
||||||
return <span>{row.name}</span>
|
formatter: SecretViewerFormatter,
|
||||||
|
width: '130px',
|
||||||
|
formatterArgs: {
|
||||||
|
secretFrom: 'api',
|
||||||
|
hasDownload: false,
|
||||||
|
actionLeft: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connect: {
|
||||||
|
label: this.$t('Connect'),
|
||||||
|
width: '80px',
|
||||||
|
formatter: AccountConnectFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
buttonIcon: 'fa fa-desktop',
|
||||||
|
titleText: '可选协议',
|
||||||
|
url: '/api/v1/assets/assets/{id}',
|
||||||
|
connectUrlTemplate: (row) => `/luna/pam_connect/${row.id}/${row.username}/${row.asset.id}/${row.asset.name}/`,
|
||||||
|
setMapItem: (id, protocol) => {
|
||||||
|
this.$store.commit('table/SET_PROTOCOL_MAP_ITEM', {
|
||||||
|
key: id,
|
||||||
|
value: protocol
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
platform: {
|
||||||
|
label: this.$t('Platform'),
|
||||||
|
width: '120px',
|
||||||
|
formatter: PlatformFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
platformAttr: 'asset.platform'
|
||||||
|
}
|
||||||
|
},
|
||||||
asset: {
|
asset: {
|
||||||
formatter: function(row) {
|
formatter: function(row) {
|
||||||
const to = {
|
return row.asset.name
|
||||||
name: 'AssetDetail',
|
|
||||||
params: { id: row.asset.id }
|
|
||||||
}
|
|
||||||
if (vm.$hasPerm('assets.view_asset')) {
|
|
||||||
return <router-link to={to}>{row.asset.name}</router-link>
|
|
||||||
} else {
|
|
||||||
return <span>{row.asset.name}</span>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
@ -216,75 +249,11 @@ export default {
|
|||||||
formatter: ActionsFormatter,
|
formatter: ActionsFormatter,
|
||||||
formatterArgs: {
|
formatterArgs: {
|
||||||
hasUpdate: false, // can set function(row, value)
|
hasUpdate: false, // can set function(row, value)
|
||||||
hasDelete: false, // can set function(row, value)
|
hasDelete: true, // can set function(row, value)
|
||||||
hasClone: this.hasClone,
|
hasClone: false,
|
||||||
|
canDelete: () => vm.$hasPerm('accounts.delete_account'),
|
||||||
moreActionsTitle: this.$t('More'),
|
moreActionsTitle: this.$t('More'),
|
||||||
extraActions: [
|
extraActions: accountOtherActions(this)
|
||||||
{
|
|
||||||
name: 'View',
|
|
||||||
title: this.$t('View'),
|
|
||||||
can: this.$hasPerm('accounts.view_accountsecret'),
|
|
||||||
type: 'primary',
|
|
||||||
callback: ({ row }) => {
|
|
||||||
// debugger
|
|
||||||
vm.secretUrl = `/api/v1/accounts/account-secrets/${row.id}/`
|
|
||||||
vm.account = row
|
|
||||||
vm.showViewSecretDialog = false
|
|
||||||
setTimeout(() => {
|
|
||||||
vm.showViewSecretDialog = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Update',
|
|
||||||
title: this.$t('Edit'),
|
|
||||||
can: this.$hasPerm('accounts.change_account') && !this.$store.getters.currentOrgIsRoot,
|
|
||||||
callback: ({ row }) => {
|
|
||||||
const data = {
|
|
||||||
...this.asset,
|
|
||||||
...row.asset
|
|
||||||
}
|
|
||||||
vm.account = row
|
|
||||||
vm.iAsset = data
|
|
||||||
vm.showAddDialog = false
|
|
||||||
vm.accountCreateUpdateTitle = this.$t('UpdateAccount')
|
|
||||||
setTimeout(() => {
|
|
||||||
vm.showAddDialog = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Test',
|
|
||||||
title: this.$t('Test'),
|
|
||||||
can: ({ row }) =>
|
|
||||||
!this.$store.getters.currentOrgIsRoot &&
|
|
||||||
this.$hasPerm('accounts.verify_account') &&
|
|
||||||
row.asset['auto_config'].ansible_enabled &&
|
|
||||||
row.asset['auto_config'].ping_enabled,
|
|
||||||
callback: ({ row }) => {
|
|
||||||
this.$axios.post(
|
|
||||||
`/api/v1/accounts/accounts/tasks/`,
|
|
||||||
{ action: 'verify', accounts: [row.id] }
|
|
||||||
).then(res => {
|
|
||||||
openTaskPage(res['task'])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ClearSecret',
|
|
||||||
title: this.$t('ClearSecret'),
|
|
||||||
can: this.$hasPerm('accounts.change_account'),
|
|
||||||
type: 'primary',
|
|
||||||
callback: ({ row }) => {
|
|
||||||
this.$axios.patch(
|
|
||||||
`/api/v1/accounts/accounts/clear-secret/`,
|
|
||||||
{ account_ids: [row.id] }
|
|
||||||
).then(() => {
|
|
||||||
this.$message.success(this.$tc('ClearSuccessMsg'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...this.columnsMeta
|
...this.columnsMeta
|
||||||
@ -320,6 +289,7 @@ export default {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
vm.iAsset = this.asset
|
vm.iAsset = this.asset
|
||||||
vm.account = {}
|
vm.account = {}
|
||||||
|
vm.addTemplate = false
|
||||||
vm.showAddDialog = true
|
vm.showAddDialog = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -336,7 +306,8 @@ export default {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
vm.iAsset = this.asset
|
vm.iAsset = this.asset
|
||||||
vm.account = {}
|
vm.account = {}
|
||||||
vm.showAddTemplateDialog = true
|
vm.showAddDialog = true
|
||||||
|
vm.addTemplate = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -347,11 +318,11 @@ export default {
|
|||||||
name: 'TestSelected',
|
name: 'TestSelected',
|
||||||
title: this.$t('TestSelected'),
|
title: this.$t('TestSelected'),
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
icon: 'fa-link',
|
icon: 'verify',
|
||||||
can: ({ selectedRows }) => {
|
can: ({ selectedRows }) => {
|
||||||
return selectedRows.length > 0 &&
|
return selectedRows.length > 0 &&
|
||||||
['clickhouse', 'redis', 'website', 'chatgpt'].indexOf(selectedRows[0].asset.type.value) === -1 &&
|
['clickhouse', 'redis', 'website', 'chatgpt'].indexOf(selectedRows[0].asset.type.value) === -1 &&
|
||||||
!this.$store.getters.currentOrgIsRoot
|
!this.$store.getters.currentOrgIsRoot
|
||||||
},
|
},
|
||||||
callback: function({ selectedRows }) {
|
callback: function({ selectedRows }) {
|
||||||
const ids = selectedRows.map(v => {
|
const ids = selectedRows.map(v => {
|
||||||
@ -416,6 +387,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
accountCreateUpdateTitle() {
|
||||||
|
if (this.addTemplate) {
|
||||||
|
return this.$t('AddAccountByTemplate')
|
||||||
|
} else {
|
||||||
|
return this.$t('AddAccount')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
url(iNew) {
|
url(iNew) {
|
||||||
this.$set(this.tableConfig, 'url', iNew)
|
this.$set(this.tableConfig, 'url', iNew)
|
||||||
@ -423,52 +403,34 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.columns.length > 0) {
|
this.setActions()
|
||||||
this.tableConfig.columns = this.columns
|
|
||||||
}
|
|
||||||
if (this.otherActions) {
|
|
||||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
|
||||||
for (const item of this.otherActions) {
|
|
||||||
actionColumn.formatterArgs.extraActions.push(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.hasDeleteAction) {
|
|
||||||
this.tableConfig.columnsMeta.actions.formatterArgs.extraActions.push(
|
|
||||||
{
|
|
||||||
name: 'Delete',
|
|
||||||
title: this.$t('Delete'),
|
|
||||||
can: this.$hasPerm('accounts.delete_account'),
|
|
||||||
type: 'primary',
|
|
||||||
callback: ({ row }) => {
|
|
||||||
const msg = this.$t('AccountDeleteConfirmMsg')
|
|
||||||
this.$confirm(msg, this.$tc('Info'), {
|
|
||||||
type: 'warning',
|
|
||||||
confirmButtonClass: 'el-button--danger',
|
|
||||||
beforeClose: async(action, instance, done) => {
|
|
||||||
if (action !== 'confirm') return done()
|
|
||||||
this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
|
|
||||||
done()
|
|
||||||
this.$refs.ListTable.reloadTable()
|
|
||||||
this.$message.success(this.$tc('DeleteSuccessMsg'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
// 由于组件嵌套较深,有可能导致 Error in activated hook: "TypeError: Cannot read properties of undefined (reading 'getList')" 的问题
|
// 由于组件嵌套较深,有可能导致 Error in activated hook: "TypeError: Cannot read properties of undefined (reading 'getList')" 的问题
|
||||||
setTimeout(() => {
|
if (this.tabDeactivated) {
|
||||||
this.refresh()
|
setTimeout(() => this.refresh(), 300)
|
||||||
}, 300)
|
}
|
||||||
|
},
|
||||||
|
deactivated() {
|
||||||
|
this.tabDeactivated = true
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setActions() {
|
||||||
|
if (this.columns.length > 0) {
|
||||||
|
this.tableConfig.columns = this.columns
|
||||||
|
}
|
||||||
|
if (this.otherActions) {
|
||||||
|
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||||
|
for (const item of this.otherActions) {
|
||||||
|
actionColumn.formatterArgs.extraActions.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
onUpdateAuthDone(account) {
|
onUpdateAuthDone(account) {
|
||||||
Object.assign(this.account, account)
|
Object.assign(this.account, account)
|
||||||
},
|
},
|
||||||
addAccountSuccess() {
|
addAccountSuccess() {
|
||||||
|
Reflect.deleteProperty(this.$route.query, 'flag')
|
||||||
this.$refs.ListTable.reloadTable()
|
this.$refs.ListTable.reloadTable()
|
||||||
},
|
},
|
||||||
async getAssetDetail() {
|
async getAssetDetail() {
|
||||||
@ -507,7 +469,7 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang="scss" scoped>
|
||||||
.cell a {
|
.cell a {
|
||||||
color: var(--color-info);
|
color: var(--color-info);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GenericListTableDialog } from '@/layout/components'
|
import { GenericListTableDialog } from '@/layout/components'
|
||||||
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
import { SecretViewerFormatter } from '@/components/Table/TableFormatters'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -33,7 +33,7 @@ export default {
|
|||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
secret: {
|
secret: {
|
||||||
label: this.$t('Password'),
|
label: this.$t('Password'),
|
||||||
formatter: ShowKeyCopyFormatter,
|
formatter: SecretViewerFormatter,
|
||||||
formatterArgs: {
|
formatterArgs: {
|
||||||
hasDownload: false,
|
hasDownload: false,
|
||||||
name: this.account.name
|
name: this.account.name
|
||||||
|
@ -1,44 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
|
:show-buttons="false"
|
||||||
:title="$tc('UpdateAssetUserToken')"
|
:title="$tc('UpdateAssetUserToken')"
|
||||||
:visible.sync="visible"
|
:visible.sync="iVisible"
|
||||||
width="50"
|
width="800px"
|
||||||
@cancel="handleCancel()"
|
|
||||||
@confirm="handleConfirm()"
|
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
>
|
>
|
||||||
<el-form label-position="right" label-width="90px">
|
<AutoDataForm
|
||||||
<el-form-item :label="$tc('Name')">
|
:fields="fields"
|
||||||
<el-input v-model="account['asset_name']" readonly />
|
:fields-meta="fieldsMeta"
|
||||||
</el-form-item>
|
:form="init"
|
||||||
<el-form-item :label="$tc('Username')">
|
:has-reset="false"
|
||||||
<el-input v-model="account['username']" readonly />
|
:has-save-continue="false"
|
||||||
</el-form-item>
|
:url="''"
|
||||||
<el-form-item :label="$tc('Password')">
|
method="patch"
|
||||||
<UpdateToken v-model="authInfo.password" />
|
@submit="handleConfirm"
|
||||||
</el-form-item>
|
/>
|
||||||
<el-form-item :label="$tc('SSHSecretKey')">
|
|
||||||
<UploadKey @input="getFile" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$tc('Passphrase')">
|
|
||||||
<UpdateToken v-model="authInfo.passphrase" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import { UpdateToken, UploadKey } from '@/components/Form/FormFields'
|
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
|
||||||
import { encryptPassword } from '@/utils/crypto'
|
import { encryptPassword } from '@/utils/crypto'
|
||||||
|
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UpdateSecretInfo',
|
name: 'UpdateSecretInfo',
|
||||||
components: {
|
components: {
|
||||||
Dialog,
|
AutoDataForm,
|
||||||
UploadKey,
|
Dialog
|
||||||
UpdateToken
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
account: {
|
account: {
|
||||||
@ -51,49 +43,59 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const accountMeta = accountFieldsMeta(this)
|
||||||
return {
|
return {
|
||||||
secretInfo: {
|
fields: [
|
||||||
password: '',
|
'name', 'secret_type', 'password', 'ssh_key', 'token',
|
||||||
private_key: '',
|
'access_key', 'passphrase', 'api_key'
|
||||||
passphrase: ''
|
],
|
||||||
|
fieldsMeta: {
|
||||||
|
...accountMeta,
|
||||||
|
name: {
|
||||||
|
...accountMeta.name,
|
||||||
|
readonly: true
|
||||||
|
},
|
||||||
|
secret_type: {
|
||||||
|
hidden: () => true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
...this.account
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:visible', val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleConfirm() {
|
handleConfirm(form) {
|
||||||
const data = {}
|
const secretType = this.account.secret_type.value
|
||||||
if (this.secretInfo.password !== '') {
|
const data = {
|
||||||
data.password = encryptPassword(this.secretInfo.password)
|
secret: encryptPassword(form[secretType])
|
||||||
}
|
|
||||||
if (this.secretInfo.private_key !== '') {
|
|
||||||
data.private_key = encryptPassword(this.secretInfo.private_key)
|
|
||||||
if (this.secretInfo.passphrase) data.passphrase = this.secretInfo.passphrase
|
|
||||||
}
|
}
|
||||||
this.$axios.patch(
|
this.$axios.patch(
|
||||||
`/api/v1/accounts/accounts/${this.account.id}/`,
|
`/api/v1/accounts/accounts/${this.account.id}/`,
|
||||||
data,
|
data,
|
||||||
{ disableFlashErrorMsg: true }
|
{ disableFlashErrorMsg: true }
|
||||||
).then(res => {
|
).then(res => {
|
||||||
this.authInfo = { password: '', private_key: '' }
|
|
||||||
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
this.$message.success(this.$tc('UpdateSuccessMsg'))
|
||||||
this.$emit('updateAuthDone', res)
|
this.iVisible = false
|
||||||
this.$emit('update:visible', false)
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
const errMsg = Object.values(err.response.data).join(', ')
|
const errMsg = Object.values(err.response.data).join(', ')
|
||||||
this.$message.error(this.$tc('UpdateErrorMsg') + ' ' + errMsg)
|
this.$message.error(this.$tc('UpdateErrorMsg') + ' ' + errMsg)
|
||||||
this.$emit('update:visible', true)
|
this.iVisible = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
this.$emit('update:visible', false)
|
this.$emit('update:visible', false)
|
||||||
},
|
|
||||||
getFile(file) {
|
|
||||||
this.secretInfo.private_key = file
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<span>{{ account['username'] }}</span>
|
<span>{{ account['username'] }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="secretTypeLabel">
|
<el-form-item :label="secretTypeLabel">
|
||||||
<ShowKeyCopyFormatter
|
<SecretViewerFormatter
|
||||||
:cell-value="secretInfo.secret"
|
:cell-value="secretInfo.secret"
|
||||||
:col="{ formatterArgs: {
|
:col="{ formatterArgs: {
|
||||||
name: account['name'],
|
name: account['name'],
|
||||||
@ -60,7 +60,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
|
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
|
||||||
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
import { SecretViewerFormatter } from '@/components/Table/TableFormatters'
|
||||||
import { encryptPassword } from '@/utils/crypto'
|
import { encryptPassword } from '@/utils/crypto'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -68,7 +68,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Dialog,
|
Dialog,
|
||||||
PasswordHistoryDialog,
|
PasswordHistoryDialog,
|
||||||
ShowKeyCopyFormatter
|
SecretViewerFormatter
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
account: {
|
account: {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ChoicesFormatter } from '@/components/Table/TableFormatters'
|
import { ChoicesFormatter } from '@/components/Table/TableFormatters'
|
||||||
|
import { openTaskPage } from '@/utils/jms'
|
||||||
|
|
||||||
export const connectivityMeta = {
|
export const connectivityMeta = {
|
||||||
formatter: ChoicesFormatter,
|
formatter: ChoicesFormatter,
|
||||||
@ -22,3 +23,276 @@ export const connectivityMeta = {
|
|||||||
},
|
},
|
||||||
width: '130px'
|
width: '130px'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const accountOtherActions = (vm) => [
|
||||||
|
{
|
||||||
|
name: 'View',
|
||||||
|
title: vm.$t('View'),
|
||||||
|
can: vm.$hasPerm('accounts.view_accountsecret'),
|
||||||
|
type: 'primary',
|
||||||
|
order: 1,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
// debugger
|
||||||
|
vm.secretUrl = `/api/v1/accounts/account-secrets/${row.id}/`
|
||||||
|
vm.account = row
|
||||||
|
vm.showViewSecretDialog = false
|
||||||
|
setTimeout(() => {
|
||||||
|
vm.showViewSecretDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
title: vm.$t('Edit'),
|
||||||
|
can: vm.$hasPerm('accounts.change_account') && !vm.$store.getters.currentOrgIsRoot,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
const data = {
|
||||||
|
...vm.asset,
|
||||||
|
...row.asset
|
||||||
|
}
|
||||||
|
vm.account = row
|
||||||
|
vm.iAsset = data
|
||||||
|
vm.showAddDialog = false
|
||||||
|
vm.accountCreateUpdateTitle = vm.$t('UpdateAccount')
|
||||||
|
setTimeout(() => {
|
||||||
|
vm.showAddDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'UpdateSecret',
|
||||||
|
title: vm.$t('EditSecret'),
|
||||||
|
can: vm.$hasPerm('accounts.change_account') && !vm.$store.getters.currentOrgIsRoot,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
const data = {
|
||||||
|
...vm.asset,
|
||||||
|
...row.asset
|
||||||
|
}
|
||||||
|
vm.account = row
|
||||||
|
vm.iAsset = data
|
||||||
|
vm.showUpdateSecretDialog = false
|
||||||
|
vm.accountCreateUpdateTitle = vm.$t('UpdateAccount')
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Update secret')
|
||||||
|
vm.showUpdateSecretDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Clone',
|
||||||
|
title: vm.$t('Duplicate'),
|
||||||
|
can: vm.$hasPerm('accounts.add_account') && !vm.$store.getters.currentOrgIsRoot,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
const data = {
|
||||||
|
...vm.asset,
|
||||||
|
...row.asset
|
||||||
|
}
|
||||||
|
vm.account = {
|
||||||
|
...row,
|
||||||
|
name: `${row.name} - ${vm.$t('Duplicate').toLowerCase()}`
|
||||||
|
}
|
||||||
|
vm.iAsset = data
|
||||||
|
vm.showAddDialog = false
|
||||||
|
vm.accountCreateUpdateTitle = vm.$t('DuplicateAccount')
|
||||||
|
setTimeout(() => {
|
||||||
|
vm.showAddDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Test',
|
||||||
|
title: vm.$t('VerifySecret'),
|
||||||
|
divided: true,
|
||||||
|
can: ({ row }) =>
|
||||||
|
!vm.$store.getters.currentOrgIsRoot &&
|
||||||
|
vm.$hasPerm('accounts.verify_account') &&
|
||||||
|
row.asset['auto_config'].ansible_enabled &&
|
||||||
|
row.asset['auto_config'].ping_enabled,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
vm.$axios.post(
|
||||||
|
`/api/v1/accounts/accounts/tasks/`,
|
||||||
|
{ action: 'verify', accounts: [row.id] }
|
||||||
|
).then(res => {
|
||||||
|
openTaskPage(res['task'])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ClearSecret',
|
||||||
|
title: vm.$t('ClearSecret'),
|
||||||
|
can: vm.$hasPerm('accounts.change_account'),
|
||||||
|
type: 'primary',
|
||||||
|
callback: ({ row }) => {
|
||||||
|
vm.$axios.patch(
|
||||||
|
`/api/v1/accounts/accounts/clear-secret/`,
|
||||||
|
{ account_ids: [row.id] }
|
||||||
|
).then(() => {
|
||||||
|
vm.$message.success(vm.$tc('ClearSuccessMsg'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SecretHistory',
|
||||||
|
// 密文历史
|
||||||
|
title: vm.$t('HistoryPassword'),
|
||||||
|
can: () => vm.$hasPerm('accounts.view_accountsecret'),
|
||||||
|
type: 'primary',
|
||||||
|
callback: ({ row }) => {
|
||||||
|
vm.currentAccountColumn = row
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
vm.showPasswordHistoryDialog = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CopyToOther',
|
||||||
|
title: vm.$t('CopyToAsset'),
|
||||||
|
type: 'primary',
|
||||||
|
divided: true,
|
||||||
|
callback: ({ row }) => {
|
||||||
|
vm.accountCreateUpdateTitle = vm.$t('CopyToOther')
|
||||||
|
vm.$route.query.flag = 'copy'
|
||||||
|
vm.iAsset = vm.asset
|
||||||
|
vm.account = row
|
||||||
|
vm.showAddDialog = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MoveToOther',
|
||||||
|
title: vm.$t('MoveToAsset'),
|
||||||
|
type: 'primary',
|
||||||
|
callback: ({ row }) => {
|
||||||
|
vm.accountCreateUpdateTitle = vm.$t('MoveToOther')
|
||||||
|
vm.$route.query.flag = 'move'
|
||||||
|
vm.iAsset = vm.asset
|
||||||
|
vm.account = row
|
||||||
|
vm.showAddDialog = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const accountQuickFilters = (vm) => [
|
||||||
|
{
|
||||||
|
label: vm.$t('Recent (7 days)'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: vm.$t('RecentlyDiscovered'),
|
||||||
|
filter: {
|
||||||
|
latest_discovery: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('RecentlyLoggedIn'),
|
||||||
|
filter: {
|
||||||
|
latest_accessed: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('RecentlyModified'),
|
||||||
|
filter: {
|
||||||
|
latest_updated: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('RecentlyChangedPassword'),
|
||||||
|
filter: {
|
||||||
|
latest_secret_changed: '1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('RecentPasswordChangeFailed'),
|
||||||
|
filter: {
|
||||||
|
latest_secret_changed_failed: '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('RiskyAccount'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: vm.$t('LongTimeNoLogin'),
|
||||||
|
filter: {
|
||||||
|
risk: 'long_time_no_login'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('UnmanagedAccount'),
|
||||||
|
filter: {
|
||||||
|
risk: 'new_found'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('WeakPassword'),
|
||||||
|
filter: {
|
||||||
|
risk: 'weak_password'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('EmptyPassword'),
|
||||||
|
filter: {
|
||||||
|
has_secret: 'false'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('LongTimeNoChangeSecret'),
|
||||||
|
filter: {
|
||||||
|
long_time_no_change_secret: 'true'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('LongTimeNoVerify'),
|
||||||
|
filter: {
|
||||||
|
long_time_no_verify: 'true'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('AccountType'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: vm.$t('All'),
|
||||||
|
filter: {
|
||||||
|
category: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('Host'),
|
||||||
|
filter: {
|
||||||
|
category: 'host'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('Database'),
|
||||||
|
filter: {
|
||||||
|
category: 'database'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('Cloud'),
|
||||||
|
filter: {
|
||||||
|
category: 'cloud'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('Device'),
|
||||||
|
filter: {
|
||||||
|
category: 'device'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Web',
|
||||||
|
filter: {
|
||||||
|
category: 'website'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: vm.$t('Other'),
|
||||||
|
filter: {
|
||||||
|
category: 'custom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<Dialog
|
<Dialog
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:title="$tc('Assets')"
|
:title="$tc('Assets')"
|
||||||
:disabled-status="!isLoaded"
|
|
||||||
custom-class="asset-select-dialog"
|
custom-class="asset-select-dialog"
|
||||||
top="2vh"
|
top="2vh"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@ -23,8 +22,8 @@
|
|||||||
:url="baseUrl"
|
:url="baseUrl"
|
||||||
class="tree-table"
|
class="tree-table"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
|
||||||
@loaded="handleTableLoaded"
|
@loaded="handleTableLoaded"
|
||||||
|
v-on="$listeners"
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
@ -142,6 +142,10 @@ export default {
|
|||||||
treeSetting.showDelete = this.$hasPerm('assets.delete_node')
|
treeSetting.showDelete = this.$hasPerm('assets.delete_node')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
reloadTable() {
|
||||||
|
console.log('Reload table')
|
||||||
|
this.$refs.TreeList.reloadTable()
|
||||||
|
},
|
||||||
setTreeUrlQuery() {
|
setTreeUrlQuery() {
|
||||||
let str = ''
|
let str = ''
|
||||||
for (const key in this.treeUrlQuery) {
|
for (const key in this.treeUrlQuery) {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import { DrawerListTable as ListTable } from '@/components'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BlockedIPList',
|
name: 'BlockedIPList',
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
>
|
>
|
||||||
<el-form :model="secretInfo" class="password-form" label-position="right" label-width="100px">
|
<el-form :model="secretInfo" class="password-form" label-position="right" label-width="100px">
|
||||||
<el-form-item :label="$tc('OldSecret')">
|
<el-form-item :label="$tc('OldSecret')">
|
||||||
<ShowKeyCopyFormatter
|
<SecretViewerFormatter
|
||||||
:cell-value="secretInfo.old_secret"
|
:cell-value="secretInfo.old_secret"
|
||||||
:col="{ formatterArgs: {
|
:col="{ formatterArgs: {
|
||||||
name: 'old_secret'
|
name: 'old_secret'
|
||||||
@ -20,7 +20,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$tc('NewSecret')">
|
<el-form-item :label="$tc('NewSecret')">
|
||||||
<ShowKeyCopyFormatter
|
<SecretViewerFormatter
|
||||||
:cell-value="secretInfo.new_secret"
|
:cell-value="secretInfo.new_secret"
|
||||||
:col="{ formatterArgs: {
|
:col="{ formatterArgs: {
|
||||||
name: 'new_secret'
|
name: 'new_secret'
|
||||||
@ -34,13 +34,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
|
import { SecretViewerFormatter } from '@/components/Table/TableFormatters'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RecordViewSecret',
|
name: 'RecordViewSecret',
|
||||||
components: {
|
components: {
|
||||||
Dialog,
|
Dialog,
|
||||||
ShowKeyCopyFormatter
|
SecretViewerFormatter
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
visible: {
|
visible: {
|
||||||
|
@ -87,7 +87,7 @@ export default {
|
|||||||
columnsExclude: ['spec_info'],
|
columnsExclude: ['spec_info'],
|
||||||
columnsShow: {
|
columnsShow: {
|
||||||
min: ['name', 'address', 'accounts'],
|
min: ['name', 'address', 'accounts'],
|
||||||
default: ['name', 'address', 'platform', 'connectivity', 'view_account', 'actions']
|
default: ['name', 'address', 'platform', 'view_account', 'actions']
|
||||||
},
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row :gutter="24">
|
<TwoCol>
|
||||||
<el-col :md="20" :sm="22">
|
<ListTable v-bind="config" />
|
||||||
<ListTable v-bind="config" />
|
</TwoCol>
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import { DrawerListTable as ListTable } from '@/components'
|
||||||
import { toM2MJsonParams } from '@/utils/jms'
|
import { toM2MJsonParams } from '@/utils/jms'
|
||||||
|
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AssetJsonTab',
|
name: 'AssetJsonTab',
|
||||||
components: {
|
components: {
|
||||||
|
TwoCol,
|
||||||
ListTable
|
ListTable
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
object: {
|
object: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -32,9 +33,11 @@ export default {
|
|||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: `/api/v1/assets/assets/?${key}=${value}`,
|
url: `/api/v1/assets/assets/?${key}=${value}`,
|
||||||
columns: ['name', 'address', 'platform',
|
columns: ['name', 'address', 'platform', 'type', 'is_active'],
|
||||||
'type', 'is_active'
|
columnsShow: {
|
||||||
],
|
min: ['name', 'address'],
|
||||||
|
default: ['name', 'address', 'platform']
|
||||||
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
label: this.$t('Asset'),
|
label: this.$t('Asset'),
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row :gutter="24">
|
<TwoCol>
|
||||||
<el-col :md="20" :sm="22">
|
<ListTable v-bind="config" />
|
||||||
<ListTable v-bind="config" />
|
</TwoCol>
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import { DrawerListTable as ListTable } from '@/components'
|
||||||
import { toM2MJsonParams } from '@/utils/jms'
|
import { toM2MJsonParams } from '@/utils/jms'
|
||||||
|
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'User',
|
name: 'User',
|
||||||
components: {
|
components: {
|
||||||
|
TwoCol,
|
||||||
ListTable
|
ListTable
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@ -34,13 +34,16 @@ export default {
|
|||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: `/api/v1/users/users/?${key}=${value}`,
|
url: `/api/v1/users/users/?${key}=${value}`,
|
||||||
columns: [
|
columns: [
|
||||||
'name', 'username', 'groups', 'system_roles',
|
'name', 'username', 'email', 'groups', 'system_roles',
|
||||||
'org_roles', 'source', 'is_valid'
|
'org_roles', 'source', 'is_valid'
|
||||||
],
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['name', 'username'],
|
||||||
|
default: ['name', 'username', 'email']
|
||||||
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
label: this.$t('Name'),
|
label: this.$t('Name'),
|
||||||
width: 85,
|
|
||||||
formatter: (row) => {
|
formatter: (row) => {
|
||||||
const to = {
|
const to = {
|
||||||
name: 'UserDetail',
|
name: 'UserDetail',
|
||||||
|
@ -1,30 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-row :gutter="20">
|
<TwoCol>
|
||||||
<el-col :md="16" :sm="24">
|
<IBox :title="title" class="block" v-bind="$attrs">
|
||||||
<IBox :title="title" class="block" v-bind="$attrs">
|
<el-timeline>
|
||||||
<el-timeline>
|
<el-timeline-item
|
||||||
<el-timeline-item
|
v-for="(activity, index) in activities"
|
||||||
v-for="(activity, index) in activities"
|
:key="index"
|
||||||
:key="index"
|
:size="activity.size"
|
||||||
:size="activity.size"
|
:timestamp="activity.timestamp"
|
||||||
:timestamp="activity.timestamp"
|
:type="activity.type"
|
||||||
:type="activity.type"
|
placement="bottom"
|
||||||
placement="bottom"
|
>
|
||||||
|
{{ activity.content }}
|
||||||
|
<el-link
|
||||||
|
v-if="activity['detail_url']"
|
||||||
|
type="primary"
|
||||||
|
@click.native="onClick(activity)"
|
||||||
>
|
>
|
||||||
{{ activity.content }}
|
{{ $tc('Detail') }}
|
||||||
<el-link
|
</el-link>
|
||||||
v-if="activity['detail_url']"
|
</el-timeline-item>
|
||||||
type="primary"
|
</el-timeline>
|
||||||
@click.native="onClick(activity)"
|
</IBox>
|
||||||
>
|
</TwoCol>
|
||||||
{{ $tc('Detail') }}
|
|
||||||
</el-link>
|
|
||||||
</el-timeline-item>
|
|
||||||
</el-timeline>
|
|
||||||
</IBox>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<DiffDetail ref="DetailDialog" :title="$tc('OperateLog')" />
|
<DiffDetail ref="DetailDialog" :title="$tc('OperateLog')" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -34,10 +32,12 @@ import IBox from '@/components/IBox/index.vue'
|
|||||||
import DiffDetail from '@/components/Dialog/DiffDetail.vue'
|
import DiffDetail from '@/components/Dialog/DiffDetail.vue'
|
||||||
import { openTaskPage } from '@/utils/jms'
|
import { openTaskPage } from '@/utils/jms'
|
||||||
import { toSafeLocalDateStr } from '@/utils/time'
|
import { toSafeLocalDateStr } from '@/utils/time'
|
||||||
|
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ResourceActivity',
|
name: 'ResourceActivity',
|
||||||
components: {
|
components: {
|
||||||
|
TwoCol,
|
||||||
IBox,
|
IBox,
|
||||||
DiffDetail
|
DiffDetail
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<IBox v-if="loading" style="width: 100%; height: 200px" />
|
<IBox v-if="loading" style="width: 100%; height: 200px" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<DetailCard v-if="hasObject && items.length > 0" :items="validItems" :loading="loading" v-bind="$attrs" />
|
<DetailCard
|
||||||
|
v-if="hasObject && items.length > 0"
|
||||||
|
:items="validItems"
|
||||||
|
:loading="loading"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -32,7 +37,7 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
showUndefine: {
|
showUndefined: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
@ -168,7 +173,7 @@ export default {
|
|||||||
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
|
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
|
||||||
let remoteMeta = data.actions['GET'] || {}
|
let remoteMeta = data.actions['GET'] || {}
|
||||||
if (this.nested) {
|
if (this.nested) {
|
||||||
remoteMeta = remoteMeta[this.nested]?.children || {}
|
remoteMeta = remoteMeta[this.nested]?.children || remoteMeta || {}
|
||||||
}
|
}
|
||||||
let fields = this.fields
|
let fields = this.fields
|
||||||
fields = fields || Object.keys(remoteMeta)
|
fields = fields || Object.keys(remoteMeta)
|
||||||
@ -220,7 +225,7 @@ export default {
|
|||||||
value = this.parseValue(value, fieldMeta.type)
|
value = this.parseValue(value, fieldMeta.type)
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
if (this.showUndefine) {
|
if (this.showUndefined) {
|
||||||
value = '-'
|
value = '-'
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<IBox :fa="fa" :title="title">
|
<IBox :fa="fa" :title="title">
|
||||||
<el-form :label-width="labelWidth" class="content" label-position="left">
|
<el-form :label-width="labelWidth" class="content detail-card" label-position="left">
|
||||||
<span v-for="item in items" :key="item.key">
|
<template v-for="item in items">
|
||||||
<el-form-item v-if="item.has !== false" :class="item.class" :label="item.key">
|
<div v-if="item.has !== false" :key="item.key" :class="item.class " :label="item.key" class="el-form-item">
|
||||||
<span slot="label"> {{ formateLabel(item.key) }}</span>
|
<span slot="label" class="el-form-item__label"> {{ formateLabel(item.key) }}</span>
|
||||||
<span
|
<span class="item-value el-form-item__content">
|
||||||
:is="item.component"
|
<template
|
||||||
v-if="item.component"
|
:is="item.component"
|
||||||
v-bind="{...item}"
|
v-if="item.component"
|
||||||
/>
|
v-bind="{...item}"
|
||||||
<ItemValue v-else :value="item.value" class="item-value" v-bind="item" />
|
/>
|
||||||
</el-form-item>
|
<ItemValue v-else :value="item.value" v-bind="item" />
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
<slot />
|
|
||||||
</IBox>
|
</IBox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ export default {
|
|||||||
},
|
},
|
||||||
labelWidth: {
|
labelWidth: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '25%'
|
default: '120px'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -71,55 +72,76 @@ export default {
|
|||||||
padding: 20px 40px;
|
padding: 20px 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item {
|
|
||||||
border-bottom: 1px dashed #EBEEF5;
|
|
||||||
padding: 1px 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.array-item {
|
|
||||||
border-bottom: none;
|
|
||||||
|
|
||||||
::v-deep .el-form-item__content {
|
|
||||||
border-bottom: 1px dashed #EBEEF5
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-form-item__label:last-child {
|
|
||||||
border: 1px dashed #EBEEF5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-form-item__label {
|
|
||||||
padding-right: 8%;
|
|
||||||
overflow: hidden;
|
|
||||||
color: var(--color-icon-primary);
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-form-item__content {
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-tag--mini {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-value span {
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 2.5;
|
line-height: 2;
|
||||||
|
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
border-bottom: 1px dashed #F4F4F4;
|
||||||
|
padding: 1px 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
//text-align: end;
|
||||||
|
line-height: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
//border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.array-item {
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
|
::v-deep .el-form-item__content {
|
||||||
|
border-bottom: 1px dashed #EBEEF5
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item__label:last-child {
|
||||||
|
border: 1px dashed #EBEEF5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__label {
|
||||||
|
//padding-right: 8%;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--color-icon-primary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
width: 33%;
|
||||||
|
min-width: 120px;
|
||||||
|
padding: 5px 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
//line-height: 1.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
width: calc(100% - 120px);
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-tag--mini {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-value {
|
||||||
|
::v-deep span {
|
||||||
|
//display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-word;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -340,13 +340,13 @@ b, strong {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr td {
|
tr td {
|
||||||
line-height: 1.42857;
|
line-height: 1.4;
|
||||||
padding: 8px;
|
padding: 8px 0;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.item td {
|
tr.item td {
|
||||||
border-top: 1px solid #e7eaec;
|
border-top: 1px dashed #EBEEF5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-margin {
|
.box-margin {
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card shadow="never">
|
<div>
|
||||||
<div slot="header" class="summary-header">
|
<div class="summary-header">
|
||||||
<span class="header-title">{{ title }}</span>
|
<el-tooltip :content="title" placement="top" :open-delay="500">
|
||||||
|
<span class="title">{{ title }}</span>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<slot>
|
<slot>
|
||||||
<h1 class="no-margins">
|
<h3 class="no-margins ">
|
||||||
<span v-if="body.disabled" class="disabled-link">{{ body.count }}</span>
|
<span v-async="iCount" class="num" @click="handleClick">
|
||||||
<router-link v-else :to="body.route">
|
-
|
||||||
<span>{{ body.count }}</span>
|
</span>
|
||||||
</router-link>
|
</h3>
|
||||||
</h1>
|
|
||||||
<small>{{ body.comment }}</small>
|
|
||||||
</slot>
|
</slot>
|
||||||
</el-card>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SummaryCard',
|
name: 'SummaryCard',
|
||||||
props: {
|
props: {
|
||||||
@ -23,56 +24,90 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
rightSideLabel: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
body: {
|
body: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
count: {
|
||||||
|
type: [Number, String, Promise],
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
route: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iCount() {
|
||||||
|
const count = this.body.count || this.count
|
||||||
|
return count
|
||||||
|
},
|
||||||
|
iRoute() {
|
||||||
|
return this.body.route || this.route
|
||||||
|
},
|
||||||
|
iDisabled() {
|
||||||
|
return this.body.disabled === undefined ? this.disabled : this.body.disabled
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
if (this.iDisabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.iRoute) {
|
||||||
|
this.$router.push(this.iRoute)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.callback.bind(this)()
|
||||||
|
this.$emit('click')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pull-right {
|
.summary-header {
|
||||||
float: right !important;
|
//color: var(--color-icon-primary);
|
||||||
}
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.header-title {
|
.title {
|
||||||
font-size: 14px;
|
font-style: normal;
|
||||||
margin: 0 0 7px;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.right-side ::v-deep .el-tag {
|
.no-margins {
|
||||||
font-weight: 600;
|
margin: 0 !important;
|
||||||
padding: 3px 8px;
|
|
||||||
text-shadow: none;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
.num {
|
||||||
font-size: 30px;
|
font-style: normal;
|
||||||
font-weight: 100;
|
font-weight: 500;
|
||||||
}
|
font-size: 24px;
|
||||||
|
line-height: 40px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.el-card__body {
|
&:hover {
|
||||||
background-color: #ffffff;
|
color: var(--color-primary);
|
||||||
color: inherit;
|
}
|
||||||
padding: 15px 20px 20px 20px !important;
|
|
||||||
border-color: #e7eaec;
|
|
||||||
border-image: none;
|
|
||||||
border-style: solid solid none;
|
|
||||||
border-width: 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-margins {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled-link {
|
|
||||||
color: #428bca;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -5,15 +5,28 @@
|
|||||||
v-if="action.dropdown"
|
v-if="action.dropdown"
|
||||||
v-show="action.dropdown.length > 0"
|
v-show="action.dropdown.length > 0"
|
||||||
:key="action.name"
|
:key="action.name"
|
||||||
|
:class="[action.name, {grouped: action.grouped }]"
|
||||||
|
:size="action.size"
|
||||||
|
:split-button="!!action.split"
|
||||||
|
:type="action.type"
|
||||||
class="action-item"
|
class="action-item"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
@click="handleClick(action)"
|
||||||
@command="handleDropdownCallback"
|
@command="handleDropdownCallback"
|
||||||
>
|
>
|
||||||
<el-button :size="size" class="more-action" v-bind="cleanButtonAction(action)">
|
<span v-if="action.split">
|
||||||
<span v-if="action.icon && !action.icon.startsWith('el-')" class="pre-icon">
|
{{ action.title }}
|
||||||
<i v-if="action.icon.startsWith('fa')" :class="'fa fa-fw ' + action.icon" />
|
</span>
|
||||||
<svg-icon v-else :icon-class="action.icon" />
|
<el-button
|
||||||
|
v-else
|
||||||
|
:class="action.name"
|
||||||
|
:size="size"
|
||||||
|
class="more-action"
|
||||||
|
v-bind="{...cleanButtonAction(action), icon: ''}"
|
||||||
|
>
|
||||||
|
<span class="pre-icon">
|
||||||
|
<Icon v-if="action.icon" :icon="action.icon" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="action.title">
|
<span v-if="action.title">
|
||||||
{{ action.title }}<i class="el-icon-arrow-down el-icon--right" />
|
{{ action.title }}<i class="el-icon-arrow-down el-icon--right" />
|
||||||
@ -29,7 +42,13 @@
|
|||||||
>
|
>
|
||||||
{{ option.group }}
|
{{ option.group }}
|
||||||
</div>
|
</div>
|
||||||
<el-tooltip :key="option.name" :content="option.tip" :disabled="!option.tip" :open-delay="500" placement="top">
|
<el-tooltip
|
||||||
|
:key="option.name"
|
||||||
|
:content="option.tip"
|
||||||
|
:disabled="!option.tip"
|
||||||
|
:open-delay="500"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
:key="option.name"
|
:key="option.name"
|
||||||
:command="[option, action]"
|
:command="[option, action]"
|
||||||
@ -37,9 +56,8 @@
|
|||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
v-bind="{...option, icon: ''}"
|
v-bind="{...option, icon: ''}"
|
||||||
>
|
>
|
||||||
<span v-if="option.icon" class="pre-icon">
|
<span v-if="actionsHasIcon(action.dropdown)" class="pre-icon">
|
||||||
<i v-if="option.icon.startsWith('fa')" :class="'fa fa-fw ' + option.icon" />
|
<Icon v-if="option.icon" :icon="option.icon" />
|
||||||
<svg-icon v-else :icon-class="option.icon" />
|
|
||||||
</span>
|
</span>
|
||||||
{{ option.title }}
|
{{ option.title }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
@ -51,16 +69,16 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
:key="action.name"
|
:key="action.name"
|
||||||
|
:class="[action.name, {grouped: action.grouped }]"
|
||||||
:size="size"
|
:size="size"
|
||||||
class="action-item"
|
class="action-item"
|
||||||
v-bind="{...cleanButtonAction(action), icon: action.icon && action.icon.startsWith('el-') ? action.icon : ''}"
|
v-bind="{...cleanButtonAction(action), icon: ''}"
|
||||||
@click="handleClick(action)"
|
@click="handleClick(action)"
|
||||||
>
|
>
|
||||||
<el-tooltip :content="action.tip" :disabled="!action.tip" :open-delay="500" placement="top">
|
<el-tooltip :content="action.tip" :disabled="!action.tip" placement="top">
|
||||||
<span :title="action.tip">
|
<span>
|
||||||
<span v-if="action.icon && !action.icon.startsWith('el-')" style="vertical-align: initial">
|
<span v-if="action.icon" style="vertical-align: initial">
|
||||||
<i v-if="action.icon.startsWith('fa')" :class="'fa ' + action.icon" />
|
<Icon :icon="action.icon" />
|
||||||
<svg-icon v-else :icon-class="action.icon" />
|
|
||||||
</span>
|
</span>
|
||||||
{{ action.title }}
|
{{ action.title }}
|
||||||
</span>
|
</span>
|
||||||
@ -72,9 +90,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { toSentenceCase } from '@/utils/common'
|
import { toSentenceCase } from '@/utils/common'
|
||||||
|
import Icon from '@/components/Widgets/Icon/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DataActions',
|
name: 'DataActions',
|
||||||
|
components: {
|
||||||
|
Icon
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
grouped: {
|
grouped: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -99,6 +121,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
actionsHasIcon(actions) {
|
||||||
|
return actions.some(action => action.icon)
|
||||||
|
},
|
||||||
hasIcon(action, type = '') {
|
hasIcon(action, type = '') {
|
||||||
const icon = action.icon
|
const icon = action.icon
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
@ -110,6 +135,7 @@ export default {
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
handleDropdownCallback(command) {
|
handleDropdownCallback(command) {
|
||||||
|
console.log('Handle drop down lick')
|
||||||
const [option, dropdown] = command
|
const [option, dropdown] = command
|
||||||
if (option.disabled) {
|
if (option.disabled) {
|
||||||
return
|
return
|
||||||
@ -156,6 +182,7 @@ export default {
|
|||||||
delete action['callback']
|
delete action['callback']
|
||||||
delete action['name']
|
delete action['name']
|
||||||
delete action['can']
|
delete action['can']
|
||||||
|
delete action['split']
|
||||||
return action
|
return action
|
||||||
},
|
},
|
||||||
cleanActions(actions) {
|
cleanActions(actions) {
|
||||||
@ -185,6 +212,10 @@ export default {
|
|||||||
}
|
}
|
||||||
delete action['can']
|
delete action['can']
|
||||||
|
|
||||||
|
if (!action.size) {
|
||||||
|
action.size = 'small'
|
||||||
|
}
|
||||||
|
|
||||||
if (action.dropdown) {
|
if (action.dropdown) {
|
||||||
action.dropdown = this.cleanActions(action.dropdown)
|
action.dropdown = this.cleanActions(action.dropdown)
|
||||||
}
|
}
|
||||||
@ -209,6 +240,10 @@ $color-drop-menu-border: #e4e7ed;
|
|||||||
.action-item {
|
.action-item {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
|
&.grouped {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
@ -250,7 +285,10 @@ $color-drop-menu-border: #e4e7ed;
|
|||||||
|
|
||||||
.el-button {
|
.el-button {
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
color: $btn-text-color;
|
|
||||||
|
&:not(.is-plain) {
|
||||||
|
color: $btn-text-color;
|
||||||
|
}
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -281,7 +319,6 @@ $color-drop-menu-border: #e4e7ed;
|
|||||||
// 下拉 options
|
// 下拉 options
|
||||||
.el-dropdown-menu {
|
.el-dropdown-menu {
|
||||||
::v-deep .more-batch-processing {
|
::v-deep .more-batch-processing {
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
@ -304,6 +341,7 @@ $color-drop-menu-border: #e4e7ed;
|
|||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
|
line-height: 34px;
|
||||||
|
|
||||||
.pre-icon {
|
.pre-icon {
|
||||||
width: 17px;
|
width: 17px;
|
||||||
@ -325,6 +363,8 @@ $color-drop-menu-border: #e4e7ed;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu__item {
|
.el-dropdown-menu__item {
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
&.is-disabled {
|
&.is-disabled {
|
||||||
color: var(--color-disabled);
|
color: var(--color-disabled);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog
|
||||||
v-if="detailVisible"
|
v-if="detailVisible"
|
||||||
|
:modal="false"
|
||||||
:show-cancel="false"
|
:show-cancel="false"
|
||||||
:show-confirm="false"
|
:show-confirm="false"
|
||||||
:title="title"
|
:title="title"
|
||||||
|
71
src/components/Dialog/ProcessingDialog.vue
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
:show-cancel="false"
|
||||||
|
:visible="iVisible"
|
||||||
|
class="processing-dialog"
|
||||||
|
height="300"
|
||||||
|
title="Processing"
|
||||||
|
width="300"
|
||||||
|
@confirm="iVisible=false"
|
||||||
|
>
|
||||||
|
<div id="load">
|
||||||
|
<div class="spinner" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Dialog from './index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProcessingDialog',
|
||||||
|
components: { Dialog },
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.processing-dialog {
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border: 5px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: var(--color-primary);
|
||||||
|
animation: spin 1s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
#load {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
69
src/components/Dialog/RemoteProcessingDialog.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
:visible="iVisible"
|
||||||
|
height="300"
|
||||||
|
title="Processing"
|
||||||
|
width="300"
|
||||||
|
class="processing-dialog"
|
||||||
|
>
|
||||||
|
<div id="load">
|
||||||
|
<div class="spinner" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Dialog from './index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RemoteProcessingDialog',
|
||||||
|
components: { Dialog },
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.processing-dialog {
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border: 5px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: var(--color-primary);
|
||||||
|
animation: spin 1s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
#load {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
96
src/components/Dialog/Secret.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
:show-cancel="false"
|
||||||
|
:title="title"
|
||||||
|
:visible.sync="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="700px"
|
||||||
|
@close="onClose"
|
||||||
|
@confirm="visible = false"
|
||||||
|
>
|
||||||
|
<el-alert type="warning" :closable="false">
|
||||||
|
{{ warningText }}
|
||||||
|
<div class="secret">
|
||||||
|
<div class="row">
|
||||||
|
<span class="col">ID:</span>
|
||||||
|
<span class="value">{{ keyInfo.id }}</span>
|
||||||
|
<i class="el-icon-copy-document copy-icon" @click="handleCopy(keyInfo.id)" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="col">Secret:</span>
|
||||||
|
<span class="value">{{ keyInfo.secret }}</span>
|
||||||
|
<i class="el-icon-copy-document copy-icon" @click="handleCopy(keyInfo.secret)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-alert>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import i18n from '@/i18n/i18n'
|
||||||
|
import { copy } from '@/utils/common'
|
||||||
|
import Dialog from '@/components/Dialog/index'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Secret',
|
||||||
|
components: {
|
||||||
|
Dialog
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: () => i18n.t('CreateAccessKey')
|
||||||
|
},
|
||||||
|
warningText: {
|
||||||
|
type: String,
|
||||||
|
default: () => i18n.t('ApiKeyWarning')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
keyInfo: { id: '', secret: '' },
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show(data) {
|
||||||
|
this.keyInfo = data
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
handleCopy(value) {
|
||||||
|
copy(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.secret {
|
||||||
|
color: #2b2f3a;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col {
|
||||||
|
width: 100px;
|
||||||
|
text-align: left;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
</style>
|
@ -82,8 +82,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
iWidth() {
|
iWidth() {
|
||||||
@ -102,59 +101,69 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dialog ::v-deep .el-dialog {
|
.dialog ::v-deep .el-dialog {
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
max-width: min(100vw, 1500px);
|
max-width: min(100vw, 1500px);
|
||||||
|
|
||||||
|
//.el-form, .form-buttons {
|
||||||
|
// margin-left: 20px;
|
||||||
|
//}
|
||||||
|
|
||||||
|
.form-group-header {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form--label-top {
|
||||||
.form-group-header {
|
.form-group-header {
|
||||||
margin-left: 20px;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.el-icon-circle-check {
|
.el-icon-circle-check {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 15px 22px;
|
padding: 15px 22px;
|
||||||
border-bottom: 1px solid #dee2e6;
|
border-bottom: 1px solid #dee2e6;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
&:has(.el-table) {
|
&:has(.el-table) {
|
||||||
background: #f3f3f4;
|
background: #f3f3f4;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
border-top: 1px solid #dee2e6;
|
|
||||||
padding: 16px 25px;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
&__footer {
|
||||||
.dialog ::v-deep .el-dialog {
|
border-top: 1px solid #dee2e6;
|
||||||
max-width: calc(100% - 30px);
|
padding: 16px 25px;
|
||||||
}
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-footer ::v-deep button.el-button {
|
@media (max-width: 900px) {
|
||||||
font-size: 13px;
|
.dialog ::v-deep .el-dialog {
|
||||||
padding: 8px 12px;
|
max-width: calc(100% - 30px);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-fade-enter-active, .dialog-fade-leave-active {
|
.dialog-footer ::v-deep button.el-button {
|
||||||
transition: opacity 1s ease;
|
font-size: 13px;
|
||||||
}
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-fade-enter, .dialog-fade-leave-to /* .dialog-fade-leave-active 在 <2.1.8 中以及被重复声明 */
|
.dialog-fade-enter-active, .dialog-fade-leave-active {
|
||||||
{
|
transition: opacity 1s ease;
|
||||||
opacity: 0;
|
}
|
||||||
}
|
|
||||||
|
.dialog-fade-enter, .dialog-fade-leave-to /* .dialog-fade-leave-active 在 <2.1.8 中以及被重复声明 */
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
289
src/components/Drawer/index.vue
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
ref="drawer"
|
||||||
|
v-el-drawer-drag-width
|
||||||
|
:append-to-body="true"
|
||||||
|
:before-close="handleClose"
|
||||||
|
:class="['drawer', { 'drawer__no-footer': !hasFooter }]"
|
||||||
|
:modal="modal"
|
||||||
|
:size="size"
|
||||||
|
:title="title"
|
||||||
|
:visible.sync="iVisible"
|
||||||
|
custom-class="drawer"
|
||||||
|
destroy-on-close
|
||||||
|
direction="rtl"
|
||||||
|
v-on="$listeners"
|
||||||
|
>
|
||||||
|
<div class="drawer__content">
|
||||||
|
<slot name="default">
|
||||||
|
<component
|
||||||
|
:is="component"
|
||||||
|
v-if="component"
|
||||||
|
ref="dynamicComponent"
|
||||||
|
v-bind="componentProps"
|
||||||
|
v-on="componentListeners"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="hasFooter" ref="drawerFooter" class="drawer__footer" />
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: '768px'
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
type: [String, Function, Object],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
componentListeners: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
modal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
hasFooter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
formLabelWidth: '80px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose(done) {
|
||||||
|
this.$emit('close-drawer')
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.drawer__no-footer {
|
||||||
|
::v-deep {
|
||||||
|
.drawer {
|
||||||
|
.page {
|
||||||
|
height: calc(100vh - 55px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer {
|
||||||
|
::v-deep {
|
||||||
|
min-width: 565px;
|
||||||
|
|
||||||
|
.el-card__body {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-submenu {
|
||||||
|
.el-tabs__header {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item.is-top {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-buttons {
|
||||||
|
margin-left: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form {
|
||||||
|
margin-right: 1px;
|
||||||
|
padding-right: 15px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.detail-card {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 中
|
||||||
|
&.content {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-buttons {
|
||||||
|
//position: absolute;
|
||||||
|
// bottom: 13px;
|
||||||
|
// margin-left: 20%;
|
||||||
|
// margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form 中的子 form
|
||||||
|
.el-form {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
.el-form-item__label {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio {
|
||||||
|
line-height: 25px;
|
||||||
|
margin-right: 13px;
|
||||||
|
|
||||||
|
.el-radio__label {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.el-form--label-top {
|
||||||
|
.el-radio-group {
|
||||||
|
.el-radio {
|
||||||
|
display: block;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
.el-form-item__label {
|
||||||
|
padding: 0 20px 0 0;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-form {
|
||||||
|
margin-left: -1px;
|
||||||
|
|
||||||
|
.form-fields {
|
||||||
|
max-height: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.form-fields {
|
||||||
|
//overflow: auto;
|
||||||
|
//max-height: calc(100vh - 180px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-checkbox-group {
|
||||||
|
.el-checkbox {
|
||||||
|
display: block;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content:has(.el-checkbox):not(:has(.el-checkbox-group)) {
|
||||||
|
display: inline-block; /* 更改为 inline-block */
|
||||||
|
//width: unset; /* 这个设置上去后,平台详情中, Automations 会有问题 */
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content {
|
||||||
|
form {
|
||||||
|
.el-form-item {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group-header {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__header {
|
||||||
|
border-bottom: 1px solid #EBEEF5;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 15px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sql.container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
height: calc(100vh - 110px);
|
||||||
|
|
||||||
|
&.tab-page {
|
||||||
|
.page-content {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
height: unset;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibox {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer__content, .tab-page-content {
|
||||||
|
background: #f3f3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer__footer {
|
||||||
|
border-top: solid 1px #f3f3f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.el-drawer__header {
|
||||||
|
// margin-bottom: 20px;
|
||||||
|
//
|
||||||
|
// span {
|
||||||
|
// font-size: 16px;
|
||||||
|
// font-weight: 800;
|
||||||
|
// color: var(--color-text-primary);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -7,18 +7,18 @@
|
|||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
>
|
>
|
||||||
<div
|
<template
|
||||||
v-for="(group, i) in groups"
|
v-for="(group, i) in groups"
|
||||||
:key="'group-'+group.name"
|
|
||||||
:slot="'id:'+group.name"
|
:slot="'id:'+group.name"
|
||||||
>
|
>
|
||||||
<FormGroupHeader
|
<FormGroupHeader
|
||||||
v-if="!groupHidden(group, i)"
|
v-if="!groupHidden(group, i)"
|
||||||
|
:key="'group-' + group.name"
|
||||||
:group="group"
|
:group="group"
|
||||||
:index="i"
|
:index="i"
|
||||||
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</template>
|
||||||
</DataForm>
|
</DataForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -163,5 +163,6 @@ export default {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import ObjectSelect2 from '@/components/Form/FormFields/NestedObjectSelect2.vue'
|
import ObjectSelect2 from '@/components/Form/FormFields/NestedObjectSelect2.vue'
|
||||||
import NestedField from '@/components/Form/AutoDataForm/components/NestedField.vue'
|
import NestedField from '@/components/Form/AutoDataForm/components/NestedField.vue'
|
||||||
import Switcher from '@/components/Form/FormFields/Switcher.vue'
|
|
||||||
import rules from '@/components/Form/DataForm/rules'
|
import rules from '@/components/Form/DataForm/rules'
|
||||||
import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
|
import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
|
||||||
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
||||||
@ -64,8 +63,9 @@ export class FormFieldGenerator {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
type = ''
|
type = 'checkbox'
|
||||||
field.component = Switcher
|
// field.component = Switcher
|
||||||
|
// field.type = 'checkbox'
|
||||||
break
|
break
|
||||||
case 'list':
|
case 'list':
|
||||||
type = 'input'
|
type = 'input'
|
||||||
|
@ -8,18 +8,20 @@
|
|||||||
v-bind="data.attrs"
|
v-bind="data.attrs"
|
||||||
>
|
>
|
||||||
<template v-if="data.label" #label>
|
<template v-if="data.label" #label>
|
||||||
<span>{{ data.label }}</span>
|
<span :title="data.label">
|
||||||
<el-tooltip
|
{{ data.label }}
|
||||||
v-if="data.helpTip"
|
<el-tooltip
|
||||||
:open-delay="500"
|
v-if="data.helpTip"
|
||||||
:tabindex="-1"
|
:open-delay="500"
|
||||||
effect="dark"
|
:tabindex="-1"
|
||||||
placement="right"
|
effect="dark"
|
||||||
popper-class="help-tips"
|
placement="right"
|
||||||
>
|
popper-class="help-tips"
|
||||||
<div slot="content" v-sanitize="data.helpTip" class="help-tip-content" /> <!-- Noncompliant -->
|
>
|
||||||
<i class="fa fa-question-circle-o help-tip-icon" />
|
<div slot="content" v-sanitize="data.helpTip" class="help-tip-content" /> <!-- Noncompliant -->
|
||||||
</el-tooltip>
|
<i class="fa fa-question-circle-o help-tip-icon" />
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="readonly && hasReadonlyContent">
|
<template v-if="readonly && hasReadonlyContent">
|
||||||
<div
|
<div
|
||||||
@ -71,6 +73,7 @@
|
|||||||
<el-tooltip v-if="opt.tip" :content="opt.tip" :open-delay="500" placement="top">
|
<el-tooltip v-if="opt.tip" :content="opt.tip" :open-delay="500" placement="top">
|
||||||
<i class="el-icon-warning-outline" />
|
<i class="el-icon-warning-outline" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
<span v-if="data.helpText">{{ data.helpText }}</span>
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<!-- WARNING: radio 用 label 属性来表示 value 的含义 -->
|
<!-- WARNING: radio 用 label 属性来表示 value 的含义 -->
|
||||||
<!-- FYI: radio 的 value 属性可以在没有 radio-group 时用来关联到同一个 v-model -->
|
<!-- FYI: radio 的 value 属性可以在没有 radio-group 时用来关联到同一个 v-model -->
|
||||||
@ -87,7 +90,7 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</template>
|
</template>
|
||||||
</custom-component>
|
</custom-component>
|
||||||
<div v-if="data.helpText" class="help-block">
|
<div v-if="data.helpText" :class="data.type" class="help-block">
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="data.helpText.startsWith('!')"
|
v-if="data.helpText.startsWith('!')"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
@ -99,6 +102,9 @@
|
|||||||
</el-alert>
|
</el-alert>
|
||||||
<span v-else v-sanitize="data.helpText" />
|
<span v-else v-sanitize="data.helpText" />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="data.helpTextFormatter" class="help-block">
|
||||||
|
<RenderHelpTextSafe :render-content="data.helpTextFormatter" />
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -121,6 +127,18 @@ function validator(data) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
RenderHelpTextSafe: {
|
||||||
|
functional: true,
|
||||||
|
props: {
|
||||||
|
renderContent: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render(h, { props }) {
|
||||||
|
return props.renderContent()
|
||||||
|
}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 🐂🍺只需要有组件选项对象,就可以立刻包装成函数式组件在 template 中使用
|
* 🐂🍺只需要有组件选项对象,就可以立刻包装成函数式组件在 template 中使用
|
||||||
* FYI: https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6
|
* FYI: https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6
|
||||||
@ -315,6 +333,10 @@ export default {
|
|||||||
::v-deep .el-alert__icon {
|
::v-deep .el-alert__icon {
|
||||||
font-size: 16px
|
font-size: 16px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.checkbox {
|
||||||
|
//display: inline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.help-tip-icon {
|
.help-tip-icon {
|
||||||
|
@ -1,68 +1,71 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElFormRender
|
<div>
|
||||||
:id="id"
|
<ElFormRender
|
||||||
ref="form"
|
:id="id"
|
||||||
:class="mobile ? 'mobile' : 'desktop'"
|
ref="form"
|
||||||
:content="fields"
|
:class="[mobile ? 'mobile' : 'desktop']"
|
||||||
:form="basicForm"
|
:content="fields"
|
||||||
:label-position="labelPosition"
|
:form="basicForm"
|
||||||
label-width="25%"
|
:label-position="iLabelPosition"
|
||||||
v-bind="$attrs"
|
class="form-fields"
|
||||||
v-on="$listeners"
|
label-width="25%"
|
||||||
>
|
v-bind="$attrs"
|
||||||
<!-- slot 透传 -->
|
v-on="$listeners"
|
||||||
<slot
|
>
|
||||||
v-for="item in fields"
|
<!-- slot 透传 -->
|
||||||
:slot="`id:${item.id}`"
|
<slot
|
||||||
:name="`id:${item.id}`"
|
v-for="item in fields"
|
||||||
/>
|
:slot="`id:${item.id}`"
|
||||||
<slot
|
:name="`id:${item.id}`"
|
||||||
v-for="item in fields"
|
/>
|
||||||
:slot="`$id:${item.id}`"
|
<slot
|
||||||
:name="`$id:${item.id}`"
|
v-for="item in fields"
|
||||||
/>
|
:slot="`$id:${item.id}`"
|
||||||
|
:name="`$id:${item.id}`"
|
||||||
|
/>
|
||||||
|
|
||||||
<el-form-item v-if="hasButtons" class="form-buttons">
|
<div v-if="hasButtons" class="form-buttons">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="defaultButton"
|
v-if="defaultButton"
|
||||||
:disabled="!canSubmit"
|
:disabled="!canSubmit"
|
||||||
:loading="isSubmitting"
|
:loading="isSubmitting"
|
||||||
:size="submitBtnSize"
|
:size="submitBtnSize"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="submitForm('form')"
|
@click="submitForm('form')"
|
||||||
>
|
>
|
||||||
{{ iSubmitBtnText }}
|
{{ iSubmitBtnText }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="defaultButton && hasSaveContinue"
|
v-if="defaultButton && hasSaveContinue"
|
||||||
size="small"
|
size="small"
|
||||||
@click="submitForm('form', true)"
|
@click="submitForm('form', true)"
|
||||||
>
|
>
|
||||||
{{ $t("SaveAndAddAnother") }}
|
{{ $t("SaveAndAddAnother") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="defaultButton && hasReset"
|
v-if="defaultButton && hasReset"
|
||||||
size="small"
|
size="small"
|
||||||
@click="resetForm('form')"
|
@click="resetForm('form')"
|
||||||
>
|
>
|
||||||
{{ $t("Reset") }}
|
{{ $t("Reset") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-for="button in moreButtons"
|
v-for="button in moreButtons"
|
||||||
v-show="!button.hidden"
|
v-show="!button.hidden"
|
||||||
:key="button.title"
|
:key="button.title"
|
||||||
:loading="button.loading"
|
:loading="button.loading"
|
||||||
size="small"
|
size="small"
|
||||||
v-bind="button"
|
v-bind="button"
|
||||||
@click="handleClick(button)"
|
@click="handleClick(button)"
|
||||||
>
|
>
|
||||||
{{ button.title }}
|
{{ button.title }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</div>
|
||||||
</ElFormRender>
|
</ElFormRender>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -121,6 +124,10 @@ export default {
|
|||||||
isSubmitting: {
|
isSubmitting: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
labelPosition: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -137,7 +144,17 @@ export default {
|
|||||||
mobile() {
|
mobile() {
|
||||||
return this.$store.state.app.device === 'mobile'
|
return this.$store.state.app.device === 'mobile'
|
||||||
},
|
},
|
||||||
labelPosition() {
|
drawer() {
|
||||||
|
return this.$store.state.common.inDrawer
|
||||||
|
},
|
||||||
|
iLabelPosition() {
|
||||||
|
if (this.labelPosition) {
|
||||||
|
return this.labelPosition
|
||||||
|
}
|
||||||
|
// if (this.drawer) {
|
||||||
|
// return 'left'
|
||||||
|
// }
|
||||||
|
// return this.drawer || this.mobile ? 'top' : 'right'
|
||||||
return this.mobile ? 'top' : 'right'
|
return this.mobile ? 'top' : 'right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -211,13 +228,17 @@ export default {
|
|||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.label-top {
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
.el-form-item__content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .el-form-item {
|
::v-deep .el-form-item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
.item-params {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-form-item__label {
|
.el-form-item__label {
|
||||||
padding: 0 30px 0 0;
|
padding: 0 30px 0 0;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
@ -230,6 +251,13 @@ export default {
|
|||||||
i {
|
i {
|
||||||
color: var(--color-icon-primary);
|
color: var(--color-icon-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
max-width: calc(100% - 25px);
|
||||||
|
white-space: nowrap; /* 禁止换行 */
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item__content {
|
.el-form-item__content {
|
||||||
@ -305,8 +333,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-form-item.form-buttons {
|
::v-deep .form-buttons {
|
||||||
margin-top: 20px;
|
margin-top: 30px;
|
||||||
|
margin-left: 25%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +115,73 @@ export default {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-tree > .el-tree-node:after {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//节点有间隙,隐藏掉展开按钮就好了,如果觉得空隙没事可以删掉
|
||||||
|
.el-tree-node__expand-icon.is-leaf {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tree > .el-tree-node:before {
|
||||||
|
border-left: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tree > .el-tree-node:after {
|
||||||
|
border-top: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tree-node__children {
|
||||||
|
padding-left: 13px;
|
||||||
|
|
||||||
|
.el-tree-node {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 13px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
left: -4px;
|
||||||
|
position: absolute;
|
||||||
|
right: auto;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child:before {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
border-left: 1px dashed #dcdcdc;
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
top: -26px;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
left: -4px;
|
||||||
|
position: absolute;
|
||||||
|
right: auto;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-top: 1px dashed #dcdcdc;
|
||||||
|
height: 20px;
|
||||||
|
top: 12px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.el-tree-node__content:hover {
|
.el-tree-node__content:hover {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
@ -129,7 +196,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-tree-node__children {
|
.el-tree-node__children {
|
||||||
margin-left: -25px;
|
//margin-left: -25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
:default-time="['00:00:01', '23:59:59']"
|
:default-time="['00:00:01', '23:59:59']"
|
||||||
:end-placeholder="$tc('DateEnd')"
|
:end-placeholder="$tc('DateEnd')"
|
||||||
:picker-options="pickerOptions"
|
:picker-options="pickerOptions"
|
||||||
:range-separator="$tc('To')"
|
|
||||||
:start-placeholder="$tc('DateStart')"
|
:start-placeholder="$tc('DateStart')"
|
||||||
|
:type="type"
|
||||||
class="datepicker"
|
class="datepicker"
|
||||||
|
range-separator="-"
|
||||||
size="small"
|
size="small"
|
||||||
type="datetimerange"
|
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@change="handleDateChange"
|
@change="handleDateChange"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
@ -28,6 +28,15 @@ export default {
|
|||||||
dateEnd: {
|
dateEnd: {
|
||||||
type: [Number, String, Date],
|
type: [Number, String, Date],
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'daterange'
|
||||||
|
// default: 'datetimerange'
|
||||||
|
},
|
||||||
|
toMinMax: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -35,6 +44,10 @@ export default {
|
|||||||
const endValue = this.dateEnd || this.$route.query['date_end']
|
const endValue = this.dateEnd || this.$route.query['date_end']
|
||||||
const dateStart = new Date(startValue)
|
const dateStart = new Date(startValue)
|
||||||
const dateTo = new Date(endValue)
|
const dateTo = new Date(endValue)
|
||||||
|
if (this.toMinMax) {
|
||||||
|
dateStart.setHours(0, 0, 0, 0)
|
||||||
|
dateTo.setHours(23, 59, 59, 999)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
value: [dateStart, dateTo],
|
value: [dateStart, dateTo],
|
||||||
pickerOptions: {
|
pickerOptions: {
|
||||||
@ -74,9 +87,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShortcutClick(picker, day) {
|
onShortcutClick(picker, day) {
|
||||||
const end = new Date()
|
let start = new Date()
|
||||||
const start = new Date()
|
let end = new Date()
|
||||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * day)
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * day)
|
||||||
|
if (this.toMinMax) {
|
||||||
|
start = new Date(start.setHours(0, 0, 0, 0))
|
||||||
|
end = new Date(end.setHours(23, 59, 59, 999))
|
||||||
|
}
|
||||||
picker.$emit('pick', [start, end])
|
picker.$emit('pick', [start, end])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,8 +108,11 @@ html:lang(pt-br) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.datepicker {
|
.datepicker {
|
||||||
|
&.el-date-editor--daterange.el-input__inner {
|
||||||
|
width: 243px;
|
||||||
|
}
|
||||||
|
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
width: 233px;
|
|
||||||
border: 1px solid #dcdee2;
|
border: 1px solid #dcdee2;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import { DrawerListTable as ListTable } from '@/components'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AttrMatchResultDialog',
|
name: 'AttrMatchResultDialog',
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
v-bind="iAttrs"
|
v-bind="iAttrs"
|
||||||
@input="handleInput"
|
@input="handleInput"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
/>
|
>
|
||||||
|
hello
|
||||||
|
</Password>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -55,22 +55,22 @@ export default {
|
|||||||
{
|
{
|
||||||
id: 'uppercase',
|
id: 'uppercase',
|
||||||
label: this.$t('Uppercase'),
|
label: this.$t('Uppercase'),
|
||||||
type: 'switch'
|
type: 'checkbox'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lowercase',
|
id: 'lowercase',
|
||||||
label: this.$t('Lowercase'),
|
label: this.$t('Lowercase'),
|
||||||
type: 'switch'
|
type: 'checkbox'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'digit',
|
id: 'digit',
|
||||||
label: this.$t('Digit'),
|
label: this.$t('Digit'),
|
||||||
type: 'switch'
|
type: 'checkbox'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'symbol',
|
id: 'symbol',
|
||||||
label: this.$t('SpecialSymbol'),
|
label: this.$t('SpecialSymbol'),
|
||||||
type: 'switch'
|
type: 'checkbox'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'exclude_symbols',
|
id: 'exclude_symbols',
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="update-token">
|
||||||
<el-button v-show="!isShow" icon="el-icon-edit" type="text" @click="isShow=true">
|
<el-button v-show="!isShow" icon="el-icon-edit" type="text" @click="isShow=true">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-input
|
<el-input
|
||||||
v-show="isShow"
|
v-show="isShow"
|
||||||
v-model.trim="curValue"
|
v-model.trim="curValue"
|
||||||
|
:disabled="disabled"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:type="type"
|
:type="type"
|
||||||
autocomplete="new-password"
|
class="password-input"
|
||||||
show-password
|
show-password
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
|
<el-button :disabled="disabled" size="small" type="text" @click="randomPassword">
|
||||||
|
<i class="fa fa-refresh" />
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { randomString } from '@/utils/string'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
@ -39,6 +45,10 @@ export default {
|
|||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => ''
|
default: () => ''
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -55,7 +65,24 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
onChange(e) {
|
onChange(e) {
|
||||||
this.$emit('input', this.curValue)
|
this.$emit('input', this.curValue)
|
||||||
|
},
|
||||||
|
randomPassword() {
|
||||||
|
this.curValue = randomString(24, true)
|
||||||
|
this.$emit('input', this.curValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.password-input {
|
||||||
|
width: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-token {
|
||||||
|
i {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<div v-if="tip !== ''" class="help-block">{{ tip }}</div>
|
<div v-if="tip !== ''" class="help-block">{{ tip }}</div>
|
||||||
<input v-model="value" hidden type="text" v-on="$listeners">
|
<input v-model="value" hidden type="text" v-on="$listeners">
|
||||||
<div>
|
<div>
|
||||||
<img :class="showBG ? 'show-bg' : ''" :src="preview" v-bind="$attrs">
|
<img v-if="preview" :class="showBG ? 'show-bg' : ''" :src="preview" v-bind="$attrs" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-weektime">
|
<div class="c-weektime">
|
||||||
<div class="c-schedue" />
|
<div class="c-schedue" />
|
||||||
<div :class="{'c-schedue': true, 'c-schedue-notransi': mode}" :style="styleValue" />
|
<div :class="{'c-schedue': true, 'c-schedue-notransi': mode}" />
|
||||||
<table :class="{'c-min-table': colspan < 2}" class="c-weektime-table">
|
<table :class="{'c-min-table': colspan < 2}" class="c-weektime-table">
|
||||||
<thead class="c-weektime-head">
|
<thead class="c-weektime-head">
|
||||||
<tr>
|
<tr>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="c-weektime-body" @mouseleave="containerLeave()">
|
<tbody class="c-weektime-body" @mouseleave="containerLeave()">
|
||||||
<tr v-for="t in weektimeData" :key="t.row">
|
<tr v-for="t in weekTimeData" :key="t.row">
|
||||||
<td>{{ t.value }}</td>
|
<td>{{ t.value }}</td>
|
||||||
<td
|
<td
|
||||||
v-for="n in t.child"
|
v-for="n in t.child"
|
||||||
@ -45,6 +45,7 @@
|
|||||||
const createArr = len => {
|
const createArr = len => {
|
||||||
return Array.from(Array(len)).map((ret, id) => id)
|
return Array.from(Array(len)).map((ret, id) => id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function splicing(list) {
|
function splicing(list) {
|
||||||
let same
|
let same
|
||||||
let i = -1
|
let i = -1
|
||||||
@ -67,6 +68,7 @@ function splicing(list) {
|
|||||||
arr.shift()
|
arr.shift()
|
||||||
return arr.join('')
|
return arr.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WeekCronSelect',
|
name: 'WeekCronSelect',
|
||||||
props: {
|
props: {
|
||||||
@ -77,7 +79,7 @@ export default {
|
|||||||
colspan: {
|
colspan: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default() {
|
default() {
|
||||||
return 2
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -100,7 +102,7 @@ export default {
|
|||||||
this.$t('Saturday'),
|
this.$t('Saturday'),
|
||||||
this.$t('Sunday')
|
this.$t('Sunday')
|
||||||
],
|
],
|
||||||
weektimeData: [],
|
weekTimeData: [],
|
||||||
timeRange: [] // 格式化之后数据
|
timeRange: [] // 格式化之后数据
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -142,10 +144,11 @@ export default {
|
|||||||
return {
|
return {
|
||||||
value: ret,
|
value: ret,
|
||||||
row: index,
|
row: index,
|
||||||
child: children(ret, index, 48)
|
child: children(ret, index, 24 * this.colspan)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.weektimeData = isData
|
console.log('isData', isData)
|
||||||
|
this.weekTimeData = isData
|
||||||
},
|
},
|
||||||
// 反解析传递过来的默认值
|
// 反解析传递过来的默认值
|
||||||
nextValue() {
|
nextValue() {
|
||||||
@ -169,7 +172,7 @@ export default {
|
|||||||
const startVal = this.countIndex(start)
|
const startVal = this.countIndex(start)
|
||||||
const endVal = this.countIndex(end)
|
const endVal = this.countIndex(end)
|
||||||
for (let i = startVal; i < (endVal === 0 ? 48 : endVal); i++) {
|
for (let i = startVal; i < (endVal === 0 ? 48 : endVal); i++) {
|
||||||
const curWeek = this.weektimeData[idNum]
|
const curWeek = this.weekTimeData[idNum]
|
||||||
curWeek.child[i].check = true
|
curWeek.child[i].check = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -209,8 +212,9 @@ export default {
|
|||||||
const nowDate = new Date(timeStamp).getTime()
|
const nowDate = new Date(timeStamp).getTime()
|
||||||
const targetStamp = new Date(nowDate + offsetGMT * 60 * 1000 + timezone * 60 * 60 * 1000).getTime()
|
const targetStamp = new Date(nowDate + offsetGMT * 60 * 1000 + timezone * 60 * 60 * 1000).getTime()
|
||||||
|
|
||||||
const beginStamp = targetStamp + col * 1800000 // col * 30 * 60 * 1000
|
// (2 / this.colspan) 原来是一个单元格 30分钟,现在是一个单元格 30 * 2 / this.colspan 分钟
|
||||||
const endStamp = beginStamp + 1800000
|
const beginStamp = targetStamp + col * 1800000 * (2 / this.colspan) // col * 30 * 60 * 1000
|
||||||
|
const endStamp = beginStamp + 1800000 * (2 / this.colspan)
|
||||||
|
|
||||||
const begin = this.formatDate(new Date(beginStamp), 'hh:mm')
|
const begin = this.formatDate(new Date(beginStamp), 'hh:mm')
|
||||||
const end = this.formatDate(new Date(endStamp), 'hh:mm')
|
const end = this.formatDate(new Date(endStamp), 'hh:mm')
|
||||||
@ -218,7 +222,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 清空时间段
|
// 清空时间段
|
||||||
clearWeektime() {
|
clearWeektime() {
|
||||||
this.weektimeData.forEach(item => {
|
this.weekTimeData.forEach(item => {
|
||||||
item.child.forEach(t => {
|
item.child.forEach(t => {
|
||||||
this.$set(t, 'check', false)
|
this.$set(t, 'check', false)
|
||||||
})
|
})
|
||||||
@ -228,7 +232,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 全选
|
// 全选
|
||||||
selectAll() {
|
selectAll() {
|
||||||
this.weektimeData.forEach(item => {
|
this.weekTimeData.forEach(item => {
|
||||||
item.child.forEach(t => {
|
item.child.forEach(t => {
|
||||||
this.$set(t, 'check', true)
|
this.$set(t, 'check', true)
|
||||||
})
|
})
|
||||||
@ -241,12 +245,15 @@ export default {
|
|||||||
this.mode = 0
|
this.mode = 0
|
||||||
},
|
},
|
||||||
setTimeRange() {
|
setTimeRange() {
|
||||||
this.timeRange = this.weektimeData.map(item => {
|
this.timeRange = this.weekTimeData.map(item => {
|
||||||
|
console.log('item', item)
|
||||||
|
console.log('Value', splicing(item.child))
|
||||||
return {
|
return {
|
||||||
id: item.row === 6 ? 0 : item.row + 1,
|
id: item.row === 6 ? 0 : item.row + 1,
|
||||||
value: splicing(item.child)
|
value: splicing(item.child)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
console.log('Time range: ', this.timeRange)
|
||||||
this.$emit('change', this.timeRange)
|
this.$emit('change', this.timeRange)
|
||||||
},
|
},
|
||||||
cellEnter(item) {
|
cellEnter(item) {
|
||||||
@ -308,7 +315,7 @@ export default {
|
|||||||
selectWeek(row, col, check) {
|
selectWeek(row, col, check) {
|
||||||
const [minRow, maxRow] = row
|
const [minRow, maxRow] = row
|
||||||
const [minCol, maxCol] = col
|
const [minCol, maxCol] = col
|
||||||
this.weektimeData.forEach(item => {
|
this.weekTimeData.forEach(item => {
|
||||||
item.child.forEach(t => {
|
item.child.forEach(t => {
|
||||||
if (t.row >= minRow && t.row <= maxRow && t.col >= minCol && t.col <= maxCol) {
|
if (t.row >= minRow && t.row <= maxRow && t.col >= minCol && t.col <= maxCol) {
|
||||||
this.$set(t, 'check', check)
|
this.$set(t, 'check', check)
|
||||||
@ -321,11 +328,12 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.c-weektime {
|
.c-weektime {
|
||||||
min-width: 640px;
|
//min-width: 440px;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-schedue {
|
.c-schedue {
|
||||||
background: #598fe6;
|
background: #598fe6;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -334,55 +342,70 @@ export default {
|
|||||||
opacity: .6;
|
opacity: .6;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-schedue-notransi {
|
.c-schedue-notransi {
|
||||||
transition: width .12s ease, height .12s ease, top .12s ease, left .12s ease;
|
transition: width .12s ease, height .12s ease, top .12s ease, left .12s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-weektime-table {
|
.c-weektime-table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
||||||
th {
|
th {
|
||||||
vertical-align: inherit;
|
vertical-align: inherit;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr, td, th {
|
tr, td, th {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border: 1px solid #dee4f5;
|
border: 1px solid #dee4f5;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
min-width: 12px;
|
min-width: 10px;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
transition: background .16s ease;
|
transition: background .16s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-weektime-head {
|
.c-weektime-head {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
.week-td {
|
.week-td {
|
||||||
width: 72px;
|
width: 72px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-weektime-body {
|
.c-weektime-body {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
td {
|
td {
|
||||||
&.weektime-atom-item {
|
&.weektime-atom-item {
|
||||||
user-select: unset;
|
user-select: unset;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ui-selected {
|
&.ui-selected {
|
||||||
background-color: #598fe6;
|
background-color: #598fe6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-weektime-preview {
|
.c-weektime-preview {
|
||||||
line-height: 2.4em;
|
line-height: 2.4em;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
font-size: 13px;
|
font-size: 11px;
|
||||||
|
|
||||||
.c-weektime-con {
|
.c-weektime-con {
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-weektime-time {
|
.c-weektime-time {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 2.4em;
|
line-height: 2.4em;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
max-width: 625px;
|
max-width: 625px;
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
@ -392,11 +415,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-min-table {
|
.c-min-table {
|
||||||
tr, td, th {
|
tr, td, th {
|
||||||
min-width: 24px;
|
min-width: 17px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-clearfix {
|
.g-clearfix {
|
||||||
&:after, &:before {
|
&:after, &:before {
|
||||||
clear: both;
|
clear: both;
|
||||||
@ -404,16 +429,20 @@ export default {
|
|||||||
display: table;
|
display: table;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-pull-left {
|
.g-pull-left {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-pull-right {
|
.g-pull-right {
|
||||||
float: right;
|
float: right;
|
||||||
color: #409eff!important;
|
color: #409eff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-pull-margin {
|
.g-pull-margin {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-tip-text {
|
.g-tip-text {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="form-group-header">
|
<div ref="formGroup" class="form-group-header">
|
||||||
<div v-if="line" class="hr-line-dashed" />
|
<div v-if="line" class="hr-line-dashed" />
|
||||||
<h3>{{ group['title'] }} </h3>
|
<h3 @click="toggle">{{ group['title'] }} </h3>
|
||||||
|
<span class="compass" @click="toggle">
|
||||||
|
<i :class="iconClass" />
|
||||||
|
</span>
|
||||||
|
<div v-if="!isVisible" class="ellipsis" @click="toggle">
|
||||||
|
<i class="el-icon-more-outline" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -20,16 +26,62 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isVisible: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iconClass() {
|
||||||
|
return this.isVisible ? 'el-icon-arrow-down' : 'el-icon-arrow-up'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.isVisible = !this.isVisible
|
||||||
|
this.toggleSiblingVisibility()
|
||||||
|
},
|
||||||
|
toggleSiblingVisibility() {
|
||||||
|
// 当前 form-group-header 的 DOM 元素
|
||||||
|
const formGroupHeader = this.$refs.formGroup
|
||||||
|
if (!formGroupHeader) return
|
||||||
|
|
||||||
|
// 找到当前 form-group-header 的下一个兄弟节点
|
||||||
|
let sibling = formGroupHeader.nextElementSibling
|
||||||
|
|
||||||
|
// 循环隐藏或显示直到找到下一个 form-group-header
|
||||||
|
while (sibling && sibling.classList.contains('el-form-item')) {
|
||||||
|
sibling.style.display = this.isVisible ? '' : 'none'
|
||||||
|
sibling = sibling.nextElementSibling
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.hr-line-dashed {
|
.hr-line-dashed {
|
||||||
border-top: 1px dashed #e7eaec;
|
border-top: 1px dashed #e7eaec;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compass {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card :class="'ibox ' + type" shadow="never" v-bind="$attrs">
|
<el-card :class="'ibox ' + type" :shadow="shadow" v-bind="$attrs">
|
||||||
<template #header>
|
<template #header>
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<div v-if="title" slot="header" class="clearfix ibox-title">
|
<div v-if="title" slot="header" class="clearfix ibox-title">
|
||||||
@ -26,6 +26,10 @@ export default {
|
|||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'default'
|
default: 'default'
|
||||||
|
},
|
||||||
|
shadow: {
|
||||||
|
type: String,
|
||||||
|
default: 'never'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -39,46 +39,32 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
html:lang(en) .quick-actions ::v-deep button {
|
.quick-actions ::v-deep table {
|
||||||
width: 80px;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
html:lang(ja) .quick-actions ::v-deep button {
|
.quick-actions ::v-deep tr > td {
|
||||||
width: 100px;
|
line-height: 1.43;
|
||||||
}
|
padding: 8px 0;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: 13px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
html:lang(zh-tw) .quick-actions ::v-deep button {
|
.quick-actions ::v-deep tr > td > span:last-child {
|
||||||
width: 65px;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
html:lang(zh-cn) .quick-actions ::v-deep button {
|
.quick-actions ::v-deep button {
|
||||||
width: 65px;
|
padding: 4px 5px;
|
||||||
}
|
font-size: 13px;
|
||||||
|
min-width: 65px;
|
||||||
|
|
||||||
.quick-actions ::v-deep table {
|
span {
|
||||||
width: 100%;
|
overflow: hidden;
|
||||||
}
|
white-space: nowrap; /* 控制文本不换行 */
|
||||||
|
text-overflow: ellipsis;
|
||||||
.quick-actions ::v-deep tr > td {
|
display: block;
|
||||||
line-height: 1.43;
|
|
||||||
padding: 8px;
|
|
||||||
vertical-align: top;
|
|
||||||
font-size: 13px;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-actions ::v-deep tr > td > span:last-child {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-actions ::v-deep button {
|
|
||||||
padding: 4px 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -197,7 +197,7 @@ export default {
|
|||||||
break
|
break
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
col.formatter = DateFormatter
|
col.formatter = DateFormatter
|
||||||
col.width = '175px'
|
col.width = '155px'
|
||||||
break
|
break
|
||||||
case 'object_related_field':
|
case 'object_related_field':
|
||||||
col.formatter = ObjectRelatedFormatter
|
col.formatter = ObjectRelatedFormatter
|
||||||
@ -358,6 +358,8 @@ export default {
|
|||||||
|
|
||||||
let configColumns = config.columns || allColumnNames
|
let configColumns = config.columns || allColumnNames
|
||||||
const columnsExclude = config.columnsExclude || []
|
const columnsExclude = config.columnsExclude || []
|
||||||
|
const columnsAdd = config.columnsAdd || []
|
||||||
|
configColumns = configColumns.concat(columnsAdd)
|
||||||
configColumns = configColumns.filter(item => !columnsExclude.includes(item))
|
configColumns = configColumns.filter(item => !columnsExclude.includes(item))
|
||||||
|
|
||||||
// 解决后端 API 返回字段中包含 actions 的问题;
|
// 解决后端 API 返回字段中包含 actions 的问题;
|
||||||
|
178
src/components/Table/CardTable/DataCardTable/CardPanel.vue
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<template>
|
||||||
|
<div class="account-panel">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="21">
|
||||||
|
<div class="title">
|
||||||
|
<span>{{ object.name }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col v-if="iActions.length !== 0" :span="3" @click.native="handleClick($event)">
|
||||||
|
<el-dropdown>
|
||||||
|
<el-link :underline="false" type="primary">
|
||||||
|
<i class="el-icon-more el-icon--right" style="color: var(--color-text-primary)" />
|
||||||
|
</el-link>
|
||||||
|
<el-dropdown-menu default="dropdown">
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="action in iActions"
|
||||||
|
:key="action.name"
|
||||||
|
:disabled="action.disabled"
|
||||||
|
@click.native="action.callback(object)"
|
||||||
|
>
|
||||||
|
<i v-if="action.icon" :class="action.icon" /> {{ action.name }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20" class="panel-content">
|
||||||
|
<el-col :span="6" class="panel-image">
|
||||||
|
<el-image :src="imageUrl" fit="contain" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="18" class="panel-info">
|
||||||
|
<InfoPanel
|
||||||
|
v-for="(obj, index) in getInfos(object)"
|
||||||
|
:key="index"
|
||||||
|
:content="obj.content"
|
||||||
|
:title="obj.title"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import InfoPanel from './InfoPanel'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CardPanel',
|
||||||
|
components: {
|
||||||
|
InfoPanel
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
object: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
infos: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
getImage: {
|
||||||
|
type: Function,
|
||||||
|
default: (obj) => ''
|
||||||
|
},
|
||||||
|
getInfos: {
|
||||||
|
type: Function,
|
||||||
|
default: (obj) => []
|
||||||
|
},
|
||||||
|
handleUpdate: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
defaultActions: [
|
||||||
|
{
|
||||||
|
id: 'update',
|
||||||
|
name: this.$tc('Update'),
|
||||||
|
icon: 'el-icon-edit',
|
||||||
|
callback: this.handleUpdate,
|
||||||
|
disabled: this.isDisabled('change')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'delete',
|
||||||
|
name: this.$tc('Delete'),
|
||||||
|
icon: 'el-icon-delete',
|
||||||
|
callback: this.handleDelete,
|
||||||
|
disabled: this.isDisabled('delete')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
imageUrl() {
|
||||||
|
return this.getImage(this.object)
|
||||||
|
},
|
||||||
|
iActions() {
|
||||||
|
const mergedActions = new Map()
|
||||||
|
this.defaultActions.forEach(a => {
|
||||||
|
mergedActions.set(a.id, { ...a })
|
||||||
|
})
|
||||||
|
this.actions.forEach(a => {
|
||||||
|
mergedActions.set(a.id, { ...a })
|
||||||
|
})
|
||||||
|
return Array.from(mergedActions.values())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isDisabled(action) {
|
||||||
|
const app = this.tableConfig.permissions?.app
|
||||||
|
const resource = this.tableConfig.permissions?.resource
|
||||||
|
return !this.$hasPerm(`${app}.${action}_${resource}`)
|
||||||
|
},
|
||||||
|
handleClick(event) {
|
||||||
|
event.stopPropagation()
|
||||||
|
},
|
||||||
|
handleDelete() {
|
||||||
|
const url = this.tableConfig.url
|
||||||
|
this.$confirm(this.$tc('DeleteConfirmMessage'), this.$tc('Delete'), {
|
||||||
|
confirmButtonText: this.$tc('Confirm'),
|
||||||
|
cancelButtonText: this.$tc('Cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.$axios.delete(`${url}${this.object.id}/`).then(() => {
|
||||||
|
this.$message({
|
||||||
|
type: 'success',
|
||||||
|
message: this.$tc('DeleteSuccess')
|
||||||
|
})
|
||||||
|
this.$emit('refresh')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.account-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
//height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100px;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.panel-image {
|
||||||
|
margin: auto 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,9 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="panel-item">
|
<div class="panel-item">
|
||||||
<span class="item-label">{{ title }} </span>
|
<span class="item-label">{{ title }} </span>
|
||||||
<el-link :underline="false" class="item-value">
|
<span :title="content" class="text-info">{{ content || '' }}</span>
|
||||||
<span class="content">{{ content }}</span>
|
|
||||||
</el-link>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -38,14 +36,22 @@ export default {
|
|||||||
|
|
||||||
.panel-item {
|
.panel-item {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 5px 0;
|
padding: 3px 0;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
@include textOverflow;
|
|
||||||
|
|
||||||
.item-label {
|
.item-label {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100px;
|
width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label::after {
|
||||||
|
content: ':';
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-info {
|
||||||
|
@include textOverflow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
src/components/Table/CardTable/DataCardTable/index.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<CardTable
|
||||||
|
ref="table"
|
||||||
|
:columns="3"
|
||||||
|
:table-config="tableConfig"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template v-slot:default="slotProps">
|
||||||
|
<CardPanel :object="slotProps.item" :table-config="tableConfig" v-bind="subComponentProps" />
|
||||||
|
</template>
|
||||||
|
</CardTable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script type="text/jsx">
|
||||||
|
import CardTable from '@/components/Table/CardTable/index.vue'
|
||||||
|
import CardPanel from './CardPanel.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SmallCard',
|
||||||
|
components: {
|
||||||
|
CardPanel,
|
||||||
|
CardTable
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
subComponentProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reloadTable() {
|
||||||
|
this.$refs.table.reloadTable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -10,7 +10,7 @@
|
|||||||
<IBox v-if="totalData.length === 0">
|
<IBox v-if="totalData.length === 0">
|
||||||
<el-empty :description="$t('NoData')" :image-size="200" class="no-data" style="padding: 20px" />
|
<el-empty :description="$t('NoData')" :image-size="200" class="no-data" style="padding: 20px" />
|
||||||
</IBox>
|
</IBox>
|
||||||
<el-col v-for="(d, index) in totalData" :key="index" :lg="8" :md="12" :sm="24" style="min-width: 335px;">
|
<el-col v-for="(d, index) in totalData" :key="index" :lg="8" :md="12" :sm="24" class="el-col">
|
||||||
<el-card
|
<el-card
|
||||||
:body-style="{ 'text-align': 'center', 'padding': '15px' }"
|
:body-style="{ 'text-align': 'center', 'padding': '15px' }"
|
||||||
:class="{'is-disabled': isDisabled(d)}"
|
:class="{'is-disabled': isDisabled(d)}"
|
||||||
@ -19,8 +19,7 @@
|
|||||||
@click.native="onView(d)"
|
@click.native="onView(d)"
|
||||||
>
|
>
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="subComponent" v-if="subComponent" :object="d" @refresh="getList" />
|
<slot :index="index" :item="d">
|
||||||
<slot v-else :index="index" :item="d">
|
|
||||||
<span v-if="d.edition === 'enterprise'" class="enterprise">
|
<span v-if="d.edition === 'enterprise'" class="enterprise">
|
||||||
{{ $t('Enterprise') }}
|
{{ $t('Enterprise') }}
|
||||||
</span>
|
</span>
|
||||||
@ -85,6 +84,10 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
// 定义 table 的配置
|
// 定义 table 的配置
|
||||||
|
columns: {
|
||||||
|
type: Number,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
@ -100,6 +103,10 @@ export default {
|
|||||||
subComponent: {
|
subComponent: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => null
|
default: () => null
|
||||||
|
},
|
||||||
|
subComponentProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -371,6 +378,10 @@ export default {
|
|||||||
border-top: 1px solid #e7eaec;
|
border-top: 1px solid #e7eaec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-col {
|
||||||
|
//min-width: 330px; 设置完后,remote app 列表会有问题
|
||||||
|
}
|
||||||
|
|
||||||
.no-data {
|
.no-data {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -105,14 +105,14 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<span :title="col.label">{{ col.label }}</span>
|
<span :title="col.label">{{ col.label }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="col.formatter && typeof col.formatter !== 'function'" v-slot:default="{row, column, index}">
|
<template v-if="col.formatter && typeof col.formatter !== 'function'" v-slot:default="{row, column, $index}">
|
||||||
<div
|
<div
|
||||||
:is="col.formatter"
|
:is="col.formatter"
|
||||||
:key="row.id"
|
:key="row.id"
|
||||||
:cell-value="row[col.prop]"
|
:cell-value="row[col.prop]"
|
||||||
:col="col"
|
:col="col"
|
||||||
:column="column"
|
:column="column"
|
||||||
:index="index"
|
:index="$index"
|
||||||
:reload="getList"
|
:reload="getList"
|
||||||
:row="row"
|
:row="row"
|
||||||
:table-data="data"
|
:table-data="data"
|
||||||
|
79
src/components/Table/DrawerListTable/PageDrawer.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<Drawer
|
||||||
|
:component="component"
|
||||||
|
:component-listeners="listener"
|
||||||
|
:size="drawerSize"
|
||||||
|
:title="title"
|
||||||
|
:visible.sync="iVisible"
|
||||||
|
append-to-body
|
||||||
|
class="form-drawer"
|
||||||
|
destroy-on-close
|
||||||
|
v-bind="props"
|
||||||
|
@close="closeDrawer"
|
||||||
|
v-on="$listeners"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Drawer from '@/components/Drawer/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Drawer },
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
component: {
|
||||||
|
type: [String, Function],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
listener: {
|
||||||
|
...this.$listeners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
drawerSize() {
|
||||||
|
const drawerWidth = localStorage.getItem('drawerWidth')
|
||||||
|
if (drawerWidth && drawerWidth > 100 && drawerWidth < 2000) {
|
||||||
|
return drawerWidth + 'px'
|
||||||
|
}
|
||||||
|
const width = window.innerWidth
|
||||||
|
if (width >= 800) return '767px'
|
||||||
|
return '90%'
|
||||||
|
},
|
||||||
|
iVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:visible', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeDrawer() {
|
||||||
|
this.iVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
235
src/components/Table/DrawerListTable/index.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ListTable
|
||||||
|
ref="ListTable"
|
||||||
|
:header-actions="iHeaderActions"
|
||||||
|
:table-config="iTableConfig"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
|
<PageDrawer
|
||||||
|
:action="action"
|
||||||
|
:class="[action]"
|
||||||
|
:component="drawerComponent"
|
||||||
|
:props="drawerProps"
|
||||||
|
:title="drawerTitle"
|
||||||
|
:visible.sync="drawerVisible"
|
||||||
|
class="page-drawer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ListTable from '../ListTable'
|
||||||
|
import PageDrawer from './PageDrawer.vue'
|
||||||
|
import { setUrlParam, toLowerCaseExcludeAbbr, toSentenceCase } from '@/utils/common'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
const drawerType = [String, Function]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GenericListPage',
|
||||||
|
components: {
|
||||||
|
ListTable, PageDrawer
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
detailDrawer: {
|
||||||
|
type: drawerType,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
createDrawer: {
|
||||||
|
type: drawerType,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
updateDrawer: {
|
||||||
|
type: drawerType,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
tableConfig: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
drawerProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
reloadOrderQuery: {
|
||||||
|
type: String,
|
||||||
|
default: '-date_updated'
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
getDrawerTitle: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
title: '',
|
||||||
|
action: '',
|
||||||
|
visible: false,
|
||||||
|
drawerVisible: false,
|
||||||
|
drawerComponent: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['inDrawer']),
|
||||||
|
drawerTitle() {
|
||||||
|
return this.getDefaultTitle()
|
||||||
|
},
|
||||||
|
iHeaderActions() {
|
||||||
|
const actions = this.headerActions
|
||||||
|
if (!actions.onCreate) {
|
||||||
|
actions.onCreate = this.onCreate
|
||||||
|
}
|
||||||
|
return actions
|
||||||
|
},
|
||||||
|
iTableConfig() {
|
||||||
|
const config = {
|
||||||
|
...this.tableConfig
|
||||||
|
}
|
||||||
|
const actionMap = {
|
||||||
|
'columnsMeta.actions.formatterArgs.onUpdate': this.onUpdate,
|
||||||
|
'columnsMeta.actions.formatterArgs.onClone': this.onClone,
|
||||||
|
'columnsMeta.name.formatterArgs.drawer': true,
|
||||||
|
'columnsMeta.name.formatterArgs.drawerComponent': this.detailDrawer
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(actionMap)) {
|
||||||
|
if (_.get(config, key)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_.set(config, key, value)
|
||||||
|
}
|
||||||
|
const columnsMeta = config.columnsMeta
|
||||||
|
for (const value of Object.values(columnsMeta)) {
|
||||||
|
if (
|
||||||
|
value.formatter && value.formatter.name === 'AmountFormatter' &&
|
||||||
|
value.formatterArgs && !value.formatterArgs.drawer
|
||||||
|
) {
|
||||||
|
value.formatterArgs.drawer = this.detailDrawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
inDrawer(val) {
|
||||||
|
if (!this.drawerVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!val) {
|
||||||
|
this.drawerVisible = false
|
||||||
|
this.reloadTable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDefaultTitle() {
|
||||||
|
let title = this.title
|
||||||
|
if (!title && this.resource) {
|
||||||
|
title = this.resource
|
||||||
|
}
|
||||||
|
if (!title) {
|
||||||
|
title = this.$route.meta?.title
|
||||||
|
title = title.replace('List', '').replace('列表', '')
|
||||||
|
title = _.trimEnd(title, 's')
|
||||||
|
}
|
||||||
|
if (!title) {
|
||||||
|
title = this.$t('NoTitle')
|
||||||
|
}
|
||||||
|
let action = this.action
|
||||||
|
if (action === 'clone') {
|
||||||
|
action = 'create'
|
||||||
|
}
|
||||||
|
title = toSentenceCase(action) + this.$t('WordSep') + toLowerCaseExcludeAbbr(title)
|
||||||
|
return title
|
||||||
|
},
|
||||||
|
getDefaultDrawer(action) {
|
||||||
|
const route = this.$route.name
|
||||||
|
const actionRouteName = route.replace('List', toSentenceCase(action))
|
||||||
|
return this.getRouteNameComponent(actionRouteName, action)
|
||||||
|
},
|
||||||
|
getRouteNameComponent(name, action) {
|
||||||
|
const route = { name: name }
|
||||||
|
if (action === 'detail' || action === 'update') {
|
||||||
|
route.params = { id: '1' }
|
||||||
|
}
|
||||||
|
const routes = this.$router.resolve(route)
|
||||||
|
if (!routes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const matched = routes.resolved.matched.filter(item => item.name === name && item.components)
|
||||||
|
if (matched.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched[0] && matched[0].components?.default) {
|
||||||
|
const component = matched[0].components.default
|
||||||
|
return component
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async showDrawer(action) {
|
||||||
|
this.action = action
|
||||||
|
if (action === 'create') {
|
||||||
|
this.drawerComponent = this.createDrawer
|
||||||
|
} else if (action === 'update') {
|
||||||
|
this.drawerComponent = this.updateDrawer || this.createDrawer
|
||||||
|
} else if (action === 'detail') {
|
||||||
|
this.drawerComponent = this.detailDrawer
|
||||||
|
} else if (action === 'clone') {
|
||||||
|
this.drawerComponent = this.createDrawer || this.getDefaultDrawer('create')
|
||||||
|
} else {
|
||||||
|
this.drawerComponent = this.createDrawer
|
||||||
|
}
|
||||||
|
if (!this.drawerComponent) {
|
||||||
|
this.drawerComponent = this.getDefaultDrawer(action)
|
||||||
|
}
|
||||||
|
if (this.getDrawerTitle) {
|
||||||
|
const actionMeta = await this.$store.getters['common/drawerActionMeta']
|
||||||
|
this.title = this.getDrawerTitle({ action, ...actionMeta })
|
||||||
|
}
|
||||||
|
this.drawerVisible = true
|
||||||
|
},
|
||||||
|
onCreate(meta) {
|
||||||
|
if (!meta) {
|
||||||
|
meta = {}
|
||||||
|
}
|
||||||
|
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||||
|
action: 'create', ...meta
|
||||||
|
}).then(() => {
|
||||||
|
this.showDrawer('create')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reloadTable() {
|
||||||
|
if (this.reloadOrderQuery) {
|
||||||
|
this.iTableConfig.url = setUrlParam(this.iTableConfig.url, 'order', this.reloadOrderQuery)
|
||||||
|
}
|
||||||
|
this.$refs.ListTable.reloadTable()
|
||||||
|
},
|
||||||
|
onClone({ row, col }) {
|
||||||
|
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||||
|
action: 'clone', row: row, col: col, id: row.id
|
||||||
|
}).then(() => {
|
||||||
|
this.showDrawer('clone')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onUpdate({ row, col }) {
|
||||||
|
this.$route.params.id = row.id
|
||||||
|
this.$store.dispatch('common/setDrawerActionMeta', {
|
||||||
|
action: 'update', row: row, col: col, id: row.id
|
||||||
|
}).then(() => {
|
||||||
|
this.showDrawer('update')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -8,14 +8,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { cleanActions } from './utils'
|
||||||
|
import { createSourceIdCache } from '@/api/common'
|
||||||
|
import { getErrorResponseMsg } from '@/utils/common'
|
||||||
|
|
||||||
import i18n from '@/i18n/i18n'
|
import i18n from '@/i18n/i18n'
|
||||||
import DataActions from '@/components/DataActions/index.vue'
|
import DataActions from '@/components/DataActions/index.vue'
|
||||||
import { createSourceIdCache } from '@/api/common'
|
|
||||||
import { cleanActions } from './utils'
|
|
||||||
import { getErrorResponseMsg } from '@/utils/common'
|
|
||||||
|
|
||||||
const defaultTrue = { type: [Boolean, Function, String], default: true }
|
const defaultTrue = { type: [Boolean, Function, String], default: true }
|
||||||
const defaultFalse = { type: [Boolean, Function, String], default: false }
|
const defaultFalse = { type: [Boolean, Function, String], default: false }
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LeftSide',
|
name: 'LeftSide',
|
||||||
components: {
|
components: {
|
||||||
@ -31,6 +33,10 @@ export default {
|
|||||||
return this.$route.name?.replace('List', 'Create')
|
return this.$route.name?.replace('List', 'Create')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeCreate: {
|
||||||
|
type: Function,
|
||||||
|
default: () => null
|
||||||
|
},
|
||||||
onCreate: {
|
onCreate: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null
|
default: null
|
||||||
@ -75,6 +81,10 @@ export default {
|
|||||||
default: () => ([])
|
default: () => ([])
|
||||||
},
|
},
|
||||||
moreActionsTitle: {
|
moreActionsTitle: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
moreActionsType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
@ -95,7 +105,7 @@ export default {
|
|||||||
title: this.$t('DeleteSelected'),
|
title: this.$t('DeleteSelected'),
|
||||||
name: 'actionDeleteSelected',
|
name: 'actionDeleteSelected',
|
||||||
has: this.hasBulkDelete,
|
has: this.hasBulkDelete,
|
||||||
icon: 'fa fa-trash-o',
|
icon: 'trash',
|
||||||
can({ selectedRows }) {
|
can({ selectedRows }) {
|
||||||
return selectedRows.length > 0 && vm.canBulkDelete
|
return selectedRows.length > 0 && vm.canBulkDelete
|
||||||
},
|
},
|
||||||
@ -128,7 +138,11 @@ export default {
|
|||||||
has: this.hasCreate && !this.moreCreates,
|
has: this.hasCreate && !this.moreCreates,
|
||||||
can: this.canCreate,
|
can: this.canCreate,
|
||||||
icon: 'plus',
|
icon: 'plus',
|
||||||
callback: this.onCreate || this.handleCreate
|
callback: () => {
|
||||||
|
this.beforeCreate()
|
||||||
|
const callback = this.onCreate || this.handleCreate
|
||||||
|
callback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
if (this.moreCreates) {
|
if (this.moreCreates) {
|
||||||
@ -140,7 +154,11 @@ export default {
|
|||||||
icon: 'plus',
|
icon: 'plus',
|
||||||
can: this.canCreate,
|
can: this.canCreate,
|
||||||
dropdown: [],
|
dropdown: [],
|
||||||
callback: this.onCreate || this.handleCreate
|
callback: () => {
|
||||||
|
this.beforeCreate()
|
||||||
|
const callback = this.onCreate || this.handleCreate
|
||||||
|
callback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const createCreateAction = Object.assign(defaultMoreCreate, this.moreCreates)
|
const createCreateAction = Object.assign(defaultMoreCreate, this.moreCreates)
|
||||||
defaultActions.push(createCreateAction)
|
defaultActions.push(createCreateAction)
|
||||||
@ -181,7 +199,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
name: 'moreActions',
|
name: 'moreActions',
|
||||||
title: this.moreActionsTitle || this.$t('MoreActions'),
|
title: this.moreActionsTitle || this.$t('MoreActions'),
|
||||||
dropdown: dropdown
|
dropdown: dropdown,
|
||||||
|
type: this.moreActionsType
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hasSelectedRows() {
|
hasSelectedRows() {
|
||||||
@ -194,6 +213,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
handleCreate() {
|
handleCreate() {
|
||||||
let route
|
let route
|
||||||
|
|
||||||
if (typeof this.createRoute === 'string') {
|
if (typeof this.createRoute === 'string') {
|
||||||
route = { name: this.createRoute }
|
route = { name: this.createRoute }
|
||||||
route.name = this.createRoute
|
route.name = this.createRoute
|
||||||
@ -202,7 +222,9 @@ export default {
|
|||||||
} else if (typeof this.createRoute === 'object') {
|
} else if (typeof this.createRoute === 'object') {
|
||||||
route = this.createRoute
|
route = this.createRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$log.debug('handle create')
|
this.$log.debug('handle create')
|
||||||
|
|
||||||
if (this.createInNewPage) {
|
if (this.createInNewPage) {
|
||||||
const { href } = this.$router.resolve(route)
|
const { href } = this.$router.resolve(route)
|
||||||
window.open(href, '_blank')
|
window.open(href, '_blank')
|
||||||
@ -248,3 +270,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
|
303
src/components/Table/ListTable/TableAction/QuickFilter.vue
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<template>
|
||||||
|
<div v-show="isExpand">
|
||||||
|
<div v-if="filters || summary" :class="isExpand ? 'expand': 'shrink' " class="quick-filter">
|
||||||
|
<div v-show="isExpand" class="quick-filter-wrap">
|
||||||
|
<div v-if="filters" class="quick-filter-zone">
|
||||||
|
<div v-for="category in iFilters" :key="category.label" class="item-zone">
|
||||||
|
<div>
|
||||||
|
<h5>{{ category.label }}</h5>
|
||||||
|
<div class="filter-options">
|
||||||
|
<span
|
||||||
|
v-for="option in category.options"
|
||||||
|
:key="option.label"
|
||||||
|
:class="option.active ? 'active' : ''"
|
||||||
|
class="item"
|
||||||
|
@click="handleFilterClick(option)"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
<span v-if="option.hasCount">
|
||||||
|
(<span v-async="getCount(option)">-</span>)
|
||||||
|
</span>
|
||||||
|
<!-- <i class="el-icon-circle-check" />-->
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="summary" class="summary-zone">
|
||||||
|
<span v-for="item of iSummary" :key="item.title" class="summary-block">
|
||||||
|
<SummaryCard
|
||||||
|
:class="item.active ? 'active' : ''"
|
||||||
|
:count="getCount(item)"
|
||||||
|
:title="item.title"
|
||||||
|
@click="handleFilterClick(item)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="expand-bar-wrap">
|
||||||
|
<div class="expand-bar" @click="toggle">
|
||||||
|
<i :class="isExpand ? 'expand': 'shrink' " class="fa fa-angle-double-up" />
|
||||||
|
<span v-show="!isExpand"> 展开过滤器 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SummaryCard from '@/components/Cards/SummaryCard/index.vue'
|
||||||
|
import { setUrlParam } from '@/utils/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'QuickFilter',
|
||||||
|
components: { SummaryCard },
|
||||||
|
props: {
|
||||||
|
filters: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
tableUrl: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
iFilters: this.cleanFilters(),
|
||||||
|
iSummary: this.cleanSummary(),
|
||||||
|
filtered: {},
|
||||||
|
activeFilters: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isExpand: {
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:expand', val)
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.expand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getCount(item) {
|
||||||
|
if (item.count || item.count === 0) {
|
||||||
|
return item.count
|
||||||
|
}
|
||||||
|
if (!item.filter) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
let url = this.tableUrl
|
||||||
|
for (const [k, v] of Object.entries({ ...item.filter, limit: 1 })) {
|
||||||
|
url = setUrlParam(url, k, v)
|
||||||
|
}
|
||||||
|
const res = await this.$axios.get(url, { raw: 1 })
|
||||||
|
item.count = res.data.count
|
||||||
|
return item.count
|
||||||
|
},
|
||||||
|
cleanSummary() {
|
||||||
|
if (!this.summary) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return this.summary.map(item => {
|
||||||
|
return {
|
||||||
|
category: 'summary',
|
||||||
|
label: item.title,
|
||||||
|
...item,
|
||||||
|
filter: item.filter || {},
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cleanFilters() {
|
||||||
|
if (!this.filters) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return this.filters.map(category => {
|
||||||
|
return {
|
||||||
|
...category,
|
||||||
|
options: category.options.map(option => {
|
||||||
|
return {
|
||||||
|
category: category.label,
|
||||||
|
...option,
|
||||||
|
active: false,
|
||||||
|
filter: option.filter || {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
this.isExpand = !this.isExpand
|
||||||
|
},
|
||||||
|
handleFilterClick(option) {
|
||||||
|
if (!option.active) {
|
||||||
|
this.activeFilters = this.activeFilters.filter(item => {
|
||||||
|
const conflict = Object.keys(item.filter).some(key => {
|
||||||
|
return Object.keys(option.filter).includes(key)
|
||||||
|
})
|
||||||
|
if (conflict) {
|
||||||
|
item.active = false
|
||||||
|
}
|
||||||
|
return !conflict
|
||||||
|
})
|
||||||
|
this.activeFilters.push(option)
|
||||||
|
} else {
|
||||||
|
this.activeFilters = this.activeFilters.filter(item => {
|
||||||
|
return item.label !== option.label && item.category !== option.category
|
||||||
|
})
|
||||||
|
}
|
||||||
|
option.active = !option.active
|
||||||
|
this.filtered = this.activeFilters.reduce((acc, item) => {
|
||||||
|
return { ...acc, ...item.filter }
|
||||||
|
}, {})
|
||||||
|
this.$emit('filter', this.filtered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.quick-filter {
|
||||||
|
background: white;
|
||||||
|
padding: 10px 10px 10px 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
place-content: stretch flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: stretch;
|
||||||
|
box-shadow: 0 1px 1px 0 rgba(54, 58, 80, .32);
|
||||||
|
|
||||||
|
&.shrink {
|
||||||
|
background: inherit;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-filter-wrap {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 70px);
|
||||||
|
|
||||||
|
.summary-zone {
|
||||||
|
padding-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-block {
|
||||||
|
.active {
|
||||||
|
::v-deep .no-margins .num {
|
||||||
|
color: var(--color-primary);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "\e720";
|
||||||
|
font-family: element-icons !important;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-filter-zone {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap; /* 允许 item-zone 换行 */
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-zone {
|
||||||
|
margin-right: 30px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 1px; /* 分割线宽度 */
|
||||||
|
height: 8px; /* 分割线高度 */
|
||||||
|
background-color: var(--color-icon-primary); /* 分割线颜色 */
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
visibility: hidden;
|
||||||
|
margin-left: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
|
||||||
|
i {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none outside none;
|
||||||
|
margin-block-start: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-options {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-bar-wrap {
|
||||||
|
margin: auto 0;
|
||||||
|
min-width: 60px;
|
||||||
|
|
||||||
|
.expand-bar {
|
||||||
|
float: right;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
i {
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
&.shrink {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -98,11 +98,23 @@ export default {
|
|||||||
canBulkUpdate: {
|
canBulkUpdate: {
|
||||||
type: [Boolean, Function, String],
|
type: [Boolean, Function, String],
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
hasQuickFilter: defaultTrue,
|
||||||
|
quickFilterExpand: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
defaultRightSideActions: [
|
defaultRightSideActions: [
|
||||||
|
{
|
||||||
|
name: 'actionFilter',
|
||||||
|
icon: 'filter',
|
||||||
|
tip: this.$t('Filter'),
|
||||||
|
has: this.hasQuickFilter,
|
||||||
|
callback: this.handleFilterClick.bind(this)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'actionSetting',
|
name: 'actionSetting',
|
||||||
icon: 'system-setting',
|
icon: 'system-setting',
|
||||||
@ -170,6 +182,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleFilterClick() {
|
||||||
|
this.$emit('update:quick-filter-expand', !this.quickFilterExpand)
|
||||||
|
},
|
||||||
handleTagSearch(val) {
|
handleTagSearch(val) {
|
||||||
this.searchTable(val)
|
this.searchTable(val)
|
||||||
},
|
},
|
||||||
@ -196,7 +211,6 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-left: 10px;
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="device" class="table-header clearfix">
|
<div :class="device" class="table-header clearfix container">
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<LeftSide
|
<LeftSide
|
||||||
v-if="hasLeftActions"
|
v-if="hasLeftActions"
|
||||||
@ -10,6 +10,7 @@
|
|||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
@init-actions-done="handleActionsDone"
|
@init-actions-done="handleActionsDone"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RightSide
|
<RightSide
|
||||||
v-if="hasRightActions"
|
v-if="hasRightActions"
|
||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
@ -18,6 +19,7 @@
|
|||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div :class="searchClass" class="search">
|
<div :class="searchClass" class="search">
|
||||||
<LabelSearch
|
<LabelSearch
|
||||||
v-if="hasLabelSearch"
|
v-if="hasLabelSearch"
|
||||||
@ -158,7 +160,7 @@ $headerHeight: 30px;
|
|||||||
.table-header {
|
.table-header {
|
||||||
.left-side {
|
.left-side {
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
//float: left;
|
||||||
|
|
||||||
::v-deep .action-item.el-dropdown > .el-button {
|
::v-deep .action-item.el-dropdown > .el-button {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -166,13 +168,14 @@ $headerHeight: 30px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.right-side {
|
.right-side {
|
||||||
float: right;
|
//float: right;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
.right-side-item.action-search {
|
.right-side-item.action-search {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
@ -181,65 +184,60 @@ $headerHeight: 30px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search.left {
|
.search.left {
|
||||||
float: left;
|
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search.right {
|
.search.right {
|
||||||
float: right;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 0;
|
||||||
|
|
||||||
|
&.mobile {
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.left-side {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-side {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
order: 2;
|
||||||
|
flex-grow: 1; /* This allows it to grow and fill available space */
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-side {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When .left-side is not present, adjust the layout */
|
||||||
|
.container:not(:has(.left-side)) .search {
|
||||||
|
margin-right: auto; /* Pushes .search to the left */
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.export-item {
|
.export-item {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 5px 20px;
|
padding: 5px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile .search {
|
|
||||||
display: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .search .datepicker {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .search.right {
|
|
||||||
clear: both;
|
|
||||||
float: none;
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
.label-search {
|
|
||||||
margin-right: 0;
|
|
||||||
|
|
||||||
::v-deep .el-button.label-button {
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .label-cascader {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .search.right .action-search {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .right-side {
|
|
||||||
padding-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 481px) {
|
|
||||||
.mobile .right-side {
|
|
||||||
float: left;
|
|
||||||
margin-left: -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .left-side {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<QuickFilter
|
||||||
|
:expand.sync="filterExpand"
|
||||||
|
:filters="quickFilters"
|
||||||
|
:summary="quickSummary"
|
||||||
|
:table-url="tableUrl"
|
||||||
|
@filter="filter"
|
||||||
|
/>
|
||||||
<TableAction
|
<TableAction
|
||||||
v-if="hasActions"
|
v-if="hasActions"
|
||||||
|
:class="{'filter-expand': filterExpand}"
|
||||||
:date-pick="handleDateChange"
|
:date-pick="handleDateChange"
|
||||||
|
:has-quick-filter="iHasQuickFilter"
|
||||||
|
:quick-filter-expand.sync="filterExpand"
|
||||||
:reload-table="reloadTable"
|
:reload-table="reloadTable"
|
||||||
:search-table="search"
|
:search-table="search"
|
||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
@ -31,11 +41,13 @@ import IBox from '../../IBox/index.vue'
|
|||||||
import TableAction from './TableAction/index.vue'
|
import TableAction from './TableAction/index.vue'
|
||||||
import Emitter from '@/mixins/emitter'
|
import Emitter from '@/mixins/emitter'
|
||||||
import AutoDataTable from '../AutoDataTable/index.vue'
|
import AutoDataTable from '../AutoDataTable/index.vue'
|
||||||
|
import QuickFilter from './TableAction/QuickFilter.vue'
|
||||||
import { getDayEnd, getDaysAgo } from '@/utils/time'
|
import { getDayEnd, getDaysAgo } from '@/utils/time'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListTable',
|
name: 'ListTable',
|
||||||
components: {
|
components: {
|
||||||
|
QuickFilter,
|
||||||
AutoDataTable,
|
AutoDataTable,
|
||||||
TableAction,
|
TableAction,
|
||||||
IBox
|
IBox
|
||||||
@ -51,6 +63,14 @@ export default {
|
|||||||
headerActions: {
|
headerActions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
quickFilters: {
|
||||||
|
type: Array,
|
||||||
|
default: () => null
|
||||||
|
},
|
||||||
|
quickSummary: {
|
||||||
|
type: Array,
|
||||||
|
default: () => null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -79,13 +99,20 @@ export default {
|
|||||||
isDeactivated: false,
|
isDeactivated: false,
|
||||||
extraQuery: extraQuery,
|
extraQuery: extraQuery,
|
||||||
actionInit: this.headerActions.has === false,
|
actionInit: this.headerActions.has === false,
|
||||||
initQuery: {}
|
initQuery: {},
|
||||||
|
filterExpand: localStorage.getItem('filterExpand') !== '0'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['currentOrgIsRoot']),
|
...mapGetters(['currentOrgIsRoot']),
|
||||||
|
iHasQuickFilter() {
|
||||||
|
const has =
|
||||||
|
(this.quickFilters && this.quickFilters.length > 0) ||
|
||||||
|
(this.quickSummary && this.quickSummary.length > 0)
|
||||||
|
return !!has
|
||||||
|
},
|
||||||
dataTable() {
|
dataTable() {
|
||||||
return this.$refs.dataTable.$refs.dataTable
|
return this.$refs.dataTable?.$refs.dataTable
|
||||||
},
|
},
|
||||||
iHeaderActions() {
|
iHeaderActions() {
|
||||||
// 如果路由中锁定了 root 组织,就不在检查 root 组织下是否可以创建等
|
// 如果路由中锁定了 root 组织,就不在检查 root 组织下是否可以创建等
|
||||||
@ -186,6 +213,11 @@ export default {
|
|||||||
this.$log.debug('ListTable: found colConfig change')
|
this.$log.debug('ListTable: found colConfig change')
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
|
},
|
||||||
|
filterExpand: {
|
||||||
|
handler(val) {
|
||||||
|
localStorage.setItem('filterExpand', val ? '1' : '0')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -208,6 +240,28 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleFilterExpandChanged(expand) {
|
||||||
|
this.filterExpand = expand
|
||||||
|
},
|
||||||
|
handleQuickFilter(option) {
|
||||||
|
if (option.route) {
|
||||||
|
this.$router.push(option.route)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (option.filter) {
|
||||||
|
const filter = { ...option.filter }
|
||||||
|
if (option.active) {
|
||||||
|
for (const key in filter) {
|
||||||
|
filter[key] = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.filter(option.filter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (option.callback) {
|
||||||
|
option.callback(option.active)
|
||||||
|
}
|
||||||
|
},
|
||||||
handleActionInitialDone() {
|
handleActionInitialDone() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.actionInit = true
|
this.actionInit = true
|
||||||
@ -282,6 +336,12 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.filter-expand {
|
||||||
|
&::v-deep button.actionFilter {
|
||||||
|
background-color: rgb(0, 0, 0, 0.08) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.table-content {
|
.table-content {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
|
140
src/components/Table/TableFormatters/AccountConnectFormatter.vue
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<el-dropdown
|
||||||
|
size="small"
|
||||||
|
trigger="hover"
|
||||||
|
:show-timeout="500"
|
||||||
|
@command="handleCommand"
|
||||||
|
@visible-change="visibleChange"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="handlePamConnect"
|
||||||
|
>
|
||||||
|
<i :class="IButtonIcon" />
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item command="Title" disabled>
|
||||||
|
{{ ITitleText }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided />
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="protocol in protocols"
|
||||||
|
:key="protocol.id"
|
||||||
|
:command="protocol.name"
|
||||||
|
>
|
||||||
|
{{ protocol.name }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BaseFormatter from './base.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AccountConnectFormatter',
|
||||||
|
extends: BaseFormatter,
|
||||||
|
props: {
|
||||||
|
buttonIcon: {
|
||||||
|
type: String,
|
||||||
|
default: 'fa fa-desktop'
|
||||||
|
},
|
||||||
|
titleText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
connectUrlTemplate: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
protocols: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
IButtonIcon() {
|
||||||
|
return this.buttonIcon
|
||||||
|
},
|
||||||
|
ITitleText() {
|
||||||
|
return this.titleText || this.$t('OptionalProtocol')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleCommand(protocol) {
|
||||||
|
if (protocol === 'Title') return
|
||||||
|
|
||||||
|
this.formatterArgs.setMapItem(this.row.id, protocol)
|
||||||
|
this.handleWindowOpen(this.row, protocol)
|
||||||
|
},
|
||||||
|
visibleChange(visible) {
|
||||||
|
if (visible) {
|
||||||
|
this.getProtocols(this.row.asset.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleWindowOpen(row, protocol) {
|
||||||
|
const url = this.formatterArgs.connectUrlTemplate(row) + `${protocol}`
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async handlePamConnect() {
|
||||||
|
const protocolMap = this.$store.getters.protocolMap
|
||||||
|
|
||||||
|
if (protocolMap.has(this.row.id)) {
|
||||||
|
// 直连
|
||||||
|
const protocol = protocolMap.get(this.row.id)
|
||||||
|
this.handleWindowOpen(this.row, protocol)
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const url = this.formatterArgs.url.replace('{id}', this.row.asset.id)
|
||||||
|
const res = await this.$axios.get(url)
|
||||||
|
|
||||||
|
if (res && res.protocols.length > 0) {
|
||||||
|
const protocol = res.protocols[0]
|
||||||
|
|
||||||
|
this.formatterArgs.setMapItem(this.row.id, protocol.name)
|
||||||
|
this.handleWindowOpen(this.row, protocol.name)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Error getting protocols: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getProtocols(assetId) {
|
||||||
|
try {
|
||||||
|
const url = this.formatterArgs.url.replace('{id}', assetId)
|
||||||
|
const res = await this.$axios.get(url)
|
||||||
|
|
||||||
|
if (res) this.protocols = res.protocols
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Error getting protocols: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.el-dropdown-menu__item.is-disabled {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-dropdown-menu__item {
|
||||||
|
transition: height 0.3s ease-in-out, padding 0.3s ease-in-out;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-dropdown-menu {
|
||||||
|
transition: min-height 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
</style>
|
@ -10,8 +10,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ActionsGroup from '@/components/ActionsGroup/index.vue'
|
|
||||||
import BaseFormatter from './base.vue'
|
import BaseFormatter from './base.vue'
|
||||||
|
import ActionsGroup from '@/components/ActionsGroup/index.vue'
|
||||||
|
|
||||||
const defaultPerformDelete = function({ row, col }) {
|
const defaultPerformDelete = function({ row, col }) {
|
||||||
const id = row.id
|
const id = row.id
|
||||||
@ -33,6 +33,7 @@ const defaultUpdateCallback = function({ row, col }) {
|
|||||||
} else {
|
} else {
|
||||||
route.name = updateRoute
|
route.name = updateRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.push(route)
|
this.$router.push(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ export default {
|
|||||||
onUpdate: defaultUpdateCallback,
|
onUpdate: defaultUpdateCallback,
|
||||||
onDelete: defaultDeleteCallback,
|
onDelete: defaultDeleteCallback,
|
||||||
onClone: defaultCloneCallback,
|
onClone: defaultCloneCallback,
|
||||||
extraActions: [] // format see defaultActions
|
extraActions: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +136,7 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'clone',
|
name: 'clone',
|
||||||
title: this.$t('Duplicate'),
|
title: this.$t('Duplicate'),
|
||||||
type: 'info',
|
type: 'primary',
|
||||||
has: colActions.hasClone,
|
has: colActions.hasClone,
|
||||||
can: colActions.canClone,
|
can: colActions.canClone,
|
||||||
callback: colActions.onClone,
|
callback: colActions.onClone,
|
||||||
@ -146,8 +147,8 @@ export default {
|
|||||||
colActions: colActions,
|
colActions: colActions,
|
||||||
defaultActions: defaultActions,
|
defaultActions: defaultActions,
|
||||||
extraActions: colActions.extraActions,
|
extraActions: colActions.extraActions,
|
||||||
moreActionsTitle: ''
|
|
||||||
// moreActionsTitle: colActions.moreActionsTitle || null
|
// moreActionsTitle: colActions.moreActionsTitle || null
|
||||||
|
moreActionsTitle: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -223,7 +224,7 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.table-actions {
|
.table-actions {
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.el-icon-arrow-down {
|
.el-icon-arrow-down {
|
||||||
|
@ -12,7 +12,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import i18n from '@/i18n/i18n'
|
||||||
import BaseFormatter from './base.vue'
|
import BaseFormatter from './base.vue'
|
||||||
|
const formatterArgsDefault = {
|
||||||
|
faChoices: {
|
||||||
|
true: 'fa-check-circle',
|
||||||
|
false: 'fa-times-circle'
|
||||||
|
},
|
||||||
|
classChoices: {
|
||||||
|
true: 'text-primary',
|
||||||
|
false: 'text-danger'
|
||||||
|
},
|
||||||
|
textChoices: {
|
||||||
|
true: i18n.t('Yes'),
|
||||||
|
false: i18n.t('No')
|
||||||
|
},
|
||||||
|
getKey({ row, cellValue }) {
|
||||||
|
return (cellValue && typeof cellValue === 'object') ? cellValue.value : cellValue
|
||||||
|
},
|
||||||
|
getText({ row, cellValue }) {
|
||||||
|
const key = this.getKey({ row, cellValue })
|
||||||
|
return (cellValue && typeof cellValue === 'object') ? cellValue.label : this.textChoices[key] || cellValue
|
||||||
|
},
|
||||||
|
getIcon({ row, cellValue }) {
|
||||||
|
const key = this.getKey({ row, cellValue })
|
||||||
|
return this.faChoices[key]
|
||||||
|
},
|
||||||
|
hasTips: false,
|
||||||
|
showIcon: true,
|
||||||
|
showText: true,
|
||||||
|
showFalse: true,
|
||||||
|
getTips: ({ row, cellValue }) => {
|
||||||
|
return cellValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChoicesFormatter',
|
name: 'ChoicesFormatter',
|
||||||
@ -21,56 +54,29 @@ export default {
|
|||||||
formatterArgsDefault: {
|
formatterArgsDefault: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default() {
|
default() {
|
||||||
return {
|
return { ...formatterArgsDefault }
|
||||||
faChoices: {
|
|
||||||
true: 'fa-check-circle',
|
|
||||||
false: 'fa-times-circle'
|
|
||||||
},
|
|
||||||
classChoices: {
|
|
||||||
true: 'text-primary',
|
|
||||||
false: 'text-danger'
|
|
||||||
},
|
|
||||||
textChoices: {
|
|
||||||
true: this.$t('Yes'),
|
|
||||||
false: this.$t('No')
|
|
||||||
},
|
|
||||||
getKey({ row, cellValue }) {
|
|
||||||
return (cellValue && typeof cellValue === 'object') ? cellValue.value : cellValue
|
|
||||||
},
|
|
||||||
getText({ row, cellValue }) {
|
|
||||||
const key = this.getKey({ row, cellValue })
|
|
||||||
return (cellValue && typeof cellValue === 'object') ? cellValue.label : this.textChoices[key] || cellValue
|
|
||||||
},
|
|
||||||
getIcon({ row, cellValue }) {
|
|
||||||
const key = this.getKey({ row, cellValue })
|
|
||||||
return this.faChoices[key]
|
|
||||||
},
|
|
||||||
hasTips: false,
|
|
||||||
showIcon: true,
|
|
||||||
showText: true,
|
|
||||||
showFalse: true,
|
|
||||||
getTips: ({ row, cellValue }) => {
|
|
||||||
return cellValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formatterArgsDefault: formatterArgsDefault,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
formatterArgs: Object.assign({}, this.formatterArgsDefault, this.col.formatterArgs)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
key() {
|
key() {
|
||||||
return this.formatterArgs.getKey(
|
const k = this.formatterArgs.getKey(
|
||||||
{ row: this.row, cellValue: this.cellValue }
|
{ row: this.row, cellValue: this.cellValue }
|
||||||
)
|
)
|
||||||
|
return k
|
||||||
},
|
},
|
||||||
icon() {
|
icon() {
|
||||||
const icon = this.formatterArgs.getIcon(
|
const icon = this.formatterArgs.getIcon(
|
||||||
{ row: this.row, cellValue: this.cellValue }
|
{ row: this.row, cellValue: this.cellValue }
|
||||||
)
|
)
|
||||||
|
console.log('choices: ', this.faChoices)
|
||||||
|
console.log('Icon: ', icon)
|
||||||
return icon
|
return icon
|
||||||
},
|
},
|
||||||
classes() {
|
classes() {
|
||||||
|
56
src/components/Table/TableFormatters/CopyableFormatter.vue
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<script>
|
||||||
|
import BaseFormatter from './base.vue'
|
||||||
|
import { copy } from '@/utils/common'
|
||||||
|
export default {
|
||||||
|
name: 'CopyableFormatter',
|
||||||
|
extends: BaseFormatter,
|
||||||
|
props: {
|
||||||
|
formatterArgsDefault: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
shadow: false,
|
||||||
|
getText: ({ cellValue }) => cellValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iCellValue() {
|
||||||
|
if (this.formatterArgs.shadow) {
|
||||||
|
return '*'.repeat(6)
|
||||||
|
} else {
|
||||||
|
return this.cellValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async copy() {
|
||||||
|
const text = await this.formatterArgs.getText({ cellValue: this.cellValue, row: this.row })
|
||||||
|
copy(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="copyable">
|
||||||
|
{{ iCellValue }} <i class="el-icon-copy-document copy" @click="copy()" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.copy {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -6,20 +6,30 @@
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:type="col.type || 'info'"
|
:type="col.type || 'info'"
|
||||||
class="detail"
|
class="detail"
|
||||||
@click="goDetail"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<slot>
|
<slot>
|
||||||
{{ iTitle }}
|
{{ iTitle }}
|
||||||
</slot>
|
</slot>
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<Drawer
|
||||||
|
v-if="formatterArgs.drawer && drawerVisible"
|
||||||
|
:component="drawerComponent"
|
||||||
|
:has-footer="false"
|
||||||
|
:title="drawerTitle"
|
||||||
|
:visible.sync="drawerVisible"
|
||||||
|
class="detail-drawer"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseFormatter from './base.vue'
|
import BaseFormatter from './base.vue'
|
||||||
|
import Drawer from '@/components/Drawer/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DetailFormatter',
|
name: 'DetailFormatter',
|
||||||
|
components: { Drawer },
|
||||||
extends: BaseFormatter,
|
extends: BaseFormatter,
|
||||||
props: {
|
props: {
|
||||||
formatterArgsDefault: {
|
formatterArgsDefault: {
|
||||||
@ -27,14 +37,19 @@ export default {
|
|||||||
default() {
|
default() {
|
||||||
return {
|
return {
|
||||||
route: this.$route.name.replace('List', 'Detail'),
|
route: this.$route.name.replace('List', 'Detail'),
|
||||||
|
can: true,
|
||||||
getRoute: null,
|
getRoute: null,
|
||||||
routeQuery: null,
|
routeQuery: null,
|
||||||
can: true,
|
drawer: false,
|
||||||
|
onClick: null,
|
||||||
openInNewPage: false,
|
openInNewPage: false,
|
||||||
removeColorOnClick: false,
|
removeColorOnClick: false,
|
||||||
getTitle({ col, row, cellValue }) {
|
beforeClick: () => {
|
||||||
return cellValue
|
|
||||||
},
|
},
|
||||||
|
getTitle({ row, cellValue }) {
|
||||||
|
return cellValue || row.name
|
||||||
|
},
|
||||||
|
getDrawerTitle: null,
|
||||||
getIcon({ col, row, cellValue }) {
|
getIcon({ col, row, cellValue }) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -45,8 +60,13 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||||
return {
|
return {
|
||||||
|
drawerTitle: '',
|
||||||
linkClicked: false,
|
linkClicked: false,
|
||||||
formatterArgs: formatterArgs
|
drawerComponent: '',
|
||||||
|
showTableDetailDrawer: false,
|
||||||
|
currentTemplate: null,
|
||||||
|
formatterArgs: formatterArgs,
|
||||||
|
drawerVisible: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -54,7 +74,8 @@ export default {
|
|||||||
return this.formatterArgs.getTitle({
|
return this.formatterArgs.getTitle({
|
||||||
col: this.col,
|
col: this.col,
|
||||||
row: this.row,
|
row: this.row,
|
||||||
cellValue: this.cellValue
|
cellValue: this.cellValue,
|
||||||
|
index: this.index
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
disabled() {
|
disabled() {
|
||||||
@ -70,18 +91,100 @@ export default {
|
|||||||
row: this.row,
|
row: this.row,
|
||||||
cellValue: this.cellValue
|
cellValue: this.cellValue
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
callbackArgs() {
|
||||||
|
return {
|
||||||
|
col: this.col,
|
||||||
|
row: this.row,
|
||||||
|
cellValue: this.cellValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getResource() {
|
||||||
|
const route = this.resolveRoute()
|
||||||
|
if (!route) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const resource = route.meta.title || route.name
|
||||||
|
return resource.replace(' details', '').replace('详情', '')
|
||||||
|
},
|
||||||
|
getDrawerTitle() {
|
||||||
|
if (this.formatterArgs?.getDrawerTitle && typeof this.formatterArgs.getDrawerTitle === 'function') {
|
||||||
|
return this.formatterArgs.getDrawerTitle({
|
||||||
|
col: this.col,
|
||||||
|
row: this.row,
|
||||||
|
cellValue: this.cellValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let title = this.cellValue || this.row.name
|
||||||
|
const resource = this.getResource()
|
||||||
|
if (resource) {
|
||||||
|
title = `${resource}: ${title}`
|
||||||
|
}
|
||||||
|
return title
|
||||||
|
},
|
||||||
|
resolveRoute() {
|
||||||
|
const route = this.getDetailRoute()
|
||||||
|
const routes = this.$router.resolve(route)
|
||||||
|
if (!routes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
|
||||||
|
if (matched.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (matched[0] && matched[0].components?.default) {
|
||||||
|
return matched[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRouteComponent() {
|
||||||
|
const route = this.resolveRoute()
|
||||||
|
if (route) {
|
||||||
|
return route.components.default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showDrawer() {
|
||||||
|
if (this.formatterArgs.drawerComponent) {
|
||||||
|
this.drawerComponent = this.formatterArgs.drawerComponent
|
||||||
|
} else {
|
||||||
|
this.drawerComponent = this.getRouteComponent()
|
||||||
|
}
|
||||||
|
const route = this.getDetailRoute()
|
||||||
|
if (route?.query?.tab) {
|
||||||
|
this.$cookie.set(route.name, route.query.tab, 1)
|
||||||
|
this.$route.query.tab = route.query.tab
|
||||||
|
}
|
||||||
|
const payload = {
|
||||||
|
action: 'detail',
|
||||||
|
row: this.row,
|
||||||
|
col: this.col,
|
||||||
|
id: route.params.id || this.row.id
|
||||||
|
}
|
||||||
|
this.$store.dispatch('common/setDrawerActionMeta', payload).then(() => {
|
||||||
|
this.drawerTitle = this.getDrawerTitle(payload)
|
||||||
|
this.drawerVisible = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleClick() {
|
||||||
|
if (this.formatterArgs.beforeClick) {
|
||||||
|
this.formatterArgs.beforeClick(this.callbackArgs)
|
||||||
|
}
|
||||||
|
if (this.formatterArgs.onClick) {
|
||||||
|
this.formatterArgs.onClick(this.callbackArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.formatterArgs.drawer) {
|
||||||
|
this.showDrawer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.goDetail()
|
||||||
|
},
|
||||||
getDetailRoute() {
|
getDetailRoute() {
|
||||||
// const defaultRoute = this.$route.name.replace('List', 'Detail')
|
// const defaultRoute = this.$route.name.replace('List', 'Detail')
|
||||||
let route = this.formatterArgs.route
|
let route = this.formatterArgs.route
|
||||||
if (this.formatterArgs.getRoute && typeof this.formatterArgs.getRoute === 'function') {
|
if (this.formatterArgs.getRoute && typeof this.formatterArgs.getRoute === 'function') {
|
||||||
route = this.formatterArgs.getRoute({
|
route = this.formatterArgs.getRoute(this.callbackArgs)
|
||||||
row: this.row,
|
|
||||||
col: this.col,
|
|
||||||
cellValue: this.cellValue
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if (!route) {
|
if (!route) {
|
||||||
console.error('No route found')
|
console.error('No route found')
|
||||||
@ -89,7 +192,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let detailRoute = { replace: true }
|
let detailRoute = { replace: true }
|
||||||
|
|
||||||
if (typeof route === 'string') {
|
if (typeof route === 'string') {
|
||||||
detailRoute.name = route
|
detailRoute.name = route
|
||||||
detailRoute.params = { id: this.row.id }
|
detailRoute.params = { id: this.row.id }
|
||||||
@ -107,18 +209,18 @@ export default {
|
|||||||
const detailRoute = this.getDetailRoute()
|
const detailRoute = this.getDetailRoute()
|
||||||
|
|
||||||
if (this.formatterArgs.openInNewPage) {
|
if (this.formatterArgs.openInNewPage) {
|
||||||
this.linkClicked = this.formatterArgs.removeColorOnClick
|
|
||||||
const { href } = this.$router.resolve(detailRoute)
|
const { href } = this.$router.resolve(detailRoute)
|
||||||
window.open(href, '_blank')
|
this.linkClicked = this.formatterArgs.removeColorOnClick
|
||||||
} else {
|
return window.open(href, '_blank')
|
||||||
this.$router.push(detailRoute)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$router.push(detailRoute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.detail {
|
.detail {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -142,4 +244,18 @@ export default {
|
|||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep .go-back {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-drawer {
|
||||||
|
::v-deep {
|
||||||
|
.el-drawer__header {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
<template>
|
||||||
|
<span class="conform-td">
|
||||||
|
<span v-if="iValue === statusMap.pending">
|
||||||
|
<el-dropdown trigger="click" @command="handleRisk">
|
||||||
|
<el-button class="confirm action" size="mini">
|
||||||
|
<i class="fa fa-check" />
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item v-for="item of iActions" :key="item.name" :command="item.name">
|
||||||
|
{{ item.label }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-tooltip :content="$tc('Ignore')" :open-delay="400">
|
||||||
|
<el-button class="ignore action" size="mini">
|
||||||
|
<svg-icon icon-class="ignore" @click="handleRisk('ignore')" />
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-tooltip v-else :content="iLabel" :open-delay="400" class="platform-status">
|
||||||
|
<span v-if="iValue === statusMap.confirmed ">
|
||||||
|
<i class="fa fa-check color-primary" />
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<svg-icon icon-class="ignore" />
|
||||||
|
</span>
|
||||||
|
</el-tooltip>
|
||||||
|
<ProcessingDialog :visible="processing" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BaseFormatter from './base.vue'
|
||||||
|
import ProcessingDialog from '@/components/Dialog/ProcessingDialog.vue'
|
||||||
|
|
||||||
|
const statusMap = {
|
||||||
|
pending: '0',
|
||||||
|
confirmed: '1',
|
||||||
|
ignored: '2'
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'ConfirmOrIgnoreFormatter',
|
||||||
|
components: { ProcessingDialog },
|
||||||
|
extends: BaseFormatter,
|
||||||
|
props: {
|
||||||
|
formatterArgsDefault: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
confirm: ({ row, cellValue }) => {
|
||||||
|
},
|
||||||
|
ignore: ({ row, cellValue }) => {
|
||||||
|
},
|
||||||
|
remove: ({ row, cellValue }) => {
|
||||||
|
},
|
||||||
|
confirmIcon: 'fa fa-check'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs),
|
||||||
|
processing: false,
|
||||||
|
statusMap: statusMap
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iValue() {
|
||||||
|
if (this.cellValueIsLabelChoice()) {
|
||||||
|
return this.cellValue.value
|
||||||
|
} else {
|
||||||
|
return this.cellValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
iLabel() {
|
||||||
|
if (this.cellValueIsLabelChoice()) {
|
||||||
|
return this.cellValue.label
|
||||||
|
} else {
|
||||||
|
return this.cellValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
iActions() {
|
||||||
|
return this.getActions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRemove() {
|
||||||
|
this.formatterArgs.remove({ row: this.row, cellValue: this.cellValue })
|
||||||
|
},
|
||||||
|
handleRisk(cmd) {
|
||||||
|
const data = {
|
||||||
|
asset: this.row.asset.id,
|
||||||
|
username: this.row.username,
|
||||||
|
action: cmd,
|
||||||
|
risk: ''
|
||||||
|
}
|
||||||
|
this.processing = true
|
||||||
|
this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data).then(() => {
|
||||||
|
if (cmd === 'add_account') {
|
||||||
|
this.row.present = true
|
||||||
|
}
|
||||||
|
if (cmd === 'ignore') {
|
||||||
|
this.row.status = { 'value': statusMap.ignored }
|
||||||
|
}
|
||||||
|
this.row.status = { 'value': statusMap.confirmed }
|
||||||
|
}).finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.processing = false
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getActions() {
|
||||||
|
const actions = [
|
||||||
|
// {
|
||||||
|
// name: 'disable_account',
|
||||||
|
// label: this.$t('Disable remote account'),
|
||||||
|
// has: this.row.remote_present
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'delete_remote',
|
||||||
|
label: this.$t('Delete remote account'),
|
||||||
|
has: this.row.remote_present
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'add_account',
|
||||||
|
label: this.$t('Add account'),
|
||||||
|
has: !this.row.present
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'change_password_add',
|
||||||
|
label: this.$t('Add account after changing password'),
|
||||||
|
has: !this.row.present
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return actions.filter(action => {
|
||||||
|
return action.has
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
|
||||||
|
.action.el-button--mini {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 1px 4px;
|
||||||
|
|
||||||
|
&.confirm {
|
||||||
|
::v-deep i {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.remove {
|
||||||
|
::v-deep i {
|
||||||
|
color: var(--color-danger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ignore {
|
||||||
|
::v-deep svg.svg-icon {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action.ignore {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
60
src/components/Table/TableFormatters/PlatformFormatter.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<span class="platform-td">
|
||||||
|
<span class="icon-zone">
|
||||||
|
<img :src="icon" alt="icon" class="asset-icon">
|
||||||
|
</span>
|
||||||
|
<span class="platform-name">{{ value.name }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BaseFormatter from './base.vue'
|
||||||
|
import { loadPlatformIcon } from '@/utils/jms'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlatformFormatter',
|
||||||
|
extends: BaseFormatter,
|
||||||
|
props: {
|
||||||
|
formatterArgsDefault: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
platformAttr: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
icon() {
|
||||||
|
return loadPlatformIcon(this.value.name, this.value.type)
|
||||||
|
},
|
||||||
|
value() {
|
||||||
|
if (!this.formatterArgs.platformAttr) {
|
||||||
|
return this.cellValue
|
||||||
|
} else {
|
||||||
|
return _.get(this.row, this.formatterArgs.platformAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.icon-zone {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-icon {
|
||||||
|
height: 1.5em;
|
||||||
|
vertical-align: -0.2em;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,8 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<span :class="formatterArgs.actionLeft ? 'left' : 'right'" class="action">
|
||||||
|
<template v-for="(item, index) in iActions">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="item.has"
|
||||||
|
:key="index"
|
||||||
|
:content="item.tooltip"
|
||||||
|
:open-delay="500"
|
||||||
|
effect="dark"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<i :class="[item.class, item.icon]" class="fa" @click="item.action()" />
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="!isEdit"
|
v-if="!isEdit"
|
||||||
:content="currentValue"
|
:content="currentValue"
|
||||||
|
:disabled="!isShow"
|
||||||
|
:open-delay="500"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<pre class="text" style="cursor: pointer">{{ currentValue }}</pre>
|
<pre class="text" style="cursor: pointer">{{ currentValue }}</pre>
|
||||||
@ -16,21 +32,6 @@
|
|||||||
size="small"
|
size="small"
|
||||||
@blur="onEditBlur"
|
@blur="onEditBlur"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span v-if="realValue" class="action">
|
|
||||||
<template v-for="(item, index) in iActions">
|
|
||||||
<el-tooltip
|
|
||||||
v-if="item.has"
|
|
||||||
:key="index"
|
|
||||||
:content="item.tooltip"
|
|
||||||
effect="dark"
|
|
||||||
open-delay="500"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<i :class="[item.class, item.icon]" class="fa" @click="item.action()" />
|
|
||||||
</el-tooltip>
|
|
||||||
</template>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ import { copy, downloadText } from '@/utils/common'
|
|||||||
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ShowKeyCopyFormatter',
|
name: 'SecretViewerFormatter',
|
||||||
extends: BaseFormatter,
|
extends: BaseFormatter,
|
||||||
props: {
|
props: {
|
||||||
formatterArgsDefault: {
|
formatterArgsDefault: {
|
||||||
@ -51,7 +52,9 @@ export default {
|
|||||||
hasDownload: true,
|
hasDownload: true,
|
||||||
hasCopy: true,
|
hasCopy: true,
|
||||||
hasEdit: true,
|
hasEdit: true,
|
||||||
defaultShow: false
|
defaultShow: false,
|
||||||
|
secretFrom: 'cellValue', // fromCellValue or api,
|
||||||
|
actionLeft: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +64,8 @@ export default {
|
|||||||
isEdit: false,
|
isEdit: false,
|
||||||
realValue: this.cellValue,
|
realValue: this.cellValue,
|
||||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {}),
|
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs || {}),
|
||||||
isShow: false
|
isShow: false,
|
||||||
|
getIt: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -107,6 +111,9 @@ export default {
|
|||||||
tooltip: this.$t('Copy')
|
tooltip: this.$t('Copy')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
if (this.formatterArgs.actionLeft) {
|
||||||
|
actions.reverse()
|
||||||
|
}
|
||||||
return actions
|
return actions
|
||||||
},
|
},
|
||||||
currentValue() {
|
currentValue() {
|
||||||
@ -117,20 +124,45 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
cellValue: {
|
||||||
|
handler: function(val) {
|
||||||
|
this.realValue = val
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.isShow = this.formatterArgs.defaultShow
|
this.isShow = this.formatterArgs.defaultShow
|
||||||
|
if (this.formatterArgs.secretFrom !== 'cellValue') {
|
||||||
|
this.realValue = '--'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onShow() {
|
async getAccountSecret() {
|
||||||
this.isShow = !this.isShow
|
if (this.formatterArgs.secretFrom === 'cellValue' || this.getIt) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await this.$axios.get(`/api/v1/accounts/account-secrets/${this.row.id}/`)
|
||||||
|
this.realValue = res.secret
|
||||||
},
|
},
|
||||||
onCopy() {
|
async onShow() {
|
||||||
|
await this.getAccountSecret()
|
||||||
|
this.isShow = !this.isShow
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isShow = false
|
||||||
|
}, 10000)
|
||||||
|
},
|
||||||
|
async onCopy() {
|
||||||
|
await this.getAccountSecret()
|
||||||
copy(this.realValue)
|
copy(this.realValue)
|
||||||
},
|
},
|
||||||
onDownload() {
|
async onDownload() {
|
||||||
|
await this.getAccountSecret()
|
||||||
downloadText(this.realValue, this.name + '.txt')
|
downloadText(this.realValue, this.name + '.txt')
|
||||||
},
|
},
|
||||||
onEdit() {
|
async onEdit() {
|
||||||
|
await this.getAccountSecret()
|
||||||
this.isEdit = !this.isEdit
|
this.isEdit = !this.isEdit
|
||||||
if (this.isEdit) {
|
if (this.isEdit) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@ -146,7 +178,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
//white-space: nowrap;
|
//white-space: nowrap;
|
||||||
@ -155,6 +187,7 @@ export default {
|
|||||||
|
|
||||||
.text {
|
.text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: inline;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -163,13 +196,17 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
float: right;
|
font-size: 13px;
|
||||||
font-size: 15px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 5px;
|
margin-left: 1px;
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.fa {
|
.fa {
|
||||||
margin-right: 10px;
|
margin-right: 5px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
58
src/components/Table/TableFormatters/TaskStatusFormatter.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<ChoicesFormatter :formatter-args-default="iFormatterArgsDefault" v-bind="$props" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ChoicesFormatter from './ChoicesFormatter.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaskStatusFormatter',
|
||||||
|
components: {
|
||||||
|
ChoicesFormatter
|
||||||
|
},
|
||||||
|
extends: ChoicesFormatter,
|
||||||
|
props: {
|
||||||
|
formatterArgsDefault: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
faChoices: {
|
||||||
|
ready: 'fa-clock-o',
|
||||||
|
pending: 'fa-clock-o',
|
||||||
|
running: 'fa-spinner',
|
||||||
|
success: 'fa-check-circle',
|
||||||
|
failed: 'fa-times-circle',
|
||||||
|
error: 'fa-exclamation-circle',
|
||||||
|
canceled: 'fa-ban'
|
||||||
|
},
|
||||||
|
classChoices: {
|
||||||
|
success: 'text-primary',
|
||||||
|
failed: 'text-danger',
|
||||||
|
error: 'text-danger'
|
||||||
|
},
|
||||||
|
textChoices: {
|
||||||
|
ready: this.$t('Ready'),
|
||||||
|
pending: this.$t('Pending'),
|
||||||
|
running: this.$t('Running'),
|
||||||
|
success: this.$t('Success'),
|
||||||
|
failed: this.$t('Failed'),
|
||||||
|
error: this.$t('Error'),
|
||||||
|
canceled: this.$t('Canceled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
iFormatterArgsDefault: Object.assign({}, ChoicesFormatter.formatterArgsDefault, this.formatterArgsDefault)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -47,6 +47,11 @@ export default {
|
|||||||
return {
|
return {
|
||||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
cellValueIsLabelChoice() {
|
||||||
|
return typeof this.cellValue === 'object' && this.cellValue['value'] !== undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,7 +6,7 @@ import ActionsFormatter from './ActionsFormatter.vue'
|
|||||||
import DeleteActionFormatter from './DeleteActionFormatter.vue'
|
import DeleteActionFormatter from './DeleteActionFormatter.vue'
|
||||||
import DateFormatter from './DateFormatter.vue'
|
import DateFormatter from './DateFormatter.vue'
|
||||||
import AccountShowFormatter from './GrantedAccountShowFormatter.vue'
|
import AccountShowFormatter from './GrantedAccountShowFormatter.vue'
|
||||||
import ShowKeyCopyFormatter from './ShowKeyCopyFormatter.vue'
|
import SecretViewerFormatter from './SecretViewerFormatter.vue'
|
||||||
import DialogDetailFormatter from './DialogDetailFormatter.vue'
|
import DialogDetailFormatter from './DialogDetailFormatter.vue'
|
||||||
import EditableInputFormatter from './EditableInputFormatter.vue'
|
import EditableInputFormatter from './EditableInputFormatter.vue'
|
||||||
import StatusFormatter from './StatusFormatter.vue'
|
import StatusFormatter from './StatusFormatter.vue'
|
||||||
@ -18,6 +18,9 @@ import ProtocolsFormatter from './ProtocolsFormatter.vue'
|
|||||||
import TagChoicesFormatter from './TagChoicesFormatter.vue'
|
import TagChoicesFormatter from './TagChoicesFormatter.vue'
|
||||||
import SwitchFormatter from './SwitchFormatter.vue'
|
import SwitchFormatter from './SwitchFormatter.vue'
|
||||||
import AccountInfoFormatter from './AccountInfoFormatter.vue'
|
import AccountInfoFormatter from './AccountInfoFormatter.vue'
|
||||||
|
import PlatformFormatter from './PlatformFormatter.vue'
|
||||||
|
import DiscoverConfirmFormatter from './DiscoverConfirmFormatter.vue'
|
||||||
|
import AccountConnectFormatter from './AccountConnectFormatter.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
DetailFormatter,
|
DetailFormatter,
|
||||||
@ -27,7 +30,7 @@ export default {
|
|||||||
DeleteActionFormatter,
|
DeleteActionFormatter,
|
||||||
DateFormatter,
|
DateFormatter,
|
||||||
AccountShowFormatter,
|
AccountShowFormatter,
|
||||||
ShowKeyCopyFormatter,
|
SecretViewerFormatter,
|
||||||
DialogDetailFormatter,
|
DialogDetailFormatter,
|
||||||
ArrayFormatter,
|
ArrayFormatter,
|
||||||
EditableInputFormatter,
|
EditableInputFormatter,
|
||||||
@ -39,7 +42,10 @@ export default {
|
|||||||
TagChoicesFormatter,
|
TagChoicesFormatter,
|
||||||
LabelsFormatter,
|
LabelsFormatter,
|
||||||
SwitchFormatter,
|
SwitchFormatter,
|
||||||
AccountInfoFormatter
|
PlatformFormatter,
|
||||||
|
AccountInfoFormatter,
|
||||||
|
DiscoverConfirmFormatter,
|
||||||
|
AccountConnectFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -50,7 +56,7 @@ export {
|
|||||||
DeleteActionFormatter,
|
DeleteActionFormatter,
|
||||||
DateFormatter,
|
DateFormatter,
|
||||||
AccountShowFormatter,
|
AccountShowFormatter,
|
||||||
ShowKeyCopyFormatter,
|
SecretViewerFormatter,
|
||||||
DialogDetailFormatter,
|
DialogDetailFormatter,
|
||||||
ArrayFormatter,
|
ArrayFormatter,
|
||||||
EditableInputFormatter,
|
EditableInputFormatter,
|
||||||
@ -62,5 +68,8 @@ export {
|
|||||||
TagChoicesFormatter,
|
TagChoicesFormatter,
|
||||||
LabelsFormatter,
|
LabelsFormatter,
|
||||||
SwitchFormatter,
|
SwitchFormatter,
|
||||||
AccountInfoFormatter
|
PlatformFormatter,
|
||||||
|
DiscoverConfirmFormatter,
|
||||||
|
AccountInfoFormatter,
|
||||||
|
AccountConnectFormatter
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
:key="componentKey"
|
:key="componentKey"
|
||||||
ref="ListTable"
|
ref="ListTable"
|
||||||
:header-actions="headerActions"
|
:header-actions="headerActions"
|
||||||
|
:quick-filters="quickFilters"
|
||||||
|
:quick-summary="quickSummary"
|
||||||
:table-config="iTableConfig"
|
:table-config="iTableConfig"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
/>
|
/>
|
||||||
@ -56,7 +58,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import { setUrlParam } from '@/utils/common'
|
import { setUrlParam } from '@/utils/common'
|
||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import ListTable from '@/components/Table/DrawerListTable/index.vue'
|
||||||
import FileTree from '@/components/Table/TreeTable/components/FileTree.vue'
|
import FileTree from '@/components/Table/TreeTable/components/FileTree.vue'
|
||||||
import IBox from '../../IBox/index.vue'
|
import IBox from '../../IBox/index.vue'
|
||||||
import TabTree from '../TabTree/index.vue'
|
import TabTree from '../TabTree/index.vue'
|
||||||
@ -94,6 +96,18 @@ export default {
|
|||||||
treeWidth: {
|
treeWidth: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => '23.6%'
|
default: () => '23.6%'
|
||||||
|
},
|
||||||
|
quickFilters: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
quickSummary: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -243,7 +257,7 @@ $origin-color: #ffffff;
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
border: 1px solid #DCDFE6;
|
border: 1px solid #DCDFE6;
|
||||||
background-color: #fff;
|
background-color: #f3f3f3;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<DataZTree ref="dataztree" :setting="treeSetting" class="data-z-tree" v-on="$listeners">
|
<DataZTree ref="dataztree" :setting="treeSetting" class="data-z-tree" v-on="$listeners">
|
||||||
<slot v-if="treeSetting.hasRightMenu" slot="rMenu">
|
<slot slot="rMenu">
|
||||||
<li v-if="treeSetting.showCreate" id="m_create" class="rmenu" tabindex="-1" @click="createTreeNode">
|
<div v-if="menu && menu.length > 0">
|
||||||
<i class="fa fa-plus-square-o" /> {{ this.$t('CreateNode') }}
|
<span v-for="item in menu" :key="item.id">
|
||||||
</li>
|
<li
|
||||||
<li v-if="treeSetting.showUpdate" id="m_edit" class="rmenu" tabindex="-1" @click="editTreeNode">
|
v-if="hasMenuItem(item)"
|
||||||
<i class="fa fa-pencil-square-o" /> {{ this.$t('RenameNode') }}
|
:id="item.id"
|
||||||
</li>
|
:key="item.id"
|
||||||
<li v-if="treeSetting.showDelete" id="m_del" class="rmenu" tabindex="-1" @click="removeTreeNode">
|
:class="{ 'disabled': checkDisabled(item) }"
|
||||||
<i class="fa fa-minus-square" /> {{ this.$t('DeleteNode') }}
|
class="rmenu"
|
||||||
</li>
|
tabindex="-1"
|
||||||
|
@click="onMenuItemClick(item)"
|
||||||
|
>
|
||||||
|
<Icon :icon="item.icon" class="icon" /> {{ item.name }}
|
||||||
|
</li>
|
||||||
|
<li v-if="item.divided" class="divider" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<slot name="rMenu" />
|
<slot name="rMenu" />
|
||||||
</slot>
|
</slot>
|
||||||
</DataZTree>
|
</DataZTree>
|
||||||
@ -17,13 +24,15 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DataZTree from '../DataZTree/index.vue'
|
import DataZTree from '../DataZTree/index.vue'
|
||||||
|
import Icon from '@/components/Widgets/Icon'
|
||||||
import $ from '@/utils/jquery-vendor'
|
import $ from '@/utils/jquery-vendor'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AutoDataZTree',
|
name: 'AutoDataZTree',
|
||||||
components: {
|
components: {
|
||||||
DataZTree
|
DataZTree,
|
||||||
|
Icon
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
setting: {
|
setting: {
|
||||||
@ -34,7 +43,32 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
defaultMenu: [
|
||||||
|
{
|
||||||
|
id: 'm_create',
|
||||||
|
name: this.$t('CreateNode'),
|
||||||
|
icon: 'fa-plus-square-o',
|
||||||
|
callback: this.createTreeNode,
|
||||||
|
has: () => this.setting.showCreate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'm_edit',
|
||||||
|
name: this.$t('RenameNode'),
|
||||||
|
icon: 'fa-pencil-square-o',
|
||||||
|
callback: this.editTreeNode,
|
||||||
|
has: () => this.setting.showUpdate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'm_del',
|
||||||
|
name: this.$t('DeleteNode'),
|
||||||
|
icon: 'fa-minus-square',
|
||||||
|
callback: this.removeTreeNode,
|
||||||
|
has: () => this.setting.showDelete
|
||||||
|
}
|
||||||
|
],
|
||||||
defaultSetting: {
|
defaultSetting: {
|
||||||
|
showDefaultMenu: true,
|
||||||
|
showMenu: false,
|
||||||
showCreate: true,
|
showCreate: true,
|
||||||
showDelete: true,
|
showDelete: true,
|
||||||
showUpdate: true,
|
showUpdate: true,
|
||||||
@ -80,12 +114,49 @@ export default {
|
|||||||
},
|
},
|
||||||
rMenu() {
|
rMenu() {
|
||||||
return this.$refs.dataztree.rMenu
|
return this.$refs.dataztree.rMenu
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
let menu = []
|
||||||
|
if (this.setting.showDefaultMenu) {
|
||||||
|
menu = menu.concat(this.defaultMenu)
|
||||||
|
}
|
||||||
|
if (this.setting.menu && this.setting.menu.length > 0) {
|
||||||
|
menu = menu.concat(this.setting.menu)
|
||||||
|
}
|
||||||
|
return menu
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
$('body').unbind('mousedown')
|
$('body').unbind('mousedown')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
checkDisabled(item) {
|
||||||
|
let disabled = item.disabled
|
||||||
|
if (typeof disabled === 'function') {
|
||||||
|
disabled = disabled(this.currentNode)
|
||||||
|
}
|
||||||
|
if (typeof disabled === 'undefined') {
|
||||||
|
disabled = false
|
||||||
|
}
|
||||||
|
return disabled
|
||||||
|
},
|
||||||
|
hasMenu(node) {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
hasMenuItem(item) {
|
||||||
|
let has = item.has
|
||||||
|
if (typeof has === 'function') {
|
||||||
|
has = has(this.currentNode)
|
||||||
|
}
|
||||||
|
if (typeof has === 'undefined') {
|
||||||
|
has = true
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
},
|
||||||
|
onMenuItemClick(item) {
|
||||||
|
item.callback(this.currentNode)
|
||||||
|
this.hideRMenu()
|
||||||
|
},
|
||||||
onAsyncSuccess(event, treeId, treeNode, msg) {
|
onAsyncSuccess(event, treeId, treeNode, msg) {
|
||||||
const nodes = JSON.parse(msg)
|
const nodes = JSON.parse(msg)
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
@ -115,7 +186,7 @@ export default {
|
|||||||
if (this.rMenu) this.rMenu.css({ 'visibility': 'hidden' })
|
if (this.rMenu) this.rMenu.css({ 'visibility': 'hidden' })
|
||||||
$('body').unbind('mousedown', this.onBodyMouseDown)
|
$('body').unbind('mousedown', this.onBodyMouseDown)
|
||||||
},
|
},
|
||||||
// Request URL: http://localhost/api/v1/assets/assets/?node_id=d8212328-538d-41a6-bcfd-1e8cc7e3aed4&show_current_asset=null&draw=2&limit=15&offset=0&_=1587022917769
|
// Request URL: http://localhost/api/v1/assets/assets/?node_id=ID&show_current_asset=null&draw=2&limit=15&offset=0&_=1587022917769
|
||||||
onSelected: function(event, treeNode) {
|
onSelected: function(event, treeNode) {
|
||||||
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
const show_current_asset = this.$cookie.get('show_current_asset') || '0'
|
||||||
if (!this.setting.url) {
|
if (!this.setting.url) {
|
||||||
@ -191,6 +262,8 @@ export default {
|
|||||||
const offset = $(`#${zTreeID}`).offset()
|
const offset = $(`#${zTreeID}`).offset()
|
||||||
const scrollTop = document.querySelector('.treebox')?.scrollTop
|
const scrollTop = document.querySelector('.treebox')?.scrollTop
|
||||||
x -= offset.left
|
x -= offset.left
|
||||||
|
x = x < 0 ? 0 : x
|
||||||
|
|
||||||
// Tmp
|
// Tmp
|
||||||
y -= (offset.top + scrollTop) / 3 - 10
|
y -= (offset.top + scrollTop) / 3 - 10
|
||||||
x += document.body.scrollLeft
|
x += document.body.scrollLeft
|
||||||
@ -199,15 +272,25 @@ export default {
|
|||||||
if (y + $(`#${rMenuID} ul`).height() >= window.innerHeight) {
|
if (y + $(`#${rMenuID} ul`).height() >= window.innerHeight) {
|
||||||
y -= $(`#${rMenuID} ul`).height()
|
y -= $(`#${rMenuID} ul`).height()
|
||||||
}
|
}
|
||||||
|
y = y < 0 ? 0 : y
|
||||||
|
|
||||||
this.rMenu.css({ 'top': y + 'px', 'left': x + 'px', 'visibility': 'visible' })
|
this.rMenu.css({ 'top': y + 'px', 'left': x + 'px', 'visibility': 'visible' })
|
||||||
$(`#${rMenuID} ul`).show()
|
$(`#${rMenuID} ul`).show()
|
||||||
$('body').bind('mousedown', this.onBodyMouseDown)
|
$('body').bind('mousedown', this.onBodyMouseDown)
|
||||||
},
|
},
|
||||||
onRightClick: function(event, treeId, treeNode) {
|
onRightClick: function(event, treeId, treeNode) {
|
||||||
if (!this.setting.showMenu) {
|
let showMenu = this.setting.showMenu
|
||||||
|
if (typeof showMenu === 'function') {
|
||||||
|
showMenu = showMenu(treeNode)
|
||||||
|
}
|
||||||
|
if (!showMenu) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!treeNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentNode = treeNode
|
||||||
|
this.currentNodeId = treeNode.meta.data.id
|
||||||
// 屏蔽收藏资产
|
// 屏蔽收藏资产
|
||||||
if (treeNode?.id === '-12') {
|
if (treeNode?.id === '-12') {
|
||||||
return
|
return
|
||||||
@ -321,9 +404,14 @@ export default {
|
|||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
.data-z-tree {
|
.data-z-tree {
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.fa {
|
.icon {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
<a id="tree-refresh"><i class="fa fa-refresh" /></a>
|
<a id="tree-refresh"><i class="fa fa-refresh" /></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :id="iRMenuID" class="rMenu">
|
<div :id="iRMenuID" class="rMenu">
|
||||||
<ul class="dropdown-menu menu-actions">
|
<ul class="dropdown-menu menu-actions">
|
||||||
<slot name="rMenu" />
|
<slot name="rMenu" />
|
||||||
@ -49,8 +50,7 @@ import axiosRetry from 'axios-retry'
|
|||||||
|
|
||||||
const defaultObject = {
|
const defaultObject = {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => ({})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export default {
|
export default {
|
||||||
name: 'ZTree',
|
name: 'ZTree',
|
||||||
@ -90,6 +90,12 @@ export default {
|
|||||||
window.removeEventListener('resize', this.updateTreeHeight)
|
window.removeEventListener('resize', this.updateTreeHeight)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onMenuClick(menu) {
|
||||||
|
if (menu.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
menu.callback()
|
||||||
|
},
|
||||||
updateTreeHeight: _.debounce(function() {
|
updateTreeHeight: _.debounce(function() {
|
||||||
const tree = document.getElementById(this.iZTreeID)
|
const tree = document.getElementById(this.iZTreeID)
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
@ -106,8 +112,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 使用 table 的高度
|
// 使用 table 的高度
|
||||||
const ztreeRect = tree.getBoundingClientRect()
|
const zTreeRect = tree.getBoundingClientRect()
|
||||||
tree.style.height = `calc(100vh - ${ztreeRect.top}px - 30px - 25px)`
|
tree.style.height = `calc(100vh - ${zTreeRect.top}px - 30px - 25px)`
|
||||||
}, 100),
|
}, 100),
|
||||||
async initTree(refresh = false) {
|
async initTree(refresh = false) {
|
||||||
const vm = this
|
const vm = this
|
||||||
@ -156,9 +162,6 @@ export default {
|
|||||||
if (this.treeSetting.showMenu) {
|
if (this.treeSetting.showMenu) {
|
||||||
this.rMenu = $(`#${this.iRMenuID}`)
|
this.rMenu = $(`#${this.iRMenuID}`)
|
||||||
}
|
}
|
||||||
if (this.treeSetting?.otherMenu) {
|
|
||||||
$('.menu-actions').append(this.otherMenu)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onSearch() {
|
onSearch() {
|
||||||
this.showTreeSearch = !this.showTreeSearch
|
this.showTreeSearch = !this.showTreeSearch
|
||||||
@ -435,6 +438,24 @@ div.rMenu li {
|
|||||||
list-style: none outside none;
|
list-style: none outside none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: #606266;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
border: medium none;
|
border: medium none;
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
@ -451,7 +472,8 @@ div.rMenu li {
|
|||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
height: 300px;
|
max-height: 320px;
|
||||||
|
min-height: 150px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/components/Widgets/Loading/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<span v-if="loading" v-loading="loading" class="loading" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Index',
|
||||||
|
props: {
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loading {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,4 +1,7 @@
|
|||||||
import i18n from '@/i18n/i18n'
|
import i18n from '@/i18n/i18n'
|
||||||
|
import CronTab from '@/components/Form/FormFields/CronTab/index.vue'
|
||||||
|
import InputWithUnit from '@/components/Form/FormFields/InputWithUnit.vue'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
export const strMatchValues = ['exact', 'not', 'in', 'contains', 'startswith', 'endswith', 'regex']
|
export const strMatchValues = ['exact', 'not', 'in', 'contains', 'startswith', 'endswith', 'regex']
|
||||||
export const typeMatchMapper = {
|
export const typeMatchMapper = {
|
||||||
@ -26,3 +29,48 @@ export const attrMatchOptions = [
|
|||||||
{ label: i18n.t('GreatEqualThan'), value: 'gte' },
|
{ label: i18n.t('GreatEqualThan'), value: 'gte' },
|
||||||
{ label: i18n.t('LessEqualThan'), value: 'lte' }
|
{ label: i18n.t('LessEqualThan'), value: 'lte' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const crontab = {
|
||||||
|
type: 'cronTab',
|
||||||
|
component: CronTab,
|
||||||
|
label: i18n.t('Crontab'),
|
||||||
|
hidden: (formValue) => {
|
||||||
|
return formValue.is_periodic === false
|
||||||
|
},
|
||||||
|
helpText: i18n.t('CrontabHelpText'),
|
||||||
|
helpTip: i18n.t('CrontabHelpTip')
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatorInterval = (rule, value, callback) => {
|
||||||
|
if (parseInt(value) < 1) {
|
||||||
|
return callback(new Error(i18n.t('EnsureThisValueIsGreaterThanOrEqualTo1')))
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const interval = {
|
||||||
|
label: i18n.t('Interval'),
|
||||||
|
hidden: (formValue) => {
|
||||||
|
return formValue.is_periodic === false
|
||||||
|
},
|
||||||
|
component: InputWithUnit,
|
||||||
|
el: {
|
||||||
|
unit: 'hour'
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{ validator: validatorInterval }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const is_periodic = {
|
||||||
|
type: 'checkbox',
|
||||||
|
hidden: (formValue) => {
|
||||||
|
return !store.getters.hasValidLicense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const periodicMeta = {
|
||||||
|
is_periodic,
|
||||||
|
interval,
|
||||||
|
crontab
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ export { default as Dialog } from './Dialog'
|
|||||||
export { default as FormGroupHeader } from './Form/FormGroupHeader'
|
export { default as FormGroupHeader } from './Form/FormGroupHeader'
|
||||||
export { default as Hamburger } from './Widgets/Hamburger'
|
export { default as Hamburger } from './Widgets/Hamburger'
|
||||||
export { default as ListTable } from './Table/ListTable'
|
export { default as ListTable } from './Table/ListTable'
|
||||||
|
export { default as DrawerListTable } from './Table/DrawerListTable'
|
||||||
export { default as RelationCard } from './Cards/RelationCard'
|
export { default as RelationCard } from './Cards/RelationCard'
|
||||||
export { default as Select2 } from './Form/FormFields/Select2'
|
export { default as Select2 } from './Form/FormFields/Select2'
|
||||||
export { default as Checkbox } from './Form/FormFields/Checkbox'
|
export { default as Checkbox } from './Form/FormFields/Checkbox'
|
||||||
@ -30,7 +31,7 @@ export { default as AccountListTable } from './Apps/AccountListTable/AccountList
|
|||||||
export { default as AssetRelationCard } from './Apps/AssetRelationCard'
|
export { default as AssetRelationCard } from './Apps/AssetRelationCard'
|
||||||
export { default as UserConfirmDialog } from './Apps/UserConfirmDialog'
|
export { default as UserConfirmDialog } from './Apps/UserConfirmDialog'
|
||||||
export { default as Announcement } from './Widgets/Announcement'
|
export { default as Announcement } from './Widgets/Announcement'
|
||||||
export { default as CronTab } from './Form/CronTab'
|
export { default as CronTab } from './Form/FormFields/CronTab'
|
||||||
export { default as Pagination } from './Table/Pagination'
|
export { default as Pagination } from './Table/Pagination'
|
||||||
export { default as Tooltip } from './Widgets/Tooltip'
|
export { default as Tooltip } from './Widgets/Tooltip'
|
||||||
export { default as ResourceActivity } from './Apps/ResourceActivity'
|
export { default as ResourceActivity } from './Apps/ResourceActivity'
|
||||||
|
23
src/directive/async.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
Vue.directive('async', {
|
||||||
|
async bind(el, binding) {
|
||||||
|
const { value: asyncFn, arg } = binding
|
||||||
|
if (typeof asyncFn === 'function') {
|
||||||
|
const result = await asyncFn(arg)
|
||||||
|
el.innerText = result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async update(el, binding) {
|
||||||
|
const { value: asyncFn, arg } = binding
|
||||||
|
if (typeof asyncFn === 'function') {
|
||||||
|
const result = await asyncFn(arg)
|
||||||
|
el.innerText = result
|
||||||
|
} else if (typeof asyncFn === 'object' && asyncFn.then) {
|
||||||
|
const result = await asyncFn
|
||||||
|
el.innerText = result
|
||||||
|
} else {
|
||||||
|
el.innerText = asyncFn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
33
src/directive/drawer-drag.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
Vue.directive('el-drawer-drag-width', {
|
||||||
|
bind(el, binding, vnode, oldVnode) {
|
||||||
|
const drawerEle = el.querySelector('.el-drawer')
|
||||||
|
// 创建触发拖拽的元素
|
||||||
|
const dragItem = document.createElement('div')
|
||||||
|
// 将元素放置到抽屉的左边边缘
|
||||||
|
dragItem.style.cssText = 'height: 100%;width: 5px;cursor: ew-resize;position: absolute;left: 0;'
|
||||||
|
drawerEle.append(dragItem)
|
||||||
|
|
||||||
|
dragItem.onmousedown = (downEvent) => {
|
||||||
|
// 拖拽时禁用文本选中
|
||||||
|
document.body.style.userSelect = 'none'
|
||||||
|
document.onmousemove = function(moveEvent) {
|
||||||
|
// 获取鼠标距离浏览器右边缘的距离
|
||||||
|
let realWidth = document.body.clientWidth - moveEvent.pageX
|
||||||
|
const width30 = document.body.clientWidth * 0.2
|
||||||
|
const width80 = document.body.clientWidth * 0.8
|
||||||
|
// 宽度不能大于浏览器宽度 80%,不能小于宽度的 20%
|
||||||
|
realWidth = realWidth > width80 ? width80 : realWidth < width30 ? width30 : realWidth
|
||||||
|
drawerEle.style.width = realWidth + 'px'
|
||||||
|
localStorage.setItem('drawerWidth', realWidth)
|
||||||
|
}
|
||||||
|
document.onmouseup = function(e) {
|
||||||
|
// 拖拽时结束时,取消禁用文本选中
|
||||||
|
document.body.style.userSelect = 'initial'
|
||||||
|
document.onmousemove = null
|
||||||
|
document.onmouseup = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
@ -1 +1,3 @@
|
|||||||
import './permission'
|
import './permission'
|
||||||
|
import './async'
|
||||||
|
import './drawer-drag'
|
||||||
|
@ -5,3 +5,12 @@ Vue.filter('date', function(value) {
|
|||||||
return toSafeLocalDateStr(value)
|
return toSafeLocalDateStr(value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.filter('async', async(asyncFn, args) => {
|
||||||
|
if (typeof asyncFn === 'function') {
|
||||||
|
return await asyncFn(args)
|
||||||
|
} else if (typeof asyncFn === 'object' && asyncFn.then) {
|
||||||
|
return await asyncFn
|
||||||
|
} else {
|
||||||
|
return asyncFn
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -14,6 +14,7 @@ router.beforeEach(async(to, from, next) => {
|
|||||||
// start progress bar
|
// start progress bar
|
||||||
// NProgress.start()
|
// NProgress.start()
|
||||||
try {
|
try {
|
||||||
|
await store.dispatch('common/cleanDrawerActionMeta')
|
||||||
await startup({ to, from, next })
|
await startup({ to, from, next })
|
||||||
next()
|
next()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -7,6 +7,19 @@
|
|||||||
"notAlphanumericUnderscore": "Only alphanumeric characters and underscores are allowed.",
|
"notAlphanumericUnderscore": "Only alphanumeric characters and underscores are allowed.",
|
||||||
"notParenthesis": "Parentheses are not allowed.",
|
"notParenthesis": "Parentheses are not allowed.",
|
||||||
"InvalidJson": "Invalid JSON format.",
|
"InvalidJson": "Invalid JSON format.",
|
||||||
"requiredHasUserNameMapped": "Username attr is required."
|
"requiredHasUserNameMapped": "Username attr is required.",
|
||||||
}
|
"NewFound": "New Found",
|
||||||
|
"LastLoginTime": "Last login time",
|
||||||
|
"Diff": "Difference",
|
||||||
|
"LastChangeTime": "Last change time",
|
||||||
|
"AccountDeleted": "Account deleted",
|
||||||
|
"WeakPassword": "Weak password",
|
||||||
|
"RepeatPassword": "Repeat password",
|
||||||
|
"LeakPassword": "Password leaked",
|
||||||
|
"Other": "Other",
|
||||||
|
"Details": "Details",
|
||||||
|
"Confirm": "Confirm",
|
||||||
|
"Ignore": "Ignore",
|
||||||
|
"Please enter a reason": "Please enter a reason",
|
||||||
|
"PleaseEnterReason": "Please enter a reason | Please enter reasons"
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"FieldRequiredError": "这个字段是必需的。",
|
"FieldRequiredError": "这个字段是必需的。",
|
||||||
"InputEmailAddress": "输入一个电子邮件地址。",
|
"InputEmailAddress": "输入一个电子邮件地址。",
|
||||||
"ServerError": "服务错误,请联系管理员。",
|
"ServerError": "服务错误,请联系管理员。",
|
||||||
"FormatError": "格式错误。",
|
"FormatError": "格式错误。",
|
||||||
"NotSpecialEmoji": "不支持表情字符。",
|
"NotSpecialEmoji": "不支持表情字符。",
|
||||||
"notAlphanumericUnderscore": "仅支持字母、数字和下划线。",
|
"notAlphanumericUnderscore": "仅支持字母、数字和下划线。",
|
||||||
"notParenthesis": "不能包含括号。",
|
"notParenthesis": "不能包含括号。",
|
||||||
"InvalidJson": "JSON 格式错误.",
|
"InvalidJson": "JSON 格式错误.",
|
||||||
"requiredHasUserNameMapped": "用户名属性是必需的。"
|
"requiredHasUserNameMapped": "用户名属性是必需的。",
|
||||||
|
"Diff": ""
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,17 @@
|
|||||||
"notAlphanumericUnderscore": "仅支持字母、数字和下划线。",
|
"notAlphanumericUnderscore": "仅支持字母、数字和下划线。",
|
||||||
"notParenthesis": "不能包含括号。",
|
"notParenthesis": "不能包含括号。",
|
||||||
"InvalidJson": "JSON 格式错误.",
|
"InvalidJson": "JSON 格式错误.",
|
||||||
"requiredHasUserNameMapped": "用户名属性是必需的。"
|
"requiredHasUserNameMapped": "用户名属性是必需的。",
|
||||||
|
"NewFound": "新发现",
|
||||||
|
"LastLoginTime": "最后登录时间",
|
||||||
|
"Diff": "差异",
|
||||||
|
"LastChangeTime": "最后修改时间",
|
||||||
|
"AccountDeleted": "账户已删除",
|
||||||
|
"WeakPassword": "弱密码",
|
||||||
|
"RepeatPassword": "重复密码",
|
||||||
|
"LeakPassword": "密码泄露",
|
||||||
|
"Other": "其他",
|
||||||
|
"Details": "详情",
|
||||||
|
"Confirm": "确认",
|
||||||
|
"Ignore": "忽略"
|
||||||
}
|
}
|
||||||
|
5
src/icons/svg/activate.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg t="1736148692584" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="41075" width="200" height="200">
|
||||||
|
<path d="M512.034996 927.992188c-229.350883 0-415.937195-186.646305-415.937196-415.927196 0-229.290891 186.586312-415.937195 415.937196-415.937196s415.927197 186.646305 415.927196 415.937196c0 229.280892-186.576314 415.927197-415.927196 415.927196z m0-927.862205C229.330886 0.129983 0.099987 229.360882 0.099987 512.064992s229.230898 511.935008 511.935009 511.935008 511.935008-229.2209 511.935008-511.935008c0-282.70411-229.2209-511.935008-511.935008-511.935009z m209.893353 352.33527c-12.948356-0.539931-26.016697 3.789519-35.895443 13.728258L464.03109 588.195327 337.977093 462.131331c-9.878746-9.878746-22.947087-14.208196-35.835451-13.728257-11.628524 0.479939-23.127064 4.819388-32.045931 13.728257-8.848877 8.848877-13.188326 20.417408-13.668265 32.045932-0.479939 12.888364 3.849511 25.956705 13.668265 35.83545l159.969691 159.959693c9.388808 9.338814 21.677248 14.028219 33.905695 14.028219 12.28844 0 24.57688-4.699403 33.905696-14.028219l255.967504-255.967504c9.878746-9.878746 14.218195-22.947087 13.738256-35.835451-0.479939-11.628524-4.819388-23.187056-13.738256-32.045932-8.908869-8.838878-20.297423-13.238319-31.915948-13.658266z"
|
||||||
|
fill="#515151" p-id="41076"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
10
src/icons/svg/activity.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="138px" height="134px" viewBox="0 0 138 134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>activity</title>
|
||||||
|
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="activity" fill="#3E3A39" fill-rule="nonzero">
|
||||||
|
<path d="M125.454545,123.012 C125.454545,123.30102 125.310273,123.469176 125.435727,123.484941 L12.7900909,123.553255 C12.7399091,123.521725 12.5454545,123.332549 12.5454545,123.012 L12.5454545,44.6614118 L125.454545,44.6614118 L125.454545,123.012 Z M12.5454545,18.865098 C12.5454545,18.5813333 12.6709091,18.4131765 12.5642727,18.386902 L37.6363636,18.386902 L37.6363636,21.0196078 C37.6363636,23.9218101 40.4447593,26.2745098 43.9090909,26.2745098 C47.3734225,26.2745098 50.1818182,23.9218101 50.1818182,21.0196078 L50.1818182,18.3921569 L87.8181818,18.3921569 L87.8181818,21.0196078 C87.8181818,23.9218101 90.6265775,26.2745098 94.0909091,26.2745098 C97.5552407,26.2745098 100.363636,23.9218101 100.363636,21.0196078 L100.363636,18.3921569 L125.260091,18.3921569 C125.385308,18.5275639 125.454545,18.6938135 125.454545,18.865098 L125.454545,34.1568627 L12.5454545,34.1568627 L12.5454545,18.865098 Z M125.423182,7.88235294 L100.363636,7.88235294 L100.363636,5.25490196 C100.363636,2.35269975 97.5552407,0 94.0909091,0 C90.6265775,0 87.8181818,2.35269975 87.8181818,5.25490196 L87.8181818,7.88235294 L50.1818182,7.88235294 L50.1818182,5.25490196 C50.1818182,2.35269975 47.3734225,0 43.9090909,0 C40.4447593,0 37.6363636,2.35269975 37.6363636,5.25490196 L37.6363636,7.88235294 L12.489,7.88235294 C5.60154545,7.88235294 0,12.811451 0,18.865098 L0,123.017255 C0,129.076157 5.60781818,134 12.4952727,134 L125.504727,134 C132.392182,134 138,129.076157 138,123.017255 L138,18.865098 C138,12.8061961 132.360818,7.87709804 125.423182,7.87709804 L125.423182,7.88235294 Z" id="形状"></path>
|
||||||
|
<path d="M44.3,76.5714286 L94.7,76.5714286 C98.1793939,76.5714286 101,74.2049337 101,71.2857143 C101,68.3664949 98.1793939,66 94.7,66 L44.3,66 C40.8206061,66 38,68.3664949 38,71.2857143 C38,74.2049337 40.8206061,76.5714286 44.3,76.5714286 M44.3,103 L94.7,103 C98.1793939,103 101,100.633505 101,97.7142857 C101,94.7950663 98.1793939,92.4285714 94.7,92.4285714 L44.3,92.4285714 C40.8206061,92.4285714 38,94.7950663 38,97.7142857 C38,100.633505 40.8206061,103 44.3,103" id="形状"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
7
src/icons/svg/backup.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<svg t="1730795663037" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="53312" width="200" height="200">
|
||||||
|
<path d="M81.4592 150.4768c-44.544 0-80.6912 35.0208-80.6912 78.2848v712.704c0 43.2128 36.1472 78.2848 80.6912 78.2848h686.6432c44.5952 0 80.7424-35.072 80.7424-78.336v-71.8848h90.3168c44.544 0 80.6912-35.0208 80.6912-78.2848V78.5408c0-43.2128-36.1472-78.2848-80.6912-78.2848H252.416c-44.544 0-80.6912 35.072-80.6912 78.336v71.8848H81.4592zM848.896 800.3584V228.7616c0-43.264-36.1472-78.336-80.7424-78.336H243.0976V69.2736h710.144v731.136h-104.448zM72.0896 941.4656V219.648h705.4336v731.136H72.0896v-9.3184z"
|
||||||
|
fill="#333333" p-id="53313"></path>
|
||||||
|
<path d="M215.1936 716.8a35.1232 35.1232 0 0 0-35.6352 34.6112c0 19.1488 15.9232 34.6112 35.6352 34.6112h203.3664a35.1232 35.1232 0 0 0 35.6864-34.6112 35.1232 35.1232 0 0 0-35.6864-34.56H215.1936z m0-186.9824a35.1232 35.1232 0 0 0-35.6352 34.56c0 19.0976 15.9232 34.6112 35.6352 34.6112h389.4272a35.1232 35.1232 0 0 0 35.6352-34.6112 35.1232 35.1232 0 0 0-35.6352-34.56H215.1936z m0-180.224a35.1232 35.1232 0 0 0-35.6352 34.56c0 19.1488 15.9232 34.6112 35.6352 34.6112h389.4272a35.1232 35.1232 0 0 0 35.6352-34.6112 35.1232 35.1232 0 0 0-35.6352-34.56H215.1936z"
|
||||||
|
fill="#333333" p-id="53314"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<svg t="1736134870052" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
<svg width="40px" height="39px" viewBox="0 0 40 39" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
p-id="30043" width="200" height="200">
|
||||||
>
|
<path d="M863.936 864.448v-304a48 48 0 0 1 96 0v345.6c0 30.08-24.32 54.4-54.4 54.4H118.528a54.4 54.4 0 0 1-54.4-54.4V118.592c0-30.08 24.32-54.4 54.4-54.4h344.96a48 48 0 1 1 0 96h-303.36v704.256h703.808z"
|
||||||
<path d="M12.7391982,21.5403486 C12.7133829,21.5732044 12.6875676,21.6078314 12.6759662,21.649499 L10.6832746,28.9553115 C10.5670838,29.3806658 10.6861086,29.8390088 11.0009841,30.1621204 C11.2365098,30.3921997 11.5468245,30.5186636 11.8773309,30.5186636 C11.9864813,30.5186636 12.0958531,30.505601 12.2034979,30.476996 L19.4574584,28.4985184 C19.4691041,28.4985184 19.4747277,28.5087028 19.4834951,28.5087028 C19.5668302,28.5087028 19.648704,28.4783709 19.710519,28.4151832 L39.1078895,9.02064673 C39.6840617,8.44372176 40,7.6578391 40,6.802791 C40,5.83394311 39.589081,4.8654938 38.8694856,4.14780247 L37.037575,2.31296933 C36.3190866,1.59337396 35.3492646,1.18170217 34.3808595,1.18170217 C33.5260328,1.18170217 32.7401945,1.49772899 32.162561,2.07319277 L12.7680688,21.4729101 C12.7479213,21.4914191 12.7535892,21.5188285 12.7391982,21.5403486 M37.2098687,7.12120901 L35.2831545,9.04646203 L32.1597714,5.87348528 L34.0591649,3.97404751 C34.3592951,3.67227884 34.9412238,3.71629326 35.2860769,4.06238625 L37.1192274,5.89721939 C37.3105173,6.08819934 37.4195791,6.34245547 37.4195791,6.59387767 C37.4184278,6.7999571 37.3450115,6.98695182 37.2098687,7.12120901 M16.1888811,21.845084 L30.1857217,7.84744639 L33.3106546,11.0225486 L19.3398506,24.9926883 L16.1888811,21.845084 L16.1888811,21.845084 Z M13.6387477,27.5200617 L14.6502373,23.8073995 L17.3483103,26.5057382 L13.6387477,27.5200617 L13.6387477,27.5200617 Z M37.3756976,14.8069445 C36.6414455,14.8069445 35.9951336,15.4039284 35.9922997,16.1481434 L35.9922997,34.2967274 C35.9922997,35.2449408 35.2222251,35.9802999 34.2725062,35.9802999 L4.43717267,35.9802999 C3.48891504,35.9802999 2.7821609,35.2450294 2.7821609,34.2967274 L2.7821609,4.42906946 C2.7821609,3.48010335 3.48887077,2.76564445 4.43717267,2.76564445 L24.9126299,2.76564445 C25.651,2.76564445 26.2501094,2.10950242 26.2501094,1.37091088 C26.2501094,0.633780616 25.6509558,-6.14926012e-16 24.9126299,-6.14926012e-16 L4.23313012,-6.14926012e-16 C1.92135638,-6.14926012e-16 6.14926012e-16,1.91329748 6.14926012e-16,4.22644388 L6.14926012e-16,34.5008586 C6.14926012e-16,36.8140935 1.92140069,38.7597597 4.23313012,38.7597597 L34.4749546,38.7597597 C36.788411,38.7597597 38.7574127,36.8140935 38.7574127,34.5008586 L38.7574127,16.139686 C38.7545788,15.4039284 38.1097725,14.8069445 37.3756976,14.8069445"
|
fill="#666666" p-id="30044"></path>
|
||||||
id="形状"></path>
|
<path d="M422.4 669.44a48 48 0 0 1-67.904-67.904L870.848 85.184a48 48 0 1 1 67.84 67.84L422.4 669.44z"
|
||||||
|
fill="#666666" p-id="30045"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 567 B |
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="40px" height="44px" viewBox="0 0 40 44" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
<svg width="162px" height="176px" viewBox="0 0 162 176" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
>
|
<title>编组</title>
|
||||||
<path d="M30.2706254,38.229976 C30.2706254,39.3606857 29.7535516,39.817019 28.4764414,39.817019 L5.16450885,39.817019 C3.87961144,39.817019 3.36253758,39.3606857 3.36253758,38.229976 L3.36253758,25.2564058 C3.36253758,24.1256961 3.88116889,23.6678054 5.16450885,23.6678054 L26.9080879,23.6678054 L26.9080879,20.3052678 L3.6179596,20.3052678 C1.39859136,20.3052678 0,21.7038592 0,23.9232274 L0,39.5600395 C0,41.7794077 1.39859136,43.1779991 3.6179596,43.1779991 L30.0229907,43.1779991 C32.2330142,43.1779991 33.6316056,41.7794077 33.6316056,39.5600395 L33.6316056,29.0488 L30.269068,29.0488 L30.269068,38.229976 L30.2706254,38.229976 Z"
|
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
id="路径"></path>
|
<g id="编组" fill="#2C2C2C" fill-rule="nonzero">
|
||||||
<path d="M15.4763873,27.703162 C15.4755615,27.5244652 15.54618,27.3528466 15.6725391,27.2264875 C15.7988982,27.1001284 15.9705168,27.0295099 16.1492136,27.0303357 L17.4948516,27.0303357 C17.8639676,27.0303357 18.1676706,27.3324885 18.1676706,27.703162 L18.1676706,35.7816624 C18.1668142,36.1528948 17.866084,36.453625 17.4948516,36.4544887 L16.1492136,36.4544887 C15.9705168,36.4553145 15.7988982,36.384696 15.6725391,36.2583369 C15.54618,36.1319778 15.4755615,35.9603592 15.4763873,35.7816624 L15.4763873,27.703162 L15.4763873,27.703162 Z M39.5047548,15.4429045 C39.1907428,15.1267586 38.7635683,14.9489692 38.3179768,14.9489692 C37.8723853,14.9489692 37.4452109,15.1267586 37.1311989,15.4429045 L22.3821341,30.1919693 C21.9578609,30.6170771 21.7926087,31.2362399 21.9486262,31.8162251 C22.1046438,32.3962103 22.5582284,32.8489044 23.1385191,33.0037818 C23.7188098,33.1586592 24.3376467,32.9921905 24.7619198,32.5670827 L39.5047548,17.8242477 C39.8217544,17.5091076 40,17.0805683 40,16.6335761 C40,16.1865839 39.8217544,15.7580447 39.5047548,15.4429045 L39.5047548,15.4429045 Z M10.0876127,20.3068253 L10.0876127,10.6973501 C10.0876127,6.72273414 13.107511,3.48790758 16.8204751,3.48790758 C20.5334393,3.48790758 23.5455503,6.72273414 23.5455503,10.6973501 L23.5455503,16.2730261 L26.9080879,16.2730261 L26.9080879,10.2176551 C26.9532494,6.58243177 25.0397574,3.20385387 21.8988728,1.37308023 C18.7579882,-0.45769341 14.8751748,-0.45769341 11.7342902,1.37308023 C8.59340562,3.20385387 6.67991361,6.58243177 6.72507515,10.2176551 L6.72507515,23.6678054 L26.9080879,23.6678054 L26.9080879,20.3052678 L10.0876127,20.3052678 L10.0876127,20.3068253 Z"
|
<path d="M145.273438,109.375 C142.304688,109.375 139.882812,111.796875 139.882812,114.765625 L139.882812,164.316406 L10.78125,164.316406 L10.78125,87.3046875 L91.1132813,87.3046875 C94.0820313,87.3046875 96.5039063,84.8828125 96.5039063,81.9140625 C96.5039063,78.9453125 94.0820313,76.5234375 91.1132813,76.5234375 L31.25,76.5234375 L31.25,50.4296875 C31.25,28.5742188 49.0234375,10.8007813 70.8984375,10.8007813 L78.90625,10.8007813 C100.761719,10.8007813 118.535156,28.5742188 118.535156,50.4296875 L118.535156,54.609375 L118.554688,54.609375 C118.730469,57.421875 121.074219,59.6484375 123.925781,59.6484375 C126.894531,59.6484375 129.316406,57.2460938 129.316406,54.2578125 C129.316406,54.1210938 129.316406,53.984375 129.296875,53.8476563 L129.296875,50.4101563 C129.296875,22.6757813 106.621094,0 78.8867188,0 L70.8789063,0 C43.1445313,0 20.46875,22.6757813 20.46875,50.4101563 L20.46875,76.5039063 L5.390625,76.5039063 C2.421875,76.5039063 0,78.9257812 0,81.8945313 L0,170 C0,172.96875 2.421875,175.390625 5.390625,175.390625 C6.03515625,175.390625 6.66015625,175.273438 7.24609375,175.058594 L143.417969,175.058594 C144.003906,175.273438 144.609375,175.390625 145.273438,175.390625 C148.242188,175.390625 150.664063,172.96875 150.664063,170 L150.664063,114.746094 C150.644531,111.796875 148.222656,109.375 145.273438,109.375 Z" id="路径"></path>
|
||||||
id="形状"></path>
|
<path d="M159.648438,56.9335938 C157.558594,54.84375 154.121094,54.84375 152.03125,56.9335938 L76.8945312,132.050781 C74.8046875,134.140625 74.8046875,137.578125 76.8945312,139.667969 C78.984375,141.757813 82.421875,141.757813 84.5117188,139.667969 L159.628906,64.5507813 C161.738281,62.4414063 161.738281,59.0234375 159.648438,56.9335938 Z" id="路径"></path>
|
||||||
</svg>
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.1 KiB |
5
src/icons/svg/disable.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg t="1736148729915" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="42152" width="200" height="200">
|
||||||
|
<path d="M512 0a512 512 0 1 0 0 1024A512 512 0 0 0 512 0zM113.777778 512a398.222222 398.222222 0 0 1 398.222222-398.222222c89.429333 0 171.690667 29.809778 238.193778 79.587555L193.422222 750.136889A395.377778 395.377778 0 0 1 113.777778 512z m398.222222 398.222222a395.719111 395.719111 0 0 1-238.136889-79.587555L830.577778 273.806222A396.060444 396.060444 0 0 1 910.222222 512a398.222222 398.222222 0 0 1-398.222222 398.222222z"
|
||||||
|
fill="#515151" p-id="42153"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 642 B |
10
src/icons/svg/discovery.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="175px" height="172px" viewBox="0 0 175 172" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>编组</title>
|
||||||
|
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="编组" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path d="M173.785375,165.454861 L123.361962,115.009064 C137.448412,98.2738101 143.521494,75.2018529 137.651474,52.4540759 C128.018006,15.1217873 89.8109099,-7.40014283 52.4770224,2.22233315 C34.3936829,6.89116772 19.2091781,18.3220175 9.72181048,34.4071119 C0.234442898,50.4958037 -2.41955769,69.3134455 2.24727825,87.3967849 C6.91431399,105.480124 18.3433652,120.666428 34.432057,130.153995 C45.3350648,136.583636 57.4928218,139.875204 69.8298572,139.875204 C75.6914829,139.875204 81.5950804,139.12991 87.4219298,137.628528 C99.4151984,134.532427 109.877505,128.483928 118.240035,120.466763 L168.495562,170.744875 C169.224268,171.475579 170.183418,171.840932 171.140568,171.840932 C172.097719,171.840932 173.054871,171.475579 173.785575,170.744875 C175.246584,169.283466 175.246584,166.916271 173.785375,165.454861 Z M85.551198,130.380442 C69.402147,134.552413 52.5955419,132.177823 38.2312808,123.705967 C23.865021,115.234111 13.6597404,101.677103 9.49156634,85.5262533 C5.32319251,69.3790011 7.69418489,52.5743947 16.1660409,38.2099337 C24.6396958,23.8418753 38.1985029,13.6347956 54.3475538,9.47021928 C59.5606195,8.12213334 64.7902738,7.47916928 69.9393825,7.47916928 C97.7275421,7.47916928 123.148307,26.2018753 130.407185,54.3242083 C135.890068,75.5734013 129.699865,97.1395794 115.85805,112.213559 C115.752321,112.298502 115.649591,112.388841 115.551457,112.486774 C115.398161,112.64007 115.264852,112.804958 115.143734,112.976043 C107.36301,121.170886 97.2760486,127.355893 85.551198,130.380442 Z" id="形状"></path>
|
||||||
|
<path d="M91.6046937,27.1300456 C86.1485929,23.9152253 79.7609238,23.0164347 73.6272824,24.5947646 C71.6270386,25.1136128 70.4234546,27.1520308 70.940304,29.1538736 C71.4553548,31.1559162 73.492174,32.3505062 75.4976142,31.8426505 C84.1721329,29.6031689 93.0403202,34.8382195 95.2798019,43.5073419 C95.7145072,45.1951976 97.2342767,46.3130396 98.9001472,46.3130396 C99.2089378,46.3130396 99.5229249,46.2764644 99.8371118,46.1925212 C101.837156,45.6774705 103.04094,43.6388527 102.52409,41.6370099 C100.940364,35.5031687 97.0625933,30.3484636 91.6046937,27.1300456 Z" id="路径"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |