Compare commits
90 Commits
v4.1
...
pr@dev@per
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
150795ebac | ||
|
|
a636bb2037 | ||
|
|
9b6f54c1ed | ||
|
|
611341307b | ||
|
|
f8479c53ff | ||
|
|
e25bf46659 | ||
|
|
6d07307e56 | ||
|
|
6fb7fe8fa1 | ||
|
|
19cd497095 | ||
|
|
d858489367 | ||
|
|
341a30ba06 | ||
|
|
8390fb7429 | ||
|
|
5389f1d011 | ||
|
|
bde642570f | ||
|
|
3e7b970fe7 | ||
|
|
2421e822b4 | ||
|
|
b2e474e3f6 | ||
|
|
541836390a | ||
|
|
bb92e3f22b | ||
|
|
c9ad797b40 | ||
|
|
2a08310efc | ||
|
|
3b2803b9a1 | ||
|
|
1b8ac9112e | ||
|
|
9cc3dd4de9 | ||
|
|
120ef70eb1 | ||
|
|
541f4ebc62 | ||
|
|
a47636abc7 | ||
|
|
78f6f4b36a | ||
|
|
5abc0b77cf | ||
|
|
464638e782 | ||
|
|
37cfeb2077 | ||
|
|
44e297f01a | ||
|
|
febc283e36 | ||
|
|
dae33f55e8 | ||
|
|
c62cd27690 | ||
|
|
83443f8187 | ||
|
|
cb46f393e0 | ||
|
|
ddf5ac2151 | ||
|
|
88173f852a | ||
|
|
d5f16e90e2 | ||
|
|
d1c0aca4ff | ||
|
|
a42edf17ec | ||
|
|
dac5dfcd1c | ||
|
|
8e6ca146e1 | ||
|
|
9c06d36eab | ||
|
|
ab7bd574f8 | ||
|
|
6007dc8621 | ||
|
|
a0e7c48dc9 | ||
|
|
71dea791bf | ||
|
|
e1b9184f41 | ||
|
|
730d47f02a | ||
|
|
e82ec68935 | ||
|
|
e8e5975d7f | ||
|
|
3cb9dec978 | ||
|
|
c025441075 | ||
|
|
050b50fa74 | ||
|
|
e26befabc3 | ||
|
|
8545fc6136 | ||
|
|
e343e0df9d | ||
|
|
74519e6f3c | ||
|
|
1305f90372 | ||
|
|
bbc3f53c0a | ||
|
|
aa605adbc7 | ||
|
|
49ea7d0969 | ||
|
|
4221bdb2ab | ||
|
|
af4010e299 | ||
|
|
9b7c4ed353 | ||
|
|
879df90503 | ||
|
|
41f841532f | ||
|
|
0b9f47dd84 | ||
|
|
d32a376e8c | ||
|
|
0f8a8845df | ||
|
|
8bb2c66b99 | ||
|
|
b3d0be2f60 | ||
|
|
5a94ddd976 | ||
|
|
7b9627a80b | ||
|
|
5fcfecc060 | ||
|
|
f4d7a2283c | ||
|
|
40bb8410d3 | ||
|
|
45344ac620 | ||
|
|
5b894c9667 | ||
|
|
d89bd15b6d | ||
|
|
5e640dd45c | ||
|
|
09aa750794 | ||
|
|
744b215800 | ||
|
|
f4e11da053 | ||
|
|
2e6b5706d5 | ||
|
|
440a5b27ef | ||
|
|
932e16844e | ||
|
|
aff2e439dd |
13
.github/workflows/build-base-image.yml
vendored
@@ -15,8 +15,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Lock Pull Request
|
||||||
|
run: |
|
||||||
|
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-d '{"state":"pending", "description":"Action running, merge disabled", "context":"Lock PR"}' \
|
||||||
|
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@@ -59,3 +64,9 @@ jobs:
|
|||||||
git push
|
git push
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Unlock Pull Request
|
||||||
|
run: |
|
||||||
|
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-d '{"state":"success", "description":"Action running, merge disabled", "context":"Lock PR"}' \
|
||||||
|
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
|
||||||
|
|||||||
2
.github/workflows/release-drafter.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
tag: ${{ steps.get_version.outputs.TAG }}
|
tag: ${{ steps.get_version.outputs.TAG }}
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16.20'
|
node-version: '20.15'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: Build web
|
- name: Build web
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
"normalize.css": "7.0.0",
|
"normalize.css": "7.0.0",
|
||||||
"npm": "^7.8.0",
|
"npm": "^7.8.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"path-to-regexp": "2.4.0",
|
"path-to-regexp": "3.3.0",
|
||||||
"v-sanitize": "^0.0.13",
|
"v-sanitize": "^0.0.13",
|
||||||
"vue": "2.6.10",
|
"vue": "2.6.10",
|
||||||
"vue-codemirror": "4.0.6",
|
"vue-codemirror": "4.0.6",
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"script-ext-html-webpack-plugin": "2.1.3",
|
"script-ext-html-webpack-plugin": "2.1.3",
|
||||||
"script-loader": "0.7.2",
|
"script-loader": "0.7.2",
|
||||||
"serve-static": "^1.13.2",
|
"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.2",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export function testEmailSetting(data) {
|
|||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importLicense(formData) {
|
export function importLicense(formData) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/v1/xpack/license/import',
|
url: '/api/v1/xpack/license/import',
|
||||||
@@ -25,6 +26,7 @@ export function importLicense(formData) {
|
|||||||
data: formData
|
data: formData
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function testLdapSetting(data, refresh = true) {
|
export function testLdapSetting(data, refresh = true) {
|
||||||
let url = '/api/v1/settings/ldap/testing/config/'
|
let url = '/api/v1/settings/ldap/testing/config/'
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
@@ -96,9 +98,17 @@ export function getPublicSettings(isOpen) {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLogo() {
|
export function getLogo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/v1/xpack/interface/setting/',
|
url: '/api/v1/xpack/interface/setting/',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPreference() {
|
||||||
|
return request({
|
||||||
|
url: '/api/v1/users/preference/?category=luna',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 961 B After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 2.4 KiB |
BIN
src/assets/img/icons/h3c.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 940 B |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -37,6 +37,10 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '/api/v1/assets/assets/'
|
default: '/api/v1/assets/assets/'
|
||||||
},
|
},
|
||||||
|
defaultPageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10
|
||||||
|
},
|
||||||
baseNodeUrl: {
|
baseNodeUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '/api/v1/assets/nodes/'
|
default: '/api/v1/assets/nodes/'
|
||||||
@@ -70,6 +74,7 @@ export default {
|
|||||||
value: iValue,
|
value: iValue,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
|
defaultPageSize: this.defaultPageSize,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: this.baseUrl,
|
url: this.baseUrl,
|
||||||
transformOption: (item) => {
|
transformOption: (item) => {
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '/api/v1/assets/assets/'
|
default: '/api/v1/assets/assets/'
|
||||||
},
|
},
|
||||||
|
typeUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '/api/v1/assets/nodes/category/tree/'
|
||||||
|
},
|
||||||
nodeUrl: {
|
nodeUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '/api/v1/assets/nodes/'
|
default: '/api/v1/assets/nodes/'
|
||||||
@@ -105,9 +109,9 @@ export default {
|
|||||||
showAssets: false,
|
showAssets: false,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
customTreeHeaderName: this.$t('TypeTree'),
|
customTreeHeaderName: this.$t('TypeTree'),
|
||||||
url: '/api/v1/assets/nodes/category/tree/',
|
url: this.typeUrl,
|
||||||
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
|
||||||
treeUrl: `/api/v1/assets/nodes/category/tree/?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
|
||||||
callback: {
|
callback: {
|
||||||
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default {
|
|||||||
formatterData = data
|
formatterData = data
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span>{formatterData}</span>
|
<span style={{ whiteSpace: 'pre-wrap', lineHeight: '1.2' }}>{formatterData}</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (this.value instanceof Array) {
|
if (this.value instanceof Array) {
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ export default {
|
|||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
&:has(.el-table) {
|
&:has(.el-table) {
|
||||||
background: #f3f3f4;
|
background: #f3f3f4;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
>
|
>
|
||||||
<span
|
<div
|
||||||
v-for="(group, i) in groups"
|
v-for="(group, i) in groups"
|
||||||
:key="'group-'+group.name"
|
:key="'group-'+group.name"
|
||||||
:slot="'id:'+group.name"
|
:slot="'id:'+group.name"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
:index="i"
|
:index="i"
|
||||||
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</div>
|
||||||
</DataForm>
|
</DataForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
|
|||||||
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
|
||||||
import { assignIfNot, toSentenceCase } from '@/utils/common'
|
import { assignIfNot, toSentenceCase } from '@/utils/common'
|
||||||
import TagInput from '@/components/Form/FormFields/TagInput.vue'
|
import TagInput from '@/components/Form/FormFields/TagInput.vue'
|
||||||
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
|
|
||||||
import i18n from '@/i18n/i18n'
|
import i18n from '@/i18n/i18n'
|
||||||
|
|
||||||
export class FormFieldGenerator {
|
export class FormFieldGenerator {
|
||||||
@@ -135,9 +134,6 @@ export class FormFieldGenerator {
|
|||||||
case 'comment':
|
case 'comment':
|
||||||
field.el.type = 'textarea'
|
field.el.type = 'textarea'
|
||||||
break
|
break
|
||||||
case 'users':
|
|
||||||
field.component = TransferSelect
|
|
||||||
field.el.label = field.label
|
|
||||||
}
|
}
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
@@ -197,6 +193,24 @@ export class FormFieldGenerator {
|
|||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setChoicesTips(field, fieldMeta, fieldRemoteMeta) {
|
||||||
|
// 设置 checkbox 的 tips
|
||||||
|
if (['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
|
||||||
|
field.options.map(option => {
|
||||||
|
if (!option.tip && field.tips) {
|
||||||
|
option.tip = field.tips[option.value]
|
||||||
|
}
|
||||||
|
if (!option.tip) {
|
||||||
|
const match = option.label.match(/^(.+?)\s*\((.*?)\)$/)
|
||||||
|
if (match) {
|
||||||
|
option.label = match[1]
|
||||||
|
option.tip = match[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
afterGenerateField(field) {
|
afterGenerateField(field) {
|
||||||
field.label = toSentenceCase(field.label)
|
field.label = toSentenceCase(field.label)
|
||||||
|
|
||||||
@@ -204,15 +218,7 @@ export class FormFieldGenerator {
|
|||||||
field.el.placeholder = field.placeholder
|
field.el.placeholder = field.placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 checkbox 的 tips
|
this.setChoicesTips(field)
|
||||||
if (field.tips && ['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
|
|
||||||
field.options.map(option => {
|
|
||||||
if (!option.tip && field.tips[option.value]) {
|
|
||||||
option.tip = field.tips[option.value]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
placement="right"
|
placement="right"
|
||||||
popper-class="help-tips"
|
popper-class="help-tips"
|
||||||
>
|
>
|
||||||
<div slot="content" v-sanitize="data.helpTip" /> <!-- Noncompliant -->
|
<div slot="content" v-sanitize="data.helpTip" class="help-tip-content" /> <!-- Noncompliant -->
|
||||||
<i class="fa fa-question-circle-o help-tip-icon" />
|
<i class="fa fa-question-circle-o help-tip-icon" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
@@ -322,4 +322,9 @@ export default {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.help-tip-content {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
defaultValue: 24,
|
||||||
displayMapper: {
|
displayMapper: {
|
||||||
'second': this.$t('Second'), // 'sec' is the default value of 'unit
|
'second': this.$t('Second'), // 'sec' is the default value of 'unit
|
||||||
'min': this.$t('Minute'), // 'min' is the default value of 'unit
|
'min': this.$t('Minute'), // 'min' is the default value of 'unit
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ export default {
|
|||||||
type: 'input-number',
|
type: 'input-number',
|
||||||
el: {
|
el: {
|
||||||
min: 8,
|
min: 8,
|
||||||
max: 30
|
max: 36,
|
||||||
|
size: 'mini'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -125,16 +125,19 @@ export default {
|
|||||||
allowCreate: {
|
allowCreate: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
defaultPageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const vm = this
|
const vm = this
|
||||||
const defaultPageSize = 10
|
|
||||||
const defaultParams = {
|
const defaultParams = {
|
||||||
search: '',
|
search: '',
|
||||||
page: 1,
|
page: 1,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
pageSize: defaultPageSize
|
pageSize: vm.defaultPageSize
|
||||||
}
|
}
|
||||||
// 设置axios全局报错提示不显示
|
// 设置axios全局报错提示不显示
|
||||||
const validateStatus = (status) => {
|
const validateStatus = (status) => {
|
||||||
@@ -194,7 +197,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
iAjax() {
|
iAjax() {
|
||||||
const defaultPageSize = 10
|
|
||||||
const defaultMakeParams = (params) => {
|
const defaultMakeParams = (params) => {
|
||||||
const page = params.page || 1
|
const page = params.page || 1
|
||||||
const offset = (page - 1) * params.pageSize
|
const offset = (page - 1) * params.pageSize
|
||||||
@@ -237,7 +239,7 @@ export default {
|
|||||||
}
|
}
|
||||||
const defaultAjax = {
|
const defaultAjax = {
|
||||||
url: '',
|
url: '',
|
||||||
pageSize: defaultPageSize,
|
pageSize: this.defaultPageSize,
|
||||||
makeParams: defaultMakeParams,
|
makeParams: defaultMakeParams,
|
||||||
transformOption: defaultTransformOption,
|
transformOption: defaultTransformOption,
|
||||||
processResults: defaultProcessResults,
|
processResults: defaultProcessResults,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default {
|
|||||||
patterns.push([/\d/, i18n.t('NUMBER_REQUIRED')])
|
patterns.push([/\d/, i18n.t('NUMBER_REQUIRED')])
|
||||||
}
|
}
|
||||||
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
|
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
|
||||||
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]")
|
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
|
||||||
patterns.push([pattern, i18n.t('SPECIAL_CHAR_REQUIRED')])
|
patterns.push([pattern, i18n.t('SPECIAL_CHAR_REQUIRED')])
|
||||||
}
|
}
|
||||||
for (const [pattern, msg] of patterns) {
|
for (const [pattern, msg] of patterns) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
v-if="showColumnSettingPopover"
|
v-if="showColumnSettingPopover"
|
||||||
:cancel-title="$tc('RestoreDefault')"
|
:cancel-title="$tc('RestoreDefault')"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
:title="$tc('TableSetting')"
|
:title="$tc('ListPreference')"
|
||||||
:visible.sync="showColumnSettingPopover"
|
:visible.sync="showColumnSettingPopover"
|
||||||
top="10%"
|
top="10%"
|
||||||
width="50%"
|
width="50%"
|
||||||
|
|||||||
@@ -44,8 +44,9 @@
|
|||||||
import DataTable from '@/components/Table/DataTable/index.vue'
|
import DataTable from '@/components/Table/DataTable/index.vue'
|
||||||
import { getUpdateObjURL } from '@/utils/common'
|
import { getUpdateObjURL } from '@/utils/common'
|
||||||
import { sleep } from '@/utils/time'
|
import { sleep } from '@/utils/time'
|
||||||
import { EditableInputFormatter, StatusFormatter } from '@/components/Table/TableFormatters'
|
import { EditableInputFormatter } from '@/components/Table/TableFormatters'
|
||||||
import { encryptPassword } from '@/utils/crypto'
|
import { encryptPassword } from '@/utils/crypto'
|
||||||
|
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ImportTable',
|
name: 'ImportTable',
|
||||||
@@ -223,38 +224,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
generateTableColumns(tableTitles, tableData) {
|
generateTableColumns(tableTitles, tableData) {
|
||||||
const vm = this
|
const columns = [{ ...getStatusColumnMeta.bind(this)().status }]
|
||||||
const columns = [{
|
|
||||||
prop: '@status',
|
|
||||||
label: vm.$t('Status'),
|
|
||||||
width: '80px',
|
|
||||||
align: 'center',
|
|
||||||
formatter: StatusFormatter,
|
|
||||||
formatterArgs: {
|
|
||||||
faChoices: {
|
|
||||||
ok: 'fa-check text-primary',
|
|
||||||
error: 'fa-times text-danger',
|
|
||||||
pending: 'fa-clock-o'
|
|
||||||
},
|
|
||||||
getChoicesKey(val) {
|
|
||||||
if (val === 'ok' || val === 'pending') {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return 'error'
|
|
||||||
},
|
|
||||||
getTip(val) {
|
|
||||||
if (val === 'ok') {
|
|
||||||
return vm.$t('Success')
|
|
||||||
} else if (val === 'pending') {
|
|
||||||
return vm.$t('Pending')
|
|
||||||
} else if (val && val.name === 'error') {
|
|
||||||
return val.error
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
hasTips: true
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
for (const item of tableTitles) {
|
for (const item of tableTitles) {
|
||||||
const dataItemLens = tableData.map(d => {
|
const dataItemLens = tableData.map(d => {
|
||||||
if (!d) {
|
if (!d) {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'actionSetting',
|
name: 'actionSetting',
|
||||||
icon: 'system-setting',
|
icon: 'system-setting',
|
||||||
tip: this.$t('TableSetting'),
|
tip: this.$t('ListPreference'),
|
||||||
has: this.hasColumnSetting,
|
has: this.hasColumnSetting,
|
||||||
callback: this.handleTableSettingClick.bind(this)
|
callback: this.handleTableSettingClick.bind(this)
|
||||||
},
|
},
|
||||||
|
|||||||
40
src/components/Table/ListTable/TableAction/const.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { StatusFormatter } from '@/components/Table/TableFormatters'
|
||||||
|
import i18n from '@/i18n/i18n'
|
||||||
|
|
||||||
|
export const getStatusColumnMeta = (prop = '@status') => {
|
||||||
|
return {
|
||||||
|
status: {
|
||||||
|
prop: prop,
|
||||||
|
label: i18n.t('Status'),
|
||||||
|
width: '80px',
|
||||||
|
align: 'center',
|
||||||
|
formatter: StatusFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
faChoices: {
|
||||||
|
ok: 'fa-check text-primary',
|
||||||
|
error: 'fa-times text-danger',
|
||||||
|
pending: 'fa-clock-o'
|
||||||
|
},
|
||||||
|
getChoicesKey: (val) => {
|
||||||
|
if (val === 'ok' || val === 'pending') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return 'error'
|
||||||
|
},
|
||||||
|
getTip: (val) => {
|
||||||
|
if (val === 'ok') {
|
||||||
|
return i18n.t('Success')
|
||||||
|
} else if (val === 'pending') {
|
||||||
|
return i18n.t('Pending')
|
||||||
|
} else if ((val && val.name === 'error') || val.error !== undefined) {
|
||||||
|
return val.error
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
hasTips: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getStatusColumnMeta
|
||||||
@@ -189,22 +189,23 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.urlUpdated[this.tableUrl] = location.href
|
this.$set(this.urlUpdated, this.tableUrl, location.href)
|
||||||
},
|
},
|
||||||
deactivated() {
|
deactivated() {
|
||||||
this.isDeactivated = true
|
this.isDeactivated = true
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
this.isDeactivated = false
|
this.$nextTick(() => {
|
||||||
const preURL = this.urlUpdated[this.tableUrl]
|
this.isDeactivated = false
|
||||||
if (!preURL || preURL === location.href) {
|
const cleanUrl = this.tableUrl.split('?')[0]
|
||||||
return
|
const preURL = this.urlUpdated[cleanUrl]
|
||||||
}
|
|
||||||
this.urlUpdated[this.tableUrl] = location.href
|
if (!preURL || preURL === location.href) return
|
||||||
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
|
|
||||||
setTimeout(() => {
|
this.$set(this.urlUpdated, this.tableUrl, location.href)
|
||||||
|
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
|
||||||
this.reloadTable()
|
this.reloadTable()
|
||||||
}, 500)
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleActionInitialDone() {
|
handleActionInitialDone() {
|
||||||
|
|||||||
@@ -131,6 +131,16 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {},
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
cellValue: {
|
||||||
|
handler(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.initial = this.formatterArgs.getLabels(this.cellValue)
|
||||||
|
this.iLabels = [...this.initial]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initial = this.formatterArgs.getLabels(this.cellValue)
|
this.initial = this.formatterArgs.getLabels(this.cellValue)
|
||||||
this.iLabels = [...this.initial]
|
this.iLabels = [...this.initial]
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
showRenameBtn: false,
|
showRenameBtn: false,
|
||||||
drag: {
|
drag: {
|
||||||
isCopy: false,
|
isCopy: false,
|
||||||
isMove: true
|
isMove: !this.$store.getters.currentOrgIsRoot
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
|
|||||||
@@ -35,13 +35,29 @@ export default {
|
|||||||
]),
|
]),
|
||||||
announcement() {
|
announcement() {
|
||||||
const ann = this.publicSettings.ANNOUNCEMENT
|
const ann = this.publicSettings.ANNOUNCEMENT
|
||||||
return { id: ann['ID'], subject: ann['SUBJECT'], content: ann['CONTENT'], link: ann['LINK'] }
|
return {
|
||||||
|
id: ann['ID'],
|
||||||
|
subject: ann['SUBJECT'],
|
||||||
|
content: ann['CONTENT'],
|
||||||
|
link: ann['LINK'],
|
||||||
|
date_start: ann['DATE_START'],
|
||||||
|
date_end: ann['DATE_END']
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enabled() {
|
enabled() {
|
||||||
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject)
|
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject) && this.isDateValid
|
||||||
},
|
},
|
||||||
title() {
|
title() {
|
||||||
return this.$t('Announcement') + ': ' + this.announcement.subject
|
return this.$t('Announcement') + ': ' + this.announcement.subject
|
||||||
|
},
|
||||||
|
isDateValid() {
|
||||||
|
if (this.announcement.date_start === undefined || this.announcement.date_end === undefined) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const now = new Date()
|
||||||
|
const start = new Date(this.announcement.date_start)
|
||||||
|
const end = new Date(this.announcement.date_end)
|
||||||
|
return now >= start && now <= end
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ export default {
|
|||||||
query[k] = v
|
query[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let key
|
||||||
if (this.$route.name.toLowerCase().includes('list')) {
|
if (this.$route.name.toLowerCase().includes('list')) {
|
||||||
return _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
|
key = _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
|
||||||
} else {
|
} else {
|
||||||
return new Date().getTime()
|
key = new Date().getTime()
|
||||||
}
|
}
|
||||||
|
return key
|
||||||
},
|
},
|
||||||
chatAiEnabled() {
|
chatAiEnabled() {
|
||||||
return this.publicSettings?.CHAT_AI_ENABLED
|
return this.publicSettings?.CHAT_AI_ENABLED
|
||||||
|
|||||||
@@ -66,11 +66,15 @@ export default {
|
|||||||
break
|
break
|
||||||
case 'logout':
|
case 'logout':
|
||||||
this.logout()
|
this.logout()
|
||||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
logout() {
|
async logout() {
|
||||||
|
const currentOrg = this.$store.getters.currentOrg
|
||||||
|
if (currentOrg.autoEnter) {
|
||||||
|
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
||||||
|
}
|
||||||
|
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
|
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="msg-detail-txt">
|
<div class="msg-detail-txt">
|
||||||
<span v-sanitize="currentMsg.content.message" />
|
<MarkDown :value="currentMsg.content.message" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -80,10 +80,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import { toSafeLocalDateStr } from '@/utils/time'
|
import { toSafeLocalDateStr } from '@/utils/time'
|
||||||
import Dialog from '@/components/Dialog'
|
import Dialog from '@/components/Dialog'
|
||||||
|
import MarkDown from '@/components/Widgets/MarkDown'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SiteMessages',
|
name: 'SiteMessages',
|
||||||
components: { Dialog },
|
components: {
|
||||||
|
Dialog,
|
||||||
|
MarkDown
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
show: false,
|
show: false,
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export default {
|
|||||||
|
|
||||||
// 未找到与之对应的
|
// 未找到与之对应的
|
||||||
& ::v-deep .el-submenu__title {
|
& ::v-deep .el-submenu__title {
|
||||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
line-height: $headerHeight;
|
line-height: $headerHeight;
|
||||||
height: $headerHeight;
|
height: $headerHeight;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
<el-alert v-if="helpMessage" type="success">
|
<el-alert v-if="helpMessage" type="success">
|
||||||
<span v-sanitize="helpMessage" class="announcement-main" />
|
<span v-sanitize="helpMessage" class="announcement-main" />
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<transition v-if="!loading" appear mode="out-in" name="fade-transform">
|
<transition appear mode="out-in" name="fade-transform">
|
||||||
<slot>
|
<slot>
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="computeActiveComponent" />
|
<component :is="computeActiveComponent" />
|
||||||
@@ -83,16 +83,18 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: false,
|
||||||
toSentenceCase: toSentenceCase
|
toSentenceCase: toSentenceCase,
|
||||||
|
activeTab: this.activeMenu
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
iActiveMenu: {
|
iActiveMenu: {
|
||||||
get() {
|
get() {
|
||||||
return this.activeMenu
|
return this.activeTab
|
||||||
},
|
},
|
||||||
set(item) {
|
set(item) {
|
||||||
|
this.activeTab = item
|
||||||
this.$emit('update:activeMenu', item)
|
this.$emit('update:activeMenu', item)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -119,16 +121,13 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route(to, from) {
|
$route(to, from) {
|
||||||
const activeTab = to.query?.tab
|
// 好像没必要
|
||||||
if (activeTab && this.iActiveMenu !== activeTab) {
|
// const activeTab = to.query?.tab
|
||||||
this.iActiveMenu = activeTab
|
// if (activeTab && this.iActiveMenu !== activeTab) {
|
||||||
}
|
// this.iActiveMenu = activeTab
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
activated() {
|
|
||||||
this.iActiveMenu = this.getPropActiveTab()
|
|
||||||
this.loading = false
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
this.iActiveMenu = this.getPropActiveTab()
|
this.iActiveMenu = this.getPropActiveTab()
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@@ -136,15 +135,8 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
handleTabClick(tab) {
|
handleTabClick(tab) {
|
||||||
this.$emit('tab-click', tab)
|
this.$emit('tab-click', tab)
|
||||||
this.$emit('update:activeMenu', tab.name)
|
this.iActiveMenu = tab.name
|
||||||
|
|
||||||
this.$cookie.set(this.$route.path, tab.name, 1)
|
this.$cookie.set(this.$route.path, tab.name, 1)
|
||||||
|
|
||||||
if (this.$router.currentRoute.query[this.$route.path]) {
|
|
||||||
this.$router.push({
|
|
||||||
query: { ...this.$route.query, [this.$route.path]: '' }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getPropActiveTab() {
|
getPropActiveTab() {
|
||||||
let activeTab = ''
|
let activeTab = ''
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ body {
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: auto;
|
-webkit-font-smoothing: auto;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.428;
|
line-height: 1.428;
|
||||||
}
|
}
|
||||||
@@ -593,3 +593,9 @@ li.rmenu i.fa {
|
|||||||
height: 6px; /* 设置水平滚动条的高度 */
|
height: 6px; /* 设置水平滚动条的高度 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.black-theme-popover {
|
||||||
|
width: 300px;
|
||||||
|
max-height: 700px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ $single-menu-height: 38px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-menu--vertical {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.el-menu {
|
.el-menu {
|
||||||
border-right: none !important;
|
border-right: none !important;
|
||||||
background-color: inherit !important;
|
background-color: inherit !important;
|
||||||
@@ -139,6 +143,10 @@ $single-menu-height: 38px;
|
|||||||
.nest-menu .level2-menu {
|
.nest-menu .level2-menu {
|
||||||
line-height: $single-menu-height;
|
line-height: $single-menu-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-tooltip {
|
||||||
|
width: 55px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ export function getErrorResponseMsg(error) {
|
|||||||
}).filter(i => i).join('; ')
|
}).filter(i => i).join('; ')
|
||||||
} else if (typeof data === 'string') {
|
} else if (typeof data === 'string') {
|
||||||
return data
|
return data
|
||||||
|
} else {
|
||||||
|
msg = error.toString()
|
||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
@@ -310,4 +312,26 @@ export function toSentenceCase(string) {
|
|||||||
}).join(' ')
|
}).join(' ')
|
||||||
return s[0].toUpperCase() + s.slice(1)
|
return s[0].toUpperCase() + s.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { BASE_URL }
|
export { BASE_URL }
|
||||||
|
|
||||||
|
export function openNewWindow(url) {
|
||||||
|
let count
|
||||||
|
let top = 50
|
||||||
|
count = parseInt(window.sessionStorage.getItem('newWindowCount'), 10)
|
||||||
|
if (isNaN(count)) {
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
let left = 100 + count * 100
|
||||||
|
top = 50 + count * 50
|
||||||
|
if (left + screen.width / 3 > screen.width) {
|
||||||
|
// 支持两排足以
|
||||||
|
top = screen.height / 3
|
||||||
|
count = 1
|
||||||
|
left = 100
|
||||||
|
}
|
||||||
|
let params = 'toolbar=yes,scrollbars=yes,resizable=yes'
|
||||||
|
params = params + `,top=${top},left=${left},width=${screen.width / 3},height=${screen.height / 3}`
|
||||||
|
window.sessionStorage.setItem('newWindowCount', `${count + 1}`)
|
||||||
|
window.open(url, '_blank', params)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ async function checkLogin({ to, from, next }) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
Vue.$log.error(e)
|
Vue.$log.error(e)
|
||||||
const status = e.response.status
|
const status = e.response.status
|
||||||
|
if (store.getters.currentOrg.autoEnter) {
|
||||||
|
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
||||||
|
}
|
||||||
if (status === 401 || status === 403) {
|
if (status === 401 || status === 403) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location = process.env.VUE_APP_LOGIN_PATH
|
window.location = process.env.VUE_APP_LOGIN_PATH
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default {
|
|||||||
initial: {
|
initial: {
|
||||||
is_periodic: false,
|
is_periodic: false,
|
||||||
password_rules: {
|
password_rules: {
|
||||||
length: 16
|
length: 36
|
||||||
},
|
},
|
||||||
interval: 24,
|
interval: 24,
|
||||||
accounts: [],
|
accounts: [],
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
initial: {
|
initial: {
|
||||||
is_periodic: false,
|
is_periodic: false,
|
||||||
password_rules: {
|
password_rules: {
|
||||||
length: 30
|
length: 36
|
||||||
},
|
},
|
||||||
interval: 24,
|
interval: 24,
|
||||||
secret_type: 'password',
|
secret_type: 'password',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import InputWithUnit from '@/components/Form/FormFields/InputWithUnit.vue'
|
|||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
const validatorInterval = (rule, value, callback) => {
|
const validatorInterval = (rule, value, callback) => {
|
||||||
if (parseInt(value) < 1) {
|
if (isNaN(parseInt(value, 10)) || parseInt(value) < 1) {
|
||||||
return callback(new Error(i18n.t('EnsureThisValueIsGreaterThanOrEqualTo1')))
|
return callback(new Error(i18n.t('EnsureThisValueIsGreaterThanOrEqualTo1')))
|
||||||
}
|
}
|
||||||
callback()
|
callback()
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
reviewers: {
|
reviewers: {
|
||||||
hidden: (item) => !['review', 'warning'].includes(item.action),
|
hidden: (item) => !['review', 'warning', 'notify_and_warn'].includes(item.action),
|
||||||
rules: [rules.RequiredChange],
|
rules: [rules.RequiredChange],
|
||||||
el: {
|
el: {
|
||||||
value: [],
|
value: [],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<TabPage
|
<TabPage
|
||||||
:active-menu.sync="config.activeMenu"
|
:active-menu.sync="config.activeMenu"
|
||||||
:submenu="config.submenu"
|
:submenu="config.submenu"
|
||||||
@tab-click="handleTabClick"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -35,18 +34,6 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleTabClick(tab) {
|
|
||||||
const query = _.cloneDeep(this.$route.query)
|
|
||||||
const newQuery = {
|
|
||||||
...query,
|
|
||||||
tab: tab.name
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$router.replace({ query: newQuery })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,17 +25,17 @@ export default {
|
|||||||
const platform = this.$route.query.type
|
const platform = this.$route.query.type
|
||||||
const baseFields = [[this.$t('Basic'), ['db_name']]]
|
const baseFields = [[this.$t('Basic'), ['db_name']]]
|
||||||
let tlsFields = ['use_ssl', 'ca_cert']
|
let tlsFields = ['use_ssl', 'ca_cert']
|
||||||
switch (platform) {
|
const platformFieldsMap = {
|
||||||
case 'redis':
|
redis: ['client_cert', 'client_key'],
|
||||||
tlsFields = tlsFields.concat(['client_cert', 'client_key'])
|
postgresql: ['client_cert', 'client_key', 'pg_ssl_mode'],
|
||||||
break
|
mysql: ['client_cert', 'client_key', 'allow_invalid_cert'],
|
||||||
case 'mysql':
|
mongodb: ['client_key', 'allow_invalid_cert']
|
||||||
tlsFields = tlsFields.concat(['client_cert', 'client_key', 'allow_invalid_cert'])
|
|
||||||
break
|
|
||||||
case 'mongodb':
|
|
||||||
tlsFields = tlsFields.concat(['client_key', 'allow_invalid_cert'])
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (platformFieldsMap[platform]) {
|
||||||
|
tlsFields = tlsFields.concat(platformFieldsMap[platform])
|
||||||
|
}
|
||||||
|
|
||||||
if (tlsFields.length > 2) {
|
if (tlsFields.length > 2) {
|
||||||
const secureField = [
|
const secureField = [
|
||||||
this.$t('Secure'), tlsFields, 2
|
this.$t('Secure'), tlsFields, 2
|
||||||
@@ -52,7 +52,14 @@ export default {
|
|||||||
},
|
},
|
||||||
use_ssl: {
|
use_ssl: {
|
||||||
label: this.$t('UseSSL'),
|
label: this.$t('UseSSL'),
|
||||||
component: 'el-switch'
|
component: 'el-switch',
|
||||||
|
on: {
|
||||||
|
change: ([event], updateForm) => {
|
||||||
|
updateForm({
|
||||||
|
pg_ssl_mode: event ? 'require' : 'prefer'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
allow_invalid_cert: {
|
allow_invalid_cert: {
|
||||||
label: this.$t('AllowInvalidCert'),
|
label: this.$t('AllowInvalidCert'),
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ export default {
|
|||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: tableUrl,
|
url: tableUrl,
|
||||||
category: 'all'
|
category: 'all',
|
||||||
|
extraQuery: { 'order': '-date_updated' }
|
||||||
},
|
},
|
||||||
headerActions: {
|
headerActions: {
|
||||||
handleImportClick: ({ selectedRows }) => {
|
handleImportClick: ({ selectedRows }) => {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default {
|
|||||||
row['auto_config'].ansible_enabled &&
|
row['auto_config'].ansible_enabled &&
|
||||||
row['auto_config'].ping_enabled,
|
row['auto_config'].ping_enabled,
|
||||||
callback: ({ row }) => {
|
callback: ({ row }) => {
|
||||||
if (row.platform.name === 'Gateway') {
|
if (row.platform.name.startsWith('Gateway')) {
|
||||||
this.GatewayVisible = true
|
this.GatewayVisible = true
|
||||||
const port = row.protocols.find(item => item.name === 'ssh').port
|
const port = row.protocols.find(item => item.name === 'ssh').port
|
||||||
if (!port) {
|
if (!port) {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ export default {
|
|||||||
row?.auto_config?.ansible_enabled &&
|
row?.auto_config?.ansible_enabled &&
|
||||||
row?.auto_config?.ping_enabled,
|
row?.auto_config?.ping_enabled,
|
||||||
callback: ({ row }) => {
|
callback: ({ row }) => {
|
||||||
if (row.platform.name === 'Gateway') {
|
if (row.platform.name.startsWith('Gateway')) {
|
||||||
this.GatewayVisible = true
|
this.GatewayVisible = true
|
||||||
const port = row.protocols.find(item => item.name === 'ssh').port
|
const port = row.protocols.find(item => item.name === 'ssh').port
|
||||||
if (!port) {
|
if (!port) {
|
||||||
|
|||||||
@@ -22,17 +22,17 @@
|
|||||||
:key="platform.id"
|
:key="platform.id"
|
||||||
:span="6"
|
:span="6"
|
||||||
>
|
>
|
||||||
<el-card
|
<el-tooltip :content="platform.name" :open-delay="1000">
|
||||||
:style="{ borderLeftColor: randomBorderColor(index) }"
|
<el-card
|
||||||
class="platform-item"
|
:style="{ borderLeftColor: randomBorderColor(index) }"
|
||||||
shadow="hover"
|
class="platform-item"
|
||||||
@click.native="createAsset(platform)"
|
shadow="hover"
|
||||||
>
|
@click.native="createAsset(platform)"
|
||||||
<img :src="loadImage(platform)" alt="icon" class="asset-icon">
|
>
|
||||||
<el-tooltip :content="platform.name">
|
<img :src="loadImage(platform)" alt="icon" class="asset-icon">
|
||||||
<span class="platform-name">{{ platform.name }}</span>
|
<span class="platform-name">{{ platform.name }}</span>
|
||||||
</el-tooltip>
|
</el-card>
|
||||||
</el-card>
|
</el-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
@@ -236,8 +236,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.asset-icon {
|
.asset-icon {
|
||||||
width: 1.5em;
|
width: 2em;
|
||||||
height: 1.5em;
|
height: 2em;
|
||||||
vertical-align: -0.2em;
|
vertical-align: -0.2em;
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,14 +89,15 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleTabClick(tab) {
|
handleTabClick(tab) {
|
||||||
const query = _.cloneDeep(this.$route.query)
|
// 这样不行,会闪
|
||||||
const newQuery = {
|
// const query = _.cloneDeep(this.$route.query)
|
||||||
...query,
|
// const newQuery = {
|
||||||
tab: tab.name
|
// ...query,
|
||||||
}
|
// tab: tab.name
|
||||||
this.$nextTick(() => {
|
// }
|
||||||
this.$router.replace({ query: newQuery })
|
// this.$nextTick(() => {
|
||||||
})
|
// this.$router.replace({ query: newQuery })
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('Timer'),
|
title: this.$t('TimerExecution'),
|
||||||
attrs: {
|
attrs: {
|
||||||
model: this.object.task,
|
model: this.object.task,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export default {
|
|||||||
submitBtnSize: 'mini',
|
submitBtnSize: 'mini',
|
||||||
submitBtnText: this.$t('Add'),
|
submitBtnText: this.$t('Add'),
|
||||||
hasReset: false,
|
hasReset: false,
|
||||||
onSubmit: () => {},
|
onSubmit: () => {
|
||||||
|
},
|
||||||
submitMethod: () => 'post',
|
submitMethod: () => 'post',
|
||||||
getUrl: () => '',
|
getUrl: () => '',
|
||||||
cleanFormValue(data) {
|
cleanFormValue(data) {
|
||||||
@@ -86,7 +87,11 @@ export default {
|
|||||||
this.formConfig.fieldsMeta.protocols.el.hidden = true
|
this.formConfig.fieldsMeta.protocols.el.hidden = true
|
||||||
}
|
}
|
||||||
this.resourceType = val
|
this.resourceType = val
|
||||||
this.formConfig.fieldsMeta.value.el.ajax.url = url
|
if (url) {
|
||||||
|
this.formConfig.fieldsMeta.value.el.ajax.url = url
|
||||||
|
} else {
|
||||||
|
this.formConfig.fieldsMeta.attr.el.remote = false
|
||||||
|
}
|
||||||
this.formConfig.fieldsMeta.value.el.options = options
|
this.formConfig.fieldsMeta.value.el.options = options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,21 +156,31 @@ export default {
|
|||||||
tableConfig: {
|
tableConfig: {
|
||||||
columns: [
|
columns: [
|
||||||
{ prop: 'attr', label: this.$t('ResourceType'), formatter: tableFormatter('resource_type') },
|
{ prop: 'attr', label: this.$t('ResourceType'), formatter: tableFormatter('resource_type') },
|
||||||
{ prop: 'value', label: this.$t('Resource'), formatter: tableFormatter('resource', () => { return this.globalResource }) },
|
{
|
||||||
|
prop: 'value', label: this.$t('Resource'), formatter: tableFormatter('resource', () => {
|
||||||
|
return this.globalResource
|
||||||
|
})
|
||||||
|
},
|
||||||
{ prop: 'protocols', label: this.$t('Other'), formatter: tableFormatter('protocols') },
|
{ prop: 'protocols', label: this.$t('Other'), formatter: tableFormatter('protocols') },
|
||||||
{ prop: 'action', label: this.$t('Action'), align: 'center', width: '100px', formatter: (row, col, cellValue, index) => {
|
{
|
||||||
return (
|
prop: 'action',
|
||||||
<div className='input-button'>
|
label: this.$t('Action'),
|
||||||
<el-button
|
align: 'center',
|
||||||
icon='el-icon-minus'
|
width: '100px',
|
||||||
size='mini'
|
formatter: (row, col, cellValue, index) => {
|
||||||
style={{ 'flexShrink': 0 }}
|
return (
|
||||||
type='danger'
|
<div className='input-button'>
|
||||||
onClick={ this.handleDelete(index) }
|
<el-button
|
||||||
/>
|
icon='el-icon-minus'
|
||||||
</div>
|
size='mini'
|
||||||
)
|
style={{ 'flexShrink': 0 }}
|
||||||
} }
|
type='danger'
|
||||||
|
onClick={this.handleDelete(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
totalData: this.value || [],
|
totalData: this.value || [],
|
||||||
hasPagination: false
|
hasPagination: false
|
||||||
@@ -177,7 +192,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
this.nameOptions.map((o) => { this.globalResource[o.value] = o.label })
|
this.nameOptions.map((o) => {
|
||||||
|
this.globalResource[o.value] = o.label
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.$emit('input', this.tableConfig.totalData)
|
this.$emit('input', this.tableConfig.totalData)
|
||||||
@@ -218,9 +235,11 @@ export default {
|
|||||||
::v-deep .el-form-item:nth-child(-n+3) {
|
::v-deep .el-form-item:nth-child(-n+3) {
|
||||||
width: 43.5%;
|
width: 43.5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-form-item:last-child {
|
::v-deep .el-form-item:last-child {
|
||||||
width: 6%;
|
width: 6%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-input {
|
.action-input {
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default {
|
|||||||
domain_enabled: true
|
domain_enabled: true
|
||||||
},
|
},
|
||||||
canSelect: (row) => {
|
canSelect: (row) => {
|
||||||
return row.platform?.name !== 'Gateway'
|
return !row.platform?.name.startsWith('Gateway')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
category: 'all',
|
category: 'all',
|
||||||
url: `/api/v1/assets/assets/?domain=${this.$route.params.id}&exclude_platform=Gateway`,
|
url: `/api/v1/assets/assets/?domain=${this.$route.params.id}&is_gateway=0`,
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ export default {
|
|||||||
BaseAssetCreateUpdate
|
BaseAssetCreateUpdate
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const platformType = this.$route.query.platform_type
|
|
||||||
return {
|
return {
|
||||||
url: '/api/v1/assets/gateways/',
|
url: '/api/v1/assets/gateways/',
|
||||||
updateInitial: async(initial) => {
|
updateInitial: async(initial) => {
|
||||||
const url = `/api/v1/assets/platforms/?name=Gateway`
|
const url = `/api/v1/assets/platforms/?name__startswith=Gateway`
|
||||||
const platform = await this.$axios.get(url)
|
const platform = await this.$axios.get(url)
|
||||||
initial.platform = parseInt(platform[0].id)
|
initial.platform = parseInt(platform[0].id)
|
||||||
initial.domain = this.$route.query.domain
|
initial.domain = this.$route.query.domain
|
||||||
@@ -26,11 +25,11 @@ export default {
|
|||||||
disabled: true
|
disabled: true
|
||||||
},
|
},
|
||||||
platform: {
|
platform: {
|
||||||
disabled: true,
|
helpText: this.$t('GatewayPlatformHelpText'),
|
||||||
el: {
|
el: {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: `/api/v1/assets/platforms/?type=${platformType}`,
|
url: `/api/v1/assets/platforms/?name__startswith=Gateway`,
|
||||||
transformOption: (item) => {
|
transformOption: (item) => {
|
||||||
return { label: item.name, value: item.id }
|
return { label: item.name, value: item.id }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: `/api/v1/assets/gateways/?domain=${this.$route.params.id}&platform=Gateway`,
|
url: `/api/v1/assets/gateways/?domain=${this.$route.params.id}`,
|
||||||
columnsExclude: [
|
columnsExclude: [
|
||||||
'info', 'spec_info', 'auto_config'
|
'info', 'spec_info', 'auto_config'
|
||||||
],
|
],
|
||||||
@@ -174,7 +174,7 @@ export default {
|
|||||||
name: 'GatewayAdd',
|
name: 'GatewayAdd',
|
||||||
title: this.$t('Add'),
|
title: this.$t('Add'),
|
||||||
callback: function() {
|
callback: function() {
|
||||||
this.AddGatewaySetting.AddGatewayDialogVisible = true
|
this.AddGatewaySetting.addGatewayDialogVisible = true
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -183,7 +183,7 @@ export default {
|
|||||||
cell: '',
|
cell: '',
|
||||||
visible: false,
|
visible: false,
|
||||||
AddGatewaySetting: {
|
AddGatewaySetting: {
|
||||||
AddGatewayDialogVisible: false
|
addGatewayDialogVisible: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default {
|
|||||||
domain_enabled: true
|
domain_enabled: true
|
||||||
},
|
},
|
||||||
canSelect: (row) => {
|
canSelect: (row) => {
|
||||||
return row.platform?.name !== 'Gateway' && this.object.assets.map(item => item.id).indexOf(row.id) === -1
|
return !row.platform?.name.startsWith('Gateway') && this.object.assets.map(item => item.id).indexOf(row.id) === -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog
|
||||||
v-if="setting.AddGatewayDialogVisible"
|
v-if="setting.addGatewayDialogVisible"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
:show-cancel="false"
|
:show-cancel="false"
|
||||||
:show-confirm="false"
|
:show-confirm="false"
|
||||||
:title="$tc('AddGatewayInDomain')"
|
:title="$tc('AddGatewayInDomain')"
|
||||||
:visible.sync="setting.AddGatewayDialogVisible"
|
:visible.sync="setting.addGatewayDialogVisible"
|
||||||
after
|
after
|
||||||
custom-class="asset-select-dialog"
|
custom-class="asset-select-dialog"
|
||||||
top="15vh"
|
top="15vh"
|
||||||
@@ -31,7 +31,7 @@ export default {
|
|||||||
setting: {
|
setting: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
return { AddGatewayDialogVisible: false }
|
return { addGatewayDialogVisible: false }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
object: {
|
object: {
|
||||||
@@ -64,7 +64,7 @@ export default {
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/api/v1/assets/assets/?domain_enabled=true&platform=Gateway'
|
url: '/api/v1/assets/assets/?domain_enabled=true&is_gateway=1'
|
||||||
},
|
},
|
||||||
disabledValues: this.object.gateways.map(item => item.id)
|
disabledValues: this.object.gateways.map(item => item.id)
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSubmitSuccess(res) {
|
onSubmitSuccess(res) {
|
||||||
this.setting.AddGatewayDialogVisible = false
|
this.setting.addGatewayDialogVisible = false
|
||||||
this.$emit('close', res)
|
this.$emit('close', res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export default {
|
|||||||
if (Object.keys(filterField?.children || {}).length > 0) {
|
if (Object.keys(filterField?.children || {}).length > 0) {
|
||||||
for (const [k, v] of Object.entries(filterField.children)) {
|
for (const [k, v] of Object.entries(filterField.children)) {
|
||||||
let component = 'el-input'
|
let component = 'el-input'
|
||||||
|
const el = {}
|
||||||
switch (v?.type) {
|
switch (v?.type) {
|
||||||
case 'list':
|
case 'list':
|
||||||
component = DynamicInput
|
component = DynamicInput
|
||||||
@@ -148,12 +149,15 @@ export default {
|
|||||||
case 'boolean':
|
case 'boolean':
|
||||||
component = Switcher
|
component = Switcher
|
||||||
break
|
break
|
||||||
|
case 'text':
|
||||||
|
el['type'] = 'textarea'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param) {
|
if (param) {
|
||||||
v.default = param[k] || v.default
|
v.default = param[k] || v.default
|
||||||
}
|
}
|
||||||
const item = { ...v, component: component }
|
const item = { ...v, component: component, el: el }
|
||||||
fieldsMeta[method].fields.push(k)
|
fieldsMeta[method].fields.push(k)
|
||||||
fieldsMeta[method].fieldsMeta[k] = item
|
fieldsMeta[method].fieldsMeta[k] = item
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export default {
|
|||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.tab.activeMenu = window.localStorage.getItem('lastTab')
|
this.tab.activeMenu = window.localStorage.getItem('lastTab') || 'host'
|
||||||
this.$refs.genericListTable.reloadTable()
|
this.$refs.genericListTable.reloadTable()
|
||||||
}, 300)
|
}, 300)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ export default {
|
|||||||
tip += current.label + ':' + current.total + '<br/>'
|
tip += current.label + ':' + current.total + '<br/>'
|
||||||
}
|
}
|
||||||
return tip
|
return tip
|
||||||
}
|
},
|
||||||
|
appendToBody: true
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: '60%',
|
top: '60%',
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default {
|
|||||||
width: '60%',
|
width: '60%',
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
hasSelection: false,
|
hasSelection: false,
|
||||||
url: `/api/v1/ops/adhocs/`,
|
url: `/api/v1/ops/adhocs/?only_mine=true`,
|
||||||
columns: ['name', 'module', 'args', 'comment', 'actions'],
|
columns: ['name', 'module', 'args', 'comment', 'actions'],
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import $ from '@/utils/jquery-vendor.js'
|
||||||
import AssetTreeTable from '@/components/Apps/AssetTreeTable'
|
import AssetTreeTable from '@/components/Apps/AssetTreeTable'
|
||||||
import QuickJobTerm from '@/views/ops/Adhoc/components/QuickJobTerm.vue'
|
import QuickJobTerm from '@/views/ops/Adhoc/components/QuickJobTerm.vue'
|
||||||
import CodeEditor from '@/components/Form/FormFields/CodeEditor'
|
import CodeEditor from '@/components/Form/FormFields/CodeEditor'
|
||||||
@@ -286,6 +287,14 @@ export default {
|
|||||||
view: {
|
view: {
|
||||||
dblClickExpand: false,
|
dblClickExpand: false,
|
||||||
showLine: true
|
showLine: true
|
||||||
|
},
|
||||||
|
callback: {
|
||||||
|
onCheck: function(_event, treeId, treeNode) {
|
||||||
|
const treeObj = $.fn.zTree.getZTreeObj(treeId)
|
||||||
|
if (treeNode.checked) {
|
||||||
|
treeObj.expandNode(treeNode, true, false, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
iShowTree: true
|
iShowTree: true
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ export default {
|
|||||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||||
lineHeight: 1.2,
|
lineHeight: 1.2,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
scrollback: 9999999,
|
||||||
rightClickSelectsWord: true,
|
rightClickSelectsWord: true,
|
||||||
theme: {
|
theme: {
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export default {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
value: [],
|
value: [],
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/api/v1/ops/playbooks/',
|
url: `/api/v1/ops/playbooks/?only_mine=true`,
|
||||||
transformOption: (item) => {
|
transformOption: (item) => {
|
||||||
return { label: item.name, value: item.id }
|
return { label: item.name, value: item.id }
|
||||||
}
|
}
|
||||||
@@ -98,6 +98,7 @@ export default {
|
|||||||
el: {
|
el: {
|
||||||
baseUrl: '/api/v1/perms/users/self/assets/',
|
baseUrl: '/api/v1/perms/users/self/assets/',
|
||||||
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
|
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
|
||||||
|
typeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree',
|
||||||
value: []
|
value: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ export default {
|
|||||||
GenericListTable
|
GenericListTable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const currentUserID = this.$store.state.users.profile.id
|
||||||
return {
|
return {
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: '/api/v1/ops/adhocs/',
|
url: '/api/v1/ops/adhocs/',
|
||||||
columnsShow: {
|
columnsShow: {
|
||||||
min: ['name', 'actions'],
|
min: ['name', 'actions'],
|
||||||
default: ['name', 'module', 'args', 'comment', 'date_created', 'actions']
|
default: ['name', 'module', 'args', 'comment', 'scope', 'date_created', 'actions', 'created_by']
|
||||||
},
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
@@ -29,11 +30,16 @@ export default {
|
|||||||
formatter: ActionsFormatter,
|
formatter: ActionsFormatter,
|
||||||
formatterArgs: {
|
formatterArgs: {
|
||||||
hasUpdate: true,
|
hasUpdate: true,
|
||||||
canUpdate: this.$hasPerm('ops.change_adhoc'),
|
canUpdate: ({ row }) => {
|
||||||
|
return this.$hasPerm('ops.change_adhoc') && row.creator === currentUserID
|
||||||
|
},
|
||||||
updateRoute: 'AdhocUpdate',
|
updateRoute: 'AdhocUpdate',
|
||||||
hasDelete: true,
|
hasDelete: true,
|
||||||
canDelete: this.$hasPerm('ops.delete_adhoc'),
|
canDelete: ({ row }) => {
|
||||||
hasClone: false
|
return this.$hasPerm('ops.delete_adhoc') && row.creator === currentUserID
|
||||||
|
},
|
||||||
|
hasClone: true,
|
||||||
|
cloneRoute: 'AdhocCreate'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
url: '/api/v1/ops/adhocs/',
|
url: '/api/v1/ops/adhocs/',
|
||||||
fields: [
|
fields: [
|
||||||
[this.$t('Basic'), ['name', 'module', 'args', 'comment']]
|
[this.$t('Basic'), ['name', 'module', 'args', 'comment', 'scope']]
|
||||||
],
|
],
|
||||||
initial: {
|
initial: {
|
||||||
module: 'shell',
|
module: 'shell',
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default {
|
|||||||
GenericListTable
|
GenericListTable
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const currentUserID = this.$store.state.users.profile.id
|
||||||
return {
|
return {
|
||||||
createDialogVisible: false,
|
createDialogVisible: false,
|
||||||
uploadDialogVisible: false,
|
uploadDialogVisible: false,
|
||||||
@@ -23,7 +24,7 @@ export default {
|
|||||||
url: '/api/v1/ops/playbooks/',
|
url: '/api/v1/ops/playbooks/',
|
||||||
columnsShow: {
|
columnsShow: {
|
||||||
min: ['name', 'actions'],
|
min: ['name', 'actions'],
|
||||||
default: ['name', 'comment', 'date_created', 'actions']
|
default: ['name', 'comment', 'scope', 'date_created', 'actions', 'created_by']
|
||||||
},
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
name: {
|
name: {
|
||||||
@@ -36,11 +37,16 @@ export default {
|
|||||||
formatter: ActionsFormatter,
|
formatter: ActionsFormatter,
|
||||||
formatterArgs: {
|
formatterArgs: {
|
||||||
hasUpdate: true,
|
hasUpdate: true,
|
||||||
canUpdate: this.$hasPerm('ops.change_playbook'),
|
canUpdate: ({ row }) => {
|
||||||
|
return this.$hasPerm('ops.change_playbook') && row.creator === currentUserID
|
||||||
|
},
|
||||||
updateRoute: 'PlaybookUpdate',
|
updateRoute: 'PlaybookUpdate',
|
||||||
hasDelete: true,
|
hasDelete: true,
|
||||||
canDelete: this.$hasPerm('ops.delete_playbook'),
|
canDelete: ({ row }) => {
|
||||||
hasClone: false
|
return this.$hasPerm('ops.delete_playbook') && row.creator === currentUserID
|
||||||
|
},
|
||||||
|
hasClone: true,
|
||||||
|
cloneRoute: 'PlaybookCreate'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
url: '/api/v1/ops/playbooks/',
|
url: '/api/v1/ops/playbooks/',
|
||||||
fields: [
|
fields: [
|
||||||
[this.$t('Basic'), ['name', 'comment']]
|
[this.$t('Basic'), ['name', 'comment', 'scope']]
|
||||||
],
|
],
|
||||||
createSuccessNextRoute: {
|
createSuccessNextRoute: {
|
||||||
name: 'Template'
|
name: 'Template'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<NewNodeDialog v-if="createDialogVisible" :visible.sync="createDialogVisible" @confirm="doCreate" />
|
<NewNodeDialog v-if="createDialogVisible" :visible.sync="createDialogVisible" @confirm="doCreate" />
|
||||||
<TreeTable ref="TreeTable" :tree-setting="treeSetting">
|
<TreeTable ref="TreeTable" :tree-setting="treeSetting">
|
||||||
<template slot="rMenu">
|
<template v-if="!disableEdit" slot="rMenu">
|
||||||
<li id="m_create_file" class="rmenu" tabindex="-1" @click="onCreate('file')">
|
<li id="m_create_file" class="rmenu" tabindex="-1" @click="onCreate('file')">
|
||||||
{{ $tc('NewFile') }}
|
{{ $tc('NewFile') }}
|
||||||
</li>
|
</li>
|
||||||
@@ -62,7 +62,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const disableEdit = this.object.creator !== this.$store.state.users.profile.id
|
||||||
return {
|
return {
|
||||||
|
disableEdit: disableEdit,
|
||||||
newNode: {},
|
newNode: {},
|
||||||
createDialogVisible: false,
|
createDialogVisible: false,
|
||||||
createType: 'directory',
|
createType: 'directory',
|
||||||
@@ -70,7 +72,8 @@ export default {
|
|||||||
closing: false,
|
closing: false,
|
||||||
DataZTree: 0,
|
DataZTree: 0,
|
||||||
cmOptions: {
|
cmOptions: {
|
||||||
mode: 'yaml'
|
mode: 'yaml',
|
||||||
|
readOnly: disableEdit
|
||||||
},
|
},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
left: {
|
left: {
|
||||||
@@ -82,6 +85,7 @@ export default {
|
|||||||
el: {
|
el: {
|
||||||
type: 'primary'
|
type: 'primary'
|
||||||
},
|
},
|
||||||
|
isVisible: disableEdit,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.onSave()
|
this.onSave()
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,7 @@ export default {
|
|||||||
el: {
|
el: {
|
||||||
type: 'primary'
|
type: 'primary'
|
||||||
},
|
},
|
||||||
|
isVisible: disableEdit,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.onReset()
|
this.onReset()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ export default {
|
|||||||
}],
|
}],
|
||||||
el: {
|
el: {
|
||||||
value: [],
|
value: [],
|
||||||
|
defaultPageSize: 300,
|
||||||
|
baseUrl: '/api/v1/assets/assets/?fields_size=mini',
|
||||||
treeSetting: {
|
treeSetting: {
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
showRefresh: false
|
showRefresh: false
|
||||||
|
|||||||
@@ -37,7 +37,12 @@ export default {
|
|||||||
notShowBuiltinTree: true,
|
notShowBuiltinTree: true,
|
||||||
url: '/api/v1/perms/asset-permissions/',
|
url: '/api/v1/perms/asset-permissions/',
|
||||||
nodeUrl: '/api/v1/perms/asset-permissions/',
|
nodeUrl: '/api/v1/perms/asset-permissions/',
|
||||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
|
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
||||||
|
edit: {
|
||||||
|
drag: {
|
||||||
|
isMove: false
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: '/api/v1/perms/asset-permissions/',
|
url: '/api/v1/perms/asset-permissions/',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import GenericListPage from '@/layout/components/GenericListPage'
|
import GenericListPage from '@/layout/components/GenericListPage'
|
||||||
import { download } from '@/utils/common'
|
import { download } from '@/utils/common'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -36,7 +37,24 @@ export default {
|
|||||||
return row.has_file
|
return row.has_file
|
||||||
},
|
},
|
||||||
tip: ({ row }) => {
|
tip: ({ row }) => {
|
||||||
return row.has_file ? this.$t('Download') : this.$t('DownloadFTPFileTip')
|
const ftpFileMaxStore = store.getters.publicSettings['FTP_FILE_MAX_STORE']
|
||||||
|
|
||||||
|
const downloadTip = this.$t('Download')
|
||||||
|
const fileNotStoredTip = this.$t('FTPFileNotStored')
|
||||||
|
const storageNotEnabledTip = this.$t('FTPStorageNotEnabled')
|
||||||
|
const unknownStorageStateTip = this.$t('FTPUnknownStorageState')
|
||||||
|
|
||||||
|
if (row.has_file) {
|
||||||
|
return downloadTip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftpFileMaxStore === 0) {
|
||||||
|
return storageNotEnabledTip
|
||||||
|
} else if (ftpFileMaxStore > 0) {
|
||||||
|
return fileNotStoredTip
|
||||||
|
} else {
|
||||||
|
return unknownStorageStateTip
|
||||||
|
}
|
||||||
},
|
},
|
||||||
callback: function({ row }) {
|
callback: function({ row }) {
|
||||||
// 跳转下载页面
|
// 跳转下载页面
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<script type="text/jsx">
|
<script type="text/jsx">
|
||||||
import ListTable from '@/components/Table/ListTable'
|
import ListTable from '@/components/Table/ListTable'
|
||||||
import { timeOffset } from '@/utils/time'
|
import { timeOffset } from '@/utils/time'
|
||||||
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
import { ActionsFormatter, ChoicesFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseList',
|
name: 'BaseList',
|
||||||
@@ -119,7 +119,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
is_locked: {
|
is_locked: {
|
||||||
label: this.$t('IsLocked')
|
label: this.$t('IsLocked'),
|
||||||
|
formatter: ChoicesFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
showFalse: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
prop: 'actions',
|
prop: 'actions',
|
||||||
|
|||||||
103
src/views/settings/Auth/Ldap/HaSyncSettingDialog.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:show-cancel="false"
|
||||||
|
:show-confirm="false"
|
||||||
|
:title="$tc('SyncSetting')"
|
||||||
|
top="10%"
|
||||||
|
v-bind="$attrs"
|
||||||
|
width="50%"
|
||||||
|
v-on="$listeners"
|
||||||
|
>
|
||||||
|
<GenericCreateUpdateForm
|
||||||
|
:has-detail-in-msg="false"
|
||||||
|
v-bind="settings"
|
||||||
|
@submitSuccess="onSuccess"
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { GenericCreateUpdateForm } from '@/layout/components'
|
||||||
|
import { CronTab, Dialog } from '@/components'
|
||||||
|
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||||
|
import { Required } from '@/components/Form/DataForm/rules'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SyncSettingDialog',
|
||||||
|
components: {
|
||||||
|
GenericCreateUpdateForm,
|
||||||
|
Dialog
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
settings: {
|
||||||
|
visible: false,
|
||||||
|
url: '/api/v1/settings/setting/?category=ldap_ha',
|
||||||
|
fields: [
|
||||||
|
'AUTH_LDAP_HA_SYNC_ORG_IDS', 'AUTH_LDAP_HA_SYNC_IS_PERIODIC', 'AUTH_LDAP_HA_SYNC_CRONTAB',
|
||||||
|
'AUTH_LDAP_HA_SYNC_INTERVAL', 'AUTH_LDAP_HA_SYNC_RECEIVERS'
|
||||||
|
],
|
||||||
|
fieldsMeta: {
|
||||||
|
AUTH_LDAP_HA_SYNC_ORG_IDS: {
|
||||||
|
component: Select2,
|
||||||
|
rules: [Required],
|
||||||
|
el: {
|
||||||
|
popperClass: 'sync-setting-org',
|
||||||
|
multiple: true,
|
||||||
|
ajax: {
|
||||||
|
url: '/api/v1/orgs/orgs/',
|
||||||
|
transformOption: (item) => {
|
||||||
|
return { label: item.name, value: item.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hidden: (formValue) => {
|
||||||
|
return !this.$hasLicense()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_SYNC_IS_PERIODIC: {
|
||||||
|
type: 'switch'
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_SYNC_CRONTAB: {
|
||||||
|
component: CronTab,
|
||||||
|
helpTip: this.$t('CrontabOfCreateUpdatePage')
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_SYNC_INTERVAL: {
|
||||||
|
helpText: this.$t('IntervalOfCreateUpdatePage')
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_SYNC_RECEIVERS: {
|
||||||
|
component: Select2,
|
||||||
|
el: {
|
||||||
|
value: [],
|
||||||
|
multiple: true,
|
||||||
|
ajax: {
|
||||||
|
url: '/api/v1/users/users/?fields_size=mini&oid=ROOT',
|
||||||
|
transformOption: (item) => {
|
||||||
|
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submitMethod: () => 'patch',
|
||||||
|
cleanFormValue(value) {
|
||||||
|
if (value['AUTH_LDAP_HA_SYNC_INTERVAL'] === '') {
|
||||||
|
value['AUTH_LDAP_HA_SYNC_INTERVAL'] = null
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSuccess() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -49,6 +49,7 @@ import { DEFAULT_ORG_ID, SYSTEM_ORG_ID } from '@/utils/org'
|
|||||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
import ListTable from '@/components/Table/ListTable/index.vue'
|
||||||
import Dialog from '@/components/Dialog/index.vue'
|
import Dialog from '@/components/Dialog/index.vue'
|
||||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||||
|
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ImportDialog',
|
name: 'ImportDialog',
|
||||||
@@ -57,6 +58,12 @@ export default {
|
|||||||
Dialog,
|
Dialog,
|
||||||
Select2
|
Select2
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
category: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dialogLdapUserImportLoginStatus: false,
|
dialogLdapUserImportLoginStatus: false,
|
||||||
@@ -75,9 +82,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: '/api/v1/settings/ldap/users/',
|
url: `/api/v1/settings/ldap/users/?category=${this.category}`,
|
||||||
columns: ['username', 'name', 'email', 'groups', 'existing'],
|
columns: ['status', 'username', 'name', 'email', 'groups', 'existing'],
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
|
...getStatusColumnMeta.bind(this)('status'),
|
||||||
username: {
|
username: {
|
||||||
label: this.$t('Username'),
|
label: this.$t('Username'),
|
||||||
width: '180px'
|
width: '180px'
|
||||||
@@ -189,7 +197,7 @@ export default {
|
|||||||
enableWS() {
|
enableWS() {
|
||||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
const port = document.location.port ? ':' + document.location.port : ''
|
const port = document.location.port ? ':' + document.location.port : ''
|
||||||
const url = '/ws/ldap/'
|
const url = `/ws/ldap/?category=${this.category}`
|
||||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||||
this.ws = new WebSocket(wsURL)
|
this.ws = new WebSocket(wsURL)
|
||||||
},
|
},
|
||||||
|
|||||||
151
src/views/settings/Auth/Ldap/Ldap.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<IBox>
|
||||||
|
<GenericCreateUpdateForm v-bind="$data" />
|
||||||
|
<ImportDialog v-if="dialogLdapUserImport" :category="category" :visible.sync="dialogLdapUserImport" />
|
||||||
|
<TestLoginDialog :visible.sync="dialogTest" :category="category" />
|
||||||
|
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
||||||
|
</IBox>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
||||||
|
import ImportDialog from './ImportDialog.vue'
|
||||||
|
import TestLoginDialog from './TestLoginDialog.vue'
|
||||||
|
import SyncSettingDialog from './SyncSettingDialog.vue'
|
||||||
|
import { IBox } from '@/components'
|
||||||
|
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
||||||
|
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Ldap',
|
||||||
|
components: {
|
||||||
|
GenericCreateUpdateForm,
|
||||||
|
IBox,
|
||||||
|
ImportDialog,
|
||||||
|
TestLoginDialog,
|
||||||
|
SyncSettingDialog
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const category = 'ldap'
|
||||||
|
return {
|
||||||
|
category: category,
|
||||||
|
url: `/api/v1/settings/setting/?category=${category}`,
|
||||||
|
dialogTest: false,
|
||||||
|
dialogLdapUserImport: false,
|
||||||
|
dialogSyncSetting: false,
|
||||||
|
encryptedFields: ['AUTH_LDAP_BIND_PASSWORD'],
|
||||||
|
fields: [
|
||||||
|
[
|
||||||
|
this.$t('Basic'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP', 'AUTH_LDAP_SERVER_URI',
|
||||||
|
'AUTH_LDAP_BIND_DN', 'AUTH_LDAP_BIND_PASSWORD'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
this.$t('Search'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP_SEARCH_OU', 'AUTH_LDAP_SEARCH_FILTER',
|
||||||
|
'AUTH_LDAP_USER_ATTR_MAP'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
this.$t('Other'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP_CONNECT_TIMEOUT', 'AUTH_LDAP_SEARCH_PAGED_SIZE',
|
||||||
|
'AUTH_LDAP_CACHE_TIMEOUT'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
fieldsMeta: {
|
||||||
|
AUTH_LDAP_BIND_DN: {
|
||||||
|
rules: [
|
||||||
|
rules.Required
|
||||||
|
]
|
||||||
|
},
|
||||||
|
AUTH_LDAP_BIND_PASSWORD: {
|
||||||
|
component: UpdateToken
|
||||||
|
},
|
||||||
|
AUTH_LDAP_SEARCH_OU: {
|
||||||
|
rules: [
|
||||||
|
rules.Required
|
||||||
|
]
|
||||||
|
},
|
||||||
|
AUTH_LDAP_USER_ATTR_MAP: {
|
||||||
|
component: JsonEditor,
|
||||||
|
rules: [JsonRequired]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasDetailInMsg: false,
|
||||||
|
moreButtons: [
|
||||||
|
{
|
||||||
|
title: this.$t('LdapConnectTest'),
|
||||||
|
loading: false,
|
||||||
|
callback: function(value, form, btn) {
|
||||||
|
if (value['AUTH_LDAP_BIND_PASSWORD'] === undefined) {
|
||||||
|
value['AUTH_LDAP_BIND_PASSWORD'] = ''
|
||||||
|
}
|
||||||
|
btn.loading = true
|
||||||
|
this.enableWS()
|
||||||
|
this.ws.onopen = (e) => {
|
||||||
|
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
||||||
|
}
|
||||||
|
this.ws.onmessage = (e) => {
|
||||||
|
const data = JSON.parse(e.data)
|
||||||
|
if (data.ok) {
|
||||||
|
this.$message.success(data.msg)
|
||||||
|
} else {
|
||||||
|
this.$message.error(data.msg)
|
||||||
|
}
|
||||||
|
btn.loading = false
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('LdapLoginTest'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogTest = true
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('LdapBulkImport'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogLdapUserImport = true
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('SyncSetting'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogSyncSetting = true
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
submitMethod: () => 'patch',
|
||||||
|
afterGetFormValue(obj) {
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
cleanFormValue(data) {
|
||||||
|
if (data['AUTH_LDAP_BIND_PASSWORD'] === '') {
|
||||||
|
delete data['AUTH_LDAP_BIND_PASSWORD']
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enableWS() {
|
||||||
|
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
|
const port = document.location.port ? ':' + document.location.port : ''
|
||||||
|
const url = '/ws/ldap/'
|
||||||
|
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||||
|
this.ws = new WebSocket(wsURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.listTable ::v-deep .table-action-right-side {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
151
src/views/settings/Auth/Ldap/LdapHA.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<IBox>
|
||||||
|
<GenericCreateUpdateForm v-bind="$data" />
|
||||||
|
<ImportDialog v-if="dialogLdapUserImport" :category="category" :visible.sync="dialogLdapUserImport" />
|
||||||
|
<TestLoginDialog :visible.sync="dialogTest" :category="category" />
|
||||||
|
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
||||||
|
</IBox>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
||||||
|
import ImportDialog from './ImportDialog.vue'
|
||||||
|
import TestLoginDialog from './TestLoginDialog.vue'
|
||||||
|
import SyncSettingDialog from './HaSyncSettingDialog.vue'
|
||||||
|
import { IBox } from '@/components'
|
||||||
|
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
||||||
|
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LdapHA',
|
||||||
|
components: {
|
||||||
|
GenericCreateUpdateForm,
|
||||||
|
IBox,
|
||||||
|
ImportDialog,
|
||||||
|
TestLoginDialog,
|
||||||
|
SyncSettingDialog
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const category = 'ldap_ha'
|
||||||
|
return {
|
||||||
|
category: category,
|
||||||
|
url: `/api/v1/settings/setting/?category=${category}`,
|
||||||
|
dialogTest: false,
|
||||||
|
dialogLdapUserImport: false,
|
||||||
|
dialogSyncSetting: false,
|
||||||
|
encryptedFields: ['AUTH_LDAP_HA_BIND_PASSWORD'],
|
||||||
|
fields: [
|
||||||
|
[
|
||||||
|
this.$t('Basic'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP_HA', 'AUTH_LDAP_HA_SERVER_URI',
|
||||||
|
'AUTH_LDAP_HA_BIND_DN', 'AUTH_LDAP_HA_BIND_PASSWORD'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
this.$t('Search'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP_HA_SEARCH_OU', 'AUTH_LDAP_HA_SEARCH_FILTER',
|
||||||
|
'AUTH_LDAP_HA_USER_ATTR_MAP'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
this.$t('Other'),
|
||||||
|
[
|
||||||
|
'AUTH_LDAP_HA_CONNECT_TIMEOUT', 'AUTH_LDAP_HA_SEARCH_PAGED_SIZE',
|
||||||
|
'AUTH_LDAP_HA_CACHE_TIMEOUT'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
fieldsMeta: {
|
||||||
|
AUTH_LDAP_HA_BIND_DN: {
|
||||||
|
rules: [
|
||||||
|
rules.Required
|
||||||
|
]
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_BIND_PASSWORD: {
|
||||||
|
component: UpdateToken
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_SEARCH_OU: {
|
||||||
|
rules: [
|
||||||
|
rules.Required
|
||||||
|
]
|
||||||
|
},
|
||||||
|
AUTH_LDAP_HA_USER_ATTR_MAP: {
|
||||||
|
component: JsonEditor,
|
||||||
|
rules: [JsonRequired]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasDetailInMsg: false,
|
||||||
|
moreButtons: [
|
||||||
|
{
|
||||||
|
title: this.$t('LdapConnectTest'),
|
||||||
|
loading: false,
|
||||||
|
callback: function(value, form, btn) {
|
||||||
|
if (value['AUTH_LDAP_HA_BIND_PASSWORD'] === undefined) {
|
||||||
|
value['AUTH_LDAP_HA_BIND_PASSWORD'] = ''
|
||||||
|
}
|
||||||
|
btn.loading = true
|
||||||
|
this.enableWS()
|
||||||
|
this.ws.onopen = (e) => {
|
||||||
|
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
||||||
|
}
|
||||||
|
this.ws.onmessage = (e) => {
|
||||||
|
const data = JSON.parse(e.data)
|
||||||
|
if (data.ok) {
|
||||||
|
this.$message.success(data.msg)
|
||||||
|
} else {
|
||||||
|
this.$message.error(data.msg)
|
||||||
|
}
|
||||||
|
btn.loading = false
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('LdapLoginTest'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogTest = true
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('LdapBulkImport'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogLdapUserImport = true
|
||||||
|
}.bind(this)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('SyncSetting'),
|
||||||
|
callback: function(value, form) {
|
||||||
|
this.dialogSyncSetting = true
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
submitMethod: () => 'patch',
|
||||||
|
afterGetFormValue(obj) {
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
cleanFormValue(data) {
|
||||||
|
if (data['AUTH_LDAP_HA_BIND_PASSWORD'] === '') {
|
||||||
|
delete data['AUTH_LDAP_HA_BIND_PASSWORD']
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enableWS() {
|
||||||
|
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
|
const port = document.location.port ? ':' + document.location.port : ''
|
||||||
|
const url = `/ws/ldap/?category=${this.category}`
|
||||||
|
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||||
|
this.ws = new WebSocket(wsURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.listTable ::v-deep .table-action-right-side {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -40,6 +40,12 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Dialog
|
Dialog
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
category: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
testLdapLoginStatus: false,
|
testLdapLoginStatus: false,
|
||||||
@@ -69,7 +75,7 @@ export default {
|
|||||||
enableWS() {
|
enableWS() {
|
||||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
const port = document.location.port ? ':' + document.location.port : ''
|
const port = document.location.port ? ':' + document.location.port : ''
|
||||||
const url = '/ws/ldap/'
|
const url = `/ws/ldap/?category=${this.category}`
|
||||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||||
this.ws = new WebSocket(wsURL)
|
this.ws = new WebSocket(wsURL)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,152 +1 @@
|
|||||||
<template>
|
|
||||||
<IBox>
|
|
||||||
<GenericCreateUpdateForm v-bind="$data" />
|
|
||||||
<ImportDialog v-if="dialogLdapUserImport" :visible.sync="dialogLdapUserImport" />
|
|
||||||
<TestLoginDialog :visible.sync="dialogTest" />
|
|
||||||
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
|
||||||
</IBox>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
|
||||||
import ImportDialog from './ImportDialog.vue'
|
|
||||||
import TestLoginDialog from './TestLoginDialog.vue'
|
|
||||||
import SyncSettingDialog from './SyncSettingDialog.vue'
|
|
||||||
import { IBox } from '@/components'
|
|
||||||
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
|
||||||
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Ldap',
|
|
||||||
components: {
|
|
||||||
GenericCreateUpdateForm,
|
|
||||||
IBox,
|
|
||||||
ImportDialog,
|
|
||||||
TestLoginDialog,
|
|
||||||
SyncSettingDialog
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
dialogTest: false,
|
|
||||||
dialogLdapUserImport: false,
|
|
||||||
dialogSyncSetting: false,
|
|
||||||
encryptedFields: ['AUTH_LDAP_BIND_PASSWORD'],
|
|
||||||
fields: [
|
|
||||||
[
|
|
||||||
this.$t('Basic'),
|
|
||||||
[
|
|
||||||
'AUTH_LDAP', 'AUTH_LDAP_SERVER_URI',
|
|
||||||
'AUTH_LDAP_BIND_DN', 'AUTH_LDAP_BIND_PASSWORD'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
this.$t('Search'),
|
|
||||||
[
|
|
||||||
'AUTH_LDAP_SEARCH_OU', 'AUTH_LDAP_SEARCH_FILTER',
|
|
||||||
'AUTH_LDAP_USER_ATTR_MAP'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
this.$t('Other'),
|
|
||||||
[
|
|
||||||
'AUTH_LDAP_CONNECT_TIMEOUT', 'AUTH_LDAP_SEARCH_PAGED_SIZE',
|
|
||||||
'AUTH_LDAP_CACHE_TIMEOUT'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
fieldsMeta: {
|
|
||||||
AUTH_LDAP_BIND_DN: {
|
|
||||||
rules: [
|
|
||||||
rules.Required
|
|
||||||
]
|
|
||||||
},
|
|
||||||
AUTH_LDAP_BIND_PASSWORD: {
|
|
||||||
component: UpdateToken
|
|
||||||
},
|
|
||||||
AUTH_LDAP_SEARCH_OU: {
|
|
||||||
rules: [
|
|
||||||
rules.Required
|
|
||||||
]
|
|
||||||
},
|
|
||||||
AUTH_LDAP_USER_ATTR_MAP: {
|
|
||||||
component: JsonEditor,
|
|
||||||
rules: [JsonRequired]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
url: '/api/v1/settings/setting/?category=ldap',
|
|
||||||
hasDetailInMsg: false,
|
|
||||||
moreButtons: [
|
|
||||||
{
|
|
||||||
title: this.$t('LdapConnectTest'),
|
|
||||||
loading: false,
|
|
||||||
callback: function(value, form, btn) {
|
|
||||||
if (value['AUTH_LDAP_BIND_PASSWORD'] === undefined) {
|
|
||||||
value['AUTH_LDAP_BIND_PASSWORD'] = ''
|
|
||||||
}
|
|
||||||
btn.loading = true
|
|
||||||
this.enableWS()
|
|
||||||
this.ws.onopen = (e) => {
|
|
||||||
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
|
||||||
}
|
|
||||||
this.ws.onmessage = (e) => {
|
|
||||||
const data = JSON.parse(e.data)
|
|
||||||
if (data.ok) {
|
|
||||||
this.$message.success(data.msg)
|
|
||||||
} else {
|
|
||||||
this.$message.error(data.msg)
|
|
||||||
}
|
|
||||||
btn.loading = false
|
|
||||||
}
|
|
||||||
}.bind(this)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t('LdapLoginTest'),
|
|
||||||
callback: function(value, form) {
|
|
||||||
this.dialogTest = true
|
|
||||||
}.bind(this)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t('LdapBulkImport'),
|
|
||||||
callback: function(value, form) {
|
|
||||||
this.dialogLdapUserImport = true
|
|
||||||
}.bind(this)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t('SyncSetting'),
|
|
||||||
callback: function(value, form) {
|
|
||||||
this.dialogSyncSetting = true
|
|
||||||
}.bind(this)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
submitMethod: () => 'patch',
|
|
||||||
afterGetFormValue(obj) {
|
|
||||||
return obj
|
|
||||||
},
|
|
||||||
cleanFormValue(data) {
|
|
||||||
if (data['AUTH_LDAP_BIND_PASSWORD'] === '') {
|
|
||||||
delete data['AUTH_LDAP_BIND_PASSWORD']
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// this.loading = false
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
enableWS() {
|
|
||||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
|
||||||
const port = document.location.port ? ':' + document.location.port : ''
|
|
||||||
const url = '/ws/ldap/'
|
|
||||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
|
||||||
this.ws = new WebSocket(wsURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.listTable ::v-deep .table-action-right-side {
|
|
||||||
padding-top: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ export default {
|
|||||||
'AUTH_PASSKEY', 'FIDO_SERVER_ID', 'FIDO_SERVER_NAME'
|
'AUTH_PASSKEY', 'FIDO_SERVER_ID', 'FIDO_SERVER_NAME'
|
||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
|
'FIDO_SERVER_ID': {
|
||||||
|
placeholder: 'js.example.org'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
submitMethod() {
|
submitMethod() {
|
||||||
return 'patch'
|
return 'patch'
|
||||||
@@ -30,8 +33,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TabPage from '@/layout/components/TabPage'
|
import TabPage from '@/layout/components/TabPage'
|
||||||
import LDAP from './Ldap'
|
import LdapHA from './Ldap/LdapHA.vue'
|
||||||
|
import LDAP from './Ldap/Ldap.vue'
|
||||||
import Base from './Base'
|
import Base from './Base'
|
||||||
import Basic from './Basic'
|
import Basic from './Basic'
|
||||||
import CAS from './CAS'
|
import CAS from './CAS'
|
||||||
@@ -28,6 +29,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
TabPage,
|
TabPage,
|
||||||
LDAP,
|
LDAP,
|
||||||
|
LdapHA,
|
||||||
Base,
|
Base,
|
||||||
Basic,
|
Basic,
|
||||||
CAS,
|
CAS,
|
||||||
@@ -45,6 +47,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
let extraBackends = []
|
let extraBackends = []
|
||||||
|
let ldapHABackends = []
|
||||||
if (this.$store.getters.hasValidLicense) {
|
if (this.$store.getters.hasValidLicense) {
|
||||||
extraBackends = [
|
extraBackends = [
|
||||||
{
|
{
|
||||||
@@ -93,6 +96,13 @@ export default {
|
|||||||
key: 'AUTH_RADIUS'
|
key: 'AUTH_RADIUS'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
ldapHABackends = [
|
||||||
|
{
|
||||||
|
title: this.$t('LDAP HA'),
|
||||||
|
name: 'LdapHA',
|
||||||
|
key: 'AUTH_LDAP_HA'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -107,6 +117,7 @@ export default {
|
|||||||
name: 'LDAP',
|
name: 'LDAP',
|
||||||
key: 'AUTH_LDAP'
|
key: 'AUTH_LDAP'
|
||||||
},
|
},
|
||||||
|
...ldapHABackends,
|
||||||
{
|
{
|
||||||
title: this.$t('CAS'),
|
title: this.$t('CAS'),
|
||||||
name: 'CAS',
|
name: 'CAS',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default {
|
|||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
ANNOUNCEMENT: {
|
ANNOUNCEMENT: {
|
||||||
fields: [
|
fields: [
|
||||||
'SUBJECT', 'CONTENT', 'LINK'
|
'SUBJECT', 'CONTENT', 'DATE_START', 'DATE_END', 'LINK'
|
||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
CONTENT: {
|
CONTENT: {
|
||||||
|
|||||||
@@ -26,11 +26,14 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$tc('Receivers')" show-overflow-tooltip>
|
<el-table-column :label="$tc('Receivers')">
|
||||||
<template v-slot="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="!scope.row.children">
|
<el-popover trigger="hover" placement="top" popper-class="black-theme-popover">
|
||||||
{{ scope.row.receivers.map(item => item.name).join(', ') }}
|
<p v-for="item in scope.row.receivers" :key="item.name">{{ item.name }}</p>
|
||||||
</span>
|
<span v-if="!scope.row.children" slot="reference" class="name-wrapper">
|
||||||
|
{{ scope.row.receivers.map(item => item.name).join(', ') }}
|
||||||
|
</span>
|
||||||
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$tc('Actions')" width="200">
|
<el-table-column :label="$tc('Actions')" width="200">
|
||||||
@@ -186,4 +189,25 @@ export default {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep .el-table .cell {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.name-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
max-height: 55px;
|
||||||
|
max-width: 200px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .black-theme-popover .el-popover__inner {
|
||||||
|
background-color: #000 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
border-color: #000 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export default {
|
|||||||
this.$t('Database'),
|
this.$t('Database'),
|
||||||
[
|
[
|
||||||
'JOB_EXECUTION_KEEP_DAYS',
|
'JOB_EXECUTION_KEEP_DAYS',
|
||||||
'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS'
|
'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS',
|
||||||
|
'ACCOUNT_CHANGE_SECRET_RECORD_KEEP_DAYS'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ export default {
|
|||||||
detailCardItems() {
|
detailCardItems() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: this.$t('Name'),
|
key: this.$t('TaskPath'),
|
||||||
value: this.object.name
|
value: this.object.name
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: this.$t('Comment'),
|
key: this.$t('Name'),
|
||||||
value: this.object.meta.comment
|
value: this.object.meta.comment
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -44,6 +44,10 @@ export default {
|
|||||||
{
|
{
|
||||||
key: this.$t('LastPublishedTime'),
|
key: this.$t('LastPublishedTime'),
|
||||||
value: this.object.last_published_time
|
value: this.object.last_published_time
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: this.$t('Description'),
|
||||||
|
value: this.object.meta.description
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default {
|
|||||||
'redis_port', 'sqlserver_port', 'oracle_port_range'
|
'redis_port', 'sqlserver_port', 'oracle_port_range'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[this.$t('Other'), ['comment']]
|
[this.$t('Other'), ['is_active', 'comment']]
|
||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
host: {
|
host: {
|
||||||
@@ -41,13 +41,17 @@ export default {
|
|||||||
},
|
},
|
||||||
oracle_port_range: {
|
oracle_port_range: {
|
||||||
disabled: true
|
disabled: true
|
||||||
|
},
|
||||||
|
is_active: {
|
||||||
|
disabled: this.$route.params.id === '00000000-0000-0000-0000-000000000001'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hasDetailInMsg: false
|
hasDetailInMsg: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {},
|
computed: {},
|
||||||
created() {},
|
created() {
|
||||||
|
},
|
||||||
methods: {}
|
methods: {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'name', 'host', 'actions',
|
'name', 'host', 'actions',
|
||||||
'http_port', 'https_port', 'ssh_port', 'rdp_port',
|
'http_port', 'https_port', 'ssh_port', 'rdp_port',
|
||||||
'mysql_port', 'mariadb_port', 'postgresql_port',
|
'mysql_port', 'mariadb_port', 'postgresql_port',
|
||||||
'redis_port', 'sqlserver_port', 'oracle_port_range'
|
'redis_port', 'sqlserver_port', 'oracle_port_range', 'is_active'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
columnsMeta: {
|
columnsMeta: {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<el-form-item style="float: right">
|
<el-form-item style="float: right">
|
||||||
<template v-if="hasActionPerm">
|
<template v-if="hasActionPerm">
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="object.status.value === 'closed'"
|
:disabled="isDisabled || object.status.value === 'closed'"
|
||||||
size="small"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleApprove"
|
@click="handleApprove"
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<i class="fa fa-check" /> {{ $t('Accept') }}
|
<i class="fa fa-check" /> {{ $t('Accept') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="object.status.value === 'closed'"
|
:disabled="isDisabled || object.status.value === 'closed'"
|
||||||
size="small"
|
size="small"
|
||||||
type="warning"
|
type="warning"
|
||||||
@click="handleReject"
|
@click="handleReject"
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isSelfTicket"
|
v-if="isSelfTicket"
|
||||||
:disabled="object.status.value === 'closed'"
|
:disabled="isDisabled || object.status.value === 'closed'"
|
||||||
size="small"
|
size="small"
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleClose"
|
@click="handleClose"
|
||||||
@@ -92,6 +92,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
isDisabled: false,
|
||||||
comments: '',
|
comments: '',
|
||||||
type_api: '',
|
type_api: '',
|
||||||
imageUrl: require('@/assets/img/avatar.png'),
|
imageUrl: require('@/assets/img/avatar.png'),
|
||||||
@@ -154,17 +155,35 @@ export default {
|
|||||||
this.createComment(function() {
|
this.createComment(function() {
|
||||||
})
|
})
|
||||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/approve/`
|
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/approve/`
|
||||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
this.$axios.put(url).then(res => {
|
||||||
|
this.reloadPage()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err)
|
||||||
|
}).finally(() => {
|
||||||
|
this.isDisabled = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
defaultReject() {
|
defaultReject() {
|
||||||
this.createComment(function() {
|
this.createComment(function() {
|
||||||
})
|
})
|
||||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/reject/`
|
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/reject/`
|
||||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
this.$axios.put(url).then(res => {
|
||||||
|
this.reloadPage()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err)
|
||||||
|
}).finally(() => {
|
||||||
|
this.isDisabled = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
defaultClose() {
|
defaultClose() {
|
||||||
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/close/`
|
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/close/`
|
||||||
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
|
this.$axios.put(url).then(res => {
|
||||||
|
this.reloadPage()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message.error(err)
|
||||||
|
}).finally(() => {
|
||||||
|
this.isDisabled = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
createComment(successCallback) {
|
createComment(successCallback) {
|
||||||
const commentText = this.form.comments
|
const commentText = this.form.comments
|
||||||
@@ -185,17 +204,42 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
handleAction(actionType) {
|
||||||
|
if (this.isDisabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isDisabled = true
|
||||||
|
let handler
|
||||||
|
switch (actionType) {
|
||||||
|
case 'approve':
|
||||||
|
handler = this.approve || this.defaultApprove
|
||||||
|
break
|
||||||
|
case 'reject':
|
||||||
|
handler = this.reject || this.defaultReject
|
||||||
|
break
|
||||||
|
case 'close':
|
||||||
|
handler = this.close || this.defaultClose
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
handler = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler()
|
||||||
|
} else {
|
||||||
|
this.$message.error('No handler for action')
|
||||||
|
}
|
||||||
|
},
|
||||||
handleApprove() {
|
handleApprove() {
|
||||||
const handler = this.approve || this.defaultApprove
|
this.handleAction('approve')
|
||||||
handler()
|
|
||||||
},
|
},
|
||||||
handleReject() {
|
handleReject() {
|
||||||
const handler = this.reject || this.defaultReject
|
this.handleAction('reject')
|
||||||
handler()
|
|
||||||
},
|
},
|
||||||
handleClose() {
|
handleClose() {
|
||||||
const handler = this.close || this.defaultClose
|
this.handleAction('close')
|
||||||
handler()
|
|
||||||
},
|
},
|
||||||
handleComment() {
|
handleComment() {
|
||||||
this.createComment(
|
this.createComment(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||||
import TransSelect from '@/components/Form/FormFields/TransferSelect.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -23,7 +22,6 @@ export default {
|
|||||||
],
|
],
|
||||||
fieldsMeta: {
|
fieldsMeta: {
|
||||||
users: {
|
users: {
|
||||||
component: TransSelect,
|
|
||||||
el: {
|
el: {
|
||||||
url: '/api/v1/users/users/?fields_size=mini&order=name',
|
url: '/api/v1/users/users/?fields_size=mini&order=name',
|
||||||
ajax: {
|
ajax: {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export default {
|
|||||||
role: { name: '', comment: '', users: [] },
|
role: { name: '', comment: '', users: [] },
|
||||||
config: {
|
config: {
|
||||||
titlePrefix: scope === 'org' ? vm.$t('OrgRole') : vm.$t('SystemRole'),
|
titlePrefix: scope === 'org' ? vm.$t('OrgRole') : vm.$t('SystemRole'),
|
||||||
|
getObjectName: (obj) => { return obj.display_name },
|
||||||
url: `/api/v1/rbac/${scope}-roles`,
|
url: `/api/v1/rbac/${scope}-roles`,
|
||||||
activeMenu: 'RoleInfo',
|
activeMenu: 'RoleInfo',
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import AssetPermissionAsset from '@/views/perms/AssetPermission/AssetPermissionD
|
|||||||
import AssetPermissionDetail from '@/views/perms/AssetPermission/AssetPermissionDetail/index.vue'
|
import AssetPermissionDetail from '@/views/perms/AssetPermission/AssetPermissionDetail/index.vue'
|
||||||
import AssetPermissionAccount from '@/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAccount.vue'
|
import AssetPermissionAccount from '@/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAccount.vue'
|
||||||
import UserAssetPermissionRules from './UserAssetPermissionRules'
|
import UserAssetPermissionRules from './UserAssetPermissionRules'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -63,7 +64,7 @@ export default {
|
|||||||
{
|
{
|
||||||
title: this.$t('UserAclLists'),
|
title: this.$t('UserAclLists'),
|
||||||
name: 'UserLoginAcl',
|
name: 'UserLoginAcl',
|
||||||
hidden: () => !vm.$hasPerm('acls.view_loginacl')
|
hidden: () => !vm.$hasPerm('acls.view_loginacl') || !store.getters.publicSettings.XPACK_LICENSE_IS_VALID
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('UserSession'),
|
title: this.$t('UserSession'),
|
||||||
|
|||||||