mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 11:55:34 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
607eec6c44 | ||
|
|
28daf41fb5 | ||
|
|
e45720af1b |
@@ -1,4 +1,3 @@
|
||||
lina
|
||||
dist
|
||||
node_modules
|
||||
.git
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# 全局环境变量 请勿随意改动
|
||||
ENV = 'development'
|
||||
|
||||
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = ''
|
||||
VUE_APP_PUBLIC_PATH = '/ui/'
|
||||
@@ -20,6 +22,5 @@ VUE_APP_LOGOUT_PATH = '/core/auth/logout/'
|
||||
|
||||
|
||||
# Dev server for core proxy
|
||||
VUE_APP_CORE_HOST = 'http://localhost:8080/'
|
||||
VUE_APP_CORE_WS = 'http://localhost:8080/'
|
||||
VUE_APP_CORE_HOST = 'http://localhost:8080'
|
||||
VUE_APP_ENV = 'development'
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,4 +15,3 @@ tests/**/coverage/
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.env.development
|
||||
|
||||
@@ -12,8 +12,8 @@ RUN npm config set sass_binary_site=${SASS_BINARY_SITE}
|
||||
RUN npm config set registry ${NPM_REGISTRY}
|
||||
RUN yarn config set registry ${NPM_REGISTRY}
|
||||
COPY package.json yarn.lock /data/
|
||||
RUN yarn install
|
||||
RUN npm rebuild node-sass
|
||||
COPY utils /data/utils/
|
||||
RUN ls && cd utils && bash -xieu build.sh dep
|
||||
|
||||
ADD . /data
|
||||
RUN cd utils && bash -xieu build.sh build
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
]
|
||||
}
|
||||
|
||||
10
nginx.conf
10
nginx.conf
@@ -1,16 +1,6 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 4 16k;
|
||||
#gzip_http_version 1.0;
|
||||
gzip_comp_level 8;
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
gzip_vary off;
|
||||
gzip_static on;
|
||||
gzip_disable "MSIE [1-6].";
|
||||
|
||||
location /ui/ {
|
||||
try_files $uri / /ui/index.html;
|
||||
alias /opt/lina/;
|
||||
|
||||
13
package.json
13
package.json
@@ -15,24 +15,18 @@
|
||||
"test:ci": "npm run lint && npm run test:unit",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icas/svgo.yml",
|
||||
"vue-i18n-extract": "vue-i18n-extract",
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'",
|
||||
"vue-i18n-report-json": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -o /tmp/abc.json",
|
||||
"vue-i18n-report-add-miss": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json' -a"
|
||||
"vue-i18n-report": "vue-i18n-extract report -v './src/**/*.?(js|vue)' -l './src/i18n/langs/**/*.json'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@ztree/ztree_v3": "3.5.44",
|
||||
"axios": "0.21.1",
|
||||
"axios": "0.18.1",
|
||||
"axios-retry": "^3.1.9",
|
||||
"cron-parser": "^4.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"echarts": "^4.7.0",
|
||||
"element-ui": "2.13.2",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"install": "^0.13.0",
|
||||
"jquery": "^3.5.0",
|
||||
"js-cookie": "2.2.0",
|
||||
"krry-transfer": "^1.7.3",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
@@ -47,10 +41,8 @@
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.topairs": "^4.3.0",
|
||||
"lodash.values": "^4.3.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-parseformat": "^3.0.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"npm": "^7.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"vue": "2.6.10",
|
||||
@@ -81,7 +73,6 @@
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-jest": "23.6.0",
|
||||
"chalk": "2.4.2",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"connect": "3.6.6",
|
||||
"element-theme-chalk": "^2.13.1",
|
||||
"eslint": "^5.15.3",
|
||||
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
@@ -8,24 +8,8 @@
|
||||
<meta http-equiv="Cache-control" content="no-cache">
|
||||
<meta http-equiv="Cache" content="no-cache">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
<style>
|
||||
::-webkit-scrollbar {
|
||||
width:14px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
border-radius:10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
box-shadow: 8px 10px 20px #C6C6C6 inset;
|
||||
border: 3px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
box-shadow: 8px 10px 20px #878787 inset;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getOrgDetail(oid) {
|
||||
return request({
|
||||
url: `/api/v1/orgs/orgs/current/?oid=${oid}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getCurrentOrg() {
|
||||
return request({
|
||||
url: `/api/v1/orgs/orgs/current/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -57,26 +57,6 @@ export function TestReplayStorage(id) {
|
||||
})
|
||||
}
|
||||
|
||||
function SetToDefaultStorage(url) {
|
||||
return request({
|
||||
url: url,
|
||||
method: 'patch',
|
||||
data: { 'is_default': true }
|
||||
})
|
||||
}
|
||||
|
||||
export function SetToDefaultCommandStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/command-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function SetToDefaultReplayStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/replay-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function getReplayStorage(id) {
|
||||
return request({
|
||||
url: `/api/v1/terminal/replay-storages/${id}/`,
|
||||
|
||||
@@ -51,7 +51,7 @@ export function refreshLdapUserCache() {
|
||||
})
|
||||
}
|
||||
|
||||
export function startLdapUserCache() {
|
||||
export function StartLdapUserCache() {
|
||||
return request({
|
||||
disableFlashErrorMsg: true,
|
||||
url: '/api/v1/settings/ldap/users/?cache_police=1',
|
||||
|
||||
BIN
src/assets/img/logo-text.png
Normal file
BIN
src/assets/img/logo-text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src/assets/img/logo.png
Normal file
BIN
src/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
@MFAVerifyDone="getAuthInfo"
|
||||
@MFAVerifyCancel="exit"
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showAuthInfo"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="80px" :model="authInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<el-input v-model="authInfo['private_key']" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTitle: this.$t('common.ViewSecret'),
|
||||
authInfo: {},
|
||||
showAuthInfo: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAuthInfo()
|
||||
},
|
||||
methods: {
|
||||
getAuthInfo() {
|
||||
const url = `/api/v1/assets/account-secrets/${this.account.id}/`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.authInfo = resp
|
||||
this.showAuthInfo = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
width="50"
|
||||
:title="this.$t('assets.UpdateAssetUserToken')"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
@confirm="handleConfirm()"
|
||||
@cancel="handleCancel()"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form label-position="right" label-width="80px">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<input type="file" @change="onPrivateKeyLoaded">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
export default {
|
||||
name: 'UpdateSecretInfo',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
authInfo: {
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleConfirm() {
|
||||
const data = {}
|
||||
if (this.authInfo.password !== '') {
|
||||
data.password = this.authInfo.password
|
||||
}
|
||||
if (this.authInfo.private_key !== '') {
|
||||
data.private_key = this.authInfo.private_key
|
||||
}
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/accounts/${this.account.id}/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.authInfo = { password: '', private_key: '' }
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$emit('updateAuthDone', res)
|
||||
this.$emit('update:visible', false)
|
||||
}).catch(err => {
|
||||
const errMsg = Object.values(err.response.data).join(', ')
|
||||
this.$message.error(this.$tc('common.updateErrorMsg') + ' ' + errMsg)
|
||||
this.$emit('update:visible', true)
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
onPrivateKeyLoaded(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.authInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,36 +0,0 @@
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import ChoicesFormatter from '@/components/TableFormatters/ChoicesFormatter'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const connectivityMeta = {
|
||||
label: i18n.t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
ok: 'fa-check',
|
||||
failed: 'fa-times',
|
||||
unknown: 'fa-circle-o'
|
||||
},
|
||||
classChoices: {
|
||||
ok: 'text-primary',
|
||||
failed: 'text-danger',
|
||||
unknown: 'text-warning'
|
||||
},
|
||||
hasTips: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
const mapper = {
|
||||
'ok': i18n.t('assets.Reachable'),
|
||||
'failed': i18n.t('assets.Unreachable'),
|
||||
'unknown': i18n.t('assets.Unknown')
|
||||
}
|
||||
let tips = mapper[cellValue]
|
||||
if (row['date_verified']) {
|
||||
const datetime = toSafeLocalDateStr(row['date_verified'])
|
||||
tips += '<br> ' + datetime
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
|
||||
<UpdateSecretInfo :visible.sync="showUpdateSecretDialog" :account="account" @updateAuthDone="onUpdateAuthDone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import { ActionsFormatter, DetailFormatter, DisplayFormatter } from '@/components/TableFormatters'
|
||||
import ShowSecretInfo from './ShowSecretInfo'
|
||||
import UpdateSecretInfo from './UpdateSecretInfo'
|
||||
import { connectivityMeta } from './const'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountListTable',
|
||||
components: {
|
||||
ListTable,
|
||||
UpdateSecretInfo,
|
||||
ShowSecretInfo
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/assets/accounts/', '/assets/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showUpdateSecretDialog: false,
|
||||
account: {},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
'hostname', 'ip', 'username', 'version', 'connectivity',
|
||||
'systemuser', 'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['username', 'actions'],
|
||||
default: ['hostname', 'ip', 'username', 'version', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ip: {
|
||||
width: '120px'
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
systemuser: {
|
||||
formatter: DisplayFormatter
|
||||
},
|
||||
version: {
|
||||
width: '70px'
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showViewSecretDialog = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.delete(`/api/v1/assets/accounts/${row.id}/`).then(() => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/${row.id}/verify/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showUpdateSecretDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
searchConfig: {
|
||||
exclude: ['systemuser', 'asset']
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpdateAuthDone(account) {
|
||||
Object.assign(this.account, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,15 +1,40 @@
|
||||
<template>
|
||||
<DataActions :actions="iActions" v-bind="$attrs" />
|
||||
<div :class="grouped ? 'el-button-group' : ''">
|
||||
<el-button v-for="item in iActions" :key="item.name" :size="size" v-bind="item" @click="handleClick(item.name)">
|
||||
<el-tooltip v-if="item.tip" effect="dark" :content="item.tip" placement="top">
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
<i v-if="item.fa" :class="'fa ' + item.fa" />{{ item.title }}
|
||||
</span>
|
||||
</el-button>
|
||||
<el-dropdown v-if="iMoreActions.length > 0" trigger="click" :placement="moreActionsPlacement" @command="handleClick">
|
||||
<el-button :size="size" :type="moreActionsType" class="btn-more-actions">
|
||||
{{ iMoreActionsTitle }}<i class="el-icon-arrow-down el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="item in iMoreActions" :key="item.name" :command="item.name" v-bind="item" @click="handleClick(item.name)">{{ item.title }} </el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataActions from '@/components/DataActions'
|
||||
export default {
|
||||
name: 'ActionsGroup',
|
||||
components: {
|
||||
DataActions
|
||||
},
|
||||
props: {
|
||||
grouped: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'small'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
@@ -36,22 +61,79 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
const actions = [...this.actions]
|
||||
if (this.iMoreAction && this.iMoreAction.dropdown.length > 0) {
|
||||
actions.push(this.iMoreAction)
|
||||
return this.cleanActions(this.actions)
|
||||
},
|
||||
iMoreActions() {
|
||||
return this.cleanActions(this.moreActions)
|
||||
},
|
||||
totalActions() {
|
||||
return [...this.actions, ...this.moreActions]
|
||||
},
|
||||
totalNamedActions() {
|
||||
const actions = {}
|
||||
for (const action of this.totalActions) {
|
||||
if (!action || !action.hasOwnProperty('name')) {
|
||||
continue
|
||||
}
|
||||
actions[action.name] = action
|
||||
}
|
||||
return actions
|
||||
},
|
||||
iMoreAction() {
|
||||
return {
|
||||
name: 'moreActions',
|
||||
title: this.iMoreActionsTitle,
|
||||
dropdown: this.moreActions || []
|
||||
}
|
||||
},
|
||||
iMoreActionsTitle() {
|
||||
return this.moreActionsTitle || this.$t('common.MoreActions')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(item) {
|
||||
const action = this.totalNamedActions[item]
|
||||
if (action && action.callback) {
|
||||
action.callback(action)
|
||||
} else {
|
||||
this.$log.debug('No callback found')
|
||||
}
|
||||
this.$emit('actionClick', item)
|
||||
},
|
||||
checkItem(item, attr, defaults) {
|
||||
if (!item) {
|
||||
return true
|
||||
}
|
||||
let ok = item[attr]
|
||||
if (ok && typeof ok === 'function') {
|
||||
ok = ok(item)
|
||||
} else if (ok == null) {
|
||||
ok = defaults === undefined ? true : defaults
|
||||
}
|
||||
return ok
|
||||
},
|
||||
cleanActions(actions) {
|
||||
const cleanedActions = []
|
||||
const cloneActions = _.cloneDeep(actions)
|
||||
for (const v of cloneActions) {
|
||||
if (!v) {
|
||||
continue
|
||||
}
|
||||
const action = Object.assign({}, v)
|
||||
// 是否拥有这个action
|
||||
const has = this.checkItem(action, 'has')
|
||||
delete action['has']
|
||||
if (!has) {
|
||||
continue
|
||||
}
|
||||
// 是否有分割线
|
||||
const divided = this.checkItem(action, 'divided', false)
|
||||
delete action['divided']
|
||||
action.divided = divided
|
||||
|
||||
// 是否是disabled
|
||||
const can = this.checkItem(action, 'can')
|
||||
delete action['can']
|
||||
action.disabled = !can
|
||||
cleanedActions.push(action)
|
||||
// 删掉callback,避免前台看到
|
||||
delete action['callback']
|
||||
}
|
||||
return cleanedActions
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<template>
|
||||
<el-alert
|
||||
v-if="enabled && !isViewed()"
|
||||
class="announcement"
|
||||
type="success"
|
||||
:center="false"
|
||||
:title="title"
|
||||
@close="onClose"
|
||||
>
|
||||
<span class="announcement-main"> {{ announcement.content }}</span>
|
||||
<span v-if="announcement.link">
|
||||
<el-link :href="announcement.link" target="_blank" class="link-more">
|
||||
{{ $t('common.ViewMore') }}
|
||||
</el-link>
|
||||
<i class="fa fa-share-square-o" />
|
||||
</span>
|
||||
</el-alert>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Announcement',
|
||||
data() {
|
||||
return {
|
||||
viewedKey: 'AnnouncementViewed'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'publicSettings'
|
||||
]),
|
||||
announcement() {
|
||||
const ann = this.publicSettings.ANNOUNCEMENT
|
||||
return { id: ann['ID'], subject: ann['SUBJECT'], content: ann['CONTENT'], link: ann['LINK'] }
|
||||
},
|
||||
enabled() {
|
||||
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject)
|
||||
},
|
||||
title() {
|
||||
return this.$t('common.Announcement') + ': ' + this.announcement.subject
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClose() {
|
||||
localStorage.setItem(this.viewedKey, this.announcement.id)
|
||||
},
|
||||
isViewed() {
|
||||
const viewedId = localStorage.getItem(this.viewedKey)
|
||||
return viewedId === this.announcement.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.announcement >>> .el-alert__content {
|
||||
width: 100%;
|
||||
}
|
||||
.announcement-main {
|
||||
word-wrap:break-word;
|
||||
}
|
||||
.link-more {
|
||||
font-size: 10px;
|
||||
margin-left: 10px;
|
||||
border-bottom: solid 1px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
@MFAVerifyDone="getAuthInfo"
|
||||
@MFAVerifyCancel="exit"
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showAuthInfo"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="80px" :model="authInfo">
|
||||
<el-form-item :label="this.$t('applications.appName')">
|
||||
<el-input v-model="account['app_display']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTitle: this.$t('common.ViewSecret'),
|
||||
authInfo: {},
|
||||
showAuthInfo: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAuthInfo()
|
||||
},
|
||||
methods: {
|
||||
getAuthInfo() {
|
||||
const url = `/api/v1/applications/account-secrets/${this.account.id}/`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.authInfo = resp
|
||||
this.showAuthInfo = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const connectivityMeta = {
|
||||
label: i18n.t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
failed: 'fa-times text-danger',
|
||||
unknown: 'fa-circle text-warning'
|
||||
},
|
||||
hasTips: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
const mapper = {
|
||||
'ok': i18n.t('assets.Reachable'),
|
||||
'failed': i18n.t('assets.Unreachable'),
|
||||
'unknown': i18n.t('assets.Unknown')
|
||||
}
|
||||
let tips = mapper[cellValue]
|
||||
if (row['date_verified']) {
|
||||
const datetime = toSafeLocalDateStr(row['date_verified'])
|
||||
tips += '<br> ' + datetime
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import { ActionsFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
import ShowSecretInfo from './ShowSecretInfo'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
ShowSecretInfo
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/applications/accounts/', '/applications/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showUpdateSecretDialog: false,
|
||||
account: {},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
'app_display', 'username', 'category_display',
|
||||
'type_display', 'systemuser', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
app_display: {
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
switch (row['category']) {
|
||||
case 'remote_app':
|
||||
return {
|
||||
name: 'RemoteAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
case 'db':
|
||||
return {
|
||||
name: 'DatabaseAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
default:
|
||||
return {
|
||||
name: 'KubernetesAppDetail',
|
||||
params: { id: row.app }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
systemuser: {
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getTitle({ row }) {
|
||||
return row.systemuser_display
|
||||
},
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'SystemUserDetail',
|
||||
params: { id: row.systemuser }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showViewSecretDialog = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function({ row }) {
|
||||
this.$message.success(this.$tc('applications.updateAccountMsg'))
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
searchConfig: {
|
||||
exclude: ['systemuser', 'asset']
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpdateAuthDone(account) {
|
||||
Object.assign(this.account, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -3,12 +3,12 @@
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<AssetSelect ref="assetSelect" :disabled="disabled" :can-select="canSelect" />
|
||||
<AssetSelect ref="assetSelect" :can-select="canSelect" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<el-button :type="type" size="small" :disabled="disabled" @click="addObjects">{{ $t('common.Add') }}</el-button>
|
||||
<el-button :type="type" size="small" @click="addObjects">{{ $t('common.Add') }}</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -38,10 +38,6 @@ export default {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, Function],
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: [Array, Number, String],
|
||||
default: () => []
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
<template>
|
||||
<div class="asset-select-dialog">
|
||||
<Select2
|
||||
ref="select2"
|
||||
v-model="select2Config.value"
|
||||
v-bind="select2Config"
|
||||
@input="onInputChange"
|
||||
@focus.stop="handleFocus"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<Select2 ref="select2" v-bind="select2Config" @input="onInputChange" @focus.stop="handleFocus" v-on="$listeners" />
|
||||
<Dialog
|
||||
v-if="dialogVisible"
|
||||
:title="this.$t('assets.Assets')"
|
||||
@@ -30,8 +23,8 @@
|
||||
|
||||
<script>
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import { DetailFormatter } from '@/components/ListTable/formatters'
|
||||
import Select2 from '@/components/Select2'
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
@@ -47,10 +40,6 @@ export default {
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, Function],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -83,7 +72,7 @@ export default {
|
||||
select2Config: select2Config,
|
||||
dialogSelect2Config: select2Config,
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/assets/?fields_size=mini',
|
||||
url: '/api/v1/assets/assets/',
|
||||
hasTree: true,
|
||||
canSelect: this.canSelect,
|
||||
columns: [
|
||||
@@ -101,18 +90,6 @@ export default {
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ipDomain'),
|
||||
sortable: 'custom'
|
||||
},
|
||||
{
|
||||
prop: 'platform',
|
||||
label: this.$t('assets.Platform'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
prop: 'protocols',
|
||||
formatter: function(row) {
|
||||
return <span> {row.protocols.toString()} </span>
|
||||
},
|
||||
label: this.$t('assets.Protocols')
|
||||
}
|
||||
],
|
||||
listeners: {
|
||||
|
||||
364
src/components/AssetUserTable/index.vue
Normal file
364
src/components/AssetUserTable/index.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template><div>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||
<div v-if="MFAConfirmed">
|
||||
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="MFAInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="MFAInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="MFAInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-row v-else :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAInput" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
<Dialog width="50" :title="this.$t('assets.UpdateAssetUserToken')" :visible.sync="showDialog" @confirm="handleConfirm()" @cancel="handleCancel()">
|
||||
<el-form label-position="right" label-width="80px" :model="dialogInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="dialogInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="dialogInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="dialogInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.sshkey')">
|
||||
<input type="file" @change="Onchange">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { ActionsFormatter, DateFormatter } from '@/components/ListTable/formatters'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
hasImport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasExport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAConfirmed: false,
|
||||
MFAInput: '',
|
||||
MFAInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
},
|
||||
showDialog: false,
|
||||
showMFADialog: false,
|
||||
dialogInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
key: ''
|
||||
},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
{
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'ip',
|
||||
label: this.$t('assets.ip'),
|
||||
width: '120px'
|
||||
},
|
||||
{
|
||||
prop: 'username',
|
||||
label: this.$t('assets.Username'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'version',
|
||||
label: this.$t('assets.Version'),
|
||||
width: '70px'
|
||||
},
|
||||
{
|
||||
prop: 'date_created',
|
||||
label: this.$t('assets.date_joined'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function(val) {
|
||||
this.MFAInfo.asset = val.cellValue
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = true
|
||||
this.MFAConfirmed = true
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: (val) => {
|
||||
this.$axios.delete(`/api/v1/assets/asset-users/${val.cellValue}/`).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: (val) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/tasks/?id=${val.cellValue}`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
callback: function(val) {
|
||||
this.showDialog = true
|
||||
this.dialogInfo.asset = val.row.asset
|
||||
this.dialogInfo.hostname = val.row.hostname
|
||||
this.dialogInfo.username = val.row.username
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
extraQuery: {
|
||||
latest: 1
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasBulkDelete: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
hasSearch: true,
|
||||
searchConfig: {
|
||||
options: [
|
||||
{
|
||||
label: this.$t('assets.OnlyLatestVersion'),
|
||||
value: 'latest',
|
||||
children: [
|
||||
{
|
||||
label: this.$t('common.Yes'),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: this.$t('common.No'),
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
return false
|
||||
}
|
||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||
const now = new Date()
|
||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.handleExport) {
|
||||
this.headerActions.handleExport = this.handleExport
|
||||
}
|
||||
if (this.handleImport) {
|
||||
this.headerActions.handleImport = this.handleImport
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
MFAConfirm() {
|
||||
if (this.MFAInput.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAInput
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
this.$store.dispatch('users/setMFAVerify')
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
}
|
||||
)
|
||||
},
|
||||
handleMFAConfirm() {
|
||||
this.MFAInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
}
|
||||
this.MFAInput = ''
|
||||
this.showMFADialog = false
|
||||
this.MFAConfirmed = false
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
Onchange(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.dialogInfo.key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
},
|
||||
handleConfirm() {
|
||||
const data = {
|
||||
asset: this.dialogInfo.asset,
|
||||
username: this.dialogInfo.username
|
||||
}
|
||||
if (this.dialogInfo.password !== '') {
|
||||
data.password = this.dialogInfo.password
|
||||
}
|
||||
if (this.dialogInfo.key !== '') {
|
||||
data.key = this.dialogInfo.key
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<DataForm
|
||||
:fields="iFields"
|
||||
:form="value"
|
||||
style="margin-left: -26%;margin-right: -6%"
|
||||
v-bind="kwargs"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataForm from '@/components/DataForm'
|
||||
|
||||
export default {
|
||||
name: 'NestedField',
|
||||
components: {
|
||||
DataForm
|
||||
},
|
||||
props: {
|
||||
fields: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
errors: {
|
||||
type: [Object, String],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
kwargs: {
|
||||
hasReset: false,
|
||||
hasSaveContinue: false,
|
||||
hasButtons: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iFields() {
|
||||
const fields = this.fields
|
||||
if (this.errors && typeof this.errors === 'object') {
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [name, error] of Object.entries(this.errors)) {
|
||||
const field = fields.find((v) => v.prop === name)
|
||||
if (!field) {
|
||||
continue
|
||||
}
|
||||
this.$log.debug(`${name}: ${error}`)
|
||||
if (typeof error === 'object' && !Array.isArray(error)) {
|
||||
error = this.objectToString(error)
|
||||
}
|
||||
field.attrs.error = error.toString()
|
||||
}
|
||||
}
|
||||
this.$log.debug('Fields change: ', fields, this.errors)
|
||||
return fields
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
objectToString(obj) {
|
||||
let data = ''
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [key, value] of Object.entries(obj)) {
|
||||
if (typeof value === 'object') {
|
||||
value = this.objectToString(value)
|
||||
}
|
||||
data += ` ${key}: ${value} `
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,20 +1,15 @@
|
||||
<template>
|
||||
<DataForm ref="dataForm" v-loading="loading" :fields="totalFields" :form="iForm" v-bind="$attrs" v-on="$listeners">
|
||||
<FormGroupHeader
|
||||
v-for="(group, i) in groups"
|
||||
:slot="'id:'+group.name"
|
||||
:key="'group-'+group.name"
|
||||
:group="group"
|
||||
:index="i"
|
||||
:line="i !== 0"
|
||||
/>
|
||||
<DataForm ref="dataForm" v-loading="loading" :fields="totalFields" v-bind="$attrs" v-on="$listeners">
|
||||
<FormGroupHeader v-for="(group, i) in groups" :slot="'id:'+group.name" :key="'group-'+group.name" :title="group.title" :line="i != 0" />
|
||||
</DataForm>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataForm from '../DataForm'
|
||||
import FormGroupHeader from '@/components/FormGroupHeader'
|
||||
import { FormFieldGenerator } from '@/components/AutoDataForm/utils'
|
||||
// import { optionUrlMeta } from '@/api/common'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import Select2 from '@/components/Select2'
|
||||
export default {
|
||||
name: 'AutoDataForm',
|
||||
components: {
|
||||
@@ -36,10 +31,6 @@ export default {
|
||||
return []
|
||||
}
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
fieldsMeta: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
@@ -47,66 +38,171 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
remoteMeta: {},
|
||||
meta: {},
|
||||
totalFields: [],
|
||||
loading: true,
|
||||
groups: [],
|
||||
iForm: this.form,
|
||||
errors: {}
|
||||
groups: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.optionUrlMetaAndGenerateColumns()
|
||||
this.optionUrlMeta()
|
||||
},
|
||||
methods: {
|
||||
optionUrlMetaAndGenerateColumns() {
|
||||
optionUrlMeta() {
|
||||
this.$store.dispatch('common/getUrlMeta', { url: this.url }).then(data => {
|
||||
this.remoteMeta = data.actions[this.method.toUpperCase()] || {}
|
||||
this.meta = data.actions[this.method.toUpperCase()] || {}
|
||||
this.generateColumns()
|
||||
this.cleanFormValue()
|
||||
}).catch(err => {
|
||||
this.$log.error(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
generateColumns() {
|
||||
const generator = new FormFieldGenerator()
|
||||
this.totalFields = generator.generateFields(this.fields, this.fieldsMeta, this.remoteMeta)
|
||||
this.groups = generator.groups
|
||||
this.$log.debug('Total fields: ', this.totalFields)
|
||||
},
|
||||
_cleanFormValue(form, remoteMeta) {
|
||||
for (const [k, v] of Object.entries(remoteMeta)) {
|
||||
if (v.default === undefined) {
|
||||
continue
|
||||
}
|
||||
const valueSet = form[k]
|
||||
if (valueSet !== undefined) {
|
||||
continue
|
||||
}
|
||||
if (v.type === 'nested object' && typeof valueSet === 'object') {
|
||||
this._cleanFormValue(valueSet, v.children)
|
||||
}
|
||||
form[k] = v.default
|
||||
generateFieldByType(type, field, fieldMeta) {
|
||||
switch (type) {
|
||||
case 'choice':
|
||||
type = 'radio-group'
|
||||
if (!fieldMeta.read_only) {
|
||||
field.options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'datetime':
|
||||
type = 'date-picker'
|
||||
field.el = {
|
||||
type: 'datetime'
|
||||
}
|
||||
break
|
||||
case 'field':
|
||||
type = ''
|
||||
field.component = Select2
|
||||
if (fieldMeta.required) {
|
||||
field.el.clearable = false
|
||||
}
|
||||
break
|
||||
case 'string':
|
||||
type = 'input'
|
||||
if (!fieldMeta.max_length) {
|
||||
field.el.type = 'textarea'
|
||||
field.el.rows = 3
|
||||
}
|
||||
break
|
||||
default:
|
||||
type = 'input'
|
||||
break
|
||||
}
|
||||
if (type === 'radio-group') {
|
||||
if (!fieldMeta.read_only) {
|
||||
const options = fieldMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
if (options.length > 4) {
|
||||
type = 'select'
|
||||
field.el.filterable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
field.type = type
|
||||
return field
|
||||
},
|
||||
cleanFormValue() {
|
||||
this._cleanFormValue(this.iForm, this.remoteMeta)
|
||||
generateFieldByName(name, field) {
|
||||
switch (name) {
|
||||
case 'email':
|
||||
field.el.type = 'email'
|
||||
break
|
||||
case 'password':
|
||||
field.el.type = 'password'
|
||||
break
|
||||
case 'comment':
|
||||
field.el.type = 'textarea'
|
||||
break
|
||||
}
|
||||
return field
|
||||
},
|
||||
generateFieldByOther(field, fieldMeta) {
|
||||
const filedRules = field.rules || []
|
||||
if (fieldMeta.required) {
|
||||
if (field.type === 'input') {
|
||||
filedRules.push(rules.Required)
|
||||
} else {
|
||||
filedRules.push(rules.RequiredChange)
|
||||
}
|
||||
}
|
||||
field.rules = filedRules
|
||||
return field
|
||||
},
|
||||
generateField(name) {
|
||||
let field = { id: name, prop: name, el: {}, attrs: {}}
|
||||
// const fieldMeta = this.meta[name] || this.meta['attrs']['children'][name] || {}
|
||||
const fieldMeta = this.meta[name] || ((this.meta['attrs']) ? (this.meta['attrs']['children'][name]) : {})
|
||||
field.label = fieldMeta.label
|
||||
field = this.generateFieldByType(fieldMeta.type, field, fieldMeta)
|
||||
field = this.generateFieldByName(name, field)
|
||||
field = this.generateFieldByOther(field, fieldMeta)
|
||||
field = Object.assign(field, this.fieldsMeta[name] || {})
|
||||
_.set(field, 'attrs.error', '')
|
||||
return field
|
||||
},
|
||||
generateFieldGroup(data) {
|
||||
const [groupTitle, fields] = data
|
||||
this.groups.push({
|
||||
id: groupTitle,
|
||||
title: groupTitle,
|
||||
name: fields[0]
|
||||
})
|
||||
return this.generateFields(fields)
|
||||
},
|
||||
generateFieldAttrs(name) {
|
||||
const fields = []
|
||||
Object.keys(this.meta[name]['children']).forEach((key, i) => {
|
||||
const filed = this.generateField(key)
|
||||
fields.push(filed)
|
||||
})
|
||||
return fields
|
||||
},
|
||||
generateFields(data) {
|
||||
let fields = []
|
||||
for (let field of data) {
|
||||
if (field instanceof Array) {
|
||||
const items = this.generateFieldGroup(field)
|
||||
fields = [...fields, ...items]
|
||||
} else if (field === 'attrs') {
|
||||
const items = this.generateFieldAttrs(field)
|
||||
fields = [...fields, ...items]
|
||||
// 修改title插入ID
|
||||
this.groups[this.groups.length - 1].name = items[0].id
|
||||
} else if (typeof field === 'string') {
|
||||
field = this.generateField(field)
|
||||
fields.push(field)
|
||||
} else if (field instanceof Object) {
|
||||
this.errors[field.prop] = ''
|
||||
_.set(field, 'attrs.error', '')
|
||||
fields.push(field)
|
||||
}
|
||||
}
|
||||
return fields
|
||||
},
|
||||
generateColumns() {
|
||||
this.totalFields = this.generateFields(this.fields)
|
||||
this.$log.debug('Total fields: ', this.totalFields)
|
||||
},
|
||||
setFieldError(name, error) {
|
||||
const field = this.totalFields.find((v) => v.prop === name)
|
||||
if (!field) {
|
||||
return
|
||||
}
|
||||
if (field.attrs.error === error) {
|
||||
error += '.'
|
||||
}
|
||||
if (field.type === 'nestedField') {
|
||||
field.el.errors = error
|
||||
} else {
|
||||
field.attrs.error = error
|
||||
if (typeof error === 'object') {
|
||||
const str = error
|
||||
error = ''
|
||||
Object.keys(str).forEach(key => {
|
||||
error += `${parseInt(key) + 1}.${str[key][0]} `
|
||||
})
|
||||
}
|
||||
// if (field.attrs.error === error) {
|
||||
// error += '.'
|
||||
// }
|
||||
field.attrs.error = error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import NestedField from '@/components/AutoDataForm/components/NestedField'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
|
||||
export class FormFieldGenerator {
|
||||
constructor() {
|
||||
this.groups = []
|
||||
}
|
||||
generateFieldByType(type, field, fieldMeta, fieldRemoteMeta) {
|
||||
switch (type) {
|
||||
case 'choice':
|
||||
type = 'radio-group'
|
||||
if (!fieldRemoteMeta.read_only) {
|
||||
field.options = fieldRemoteMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'datetime':
|
||||
type = 'date-picker'
|
||||
field.el = {
|
||||
type: 'datetime'
|
||||
}
|
||||
break
|
||||
case 'field':
|
||||
type = ''
|
||||
field.component = Select2
|
||||
if (fieldRemoteMeta.required) {
|
||||
field.el.clearable = false
|
||||
}
|
||||
break
|
||||
case 'string':
|
||||
type = 'input'
|
||||
if (!fieldRemoteMeta['max_length']) {
|
||||
field.el.type = 'textarea'
|
||||
field.el.rows = 3
|
||||
}
|
||||
if (fieldRemoteMeta['write_only']) {
|
||||
field.el.type = 'password'
|
||||
}
|
||||
break
|
||||
case 'boolean':
|
||||
type = 'checkbox'
|
||||
break
|
||||
case 'nested object':
|
||||
type = 'nestedField'
|
||||
field.component = NestedField
|
||||
field.label = ''
|
||||
field.labelWidth = 0
|
||||
field.el.fields = this.generateNestFields(field, fieldMeta, fieldRemoteMeta)
|
||||
field.el.errors = {}
|
||||
Vue.$log.debug('All fields in generate: ', field.el.allFields)
|
||||
break
|
||||
default:
|
||||
type = 'input'
|
||||
break
|
||||
}
|
||||
if (type === 'radio-group') {
|
||||
if (!fieldRemoteMeta.read_only) {
|
||||
const options = fieldRemoteMeta.choices.map(v => {
|
||||
return { label: v.display_name, value: v.value }
|
||||
})
|
||||
if (options.length > 4) {
|
||||
type = 'select'
|
||||
field.el.filterable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
field.type = type
|
||||
return field
|
||||
}
|
||||
generateNestFields(field, fieldMeta, fieldRemoteMeta) {
|
||||
const fields = []
|
||||
const nestedFields = fieldMeta.fields || []
|
||||
const nestedFieldsMeta = fieldMeta.fieldsMeta || {}
|
||||
const nestedFieldsRemoteMeta = fieldRemoteMeta.children || {}
|
||||
for (const name of nestedFields) {
|
||||
const f = this.generateField(name, nestedFieldsMeta, nestedFieldsRemoteMeta)
|
||||
fields.push(f)
|
||||
}
|
||||
Vue.$log.debug('NestFields: ', fields)
|
||||
return fields
|
||||
}
|
||||
generateFieldByName(name, field) {
|
||||
switch (name) {
|
||||
case 'email':
|
||||
field.el.type = 'email'
|
||||
break
|
||||
case 'password':
|
||||
field.el.type = 'password'
|
||||
break
|
||||
case 'comment':
|
||||
field.el.type = 'textarea'
|
||||
break
|
||||
}
|
||||
return field
|
||||
}
|
||||
generateFieldByOther(field, fieldMeta, fieldRemoteMeta) {
|
||||
const filedRules = field.rules || []
|
||||
if (fieldRemoteMeta.required) {
|
||||
if (field.type === 'input') {
|
||||
filedRules.push(rules.Required)
|
||||
} else {
|
||||
filedRules.push(rules.RequiredChange)
|
||||
}
|
||||
}
|
||||
field.rules = filedRules
|
||||
return field
|
||||
}
|
||||
generateField(name, fieldsMeta, remoteFieldsMeta) {
|
||||
let field = { id: name, prop: name, el: {}, attrs: {}, rules: [] }
|
||||
const remoteFieldMeta = remoteFieldsMeta[name] || {}
|
||||
const fieldMeta = fieldsMeta[name] || {}
|
||||
field.label = remoteFieldMeta.label
|
||||
field.helpText = remoteFieldMeta.help_text
|
||||
field = this.generateFieldByType(remoteFieldMeta.type, field, fieldMeta, remoteFieldMeta)
|
||||
field = this.generateFieldByName(name, field)
|
||||
field = this.generateFieldByOther(field, fieldMeta, remoteFieldMeta)
|
||||
const el = assignIfNot(fieldMeta.el || {}, field.el)
|
||||
const rules = fieldMeta.rules || field.rules
|
||||
field = Object.assign(field, fieldMeta)
|
||||
field.el = el
|
||||
field.rules = rules
|
||||
_.set(field, 'attrs.error', '')
|
||||
// Vue.$log.debug('Generate field: ', name, field)
|
||||
return field
|
||||
}
|
||||
generateFieldGroup(field, fieldsMeta, remoteFieldsMeta) {
|
||||
const [groupTitle, fields] = field
|
||||
this.groups.push({
|
||||
id: groupTitle,
|
||||
title: groupTitle,
|
||||
name: fields[0],
|
||||
fields: fields
|
||||
})
|
||||
return this.generateFields(fields, fieldsMeta, remoteFieldsMeta)
|
||||
}
|
||||
generateFields(_fields, fieldsMeta, remoteFieldsMeta) {
|
||||
let fields = []
|
||||
for (let field of _fields) {
|
||||
if (field instanceof Array) {
|
||||
const items = this.generateFieldGroup(field, fieldsMeta, remoteFieldsMeta)
|
||||
fields = [...fields, ...items]
|
||||
} else if (typeof field === 'string') {
|
||||
field = this.generateField(field, fieldsMeta, remoteFieldsMeta)
|
||||
fields.push(field)
|
||||
} else if (field instanceof Object) {
|
||||
if (this.errors) {
|
||||
this.errors[field.prop] = ''
|
||||
}
|
||||
fields.push(field)
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TagSearch :options="iOption" v-bind="$attrs" v-on="$listeners" />
|
||||
<TagSearch :options="options" v-bind="$attrs" v-on="$listeners" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -23,22 +23,9 @@ export default {
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalOptions: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iOption() {
|
||||
return this.options.concat(this.internalOptions)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options() {
|
||||
// 空函数,方便子组件刷新
|
||||
},
|
||||
url() {
|
||||
this.genericOptions()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -49,7 +36,6 @@ export default {
|
||||
methods: {
|
||||
async genericOptions() {
|
||||
const vm = this // 透传This
|
||||
vm.internalOptions = [] // 重置
|
||||
const data = await this.optionUrlMeta()
|
||||
const meta = data.actions['GET'] || {}
|
||||
for (const [name, field] of Object.entries(meta)) {
|
||||
@@ -63,6 +49,7 @@ export default {
|
||||
label: field.label,
|
||||
type: field.type,
|
||||
value: name
|
||||
|
||||
}
|
||||
if (field.type === 'choice' && field.choices) {
|
||||
option.children = field.choices.map(item => {
|
||||
@@ -82,7 +69,7 @@ export default {
|
||||
{ label: this.$t('common.No'), value: false }
|
||||
]
|
||||
}
|
||||
vm.internalOptions.push(option)
|
||||
vm.options.push(option)
|
||||
}
|
||||
},
|
||||
optionUrlMeta() {
|
||||
@@ -94,4 +81,5 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="showColumnSettingPopover"
|
||||
:title="$t('common.CustomCol')"
|
||||
:visible.sync="showColumnSettingPopover"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
width="35%"
|
||||
top="10%"
|
||||
@confirm="handleColumnConfirm()"
|
||||
>
|
||||
<el-alert type="success">
|
||||
{{ this.$t('common.TableColSettingInfo') }}
|
||||
</el-alert>
|
||||
<el-checkbox-group
|
||||
v-model="iCurrentColumns"
|
||||
>
|
||||
<el-row>
|
||||
<el-col
|
||||
v-for="item in totalColumnsList"
|
||||
:key="item.prop"
|
||||
:span="8"
|
||||
style="margin-top:5px;"
|
||||
>
|
||||
<el-checkbox
|
||||
:label="item.prop"
|
||||
:disabled="
|
||||
item.prop==='id' ||
|
||||
item.prop==='actions' ||
|
||||
minColumns.indexOf(item.prop)!==-1
|
||||
"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-checkbox-group>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog/index'
|
||||
export default {
|
||||
name: 'ColumnSettingPopover',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
totalColumnsList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
currentColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
minColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showColumnSettingPopover: false,
|
||||
iCurrentColumns: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showColumnSettingPopover', ({ url }) => {
|
||||
if (url === this.url) {
|
||||
this.showColumnSettingPopover = true
|
||||
this.iCurrentColumns = this.currentColumns
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleColumnConfirm() {
|
||||
this.showColumnSettingPopover = false
|
||||
this.$emit('columnsUpdate', { columns: this.iCurrentColumns, url: this.url })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
</style>
|
||||
@@ -1,41 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<DataTable
|
||||
v-if="!loading"
|
||||
ref="dataTable"
|
||||
v-loading="loading"
|
||||
:config="iConfig"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
@filter-change="filterChange"
|
||||
/>
|
||||
<ColumnSettingPopover
|
||||
:current-columns="popoverColumns.currentCols"
|
||||
:total-columns-list="popoverColumns.totalColumnsList"
|
||||
:min-columns="popoverColumns.minCols"
|
||||
:url="config.url"
|
||||
@columnsUpdate="handlePopoverColumnsChange"
|
||||
/>
|
||||
</div>
|
||||
<DataTable v-if="!loading" ref="dataTable" v-loading="loading" :config="iConfig" v-bind="$attrs" v-on="$listeners" @filter-change="filterChange" />
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import DataTable from '../DataTable'
|
||||
import {
|
||||
DateFormatter,
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ActionsFormatter,
|
||||
ChoicesFormatter
|
||||
} from '@/components/TableFormatters'
|
||||
import { DateFormatter, DetailFormatter, DisplayFormatter, BooleanFormatter, ActionsFormatter } from '@/components/ListTable/formatters'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import ColumnSettingPopover from './components/ColumnSettingPopover'
|
||||
import { newURL } from '@/utils/common'
|
||||
export default {
|
||||
name: 'AutoDataTable',
|
||||
components: {
|
||||
DataTable,
|
||||
ColumnSettingPopover
|
||||
DataTable
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
@@ -53,18 +27,8 @@ export default {
|
||||
method: 'get',
|
||||
autoConfig: {},
|
||||
iConfig: {},
|
||||
meta: {},
|
||||
cleanedColumnsShow: {},
|
||||
totalColumns: [],
|
||||
popoverColumns: {
|
||||
totalColumnsList: [],
|
||||
minCols: [],
|
||||
currentCols: []
|
||||
}
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
config: {
|
||||
@@ -80,18 +44,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async optionUrlMetaAndGenCols() {
|
||||
if (this.config.url === '') { return }
|
||||
const url = (this.config.url.indexOf('?') === -1) ? `${this.config.url}?draw=1&display=1` : `${this.config.url}&draw=1&display=1`
|
||||
this.$store.dispatch('common/getUrlMeta', { url: url }).then(data => {
|
||||
const method = this.method.toUpperCase()
|
||||
this.meta = data.actions && data.actions[method] ? data.actions[method] : {}
|
||||
this.generateTotalColumns()
|
||||
}).then(() => {
|
||||
// 根据当前列重新生成最终渲染表格
|
||||
this.filterShowColumns()
|
||||
}).then(() => {
|
||||
// 生成给子组件使用的TotalColList
|
||||
this.generatePopoverColumns()
|
||||
this.meta = data.actions[this.method.toUpperCase()] || {}
|
||||
this.generateColumns()
|
||||
}).catch((error) => {
|
||||
this.$log.error('Error occur: ', error)
|
||||
}).finally(() => {
|
||||
@@ -107,7 +63,7 @@ export default {
|
||||
break
|
||||
case 'actions':
|
||||
col = {
|
||||
prop: 'actions',
|
||||
prop: 'id',
|
||||
label: i18n.t('common.Actions'),
|
||||
align: 'center',
|
||||
width: '150px',
|
||||
@@ -117,7 +73,7 @@ export default {
|
||||
break
|
||||
case 'is_valid':
|
||||
col.label = i18n.t('common.Validity')
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatter = BooleanFormatter
|
||||
col.align = 'center'
|
||||
col.width = '80px'
|
||||
break
|
||||
@@ -137,7 +93,7 @@ export default {
|
||||
col.formatter = DisplayFormatter
|
||||
break
|
||||
case 'boolean':
|
||||
col.formatter = ChoicesFormatter
|
||||
col.formatter = BooleanFormatter
|
||||
col.align = 'center'
|
||||
col.width = '80px'
|
||||
break
|
||||
@@ -184,12 +140,12 @@ export default {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item['display_name'], value: 'True' }
|
||||
return { text: item.display_name, value: 'True' }
|
||||
} else {
|
||||
return { text: item['display_name'], value: 'False' }
|
||||
return { text: item.display_name, value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item['display_name'], value: item.value }
|
||||
return { text: item.display_name, value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
@@ -209,7 +165,7 @@ export default {
|
||||
col = this.addFilterIfNeed(col)
|
||||
return col
|
||||
},
|
||||
generateTotalColumns() {
|
||||
generateColumns() {
|
||||
const config = _.cloneDeep(this.config)
|
||||
const columns = []
|
||||
for (let col of config.columns) {
|
||||
@@ -220,80 +176,9 @@ export default {
|
||||
columns.push(col)
|
||||
}
|
||||
}
|
||||
// 第一次初始化时记录 totalColumns
|
||||
this.totalColumns = columns
|
||||
config.columns = columns
|
||||
this.iConfig = config
|
||||
},
|
||||
// 生成给子组件使用的TotalColList
|
||||
cleanColumnsShow() {
|
||||
const totalColumnsNames = this.totalColumns.map(obj => obj.prop)
|
||||
// 默认列
|
||||
let defaultColumnsNames = _.get(this.iConfig, 'columnsShow.default', [])
|
||||
if (defaultColumnsNames.length === 0) {
|
||||
defaultColumnsNames = totalColumnsNames
|
||||
}
|
||||
// Clean it
|
||||
defaultColumnsNames = totalColumnsNames.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 最小列
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['actions', 'id'])
|
||||
.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 应该显示的列
|
||||
const _tableConfig = localStorage.getItem('tableConfig')
|
||||
? JSON.parse(localStorage.getItem('tableConfig'))
|
||||
: {}
|
||||
const tableName = this.config.name || this.$route.name + '_' + newURL(this.iConfig.url).pathname
|
||||
const configShowColumnsNames = _.get(_tableConfig[tableName], 'showColumns', null)
|
||||
let showColumnsNames = configShowColumnsNames || defaultColumnsNames
|
||||
if (showColumnsNames.length === 0) {
|
||||
showColumnsNames = totalColumnsNames
|
||||
}
|
||||
// 校对显示的列,是不是包含最小列
|
||||
minColumnsNames.forEach((v, i) => {
|
||||
if (showColumnsNames.indexOf(v) === -1) {
|
||||
showColumnsNames.push(v)
|
||||
}
|
||||
})
|
||||
// Clean it
|
||||
showColumnsNames = totalColumnsNames.filter(n => showColumnsNames.indexOf(n) > -1)
|
||||
|
||||
this.cleanedColumnsShow = {
|
||||
default: defaultColumnsNames,
|
||||
show: showColumnsNames,
|
||||
min: minColumnsNames,
|
||||
configShow: configShowColumnsNames
|
||||
}
|
||||
this.$log.debug('Cleaned columns show: ', this.cleanedColumnsShow)
|
||||
},
|
||||
filterShowColumns() {
|
||||
this.cleanColumnsShow()
|
||||
this.iConfig.columns = this.totalColumns.filter(obj => {
|
||||
return this.cleanedColumnsShow.show.indexOf(obj.prop) > -1
|
||||
})
|
||||
},
|
||||
generatePopoverColumns() {
|
||||
this.popoverColumns.totalColumnsList = this.totalColumns.map(obj => {
|
||||
return { prop: obj.prop, label: obj.label }
|
||||
})
|
||||
this.popoverColumns.currentCols = this.cleanedColumnsShow.show
|
||||
this.popoverColumns.minCols = this.cleanedColumnsShow.min
|
||||
this.$log.debug('Popover cols: ', this.popoverColumns)
|
||||
},
|
||||
handlePopoverColumnsChange({ columns, url }) {
|
||||
this.$log.debug('Columns change: ', columns)
|
||||
this.popoverColumns.currentCols = columns
|
||||
const _tableConfig = localStorage.getItem('tableConfig')
|
||||
? JSON.parse(localStorage.getItem('tableConfig'))
|
||||
: {}
|
||||
const tableName = this.config.name || this.$route.name + '_' + newURL(url).pathname
|
||||
_tableConfig[tableName] = {
|
||||
'showColumns': columns
|
||||
}
|
||||
localStorage.setItem('tableConfig', JSON.stringify(_tableConfig))
|
||||
this.filterShowColumns()
|
||||
},
|
||||
filterChange(filters) {
|
||||
const key = Object.keys(filters)[0]
|
||||
const attr = {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<DataZTree ref="dataztree" :setting="treeSetting" class="data-z-tree" v-on="$listeners">
|
||||
<slot v-if="treeSetting.hasRightMenu" slot="rMenu">
|
||||
<DataZTree ref="dataztree" :setting="treeSetting">
|
||||
<slot slot="rMenu">
|
||||
<li id="m_create" class="rmenu" tabindex="-1" @click="createTreeNode">
|
||||
<i class="fa fa-plus-square-o" /> {{ this.$t('tree.CreateNode') }}
|
||||
</li>
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
autoParam: ['id=key', 'name=n', 'level=lv'],
|
||||
type: 'get',
|
||||
headers: {
|
||||
'X-JMS-ORG': this.$store.getters.currentOrg ? this.$store.getters.currentOrg.id : ''
|
||||
'X-JMS-ORG': JSON.parse(this.$cookie.get('jms_current_org')) ? JSON.parse(this.$cookie.get('jms_current_org')).id : ''
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
@@ -55,8 +55,7 @@ export default {
|
||||
// beforeDrag
|
||||
// onDrag
|
||||
// beforeAsync: this.defaultCallback.bind(this, 'beforeAsync')
|
||||
},
|
||||
hasRightMenu: true
|
||||
}
|
||||
},
|
||||
currentNode: '',
|
||||
currentNodeId: ''
|
||||
@@ -64,7 +63,6 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
treeSetting() {
|
||||
this.$log.debug('Settings: ', this.setting)
|
||||
return _.merge(this.defaultSetting, this.setting)
|
||||
},
|
||||
zTree() {
|
||||
@@ -78,10 +76,6 @@ export default {
|
||||
$('body').unbind('mousedown')
|
||||
},
|
||||
methods: {
|
||||
refreshTree: function() {
|
||||
const refreshIconRef = $('#tree-refresh')
|
||||
refreshIconRef.click()
|
||||
},
|
||||
editTreeNode: function() {
|
||||
this.hideRMenu()
|
||||
const currentNode = this.zTree.getSelectedNodes()[0]
|
||||
@@ -89,7 +83,7 @@ export default {
|
||||
return
|
||||
}
|
||||
if (currentNode) {
|
||||
currentNode.name = currentNode.meta.data.value
|
||||
currentNode.name = currentNode.meta.node.value
|
||||
}
|
||||
this.zTree.editName(currentNode)
|
||||
},
|
||||
@@ -104,19 +98,15 @@ export default {
|
||||
if (this.setting.url.indexOf('?') !== -1) {
|
||||
combinator = '&'
|
||||
}
|
||||
let url = ''
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
if (treeNode.meta.type === 'node') {
|
||||
this.currentNode = treeNode
|
||||
this.currentNodeId = treeNode.meta.data.id
|
||||
query['node'] = this.currentNodeId
|
||||
url = `${this.setting.url}${combinator}node_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
this.currentNodeId = treeNode.meta.node.id
|
||||
this.$route.query['node'] = this.currentNodeId
|
||||
this.$emit('urlChange', `${this.setting.url}${combinator}node_id=${treeNode.meta.node.id}&show_current_asset=${show_current_asset}`)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
query['asset'] = treeNode.meta.data.id
|
||||
url = `${this.setting.url}${combinator}asset_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
this.$route.query['asset'] = treeNode.meta.asset.id
|
||||
this.$emit('urlChange', `${this.setting.url}${combinator}asset_id=${treeNode.meta.asset.id}&show_current_asset=${show_current_asset}`)
|
||||
}
|
||||
this.$router.push({ query })
|
||||
this.$emit('urlChange', url)
|
||||
},
|
||||
removeTreeNode: function() {
|
||||
this.hideRMenu()
|
||||
@@ -125,13 +115,12 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$axios.delete(
|
||||
`${this.treeSetting.nodeUrl}${currentNode.meta.data.id}/`
|
||||
`${this.treeSetting.nodeUrl}${currentNode.meta.node.id}/`
|
||||
).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.zTree.removeNode(currentNode)
|
||||
this.refreshTree()
|
||||
}).catch(() => {
|
||||
// this.$message.error(this.$t('common.deleteErrorMsg') + ' ' + error)
|
||||
// this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
|
||||
})
|
||||
},
|
||||
onRename: function(event, treeId, treeNode, isCancel) {
|
||||
@@ -143,14 +132,14 @@ export default {
|
||||
url,
|
||||
{ 'value': treeNode.name }
|
||||
).then(res => {
|
||||
let assetsAmount = treeNode.meta.data.assetsAmount
|
||||
let assetsAmount = treeNode.meta.node.assetsAmount
|
||||
if (!assetsAmount) {
|
||||
assetsAmount = 0
|
||||
}
|
||||
treeNode.name = treeNode.name + ' (' + assetsAmount + ')'
|
||||
this.zTree.updateNode(treeNode)
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).finally(() => { this.refreshTree() })
|
||||
})
|
||||
},
|
||||
onBodyMouseDown: function(event) {
|
||||
const rMenuID = this.$refs.dataztree.$refs.ztree.iRMenuID
|
||||
@@ -208,9 +197,9 @@ export default {
|
||||
onDrop: function(event, treeId, treeNodes, targetNode, moveType) {
|
||||
const treeNodesIds = []
|
||||
$.each(treeNodes, function(index, value) {
|
||||
treeNodesIds.push(value.meta.data.id)
|
||||
treeNodesIds.push(value.meta.node.id)
|
||||
})
|
||||
const theUrl = `${this.treeSetting.nodeUrl}${targetNode.meta.data.id}/children/add/`
|
||||
const theUrl = `${this.treeSetting.nodeUrl}${targetNode.meta.node.id}/children/add/`
|
||||
this.$axios.put(
|
||||
theUrl, {
|
||||
nodes: treeNodesIds
|
||||
@@ -219,7 +208,7 @@ export default {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
|
||||
}).finally(() => this.refreshTree())
|
||||
})
|
||||
},
|
||||
createTreeNode: function() {
|
||||
this.hideRMenu()
|
||||
@@ -229,21 +218,21 @@ export default {
|
||||
}
|
||||
this.zTree.expandNode(parentNode, true, false, true, false)
|
||||
// http://localhost/api/v1/assets/nodes/85aa4ee2-0bd9-41db-9079-aa3646448d0c/children/
|
||||
const url = `${this.treeSetting.nodeUrl}${parentNode.meta.data.id}/children/`
|
||||
const url = `${this.treeSetting.nodeUrl}${parentNode.meta.node.id}/children/`
|
||||
this.$axios.post(url, {}).then(data => {
|
||||
const newNode = {
|
||||
id: data['key'],
|
||||
name: data['value'],
|
||||
pId: parentNode.id,
|
||||
meta: {
|
||||
data: data
|
||||
'node': data
|
||||
}
|
||||
}
|
||||
newNode.checked = this.zTree.getSelectedNodes()[0].checked
|
||||
this.zTree.addNodes(parentNode, 0, newNode)
|
||||
// vm.$refs.dataztree.refresh()
|
||||
const node = this.zTree.getNodeByParam('id', newNode.id, parentNode)
|
||||
this.currentNodeId = node.meta.data.id || newNode.id
|
||||
this.currentNodeId = node.meta.node.id || newNode.id
|
||||
this.zTree.editName(node)
|
||||
this.$message.success(this.$t('common.createSuccessMsg'))
|
||||
}).catch(error => {
|
||||
@@ -251,21 +240,16 @@ export default {
|
||||
})
|
||||
},
|
||||
refresh: function() {
|
||||
|
||||
},
|
||||
getSelectedNodes: function() {
|
||||
return this.zTree.getSelectedNodes()
|
||||
},
|
||||
getNodes: function() {
|
||||
return this.zTree.getNodes()
|
||||
},
|
||||
selectNode: function(node) {
|
||||
return this.zTree.selectNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang='less' scoped>
|
||||
.rmenu {
|
||||
font-size: 12px;
|
||||
padding: 0 16px;
|
||||
@@ -288,8 +272,4 @@ export default {
|
||||
.rmenu:hover{
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.data-z-tree >>> .fa {
|
||||
width: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane v-if="shouldHide('min')" :label="this.$t('common.CronTab.min')">
|
||||
<CrontabMin
|
||||
ref="cronmin"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
@update="updateContabValue"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="shouldHide('hour')" :label="this.$t('common.CronTab.hour')">
|
||||
<CrontabHour
|
||||
ref="cronhour"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
@update="updateContabValue"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="shouldHide('day')" :label="this.$t('common.CronTab.day')">
|
||||
<CrontabDay
|
||||
ref="cronday"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
@update="updateContabValue"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="shouldHide('month')" :label="this.$t('common.CronTab.month')">
|
||||
<CrontabMonth
|
||||
ref="cronmonth"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
@update="updateContabValue"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="shouldHide('week')" :label="this.$t('common.CronTab.week')">
|
||||
<CrontabWeek
|
||||
ref="cronweek"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
@update="updateContabValue"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="popup-main">
|
||||
<div class="popup-result">
|
||||
<p class="title">{{ this.$t('common.CronTab.timeExpression') }}</p>
|
||||
<table>
|
||||
<thead>
|
||||
<th v-for="item of tabTitles" :key="item" width="40">{{ item }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<td>
|
||||
<el-input
|
||||
v-model.trim="contabValueObj.min"
|
||||
min="0"
|
||||
max="5"
|
||||
size="small"
|
||||
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-model.trim="contabValueObj.hour"
|
||||
size="small"
|
||||
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-model.trim="contabValueObj.day"
|
||||
size="small"
|
||||
onkeyup="value=value.replace(/[^\0-9\\-\*\,]/g,'')"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-model.trim="contabValueObj.month"
|
||||
size="small"
|
||||
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-model.trim="contabValueObj.week"
|
||||
size="small"
|
||||
onkeyup="value=value.replace(/[^\0-9\-\*\,]/g,'')"
|
||||
/>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="margin: 0 auto; text-align: center">
|
||||
<div style="font-size: 13px;">{{ this.$t('common.CronTab.cronExpression') }}</div>
|
||||
<div style="font-size: 13px;">{{ contabValueString }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<CrontabResult :ex="contabValueString" />
|
||||
|
||||
<div class="pop_btn">
|
||||
<el-button
|
||||
size="small"
|
||||
@click="clearCron"
|
||||
>
|
||||
{{ this.$t('common.Reset') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="submitFill"
|
||||
>
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CrontabMin from './components/Crontab-Min.vue'
|
||||
import CrontabHour from './components/Crontab-Hour.vue'
|
||||
import CrontabDay from './components/Crontab-Day.vue'
|
||||
import CrontabMonth from './components/Crontab-Month.vue'
|
||||
import CrontabWeek from './components/Crontab-Week.vue'
|
||||
import CrontabResult from './components/Crontab-Result.vue'
|
||||
|
||||
export default {
|
||||
name: 'Vcrontab',
|
||||
components: {
|
||||
CrontabMin,
|
||||
CrontabHour,
|
||||
CrontabDay,
|
||||
CrontabMonth,
|
||||
CrontabWeek,
|
||||
CrontabResult
|
||||
},
|
||||
props: {
|
||||
expression: {
|
||||
type: String,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
hideComponent: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabTitles: [this.$t('common.CronTab.min'), this.$t('common.CronTab.hour'), this.$t('common.CronTab.day'), this.$t('common.CronTab.month'), this.$t('common.CronTab.week')],
|
||||
tabActive: 0,
|
||||
myindex: 0,
|
||||
contabValueObj: {
|
||||
second: '0',
|
||||
min: '0',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '*'
|
||||
// year: "",
|
||||
},
|
||||
newContabValueString: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
contabValueString: {
|
||||
get() {
|
||||
const obj = this.contabValueObj
|
||||
const str =
|
||||
obj.min +
|
||||
' ' +
|
||||
obj.hour +
|
||||
' ' +
|
||||
obj.day +
|
||||
' ' +
|
||||
obj.month +
|
||||
' ' +
|
||||
obj.week
|
||||
return str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expression: 'resolveExp',
|
||||
hideComponent(value) {
|
||||
// 隐藏部分组件
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.resolveExp()
|
||||
},
|
||||
methods: {
|
||||
shouldHide(key) {
|
||||
if (this.hideComponent && this.hideComponent.includes(key)) return false
|
||||
return true
|
||||
},
|
||||
resolveExp() {
|
||||
// 反解析 表达式
|
||||
if (this.expression) {
|
||||
const arr = this.expression.split(' ')
|
||||
if (arr.length >= 5) {
|
||||
// 5 位以上是合法表达式
|
||||
const obj = {
|
||||
min: arr[0],
|
||||
hour: arr[1],
|
||||
day: arr[2],
|
||||
month: arr[3],
|
||||
week: arr[4]
|
||||
}
|
||||
this.contabValueObj = {
|
||||
...this.contabValueObj,
|
||||
...obj
|
||||
}
|
||||
for (const i in obj) {
|
||||
if (obj[i]) this.changeRadio(i, obj[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有传入的表达式 则还原
|
||||
this.clearCron()
|
||||
}
|
||||
},
|
||||
// tab切换值
|
||||
tabCheck(index) {
|
||||
this.tabActive = index
|
||||
},
|
||||
// 由子组件触发,更改表达式组成的字段值
|
||||
updateContabValue(name, value, from) {
|
||||
this.contabValueObj[name] = value
|
||||
if (from && from !== name) {
|
||||
console.log(`来自组件 ${from} 改变了 ${name} ${value}`)
|
||||
this.changeRadio(name, value)
|
||||
}
|
||||
},
|
||||
// 赋值到组件
|
||||
changeRadio(name, value) {
|
||||
const arr = ['second', 'min', 'hour', 'month']
|
||||
const refName = 'cron' + name
|
||||
let insVlaue
|
||||
|
||||
if (!this.$refs[refName]) return
|
||||
|
||||
if (arr.includes(name)) {
|
||||
if (value === '*') {
|
||||
insVlaue = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].cycle01 = 0)
|
||||
: (this.$refs[refName].cycle01 = indexArr[0])
|
||||
this.$refs[refName].cycle02 = indexArr[1]
|
||||
insVlaue = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].average01 = 0)
|
||||
: (this.$refs[refName].average01 = indexArr[0])
|
||||
this.$refs[refName].average02 = indexArr[1]
|
||||
insVlaue = 3
|
||||
} else {
|
||||
insVlaue = 4
|
||||
this.$refs[refName].checkboxList = value.split(',')
|
||||
}
|
||||
} else if (name === 'day') {
|
||||
if (value === '*') {
|
||||
insVlaue = 1
|
||||
} else if (value === '?') {
|
||||
insVlaue = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].cycle01 = 0)
|
||||
: (this.$refs[refName].cycle01 = indexArr[0])
|
||||
this.$refs[refName].cycle02 = indexArr[1]
|
||||
insVlaue = 3
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].average01 = 0)
|
||||
: (this.$refs[refName].average01 = indexArr[0])
|
||||
this.$refs[refName].average02 = indexArr[1]
|
||||
insVlaue = 4
|
||||
} else if (value.indexOf('W') > -1) {
|
||||
const indexArr = value.split('W')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].workday = 0)
|
||||
: (this.$refs[refName].workday = indexArr[0])
|
||||
insVlaue = 5
|
||||
} else if (value === 'L') {
|
||||
insVlaue = 6
|
||||
} else {
|
||||
this.$refs[refName].checkboxList = value.split(',')
|
||||
insVlaue = 7
|
||||
}
|
||||
} else if (name === 'week') {
|
||||
if (value === '*') {
|
||||
insVlaue = 1
|
||||
} else if (value === '?') {
|
||||
insVlaue = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].cycle01 = 0)
|
||||
: (this.$refs[refName].cycle01 = indexArr[0])
|
||||
this.$refs[refName].cycle02 = indexArr[1]
|
||||
insVlaue = 3
|
||||
} else if (value.indexOf('#') > -1) {
|
||||
const indexArr = value.split('#')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].average01 = 1)
|
||||
: (this.$refs[refName].average01 = indexArr[0])
|
||||
this.$refs[refName].average02 = indexArr[1]
|
||||
insVlaue = 4
|
||||
} else if (value.indexOf('L') > -1) {
|
||||
const indexArr = value.split('L')
|
||||
isNaN(indexArr[0])
|
||||
? (this.$refs[refName].weekday = 1)
|
||||
: (this.$refs[refName].weekday = indexArr[0])
|
||||
insVlaue = 5
|
||||
} else {
|
||||
this.$refs[refName].checkboxList = value.split(',')
|
||||
insVlaue = 6
|
||||
}
|
||||
} else if (name === 'year') {
|
||||
if (value === '') {
|
||||
insVlaue = 1
|
||||
} else if (value === '*') {
|
||||
insVlaue = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
insVlaue = 3
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
insVlaue = 4
|
||||
} else {
|
||||
this.$refs[refName].checkboxList = value.split(',')
|
||||
insVlaue = 5
|
||||
}
|
||||
}
|
||||
this.$refs[refName].radioValue = insVlaue
|
||||
},
|
||||
// 表单选项的子组件校验数字格式(通过-props传递)
|
||||
checkNumber(value, minLimit, maxLimit) {
|
||||
// 检查必须为整数
|
||||
value = Math.floor(value)
|
||||
if (value < minLimit) {
|
||||
value = minLimit
|
||||
} else if (value > maxLimit) {
|
||||
value = maxLimit
|
||||
}
|
||||
return value
|
||||
},
|
||||
// 隐藏弹窗
|
||||
hidePopup() {
|
||||
this.$emit('hide')
|
||||
},
|
||||
// 填充表达式
|
||||
submitFill() {
|
||||
this.$emit('fill', this.contabValueString)
|
||||
this.hidePopup()
|
||||
},
|
||||
clearCron() {
|
||||
// 还原选择项
|
||||
this.contabValueObj = {
|
||||
second: '0',
|
||||
min: '0',
|
||||
hour: '0',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '*'
|
||||
// year: "",
|
||||
}
|
||||
for (const j in this.contabValueObj) {
|
||||
this.changeRadio(j, this.contabValueObj[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.pop_btn {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.popup-main {
|
||||
position: relative;
|
||||
margin: 10px auto 0;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.popup-title {
|
||||
overflow: hidden;
|
||||
line-height: 34px;
|
||||
padding-top: 6px;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.popup-result {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
margin: 17px auto;
|
||||
padding: 10px 10px 10px;
|
||||
border: 1px solid #dcdfe6;
|
||||
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 12%), 0 0 6px 0 rgb(0 0 0 / 4%);
|
||||
}
|
||||
.popup-result .title {
|
||||
position: absolute;
|
||||
top: -17px;
|
||||
left: 50%;
|
||||
width: 140px;
|
||||
font-size: 14px;
|
||||
margin-left: -70px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
background: #fff;
|
||||
}
|
||||
.popup-result table {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.popup-result table span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-family: arial;
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
.popup-result-scroll {
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
height: 10em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.el-form-item--mini.el-form-item,
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,189 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1">
|
||||
{{ this.$t('common.CronTab.day') }},{{ this.$t('common.CronTab.wildcardsAllowed') }}[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="cycle01" :min="0" :max="31" /> -
|
||||
<el-input-number v-model="cycle02" :min="0" :max="31" /> {{ this.$t('common.CronTab.day') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
{{ this.$t('common.CronTab.every') }}
|
||||
<el-input-number v-model="average02" :min="1" :max="31" /> {{ this.$t('common.CronTab.day') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="7">
|
||||
{{ this.$t('common.CronTab.appoint') }}
|
||||
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
|
||||
<el-option v-for="item in 31" :key="item" :value="item">{{ item }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CrontabDay',
|
||||
props: {
|
||||
cron: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioValue: 1,
|
||||
workday: 1,
|
||||
cycle01: 1,
|
||||
cycle02: 2,
|
||||
average01: 1,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
checkNum: this.$options.propsData.check
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cycleTotal: {
|
||||
get() {
|
||||
return this.cycle01 + '-' + this.cycle02
|
||||
},
|
||||
set() {
|
||||
this.cycle01 = this.checkNum(this.cycle01, 1, 31)
|
||||
this.cycle02 = this.checkNum(this.cycle02, 1, 31)
|
||||
}
|
||||
},
|
||||
averageTotal: {
|
||||
get() {
|
||||
return '*' + '/' + this.average02
|
||||
},
|
||||
set() {
|
||||
this.average01 = this.checkNum(this.average01, 1, 31)
|
||||
this.average02 = this.checkNum(this.average02, 1, 31)
|
||||
}
|
||||
},
|
||||
checkboxString: {
|
||||
get() {
|
||||
const str = this.checkboxList.join()
|
||||
return str === '' ? '*' : str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'radioValue': 'radioChange',
|
||||
'cycleTotal': 'cycleChange',
|
||||
'averageTotal': 'averageChange',
|
||||
'workdayCheck': 'workdayChange',
|
||||
'checkboxString': 'checkboxChange'
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
const arrs = []
|
||||
for (let index = 0; index < this.checkboxList.length; index++) {
|
||||
const cur = this.checkboxList[index]
|
||||
arrs.push(parseFloat(cur))
|
||||
}
|
||||
this.checkboxList = arrs
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 单选按钮值变化时
|
||||
radioChange() {
|
||||
('day rachange')
|
||||
if (this.radioValue === 1) {
|
||||
this.$emit('update', 'day', '*', 'day')
|
||||
} else {
|
||||
if (this.cron.hour === '*') {
|
||||
this.$emit('update', 'hour', '0', 'day')
|
||||
}
|
||||
if (this.cron.min === '*') {
|
||||
this.$emit('update', 'min', '0', 'day')
|
||||
}
|
||||
if (this.cron.second === '*') {
|
||||
this.$emit('update', 'second', '0', 'day')
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.radioValue) {
|
||||
case 2:
|
||||
this.$emit('update', 'day', '?')
|
||||
break
|
||||
case 3:
|
||||
this.$emit('update', 'day', this.cycle01 + '-' + this.cycle02)
|
||||
break
|
||||
case 4:
|
||||
this.$emit('update', 'day', '*' + '/' + this.average02)
|
||||
break
|
||||
case 5:
|
||||
this.$emit('update', 'day', this.workday + 'W')
|
||||
break
|
||||
case 6:
|
||||
this.$emit('update', 'day', 'L')
|
||||
break
|
||||
case 7:
|
||||
this.$emit('update', 'day', this.checkboxString)
|
||||
break
|
||||
}
|
||||
('day rachange end')
|
||||
},
|
||||
// 周期两个值变化时
|
||||
cycleChange() {
|
||||
if (this.radioValue === 3) {
|
||||
this.$emit('update', 'day', this.cycleTotal)
|
||||
}
|
||||
},
|
||||
// 平均两个值变化时
|
||||
averageChange() {
|
||||
if (this.radioValue === 4) {
|
||||
this.$emit('update', 'day', this.averageTotal)
|
||||
}
|
||||
},
|
||||
// 最近工作日值变化时
|
||||
workdayChange() {
|
||||
if (this.radioValue === 5) {
|
||||
this.$emit('update', 'day', this.workday + 'W')
|
||||
}
|
||||
},
|
||||
// checkbox值变化时
|
||||
checkboxChange() {
|
||||
if (this.radioValue === 7) {
|
||||
this.$emit('update', 'day', this.checkboxString)
|
||||
}
|
||||
},
|
||||
// 父组件传递的week发生变化触发
|
||||
weekChange() {
|
||||
// 判断week值与day不能同时为“?”
|
||||
if (this.cron.week === '?' && this.radioValue === 2) {
|
||||
this.radioValue = '1'
|
||||
} else if (this.cron.week !== '?' && this.radioValue !== 2) {
|
||||
this.radioValue = '2'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,158 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1">
|
||||
{{ this.$t('common.CronTab.hour') }},{{ this.$t('common.CronTab.wildcardsAllowed') }}[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="cycle01" :min="0" :max="60" /> -
|
||||
<el-input-number v-model="cycle02" :min="0" :max="60" /> {{ this.$t('common.CronTab.hour') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.every') }}
|
||||
<el-input-number v-model="average02" :min="1" :max="60" /> {{ this.$t('common.CronTab.hour') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
{{ this.$t('common.CronTab.appoint') }}
|
||||
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
|
||||
<el-option v-for="item in 24" :key="item" :value="item-1">{{ item-1 }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CrontabHour',
|
||||
props: {
|
||||
cron: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioValue: 1,
|
||||
cycle01: 0,
|
||||
cycle02: 1,
|
||||
average01: 0,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
checkNum: this.$options.propsData.check
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cycleTotal: {
|
||||
get() {
|
||||
return this.cycle01 + '-' + this.cycle02
|
||||
},
|
||||
set() {
|
||||
this.cycle01 = this.checkNum(this.cycle01, 0, 23)
|
||||
this.cycle02 = this.checkNum(this.cycle02, 0, 23)
|
||||
}
|
||||
},
|
||||
averageTotal: {
|
||||
get() {
|
||||
return '*' + '/' + this.average02
|
||||
},
|
||||
set() {
|
||||
this.average01 = this.checkNum(this.average01, 0, 23)
|
||||
this.average02 = this.checkNum(this.average02, 1, 23)
|
||||
}
|
||||
},
|
||||
checkboxString: {
|
||||
get() {
|
||||
const str = this.checkboxList.join()
|
||||
return str === '' ? '*' : str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'radioValue': 'radioChange',
|
||||
'cycleTotal': 'cycleChange',
|
||||
'averageTotal': 'averageChange',
|
||||
'checkboxString': 'checkboxChange'
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
const arrs = []
|
||||
for (let index = 0; index < this.checkboxList.length; index++) {
|
||||
const cur = this.checkboxList[index]
|
||||
arrs.push(parseFloat(cur))
|
||||
}
|
||||
this.checkboxList = arrs
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 单选按钮值变化时
|
||||
radioChange() {
|
||||
if (this.radioValue === 1) {
|
||||
this.$emit('update', 'hour', '*', 'hour')
|
||||
// this.$emit('update', 'day', '*', 'hour')
|
||||
} else {
|
||||
if (this.cron.min === '*') {
|
||||
this.$emit('update', 'min', '0', 'hour')
|
||||
}
|
||||
if (this.cron.second === '*') {
|
||||
this.$emit('update', 'second', '0', 'hour')
|
||||
}
|
||||
}
|
||||
switch (this.radioValue) {
|
||||
case 2:
|
||||
this.$emit('update', 'hour', this.cycle01 + '-' + this.cycle02)
|
||||
break
|
||||
case 3:
|
||||
this.$emit('update', 'hour', '*' + '/' + this.average02)
|
||||
break
|
||||
case 4:
|
||||
this.$emit('update', 'hour', this.checkboxString)
|
||||
break
|
||||
}
|
||||
},
|
||||
// 周期两个值变化时
|
||||
cycleChange() {
|
||||
if (this.radioValue === 2) {
|
||||
this.$emit('update', 'hour', this.cycleTotal)
|
||||
}
|
||||
},
|
||||
// 平均两个值变化时
|
||||
averageChange() {
|
||||
if (this.radioValue === 3) {
|
||||
this.$emit('update', 'hour', this.averageTotal)
|
||||
}
|
||||
},
|
||||
// checkbox值变化时
|
||||
checkboxChange() {
|
||||
if (this.radioValue === 4) {
|
||||
this.$emit('update', 'hour', this.checkboxString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px
|
||||
}
|
||||
</style>
|
||||
@@ -1,156 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1" size="mini">
|
||||
{{ this.$t('common.CronTab.min') }},{{ this.$t('common.CronTab.wildcardsAllowed') }}[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="cycle01" :min="0" :max="60" /> -
|
||||
<el-input-number v-model="cycle02" :min="0" :max="60" /> {{ this.$t('common.CronTab.min') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="average02" :min="1" :max="60" /> {{ this.$t('common.CronTab.min') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
{{ this.$t('common.CronTab.appoint') }}
|
||||
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%" size="small">
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{ item-1 }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CrontabMin',
|
||||
props: {
|
||||
cron: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioValue: 1,
|
||||
cycle01: 1,
|
||||
cycle02: 2,
|
||||
average01: 0,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
checkNum: this.$options.propsData.check
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cycleTotal: {
|
||||
get() {
|
||||
return this.cycle01 + '-' + (this.cycle02 ? this.cycle02 : '')
|
||||
},
|
||||
set() {
|
||||
this.cycle01 = this.checkNum(this.cycle01, 0, 59)
|
||||
this.cycle02 = this.checkNum(this.cycle02, 0, 59)
|
||||
}
|
||||
},
|
||||
averageTotal: {
|
||||
get() {
|
||||
return '*' + '/' + (this.average02 ? this.average02 : 0)
|
||||
},
|
||||
set() {
|
||||
this.average01 = this.checkNum(this.average01, 0, 59)
|
||||
this.average02 = this.checkNum(this.average02, 1, 59)
|
||||
}
|
||||
},
|
||||
checkboxString: {
|
||||
get() {
|
||||
const str = this.checkboxList.join()
|
||||
return str === '' ? '*' : str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'radioValue': 'radioChange',
|
||||
'cycleTotal': 'cycleChange',
|
||||
'averageTotal': 'averageChange',
|
||||
'checkboxString': 'checkboxChange'
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
const arrs = []
|
||||
for (let index = 0; index < this.checkboxList.length; index++) {
|
||||
const cur = this.checkboxList[index]
|
||||
arrs.push(parseFloat(cur))
|
||||
}
|
||||
this.checkboxList = arrs
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 单选按钮值变化时
|
||||
radioChange() {
|
||||
if (this.radioValue !== 1 && this.cron.second === '*') {
|
||||
this.$emit('update', 'second', '0', 'min')
|
||||
}
|
||||
switch (this.radioValue) {
|
||||
case 1:
|
||||
this.$emit('update', 'min', '*', 'min')
|
||||
this.$emit('update', 'hour', '*', 'min')
|
||||
break
|
||||
case 2:
|
||||
this.$emit('update', 'min', this.cycle01 + '-' + this.cycle02, 'min')
|
||||
break
|
||||
case 3:
|
||||
this.$emit('update', 'min', '*' + '/' + this.average02, 'min')
|
||||
break
|
||||
case 4:
|
||||
this.$emit('update', 'min', this.checkboxString, 'min')
|
||||
break
|
||||
}
|
||||
},
|
||||
// 周期两个值变化时
|
||||
cycleChange() {
|
||||
if (this.radioValue === 2) {
|
||||
this.$emit('update', 'min', this.cycleTotal, 'min')
|
||||
}
|
||||
},
|
||||
// 平均两个值变化时
|
||||
averageChange() {
|
||||
if (this.radioValue === 3) {
|
||||
this.$emit('update', 'min', this.averageTotal, 'min')
|
||||
}
|
||||
},
|
||||
// checkbox值变化时
|
||||
checkboxChange() {
|
||||
if (this.radioValue === 4) {
|
||||
this.$emit('update', 'min', this.checkboxString, 'min')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,163 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1">
|
||||
{{ this.$t('common.CronTab.month') }},{{ this.$t('common.CronTab.wildcardsAllowed') }}[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
{{ this.$t('common.CronTab.from') }}
|
||||
<el-input-number v-model="cycle01" :min="1" :max="12" /> -
|
||||
<el-input-number v-model="cycle02" :min="1" :max="12" /> {{ this.$t('common.CronTab.month') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.every') }}
|
||||
<el-input-number v-model="average02" :min="1" :max="12" /> {{ this.$t('common.CronTab.month') }}{{ this.$t('common.CronTab.executeOnce') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
{{ this.$t('common.CronTab.appoint') }}
|
||||
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
|
||||
<el-option v-for="item in 12" :key="item" :value="item">{{ item }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CrontabMonth',
|
||||
props: {
|
||||
cron: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioValue: 1,
|
||||
cycle01: 1,
|
||||
cycle02: 2,
|
||||
average01: 1,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
checkNum: this.check
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cycleTotal: {
|
||||
get() {
|
||||
return this.cycle01 + '-' + this.cycle02
|
||||
},
|
||||
set() {
|
||||
this.cycle01 = this.checkNum(this.cycle01, 1, 12)
|
||||
this.cycle02 = this.checkNum(this.cycle02, 1, 12)
|
||||
}
|
||||
},
|
||||
averageTotal: {
|
||||
get() {
|
||||
return '*' + '/' + this.average02
|
||||
},
|
||||
set() {
|
||||
this.average01 = this.checkNum(this.average01, 1, 12)
|
||||
this.average02 = this.checkNum(this.average02, 1, 12)
|
||||
}
|
||||
},
|
||||
checkboxString: {
|
||||
get() {
|
||||
const str = this.checkboxList.join()
|
||||
return str === '' ? '*' : str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'radioValue': 'radioChange',
|
||||
'cycleTotal': 'cycleChange',
|
||||
'averageTotal': 'averageChange',
|
||||
'checkboxString': 'checkboxChange'
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
const arrs = []
|
||||
for (let index = 0; index < this.checkboxList.length; index++) {
|
||||
const cur = this.checkboxList[index]
|
||||
arrs.push(parseFloat(cur))
|
||||
}
|
||||
this.checkboxList = arrs
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 单选按钮值变化时
|
||||
radioChange() {
|
||||
if (this.radioValue === 1) {
|
||||
this.$emit('update', 'month', '*')
|
||||
} else {
|
||||
if (this.cron.day === '*') {
|
||||
this.$emit('update', 'day', '*', 'month')
|
||||
}
|
||||
if (this.cron.hour === '*') {
|
||||
this.$emit('update', 'hour', '0', 'month')
|
||||
}
|
||||
if (this.cron.min === '*') {
|
||||
this.$emit('update', 'min', '0', 'month')
|
||||
}
|
||||
if (this.cron.second === '*') {
|
||||
this.$emit('update', 'second', '0', 'month')
|
||||
}
|
||||
}
|
||||
switch (this.radioValue) {
|
||||
case 2:
|
||||
this.$emit('update', 'month', this.cycle01 + '-' + this.cycle02)
|
||||
break
|
||||
case 3:
|
||||
this.$emit('update', 'month', '*' + '/' + this.average02)
|
||||
break
|
||||
case 4:
|
||||
this.$emit('update', 'month', this.checkboxString)
|
||||
break
|
||||
}
|
||||
},
|
||||
// 周期两个值变化时
|
||||
cycleChange() {
|
||||
if (this.radioValue === 2) {
|
||||
this.$emit('update', 'month', this.cycleTotal)
|
||||
}
|
||||
},
|
||||
// 平均两个值变化时
|
||||
averageChange() {
|
||||
if (this.radioValue === 3) {
|
||||
this.$emit('update', 'month', this.averageTotal)
|
||||
}
|
||||
},
|
||||
// checkbox值变化时
|
||||
checkboxChange() {
|
||||
if (this.radioValue === 4) {
|
||||
this.$emit('update', 'month', this.checkboxString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,62 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<div class="popup-result">
|
||||
<p class="title">{{ this.$t('common.CronTab.runningTimes') }}</p>
|
||||
<ul class="popup-result-scroll">
|
||||
<template v-if="isShow">
|
||||
<li v-for="item in resultList" :key="item">{{ item }}</li>
|
||||
</template>
|
||||
<li v-else>{{ this.$t('common.CronTab.calculationResults') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import parser from 'cron-parser'
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'CrontabResult',
|
||||
props: {
|
||||
ex: {
|
||||
type: String,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dayRule: '',
|
||||
dayRuleSup: '',
|
||||
dateArr: [],
|
||||
resultList: [],
|
||||
isShow: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'ex': 'expressionChange222'
|
||||
},
|
||||
mounted: function() {
|
||||
// 初始化 获取一次结果
|
||||
this.expressionChange222()
|
||||
},
|
||||
methods: {
|
||||
expressionChange222() {
|
||||
this.isShow = true
|
||||
const rule = 0 + ' ' + this.$options.propsData.ex
|
||||
try {
|
||||
this.resultList = []
|
||||
var interval = parser.parseExpression(rule)
|
||||
for (let index = 0; index < 5; index++) {
|
||||
const cur = interval.next().toString()
|
||||
this.resultList.push(moment(cur).format('YYYY-MM-DD HH:mm:ss'))
|
||||
}
|
||||
} catch (error) {
|
||||
this.isShow = false
|
||||
console.log(error, 'error')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -1,178 +0,0 @@
|
||||
/* eslint-disable */
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1">
|
||||
{{ this.$t('common.CronTab.week') }},{{ this.$t('common.CronTab.wildcardsAllowed') }}[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
{{ this.$t('common.CronTab.cycleFromWeek') }}
|
||||
<el-input-number v-model="cycle01" :min="1" :max="7" /> -
|
||||
<el-input-number v-model="cycle02" :min="1" :max="7" />
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="6">
|
||||
{{ this.$t('common.CronTab.appoint') }}
|
||||
<el-select v-model="checkboxList" clearable :placeholder="this.$t('common.CronTab.manyChoose')" multiple style="width:100%">
|
||||
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{ item }}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CrontabWeek',
|
||||
props: {
|
||||
cron: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioValue: 2,
|
||||
weekday: 1,
|
||||
cycle01: 1,
|
||||
cycle02: 2,
|
||||
average01: 1,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
weekList: [this.$t('common.CronTab.Monday'), this.$t('common.CronTab.Tuesday'), this.$t('common.CronTab.Wednesday'), this.$t('common.CronTab.Thursday'), this.$t('common.CronTab.Friday'), this.$t('common.CronTab.Saturday'), this.$t('common.CronTab.Sunday')],
|
||||
checkNum: this.$options.propsData.check
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cycleTotal: {
|
||||
get() {
|
||||
return this.cycle01 + '-' + this.cycle02
|
||||
},
|
||||
set() {
|
||||
this.cycle01 = this.checkNum(this.cycle01, 1, 7)
|
||||
this.cycle02 = this.checkNum(this.cycle02, 1, 7)
|
||||
}
|
||||
},
|
||||
averageTotal: {
|
||||
get() {
|
||||
return this.average01 + '#' + this.average02
|
||||
},
|
||||
set() {
|
||||
this.average01 = this.checkNum(this.average01, 1, 4)
|
||||
this.average02 = this.checkNum(this.average02, 1, 7)
|
||||
}
|
||||
},
|
||||
checkboxString: {
|
||||
get() {
|
||||
const str = this.checkboxList.join()
|
||||
return str === '' ? '*' : str
|
||||
},
|
||||
set() {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'radioValue': 'radioChange',
|
||||
'cycleTotal': 'cycleChange',
|
||||
'averageTotal': 'averageChange',
|
||||
'weekdayCheck': 'weekdayChange',
|
||||
'checkboxString': 'checkboxChange'
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
const arrs = []
|
||||
for (let index = 0; index < this.checkboxList.length; index++) {
|
||||
const cur = this.checkboxList[index]
|
||||
arrs.push(parseFloat(cur))
|
||||
}
|
||||
this.checkboxList = arrs
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 单选按钮值变化时
|
||||
radioChange() {
|
||||
if (this.radioValue === 1) {
|
||||
this.$emit('update', 'week', '*')
|
||||
this.$emit('update', 'year', '*')
|
||||
} else {
|
||||
if (this.cron.month === '*') {
|
||||
this.$emit('update', 'month', '*', 'week')
|
||||
}
|
||||
if (this.cron.day === '*') {
|
||||
this.$emit('update', 'day', '*', 'week')
|
||||
}
|
||||
if (this.cron.hour === '*') {
|
||||
this.$emit('update', 'hour', '*', 'week')
|
||||
}
|
||||
if (this.cron.min === '*') {
|
||||
this.$emit('update', 'min', '*', 'week')
|
||||
}
|
||||
if (this.cron.second === '*') {
|
||||
this.$emit('update', 'second', '*', 'week')
|
||||
}
|
||||
}
|
||||
switch (this.radioValue) {
|
||||
case 2:
|
||||
this.$emit('update', 'week', '?')
|
||||
break
|
||||
case 3:
|
||||
this.$emit('update', 'week', this.cycle01 + '-' + this.cycle02)
|
||||
break
|
||||
case 4:
|
||||
this.$emit('update', 'week', this.average01 + '#' + this.average02)
|
||||
break
|
||||
case 5:
|
||||
this.$emit('update', 'week', this.weekday + 'L')
|
||||
break
|
||||
case 6:
|
||||
this.$emit('update', 'week', this.checkboxString)
|
||||
break
|
||||
}
|
||||
},
|
||||
// 根据互斥事件,更改radio的值
|
||||
|
||||
// 周期两个值变化时
|
||||
cycleChange() {
|
||||
if (this.radioValue === 3) {
|
||||
this.$emit('update', 'week', this.cycleTotal)
|
||||
}
|
||||
},
|
||||
// 平均两个值变化时
|
||||
averageChange() {
|
||||
if (this.radioValue === 4) {
|
||||
this.$emit('update', 'week', this.averageTotal)
|
||||
}
|
||||
},
|
||||
// 最近工作日值变化时
|
||||
weekdayChange() {
|
||||
if (this.radioValue === 5) {
|
||||
this.$emit('update', 'week', this.weekday + 'L')
|
||||
}
|
||||
},
|
||||
// checkbox值变化时
|
||||
checkboxChange() {
|
||||
if (this.radioValue === 6) {
|
||||
this.$emit('update', 'week', this.checkboxString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item--small.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="box">
|
||||
<el-input v-model="input" clearable @focus="showDialog" @clear="onClear" />
|
||||
</div>
|
||||
<el-dialog :title="this.$t('common.CronTab.newCron')" :visible.sync="showCron" top="8vh" width="580px" append-to-body>
|
||||
<Crontab
|
||||
:expression="expression"
|
||||
@hide="showCron = false"
|
||||
@fill="crontabFill"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Crontab from './Crontab.vue'
|
||||
export default {
|
||||
components: { Crontab },
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
input: _.cloneDeep(this.value),
|
||||
expression: _.cloneDeep(this.value),
|
||||
showCron: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
crontabFill(value) {
|
||||
// 确定后回传的值
|
||||
this.input = value
|
||||
this.$emit('change', value)
|
||||
},
|
||||
showDialog() {
|
||||
this.expression = this.input
|
||||
this.showCron = true
|
||||
},
|
||||
onClear() {
|
||||
this.input = ''
|
||||
this.$emit('change', '')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-dialog__body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,174 +0,0 @@
|
||||
<template>
|
||||
<div :class="grouped ? 'el-button-group' : 'el-button-ungroup'">
|
||||
<template v-for="action in iActions">
|
||||
<el-dropdown
|
||||
v-if="action.dropdown"
|
||||
v-show="action.dropdown.length > 0"
|
||||
:key="action.name"
|
||||
class="action-item"
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
@command="handleDropdownCallback"
|
||||
>
|
||||
<el-button :size="size" v-bind="cleanButtonAction(action)">
|
||||
{{ action.title }}<i class="el-icon-arrow-down el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<template v-for="option in action.dropdown">
|
||||
<div v-if="option.group" :key="'group:'+option.name" class="dropdown-menu-title">
|
||||
{{ option.group }}
|
||||
</div>
|
||||
<el-dropdown-item
|
||||
:key="option.name"
|
||||
:command="[option, action]"
|
||||
v-bind="option"
|
||||
>
|
||||
{{ option.title }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-button
|
||||
v-else
|
||||
:key="action.name"
|
||||
:size="size"
|
||||
v-bind="cleanButtonAction(action)"
|
||||
class="action-item"
|
||||
@click="handleClick(action)"
|
||||
>
|
||||
<el-tooltip :disabled="!action.tip" :content="action.tip" placement="top">
|
||||
<span>
|
||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'DataActions',
|
||||
props: {
|
||||
grouped: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'small'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
return this.cleanActions(this.actions)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDropdownCallback(command) {
|
||||
const [option, dropdown] = command
|
||||
const defaultCallback = () => this.$log.debug('No callback found: ', option, dropdown)
|
||||
let callback = option.callback
|
||||
if (!callback) {
|
||||
callback = dropdown.callback
|
||||
}
|
||||
if (!callback) {
|
||||
callback = defaultCallback
|
||||
}
|
||||
return callback(option)
|
||||
},
|
||||
handleClick(action) {
|
||||
if (action && action.callback) {
|
||||
action.callback(action)
|
||||
} else {
|
||||
this.$log.debug('No callback found')
|
||||
}
|
||||
this.$emit('actionClick', action)
|
||||
},
|
||||
checkItem(item, attr, defaults) {
|
||||
if (!item) {
|
||||
return true
|
||||
}
|
||||
let ok = item[attr]
|
||||
if (ok && typeof ok === 'function') {
|
||||
ok = ok(item)
|
||||
} else if (ok == null) {
|
||||
ok = defaults === undefined ? true : defaults
|
||||
}
|
||||
return ok
|
||||
},
|
||||
cleanButtonAction(action) {
|
||||
action = _.cloneDeep(action)
|
||||
delete action['dropdown']
|
||||
delete action['callback']
|
||||
delete action['name']
|
||||
delete action['can']
|
||||
return action
|
||||
},
|
||||
cleanActions(actions) {
|
||||
const cleanedActions = []
|
||||
const cloneActions = _.cloneDeep(actions)
|
||||
for (const v of cloneActions) {
|
||||
if (!v) {
|
||||
continue
|
||||
}
|
||||
const action = Object.assign({}, v)
|
||||
// 是否拥有这个action
|
||||
const has = this.checkItem(action, 'has')
|
||||
delete action['has']
|
||||
if (!has) {
|
||||
continue
|
||||
}
|
||||
// 是否有分割线
|
||||
action.divided = this.checkItem(action, 'divided', false)
|
||||
|
||||
// 是否是disabled
|
||||
const can = this.checkItem(action, 'can')
|
||||
action.disabled = !can
|
||||
|
||||
if (action.dropdown) {
|
||||
// const dropdown = this.cleanActions(action.dropdown)
|
||||
action.dropdown = this.cleanActions(action.dropdown)
|
||||
}
|
||||
cleanedActions.push(action)
|
||||
}
|
||||
return cleanedActions
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dropdown-menu-title {
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 30px;
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: solid 1px #e4e7ed;
|
||||
}
|
||||
|
||||
.dropdown-menu-title:first-child {
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.el-button-ungroup .action-item {
|
||||
margin-left: 4px
|
||||
}
|
||||
|
||||
.el-button-ungroup .action-item:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -97,7 +97,6 @@ export default {
|
||||
}
|
||||
},
|
||||
props: {
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
data: Object,
|
||||
prop: {
|
||||
type: String,
|
||||
@@ -105,13 +104,10 @@ export default {
|
||||
return this.data.id
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line vue/require-prop-types,vue/require-default-prop
|
||||
itemValue: {},
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
value: Object,
|
||||
disabled: Boolean,
|
||||
readonly: Boolean,
|
||||
// eslint-disable-next-line vue/require-default-prop
|
||||
options: Array
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-form ref="elForm" v-bind="$attrs" :model="value" class="el-form-renderer">
|
||||
<template v-for="item in innerContent">
|
||||
<slot v-if="!isHidden(item)" :name="`id:${item.id}`" />
|
||||
<slot :name="`id:${item.id}`" />
|
||||
<component
|
||||
:is="item.type === GROUP ? 'render-form-group' : 'render-form-item'"
|
||||
:key="item.id"
|
||||
@@ -13,7 +13,7 @@
|
||||
:options="options[item.id]"
|
||||
@updateValue="updateValue"
|
||||
/>
|
||||
<slot v-if="!isHidden(item)" :name="`$id:${item.id}`" />
|
||||
<slot :name="`$id:${item.id}`" />
|
||||
</template>
|
||||
<slot />
|
||||
</el-form>
|
||||
@@ -202,18 +202,6 @@ export default {
|
||||
setOptions(id, options) {
|
||||
_set(this.options, id, options)
|
||||
this.options = { ...this.options } // 设置之前不存在的 options 时需要重新设置响应式更新
|
||||
},
|
||||
isHidden(item) {
|
||||
if (!item.el || !item.el['hiddenGroup']) {
|
||||
return false
|
||||
}
|
||||
if (item.hidden === true) {
|
||||
return true
|
||||
}
|
||||
if (typeof item.hidden === 'function') {
|
||||
return item.hidden(this.value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:content="fields"
|
||||
:form="basicForm"
|
||||
label-position="right"
|
||||
label-width="20%"
|
||||
label-width="17%"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
@@ -12,7 +12,7 @@
|
||||
<slot v-for="item in fields" :slot="`id:${item.id}`" :name="`id:${item.id}`" />
|
||||
<slot v-for="item in fields" :slot="`$id:${item.id}`" :name="`$id:${item.id}`" />
|
||||
|
||||
<el-form-item v-if="hasButtons" class="form-buttons">
|
||||
<el-form-item>
|
||||
<el-button v-for="button in moreButtons" :key="button.title" size="small" v-bind="button" @click="handleClick(button)">{{ button.title }}</el-button>
|
||||
<el-button v-if="defaultButton && hasReset" size="small" @click="resetForm('form')">{{ $t('common.Reset') }}</el-button>
|
||||
<el-button v-if="defaultButton && hasSaveContinue" size="small" @click="submitForm('form', true)">{{ $t('common.SaveAndAddAnother') }}</el-button>
|
||||
@@ -32,10 +32,6 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasButtons: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasReset: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@@ -51,7 +47,7 @@ export default {
|
||||
// 初始值
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: () => { return {} }
|
||||
},
|
||||
moreButtons: {
|
||||
type: Array,
|
||||
|
||||
@@ -8,27 +8,7 @@ export const RequiredChange = {
|
||||
required: true, message: i18n.t('common.fieldRequiredError'), trigger: 'change'
|
||||
}
|
||||
|
||||
export const EmailCheck = {
|
||||
type: 'email',
|
||||
message: i18n.t('common.InputEmailAddress'),
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
|
||||
export default {
|
||||
Required,
|
||||
RequiredChange,
|
||||
EmailCheck
|
||||
}
|
||||
|
||||
export const JsonRequired = {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
try {
|
||||
JSON.parse(value)
|
||||
callback()
|
||||
} catch (e) {
|
||||
callback(new Error(i18n.t('common.InvalidJson')))
|
||||
}
|
||||
}
|
||||
RequiredChange
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ import getLocatedSlotKeys from './utils/extract-keys'
|
||||
import transformSearchImmediatelyItem from './utils/search-immediately-item'
|
||||
import isFalsey from './utils/is-falsey'
|
||||
import merge from 'deepmerge'
|
||||
const defaultFirstPage = 1
|
||||
const defaultFirstPage = 0
|
||||
const noPaginationDataPath = 'payload'
|
||||
|
||||
export default {
|
||||
@@ -426,6 +426,7 @@ export default {
|
||||
onEdit: {
|
||||
type: Function,
|
||||
default(row) {
|
||||
// console.log('On delete row')
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -722,10 +723,6 @@ export default {
|
||||
default(row, index) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
totalData: {
|
||||
type: Array,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -813,13 +810,6 @@ export default {
|
||||
},
|
||||
_searchForm() {
|
||||
return transformSearchImmediatelyItem(this.collapseForm, this)
|
||||
},
|
||||
lastPageNum() {
|
||||
// page
|
||||
const pageOffset = this.firstPage - defaultFirstPage
|
||||
const pageCount = Math.ceil(this.total / this.size)
|
||||
const lastPageNum = pageCount + pageOffset
|
||||
return lastPageNum
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -838,13 +828,6 @@ export default {
|
||||
* @property {array} rows - 已选中的行数据的数组
|
||||
*/
|
||||
this.$emit('selection-change', val)
|
||||
},
|
||||
totalData(val) {
|
||||
if (val && val.length !== this.total) {
|
||||
this.page = defaultFirstPage
|
||||
this.total = val.length
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -861,9 +844,6 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.totalData) {
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getQuery() {
|
||||
@@ -897,58 +877,12 @@ export default {
|
||||
}
|
||||
return query
|
||||
},
|
||||
getPageData() {
|
||||
return this.data
|
||||
},
|
||||
async gotoNextPage() {
|
||||
if (!this.hasNextPage()) {
|
||||
return false
|
||||
}
|
||||
this.page += 1
|
||||
await this.getList({ loading: true })
|
||||
},
|
||||
hasNextPage() {
|
||||
return this.page < this.lastPageNum
|
||||
},
|
||||
getList({ loading = true } = {}) {
|
||||
const { url } = this
|
||||
if (url) {
|
||||
return this.getListFromRemote({ loading: loading })
|
||||
}
|
||||
if (this.totalData) {
|
||||
return this.getListFromStaticData({ loading: true })
|
||||
}
|
||||
// this.$log.debug("last page is: ", this.lastPageNum)
|
||||
},
|
||||
getListFromStaticData({ loading = true } = {}) {
|
||||
if (loading) {
|
||||
this.loading = true
|
||||
}
|
||||
if (!this.hasPagination) {
|
||||
this.data = this.totalData
|
||||
this.loading = false
|
||||
if (this.isTree) {
|
||||
this.data = this.tree2Array(this.data, this.expandAll)
|
||||
}
|
||||
return this.data
|
||||
}
|
||||
// page
|
||||
const pageOffset = this.firstPage - defaultFirstPage
|
||||
const page = this.page === 0 ? 1 : this.page
|
||||
const start = (page + pageOffset - 1) * this.size
|
||||
const end = (page + pageOffset) * this.size
|
||||
this.$log.debug(`page: ${page}, size: ${this.size}, start: ${start}, end: ${end}`)
|
||||
this.data = this.totalData.slice(start, end)
|
||||
this.loading = false
|
||||
this.data = this.tree2Array(this.data, this.expandAll)
|
||||
return this.data
|
||||
},
|
||||
/**
|
||||
* 手动刷新列表数据,选项的默认值为: { loading: true }
|
||||
* @public
|
||||
* @param {object} options 方法选项
|
||||
*/
|
||||
getListFromRemote({ loading = true } = {}) {
|
||||
getList({ loading = true } = {}) {
|
||||
const { url } = this
|
||||
if (!url) {
|
||||
return
|
||||
|
||||
@@ -94,20 +94,12 @@ export default {
|
||||
tableConfig() {
|
||||
const tableDefaultConfig = this.defaultConfig
|
||||
tableDefaultConfig.paginationSize = _.get(this.globalTableConfig, 'paginationSize', 15)
|
||||
let tableAttrs = tableDefaultConfig.tableAttrs
|
||||
if (this.config.tableAttrs) {
|
||||
tableAttrs = Object.assign(tableAttrs, this.config.tableAttrs)
|
||||
}
|
||||
const config = Object.assign(tableDefaultConfig, this.config)
|
||||
config.tableAttrs = tableAttrs
|
||||
return config
|
||||
},
|
||||
iListeners() {
|
||||
return Object.assign({}, this.$listeners, this.tableConfig.listeners)
|
||||
},
|
||||
dataTable() {
|
||||
return this.$refs.table
|
||||
},
|
||||
...mapGetters({
|
||||
'globalTableConfig': 'tableConfig'
|
||||
})
|
||||
|
||||
@@ -95,8 +95,6 @@ export default {
|
||||
vm.zTree.destroy()
|
||||
}
|
||||
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
|
||||
// 手动上报事件, Tree加载完成
|
||||
this.$emit('TreeInitFinish', this.zTree)
|
||||
if (this.treeSetting.showRefresh) {
|
||||
this.rootNodeAddDom(
|
||||
this.zTree,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ZTree ref="ztree" :setting="treeSetting" v-on="$listeners">
|
||||
<ZTree ref="ztree" :setting="treeSetting">
|
||||
<!--Slot透传-->
|
||||
<div slot="rMenu" slot-scope="{data}">
|
||||
<slot name="rMenu" :data="data" />
|
||||
|
||||
@@ -28,30 +28,13 @@ export default {
|
||||
return this.formatter(this.item, this.value)
|
||||
}
|
||||
if (typeof this.value === 'boolean') {
|
||||
return (
|
||||
<span class='item-value'>{this.toChoicesDisplay(this.value)}</span>
|
||||
)
|
||||
return <span>{this.toChoicesDisplay(this.value)}</span>
|
||||
}
|
||||
if (this.value instanceof Array) {
|
||||
const newArr = this.value || []
|
||||
return (
|
||||
<span class='item-value'>
|
||||
{
|
||||
newArr.map((item, index) => <div key={index}>{item.key}:{item.value} </div>)
|
||||
}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<span class='item-value'>{this.value}</span>
|
||||
)
|
||||
return <span>{this.value}</span>
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-value {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -55,6 +55,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -73,8 +74,4 @@ export default {
|
||||
/*padding-top: 10px;*/
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<el-link @click="onClick">
|
||||
{{ title }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Link',
|
||||
props: {
|
||||
href: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
window.open(this.href)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,409 +0,0 @@
|
||||
<template>
|
||||
<div class="c-weektime">
|
||||
<div class="c-schedue" />
|
||||
<div :class="{'c-schedue': true, 'c-schedue-notransi': mode}" :style="styleValue" />
|
||||
<table class="c-weektime-table" :class="{'c-min-table': colspan < 2}">
|
||||
<thead class="c-weektime-head">
|
||||
<tr>
|
||||
<th rowspan="8" class="week-td">{{ this.$t('common.WeekCronSelect.WeekOrTime') }}</th>
|
||||
<th :colspan="12 * colspan">00:00 - 12:00</th>
|
||||
<th :colspan="12 * colspan">12:00 - 24:00</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-for="t in theadArr" :key="t" :colspan="colspan">{{ t }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="c-weektime-body">
|
||||
<tr v-for="t in weektimeData" :key="t.row">
|
||||
<td>{{ t.value }}</td>
|
||||
<td
|
||||
v-for="n in t.child"
|
||||
:key="`${n.row}-${n.col}`"
|
||||
:data-week="n.row"
|
||||
:data-time="n.col"
|
||||
:class="selectClasses(n)"
|
||||
class="weektime-atom-item"
|
||||
@mouseenter="cellEnter(n)"
|
||||
@mousedown="cellDown(n)"
|
||||
@mouseup="cellUp(n)"
|
||||
/>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="49" class="c-weektime-preview">
|
||||
<div class="g-clearfix c-weektime-con">
|
||||
<span class="g-pull-left">{{ this.$t('common.WeekCronSelect.CanDragSelect') }}</span>
|
||||
<a class="g-pull-right" @click.prevent="clearWeektime">{{ this.$t('common.WeekCronSelect.ClearSelection') }}</a>
|
||||
<a class="g-pull-right g-pull-margin" @click.prevent="selectAll">{{ this.$t('common.WeekCronSelect.SelectAll') }}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const createArr = len => {
|
||||
return Array.from(Array(len)).map((ret, id) => id)
|
||||
}
|
||||
function splicing(list) {
|
||||
let same
|
||||
let i = -1
|
||||
const len = list.length
|
||||
const arr = []
|
||||
|
||||
if (!len) return
|
||||
while (++i < len) {
|
||||
const item = list[i]
|
||||
if (item.check) {
|
||||
if (item.check !== Boolean(same)) {
|
||||
arr.push(...['、', item.begin, '~', item.end])
|
||||
} else if (arr.length) {
|
||||
arr.pop()
|
||||
arr.push(item.end)
|
||||
}
|
||||
}
|
||||
same = Boolean(item.check)
|
||||
}
|
||||
arr.shift()
|
||||
return arr.join('')
|
||||
}
|
||||
export default {
|
||||
name: 'WeekCronSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
colspan: {
|
||||
type: Number,
|
||||
default() {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
left: 0,
|
||||
top: 0,
|
||||
mode: 0,
|
||||
row: 0,
|
||||
col: 0,
|
||||
theadArr: [],
|
||||
weekArr: [
|
||||
this.$t('common.WeekCronSelect.Monday'),
|
||||
this.$t('common.WeekCronSelect.Tuesday'),
|
||||
this.$t('common.WeekCronSelect.Wednesday'),
|
||||
this.$t('common.WeekCronSelect.Thursday'),
|
||||
this.$t('common.WeekCronSelect.Friday'),
|
||||
this.$t('common.WeekCronSelect.Saturday'),
|
||||
this.$t('common.WeekCronSelect.Sunday')
|
||||
],
|
||||
weektimeData: [],
|
||||
timeRange: [] // 格式化之后数据
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleValue() {
|
||||
return {
|
||||
width: `${this.width}px`,
|
||||
height: `${this.height}px`,
|
||||
left: `${this.left}px`,
|
||||
top: `${this.top}px`
|
||||
}
|
||||
},
|
||||
selectClasses() {
|
||||
return n => n.check ? 'ui-selected' : ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
if (this.value.length > 0) this.nextValue()
|
||||
},
|
||||
methods: {
|
||||
// 初始化数据结构
|
||||
init() {
|
||||
this.theadArr = createArr(24)
|
||||
const isData = this.weekArr.map((ret, index) => {
|
||||
const children = (ret, row, max) => {
|
||||
return createArr(max).map((t, col) => {
|
||||
const curValue = this.formatWeektime(col)
|
||||
return {
|
||||
week: ret,
|
||||
value: curValue,
|
||||
begin: curValue.split('~')[0],
|
||||
end: curValue.split('~')[1],
|
||||
row: row,
|
||||
col: col
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
value: ret,
|
||||
row: index,
|
||||
child: children(ret, index, 48)
|
||||
}
|
||||
})
|
||||
this.weektimeData = isData
|
||||
},
|
||||
// 反解析传递过来的默认值
|
||||
nextValue() {
|
||||
const deepValue = _.cloneDeep(this.value)
|
||||
for (let i = 0, len = deepValue.length; i < len; i++) {
|
||||
const cur = deepValue[i]
|
||||
const curValue = cur?.value
|
||||
if (curValue.length > 0) {
|
||||
const childValue = curValue.split('、')
|
||||
for (let j = 0; j < childValue.length; j++) {
|
||||
const curJ = childValue[j]
|
||||
this.renderWeekRange(curJ, cur.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 渲染时间区间
|
||||
renderWeekRange(val, id) {
|
||||
const idNum = id === 0 ? 6 : id - 1
|
||||
const [start, end] = val.split('~')
|
||||
const startVal = this.countIndex(start)
|
||||
const endVal = this.countIndex(end)
|
||||
for (let i = startVal; i < (endVal === 0 ? 48 : endVal); i++) {
|
||||
const curWeek = this.weektimeData[idNum]
|
||||
curWeek.child[i].check = true
|
||||
}
|
||||
},
|
||||
// 计算索引
|
||||
countIndex(val) {
|
||||
const one = val.substr(0, 2)
|
||||
const a1 = one.startsWith('0') ? one.substr(1, 2) : one
|
||||
var reg = RegExp(/30/)
|
||||
const a2 = val.match(reg) ? 1 : 0
|
||||
const curIndex = (a1 * 2) + a2
|
||||
return curIndex
|
||||
},
|
||||
formatDate(date, fmt) {
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds(),
|
||||
'q+': Math.floor((date.getMonth() + 3) / 3),
|
||||
'S': date.getMilliseconds()
|
||||
}
|
||||
if (/(y+)/.test(fmt)) {
|
||||
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||
}
|
||||
for (var k in o) {
|
||||
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
},
|
||||
formatWeektime(col) {
|
||||
const timeStamp = 1542384000000 // '2018-11-17 00:00:00'
|
||||
const beginStamp = timeStamp + col * 1800000 // col * 30 * 60 * 1000
|
||||
const endStamp = beginStamp + 1800000
|
||||
|
||||
const begin = this.formatDate(new Date(beginStamp), 'hh:mm')
|
||||
const end = this.formatDate(new Date(endStamp), 'hh:mm')
|
||||
return `${begin}~${end}`
|
||||
},
|
||||
// 清空时间段
|
||||
clearWeektime() {
|
||||
this.weektimeData.forEach(item => {
|
||||
item.child.forEach(t => {
|
||||
this.$set(t, 'check', false)
|
||||
})
|
||||
})
|
||||
this.timeRange = []
|
||||
this.$emit('change', this.timeRange)
|
||||
},
|
||||
// 全选
|
||||
selectAll() {
|
||||
this.weektimeData.forEach(item => {
|
||||
item.child.forEach(t => {
|
||||
this.$set(t, 'check', true)
|
||||
})
|
||||
})
|
||||
this.setTimeRange()
|
||||
},
|
||||
setTimeRange() {
|
||||
this.timeRange = this.weektimeData.map(item => {
|
||||
return {
|
||||
id: item.row === 6 ? 0 : item.row + 1,
|
||||
value: splicing(item.child)
|
||||
}
|
||||
})
|
||||
this.$emit('change', this.timeRange)
|
||||
},
|
||||
cellEnter(item) {
|
||||
const ele = document.querySelector(`td[data-week='${item.row}'][data-time='${item.col}']`)
|
||||
if (ele && !this.mode) {
|
||||
this.left = ele.offsetLeft
|
||||
this.top = ele.offsetTop
|
||||
} else {
|
||||
if (item.col <= this.col && item.row <= this.row) {
|
||||
this.width = (this.col - item.col + 1) * ele.offsetWidth
|
||||
this.height = (this.row - item.row + 1) * ele.offsetHeight
|
||||
this.left = ele.offsetLeft
|
||||
this.top = ele.offsetTop
|
||||
} else if (item.col >= this.col && item.row >= this.row) {
|
||||
this.width = (item.col - this.col + 1) * ele.offsetWidth
|
||||
this.height = (item.row - this.row + 1) * ele.offsetHeight
|
||||
if (item.col > this.col && item.row === this.row) this.top = ele.offsetTop
|
||||
if (item.col === this.col && item.row > this.row) this.left = ele.offsetLeft
|
||||
} else if (item.col > this.col && item.row < this.row) {
|
||||
this.width = (item.col - this.col + 1) * ele.offsetWidth
|
||||
this.height = (this.row - item.row + 1) * ele.offsetHeight
|
||||
this.top = ele.offsetTop
|
||||
} else if (item.col < this.col && item.row > this.row) {
|
||||
this.width = (this.col - item.col + 1) * ele.offsetWidth
|
||||
this.height = (item.row - this.row + 1) * ele.offsetHeight
|
||||
this.left = ele.offsetLeft
|
||||
}
|
||||
}
|
||||
},
|
||||
cellDown(item) {
|
||||
const ele = document.querySelector(`td[data-week='${item.row}'][data-time='${item.col}']`)
|
||||
this.check = Boolean(item.check)
|
||||
this.mode = 1
|
||||
if (ele) {
|
||||
this.width = ele.offsetWidth
|
||||
this.height = ele.offsetHeight
|
||||
}
|
||||
|
||||
this.row = item.row
|
||||
this.col = item.col
|
||||
},
|
||||
cellUp(item) {
|
||||
if (item.col <= this.col && item.row <= this.row) {
|
||||
this.selectWeek([item.row, this.row], [item.col, this.col], !this.check)
|
||||
} else if (item.col >= this.col && item.row >= this.row) {
|
||||
this.selectWeek([this.row, item.row], [this.col, item.col], !this.check)
|
||||
} else if (item.col > this.col && item.row < this.row) {
|
||||
this.selectWeek([item.row, this.row], [this.col, item.col], !this.check)
|
||||
} else if (item.col < this.col && item.row > this.row) {
|
||||
this.selectWeek([this.row, item.row], [item.col, this.col], !this.check)
|
||||
}
|
||||
|
||||
this.width = 0
|
||||
this.height = 0
|
||||
this.mode = 0
|
||||
|
||||
this.setTimeRange()
|
||||
},
|
||||
selectWeek(row, col, check) {
|
||||
const [minRow, maxRow] = row
|
||||
const [minCol, maxCol] = col
|
||||
this.weektimeData.forEach(item => {
|
||||
item.child.forEach(t => {
|
||||
if (t.row >= minRow && t.row <= maxRow && t.col >= minCol && t.col <= maxCol) {
|
||||
this.$set(t, 'check', check)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.c-weektime {
|
||||
min-width: 640px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.c-schedue {
|
||||
background: #598fe6;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: .6;
|
||||
pointer-events: none;
|
||||
}
|
||||
.c-schedue-notransi {
|
||||
transition: width .12s ease, height .12s ease, top .12s ease, left .12s ease;
|
||||
}
|
||||
.c-weektime-table {
|
||||
border-collapse: collapse;
|
||||
th {
|
||||
vertical-align: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
tr {
|
||||
height: 30px;
|
||||
}
|
||||
tr, td, th {
|
||||
user-select: none;
|
||||
border: 1px solid #dee4f5;
|
||||
text-align: center;
|
||||
min-width: 12px;
|
||||
line-height: 1.6em;
|
||||
transition: background .16s ease;
|
||||
}
|
||||
.c-weektime-head {
|
||||
font-size: 12px;
|
||||
.week-td {
|
||||
width: 72px;
|
||||
}
|
||||
}
|
||||
.c-weektime-body {
|
||||
font-size: 12px;
|
||||
td {
|
||||
&.weektime-atom-item {
|
||||
user-select: unset;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
&.ui-selected {
|
||||
background-color: #598fe6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.c-weektime-preview {
|
||||
line-height: 2.4em;
|
||||
padding: 0 10px;
|
||||
font-size: 13px;
|
||||
.c-weektime-con {
|
||||
line-height: 42px;
|
||||
user-select: none;
|
||||
}
|
||||
.c-weektime-time {
|
||||
text-align: left;
|
||||
line-height: 2.4em;
|
||||
p {
|
||||
max-width: 625px;
|
||||
line-height: 1.4em;
|
||||
word-break: break-all;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.c-min-table {
|
||||
tr, td, th {
|
||||
min-width: 24px;
|
||||
}
|
||||
}
|
||||
.g-clearfix {
|
||||
&:after, &:before {
|
||||
clear: both;
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
.g-pull-left {
|
||||
float: left;
|
||||
}
|
||||
.g-pull-right {
|
||||
float: right;
|
||||
color: #409eff!important;
|
||||
}
|
||||
.g-pull-margin {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.g-tip-text {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
@@ -1,33 +0,0 @@
|
||||
import DatetimeRangePicker from './DatetimeRangePicker'
|
||||
import Link from './Link'
|
||||
import PasswordInput from './PasswordInput'
|
||||
import Select2 from './Select2'
|
||||
import Swicher from './Swicher'
|
||||
import UploadField from './UploadField'
|
||||
import UploadKey from './UploadKey'
|
||||
import UserPassword from './UserPassword'
|
||||
import WeekCronSelect from './WeekCronSelect'
|
||||
|
||||
export default {
|
||||
DatetimeRangePicker,
|
||||
Link,
|
||||
PasswordInput,
|
||||
Select2,
|
||||
Swicher,
|
||||
UploadKey,
|
||||
UploadField,
|
||||
UserPassword,
|
||||
WeekCronSelect
|
||||
}
|
||||
|
||||
export {
|
||||
DatetimeRangePicker,
|
||||
Link,
|
||||
PasswordInput,
|
||||
Select2,
|
||||
Swicher,
|
||||
UploadKey,
|
||||
UploadField,
|
||||
UserPassword,
|
||||
WeekCronSelect
|
||||
}
|
||||
@@ -1,24 +1,20 @@
|
||||
<template>
|
||||
<div class="form-group-header">
|
||||
<div v-if="line" class="hr-line-dashed" />
|
||||
<h3>{{ group.title }}</h3>
|
||||
<h3>{{ title }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Title'
|
||||
},
|
||||
line: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
group: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import { DetailFormatter, SystemUserFormatter } from '@/components/TableFormatters'
|
||||
import { DetailFormatter, SystemUserFormatter } from '@/components/ListTable/formatters'
|
||||
import TreeTable from '../TreeTable'
|
||||
|
||||
export default {
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
vm.tableConfig.initialUrl = vm.tableConfig.url
|
||||
}
|
||||
const initialUrl = vm.tableConfig.initialUrl
|
||||
const nodeId = node.meta.data.id
|
||||
const nodeId = node.meta.node.id
|
||||
const url = initialUrl.replace('/assets/', `/nodes/${nodeId}/assets/`)
|
||||
vm.tableConfig.url = url
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<IBox v-bind="$attrs">
|
||||
<div v-if="contentHeading" class="ibox-heading">
|
||||
<div class="ibox-heading">
|
||||
<slot name="content-heading">
|
||||
<h3 v-if="contentHeading.title"><i v-if="contentHeading.fa" :class="'fa ' + contentHeading.fa" /> {{ contentHeading.title }}</h3>
|
||||
<small v-if="contentHeading.content"><i class="fa fa-tim" /> {{ contentHeading.content }}</small>
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
props: {
|
||||
contentHeading: {
|
||||
type: Object,
|
||||
default: null
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
v-if="mfaDialogShow"
|
||||
@MFAVerifyDone="showExportDialog"
|
||||
@MFAVerifyCancel="handleExportCancel"
|
||||
/>
|
||||
<Dialog
|
||||
v-if="exportDialogShow"
|
||||
:title="$t('common.Export')"
|
||||
:visible.sync="exportDialogShow"
|
||||
:destroy-on-close="true"
|
||||
@confirm="handleExportConfirm()"
|
||||
@cancel="handleExportCancel()"
|
||||
>
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
selectedRows: {
|
||||
@@ -48,20 +32,12 @@ export default {
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeExport: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
mfaVerifyRequired: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => ''
|
||||
},
|
||||
performExport: {
|
||||
type: Function,
|
||||
default(selectedRows, exportOptions, query, exportType) {
|
||||
return this.defaultPerformExport(selectedRows, exportOptions, query, exportType)
|
||||
default(selectedRows, exportOptions, query) {
|
||||
return this.defaultPerformExport(selectedRows, exportOptions, query)
|
||||
}
|
||||
},
|
||||
canExportAll: {
|
||||
@@ -79,12 +55,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportDialogShow: false,
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {},
|
||||
mfaVerified: false,
|
||||
mfaDialogShow: false
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -141,33 +115,18 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url, name }) => {
|
||||
// Todo: 没有时间了,只能先这么处理了
|
||||
if (url === this.url || url.indexOf(this.url) > -1 || url.indexOf('account') > -1) {
|
||||
this.showExportDialog()
|
||||
}
|
||||
this.$eventBus.$on('showExportDialog', (row) => {
|
||||
this.showExportDialog = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
showExportDialog() {
|
||||
if (!this.mfaVerifyRequired) {
|
||||
this.exportDialogShow = true
|
||||
return
|
||||
}
|
||||
// 这是需要校验 MFA 的
|
||||
if (!this.mfaDialogShow) {
|
||||
this.mfaDialogShow = true
|
||||
} else {
|
||||
this.exportDialogShow = true
|
||||
}
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
async defaultPerformExport(selectRows, exportOption, q, exportTypeOption) {
|
||||
async defaultPerformExport(selectRows, exportOption, q) {
|
||||
const url = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
const query = Object.assign({}, q)
|
||||
if (exportOption === 'selected') {
|
||||
@@ -178,8 +137,13 @@ export default {
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = exportTypeOption
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
@@ -187,20 +151,17 @@ export default {
|
||||
},
|
||||
async handleExport() {
|
||||
const listTableRef = this.$parent.$parent.$parent.$parent
|
||||
const query = listTableRef['dataTable'].getQuery()
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
await this.beforeExport()
|
||||
return this.performExport(this.selectedRows, this.exportOption, query, this.exportTypeOption)
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.handleExport()
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ExportDialog :selected-rows="selectedRows" v-bind="exportOptions" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" v-bind="importOptions" v-on="$listeners" />
|
||||
<ExportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@ import ExportDialog from './ExportDialog'
|
||||
import ImportDialog from './ImportDialog'
|
||||
|
||||
export default {
|
||||
name: 'ImExportDialog',
|
||||
name: 'DialogAction',
|
||||
components: {
|
||||
ExportDialog,
|
||||
ImportDialog
|
||||
@@ -20,13 +20,9 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
url: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,73 +1,64 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:title="importTitle"
|
||||
:title="$t('common.Import')"
|
||||
:visible.sync="showImportDialog"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:loading-status="loadStatus"
|
||||
width="80%"
|
||||
class="importDialog"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
@close="handleImportCancel"
|
||||
@confirm="handleImportConfirm"
|
||||
@cancel="handleImportCancel()"
|
||||
>
|
||||
<el-form v-if="!showTable" label-position="left" style="padding-left: 50px">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="importTypeOption">
|
||||
<el-radio v-for="option of importTypeOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.Import' )" :label-width="'100px'">
|
||||
<el-radio v-model="importOption" class="export-item" label="create">{{ this.$t('common.Create') }}</el-radio>
|
||||
<el-radio v-model="importOption" class="export-item" label="update">{{ this.$t('common.Update') }}</el-radio>
|
||||
<el-radio v-model="importOption" class="export-item" label="1">{{ this.$t('common.Create') }}</el-radio>
|
||||
<el-radio v-model="importOption" class="export-item" label="2">{{ this.$t('common.Update') }}</el-radio>
|
||||
<div style="line-height: 1.5">
|
||||
<span class="el-upload__tip">
|
||||
{{ downloadTemplateTitle }}
|
||||
<el-link type="success" :underline="false" style="padding-left: 10px" @click="downloadTemplateFile('csv')"> CSV </el-link>
|
||||
<el-link type="success" :underline="false" style="padding-left: 10px" @click="downloadTemplateFile('xlsx')"> XLSX </el-link>
|
||||
<span v-if="importOption==='1'" class="el-upload__tip">
|
||||
{{ this.$t('common.imExport.downloadImportTemplateMsg') }}
|
||||
<el-link type="success" :underline="false" :href="downloadImportTempUrl">{{ this.$t('common.Download') }}</el-link>
|
||||
</span>
|
||||
<span v-else class="el-upload__tip">
|
||||
{{ this.$t('common.imExport.downloadUpdateTemplateMsg') }}
|
||||
<el-link type="success" :underline="false" @click="downloadUpdateTempUrl">{{ this.$t('common.Download') }}</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.Upload' )" :label-width="'100px'" class="file-uploader">
|
||||
<el-form-item :label="$t('common.Upload' )" :label-width="'100px'">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
drag
|
||||
action="string"
|
||||
list-type="text/csv"
|
||||
:http-request="handleImport"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:on-change="onFileChange"
|
||||
:before-upload="beforeUpload"
|
||||
accept=".csv,.xlsx"
|
||||
>
|
||||
<i class="el-icon-upload" />
|
||||
<div class="el-upload__text">{{ $t('common.imExport.dragUploadFileInfo') }}</div>
|
||||
<div slot="tip" class="el-upload__tip">
|
||||
<span :class="{'hasError': hasFileFormatOrSizeError }">{{ $t('common.imExport.uploadCsvLth10MHelpText') }}</span>
|
||||
<div v-if="renderError" class="hasError">{{ renderError }}</div>
|
||||
</div>
|
||||
<el-button size="mini" type="default">{{ this.$t('common.SelectFile') }}</el-button>
|
||||
<!-- <div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>-->
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div v-else class="importTableZone">
|
||||
<ImportTable
|
||||
ref="importTable"
|
||||
:json-data="jsonData"
|
||||
:import-option="importOption"
|
||||
:url="url"
|
||||
@cancel="cancelUpload"
|
||||
@finish="closeDialog"
|
||||
/>
|
||||
<div v-if="errorMsg" class="error-msg error-results">
|
||||
<ul v-if="typeof errorMsg === 'object'">
|
||||
<li v-for="(item, index) in errorMsg" :key="item + '-' + index"> {{ item }}</li>
|
||||
</ul>
|
||||
<span v-else>{{ errorMsg }}</span>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import ImportTable from '@/components/ListTable/TableAction/ImportTable'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
|
||||
export default {
|
||||
name: 'ImportDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
ImportTable
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
selectedRows: {
|
||||
@@ -82,119 +73,103 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
showImportDialog: false,
|
||||
importOption: 'create',
|
||||
importOption: '1',
|
||||
isCsv: true,
|
||||
errorMsg: '',
|
||||
loadStatus: false,
|
||||
importTypeOption: 'csv',
|
||||
importTypeIsCsv: true,
|
||||
showTable: false,
|
||||
renderError: '',
|
||||
hasFileFormatOrSizeError: false,
|
||||
jsonData: {}
|
||||
importTypeOption: 'csv'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasSelected() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
importTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
},
|
||||
upLoadUrl() {
|
||||
return this.url
|
||||
},
|
||||
downloadImportTempUrl() {
|
||||
const format = this.importTypeOption === 'csv' ? 'format=csv&template=import&limit=1' : 'format=xlsx&template=import&limit=1'
|
||||
const url = (this.url.indexOf('?') === -1) ? `${this.url}?${format}` : `${this.url}&${format}`
|
||||
return url
|
||||
},
|
||||
uploadHelpTextClass() {
|
||||
const cls = ['el-upload__tip']
|
||||
if (!this.isCsv) {
|
||||
cls.push('error-msg')
|
||||
}
|
||||
return cls
|
||||
},
|
||||
downloadTemplateTitle() {
|
||||
if (this.importOption === 'create') {
|
||||
return this.$t('common.imExport.downloadImportTemplateMsg')
|
||||
} else {
|
||||
return this.$t('common.imExport.downloadUpdateTemplateMsg')
|
||||
}
|
||||
},
|
||||
importTitle() {
|
||||
if (this.importOption === 'create') {
|
||||
return this.$t('common.Import') + this.$t('common.Create')
|
||||
} else {
|
||||
return this.$t('common.Import') + this.$t('common.Update')
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
importOption(val) {
|
||||
this.showTable = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showImportDialog', ({ url }) => {
|
||||
if (url === this.url) {
|
||||
this.showImportDialog = true
|
||||
}
|
||||
this.$eventBus.$on('showImportDialog', (row) => {
|
||||
this.showImportDialog = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
closeDialog() {
|
||||
this.showImportDialog = false
|
||||
},
|
||||
cancelUpload() {
|
||||
this.showTable = false
|
||||
this.renderError = ''
|
||||
this.jsonData = {}
|
||||
},
|
||||
onFileChange(file, fileList) {
|
||||
fileList.splice(0, fileList.length)
|
||||
if (file.status !== 'ready') {
|
||||
return
|
||||
}
|
||||
// const isCsv = file.raw.type = 'text/csv'
|
||||
if (!this.beforeUpload(file)) {
|
||||
return
|
||||
}
|
||||
const isCsv = file.name.indexOf('csv') > -1
|
||||
const url = new URL(this.url, 'http://localhost')
|
||||
url.pathname += 'render-to-json/'
|
||||
const renderToJsonUrl = url.toString().replace('http://localhost', '')
|
||||
this.$axios.post(
|
||||
renderToJsonUrl,
|
||||
file.raw,
|
||||
{ headers: { 'Content-Type': isCsv ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
|
||||
).then(data => {
|
||||
this.jsonData = data
|
||||
this.showTable = true
|
||||
performUpdate(item) {
|
||||
this.$axios.put(
|
||||
this.upLoadUrl,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
fileList.splice(0, fileList.length)
|
||||
this.renderError = getErrorResponseMsg(error)
|
||||
this.catchError(error)
|
||||
}).finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const isLt30M = file.size / 1024 / 1024 < 30
|
||||
if (!isLt30M) {
|
||||
this.hasFileFormatOrSizeError = true
|
||||
}
|
||||
return isLt30M
|
||||
},
|
||||
async downloadTemplateFile(tp) {
|
||||
const downloadUrl = await this.getDownloadTemplateUrl(tp)
|
||||
window.open(downloadUrl)
|
||||
},
|
||||
async getDownloadTemplateUrl(tp) {
|
||||
const template = this.importOption === 'create' ? 'import' : 'update'
|
||||
let query = `format=${tp}&template=${template}`
|
||||
if (this.importOption === 'update' && this.selectedRows.length > 0) {
|
||||
const resources = []
|
||||
for (const item of this.selectedRows) {
|
||||
resources.push(item.id)
|
||||
}
|
||||
const resp = await createSourceIdCache(resources)
|
||||
query += `&spm=${resp.spm}`
|
||||
} else {
|
||||
query += '&limit=1'
|
||||
}
|
||||
return this.url.indexOf('?') === -1 ? `${this.url}?${query}` : `${this.url}&${query}`
|
||||
performCreate(item) {
|
||||
this.$axios.post(
|
||||
this.upLoadUrl,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': this.importTypeOption === 'csv' ? 'text/csv' : 'text/xlsx' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
this.catchError(error)
|
||||
}).finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
catchError(error) {
|
||||
console.log(error)
|
||||
this.$refs.upload.clearFiles()
|
||||
if (error.response && error.response.status === 400) {
|
||||
const errorData = error.response.data
|
||||
const totalErrorMsg = []
|
||||
errorData.forEach((value, index) => {
|
||||
if (typeof value === 'string') {
|
||||
totalErrorMsg.push(`line ${index}. ${value}`)
|
||||
} else {
|
||||
const errorMsg = [`line ${index}. `]
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
if (v) {
|
||||
errorMsg.push(`${k}: ${v}`)
|
||||
}
|
||||
}
|
||||
if (errorMsg.length > 1) {
|
||||
totalErrorMsg.push(errorMsg.join(' '))
|
||||
}
|
||||
}
|
||||
})
|
||||
this.errorMsg = totalErrorMsg
|
||||
}
|
||||
},
|
||||
onSuccess(msg) {
|
||||
this.errorMsg = ''
|
||||
@@ -206,14 +181,40 @@ export default {
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
handleImport(item) {
|
||||
this.loadStatus = true
|
||||
if (this.importOption === '1') {
|
||||
this.performCreate(item)
|
||||
} else {
|
||||
this.performUpdate(item)
|
||||
}
|
||||
},
|
||||
async downloadUpdateTempUrl() {
|
||||
var resources = []
|
||||
const data = this.selectedRows
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
resources.push(data[index].id)
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
const baseUrl = (process.env.VUE_APP_ENV === 'production') ? (`${this.url}`) : (`${process.env.VUE_APP_BASE_API}${this.url}`)
|
||||
const format = this.importTypeOption === 'csv' ? '?format=csv&template=update&spm=' : '?format=xlsx&template=update&spm='
|
||||
const url = `${baseUrl}${format}` + spm.spm
|
||||
return this.downloadCsv(url)
|
||||
},
|
||||
async handleImportConfirm() {
|
||||
this.$refs['importTable'].performUpload()
|
||||
this.$refs.upload.submit()
|
||||
},
|
||||
handleImportCancel() {
|
||||
this.showImportDialog = false
|
||||
this.showTable = false
|
||||
this.renderError = ''
|
||||
this.jsonData = {}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
this.isCsv = this.importTypeOption === 'csv' ? _.endsWith(file.name, 'csv') : _.endsWith(file.name, 'xlsx')
|
||||
if (!this.isCsv) {
|
||||
this.$message.error(
|
||||
this.$t('common.NeedSpecifiedFile')
|
||||
)
|
||||
}
|
||||
return this.isCsv
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,49 +231,4 @@ export default {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.importDialog >>> .el-form-item.file-uploader {
|
||||
padding-right: 150px;
|
||||
}
|
||||
|
||||
.file-uploader >>> .el-upload {
|
||||
width: 100%;
|
||||
//padding-right: 150px;
|
||||
}
|
||||
|
||||
.file-uploader >>> .el-upload-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.importTableZone {
|
||||
padding: 0 20px;
|
||||
|
||||
.importTable {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tableFilter {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.importTable >>> .el-dialog__body {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.export-item {
|
||||
margin-left: 80px;
|
||||
}
|
||||
|
||||
.export-item:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.hasError {
|
||||
color: $--color-danger;
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
line-height: 1.5;
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<div class="tableFilter">
|
||||
<el-radio-group v-model="importStatusFilter" size="small">
|
||||
<el-radio-button label="all">{{ $t('common.Total') }}</el-radio-button>
|
||||
<el-radio-button label="ok">{{ $t('common.Success') }}</el-radio-button>
|
||||
<el-radio-button label="error">{{ $t('common.Failed') }}</el-radio-button>
|
||||
<el-radio-button label="pending">{{ $t('common.Pending') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" style="text-align: center">
|
||||
<span class="summary-item summary-total"> {{ $t('common.Total') }}: {{ totalCount }}</span>
|
||||
<span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span>
|
||||
<span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span>
|
||||
<span class="summary-item summary-pending"> {{ $t('common.Pending') }}: {{ pendingCount }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="row">
|
||||
<el-progress :percentage="processedPercent" />
|
||||
</div>
|
||||
<DataTable v-if="tableGenDone" id="importTable" ref="dataTable" :config="tableConfig" class="importTable" />
|
||||
<div class="row" style="padding-top: 20px">
|
||||
<div style="float: right">
|
||||
<el-button size="small" @click="performCancel">{{ $t('common.Cancel') }}</el-button>
|
||||
<el-button size="small" type="primary" @click="performImportAction">{{ importActionTitle }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from '@/components/DataTable'
|
||||
import { sleep, getUpdateObjURL } from '@/utils/common'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/TableFormatters'
|
||||
export default {
|
||||
name: 'ImportTable',
|
||||
components: {
|
||||
DataTable
|
||||
},
|
||||
props: {
|
||||
jsonData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
importOption: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columns: [],
|
||||
importStatusFilter: 'all',
|
||||
iTotalData: [],
|
||||
tableConfig: {
|
||||
hasSelection: false,
|
||||
// hasPagination: false,
|
||||
columns: [],
|
||||
totalData: [],
|
||||
paginationSize: 10,
|
||||
paginationSizes: [10],
|
||||
tableAttrs: {
|
||||
stripe: true, // 斑马纹表格
|
||||
border: true, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark'
|
||||
}
|
||||
},
|
||||
tableGenDone: false,
|
||||
importTaskStatus: 'pending', // pending, started, stopped, done
|
||||
importTaskResult: '', // success, hasError
|
||||
hasImport: false,
|
||||
hasContinueButton: false,
|
||||
importActions: {
|
||||
import: this.$t('common.Import'),
|
||||
continue: this.$t('common.Continue'),
|
||||
stop: this.$t('common.Stop'),
|
||||
finished: this.$t('common.Finished')
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableColumnNameMapper() {
|
||||
const mapper = {}
|
||||
for (const column of this.tableConfig.columns) {
|
||||
mapper[column['prop']] = column['label']
|
||||
}
|
||||
return mapper
|
||||
},
|
||||
importAction() {
|
||||
switch (this.importTaskStatus) {
|
||||
case 'pending':
|
||||
return 'import'
|
||||
case 'started':
|
||||
return 'stop'
|
||||
}
|
||||
if (this.totalCount === this.successCount) {
|
||||
return 'finished'
|
||||
} else {
|
||||
return 'continue'
|
||||
}
|
||||
},
|
||||
importActionTitle() {
|
||||
return this.importActions[this.importAction]
|
||||
},
|
||||
successData() {
|
||||
return this.iTotalData.filter((item) => {
|
||||
return item['@status'] === 'ok'
|
||||
})
|
||||
},
|
||||
failedData() {
|
||||
return this.iTotalData.filter((item) => {
|
||||
return typeof item['@status'] === 'object' && item['@status'].name === 'error'
|
||||
})
|
||||
},
|
||||
pendingData() {
|
||||
return this.iTotalData.filter((item) => {
|
||||
return item['@status'] === 'pending'
|
||||
})
|
||||
},
|
||||
totalCount() {
|
||||
return this.iTotalData.length
|
||||
},
|
||||
successCount() {
|
||||
return this.successData.length
|
||||
},
|
||||
failedCount() {
|
||||
return this.failedData.length
|
||||
},
|
||||
pendingCount() {
|
||||
return this.pendingData.length
|
||||
},
|
||||
processedCount() {
|
||||
return this.totalCount - this.pendingCount
|
||||
},
|
||||
processedPercent() {
|
||||
if (this.totalCount === 0) {
|
||||
return 0
|
||||
}
|
||||
return Math.round(this.processedCount / this.totalCount * 100)
|
||||
},
|
||||
elDataTable() {
|
||||
return this.$refs['dataTable'].dataTable
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
importStatusFilter(val) {
|
||||
if (val === 'all') {
|
||||
this.tableConfig.totalData = this.iTotalData
|
||||
} else if (val === 'error') {
|
||||
this.tableConfig.totalData = this.failedData
|
||||
} else {
|
||||
this.tableConfig.totalData = this.iTotalData.filter((item) => {
|
||||
return item['@status'] === val
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.generateTable()
|
||||
},
|
||||
methods: {
|
||||
generateTableColumns(tableTitles, tableData) {
|
||||
const vm = this
|
||||
const columns = [{
|
||||
prop: '@status',
|
||||
label: vm.$t('common.Status'),
|
||||
width: '80px',
|
||||
align: 'center',
|
||||
formatter: StatusFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
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('common.Success')
|
||||
} else if (val === 'pending') {
|
||||
return vm.$t('common.Pending')
|
||||
} else if (val && val.name === 'error') {
|
||||
return val.error
|
||||
}
|
||||
return ''
|
||||
},
|
||||
hasTips: true
|
||||
}
|
||||
}]
|
||||
for (const item of tableTitles) {
|
||||
const dataItemLens = tableData.map(d => {
|
||||
const prop = item[1]
|
||||
const itemColData = d[prop]
|
||||
if (!d) {
|
||||
return 0
|
||||
}
|
||||
if (typeof itemColData !== 'number' && (!itemColData || !itemColData.length)) {
|
||||
return 0
|
||||
}
|
||||
return itemColData.length
|
||||
})
|
||||
let colMaxWidth = Math.max(...dataItemLens) * 10
|
||||
if (colMaxWidth === 0) {
|
||||
continue
|
||||
}
|
||||
colMaxWidth = Math.min(180, colMaxWidth)
|
||||
colMaxWidth = Math.max(colMaxWidth, 100)
|
||||
columns.push({
|
||||
prop: item[1],
|
||||
label: item[0],
|
||||
minWidth: colMaxWidth + 'px',
|
||||
showOverflowTooltip: true,
|
||||
formatter: EditableInputFormatter,
|
||||
formatterArgs: {
|
||||
onEnter: ({ row, col, oldValue, newValue }) => {
|
||||
const prop = col.prop
|
||||
row['@status'] = 'pending'
|
||||
this.$log.debug(`Set value ${oldValue} => ${newValue}`)
|
||||
this.$set(row, prop, newValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return columns
|
||||
},
|
||||
generateTableData(tableTitles, tableData) {
|
||||
const totalData = []
|
||||
tableData.forEach(item => {
|
||||
this.$set(item, '@status', 'pending')
|
||||
totalData.push(item)
|
||||
})
|
||||
return totalData
|
||||
},
|
||||
generateTable() {
|
||||
const tableTitles = this.jsonData['title']
|
||||
const tableData = this.jsonData['data']
|
||||
const columns = this.generateTableColumns(tableTitles, tableData)
|
||||
const totalData = this.generateTableData(tableTitles, tableData)
|
||||
this.tableConfig.columns = columns
|
||||
this.tableGenDone = true
|
||||
setTimeout(() => {
|
||||
this.iTotalData = totalData
|
||||
this.tableConfig.totalData = totalData
|
||||
}, 200)
|
||||
},
|
||||
beautifyErrorData(errorData) {
|
||||
if (typeof errorData === 'string') {
|
||||
return errorData
|
||||
} else if (Array.isArray(errorData)) {
|
||||
return errorData
|
||||
} else if (typeof errorData !== 'object') {
|
||||
return errorData
|
||||
}
|
||||
const data = []
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [key, value] of Object.entries(errorData)) {
|
||||
if (typeof value === 'object') {
|
||||
value = this.beautifyErrorData(value)
|
||||
}
|
||||
let label = this.tableColumnNameMapper[key]
|
||||
if (!label) {
|
||||
label = key
|
||||
}
|
||||
data.push(`${label}: ${value}`)
|
||||
}
|
||||
return data
|
||||
},
|
||||
performCancel() {
|
||||
this.performStop()
|
||||
this.$emit('cancel')
|
||||
},
|
||||
performFinish() {
|
||||
this.performStop()
|
||||
this.$emit('finish')
|
||||
},
|
||||
taskIsStopped() {
|
||||
return this.importTaskStatus === 'stopped'
|
||||
},
|
||||
performImportAction() {
|
||||
switch (this.importAction) {
|
||||
case 'continue':
|
||||
return this.performContinue()
|
||||
case 'import':
|
||||
return this.performUpload()
|
||||
case 'stop':
|
||||
return this.performStop()
|
||||
case 'finished':
|
||||
return this.performFinish()
|
||||
}
|
||||
},
|
||||
performContinue() {
|
||||
if (this.importTaskStatus === 'done') {
|
||||
for (const item of this.failedData) {
|
||||
item['@status'] = 'pending'
|
||||
}
|
||||
this.tableConfig.totalData = this.pendingData
|
||||
}
|
||||
this.importTaskStatus = 'started'
|
||||
setTimeout(() => {
|
||||
this.performUpload()
|
||||
}, 100)
|
||||
},
|
||||
performStop() {
|
||||
this.importTaskStatus = 'stopped'
|
||||
},
|
||||
async performUploadCurrentPageData() {
|
||||
const currentData = this.elDataTable.getPageData()
|
||||
for (const item of currentData) {
|
||||
if (item['@status'] !== 'pending') {
|
||||
continue
|
||||
}
|
||||
if (this.taskIsStopped()) {
|
||||
return
|
||||
}
|
||||
await this.performUploadObject(item)
|
||||
await sleep(100)
|
||||
}
|
||||
},
|
||||
async performUpload() {
|
||||
this.importTaskStatus = 'started'
|
||||
this.importStatusFilter = 'pending'
|
||||
while (!this.taskIsStopped()) {
|
||||
await this.performUploadCurrentPageData()
|
||||
const hasNextPage = this.elDataTable.hasNextPage()
|
||||
if (hasNextPage && !this.taskIsStopped()) {
|
||||
await this.elDataTable.gotoNextPage()
|
||||
await sleep(100)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this.pendingCount === 0) {
|
||||
this.importTaskStatus = 'done'
|
||||
}
|
||||
if (this.failedCount > 0) {
|
||||
this.$message.error(this.$t('common.imExport.hasImportErrorItemMsg') + '')
|
||||
}
|
||||
},
|
||||
async performUpdateObject(item) {
|
||||
const updateUrl = getUpdateObjURL(this.url, item.id)
|
||||
return this.$axios.put(
|
||||
updateUrl,
|
||||
item,
|
||||
{ disableFlashErrorMsg: true }
|
||||
)
|
||||
},
|
||||
async performUploadObject(item) {
|
||||
let handler = this.performCreateObject
|
||||
if (this.importOption === 'update') {
|
||||
handler = this.performUpdateObject
|
||||
}
|
||||
try {
|
||||
await handler.bind(this)(item)
|
||||
item['@status'] = 'ok'
|
||||
} catch (error) {
|
||||
const errorData = error?.response?.data
|
||||
const _error = this.beautifyErrorData(errorData)
|
||||
item['@status'] = {
|
||||
name: 'error',
|
||||
error: _error
|
||||
}
|
||||
}
|
||||
},
|
||||
async performCreateObject(item) {
|
||||
return this.$axios.post(
|
||||
this.url,
|
||||
item,
|
||||
{ disableFlashErrorMsg: true }
|
||||
)
|
||||
},
|
||||
keepElementInViewport() {
|
||||
const tableRef = document.getElementById('importTable')
|
||||
const pendingRef = tableRef?.getElementsByClassName('pendingStatus')[0]
|
||||
if (!pendingRef) {
|
||||
return
|
||||
}
|
||||
const parentTdRef = pendingRef.parentElement.parentElement.parentElement.parentElement
|
||||
const rect = parentTdRef.getBoundingClientRect()
|
||||
let windowInnerHeight = window.innerHeight || document.documentElement.clientHeight
|
||||
windowInnerHeight = windowInnerHeight * 0.97 - 150
|
||||
const inViewport = (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= windowInnerHeight
|
||||
)
|
||||
if (!inViewport) {
|
||||
parentTdRef.scrollIntoView({ behavior: 'auto', block: 'start', inline: 'start' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/styles/element-variables.scss";
|
||||
.summary-item {
|
||||
padding: 0 10px
|
||||
}
|
||||
|
||||
.summary-success {
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
.summary-failed {
|
||||
color: $--color-danger;
|
||||
}
|
||||
|
||||
.importTable >>> .cell {
|
||||
min-height: 20px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,46 +1,34 @@
|
||||
<template>
|
||||
<DataActions
|
||||
v-if="hasLeftActions"
|
||||
:actions="iActions"
|
||||
v-bind="$attrs"
|
||||
class="header-action"
|
||||
/>
|
||||
<ActionsGroup v-if="hasLeftActions" :actions="actions" :more-actions="moreActions" :more-actions-title="moreActionsTitle" v-bind="$attrs" class="header-action" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '@/i18n/i18n'
|
||||
import DataActions from '@/components/DataActions'
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import { cleanActions } from './utils'
|
||||
|
||||
const defaultTrue = { type: [Boolean, Function], default: true }
|
||||
const defaultFalse = { type: [Boolean, Function], default: false }
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
const defaultFalse = { type: Boolean, default: false }
|
||||
export default {
|
||||
name: 'LeftSide',
|
||||
components: {
|
||||
DataActions
|
||||
ActionsGroup
|
||||
},
|
||||
props: {
|
||||
hasLeftActions: defaultTrue,
|
||||
hasCreate: defaultTrue,
|
||||
canCreate: defaultTrue,
|
||||
createRoute: {
|
||||
type: [String, Object, Function],
|
||||
default: function() {
|
||||
return this.$route.name.replace('List', 'Create')
|
||||
}
|
||||
},
|
||||
createInNewPage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasBulkDelete: defaultTrue,
|
||||
hasBulkUpdate: defaultFalse,
|
||||
hasMoreActions: defaultTrue,
|
||||
hasLeftActions: defaultTrue,
|
||||
tableUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
createRoute: {
|
||||
type: [String, Object],
|
||||
default: function() {
|
||||
return this.$route.name.replace('List', 'Create')
|
||||
}
|
||||
},
|
||||
reloadTable: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
@@ -65,41 +53,23 @@ export default {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
moreCreates: {
|
||||
moreActionsButton: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
createTitle: {
|
||||
type: String,
|
||||
default: () => i18n.t('common.Create')
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const defaultActions = [
|
||||
{
|
||||
name: 'actionCreate',
|
||||
title: this.createTitle,
|
||||
type: 'primary',
|
||||
has: this.hasCreate && !this.moreCreates,
|
||||
can: this.canCreate,
|
||||
callback: this.handleCreate
|
||||
}
|
||||
]
|
||||
if (this.moreCreates) {
|
||||
const defaultMoreCreate = {
|
||||
name: 'actionMoreCreate',
|
||||
title: this.createTitle,
|
||||
type: 'primary',
|
||||
has: true,
|
||||
can: this.canCreate,
|
||||
dropdown: [],
|
||||
callback: this.handleCreate
|
||||
}
|
||||
const createCreateAction = Object.assign(defaultMoreCreate, this.moreCreates)
|
||||
defaultActions.push(createCreateAction)
|
||||
}
|
||||
return {
|
||||
defaultActions: defaultActions,
|
||||
defaultActions: [
|
||||
{
|
||||
name: 'actionCreate',
|
||||
title: this.$t('common.Create'),
|
||||
type: 'primary',
|
||||
has: this.hasCreate,
|
||||
can: true,
|
||||
callback: this.handleCreate
|
||||
}
|
||||
],
|
||||
defaultMoreActions: [
|
||||
{
|
||||
title: this.$t('common.deleteSelected'),
|
||||
@@ -122,9 +92,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
return [...this.actions, this.moreAction]
|
||||
},
|
||||
actions() {
|
||||
const actions = [...this.defaultActions, ...this.extraActions]
|
||||
return cleanActions(actions, true, {
|
||||
@@ -132,20 +99,12 @@ export default {
|
||||
reloadTable: this.reloadTable
|
||||
})
|
||||
},
|
||||
moreAction() {
|
||||
if (!this.hasMoreActions) {
|
||||
return
|
||||
}
|
||||
let dropdown = [...this.defaultMoreActions, ...this.extraMoreActions]
|
||||
dropdown = cleanActions(dropdown, true, {
|
||||
moreActions() {
|
||||
const actions = [...this.defaultMoreActions, ...this.extraMoreActions]
|
||||
return cleanActions(actions, true, {
|
||||
selectedRows: this.selectedRows,
|
||||
reloadTable: this.reloadTable
|
||||
})
|
||||
return {
|
||||
name: 'moreActions',
|
||||
title: this.moreActionsTitle || this.$t('common.MoreActions'),
|
||||
dropdown: dropdown
|
||||
}
|
||||
},
|
||||
hasSelectedRows() {
|
||||
return this.selectedRows.length > 0
|
||||
@@ -153,22 +112,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleCreate() {
|
||||
let route
|
||||
let route = {}
|
||||
if (typeof this.createRoute === 'string') {
|
||||
route = { name: this.createRoute }
|
||||
route.name = this.createRoute
|
||||
} else if (typeof this.createRoute === 'function') {
|
||||
route = this.createRoute()
|
||||
} else if (typeof this.createRoute === 'object') {
|
||||
} else {
|
||||
route = this.createRoute
|
||||
}
|
||||
this.$router.push(route)
|
||||
this.$log.debug('handle create')
|
||||
if (this.createInNewPage) {
|
||||
const { href } = this.$router.resolve(route)
|
||||
window.open(href, '_blank')
|
||||
} else {
|
||||
this.$router.push(route)
|
||||
}
|
||||
},
|
||||
defaultBulkDeleteCallback({ selectedRows, reloadTable }) {
|
||||
const msg = this.$t('common.deleteWarningMsg') + ' ' + selectedRows.length + ' ' + this.$t('common.rows') + ' ?'
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog
|
||||
:selected-rows="selectedRows"
|
||||
:export-options="iExportOptions"
|
||||
:import-options="iImportOptions"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" v-bind="$attrs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +9,6 @@
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import ImExportDialog from './ImExportDialog'
|
||||
import { cleanActions } from './utils'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
|
||||
@@ -30,41 +24,20 @@ export default {
|
||||
default: ''
|
||||
},
|
||||
hasExport: defaultTrue,
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleExportClick: {
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows })
|
||||
}
|
||||
},
|
||||
hasImport: defaultTrue,
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleImportClick: {
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
}
|
||||
},
|
||||
hasColumnSetting: defaultTrue,
|
||||
handleTableSettingClick: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl, row: selectedRows, name: this.name })
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows })
|
||||
}
|
||||
},
|
||||
hasRefresh: defaultTrue,
|
||||
handleRefreshClick: {
|
||||
type: Function,
|
||||
default: function() {
|
||||
this.reloadTable()
|
||||
}
|
||||
},
|
||||
selectedRows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
@@ -81,12 +54,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: [
|
||||
{ name: 'actionColumnSetting', fa: 'fa-cog', tip: this.$t('common.CustomCol'), has: this.hasColumnSetting, callback: this.handleTableSettingClick.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImportClick.bind(this) },
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExportClick.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', tip: this.$t('common.Refresh'), has: this.hasRefresh, callback: this.handleRefreshClick.bind(this) }
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExport.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImport.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', tip: this.$t('common.Refresh'), has: this.hasRefresh, callback: this.handleRefresh }
|
||||
],
|
||||
dialogExportVisible: false
|
||||
dialogExportVisible: false,
|
||||
exportValue: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -100,18 +73,20 @@ export default {
|
||||
},
|
||||
hasSelectedRows() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
iImportOptions() {
|
||||
return assignIfNot(this.importOptions, { url: this.tableUrl })
|
||||
},
|
||||
iExportOptions() {
|
||||
const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||
return options
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
},
|
||||
// handleExport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showExportDialog', { selectedRows })
|
||||
// },
|
||||
// handleImport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showImportDialog', { selectedRows })
|
||||
// },
|
||||
handleRefresh() {
|
||||
this.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<script>
|
||||
import AutoDataSearch from '@/components/AutoDataSearch'
|
||||
import LeftSide from './LeftSide'
|
||||
import DatetimeRangePicker from '@/components/FormFields/DatetimeRangePicker'
|
||||
import DatetimeRangePicker from '@/components/DatetimeRangePicker'
|
||||
import RightSide from './RightSide'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
|
||||
@@ -5,9 +5,7 @@ export function cleanActions(actions, canDefaults, { selectedRows, reloadTable }
|
||||
cloneActions.forEach((action) => {
|
||||
action.has = cleanBoolean(action, 'has', true, { selectedRows, reloadTable })
|
||||
action.can = cleanBoolean(action, 'can', true, { selectedRows, reloadTable })
|
||||
if (!action.dropdown) {
|
||||
action.callback = cleanCallback(action, { selectedRows, reloadTable })
|
||||
}
|
||||
action.callback = cleanCallback(action, { selectedRows, reloadTable })
|
||||
cleanedActions.push(action)
|
||||
})
|
||||
return cleanedActions
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
<template>
|
||||
<ActionsGroup v-loading="loadingStatus" :size="'mini'" :actions="actions" :more-actions="moreActions" :more-actions-title="moreActionsTitle" />
|
||||
<ActionsGroup :size="'mini'" :actions="actions" :more-actions="moreActions" :more-actions-title="moreActionsTitle" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
import BaseFormatter from './base'
|
||||
|
||||
const defaultPerformDelete = function({ row, col }) {
|
||||
const id = row.id
|
||||
const url = new URL(this.url, location.origin)
|
||||
url.pathname += `${id}/`
|
||||
const deleteUrl = url.href
|
||||
return this.$axios.delete(deleteUrl)
|
||||
const url = `${this.url}${id}/`
|
||||
return this.$axios.delete(url)
|
||||
}
|
||||
|
||||
const defaultUpdateCallback = function({ row, col }) {
|
||||
const id = row.id
|
||||
let route = { params: { id: id }}
|
||||
@@ -84,15 +81,11 @@ export default {
|
||||
default: function() {
|
||||
return {
|
||||
hasUpdate: true, // can set function(row, value)
|
||||
canUpdate: () => {
|
||||
return !this.$store.getters.currentOrgIsRoot
|
||||
}, // can set function(row, value)
|
||||
canUpdate: true, // can set function(row, value)
|
||||
hasDelete: true, // can set function(row, value)
|
||||
canDelete: true,
|
||||
hasClone: true,
|
||||
canClone: () => {
|
||||
return !this.$store.getters.currentOrgIsRoot
|
||||
},
|
||||
hasClone: false,
|
||||
canClone: true,
|
||||
updateRoute: this.$route.name.replace('List', 'Update'),
|
||||
cloneRoute: this.$route.name.replace('List', 'Create'),
|
||||
performDelete: defaultPerformDelete,
|
||||
@@ -147,12 +140,10 @@ export default {
|
||||
let actions = [...this.defaultActions, ...this.extraActions]
|
||||
actions = _.cloneDeep(actions)
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.cleanBoolean(v, 'has', true)
|
||||
v.can = this.cleanBoolean(v, 'can', true)
|
||||
v.callback = this.cleanCallback(v, 'callback')
|
||||
v.fa = this.cleanValue(v, 'fa')
|
||||
v.has = this.cleanBoolean(v, 'has')
|
||||
v.can = this.cleanBoolean(v, 'can')
|
||||
v.callback = this.cleanCallback(v)
|
||||
v.order = v.order || 100
|
||||
v.tip = this.cleanValue(v, 'tip')
|
||||
return v
|
||||
})
|
||||
actions = actions.filter((v) => v.has)
|
||||
@@ -170,21 +161,18 @@ export default {
|
||||
return []
|
||||
}
|
||||
return this.cleanedActions.slice(1, this.cleanedActions.length)
|
||||
},
|
||||
loadingStatus() {
|
||||
return this.col.formatterArgs.loading
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cleanBoolean(item, attr, defaults) {
|
||||
cleanBoolean(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? defaults : ok
|
||||
return ok === undefined ? true : ok
|
||||
}
|
||||
return this.cleanValue(item, attr)
|
||||
return ok(this.row, this.cellValue)
|
||||
},
|
||||
cleanCallback(item, attr) {
|
||||
const callback = item[attr]
|
||||
cleanCallback(item) {
|
||||
const callback = item.callback
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
@@ -193,20 +181,6 @@ export default {
|
||||
tableData: this.tableData
|
||||
}
|
||||
return () => { return callback.bind(this)(attrs) }
|
||||
},
|
||||
cleanValue(item, attr) {
|
||||
const value = item[attr]
|
||||
if (!value || typeof value !== 'function') {
|
||||
return value
|
||||
}
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
return value(attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip v-if="formatterArgs.hasTips" placement="bottom" effect="dark">
|
||||
<div slot="content">
|
||||
<template v-if="tipsIsArray">
|
||||
<div v-for="tip of tips" :key="tip">
|
||||
<span>{{ tip }}</span>
|
||||
<br>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else>
|
||||
{{ tips }}
|
||||
</span>
|
||||
</div>
|
||||
<div slot="content">{{ tipStatus }}<br>{{ tipTime }}</div>
|
||||
<i :class="'fa ' + iconClass" />
|
||||
</el-tooltip>
|
||||
<i v-else :class="'fa ' + iconClass" />
|
||||
@@ -20,8 +10,9 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'StatusFormatter',
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
@@ -32,12 +23,19 @@ export default {
|
||||
true: 'fa-check text-primary',
|
||||
false: 'fa-times text-danger'
|
||||
},
|
||||
getChoicesKey(val) {
|
||||
typeChange(val) {
|
||||
return !!val
|
||||
},
|
||||
getTip(val, col) {
|
||||
},
|
||||
hasTips: false
|
||||
hasTips: false,
|
||||
tipStatus(val, vm) {
|
||||
if (val.status === 0) {
|
||||
return vm.$t('assets.Unreachable')
|
||||
} else if (val.status === 1) {
|
||||
return vm.$t('assets.Reachable')
|
||||
} else if (val.status === 2) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,15 +47,15 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
const key = this.formatterArgs.getChoicesKey(this.cellValue)
|
||||
return this.formatterArgs.iconChoices[key] + ' ' + key + 'Status'
|
||||
const key = this.formatterArgs.typeChange(this.cellValue)
|
||||
return this.formatterArgs.iconChoices[key]
|
||||
},
|
||||
tips() {
|
||||
tipStatus() {
|
||||
const vm = this
|
||||
return this.formatterArgs.getTip(this.cellValue, vm)
|
||||
return this.formatterArgs.tipStatus(this.cellValue, vm)
|
||||
},
|
||||
tipsIsArray() {
|
||||
return Array.isArray(this.tips)
|
||||
tipTime() {
|
||||
return toSafeLocalDateStr(this.cellValue.datetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<ActionsGroup :size="'mini'" :actions="cleanedActions" :more-actions="cleanMoreActions" v-bind="$attrs" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'CustomActionsFormatterVue',
|
||||
components: {
|
||||
ActionsGroup
|
||||
},
|
||||
extends: BaseFormatter,
|
||||
computed: {
|
||||
cleanedActions() {
|
||||
if (this.col.actions.actions instanceof Array) {
|
||||
const copy = _.cloneDeep(this.col.actions.actions)
|
||||
let actions = [...copy]
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.checkBool(v, 'has')
|
||||
v.can = this.checkBool(v, 'can')
|
||||
v.callback = this.cleanCallback(v)
|
||||
return v
|
||||
})
|
||||
return actions
|
||||
}
|
||||
return []
|
||||
},
|
||||
cleanMoreActions() {
|
||||
if (this.col.actions.extraActions instanceof Array) {
|
||||
const copy = _.cloneDeep(this.col.actions.extraActions)
|
||||
let actions = [...copy]
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.checkBool(v, 'has')
|
||||
v.can = this.checkBool(v, 'can')
|
||||
v.callback = this.cleanCallback(v)
|
||||
return v
|
||||
})
|
||||
return actions
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// console.log(this.col)
|
||||
},
|
||||
methods: {
|
||||
checkBool(item, attr, defaults) {
|
||||
if (!item) {
|
||||
return false
|
||||
}
|
||||
let ok = item[attr]
|
||||
if (ok && typeof ok === 'function') {
|
||||
ok = ok(this.row, this.cellValue)
|
||||
} else if (ok == null) {
|
||||
ok = defaults === undefined ? true : defaults
|
||||
}
|
||||
return ok
|
||||
},
|
||||
cleanCallback(item) {
|
||||
const callback = item.callback
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
return () => { return callback.bind(this)(attrs) }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-button ref="deleteButton" size="mini" type="danger" :disabled="iDisabled" @click="onDelete(col, row, cellValue, reload)">
|
||||
<el-button ref="deleteButton" size="mini" type="danger" :disabled="canDelete" @click="onDelete(col, row, cellValue, reload)">
|
||||
<i class="fa fa-minus" />
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -11,19 +11,18 @@ export default {
|
||||
name: 'DeleteActionFormatter',
|
||||
extends: BaseFormatter,
|
||||
computed: {
|
||||
iDisabled() {
|
||||
// 禁用
|
||||
return (this.disabled() || this.$store.getters.currentOrgIsRoot)
|
||||
canDelete() {
|
||||
return this.iCanDelete()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
defaultOnDelete(col, row, cellValue, reload) {
|
||||
const url = col.deleteUrl + cellValue
|
||||
this.$axios.delete(url).then(res => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
reload()
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.deleteErrorMsg') + ' ' + error)
|
||||
this.$message.error(this.$t('common.deleteErrorMsg' + ' ' + error))
|
||||
})
|
||||
},
|
||||
onDelete(col, row, cellValue, reload) {
|
||||
@@ -33,7 +32,7 @@ export default {
|
||||
this.defaultOnDelete(col, row, cellValue, reload)
|
||||
}
|
||||
},
|
||||
disabled() {
|
||||
iCanDelete() {
|
||||
if (this.col.objects === 'all') {
|
||||
return false
|
||||
}
|
||||
@@ -25,16 +25,8 @@ export default {
|
||||
data() {
|
||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
return {
|
||||
formatterArgs: formatterArgs
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iTitle() {
|
||||
return this.formatterArgs.getTitle({
|
||||
col: this.col,
|
||||
row: this.row,
|
||||
cellValue: this.cellValue
|
||||
})
|
||||
formatterArgs: formatterArgs,
|
||||
iTitle: formatterArgs.getTitle({ col: this.col, row: this.row, cellValue: this.cellValue })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -71,11 +63,6 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.detail {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span :class="cls"> {{ value }}</span>
|
||||
<span>{{ display }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -19,27 +19,19 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
display: this.getValue()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
const displayKey = this.formatterArgs.displayKey
|
||||
methods: {
|
||||
getValue() {
|
||||
const formatterArgs = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
const displayKey = formatterArgs.displayKey
|
||||
let value = this.row[displayKey]
|
||||
if (value === undefined) {
|
||||
value = this.row[this.col.prop]
|
||||
}
|
||||
return value
|
||||
},
|
||||
cls() {
|
||||
const classChoices = this.formatterArgs?.classChoices
|
||||
if (!classChoices) {
|
||||
return ''
|
||||
}
|
||||
return classChoices[this.cellValue]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
116
src/components/ListTable/formatters/LoadingActionsFormatter.vue
Normal file
116
src/components/ListTable/formatters/LoadingActionsFormatter.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<ActionsGroup v-loading="loadStatus" :size="'mini'" :actions="actions" :more-actions="moreActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionsGroup from '@/components/ActionsGroup/index'
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'LoadingActionsFormatter',
|
||||
components: { ActionsGroup },
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
hasUpdate: true, // can set function(row, value)
|
||||
canUpdate: true, // can set function(row, value)
|
||||
hasDelete: true, // can set function(row, value)
|
||||
canDelete: true,
|
||||
updateRoute: this.$route.name.replace('List', 'Update'),
|
||||
extraActions: [] // format see defaultActions
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const colActions = Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
const defaultActions = [
|
||||
{
|
||||
name: 'update',
|
||||
title: this.$t('common.Update'),
|
||||
type: 'primary',
|
||||
has: colActions.hasUpdate,
|
||||
can: colActions.canUpdate,
|
||||
callback: colActions.onUpdate
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'danger',
|
||||
has: colActions.hasDelete,
|
||||
can: colActions.canDelete,
|
||||
callback: colActions.onDelete
|
||||
}
|
||||
]
|
||||
return {
|
||||
colActions: colActions,
|
||||
defaultActions: defaultActions,
|
||||
extraActions: colActions.extraActions
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cleanedActions() {
|
||||
let actions = [...this.defaultActions, ...this.extraActions]
|
||||
actions = _.cloneDeep(actions)
|
||||
actions = actions.map((v) => {
|
||||
v.has = this.cleanBoolean(v, 'has')
|
||||
v.can = this.cleanBoolean(v, 'can')
|
||||
v.fa = this.cleanFa(v, 'fa')
|
||||
v.callback = this.cleanCallback(v)
|
||||
return v
|
||||
})
|
||||
actions = actions.filter((v) => v.has)
|
||||
return actions
|
||||
},
|
||||
actions() {
|
||||
if (this.cleanedActions.length <= 2) {
|
||||
return this.cleanedActions
|
||||
}
|
||||
return this.cleanedActions.slice(0, 1)
|
||||
},
|
||||
moreActions() {
|
||||
if (this.cleanedActions.length <= 2) {
|
||||
return []
|
||||
}
|
||||
return this.cleanedActions.slice(1, this.cleanedActions.length)
|
||||
},
|
||||
loadStatus() {
|
||||
return this.col.formatterArgs.loading
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cleanBoolean(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? true : ok
|
||||
}
|
||||
return ok(this.row, this.cellValue)
|
||||
},
|
||||
cleanCallback(item) {
|
||||
const callback = item.callback
|
||||
const attrs = {
|
||||
reload: this.reload,
|
||||
row: this.row,
|
||||
col: this.col,
|
||||
cellValue: this.cellValue,
|
||||
tableData: this.tableData
|
||||
}
|
||||
return () => { return callback.bind(this)(attrs) }
|
||||
},
|
||||
cleanFa(item, attr) {
|
||||
const ok = item[attr]
|
||||
if (typeof ok !== 'function') {
|
||||
return ok === undefined ? false : ok
|
||||
}
|
||||
return ok(this.row, this.cellValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,42 +1,39 @@
|
||||
import DetailFormatter from './DetailFormatter'
|
||||
import ArrayFormatter from './ArrayFormatter'
|
||||
import DisplayFormatter from './DisplayFormatter'
|
||||
import ChoicesFormatter from './ChoicesFormatter'
|
||||
import BooleanFormatter from './ChoicesFormatter'
|
||||
import ActionsFormatter from './ActionsFormatter'
|
||||
import CustomActionsFormatter from './CustomActionsFormatter'
|
||||
import DeleteActionFormatter from './DeleteActionFormatter'
|
||||
import DateFormatter from './DateFormatter'
|
||||
import SystemUserFormatter from './GrantedSystemUsersShowFormatter'
|
||||
import ShowKeyFormatter from '@/components/TableFormatters/ShowKeyFormatter'
|
||||
import ShowKeyFormatter from '@/components/ListTable/formatters/ShowKeyFormatter'
|
||||
import DialogDetailFormatter from './DialogDetailFormatter'
|
||||
import EditableInputFormatter from './EditableInputFormatter'
|
||||
import StatusFormatter from './StatusFormatter'
|
||||
import LoadingActionsFormatter from './LoadingActionsFormatter'
|
||||
|
||||
export default {
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ChoicesFormatter,
|
||||
BooleanFormatter,
|
||||
ActionsFormatter,
|
||||
CustomActionsFormatter,
|
||||
DeleteActionFormatter,
|
||||
DateFormatter,
|
||||
SystemUserFormatter,
|
||||
ShowKeyFormatter,
|
||||
DialogDetailFormatter,
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter
|
||||
LoadingActionsFormatter
|
||||
}
|
||||
|
||||
export {
|
||||
DetailFormatter,
|
||||
DisplayFormatter,
|
||||
ChoicesFormatter,
|
||||
BooleanFormatter,
|
||||
ActionsFormatter,
|
||||
CustomActionsFormatter,
|
||||
DeleteActionFormatter,
|
||||
DateFormatter,
|
||||
SystemUserFormatter,
|
||||
ShowKeyFormatter,
|
||||
DialogDetailFormatter,
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter
|
||||
LoadingActionsFormatter
|
||||
}
|
||||
@@ -1,21 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<TableAction
|
||||
:table-url="tableUrl"
|
||||
:search-table="search"
|
||||
:date-pick="handleDateChange"
|
||||
:selected-rows="selectedRows"
|
||||
:reload-table="reloadTable"
|
||||
v-bind="headerActions"
|
||||
/>
|
||||
<TableAction :table-url="iTableConfig.url" :search-table="search" :date-pick="handleDateChange" v-bind="headerActions" :selected-rows="selectedRows" :reload-table="reloadTable" />
|
||||
<IBox class="table-content">
|
||||
<AutoDataTable
|
||||
ref="dataTable"
|
||||
:filter-table="filter"
|
||||
:config="iTableConfig"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<AutoDataTable ref="dataTable" :filter-table="filter" :config="iTableConfig" @selection-change="handleSelectionChange" v-on="$listeners" />
|
||||
</IBox>
|
||||
</div>
|
||||
</template>
|
||||
@@ -55,17 +42,26 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
dataTable() {
|
||||
return this.$refs.dataTable.$refs.dataTable
|
||||
},
|
||||
hasCreateAction() {
|
||||
const hasLeftAction = this.headerActions.hasLeftActions
|
||||
if (hasLeftAction === false) {
|
||||
return false
|
||||
}
|
||||
const hasCreate = this.headerActions.hasCreate
|
||||
if (hasCreate === false) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
iTableConfig() {
|
||||
const config = deepmerge(this.tableConfig, { extraQuery: this.extraQuery })
|
||||
this.$log.debug('Header actions', this.headerActions)
|
||||
this.$log.debug('ListTable: iTableConfig change', config)
|
||||
return config
|
||||
},
|
||||
tableUrl() {
|
||||
return this.iTableConfig.url
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -90,11 +86,9 @@ export default {
|
||||
this.dataTable.getList()
|
||||
},
|
||||
search(attrs) {
|
||||
this.$emit('TagSearch', attrs)
|
||||
return this.dataTable.search(attrs, true)
|
||||
},
|
||||
filter(attrs) {
|
||||
this.$emit('TagFilter', attrs)
|
||||
this.$refs.dataTable.$refs.dataTable.search(attrs, true)
|
||||
},
|
||||
handleDateChange(attrs) {
|
||||
@@ -108,7 +102,6 @@ export default {
|
||||
date_from: attrs[0].toISOString(),
|
||||
date_to: attrs[1].toISOString()
|
||||
}
|
||||
this.$emit('TagDateChange', attrs)
|
||||
return this.dataTable.searchDate(query)
|
||||
},
|
||||
toggleRowSelection(row, isSelected) {
|
||||
@@ -129,6 +122,15 @@ export default {
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
|
||||
/*background: white;*/
|
||||
/*}*/
|
||||
|
||||
/*& >>> .el-table th, .el-table tr {*/
|
||||
/*background-color: red;*/
|
||||
/*!*background-color: #FAFAFA;*!*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:title="$t('common.MFAVerify')"
|
||||
:width="'50'"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:visible.sync="visible"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAToken" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="verifyMFA">
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
name: 'MFAVerifyDialog',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAToken: '',
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$emit('MFAVerifyCancel', true)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$axios.get('/api/v1/authentication/otp/verify/', { disableFlashErrorMsg: true }).then(() => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
}).catch(err => {
|
||||
this.$log.debug('Verify otp code error: ', err)
|
||||
this.visible = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
verifyMFA() {
|
||||
if (this.MFAToken.length !== 6) {
|
||||
return this.$message.error(this.$tc('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAToken
|
||||
}
|
||||
).then(res => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Switcher from '../FormFields/Swicher'
|
||||
import Switcher from '../Swicher'
|
||||
export default {
|
||||
name: 'ActionItem',
|
||||
components: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<IBox :fa="fa" :title="title" v-bind="$attrs">
|
||||
<IBox fa="fa-edit" :title="title" v-bind="$attrs">
|
||||
<div v-for="action of actions" :key="action.title" class="quick-actions">
|
||||
<table>
|
||||
<ActionItem v-if="action.has === undefined || action.has" :action="action" />
|
||||
@@ -20,10 +20,6 @@ export default {
|
||||
ActionItem
|
||||
},
|
||||
props: {
|
||||
fa: {
|
||||
type: String,
|
||||
default: () => 'fa-edit'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default() {
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
<template>
|
||||
<IBox :fa="icon" :type="type" :title="title" v-bind="$attrs">
|
||||
<table style="width: 100%;table-layout:fixed;" class="CardTable">
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<Select2 ref="select2" v-model="select2.value" :disabled="iDisabled" v-bind="select2" />
|
||||
<Select2 ref="select2" v-model="select2.value" v-bind="select2" />
|
||||
</td>
|
||||
</tr>
|
||||
<slot />
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<el-button :type="type" size="small" :loading="submitLoading" :disabled="iDisabled" @click="addObjects">
|
||||
{{ $t('common.Add') }}
|
||||
</el-button>
|
||||
<el-button :type="type" size="small" :loading="submitLoading" @click="addObjects">{{ $t('common.Add') }}</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-if="showHasObjects">
|
||||
<tr v-for="obj of iHasObjects" :key="obj.value" class="item">
|
||||
<td style="width: 100%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
|
||||
<el-tooltip style="margin: 4px;" effect="dark" :content="obj.label" placement="left">
|
||||
<b>{{ obj.label }}</b>
|
||||
</el-tooltip>
|
||||
</td>
|
||||
<tr v-for="obj of iHasObjects" :key="obj.value" style="width: 100%" class="item">
|
||||
<td><b>{{ obj.label }}</b></td>
|
||||
<td>
|
||||
<el-button size="mini" :disabled="iDisabled" type="danger" style="float: right" @click="removeObject(obj)">
|
||||
<el-button size="mini" type="danger" style="float: right" @click="removeObject(obj)">
|
||||
<i class="fa fa-minus" />
|
||||
</el-button>
|
||||
</td>
|
||||
@@ -30,7 +24,7 @@
|
||||
</template>
|
||||
<tr v-if="params.hasMore && showHasMore" class="item">
|
||||
<td colspan="2">
|
||||
<el-button :type="type" :disabled="iDisabled" size="small" style="width: 100%" @click="loadMore">
|
||||
<el-button :type="type" size="small" style="width: 100%" @click="loadMore">
|
||||
<i class="fa fa-arrow-down" />
|
||||
{{ $t('common.More') }}
|
||||
</el-button>
|
||||
@@ -41,10 +35,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Select2 from '../FormFields/Select2'
|
||||
import Select2 from '../Select2'
|
||||
import IBox from '../IBox'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'RelationCard',
|
||||
components: {
|
||||
@@ -90,10 +83,6 @@ export default {
|
||||
type: [Array, Number, String],
|
||||
default: () => []
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, Function],
|
||||
default: null
|
||||
},
|
||||
showHasMore: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@@ -145,13 +134,11 @@ export default {
|
||||
ajax: this.objectsAjax,
|
||||
options: this.objects,
|
||||
value: this.value,
|
||||
disabled: this.disabled,
|
||||
disabledValues: []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot']),
|
||||
iAjax() {
|
||||
return this.$refs.select2.iAjax
|
||||
},
|
||||
@@ -160,12 +147,6 @@ export default {
|
||||
},
|
||||
hasObjectLeftLength() {
|
||||
return this.totalHasObjectsLength - this.iHasObjects.length
|
||||
},
|
||||
iDisabled() {
|
||||
if (this.disabled !== null) {
|
||||
return this.disabled
|
||||
}
|
||||
return this.currentOrgIsRoot
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
<el-select
|
||||
ref="select"
|
||||
v-model="iValue"
|
||||
v-loading="!initialized"
|
||||
v-loadmore="loadMore"
|
||||
:options="iOptions"
|
||||
:remote="remote"
|
||||
:remote-method="filterOptions"
|
||||
:multiple="multiple"
|
||||
:clearable="clearable"
|
||||
filterable
|
||||
:clearable="clearable"
|
||||
popper-append-to-body
|
||||
class="select2"
|
||||
v-bind="$attrs"
|
||||
@@ -98,6 +97,7 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
initialized: false,
|
||||
iValue: this.value ? this.value : [],
|
||||
defaultParams: _.cloneDeep(defaultParams),
|
||||
params: _.cloneDeep(defaultParams),
|
||||
iOptions: this.options || [],
|
||||
@@ -112,18 +112,6 @@ export default {
|
||||
optionsValues() {
|
||||
return this.iOptions.map((v) => v.value)
|
||||
},
|
||||
iValue: {
|
||||
set(val) {
|
||||
const noValue = !this.value || this.value.length === 0
|
||||
if (noValue && !this.initialized) {
|
||||
return
|
||||
}
|
||||
this.$emit('input', val)
|
||||
},
|
||||
get() {
|
||||
return this.value
|
||||
}
|
||||
},
|
||||
iAjax() {
|
||||
const defaultPageSize = 10
|
||||
const defaultMakeParams = (params) => {
|
||||
@@ -173,14 +161,11 @@ export default {
|
||||
this.iValue = iNew
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
mounted() {
|
||||
// this.$log.debug('Select2 url is: ', this.iAjax.url)
|
||||
if (!this.initialized) {
|
||||
await this.initialSelect()
|
||||
setTimeout(() => {
|
||||
this.iValue = this.value
|
||||
this.initialized = true
|
||||
})
|
||||
this.initialSelect()
|
||||
this.initialized = true
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
// 因为elform存在问题,这个来清楚验证
|
||||
@@ -258,13 +243,9 @@ export default {
|
||||
// this.$log.debug('Select ajax config', this.iAjax)
|
||||
if (this.iAjax.url) {
|
||||
if (this.value && this.value.length !== 0) {
|
||||
this.$log.debug('Start init select2 value, ', this.value)
|
||||
let value = this.value
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value]
|
||||
}
|
||||
const data = await createSourceIdCache(value)
|
||||
this.params.spm = data['spm']
|
||||
this.$log.debug('Start init select2 value')
|
||||
const data = await createSourceIdCache(this.value)
|
||||
this.params.spm = data.spm
|
||||
await this.getInitialOptions()
|
||||
}
|
||||
await this.getOptions()
|
||||
@@ -2,11 +2,15 @@
|
||||
<el-card shadow="never">
|
||||
<div slot="header" class="summary-header">
|
||||
<span class="header-title">{{ title }}</span>
|
||||
<span class="pull-right right-side">
|
||||
<slot name="header-right">
|
||||
<el-tag :type="rightSideLabel.type || 'success'" effect="dark" size="mini">{{ rightSideLabel.title }}</el-tag>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
<slot>
|
||||
<h1 class="no-margins">
|
||||
<span v-if="body.disabled" class="disabled-link">{{ body.count }}</span>
|
||||
<router-link v-else :to="body.route">
|
||||
<router-link :to="body.route">
|
||||
<span>{{ body.count }}</span>
|
||||
</router-link>
|
||||
</h1>
|
||||
@@ -70,8 +74,4 @@ export default {
|
||||
.no-margins {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.disabled-link {
|
||||
color: #428bca;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<span>{{ cellValue.toString() }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'ArrayFormatter',
|
||||
extends: BaseFormatter
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<i :class="'fa ' + iconClass" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'BooleanFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
iconChoices: {
|
||||
true: 'fa-check text-primary',
|
||||
false: 'fa-times text-danger'
|
||||
},
|
||||
showFalse: true,
|
||||
typeChange(val) {
|
||||
return !!val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
const key = this.formatterArgs.typeChange(this.cellValue)
|
||||
if (!key && !this.formatterArgs.showFalse) {
|
||||
return ''
|
||||
}
|
||||
return this.formatterArgs.iconChoices[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,82 +0,0 @@
|
||||
<template>
|
||||
<el-tooltip v-if="shown" :disabled="!formatterArgs.hasTips" placement="bottom" effect="dark">
|
||||
<div slot="content" v-html="tips" />
|
||||
<span :class="classes">
|
||||
<i v-if="formatterArgs.useIcon" :class="'fa ' + icon" />
|
||||
<span v-if="formatterArgs.useText">{{ text }}</span>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
export default {
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
iconChoices: {
|
||||
true: 'fa-check',
|
||||
false: 'fa-times'
|
||||
},
|
||||
classChoices: {
|
||||
true: 'text-primary',
|
||||
false: 'text-danger'
|
||||
},
|
||||
textChoices: {
|
||||
true: this.$t('common.Yes'),
|
||||
false: this.$t('common.No')
|
||||
},
|
||||
getKey({ row, cellValue }) {
|
||||
return cellValue
|
||||
},
|
||||
hasTips: false,
|
||||
useIcon: true,
|
||||
useText: false,
|
||||
showFalse: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
return cellValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
key() {
|
||||
return this.formatterArgs.getKey(
|
||||
{ row: this.row, cellValue: this.cellValue }
|
||||
)
|
||||
},
|
||||
icon() {
|
||||
return this.formatterArgs.iconChoices[this.key]
|
||||
},
|
||||
classes() {
|
||||
return this.formatterArgs.classChoices[this.key]
|
||||
},
|
||||
text() {
|
||||
return this.formatterArgs.textChoices[this.key]
|
||||
},
|
||||
tips() {
|
||||
return this.formatterArgs.getTips({ cellValue: this.cellValue, row: this.row })
|
||||
},
|
||||
shown() {
|
||||
if (!this.formatterArgs.showFalse && !this.key) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<div style="width: 100%;min-height: 20px" @click.stop="editCell">
|
||||
<el-input
|
||||
v-if="inEditMode"
|
||||
v-model="value"
|
||||
size="mini"
|
||||
class="editInput"
|
||||
@keyup.enter.native="onInputEnter"
|
||||
@blur="onInputEnter"
|
||||
/>
|
||||
<template v-else>
|
||||
<span>{{ cellValue }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
|
||||
export default {
|
||||
name: 'EditableInputFormatter',
|
||||
components: {
|
||||
},
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
trigger: 'click',
|
||||
onEnter: ({ row, col, oldValue, newValue }) => {
|
||||
const prop = col.prop
|
||||
this.$log.debug(`Set value ${oldValue} => ${newValue}`)
|
||||
this.$set(row, prop, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const valueIsString = typeof this.cellValue === 'string'
|
||||
const jsonValue = this.cellValue ? JSON.stringify(this.cellValue) : ''
|
||||
return {
|
||||
inEditMode: false,
|
||||
value: valueIsString ? this.cellValue || '' : jsonValue,
|
||||
valueIsString: valueIsString,
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editCell() {
|
||||
this.inEditMode = true
|
||||
},
|
||||
onInputEnter() {
|
||||
let validValue = this.value
|
||||
try {
|
||||
validValue = JSON.parse(validValue)
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
this.formatterArgs.onEnter({
|
||||
row: this.row, col: this.col,
|
||||
oldValue: this.cellValue,
|
||||
newValue: validValue
|
||||
})
|
||||
this.inEditMode = false
|
||||
},
|
||||
cancelEdit() {
|
||||
this.inEditMode = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.editInput >>> .el-input__inner {
|
||||
padding: 2px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.editInput {
|
||||
padding: -6px;
|
||||
}
|
||||
</style>
|
||||
@@ -5,10 +5,8 @@
|
||||
<component
|
||||
:is="component"
|
||||
ref="AutoDataZTree"
|
||||
:key="componentTreeKey"
|
||||
:setting="treeSetting"
|
||||
class="auto-data-ztree"
|
||||
v-on="$listeners"
|
||||
@urlChange="handleUrlChange"
|
||||
>
|
||||
<div slot="rMenu" slot-scope="{data}">
|
||||
@@ -24,7 +22,7 @@
|
||||
</div>
|
||||
<div class="transition-box" style="width: calc(100% - 17px);">
|
||||
<slot name="table">
|
||||
<ListTable ref="ListTable" :key="componentKey" :table-config="iTableConfig" :header-actions="headerActions" v-on="$listeners" />
|
||||
<ListTable ref="ListTable" :key="componentKey" :table-config="iTableConfig" :header-actions="headerActions" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,40 +63,25 @@ export default {
|
||||
return {
|
||||
iTableConfig: this.tableConfig,
|
||||
iShowTree: this.showTree,
|
||||
componentKey: 0,
|
||||
componentTreeKey: 0
|
||||
componentKey: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
treeConfig: {
|
||||
handler(val) {
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleUrlChange(url) {
|
||||
this.$set(this.iTableConfig, 'url', url)
|
||||
this.$emit('urlChange', url)
|
||||
handleUrlChange(_url) {
|
||||
this.$set(this.iTableConfig, 'url', _url)
|
||||
this.$emit('urlChange', _url)
|
||||
this.forceRerender()
|
||||
},
|
||||
forceRerender() {
|
||||
this.componentKey += 1
|
||||
},
|
||||
forceRerenderTree() {
|
||||
this.componentTreeKey += 1
|
||||
},
|
||||
hideRMenu() {
|
||||
this.$refs.AutoDataZTree.hideRMenu()
|
||||
},
|
||||
getSelectedNodes: function() {
|
||||
return this.$refs.AutoDataZTree.getSelectedNodes()
|
||||
},
|
||||
getNodes: function() {
|
||||
return this.$refs.AutoDataZTree.getNodes()
|
||||
},
|
||||
selectNode: function(node) {
|
||||
return this.$refs.AutoDataZTree.selectNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// value: {
|
||||
// type: String,
|
||||
// default: () => ''
|
||||
// },
|
||||
tip: {
|
||||
value: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
toFormat: {
|
||||
tip: {
|
||||
type: String,
|
||||
default: () => 'string'
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -29,11 +26,7 @@ export default {
|
||||
const vm = this
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
let result = this.result
|
||||
if (vm.toFormat === 'object') {
|
||||
result = JSON.parse(result)
|
||||
}
|
||||
vm.$emit('input', result)
|
||||
vm.$emit('input', this.result)
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
@@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PasswordInput from './PasswordInput'
|
||||
import PasswordInput from '../PasswordInput'
|
||||
import { mapGetters } from 'vuex'
|
||||
import store from '@/store'
|
||||
import i18n from '@/i18n/i18n'
|
||||
@@ -22,12 +22,8 @@ export default {
|
||||
}
|
||||
},
|
||||
rules(item) {
|
||||
let userIsOrgAdmin = item.el.userIsOrgAdmin
|
||||
// undefined 个人信息更新或用户更改密码页面,使用当前用户;否则使用更新用户表单中传递的值
|
||||
userIsOrgAdmin = userIsOrgAdmin === undefined ? store.getters.currentUserIsAdmin : userIsOrgAdmin
|
||||
|
||||
const passwordRule = store.getters.publicSettings.PASSWORD_RULE
|
||||
const validatePassword = function(rule, value, callback) {
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
return callback()
|
||||
}
|
||||
@@ -50,10 +46,7 @@ export default {
|
||||
return callback(new Error(msg))
|
||||
}
|
||||
}
|
||||
let secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
if (userIsOrgAdmin) {
|
||||
secureLength = passwordRule ? passwordRule.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH : 7
|
||||
}
|
||||
const secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
if (value.length < secureLength) {
|
||||
return callback(new Error(i18n.t('common.password.MIN_LENGTH_ERROR', [secureLength])))
|
||||
}
|
||||
@@ -66,12 +59,17 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
attrs: {
|
||||
secureLength: 7
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['publicSettings'])
|
||||
},
|
||||
created() {
|
||||
const passwordRule = this.publicSettings.PASSWORD_RULE || {}
|
||||
this.attrs.secureLength = passwordRule ? passwordRule.SECURITY_PASSWORD_MIN_LENGTH : 7
|
||||
},
|
||||
methods: {
|
||||
handleInput(value) {
|
||||
this.$emit('input', value)
|
||||
@@ -12,19 +12,14 @@ export { default as FormGroupHeader } from './FormGroupHeader'
|
||||
export { default as Hamburger } from './Hamburger'
|
||||
export { default as ListTable } from './ListTable'
|
||||
export { default as RelationCard } from './RelationCard'
|
||||
export { default as Select2 } from './FormFields/Select2'
|
||||
export { default as UploadKey } from './FormFields/UploadKey.vue'
|
||||
export { default as Select2 } from './Select2'
|
||||
export { default as AssetSelect } from './AssetSelect'
|
||||
export { default as SvgIcon } from './SvgIcon'
|
||||
export { default as TreeTable } from './TreeTable'
|
||||
export { default as IBox } from './IBox'
|
||||
export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './FormFields/Swicher'
|
||||
export { default as Switcher } from './Swicher'
|
||||
export { default as SummaryCard } from './SummaryCard'
|
||||
export { default as UploadField } from './FormFields/UploadField'
|
||||
export { default as AccountListTable } from './AccountListTable/index'
|
||||
export { default as AppAccountListTable } from './AppAccountListTable'
|
||||
export { default as UploadField } from './UploadField'
|
||||
export { default as AssetUserTable } from './AssetUserTable'
|
||||
export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as MFAVerifyDialog } from './MFAVerifyDialog'
|
||||
export { default as Announcement } from './Announcement'
|
||||
export { default as CronTab } from './CronTab'
|
||||
|
||||
@@ -1,44 +1,7 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "资产账号列表,点击左侧资产进行查看",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "应用账号列表,点击左侧应用进行查看",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看"
|
||||
},
|
||||
"acl": {
|
||||
"name": "名称",
|
||||
"username": "用户名",
|
||||
"ip_group": "IP 组",
|
||||
"ip_group_help_text": "格式为逗号分隔的字符串, * 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64",
|
||||
"action": "动作",
|
||||
"Rules": "规则",
|
||||
"Action": "动作",
|
||||
"priority": "优先级",
|
||||
"date_created": "创建时间",
|
||||
"created_by": "创建者",
|
||||
"asset": "资产信息",
|
||||
"users":"用户",
|
||||
"system_user": "系统用户",
|
||||
"username_group":"用户名",
|
||||
"hostname_group":"资产名",
|
||||
"asset_ip_group": "资产IP",
|
||||
"system_users_name_group": "系统用户名称",
|
||||
"system_users_protocol_group": "系统用户协议",
|
||||
"system_users_username_group": "系统用户名",
|
||||
"apply_login_asset": "申请登录资产",
|
||||
"apply_login_system_user": "申请登录系统用户",
|
||||
"apply_login_user": "申请登录用户",
|
||||
"login_confirm_user": "登录复核 受理人",
|
||||
"RuleDetail": "规则详情"
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
"updateAccountMsg": "请更新系统用户的账号信息",
|
||||
"associateApplication": "关联应用",
|
||||
"RemoteApp": "远程应用",
|
||||
"Database": "数据库",
|
||||
"Cloud": "云",
|
||||
"App": "应用",
|
||||
"applicationsType": {
|
||||
"chrome": "Chrome",
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
@@ -90,15 +53,11 @@
|
||||
"DBInfo": "数据库信息"
|
||||
},
|
||||
"assets": {
|
||||
"AppList": "应用列表",
|
||||
"AssociateSystemUsers": "关联系统用户",
|
||||
"AssociateAssets": "关联资产",
|
||||
"AssociateNodes": "关联节点",
|
||||
"Action": "动作",
|
||||
"ActiveSelected": "激活所选",
|
||||
"AdminUser": "特权用户",
|
||||
"AdminUser": "管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserListHelpMessage": "<b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, 如 root 或 拥有 `NOPASSWD: ALL` sudo 权限的用户。 JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。",
|
||||
"AdminUserListHelpMessage": "管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。\n",
|
||||
"Asset": "资产",
|
||||
"HardwareInfo": "硬件信息",
|
||||
"AssetDetail": "资产详情",
|
||||
@@ -109,13 +68,11 @@
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
"SshPort": "SSH 端口",
|
||||
"AssetNumber": "资产编号",
|
||||
"AssetUserList": "账号列表",
|
||||
"AssetUserList": "资产用户列表",
|
||||
"Assets": "资产",
|
||||
"Auth": "认证",
|
||||
"AccountList": "账号列表",
|
||||
"PermUserList": "授权用户",
|
||||
"ViewPerm": "查看授权",
|
||||
"AutoGenerateKey": "自动生成",
|
||||
"AutoGenerateKey": "自动生成密钥",
|
||||
"AutoPush": "自动推送",
|
||||
"BasePlatform": "基础平台",
|
||||
"Basic": "基本",
|
||||
@@ -128,7 +85,6 @@
|
||||
"CommandFilterRules": "命令过滤器规则",
|
||||
"Comment": "备注",
|
||||
"Cpu": "CPU",
|
||||
"CommonUser": "普通用户",
|
||||
"CreatedBy": "创建者",
|
||||
"Database": "数据库",
|
||||
"DateJoined": "创建日期",
|
||||
@@ -151,7 +107,7 @@
|
||||
"LoginModeHelpMessage": "如果选择手动登录模式,用户名和密码可以不填写",
|
||||
"LoginModel": "登录模式",
|
||||
"Memory": "内存",
|
||||
"Meta": "元数据",
|
||||
"Meta": "源数据",
|
||||
"Model": "型号",
|
||||
"Name": "名称",
|
||||
"NoInputCommand": "未输入命令",
|
||||
@@ -170,11 +126,9 @@
|
||||
"PriorityHelpMessage": "1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为默认登录用户",
|
||||
"Protocol": "协议",
|
||||
"Protocols": "协议组",
|
||||
"LoginOption": "登录选项",
|
||||
"PublicIp": "公网IP",
|
||||
"Push": "推送",
|
||||
"PushSystemUserNow": "推送系统用户",
|
||||
"PushAllSystemUsersToAsset": "推送所有系统用户到资产",
|
||||
"QuickUpdate": "快速更新",
|
||||
"Reachable": "可连接",
|
||||
"Unreachable": "不可连接",
|
||||
@@ -194,17 +148,15 @@
|
||||
"PasswordHelpMessage": "密码或密钥密码",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserListHelpMessage": "<b>系统用户</b> 是JumpServer 登录资产时使用的账号,如 root `ssh root@host`,而不是使用该用户名登录资产(ssh admin@host)`;<br><b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等;</br><b>普通用户</b> 可以在资产上预先存在,也可以由 特权用户 来自动创建。",
|
||||
"DynamicUsername": "动态用户名",
|
||||
"SystemUserListHelpMessage": "系统用户是 JumpServer 跳转登录资产时使用的用户,可以理解为登录资产用户,如 web,sa,dba(`ssh web@some-host`),而不是使用某个用户的用户名跳转登录服务器(`ssh xiaoming@some-host`); 简单来说是用户使用自己的用户名登录 JumpServer,JumpServer 使用系统用户登录资产。 系统用户创建时,如果选择了自动推送,JumpServer 会使用 Ansible 自动推送系统用户到资产中,如果资产(交换机)不支持 Ansible,请手动填写账号密码。\n",
|
||||
"SystemUsers": "系统用户",
|
||||
"Test": "测试",
|
||||
"TestAssetsConnective": "测试资产可连接性",
|
||||
"TestAllSystemUsersConnective": "测试所有系统用户可连接性",
|
||||
"TestConnection": "测试连接",
|
||||
"Type": "类型",
|
||||
"UnselectedAssets": "未选择资产或所选择的资产不支持SSH协议连接",
|
||||
"UnselectedAssets": "未选择资产",
|
||||
"UnselectedNodes": "未选择节点",
|
||||
"UpdateAssetUserToken": "更新账号认证信息",
|
||||
"UpdateAssetUserToken": "更新资产用户认证信息",
|
||||
"Username": "用户名",
|
||||
"UsernameHelpMessage": "用户名是动态的,登录资产时使用当前用户的用户名登录",
|
||||
"Value": "值",
|
||||
@@ -212,44 +164,26 @@
|
||||
"Version": "版本",
|
||||
"command_filter_list": "命令过滤器列表",
|
||||
"date_joined": "创建日期",
|
||||
"sshKeyFingerprint": "SSH 指纹",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
"LinuxUserAffiliateGroup": "用户附属组",
|
||||
"ipDomain": "IP(域名)",
|
||||
"HostProtocol": "主机协议",
|
||||
"DatabaseProtocol": "数据库协议",
|
||||
"OtherProtocol": "其它协议",
|
||||
"PasswordOrToken": "密码 / 令牌"
|
||||
"ipDomain": "IP(域名)"
|
||||
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "主机",
|
||||
"RunUser": "运行用户",
|
||||
"User": "用户",
|
||||
"Username": "用户名",
|
||||
"View": "查看",
|
||||
"SystemUserName": "系统用户名"
|
||||
"View": "查看"
|
||||
},
|
||||
"auth": {
|
||||
"LoginRequiredMsg": "账号已退出,请重新登录",
|
||||
"ReLogin": "重新登录"
|
||||
},
|
||||
"common": {
|
||||
"ViewMore": "查看更多",
|
||||
"Announcement": "公告",
|
||||
"Logging": "日志记录",
|
||||
"Database": "数据库记录",
|
||||
"Params": "参数",
|
||||
"BasicInfo": "基本信息",
|
||||
"DateUpdated": "更新日期",
|
||||
"ApprovaLevel": "审批信息",
|
||||
"MFAVerify": "验证 MFA",
|
||||
"ViewSecret": "查看密码",
|
||||
"ConnectWebSocketError": "连接 WebSocket 失败",
|
||||
"Action": "动作",
|
||||
"RequestTickets": "申请工单",
|
||||
"Actions": "操作",
|
||||
@@ -264,21 +198,13 @@
|
||||
"UpdateAssetDetail": "配置更多信息",
|
||||
"AddSuccessMsg": "添加成功",
|
||||
"Auth": "认证",
|
||||
"bind": "绑定",
|
||||
"unbind": "解绑",
|
||||
"PushSelected":"推送所选",
|
||||
"PushSelectedSystemUsersToAsset": "推送所选系统用户到资产",
|
||||
"TestSelected": "测试所选",
|
||||
"TestSelectedSystemUsersConnective": "测试所选系统用户可连接性",
|
||||
"BadRequestErrorMsg": "请求错误,请检查填写内容",
|
||||
"BadRoleErrorMsg": "请求错误,无该操作权限",
|
||||
"BadConflictErrorMsg": "正在刷新中,请稍后再试",
|
||||
"Basic": "基本",
|
||||
"PleaseAgreeToTheTerms": "请同意条款",
|
||||
"OpenId": "OpenId设置",
|
||||
"Radius": "Radius设置",
|
||||
"Cas": "Cas设置",
|
||||
"other": "其它设置",
|
||||
"BasicInfo": "基本信息",
|
||||
"ApplyInfo": "申请信息",
|
||||
"Cancel": "取消",
|
||||
"Close": "关闭",
|
||||
@@ -303,16 +229,10 @@
|
||||
"EnterForSearch": "按回车进行搜索",
|
||||
"Export": "导出",
|
||||
"Import": "导入",
|
||||
"ContinueImport": "继续导入",
|
||||
"Continue": "继续",
|
||||
"Stop": "停止",
|
||||
"Finished": "完成",
|
||||
"Refresh": "刷新",
|
||||
"Info": "提示",
|
||||
"MFAConfirm": "MFA 认证",
|
||||
"MFARequireForSecurity": "为了安全请输入MFA",
|
||||
"PasswordConfirm": "密码认证",
|
||||
"PasswordRequireForSecurity": "为了安全请输入密码",
|
||||
"Members": "成员",
|
||||
"More": "更多",
|
||||
"Message": "消息",
|
||||
@@ -346,7 +266,6 @@
|
||||
"View": "查看",
|
||||
"Yes": "是",
|
||||
"action": "动作",
|
||||
"User": "用户",
|
||||
"activateSelected": "激活所选",
|
||||
"bulkDeleteErrorMsg": "批量删除失败: ",
|
||||
"bulkDeleteSuccessMsg": "批量删除成功",
|
||||
@@ -372,26 +291,16 @@
|
||||
"fieldRequiredError": "这个字段是必填项",
|
||||
"getErrorMsg": "获取失败",
|
||||
"MFAErrorMsg": "MFA错误,请检查",
|
||||
"Total": "总共",
|
||||
"Success": "成功",
|
||||
"Failed": "失败",
|
||||
"Pending": "等待",
|
||||
"Status": "状态",
|
||||
"InputEmailAddress": "请输入正确的邮箱地址",
|
||||
"Receivers": "接收人",
|
||||
"imExport": {
|
||||
"ExportAll": "导出所有",
|
||||
"ExportOnlyFiltered": "仅导出搜索结果",
|
||||
"ExportOnlySelectedItems": "仅导出选择项",
|
||||
"ExportRange": "导出范围",
|
||||
"createSuccessMsg": "导入创建成功,总共:{count}",
|
||||
"downloadImportTemplateMsg": "下载创建模板",
|
||||
"downloadImportTemplateMsg": "下载导入模板",
|
||||
"downloadUpdateTemplateMsg": "下载更新模板",
|
||||
"onlyCSVFilesTips": "仅支持csv文件导入",
|
||||
"updateSuccessMsg": "导入更新成功,总共:{count}",
|
||||
"uploadCsvLth10MHelpText": "只能上传 csv/xlsx, 且不超过 10M",
|
||||
"dragUploadFileInfo": "将文件拖到此处,或点击上传",
|
||||
"hasImportErrorItemMsg": "存在导入失败项,点击左侧 x 查看失败原因,点击表格编辑后,可以继续导入失败项"
|
||||
"updateSuccessMsg": "导入更新成功,总共:{count}"
|
||||
},
|
||||
"fileType": "文件类型",
|
||||
"isValid": "有效",
|
||||
@@ -429,57 +338,7 @@
|
||||
"SPECIAL_CHAR_REQUIRED": "须包含特殊字符",
|
||||
"MIN_LENGTH_ERROR": "密码最小长度 {0} 位"
|
||||
},
|
||||
"lastCannotBeDeleteMsg": "最后一项,不能被删除",
|
||||
"InvalidJson": "不是合法 JSON",
|
||||
"time_period": "时段",
|
||||
"WeekCronSelect": {
|
||||
"Monday": "星期一",
|
||||
"Tuesday": "星期二",
|
||||
"Wednesday": "星期三",
|
||||
"Thursday": "星期四",
|
||||
"Friday": "星期五",
|
||||
"Saturday": "星期六",
|
||||
"Sunday": "星期天",
|
||||
"ClearSelection": "清空选择",
|
||||
"SelectAll": "全选",
|
||||
"CanDragSelect": "可拖动鼠标选择时间段",
|
||||
"WeekOrTime": "星期/时间"
|
||||
},
|
||||
"CronTab": {
|
||||
"newCron": "生成 cron",
|
||||
"timeExpression": "时间表达式",
|
||||
"cronExpression": "crontab完整表达式",
|
||||
"min": "分钟",
|
||||
"hour": "小时",
|
||||
"day": "日",
|
||||
"month": "月",
|
||||
"week": "周",
|
||||
"wildcardsAllowed": "允许的通配符",
|
||||
"from": "从",
|
||||
"every": "每",
|
||||
"appoint": "指定",
|
||||
"manyChoose": "可多选",
|
||||
"startEvery": "开始,每",
|
||||
"executeOnce": "执行一次",
|
||||
"lastDay": "本月最后一天",
|
||||
"everyMonth": "每月",
|
||||
"lastWorking": "最近的那个工作日",
|
||||
"lastWeek": "本月最后一个星期",
|
||||
"num": "号",
|
||||
"cycleFromWeek": "周期从星期",
|
||||
"the": "第",
|
||||
"weekOf": "周的星期",
|
||||
"Monday": "周一",
|
||||
"Tuesday": "周二",
|
||||
"Wednesday": "周三",
|
||||
"Thursday": "周四",
|
||||
"Friday": "周五",
|
||||
"Saturday": "周六",
|
||||
"Sunday": "周日",
|
||||
"runningTimes": "最近5次运行时间",
|
||||
"calculationResults": "cron 表达式错误",
|
||||
"failedConditions": "没有达到条件的结果!"
|
||||
}
|
||||
"lastCannotBeDeleteMsg": "最后一项,不能被删除"
|
||||
},
|
||||
"dashboard": {
|
||||
"ActiveAsset": "近期被登录过",
|
||||
@@ -571,7 +430,6 @@
|
||||
"User": "用户",
|
||||
"UserGroups": "用户组",
|
||||
"PermName":"授权名称",
|
||||
"Permissions": "授权列表",
|
||||
"DatabaseAppPermission": "数据库授权",
|
||||
"RemoteAppPermission": "远程应用授权",
|
||||
"addApplicationToThisPermission": "添加应用",
|
||||
@@ -594,8 +452,6 @@
|
||||
"downloadFile": "下载文件",
|
||||
"hostName": "主机名",
|
||||
"isValid": "有效",
|
||||
"fromTicket": "来自工单",
|
||||
"isEffective": "起作用的",
|
||||
"nodeCount": "节点数量",
|
||||
"refreshFail": "刷新失败",
|
||||
"refreshPermissionCache": "刷新授权缓存",
|
||||
@@ -609,7 +465,7 @@
|
||||
"systemUserCount": "系统用户数量",
|
||||
"upDownload": "上传下载",
|
||||
"uploadFile": "上传文件",
|
||||
"clipboardCopyPaste":"剪贴板复制粘贴",
|
||||
"clipboardCopyPaste":"复制粘贴",
|
||||
"clipboardCopy":"剪切板复制",
|
||||
"clipboardPaste":"剪切板粘贴",
|
||||
"userCount": "用户数量",
|
||||
@@ -618,15 +474,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"TicketFlow": "工单流",
|
||||
"TicketFlowCreate": "创建审批流",
|
||||
"TicketFlowUpdate": "更新审批流",
|
||||
"Accounts": "账号管理",
|
||||
"AssetAccount": "资产账号",
|
||||
"ApplicationAccount": "应用账号",
|
||||
"Ticket":"工单",
|
||||
"SessionDetail": "会话详情",
|
||||
"CommandConfirm": "命令复核",
|
||||
"AdminUserCreate": "创建管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserList": "管理用户",
|
||||
@@ -635,7 +483,6 @@
|
||||
"AssetCreate": "创建资产",
|
||||
"AssetDetail": "资产详情",
|
||||
"AssetList": "资产列表",
|
||||
"Session": "会话",
|
||||
"AssetPermission": "资产授权",
|
||||
"AssetPermissionCreate": "创建资产授权规则",
|
||||
"AssetPermissionDetail": "资产授权详情",
|
||||
@@ -674,16 +521,7 @@
|
||||
"KubernetesAppPermissionDetail": "Kubernetes授权详情",
|
||||
"KubernetesAppPermissionUpdate": "更新Kubernetes授权规则",
|
||||
"KubernetesAppUpdate": "更新Kubernetes",
|
||||
"Acl": "访问控制",
|
||||
"UserAclList": "用户登录",
|
||||
"UserLoginACLCreate": "创建用户登录规则",
|
||||
"UserAclLists": "用户登录规则",
|
||||
"UserLoginACLUpdate": "更新用户登录规则",
|
||||
"UserAclDetail": "用户登录规则详情",
|
||||
"AssetAclList": "资产登录",
|
||||
"AssetAclCreate": "创建资产登录规则",
|
||||
"AssetAclUpdate": "更新资产登录规则",
|
||||
"AssetAclDetail": "资产登录规则详情",
|
||||
|
||||
"DomainCreate": "创建网域",
|
||||
"DomainDetail": "网域详情",
|
||||
"DomainList": "网域列表",
|
||||
@@ -720,7 +558,7 @@
|
||||
"ApplicationPermissionUpdate": "更新应用授权规则",
|
||||
"RemoteAppUpdate": "更新远程应用",
|
||||
"ReplayStorageUpdate": "更新录像存储",
|
||||
"Detail": "详情",
|
||||
"SessionDetail": "会话详情",
|
||||
"SessionOffline": "历史会话",
|
||||
"SessionOnline": "在线会话",
|
||||
"Sessions": "会话管理",
|
||||
@@ -729,7 +567,6 @@
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserList": "系统用户",
|
||||
"SystemUserUpdate": "更新系统用户",
|
||||
"AssetUserList": "资产用户",
|
||||
"TaskDetail": "任务详情",
|
||||
"TaskList": "任务列表",
|
||||
"TaskMonitor": "任务监控",
|
||||
@@ -737,10 +574,6 @@
|
||||
"TicketDetail": "工单详情",
|
||||
"TicketCreate": "创建工单",
|
||||
"Tickets": "工单管理",
|
||||
"Templates": "模版管理",
|
||||
"TemplateDetail": "模版详情",
|
||||
"TemplateCreate": "创建模版",
|
||||
"TemplateUpdate": "更新模版",
|
||||
"UserCreate": "创建用户",
|
||||
"UserDetail": "用户详情",
|
||||
"UserFirstLogin": "首次登录",
|
||||
@@ -754,16 +587,9 @@
|
||||
"UserUpdate": "更新用户",
|
||||
"Users": "用户管理",
|
||||
"WebFTP": "文件管理",
|
||||
"WebTerminal": "Web终端",
|
||||
"Notifications": "通知",
|
||||
"SiteMessageList": "站内信",
|
||||
"UserLoginACL": "用户登录"
|
||||
"WebTerminal": "Web终端"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "设置为默认存储",
|
||||
"SetToDefault": "设为默认",
|
||||
"SetSuccess": "设置成功",
|
||||
"SetFailed": "设置失败",
|
||||
"StorageConfiguration": "存储配置",
|
||||
"accountKey": "账户密钥",
|
||||
"accountName": "账户名称",
|
||||
@@ -773,7 +599,6 @@
|
||||
"target": "目标",
|
||||
"bucket": "桶名称",
|
||||
"command": "命令",
|
||||
"Activity": "活动",
|
||||
"commandStorage": "命令存储",
|
||||
"comment": "备注",
|
||||
"containerName": "容器名称",
|
||||
@@ -799,7 +624,6 @@
|
||||
"name": "名称",
|
||||
"protocol": "协议",
|
||||
"region": "地域",
|
||||
"EsDisabled": "节点不可用, 请联系管理员",
|
||||
"sessionActiveCount": "在线会话数量",
|
||||
"systemCpuLoad": "CPU负载",
|
||||
"systemDiskUsedPercent": "硬盘使用率",
|
||||
@@ -825,11 +649,10 @@
|
||||
"common": "普通"
|
||||
},
|
||||
"Monitor": "监控",
|
||||
"XRDPNotSupport": "RDP 客户端会话, 暂不支持监控",
|
||||
"sessionMonitor": "监控",
|
||||
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
|
||||
"helpText": {
|
||||
"esUrl": "提示:如果有多台主机,请使用逗号 ( , ) 进行分割。(eg: http://www.jumpserver.a.com:3000,http://www.jumpserver.b.com:3000)",
|
||||
"esUrl": "提示:如果有多台主机,请使用逗号 ( , ) 进行分割。(eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex": "es提供默认index:jumpserver",
|
||||
"esDocType": "es默认文档类型:command",
|
||||
"s3Endpoint": "S3 格式: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China) 格式: http://s3.{REGION_NAME}.amazonaws.com.cn<br>如: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
@@ -837,32 +660,6 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"Feature": "功能",
|
||||
"AlibabaCloud": "阿里云",
|
||||
"TencentCloud": "腾讯云",
|
||||
"Radius": "Radius",
|
||||
"VerifySignTmpl": "验证码短信模板",
|
||||
"Enable": "启用",
|
||||
"AuthLimit": "登录限制",
|
||||
"SMTP": "邮件服务器",
|
||||
"Perm": "授权",
|
||||
"Setting": "设置",
|
||||
"Config": "配置",
|
||||
"AuthMethod": "认证方式",
|
||||
"SSO": "单点认证",
|
||||
"AppAuth": "App认证",
|
||||
"OtherAuth": "其它认证",
|
||||
"Ops": "任务",
|
||||
"OTP": "OTP(虚拟MFA)",
|
||||
"JMSSSO": "SSO Token 登录",
|
||||
"MessageSub": "消息订阅",
|
||||
"Cleaning": "定期清理",
|
||||
"Other": "其它设置",
|
||||
"CASSetting": "CAS 配置",
|
||||
"Auth": "认证设置",
|
||||
"SyncSetting": "同步设置",
|
||||
"Advanced": "高级设置",
|
||||
"InsecureCommandNotifyToSubscription": "危险命令通知已升级到消息订阅中,支持更多通知方式",
|
||||
"ApiKeyList": "API Key 列表",
|
||||
"AssetCount": "资产数量",
|
||||
"Basic": "基本设置",
|
||||
@@ -870,20 +667,17 @@
|
||||
"Create": "创建",
|
||||
"Edition": "版本",
|
||||
"Email": "邮件设置",
|
||||
"EmailContent": "邮件内容定制",
|
||||
"EmailContent": "邮件内容设置",
|
||||
"Expired": "过期时间",
|
||||
"Hostname": "主机名",
|
||||
"ImportLicense": "导入许可证",
|
||||
"ImportLicenseTip": "请导入许可证",
|
||||
"Ldap": "LDAP",
|
||||
"Ldap": "LDAP设置",
|
||||
"License": "许可证",
|
||||
"Senior": "高级",
|
||||
"LicenseFile": "许可证文件",
|
||||
"PasswordCheckRule": "密码强弱规则",
|
||||
"PasswordCheckRule": "密码校验规则",
|
||||
"Security": "安全设置",
|
||||
"SecuritySetting": "安全设置",
|
||||
"SystemMessageSubscription": "系统消息订阅",
|
||||
"insecureCommandEmailUpdate": "点我设置",
|
||||
"SubscriptionID": "订阅授权ID",
|
||||
"Terminal": "终端设置",
|
||||
"all": "全部",
|
||||
@@ -893,15 +687,13 @@
|
||||
"authLdapSearchFilter": "用户过滤器",
|
||||
"authLdapSearchOu": "用户OU",
|
||||
"authLdapServerUri": "LDAP地址",
|
||||
"authLdapUserAttrMap": "用户属性映射",
|
||||
"authCASAttrMap": "用户属性映射",
|
||||
"SignaturesAndTemplates": "Signatures and Templates",
|
||||
"authLdapUserAttrMap": "LDAP属性映射",
|
||||
"unselectedUser": "没有选择用户",
|
||||
"auto": "自动",
|
||||
"basicSetting": "基本设置",
|
||||
"communityEdition": "社区版",
|
||||
"consult": "咨询",
|
||||
"CreateUserSetting": "创建用户内容",
|
||||
"createUserSetting": "创建用户设置",
|
||||
"emailCustomUserCreatedBody": "创建用户邮件的内容",
|
||||
"emailCustomUserCreatedHonorific": "创建用户邮件的敬语",
|
||||
"emailCustomUserCreatedSignature": "署名",
|
||||
@@ -916,11 +708,9 @@
|
||||
"emailTest": "测试连接",
|
||||
"emailUserSSL": "使用SSL",
|
||||
"emailUserTLS": "使用TLS",
|
||||
"InsecureCommandAlert": "危险命令告警",
|
||||
"SecurityInsecureCommand": "危险命令告警",
|
||||
"Insecure_Command_Alert": "危险命令告警",
|
||||
"SecurityInsecureCommandEmailReceiver": "告警接收邮件",
|
||||
"MailSend": "邮件发送",
|
||||
"LDAPServerInfo": "LDAP 服务器",
|
||||
"LDAPUser": "LDAP 用户",
|
||||
"helpText": {
|
||||
"ApiKeyList": "使用api key签名请求头,每个请求的头部是不一样的, 请查阅使用文档",
|
||||
"authLdapSearchFilter": "可能的选项是(cn或uid或sAMAccountName=%(user)s)",
|
||||
@@ -930,7 +720,7 @@
|
||||
"emailCustomUserCreatedHonorific": "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)",
|
||||
"emailCustomUserCreatedSignature": "提示: 邮件的署名 (例如: jumpserver)",
|
||||
"emailCustomUserCreatedSubject": "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)",
|
||||
"emailEmailFrom": "",
|
||||
"emailEmailFrom": "提示:发送邮件账号,默认使用SMTP账号作为发送账号",
|
||||
"emailHostPassword": "提示:一些邮件提供商需要输入的是Token",
|
||||
"emailRecipient": "提示:仅用来作为测试邮件收件人",
|
||||
"emailSubjectPrefix": "提示: 一些关键字可能会被邮件提供商拦截,如 跳板机、JumpServer",
|
||||
@@ -964,7 +754,7 @@
|
||||
"import": "导入",
|
||||
"importLdapUserTip": "请先提交LDAP配置再进行导入",
|
||||
"importLdapUserTitle": "LDAP 用户列表",
|
||||
"ldapBulkImport": "用户导入",
|
||||
"ldapBulkImport": "一键导入",
|
||||
"ldapConnectTest": "测试连接",
|
||||
"ldapLoginTest": "测试登录",
|
||||
"password": "密码",
|
||||
@@ -999,29 +789,12 @@
|
||||
"refreshLdapCache":"刷新Ldap缓存,请稍后",
|
||||
"LicenseExpired": "许可证已经过期",
|
||||
"LicenseWillBe": "许可证即将在 ",
|
||||
"Expire": " 过期",
|
||||
"WeCom": "企业微信",
|
||||
"DingTalk": "钉钉",
|
||||
"dingTalkTest": "测试",
|
||||
"weComTest": "测试",
|
||||
"FeiShu": "飞书",
|
||||
"SMS": "短信设置",
|
||||
"feiShuTest": "测试",
|
||||
"setting": "设置",
|
||||
"SMSProvider": "短信服务商"
|
||||
"Expire": " 过期"
|
||||
},
|
||||
"settings": {
|
||||
"setting": "设置"
|
||||
},
|
||||
"tickets": {
|
||||
"OneAssigneeType": "一级受理人类型",
|
||||
"OneAssignee": "一级受理人",
|
||||
"TwoAssigneeType": "二级受理人类型",
|
||||
"TwoAssignee": "二级受理人",
|
||||
"SuperAdmin": "超级管理员",
|
||||
"OrgAdmin": "组织管理员",
|
||||
"SuperOrgAdmin": "超级管理员+组织管理员",
|
||||
"CustomUser": "自定义用户",
|
||||
"ApprovalLevel": "审批级别",
|
||||
"FlowDetail": "流程详情",
|
||||
"PermissionName": "授权规则名称",
|
||||
"Accept": "同意",
|
||||
"AssignedMe": "待我审批",
|
||||
"Assignee": "处理人",
|
||||
@@ -1038,11 +811,12 @@
|
||||
"status": "状态",
|
||||
"title": "标题",
|
||||
"action": "动作",
|
||||
"IPGroup": "IP 组",
|
||||
"type": "类型",
|
||||
"user": "用户",
|
||||
"Status": "状态",
|
||||
"Open": "待处理",
|
||||
"OrgName":"授权组织名称",
|
||||
"OrgName":"组织名称",
|
||||
"AssignedInfo":"审批信息",
|
||||
"OpenTicket": "创建工单",
|
||||
"HandleTicket": "处理工单",
|
||||
@@ -1058,18 +832,11 @@
|
||||
"Approved": "已同意",
|
||||
"Rejected": "已拒绝",
|
||||
"Closed": "已完成",
|
||||
"StateClosed": "已关闭",
|
||||
"helpText": {
|
||||
"ips": "请输入逗号分割的IP地址组",
|
||||
"fuzzySearch": "支持模糊搜索",
|
||||
"application": "请输入逗号分割的应用名称组"
|
||||
},
|
||||
"ApplyRunUser": "申请运行的用户",
|
||||
"ApplyRunAsset": "申请运行的资产",
|
||||
"ApplyRunSystemUser": "申请运行的系统用户",
|
||||
"ApplyRunCommand": "申请运行的命令",
|
||||
"ApplyFromSession": "会话",
|
||||
"ApplyFromCMDFilterRule": "命令过滤规则"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "添加资产到节点",
|
||||
@@ -1085,9 +852,6 @@
|
||||
"UpdateNodeAssetHardwareInfo": "更新节点资产硬件信息"
|
||||
},
|
||||
"users": {
|
||||
"MessageSubscription": "消息订阅",
|
||||
"AuthSettings": "认证配置",
|
||||
"UserName": "姓名",
|
||||
"Account": "账户",
|
||||
"Authentication": "认证",
|
||||
"Comment": "备注",
|
||||
@@ -1099,10 +863,6 @@
|
||||
"DatePasswordUpdated": "密码更新日期",
|
||||
"DescribeOfGuide": "欢迎使用JumpServer堡垒机系统,获取更多信息请点击",
|
||||
"Email": "邮件",
|
||||
"Phone": "手机号",
|
||||
"WeCom": "企业微信",
|
||||
"DingTalk": "钉钉",
|
||||
"FeiShu": "飞书",
|
||||
"FingerPrint": "指纹",
|
||||
"FirstLogin": "首次登录",
|
||||
"OrgUser": "组织用户",
|
||||
@@ -1112,9 +872,6 @@
|
||||
"Invite": "邀请",
|
||||
"InviteUserInOrg": "邀请用户加入此组织",
|
||||
"Guide": "向导",
|
||||
"setWeCom": "设置企业微信认证",
|
||||
"setDingTalk": "设置钉钉认证",
|
||||
"setFeiShu": "设置飞书认证",
|
||||
"HelpText": {
|
||||
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "启用多因子认证,使账号更加安全。<br/> 启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速修改->更改多因子设置)中直接绑定!",
|
||||
"MFAOfUserFirstLoginUserGuidePage": "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:设置复杂密码,并启用多因子认证)",
|
||||
@@ -1157,19 +914,12 @@
|
||||
"resetMFAWarningMsg": "你确定要重置用户的 MFA 吗?",
|
||||
"resetMFAdSuccessMsg": "重置MFA成功, 用户可以重新设置MFA了",
|
||||
"resetPassword": "重置密码",
|
||||
"resetPasswordSuccessMsg": "已向用户发送重置密码消息",
|
||||
"resetPasswordSuccessMsg": "发送邮件任务已提交, 用户稍后会收到重置密码邮件",
|
||||
"resetPasswordWarningMsg": "你确定要发送重置用户密码的邮件吗",
|
||||
"resetSSHKey": "重置SSH密钥",
|
||||
"resetSSHKeySuccessMsg": "发送邮件任务已提交, 用户稍后会收到重置密钥邮件",
|
||||
"resetSSHKeyWarningMsg": "你确定要发送重置用户的SSH Key的邮件吗?",
|
||||
"resetWechat": "解绑企业微信",
|
||||
"resetWechatLoginWarningMsg": "你确定要解绑用户的 企业微信 吗?",
|
||||
"resetWechatLoginSuccessMsg": "重置成功, 用户可以重新绑定企业微信了",
|
||||
"resetDingTalk": "解绑钉钉",
|
||||
"resetDingTalkLoginWarningMsg": "你确定要解绑用户的 钉钉 吗?",
|
||||
"resetDingTalkLoginSuccessMsg": "重置成功, 用户可以重新绑定钉钉了",
|
||||
"send": "发送",
|
||||
"unbind": "解绑",
|
||||
"unblock": "解锁",
|
||||
"unblockSuccessMsg": "解锁成功",
|
||||
"unblockUser": "解锁用户"
|
||||
@@ -1188,56 +938,26 @@
|
||||
"remoteAppPermissionRules": "远程应用授权规则"
|
||||
},
|
||||
"dateLastLogin": "最后登录日期",
|
||||
"needUpdatePasswordNextLogin": "下次登录须修改密码",
|
||||
"UpdatePassword": "更新密码",
|
||||
"SetPublicKey": "设置SSH公钥",
|
||||
"passwordExpired": "密码过期了",
|
||||
"passwordWillExpiredPrefixMsg": "密码即将在 ",
|
||||
"passwordWillExpiredSuffixMsg": "天 后过期,请尽快修改您的密码。"
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "消息类型",
|
||||
"Receivers": "接收人",
|
||||
"Subscription": "消息订阅",
|
||||
"ChangeReceiver": "修改消息接收人",
|
||||
"Subject": "主题",
|
||||
"Message": "消息",
|
||||
"DeliveryTime": "发送时间",
|
||||
"HasRead": "是否已读",
|
||||
"Sender": "发送人",
|
||||
"MarkAsRead": "标记已读",
|
||||
"NoUnreadMsg": "暂无未读消息",
|
||||
"SiteMessage": "站内信",
|
||||
"SMS": "短信"
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "管理员",
|
||||
"Asset": "资产",
|
||||
"Database": "数据库",
|
||||
"AssetCount": "资产数量",
|
||||
"Auditor": "审计员",
|
||||
"ChangeAuthPlan": {
|
||||
"AddAsset": "添加资产",
|
||||
"AddNode": "添加节点",
|
||||
"AddSystemUser": "添加系统用户",
|
||||
"Asset": "资产",
|
||||
"Database": "数据库",
|
||||
"DatabaseId": "数据库Id",
|
||||
"AppAmount": "应用数量",
|
||||
"SystemUserAmount": "系统用户数量",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserId": "系统用户Id",
|
||||
"AssetAmount": "资产数量",
|
||||
"AssetAndNode": "资产和节点",
|
||||
"ChangeAuthPlan": "改密计划",
|
||||
"AssetChangeAuthPlan": "资产改密计划",
|
||||
"AppChangeAuthPlan": "应用改密计划",
|
||||
"AssetChangeAuthPlanCreate": "创建资产改密计划",
|
||||
"AppChangeAuthPlanCreate": "创建应用改密计划",
|
||||
"AssetChangeAuthPlanUpdate": "更新资产改密计划",
|
||||
"AppChangeAuthPlanUpdate": "更新应用改密计划",
|
||||
"SymbolSet": "特殊符号集合",
|
||||
"SymbolSetHelpText": "请输入此类型数据库支持的特殊符号集合,若生成的随机密码中有此类数据库不支持的特殊字符,改密计划将会失败",
|
||||
"ChangeAuthPlanCreate": "创建改密计划",
|
||||
"ChangeAuthPlanUpdate": "更新改密计划",
|
||||
"CyclePerform": "周期执行",
|
||||
"DateJoined": "创建日期",
|
||||
"DateStart": "开始日期",
|
||||
@@ -1261,7 +981,6 @@
|
||||
"NodeAmount": "节点数量",
|
||||
"PasswordLength": "密码长度",
|
||||
"PasswordStrategy": "密码策略",
|
||||
"SecretKeyStrategy": "密钥策略",
|
||||
"RegularlyPerform": "定期执行",
|
||||
"Result": "结果",
|
||||
"Retry": "重试",
|
||||
@@ -1269,22 +988,15 @@
|
||||
"TaskList": "任务列表",
|
||||
"TimeDelta": "运行时间",
|
||||
"Timer": "定时执行",
|
||||
"TimerPeriod": "定时执行周期",
|
||||
"Username": "用户名"
|
||||
},
|
||||
"Cloud": {
|
||||
"ServerAccountKey": "服务账号密钥",
|
||||
"IPNetworkSegment": "IP网段",
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"QingyunPrivatecloud": "青云私有云",
|
||||
"HuaweiPrivatecloud": "华为私有云",
|
||||
"GCP": "谷歌云",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
"Azure":"Azure(中国)",
|
||||
"Azure_Int": "Azure(国际)",
|
||||
"HostnameStrategy": "用于生成资产主机名。例如:1. 实例名称 (instanceDemo);2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "资产信息保持最新",
|
||||
"AccountCreate": "创建账户",
|
||||
@@ -1307,7 +1019,7 @@
|
||||
"Name": "名称",
|
||||
"Account":"账户",
|
||||
"Node": "节点",
|
||||
"AdminUser":"特权用户",
|
||||
"AdminUser":"管理用户",
|
||||
"Periodic":"执行周期",
|
||||
"PeriodicPerform":"定时执行",
|
||||
"RegularlyPerform": "定期执行",
|
||||
@@ -1328,9 +1040,6 @@
|
||||
"Log": "日志",
|
||||
"DeleteReleasedAssets": "删除已释放资产"
|
||||
},
|
||||
"Template": {
|
||||
"Template": "模版管理"
|
||||
},
|
||||
"Corporation": "公司",
|
||||
"Edition": "版本",
|
||||
"Execute": "执行",
|
||||
@@ -1340,9 +1049,7 @@
|
||||
"GatherUserList": "收集用户",
|
||||
"GatherUserTaskCreate": "创建任务",
|
||||
"GatherUserTaskList": "任务列表",
|
||||
"GatherUserTaskUpdate": "更新任务",
|
||||
"GatherUserTaskDetail": "任务详情",
|
||||
"GatherUserTaskExecutionList": "任务执行列表"
|
||||
"GatherUserTaskUpdate": "更新任务"
|
||||
},
|
||||
"Import": "导入",
|
||||
"ImportLicense": "导入许可证",
|
||||
@@ -1372,7 +1079,7 @@
|
||||
"users_amount": "用户数量",
|
||||
"groups_amount": "用户组数量",
|
||||
"assets_amount": "资产数量",
|
||||
"admin_users_amount": "特权用户数量",
|
||||
"admin_users_amount": "管理用户数量",
|
||||
"system_users_amount": "系统用户数量",
|
||||
"applications_amount": "应用数量",
|
||||
"asset_perms_amount": "资产授权数量",
|
||||
|
||||
@@ -1,39 +1,7 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "Application account list, please click on the application on the left to view",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
},
|
||||
"acl": {
|
||||
"name": "Name",
|
||||
"username": "Username",
|
||||
"ip_group": "IP group",
|
||||
"action": "Action",
|
||||
"priority": "Priority",
|
||||
"date_created": "Date created",
|
||||
"created_by": "Created by",
|
||||
"asset": "Asset",
|
||||
"system_user": "System user",
|
||||
"username_group":"Username group",
|
||||
"hostname_group":"Hostname group",
|
||||
"asset_ip_group": "Asset ip group",
|
||||
"system_users_name_group": "Systemusers name group",
|
||||
"system_users_protocol_group": "Systemusers protocol group",
|
||||
"system_users_username_group": "systemusers username group",
|
||||
"apply_login_asset": "Apply login asset",
|
||||
"apply_login_system_user": "Apply login system user",
|
||||
"apply_login_user": "Apply login user",
|
||||
"RuleDetail": "Rule detail"
|
||||
},
|
||||
"applications": {
|
||||
"": "",
|
||||
"updateAccountMsg": "Please update system user account info",
|
||||
"associateApplication": "Associate application",
|
||||
"RemoteApp": "Remote app",
|
||||
"Database": "Database",
|
||||
"Cloud": "Cloud",
|
||||
"App": "Application",
|
||||
"applicationsType": {
|
||||
"chrome": "Chrome",
|
||||
"mysql_workbench": "MySQL Workbench",
|
||||
@@ -85,24 +53,16 @@
|
||||
"DBInfo": "Database Info"
|
||||
},
|
||||
"assets": {
|
||||
"AppList": "Application list",
|
||||
"AssociateSystemUsers": "Associate system users",
|
||||
"AssociateAssets": "Associate assets",
|
||||
"AssociateNodes": "Associate nodes",
|
||||
"Action": "Action",
|
||||
"ActiveSelected": "Active selected",
|
||||
"AdminUser": "Admin user",
|
||||
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"DynamicUsername": "Dynamic username",
|
||||
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
|
||||
"Asset": "Asset",
|
||||
"HardwareInfo": "Hardware info",
|
||||
"Hardware": "Hardware",
|
||||
"AccountList": "Account list",
|
||||
"PermUserList": "Authorized user",
|
||||
"ViewPerm": "View permission",
|
||||
"LoginOption": "Login option",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Asset list",
|
||||
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
|
||||
@@ -126,7 +86,6 @@
|
||||
"CommandFilterRules": "Command filter rules",
|
||||
"Comment": "Comment",
|
||||
"Cpu": "Cpu",
|
||||
"CommonUser": "Common user",
|
||||
"CreatedBy": "Created by",
|
||||
"Database": "Database",
|
||||
"DateJoined": "Date joined",
|
||||
@@ -170,7 +129,6 @@
|
||||
"PublicIp": "Public ip",
|
||||
"Push": "Push",
|
||||
"PushSystemUserNow": "Push system user now",
|
||||
"PushAllSystemUsersToAsset": "Push all system users to asset",
|
||||
"QuickUpdate": "Quick update",
|
||||
"Reachable": "Reachable",
|
||||
"Unreachable": "Unreachable",
|
||||
@@ -186,18 +144,17 @@
|
||||
"Rules": "Rules",
|
||||
"SFTPHelpMessage": "SFTP root dir, tmp, home or custom",
|
||||
"SerialNumber": "Serial number",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami, /bin/ifconfig",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig",
|
||||
"PasswordHelpMessage": "Password or private key password",
|
||||
"SystemUser": "System user",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserListHelpMessage": "<b>System user</b> is the account JumpServer used to log into the asset, such as using root `ssh root@host`, rather than the current user username(ssh admin@host)`;<br><b>Admin user</b> is the account that already exists on an asset, and have privileged permissions, JumpServer using this create common system user, and gather hardware Etc;</br><b>Common user</b> can pre-exist assets or created automatically by the admin user.",
|
||||
"SystemUserListHelpMessage": "System user is JumpServer jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); In simple terms, users log into JumpServer using their own username, and JumpServer uses system users to log into assets. When system users are created, if you choose auto push JumpServer to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.\n",
|
||||
"SystemUsers": "System users",
|
||||
"Test": "Test",
|
||||
"TestAssetsConnective": "Test assets connective",
|
||||
"TestAllSystemUsersConnective": "Test all system users connective",
|
||||
"TestConnection": "Test connection",
|
||||
"Type": "Type",
|
||||
"UnselectedAssets": "No asset selected or the selected asset does not support SSH protocol connection",
|
||||
"UnselectedAssets": "Unselected assets",
|
||||
"UnselectedNodes": "Unselected nodes",
|
||||
"UpdateAssetUserToken": "Update asset user auth",
|
||||
"Username": "Username",
|
||||
@@ -207,25 +164,18 @@
|
||||
"Version": "Version",
|
||||
"command_filter_list": "Command filter list",
|
||||
"date_joined": "Date joined",
|
||||
"sshKeyFingerprint": "SSH fingerprint",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"GroupsHelpMessage": "Please fill in user groups, separated by commas if there are multiple user groups(Please fill in the existing user groups)",
|
||||
"HomeHelpMessage": "Default home directory: /home/system username",
|
||||
"Home": "Home",
|
||||
"LinuxUserAffiliateGroup": "Linux user affiliate group",
|
||||
"ipDomain": "IP(Domain)",
|
||||
"HostProtocol": "Host Protocol",
|
||||
"DatabaseProtocol": "Database Protocol",
|
||||
"OtherProtocol": "Other Protocol",
|
||||
"PasswordOrToken": "Password / Token"
|
||||
"ipDomain": "IP(Domain)"
|
||||
},
|
||||
"audits": {
|
||||
"Hosts": "Host",
|
||||
"RunUser": "Run user",
|
||||
"User": "User",
|
||||
"Username": "Username",
|
||||
"SystemUserName": "System username",
|
||||
"View": "View"
|
||||
},
|
||||
"auth": {
|
||||
@@ -233,14 +183,6 @@
|
||||
"ReLogin": "Re-Login"
|
||||
},
|
||||
"common": {
|
||||
"ViewMore": "View more",
|
||||
"Announcement": "Announcement",
|
||||
"Logging": "Logging",
|
||||
"Database": "Database",
|
||||
"Params": "Params",
|
||||
"MFAVerify": "Verify MFA",
|
||||
"ViewSecret": "View secret",
|
||||
"ConnectWebSocketError": "Connect Websocket failed",
|
||||
"Nothing": "Nothing",
|
||||
"Action": "Action",
|
||||
"CustomCol":"Custom table col",
|
||||
@@ -255,9 +197,6 @@
|
||||
"Add": "Add",
|
||||
"PleaseAgreeToTheTerms": "Please agree to the terms",
|
||||
"PushSelected":"Push selected",
|
||||
"PushSelectedSystemUsersToAsset": "Push selected system users to asset",
|
||||
"TestSelected": "Test selected",
|
||||
"TestSelectedSystemUsersConnective": "Test selected system users connective",
|
||||
"UpdateAssetDetail": "Update more detail",
|
||||
"AddSuccessMsg": "Add success",
|
||||
"Auth": "Authorization",
|
||||
@@ -274,7 +213,7 @@
|
||||
"Confirm": "Confirm",
|
||||
"Create": "Create",
|
||||
"CreatedBy": "Created by",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set, give priority to Regularly perform",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set,give priority to Regularly perform",
|
||||
"DateEnd": "End date",
|
||||
"Resource": "Resource",
|
||||
"DateLast24Hours": "Last 24 hours",
|
||||
@@ -290,16 +229,10 @@
|
||||
"EnterForSearch": "Press enter to search",
|
||||
"Export": "Export",
|
||||
"Import": "Import",
|
||||
"ContinueImport": "ContinueImport",
|
||||
"Continue": "Continue",
|
||||
"Stop": "Stop",
|
||||
"Finished": "Finished",
|
||||
"Refresh": "Refresh",
|
||||
"Info": "Info",
|
||||
"MFAConfirm": "MFA Confirm",
|
||||
"MFARequireForSecurity": "MFA required for security",
|
||||
"PasswordConfirm": "Password Confirm",
|
||||
"PasswordRequireForSecurity": "Password required for security",
|
||||
"Members": "Members",
|
||||
"More": "More",
|
||||
"Message": "Message",
|
||||
@@ -311,24 +244,19 @@
|
||||
"Other": "Other",
|
||||
"Others": "Others",
|
||||
"Push": "Push",
|
||||
"Receivers": "Receivers",
|
||||
"QuickUpdate": "Quick update",
|
||||
"RemoveSuccessMsg": "Remove success",
|
||||
"Reset": "Reset",
|
||||
"Search": "Search",
|
||||
"MFAErrorMsg": "MFA Error, please check",
|
||||
"InputEmailAddress": "Please enter your email address",
|
||||
"MFAErrorMsg": "MFA Error,please check",
|
||||
"Select": "Select",
|
||||
"SelectFile": "Select file",
|
||||
"Show": "Show",
|
||||
"Submit": "Submit",
|
||||
"Test": "Test",
|
||||
"SaveAndAddAnother":"Save and add another",
|
||||
"TestSuccessMsg": "Test Success",
|
||||
"To": "To",
|
||||
"Update": "Update",
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"Upload": "Upload",
|
||||
"Clone": "Clone",
|
||||
"Username": "Username",
|
||||
@@ -362,11 +290,6 @@
|
||||
"fieldRequiredError": "This field is required",
|
||||
"getErrorMsg": "Get failed",
|
||||
"fileType": "File type",
|
||||
"Status": "Status",
|
||||
"Total": "Total",
|
||||
"Success": "Success",
|
||||
"Failed": "Failed",
|
||||
"Pending": "Pending",
|
||||
"imExport": {
|
||||
"ExportAll": "Export all",
|
||||
"ExportOnlyFiltered": "Export only filtered",
|
||||
@@ -376,10 +299,7 @@
|
||||
"downloadImportTemplateMsg": "Download import template",
|
||||
"downloadUpdateTemplateMsg": "Download update template",
|
||||
"onlyCSVFilesTips": "Only csv supported",
|
||||
"updateSuccessMsg": "Update success, total: {count}",
|
||||
"dragUploadFileInfo": "Drag file here or click to upload",
|
||||
"uploadCsvLth10MHelpText": "csv/xlsx files with a size less than 10M",
|
||||
"hasImportErrorItemMsg": "There is an error item, click the x icon to view the details, and continue to import after editing"
|
||||
"updateSuccessMsg": "Update success, total: {count}"
|
||||
},
|
||||
"isValid": "Is valid",
|
||||
"nav": {
|
||||
@@ -416,58 +336,7 @@
|
||||
"SPECIAL_CHAR_REQUIRED": "Special char required",
|
||||
"MIN_LENGTH_ERROR": "Password minimum length {}"
|
||||
},
|
||||
"lastCannotBeDeleteMsg": "The last one can't be delete",
|
||||
"InvalidJson": "Not a valid json format",
|
||||
"time_period": "Time period",
|
||||
"CronTab": {
|
||||
"newCron": "new cron",
|
||||
"timeExpression": "time expression",
|
||||
"cronExpression": "cron expression",
|
||||
"min": "minute",
|
||||
"hour": "hour",
|
||||
"day": "day",
|
||||
"month": "month",
|
||||
"week": "week",
|
||||
"wildcardsAllowed": "wildcards allowed",
|
||||
"from": "from",
|
||||
"every": "every",
|
||||
"appoint": "appoint",
|
||||
"manyChoose": "many choose",
|
||||
"startEvery": " start, every",
|
||||
"executeOnce": " execute once",
|
||||
"lastDay": "last day of the month",
|
||||
"everyMonth": "every month",
|
||||
"lastWorking": "the last working day",
|
||||
"lastWeek": "last week of the month",
|
||||
"num": "number ",
|
||||
"cycleFromWeek": "cycle from week",
|
||||
"the": "the",
|
||||
"weekOf": "week of week",
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
"Wednesday": "Wednesday",
|
||||
"Thursday": "Thursday",
|
||||
"Friday": "Friday",
|
||||
"Saturday": "Saturday",
|
||||
"Sunday": "Sunday",
|
||||
"runningTimes": "last 5 running times",
|
||||
"calculationResults": "calculationResults...",
|
||||
"failedConditions": "cron expression error"
|
||||
},
|
||||
"Cycle": "Cycle",
|
||||
"WeekCronSelect": {
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
"Wednesday": "Wednesday",
|
||||
"Thursday": "Thursday",
|
||||
"Friday": "Friday",
|
||||
"Saturday": "Saturday",
|
||||
"Sunday": "Sunday",
|
||||
"ClearSelection": "Clear selection",
|
||||
"SelectAll": "Select all",
|
||||
"CanDragSelect": "Can drag the mouse to select a time period",
|
||||
"WeekOrTime": "Week/Time"
|
||||
}
|
||||
"lastCannotBeDeleteMsg": "The last one can't be delete"
|
||||
},
|
||||
"dashboard": {
|
||||
"ActiveAsset": "Asset active",
|
||||
@@ -548,7 +417,6 @@
|
||||
"perms": {
|
||||
"": "",
|
||||
"Actions": "Permission",
|
||||
"Permission": "Permissions",
|
||||
"Asset": "Asset",
|
||||
"Basic": "Basic",
|
||||
"Exclude": "Exclude",
|
||||
@@ -559,7 +427,6 @@
|
||||
"SystemUser": "System user",
|
||||
"User": "User",
|
||||
"UserGroups": "UserGroups",
|
||||
"Permissions": "Permissions",
|
||||
"DatabaseAppPermission": "Databases permissions",
|
||||
"RemoteAppPermission": "Remote apps permissions",
|
||||
"KubernetesAppPermission": "Kubernetes permissions",
|
||||
@@ -583,8 +450,6 @@
|
||||
"downloadFile": "Download file",
|
||||
"hostName": "Hostname",
|
||||
"isValid": "Validity",
|
||||
"fromTicket": "From ticket",
|
||||
"isEffective": "Effective",
|
||||
"nodeCount": "Node count",
|
||||
"refreshFail": "Refresh fail",
|
||||
"refreshPermissionCache": "Refresh permission cache",
|
||||
@@ -607,34 +472,29 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"SessionDetail": "SessionDetail",
|
||||
"Accounts": "Accounts",
|
||||
"AssetAccount": "Asset account",
|
||||
"ApplicationAccount": "Application account",
|
||||
"Ticket": "Tickets",
|
||||
"CommandConfirm": "Command confirm",
|
||||
"AdminUserCreate": "Admin user create",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"AdminUserList": "Admin Users",
|
||||
"AdminUserList": "Admin users",
|
||||
"AdminUserUpdate": "Admin user update",
|
||||
"Applications": "Applications",
|
||||
"AssetCreate": "Asset create",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Assets",
|
||||
"AssetPermission": "Asset Permissions",
|
||||
"AssetPermission": "Asset permissions",
|
||||
"AssetPermissionCreate": "Asset permissions create",
|
||||
"AssetPermissionDetail": "Asset permissions detail",
|
||||
"AssetPermissionUpdate": "Asset permissions update",
|
||||
"AssetUpdate": "Asset update",
|
||||
"Assets": "Assets",
|
||||
"Audits": "Audits",
|
||||
"BatchCommand": "Batch Command",
|
||||
"BatchCommandLog": "Batch Command Log",
|
||||
"BatchCommand": "Batch command",
|
||||
"BatchCommandLog": "Batch command log",
|
||||
"CeleryTaskLog": "Celery task log",
|
||||
"CommandExecutions": "CommandExecutions ",
|
||||
"CommandFilterCreate": "Command filter create",
|
||||
"CommandFilterDetail": "Command filter detail",
|
||||
"CommandFilterList": "Command Filters",
|
||||
"CommandFilterList": "Command filters",
|
||||
"CommandFilterRulesCreate": "Command filter rules create",
|
||||
"CommandFilterRulesUpdate": "Command filter rules update",
|
||||
"CommandFilterUpdate": "Command filter update",
|
||||
@@ -643,7 +503,7 @@
|
||||
"CreateCommandStorage": "Create command storage",
|
||||
"CreateReplayStorage": "Create replay storage",
|
||||
"Dashboard": "Dashboard",
|
||||
"DatabaseApp": "Database Apps",
|
||||
"DatabaseApp": "Database apps",
|
||||
"DatabaseAppCreate": "Database app create",
|
||||
"DatabaseAppDetail": "Database app detail",
|
||||
"DatabaseAppPermission": "Databases permissions",
|
||||
@@ -651,7 +511,7 @@
|
||||
"DatabaseAppPermissionDetail": "Databases permissions detail",
|
||||
"DatabaseAppPermissionUpdate": "Databases permissions update",
|
||||
"DatabaseAppUpdate": "Database app update",
|
||||
"KubernetesApp": "Kubernetes Apps",
|
||||
"KubernetesApp": "Kubernetes apps",
|
||||
"KubernetesAppCreate": "Kubernetes app create",
|
||||
"KubernetesAppDetail": "Kubernetes app detail",
|
||||
"KubernetesAppPermission": "Kubernetes permissions",
|
||||
@@ -659,65 +519,50 @@
|
||||
"KubernetesAppPermissionDetail": "Kubernetes permissions detail",
|
||||
"KubernetesAppPermissionUpdate": "Kubernetes permissions update",
|
||||
"KubernetesAppUpdate": "Kubernetes app update",
|
||||
"Acl": "Access Control",
|
||||
"UserAclList": "User acl list",
|
||||
"UserAclCreate": "User acl create",
|
||||
"UserAclUpdate": "User acl update",
|
||||
"UserAclLists": "User acl lists",
|
||||
"UserAclDetail": "User acl detail",
|
||||
"AssetAclList": "Asset Acl",
|
||||
"AssetAclCreate": "Asset acl create",
|
||||
"AssetAclUpdate": "Asset acl update",
|
||||
"AssetAclDetail": "Asset acl detail",
|
||||
"DomainCreate": "Domain create",
|
||||
"DomainDetail": "Domain detail",
|
||||
"DomainList": "Domains",
|
||||
"DomainUpdate": "Domain update",
|
||||
"FileManager": "File Manager",
|
||||
"FtpLog": "FTP Logs",
|
||||
"FileManager": "File manager",
|
||||
"FtpLog": "FTP logs",
|
||||
"GatewayCreate": "Gateway create",
|
||||
"GatewayUpdate": "Gateway update",
|
||||
"JobCenter": "Jobcenter",
|
||||
"LabelCreate": "Label create",
|
||||
"LabelList": "Labels",
|
||||
"LabelUpdate": "Label update",
|
||||
"LoginLog": "Login Logs",
|
||||
"MyApps": "My Apps",
|
||||
"MyAssets": "My Assets",
|
||||
"OperateLog": "Operation Logs",
|
||||
"PasswordChangeLog": "Password Update Logs",
|
||||
"LoginLog": "Login logs",
|
||||
"MyApps": "My apps",
|
||||
"MyAssets": "My assets",
|
||||
"OperateLog": "Operation logs",
|
||||
"PasswordChangeLog": "Password update logs",
|
||||
"Perms": "Permissions",
|
||||
"PersonalInformationImprovement": "PersonalInformationImprovement",
|
||||
"PlatformCreate": "Platform create",
|
||||
"PlatformDetail": "Platform detail",
|
||||
"PlatformList": "Platforms",
|
||||
"PlatformUpdate": "Platform update",
|
||||
"RemoteApp": "Remote Apps",
|
||||
"RemoteApp": "Remote apps",
|
||||
"RemoteAppDetail": "Remote app detail",
|
||||
"RemoteAppPermission": "Remote apps permissions",
|
||||
"ApplicationPermission": "Application Permissions",
|
||||
"ApplicationPermission": "Application permissions",
|
||||
"RemoteAppPermissionCreate": "Remote apps permission create",
|
||||
"RemoteAppPermissionDetail": "Remote apps permissions detail",
|
||||
"RemoteAppPermissionUpdate": "Remote app permission update",
|
||||
"ApplicationDetail": "Application detail",
|
||||
"ApplicationPermissionCreate": "Application permission create",
|
||||
"ApplicationPermissionDetail": "Application permission detail",
|
||||
"ApplicationPermissionUpdate": "Application permission update",
|
||||
"RemoteAppUpdate": "Remote app update",
|
||||
"ReplayStorageUpdate": "Replay storage update",
|
||||
"Detail": "Detail",
|
||||
"Activity": "Activity",
|
||||
"SessionDetail": "Sessions detail",
|
||||
"SessionOffline": "Sessions offline",
|
||||
"SessionOnline": "Sessions online",
|
||||
"Sessions": "Sessions",
|
||||
"Settings": "Settings",
|
||||
"SystemUserCreate": "System user create",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserList": "System Users",
|
||||
"SystemUserList": "System users",
|
||||
"SystemUserUpdate": "System user update",
|
||||
"TaskDetail": "Tasks detail",
|
||||
"TaskList": "Tasks",
|
||||
"TaskMonitor": "Task Monitor",
|
||||
"TaskMonitor": "Task monitor",
|
||||
"Terminal": "Terminal",
|
||||
"TicketDetail": "Ticket detail",
|
||||
"TicketCreate": "Ticket create",
|
||||
@@ -727,7 +572,7 @@
|
||||
"UserFirstLogin": "UserFirstLogin",
|
||||
"UserGroupCreate": "User group create",
|
||||
"UserGroupDetail": "User group detail",
|
||||
"UserGroupList": "User Groups",
|
||||
"UserGroupList": "User groups",
|
||||
"UserGroupUpdate": "User group update",
|
||||
"UserGuide": "UserGuide",
|
||||
"UserList": "Users",
|
||||
@@ -735,16 +580,9 @@
|
||||
"UserUpdate": "User update",
|
||||
"Users": "Users",
|
||||
"WebFTP": "WebFTP",
|
||||
"WebTerminal": "Web Terminal",
|
||||
"Notifications": "Notifications",
|
||||
"SiteMessageList": "Site message",
|
||||
"UserLoginACL": "User Login ACL"
|
||||
"WebTerminal": "Web terminal"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "Set to default storage",
|
||||
"SetToDefault": "Set to default",
|
||||
"SetSuccess": "Set success",
|
||||
"SetFailed": "Set failed",
|
||||
"StorageConfiguration": "Storage configuration",
|
||||
"accountKey": "Account key",
|
||||
"accountName": "Account name",
|
||||
@@ -754,7 +592,6 @@
|
||||
"target": "Target",
|
||||
"bucket": "Bucket",
|
||||
"command": "Command",
|
||||
"Activity": "Activity",
|
||||
"commandStorage": "Command storage",
|
||||
"comment": "Comment",
|
||||
"containerName": "Container name",
|
||||
@@ -772,7 +609,6 @@
|
||||
"systemCpuLoad": "cpu load",
|
||||
"systemDiskUsedPercent": "disk used percent",
|
||||
"systemMemoryUsedPercent": "memory used percent",
|
||||
"EsDisabled": "Node is unavailable, please contact administrator",
|
||||
"go": "Go",
|
||||
"goto": "Goto",
|
||||
"hosts": "Hosts",
|
||||
@@ -806,11 +642,10 @@
|
||||
"common": "common"
|
||||
},
|
||||
"Monitor": "Monitor",
|
||||
"XRDPNotSupport": "RDP Client session not support now",
|
||||
"sessionMonitor": "Session Monitor",
|
||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
||||
"helpText": {
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (, ) to split (eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)",
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex":"Es provides the default index: jumpserver",
|
||||
"esDocType": "Es provides the default document type: command",
|
||||
"s3Endpoint": "S3: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn<br>Example: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
@@ -818,30 +653,6 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"Feature": "Feature",
|
||||
"SMSProvider": "SMS provider",
|
||||
"SMS": "SMS setting",
|
||||
"AlibabaCloud": "Alibaba cloud",
|
||||
"TencentCloud": "Tencent cloud",
|
||||
"VerifySignTmpl": "Verification code template",
|
||||
"Radius": "Radius",
|
||||
"Enable": "Enable",
|
||||
"Perm": "Permission",
|
||||
"SMTP": "SMTP server",
|
||||
"Setting": "Setting",
|
||||
"AuthMethod": "Auth methods",
|
||||
"AuthLimit": "Auth limit",
|
||||
"Ops": "Task",
|
||||
"OTP": "OTP(MFA)",
|
||||
"MessageSub": "Message subscription",
|
||||
"Cleaning": "Period clean",
|
||||
"Perms": "授权",
|
||||
"CASSetting": "CAS setting",
|
||||
"Other": "More...",
|
||||
"Auth": "Auth",
|
||||
"SyncSetting": "Sync setting",
|
||||
"Advanced": "Advanced",
|
||||
"InsecureCommandNotifyToSubscription": "Insecure command notification setting, change to system message subscription, support more notify method",
|
||||
"ApiKeyList": "Api key list",
|
||||
"AssetCount": "Asset count",
|
||||
"Basic": "Basic setting",
|
||||
@@ -861,8 +672,6 @@
|
||||
"Security": "Security setting",
|
||||
"SecuritySetting": "Security setting",
|
||||
"SubscriptionID": "Subscription ID",
|
||||
"SystemMessageSubscription": "System messages",
|
||||
"insecureCommandEmailUpdate": "Setting",
|
||||
"Terminal": "Terminal setting",
|
||||
"all": "All",
|
||||
"authLdap": "Enable LDAP auth",
|
||||
@@ -872,13 +681,12 @@
|
||||
"authLdapSearchOu": "User OU",
|
||||
"authLdapServerUri": "LDAP server",
|
||||
"authLdapUserAttrMap": "User attr map",
|
||||
"authCASAttrMap": "User attr map",
|
||||
"unselectedUser": "Unselected user",
|
||||
"auto": "Auto",
|
||||
"basicSetting": "Basic setting",
|
||||
"communityEdition": "Community edition",
|
||||
"consult": "Consult",
|
||||
"CreateUserSetting": "Create User setting",
|
||||
"createUserSetting": "Create User setting",
|
||||
"emailCustomUserCreatedBody": "Create user email content",
|
||||
"emailCustomUserCreatedHonorific": "Create user honorific",
|
||||
"emailCustomUserCreatedSignature": "Signature",
|
||||
@@ -893,15 +701,11 @@
|
||||
"emailTest": "Test connection",
|
||||
"emailUserSSL": "Use SSL",
|
||||
"emailUserTLS": "Use TLS",
|
||||
"MailSend": "Mail send",
|
||||
"LDAPServerInfo": "LDAP Server",
|
||||
"LDAPUser": "LDAP User",
|
||||
"InsecureCommandAlert": "Insecure command alert",
|
||||
"helpText": {
|
||||
"ApiKeyList": "The API key is used to sign the request header. The header of each request is different. Please refer to the usage documentation",
|
||||
"authLdapSearchFilter": "Choice may be (cn|uid|sAMAccountName)=%(user)s)",
|
||||
"authLdapSearchOu": "Use | split User OUs",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username, name, email is jumpserver attr",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username,name,email is jumpserver attr",
|
||||
"emailCustomUserCreatedBody": "Tips:When creating a user, send the content of the email",
|
||||
"emailCustomUserCreatedHonorific": "Tips: When creating a user, send the honorific of the email (eg:Hello)",
|
||||
"emailCustomUserCreatedSignature": "Tips: Email signature (eg:jumpserver)",
|
||||
@@ -973,18 +777,12 @@
|
||||
"refreshLdapCache":"Refreshing Ldap cache ",
|
||||
"LicenseExpired": "License expired",
|
||||
"LicenseWillBe": "License will expire at ",
|
||||
"Expire": "",
|
||||
"WeCom": "WeCom",
|
||||
"DingTalk": "DingTalk",
|
||||
"dingTalkTest": "Test",
|
||||
"weComTest": "Test",
|
||||
"FeiShu": "FeiShu",
|
||||
"feiShuTest": "Test",
|
||||
"Expire": ""
|
||||
},
|
||||
"settings": {
|
||||
"setting": "Setting"
|
||||
},
|
||||
|
||||
"tickets": {
|
||||
"PermissionName": "Permission name",
|
||||
"Accept": "Accept",
|
||||
"AssignedMe": "Assigned me",
|
||||
"Assignee": "Assignee",
|
||||
@@ -1026,13 +824,7 @@
|
||||
"ips": "Enter the IP address group, separated by commas",
|
||||
"fuzzySearch": "Support for fuzzy search",
|
||||
"application": "Enter the application group, separated by commas"
|
||||
},
|
||||
"ApplyRunUser": "Apply run user",
|
||||
"ApplyRunAsset": "Apply run asset",
|
||||
"ApplyRunSystemUser": "Apply run system user",
|
||||
"ApplyRunCommand": "Apply run command",
|
||||
"ApplyFromSession": "Session",
|
||||
"ApplyFromCMDFilterRule": "Command filter rule"
|
||||
}
|
||||
},
|
||||
"tree": {
|
||||
"AddAssetToNode": "Add asset to node",
|
||||
@@ -1048,9 +840,6 @@
|
||||
"UpdateNodeAssetHardwareInfo": "Update node asset hardware information"
|
||||
},
|
||||
"users": {
|
||||
"MessageSubscription": "Message Subscription",
|
||||
"AuthSettings": "Auth settings",
|
||||
"UserName": "Name",
|
||||
"Account": "Account",
|
||||
"Existing":"Existing",
|
||||
"Authentication": "Account",
|
||||
@@ -1060,16 +849,9 @@
|
||||
"DateJoined": "Date joined",
|
||||
"DateLastLogin": "Date last login",
|
||||
"DatePasswordLastUpdated": "Date password last updated",
|
||||
"setWeCom": "Set wecom login",
|
||||
"setDingTalk": "Set dingtalk login",
|
||||
"setFeiShu": "Set feishu login",
|
||||
"DatePasswordUpdated": "Date password updated",
|
||||
"DescribeOfGuide": "Welcome to JumpServer. Click here for more information",
|
||||
"Email": "Email",
|
||||
"Phone": "Phone",
|
||||
"WeCom": "WeCom",
|
||||
"DingTalk": "DingTalk",
|
||||
"FeiShu": "FeiShu",
|
||||
"FingerPrint": "Fingerprint",
|
||||
"FirstLogin": "First login",
|
||||
"InviteUser": "Invite user",
|
||||
@@ -1121,16 +903,12 @@
|
||||
"resetMFAWarningMsg": "This will reset the user MFA setting, user can reset it",
|
||||
"resetMFAdSuccessMsg": "Reset MFA success",
|
||||
"resetPassword": "Reset password",
|
||||
"resetPasswordSuccessMsg": "A password reset message has been sent to the user",
|
||||
"resetPasswordSuccessMsg": "An e-mail has been sent to the user`s mailbox",
|
||||
"resetPasswordWarningMsg": "This will reset the user password and send a reset mail",
|
||||
"resetSSHKey": "Reset SSH key",
|
||||
"resetSSHKeySuccessMsg": "An e-mail has been sent to the user`s mailbox",
|
||||
"resetSSHKeyWarningMsg": "This will reset the user public key and send a reset mail",
|
||||
"resetWechat": "Reset Wechat",
|
||||
"resetWechatLoginWarningMsg": "This will reset the user Wechat setting, user can reset it",
|
||||
"resetWechatLoginSuccessMsg": "Reset Wechat success",
|
||||
"send": "Send",
|
||||
"unbind": "Unbind",
|
||||
"unblock": "Unblock",
|
||||
"unblockSuccessMsg": "Account has unblocked",
|
||||
"unblockUser": "Unblock login"
|
||||
@@ -1148,55 +926,26 @@
|
||||
"ApplicationPermissionRules": "Application permission rules",
|
||||
"remoteAppPermissionRules": "Remote app permission rules"
|
||||
},
|
||||
"needUpdatePasswordNextLogin": "Update password next login",
|
||||
"UpdatePassword": "Update password",
|
||||
"SetPublicKey": "Set public key",
|
||||
"UpdatePassword": "",
|
||||
"UpdatePublicKey": "",
|
||||
"passwordExpired": "Password expired",
|
||||
"passwordWillExpiredPrefixMsg": "The password will expire in ",
|
||||
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible."
|
||||
},
|
||||
"notifications": {
|
||||
"MessageType": "Message Type",
|
||||
"Receivers": "Receivers",
|
||||
"Subscription": "Subscription",
|
||||
"ChangeReceiver": "Change Receivers",
|
||||
"Subject": "Subject",
|
||||
"Message": "Message",
|
||||
"DeliveryTime": "Delivery time",
|
||||
"HasRead": "Has read",
|
||||
"Sender": "Sender",
|
||||
"MarkAsRead": "Mark as read",
|
||||
"NoUnreadMsg": "No unread messages",
|
||||
"SiteMessage": "Site messages",
|
||||
"SMS": "SMS"
|
||||
},
|
||||
"xpack": {
|
||||
"Admin": "Admin",
|
||||
"Asset": "Asset",
|
||||
"Database": "Database",
|
||||
"AssetCount": "Asset count",
|
||||
"Auditor": "Auditor",
|
||||
"ChangeAuthPlan": {
|
||||
"AddAsset": "Add asset",
|
||||
"AddNode": "Add node",
|
||||
"AddSystemUser": "Add systemuser",
|
||||
"Asset": "Asset",
|
||||
"Database": "Database",
|
||||
"DatabaseId": "Database Id",
|
||||
"SystemUser": "SystemUser",
|
||||
"SystemUserId": "SystemUser Id",
|
||||
"AssetAmount": "Asset",
|
||||
"AssetAndNode": "Asset and Node",
|
||||
"ChangeAuthPlan": "Change Auth Plan",
|
||||
"AssetChangeAuthPlan": "Asset Change Auth Plan",
|
||||
"AppChangeAuthPlan": "App Change Auth Plan",
|
||||
"AssetChangeAuthPlanCreate": "Create Asset change auth plan",
|
||||
"AppChangeAuthPlanCreate": "Create App change auth plan",
|
||||
"AssetChangeAuthPlanUpdate": "Update Asset change auth plan",
|
||||
"AppChangeAuthPlanUpdate": "Update App change auth plan",
|
||||
"SymbolSet": "Special symbol set",
|
||||
"SymbolSetHelpText": "Please enter the special symbol set supported by this type of database. If there are special characters in the generated random password that are not supported by this type of database, the password change plan will fail",
|
||||
"ChangeAuthPlan": "Change auth plan",
|
||||
"ChangeAuthPlanCreate": "Create change auth plan",
|
||||
"ChangeAuthPlanUpdate": "Update change auth plan",
|
||||
"CyclePerform": "Cycle perform",
|
||||
"DateJoined": "Date joined",
|
||||
"DateStart": "Date start",
|
||||
@@ -1230,18 +979,12 @@
|
||||
"Username": "Username"
|
||||
},
|
||||
"Cloud": {
|
||||
"ServerAccountKey": "Server Account Key",
|
||||
"IPNetworkSegment": "Ip Network Segment",
|
||||
"Aliyun": "Ali Cloud",
|
||||
"Qcloud": "Tencent Cloud",
|
||||
"QingyunPrivatecloud": "Qingyun Private Cloud",
|
||||
"HuaweiPrivatecloud": "Huawei Private Cloud",
|
||||
"GCP": "Google Cloud Platform",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
"HuaweiCloud": "Huawei Cloud",
|
||||
"Azure":"Azure(China)",
|
||||
"Azure_Int": "Azure(International)",
|
||||
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo);2. Instance name and Partial IP (instanceDemo-250.1)",
|
||||
"IsAlwaysUpdate": "Asset info is kept up-to-date",
|
||||
"AccountCreate": "Create account",
|
||||
@@ -1249,7 +992,7 @@
|
||||
"AccountUpdate": "Update account",
|
||||
"AccountDetail": "Account detail",
|
||||
"Cloud": "Cloud center",
|
||||
"CloudCenter": "Cloud Center",
|
||||
"CloudCenter": "Cloud center",
|
||||
"Provider": "Provider",
|
||||
"Validity": "Validity",
|
||||
"IsAlwaysUpdateHelpTips": "Whether the asset information, including Hostname, IP, Platform, and AdminUser, is updated synchronously each time a synchronization task is performed",
|
||||
@@ -1290,18 +1033,16 @@
|
||||
"Execute": "Execute",
|
||||
"Expired": "Expired",
|
||||
"GatherUser": {
|
||||
"GatherUser": "Gather User",
|
||||
"GatherUser": "Gather user",
|
||||
"GatherUserList": "Gather user",
|
||||
"GatherUserTaskCreate": "Create gather user task",
|
||||
"GatherUserTaskList": "Gather user task list",
|
||||
"GatherUserTaskUpdate": "Update gather user task",
|
||||
"GatherUserTaskDetail": "Gather user detail",
|
||||
"GatherUserTaskExecutionList": "Gather user task execution list"
|
||||
"GatherUserTaskUpdate": "Update gather user task"
|
||||
},
|
||||
"Import": "Import",
|
||||
"ImportLicense": "Import license",
|
||||
"ImportLicenseTip": "Please Import License",
|
||||
"InterfaceSettings": "Interface Setting",
|
||||
"InterfaceSettings": "Interface setting",
|
||||
"License": "License",
|
||||
"LicenseDetail": "License detail",
|
||||
"SystemMonitor": "System Monitor",
|
||||
@@ -1317,7 +1058,7 @@
|
||||
"Organization": {
|
||||
"OrganizationCreate": "Create organization",
|
||||
"OrganizationDetail": "Org detail",
|
||||
"OrganizationList": "Organizations",
|
||||
"OrganizationList": "Organlizations",
|
||||
"OrganizationUpdate": "Update org",
|
||||
"OrganizationMembership": "Organization membership",
|
||||
"DeleteOrgTitle":"Please ensure that the following information in the organization has been deleted",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user