Compare commits

..

5 Commits
v3.7 ... v3.5.4

Author SHA1 Message Date
fit2bot
7aa40de518 feat: Update v3.5.4 2023-08-15 11:34:29 +08:00
老广
17fd374e0a Merge pull request #3322 from jumpserver/pr@v3.5@fix_msg_subscribe_for_xss
fix: 修复系统设置 > 消息订阅 > 修改订阅人 因为用户名导致的 xss
2023-08-08 16:17:09 +08:00
ibuler
df5d15c1e5 fix: 修复系统设置 > 消息订阅 > 修改订阅人 因为用户名导致的 xss 2023-08-08 03:08:02 +00:00
“huailei000”
b58c21a79f perf: 命令记录列表增加账号字段 2023-08-03 16:16:38 +08:00
“huailei000”
3d81f92667 perf: 修改github配置 2023-07-21 14:56:38 +08:00
449 changed files with 4201 additions and 8533 deletions

View File

@@ -1,28 +1,13 @@
FROM node:16.17.1-bullseye-slim as stage-build
FROM node:14.16 as stage-build
ARG TARGETARCH
ARG DEPENDENCIES=" \
g++ \
make \
python3"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=lina \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get install -y --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash \
&& rm -rf /var/lib/apt/lists/*
ARG NPM_REGISTRY="https://registry.npmmirror.com"
WORKDIR /data
RUN set -ex \
&& npm config set registry ${NPM_REGISTRY} \
&& yarn config set registry ${NPM_REGISTRY}
WORKDIR /data
ADD package.json yarn.lock /data
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=lina \
yarn install
@@ -34,6 +19,6 @@ RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=lin
sed -i "s@version-dev@${VERSION}@g" src/layout/components/NavHeader/About.vue \
&& yarn build
FROM nginx:1.24-bullseye
FROM nginx:1.24
COPY --from=stage-build /data/lina /opt/lina
COPY nginx.conf /etc/nginx/conf.d/default.conf

1
GITSHA Normal file
View File

@@ -0,0 +1 @@
17fd374e0adc7a7a9992dfe4f86fa4125974ec85

View File

@@ -44,11 +44,3 @@ export function renameFile(playbookId, node) {
data: node
})
}
export function createJob(form) {
return request({
url: '/api/v1/ops/jobs/',
method: 'post',
data: form
})
}

View File

@@ -8,11 +8,24 @@ export function terminateSession(data) {
})
}
export function toggleLockSession(data) {
export function getSessionDetail(id) {
return request({
url: '/api/v1/terminal/tasks/toggle-lock-session/',
method: 'post',
data: data
url: `/api/v1/terminal/sessions/${id}/`,
method: 'get'
})
}
export function getSessionCommands(id) {
return request({
url: `/api/v1/terminal/commands/?session_id=${id}`,
method: 'get'
})
}
export function getTerminalDetail(id) {
return request({
url: `/api/v1/terminal/terminals/${id}/`,
method: 'get'
})
}

View File

@@ -8,12 +8,12 @@
</template>
<script>
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
import { UpdateToken, UploadSecret } from '@/components/Form/FormFields'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
import AutoDataForm from '@/components/AutoDataForm'
import { UpdateToken, UploadSecret } from '@/components/FormFields'
import Select2 from '@/components/FormFields/Select2'
import AssetSelect from '@/components/AssetSelect'
import { encryptPassword } from '@/utils/crypto'
import { Required, RequiredChange } from '@/components/Form/DataForm/rules'
import { Required, RequiredChange } from '@/components/DataForm/rules'
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
export default {
@@ -214,12 +214,11 @@ export default {
},
push_now: {
helpText: this.$t('accounts.AccountPush.WindowsPushHelpText'),
hidden: (formValue) => {
hidden: () => {
const automation = this.iPlatform.automation || {}
return !automation.push_account_enabled ||
!automation.ansible_enabled ||
!this.$hasPerm('accounts.push_account') ||
(formValue.secret_type === 'ssh_key' && this.iPlatform.type.value === 'windows') ||
this.addTemplate
}
},
@@ -305,9 +304,6 @@ export default {
form.secret = form[secretType]
form.secret = this.encryptPassword ? encryptPassword(form.secret) : form.secret
//
delete form[secretType]
if (!form.secret) {
delete form['secret']
}

View File

@@ -24,8 +24,8 @@
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import AccountCreateUpdateForm from '@/components/Apps/AccountCreateUpdateForm/index.vue'
import Dialog from '@/components/Dialog'
import AccountCreateUpdateForm from '@/components/AccountCreateUpdateForm'
export default {
name: 'CreateAccountDialog',

View File

@@ -41,11 +41,11 @@
</template>
<script>
import ListTable from '@/components/Table/ListTable/index.vue'
import { ActionsFormatter } from '@/components/Table/TableFormatters'
import ViewSecret from './ViewSecret.vue'
import UpdateSecretInfo from './UpdateSecretInfo.vue'
import AccountCreateUpdate from './AccountCreateUpdate.vue'
import ListTable from '@/components/ListTable/index'
import { ActionsFormatter } from '@/components/TableFormatters'
import ViewSecret from './ViewSecret'
import UpdateSecretInfo from './UpdateSecretInfo'
import AccountCreateUpdate from './AccountCreateUpdate'
import { connectivityMeta } from './const'
import { openTaskPage } from '@/utils/jms'
import ResultDialog from './BulkCreateResultDialog.vue'
@@ -111,7 +111,7 @@ export default {
type: Array,
default: () => ([
'name', 'username', 'asset', 'privileged',
'secret_type', 'is_active', 'date_updated'
'secret_type', 'date_updated'
])
},
headerExtraActions: {
@@ -142,6 +142,10 @@ export default {
order: '-date_updated'
},
columnsExclude: ['spec_info'],
columns: [
'name', 'username', 'asset', 'privileged',
'secret_type', 'source', 'actions'
],
columnsShow: {
min: ['name', 'username', 'actions'],
default: this.columnsDefault

View File

@@ -15,7 +15,7 @@
<script>
import Dialog from '@/components/Dialog/index.vue'
import DataTable from '@/components/Table/DataTable/index.vue'
import DataTable from '@/components/DataTable/index.vue'
export default {
name: 'ResultDialog',

View File

@@ -4,7 +4,7 @@
<script>
import { GenericListTableDialog } from '@/layout/components'
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
import { ShowKeyCopyFormatter } from '@/components/TableFormatters'
export default {
components: {
@@ -29,7 +29,7 @@ export default {
tableConfig: {
id: 'history_date',
url: `/api/v1/accounts/account-secrets/${this.account.id}/histories/`,
columns: ['secret', 'version', 'history_date'],
columns: ['secret', 'secret_type', 'version', 'history_date'],
columnsMeta: {
secret: {
label: this.$t('assets.Password'),

View File

@@ -29,8 +29,8 @@
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import { UpdateToken, UploadKey } from '@/components/Form/FormFields'
import Dialog from '@/components/Dialog'
import { UpdateToken, UploadKey } from '@/components/FormFields'
import { encryptPassword } from '@/utils/crypto'
export default {

View File

@@ -65,10 +65,10 @@
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
import UserConfirmDialog from '@/components/Apps/UserConfirmDialog/index.vue'
import { ShowKeyCopyFormatter } from '@/components/Table/TableFormatters'
import Dialog from '@/components/Dialog'
import PasswordHistoryDialog from './PasswordHistoryDialog'
import UserConfirmDialog from '@/components/UserConfirmDialog'
import { ShowKeyCopyFormatter } from '@/components/TableFormatters'
import { encryptPassword } from '@/utils/crypto'
export default {

View File

@@ -1,5 +1,5 @@
import i18n from '@/i18n/i18n'
import { ChoicesFormatter } from '@/components/Table/TableFormatters'
import { ChoicesFormatter } from '@/components/TableFormatters'
export const connectivityMeta = {
label: i18n.t('assets.Connectivity'),

View File

@@ -7,7 +7,7 @@
:title="title"
@close="onClose"
>
<MarkDown class="markdown" :value="announcement.content" />
<span class="announcement-main">{{ announcement.content }}</span>
<span v-if="announcement.link">
<el-link :href="announcement.link" target="_blank" type="info" class="link-more">
{{ $t('common.ViewMore') }}
@@ -19,11 +19,9 @@
<script>
import { mapGetters } from 'vuex'
import MarkDown from '@/components/Widgets/MarkDown'
export default {
name: 'Announcement',
components: { MarkDown },
data() {
return {
viewedKey: 'AnnouncementViewed'
@@ -56,7 +54,7 @@ export default {
}
</script>
<style lang="scss" scoped>
<style scoped>
.announcement >>> .el-alert__content {
width: 100%;
}
@@ -72,14 +70,4 @@ export default {
.icon {
vertical-align: text-bottom;
}
>>> .markdown-body {
background-color: transparent !important;
a {
color: var(--color-info) !important;
}
h1, h2, h3, h4, h5 {
margin-top: 0;
margin-bottom: 10px;
}
}
</style>

View File

@@ -1,97 +0,0 @@
<template>
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import ListTable from '@/components/Table/ListTable/index.vue'
export default {
name: 'BlockedIPList',
components: {
ListTable
},
props: {
object: {
type: Object,
required: false,
default: () => ({})
}
},
data() {
const vm = this
return {
tableConfig: {
url: '/api/v1/settings/security/block-ip/',
columns: [
'ip', 'actions'
],
columnsMeta: {
ip: {
label: this.$t('assets.ip')
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'UnlockIP',
title: this.$t('setting.Unblock'),
can: this.$hasPerm('settings.change_security'),
type: 'primary',
callback: ({ row }) => {
this.$axios.post(
'/api/v1/settings/security/unlock-ip/',
{ ips: [row.ip] }
).then(() => {
vm.$message.success(this.$tc('common.UnlockSuccessMsg'))
vm.$refs.ListTable.reloadTable()
})
}
}
]
}
}
}
},
headerActions: {
hasExport: false,
hasImport: false,
hasCreate: false,
hasSearch: false,
hasRefresh: true,
hasBulkDelete: false,
hasBulkUpdate: false,
hasLeftActions: true,
hasRightActions: true,
extraMoreActions: [
{
name: 'UnlockSelected',
title: this.$t('setting.BulkUnblock'),
type: 'primary',
can: ({ selectedRows }) => {
return selectedRows.length > 0
},
callback: function({ selectedRows }) {
vm.$axios.post(
'/api/v1/settings/security/unlock-ip/',
{
ips: selectedRows.map(v => { return v.ip })
}
).then(res => {
vm.$message.success(vm.$tc('common.UnlockSuccessMsg'))
vm.$refs.ListTable.reloadTable()
})
}
}
]
}
}
}
}
</script>
<style lang='less' scoped>
</style>

View File

@@ -1,75 +0,0 @@
<template>
<div>
<div>
<el-button
size="mini"
type="primary"
@click="onOpenDialog"
>{{ $tc('common.View') }}</el-button>
</div>
<Dialog
v-if="visible"
:visible.sync="visible"
:title="title"
width="40%"
:show-cancel="false"
:show-confirm="false"
:destroy-on-close="true"
v-bind="$attrs"
v-on="$listeners"
>
<BlockedIPList />
</Dialog>
</div>
</template>
<script>
import { Dialog } from '@/components'
import BlockedIPList from '@/components/Apps/BlockedIPs/BlockedIPList'
export default {
componentName: 'BlockedIPs',
components: {
BlockedIPList,
Dialog
},
props: {
value: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: function() {
return this.$t('setting.BlockedIPS')
}
},
url: {
type: String,
default: `/api/v1/assets/platform-automation-methods/`
}
},
data() {
return {
remoteMeta: {},
visible: false,
form: this.value,
config: {
url: this.url,
hasSaveContinue: false,
hasButtons: true,
fields: [],
fieldsMeta: {}
}
}
},
methods: {
onOpenDialog() {
this.visible = true
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,14 +1,14 @@
<template>
<IBox :fa="icon" :title="title" :type="type" v-bind="$attrs">
<IBox :fa="icon" :type="type" :title="title" v-bind="$attrs">
<table style="width: 100%">
<tr>
<td colspan="2">
<AssetSelect ref="assetSelect" :can-select="canSelect" :disabled="disabled" />
<AssetSelect ref="assetSelect" :disabled="disabled" :can-select="canSelect" />
</td>
</tr>
<tr>
<td colspan="2">
<el-button :disabled="disabled" :type="type" size="small" @click="addObjects">{{ $t('common.Add') }}</el-button>
<el-button :type="type" size="small" :disabled="disabled" @click="addObjects">{{ $t('common.Add') }}</el-button>
</td>
</tr>
</table>
@@ -16,8 +16,8 @@
</template>
<script>
import IBox from '@/components/IBox/index.vue'
import AssetSelect from '@/components/Apps/AssetSelect/index.vue'
import IBox from '@/components/IBox'
import AssetSelect from '@/components/AssetSelect'
export default {
name: 'AssetRelationCard',

View File

@@ -24,8 +24,8 @@
</template>
<script>
import AssetTreeTable from '@/components/Apps/AssetTreeTable/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import AssetTreeTable from '@/components/AssetTreeTable'
import Dialog from '@/components/Dialog'
export default {
componentName: 'AssetSelectDialog',

View File

@@ -25,7 +25,7 @@
</template>
<script>
import Select2 from '@/components/Form/FormFields/Select2.vue'
import Select2 from '@/components/FormFields/Select2'
import AssetSelectDialog from './dialog.vue'
import { b } from 'css-color-function/lib/adjusters'

View File

@@ -1,10 +1,10 @@
<template>
<TreeTable
ref="TreeList"
:active-menu.sync="treeTableConfig.activeMenu"
:table-config="tableConfig"
:tree-tab-config="treeTableConfig"
component="TabTree"
:table-config="tableConfig"
:active-menu.sync="treeTableConfig.activeMenu"
:tree-tab-config="treeTableConfig"
v-bind="$attrs"
v-on="$listeners"
>
@@ -12,13 +12,13 @@
<slot name="table" />
</template>
<div slot="rMenu" slot-scope="{data}">
<slot :data="data" name="rMenu" />
<slot name="rMenu" :data="data" />
</div>
</TreeTable>
</template>
<script>
import TreeTable from '../../Table/TreeTable/index.vue'
import TreeTable from '../TreeTable'
import { setRouterQuery, setUrlParam } from '@/utils/common'
import $ from '@/utils/jquery-vendor'

View File

@@ -13,7 +13,7 @@
</template>
<script>
import DataForm from '@/components/Form/DataForm/index.vue'
import DataForm from '@/components/DataForm'
export default {
name: 'NestedField',

View File

@@ -23,9 +23,9 @@
</template>
<script>
import DataForm from '../DataForm/index.vue'
import FormGroupHeader from '@/components/Form/FormGroupHeader/index.vue'
import { FormFieldGenerator } from '@/components/Form/AutoDataForm/utils'
import DataForm from '../DataForm'
import FormGroupHeader from '@/components/FormGroupHeader'
import { FormFieldGenerator } from '@/components/AutoDataForm/utils'
export default {
name: 'AutoDataForm',
@@ -67,9 +67,6 @@ export default {
}
},
computed: {
dataForm() {
return this.$refs.dataForm
},
iForm() {
const iForm = {}
Object.entries(this.form).forEach(([key, value]) => {

View File

@@ -1,13 +1,13 @@
import Vue from 'vue'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import ObjectSelect2 from '@/components/Form/FormFields/NestedObjectSelect2.vue'
import NestedField from '@/components/Form/AutoDataForm/components/NestedField.vue'
import Switcher from '@/components/Form/FormFields/Switcher.vue'
import rules from '@/components/Form/DataForm/rules'
import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import Select2 from '@/components/FormFields/Select2'
import ObjectSelect2 from '@/components/FormFields/NestedObjectSelect2'
import NestedField from '@/components/AutoDataForm/components/NestedField'
import Switcher from '@/components/FormFields/Switcher'
import rules from '@/components/DataForm/rules'
import BasicTree from '@/components/FormFields/BasicTree'
import JsonEditor from '@/components/FormFields/JsonEditor'
import { assignIfNot } from '@/utils/common'
import TagInput from '@/components/Form/FormFields/TagInput.vue'
import TagInput from '@/components/FormFields/TagInput.vue'
export class FormFieldGenerator {
constructor(emit) {

View File

@@ -3,9 +3,8 @@
</template>
<script>
import TagSearch from '@/components/Table/TagSearch/index.vue'
import TagSearch from '@/components/TagSearch'
import i18n from '@/i18n/i18n'
export default {
name: 'AutoDataSearch',
components: {

View File

@@ -36,7 +36,7 @@
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import Dialog from '@/components/Dialog/index'
export default {
name: 'ColumnSettingPopover',

View File

@@ -20,14 +20,19 @@
</template>
<script type="text/jsx">
import DataTable from '@/components/Table/DataTable/index.vue'
import DataTable from '../DataTable'
import {
ActionsFormatter, ArrayFormatter, ChoicesFormatter, DateFormatter, DetailFormatter, DisplayFormatter,
ActionsFormatter,
ArrayFormatter,
ChoicesFormatter,
DateFormatter,
DetailFormatter,
DisplayFormatter,
ObjectRelatedFormatter
} from '@/components/Table/TableFormatters'
} from '@/components/TableFormatters'
import i18n from '@/i18n/i18n'
import { newURL, replaceAllUUID } from '@/utils/common'
import ColumnSettingPopover from './components/ColumnSettingPopover.vue'
import ColumnSettingPopover from './components/ColumnSettingPopover'
export default {
name: 'AutoDataTable',

View File

@@ -16,7 +16,7 @@
</template>
<script>
import DataZTree from '../DataZTree/index.vue'
import DataZTree from '../DataZTree'
import $ from '@/utils/jquery-vendor'
import { mapGetters } from 'vuex'

View File

@@ -2,23 +2,21 @@
<div>
<div>
<el-button
:disabled="isDisabled"
size="mini"
type="primary"
:disabled="isDisabled"
@click="onOpenDialog"
>
{{ $tc('common.Setting') }}
</el-button>
>{{ $tc('common.Setting') }}</el-button>
</div>
<Dialog
v-if="visible"
:destroy-on-close="true"
width="60%"
:visible.sync="visible"
:title="title"
:show-cancel="false"
:show-confirm="false"
:title="title"
:visible.sync="visible"
:destroy-on-close="true"
v-bind="$attrs"
width="60%"
v-on="$listeners"
>
<AutoDataForm
@@ -33,8 +31,7 @@
</template>
<script>
import Dialog from '../../Dialog'
import AutoDataForm from '../../Form/AutoDataForm'
import { Dialog, AutoDataForm } from '@/components'
export default {
componentName: 'AutomationParams',
@@ -61,10 +58,6 @@ export default {
type: Array,
default: () => []
},
platforms: {
type: Array,
default: () => []
},
method: {
type: String,
default: ''
@@ -75,12 +68,13 @@ export default {
}
},
data() {
const vm = this
return {
remoteMeta: {},
visible: false,
isDisabled: true,
form: this.value,
node_ids: this.nodes,
asset_ids: this.assets,
config: {
url: this.url,
hasSaveContinue: false,
@@ -88,8 +82,7 @@ export default {
method: 'get',
fields: [],
fieldsMeta: {}
},
onFieldChangeHandler: _.debounce(vm.handleFieldChange, 1000)
}
}
},
computed: {
@@ -99,27 +92,22 @@ export default {
},
watch: {
nodes: {
handler() {
this.onFieldChangeHandler()
handler(val) {
this.node_ids = val
this.onFieldChangeHandle()
},
deep: true
},
assets: {
handler() {
this.onFieldChangeHandler()
handler(val) {
this.asset_ids = val
this.onFieldChangeHandle()
},
deep: true
},
platforms: {
handler(newVal) {
this.onFieldChangeHandler()
},
deep: true,
immediate: true
}
},
async mounted() {
await this.getUrlMeta()
created() {
this.getUrlMeta()
},
methods: {
async getUrlMeta() {
@@ -130,31 +118,31 @@ export default {
return await this.$axios.post(
'/api/v1/assets/platforms/filter-nodes-assets/',
{
'node_ids': this.nodes,
'asset_ids': this.assets,
'platform_ids': this.platforms.map(i => i.id || i.pk || i)
'node_ids': this.node_ids,
'asset_ids': this.asset_ids
}
)
},
async handleFieldChange() {
async onFieldChangeHandle() {
const platforms = await this.getFilterPlatforms()
let pushAccountMethods = platforms.map(i => i.automation[this.method])
pushAccountMethods = _.uniq(pushAccountMethods)
//
const hasCanSettingPushMethods = _.intersection(pushAccountMethods, Object.keys(this.remoteMeta))
this.setFormConfig(hasCanSettingPushMethods)
this.isDisabled = hasCanSettingPushMethods.length <= 0
if (hasCanSettingPushMethods.length > 0) {
this.isDisabled = false
this.$emit('input', this.form)
} else {
this.isDisabled = true
this.$emit('input', {})
}
},
setFormConfig(methods) {
const newForm = {}
const fields = []
const fieldsMeta = {}
this.config.fields = []
// Todo: serializer,
const typeMapper = {
'string': 'input',
'boolean': 'switch'
}
for (const method of methods) {
const filterField = this.remoteMeta[method] || {}
@@ -171,7 +159,7 @@ export default {
for (const [k, v] of Object.entries(filterField.children)) {
const item = {
...v,
type: typeMapper[v.type] || 'input'
type: 'input'
}
delete item.default
fieldsMeta[method].fields.push(k)
@@ -188,11 +176,8 @@ export default {
this.visible = true
},
onSubmit(form) {
this.form = form
this.visible = false
this.$emit('input', form)
setTimeout(() => {
this.visible = false
}, 100)
this.$log.debug('Auto push form:', form)
}
}

View File

@@ -25,9 +25,7 @@
multiple
style="width:100%"
>
<el-option v-for="(item,index) of weekList" :key="index" :value="index === 6 ? 0 : (index + 1)">
{{ item }}
</el-option>
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{ item }}</el-option>
</el-select>
</el-radio>
</el-form-item>

View File

@@ -38,11 +38,11 @@
v-if="defaultButton"
:disabled="!canSubmit"
:loading="isSubmitting"
:size="submitBtnSize"
size="small"
type="primary"
@click="submitForm('form')"
>
{{ submitBtnText }}
{{ $t('common.Submit') }}
</el-button>
</el-form-item>
</ElFormRender>
@@ -73,16 +73,6 @@ export default {
type: Boolean,
default: true
},
submitBtnSize: {
type: String,
default: 'small'
},
submitBtnText: {
type: String,
default() {
return this.$t('common.Submit')
}
},
hasSaveContinue: {
type: Boolean,
default: true
@@ -136,8 +126,8 @@ export default {
})
},
//
resetForm() {
this.$refs['form'].resetFields()
resetForm(formName) {
this.$refs[formName].resetFields()
},
handleClick(button) {
const callback = button.callback || function(values, form) {
@@ -148,7 +138,6 @@ export default {
callback(values, form, button)
},
getFormValue() {
return this.$refs.form.getFormValue()
}
}
}

View File

@@ -1216,7 +1216,7 @@ export default {
},
// table
// https://github.com/PanJiaChen/vue-element-admin/tree/master/@/components/TreeTable
// https://github.com/PanJiaChen/vue-element-admin/tree/master/src/components/TreeTable
tree2Array(data, expandAll, parent = null, level = null) {
let tmp = []
data.forEach(record => {

View File

@@ -46,7 +46,7 @@ import $ from '@/utils/jquery-vendor.js'
import '@ztree/ztree_v3/js/jquery.ztree.all.min.js'
import '@ztree/ztree_v3/js/jquery.ztree.exhide.min.js'
import '@/styles/ztree.css'
import '@/styles/ztree_icon.scss'
import '@/styles/ztree_icon.css'
import axiosRetry from 'axios-retry'
const defaultObject = {

View File

@@ -2,14 +2,13 @@
<ZTree ref="ztree" :setting="treeSetting" v-on="$listeners">
<!--Slot透传-->
<div slot="rMenu" slot-scope="{data}">
<slot :data="data" name="rMenu" />
<slot name="rMenu" :data="data" />
</div>
</ZTree>
</template>
<script>
import ZTree from './components/ZTree/index.vue'
import ZTree from './components/ZTree'
export default {
name: 'DataZTree',
components: {

View File

@@ -3,7 +3,7 @@
</template>
<script>
import DetailCard from './index.vue'
import DetailCard from './index'
import { copy, toSafeLocalDateStr } from '@/utils/common'
export default {

View File

@@ -1,8 +1,8 @@
<template>
<IBox :fa="fa" :title="title">
<IBox :title="title" :fa="fa">
<el-form class="content" label-position="left" label-width="25%">
<el-form-item v-for="item in items" :key="item.key" :label="item.key">
<ItemValue :value="item.value" class="item-value" v-bind="item" />
<ItemValue class="item-value" :value="item.value" v-bind="item" />
</el-form-item>
</el-form>
<slot />
@@ -10,8 +10,8 @@
</template>
<script>
import IBox from '../../IBox/index.vue'
import ItemValue from './ItemValue.vue'
import IBox from '../IBox'
import ItemValue from './ItemValue'
export default {
name: 'DetailCard',

View File

@@ -1,62 +0,0 @@
<template>
<div>
<GenericCreateUpdateForm
class="attr-form"
v-bind="formConfig"
@submit="onSubmit"
/>
<DataTable :config="tableConfig" class="attr-list" />
</div>
</template>
<script>
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm'
import DataTable from '@/components/Table/DataTable/index.vue'
export default {
name: 'AttrInput',
components: { DataTable, GenericCreateUpdateForm },
props: {
formConfig: {
type: Object,
default: () => ({})
},
tableConfig: {
type: Object,
default: () => ({})
},
beforeSubmit: {
type: Function,
default: (val) => { return true }
}
},
data() {
return {}
},
methods: {
onSubmit(value) {
if (this.beforeSubmit(value)) {
const clonedValue = JSON.parse(JSON.stringify(value))
this.tableConfig.totalData.push(clonedValue)
this.$emit('submit', this.tableConfig.totalData)
}
}
}
}
</script>
<style lang="scss" scoped>
.attr-form {
>>> .el-select {
width: 100%;
}
>>> .el-form-item__content {
width: 100%;
}
>>> .form-buttons {
margin: auto;
}
}
</style>

View File

@@ -1,101 +0,0 @@
<template>
<div style="display: block">
<el-button size="mini" type="primary" @click="visible=true">
{{ $t('common.Setting') }}
</el-button>
<Dialog
:destroy-on-close="true"
:title="$tc('common.PasswordRule')"
:visible.sync="visible"
width="600px"
@cancel="handleCancel"
@confirm="handleConfirm"
@open="handleOpen"
>
<AutoDataForm ref="dataform" v-bind="form" />
</Dialog>
</div>
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
export default {
name: 'PasswordRule',
components: { Dialog, AutoDataForm },
props: {
value: {
type: Object,
default: () => ({
length: 16
})
}
},
data() {
return {
visible: false,
form: {
url: '',
hasButtons: false,
hasReset: false,
hasSaveContinue: false,
form: Object.assign({}, this.value),
fields: [
{
id: 'length',
label: this.$t('common.Length'),
type: 'input-number',
el: {
min: 8,
max: 30
}
},
{
id: 'uppercase',
label: this.$t('common.Uppercase'),
type: 'switch'
},
{
id: 'lowercase',
label: this.$t('common.Lowercase'),
type: 'switch'
},
{
id: 'digit',
label: this.$t('common.Digit'),
type: 'switch'
},
{
id: 'symbol',
label: this.$t('common.SpecialSymbol'),
type: 'switch'
}
]
}
}
},
methods: {
handleConfirm() {
const formValue = this.$refs.dataform.dataForm.getFormValue()
this.form.form = formValue
this.$emit('input', formValue)
setTimeout(() => {
this.visible = false
}, 100)
},
handleCancel() {
this.$refs.dataform.dataForm.resetForm()
setTimeout(() => {
this.visible = false
}, 100)
},
handleOpen() {
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,31 +0,0 @@
<template>
<div class="input-text">
{{ value.toString() }}
</div>
</template>
<script>
export default {
props: {
value: {
type: [String, Boolean],
default: () => false
}
},
data() {
return {}
}
}
</script>
<style lang='scss' scoped>
.input-text {
border: solid 1px #dcdfe6;
line-height: 32px;
padding-left: 5px;
height: 32px;
margin-top: 4px;
font-size: 13px;
}
</style>

View File

@@ -1,69 +0,0 @@
import Link from './Link.vue'
import Select2 from './Select2.vue'
import TagInput from './TagInput.vue'
import Switcher from './Switcher.vue'
import AttrInput from './AttrInput.vue'
import UploadKey from './UploadKey.vue'
import JsonEditor from './JsonEditor.vue'
import PhoneInput from './PhoneInput.vue'
import UploadField from './UploadField.vue'
import UpdateToken from './UpdateToken.vue'
import UserPassword from './UserPassword.vue'
import UploadSecret from './UploadSecret.vue'
import TextReadonly from './TextReadonly.vue'
import DynamicInput from './DynamicInput.vue'
import PasswordInput from './PasswordInput.vue'
import WeekCronSelect from './WeekCronSelect.vue'
import BoolTextReadonly from './BoolTextReadonly.vue'
import NestedObjectSelect2 from './NestedObjectSelect2.vue'
import DatetimeRangePicker from './DatetimeRangePicker.vue'
import JSONManyToManySelect from './JSONManyToManySelect/index.vue'
import PasswordRule from './PasswordRule.vue'
export default {
Link,
Switcher,
Select2,
TagInput,
AttrInput,
UploadKey,
JsonEditor,
UpdateToken,
PhoneInput,
UploadField,
UserPassword,
DynamicInput,
PasswordInput,
UploadSecret,
PasswordRule,
TextReadonly,
WeekCronSelect,
BoolTextReadonly,
NestedObjectSelect2,
DatetimeRangePicker,
JSONManyToManySelect
}
export {
Link,
Switcher,
Select2,
TagInput,
AttrInput,
UploadKey,
JsonEditor,
UpdateToken,
PhoneInput,
UploadField,
UserPassword,
DynamicInput,
PasswordInput,
UploadSecret,
PasswordRule,
TextReadonly,
WeekCronSelect,
BoolTextReadonly,
NestedObjectSelect2,
DatetimeRangePicker,
JSONManyToManySelect
}

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div v-for="(command, index) in iValue" :key="index" :prop="'iValue.' + index + '.value'" class="command-item">
<el-input v-model="iValue[index]" size="mini">
<div v-for="(command, index) in value" :key="index" :prop="'value.' + index + '.value'" class="command-item">
<el-input v-model="value[index]" size="mini">
<template slot="prepend"> {{ inputTitle + ' ' + (index + 1) }}</template>
</el-input>
<div class="input-button">
@@ -14,7 +14,7 @@
@click="handleDelete(command)"
/>
<el-button
v-if="index === iValue.length - 1"
v-if="index === value.length - 1"
icon="el-icon-plus"
size="mini"
style="flex-shrink: 0;"
@@ -27,7 +27,6 @@
</template>
<script>
export default {
props: {
value: {
@@ -40,32 +39,20 @@ export default {
}
},
data() {
return {
iValue: ['']
}
},
watch: {
iValue: {
handler(v) {
this.$emit('input', Array.from(v))
}
}
},
created() {
this.iValue = Array.from(this.value)
return {}
},
methods: {
handleDelete(command) {
const index = this.iValue.indexOf(command)
const index = this.value.indexOf(command)
if (index !== -1) {
this.iValue.splice(index, 1)
this.value.splice(index, 1)
}
},
handleAdd() {
this.iValue.push('')
this.value.push('')
},
deleteDisabled() {
return this.iValue.length <= 1
return this.value.length <= 1
}
}
}

View File

@@ -18,10 +18,10 @@
</template>
<script>
import DataForm from '@/components/Form/DataForm/index.vue'
import DataForm from '@/components/DataForm/index.vue'
import Dialog from '@/components/Dialog/index.vue'
import ValueField from '@/components/Form/FormFields/JSONManyToManySelect/ValueField.vue'
import { attrMatchOptions, typeMatchMapper } from '@/components/const'
import ValueField from '@/components/FormFields/JSONManyToManySelect/ValueField.vue'
import { attrMatchOptions, typeMatchMapper } from './const'
export default {
name: 'AttrFormDialog',

View File

@@ -13,7 +13,7 @@
<script>
import Dialog from '@/components/Dialog/index.vue'
import ListTable from '@/components/Table/ListTable/index.vue'
import ListTable from '@/components/ListTable/index.vue'
export default {
name: 'AttrMatchResultDialog',

View File

@@ -8,9 +8,9 @@
</template>
<script>
import TagInput from '@/components/Form/FormFields/TagInput.vue'
import Select2 from '@/components/Form/FormFields/Select2.vue'
import Switcher from '@/components/Form/FormFields/Switcher.vue'
import TagInput from '@/components/FormFields/TagInput.vue'
import Select2 from '@/components/FormFields/Select2.vue'
import Switcher from '@/components/FormFields/Switcher.vue'
export default {
name: 'ValueField',

View File

@@ -9,7 +9,7 @@
</template>
<script>
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
import BaseFormatter from '@/components/TableFormatters/base.vue'
import { setUrlParam } from '@/utils/common'
export default {

View File

@@ -38,12 +38,12 @@
<script>
import Select2 from '../Select2.vue'
import DataTable from '@/components/Table/DataTable/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import ValueFormatter from './ValueFormatter.vue'
import AttrFormDialog from './AttrFormDialog.vue'
import AttrMatchResultDialog from './AttrMatchResultDialog.vue'
import { setUrlParam } from '@/utils/common'
import { attrMatchOptions } from '@/components/const'
import { attrMatchOptions } from './const'
import { toM2MJsonParams } from '@/utils/jms'
export default {

View File

@@ -60,7 +60,7 @@ export default {
</script>
<style lang="scss" scoped>
@import "~@/styles/variables";
@import "~@/styles/variables.scss";
.json-editor {
.resize {

View File

@@ -9,7 +9,7 @@
</template>
<script>
import Select2 from './Select2.vue'
import Select2 from './Select2'
export default {
name: 'NestedObjectSelect2',

View File

@@ -30,7 +30,7 @@
<script>
import { AutoDataForm, Dialog } from '@/components'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import JsonEditor from '@/components/FormFields/JsonEditor.vue'
export default {
name: 'ProtocolSetting',

View File

@@ -71,7 +71,7 @@
</template>
<script>
import ProtocolSettingDialog from './ProtocolSettingDialog.vue'
import ProtocolSettingDialog from './ProtocolSettingDialog'
export default {
components: {
@@ -305,7 +305,7 @@ export default {
items = protocols.filter(item => allProtocolNames.indexOf(item.name) !== -1)
} else {
const defaults = choices.filter(item => (item.required || item.primary || item.default))
if (defaults.length === 0 && choices.length !== 0) {
if (defaults.length === 0) {
defaults.push(choices[0])
}
items = defaults

View File

@@ -10,13 +10,12 @@
@click="handleTagClick(v, k)"
@close="handleTagClose(v)"
>
{{ isCheckShowPassword ? changeTagShowValue(v) : v }}
{{ v }}
</el-tag>
<component
:is="component"
ref="SearchInput"
v-model.trim="filterValue"
:type="inputType"
:fetch-suggestions="autocomplete"
:placeholder="this.$t('common.EnterToContinue')"
class="search-input"
@@ -26,13 +25,6 @@
@select="handleSelect"
@keyup.enter.native="handleConfirm"
/>
<span
v-if="replaceShowPassword && filterTags.length > 0"
class="show-password"
@click="handleShowPassword"
>
<i class="fa" :class="[isCheckShowPassword ? 'fa-eye-slash' : 'fa-eye']" />
</span>
</div>
</template>
@@ -58,22 +50,6 @@ export default {
autocomplete: {
type: Function,
default: null
},
replaceShowPassword: {
type: Boolean,
default: false
},
replaceRule: {
type: String,
default: ''
},
replaceContent: {
type: String,
default: '*'
},
inputType: {
type: String,
default: () => 'text'
}
},
data() {
@@ -81,7 +57,6 @@ export default {
filterTags: this.value,
focus: false,
filterValue: '',
isCheckShowPassword: this.replaceShowPassword,
component: this.autocomplete ? 'el-autocomplete' : 'el-input'
}
},
@@ -101,7 +76,6 @@ export default {
},
handleConfirm() {
if (this.filterValue === '') return
if (!this.filterTags.includes(this.filterValue)) {
this.filterTags.push(this.filterValue)
this.filterValue = ''
@@ -115,23 +89,6 @@ export default {
this.$delete(this.filterTags, k)
this.filterValue = v
this.$refs.SearchInput.focus()
},
matchRule(value) {
const regex = new RegExp(this.replaceRule)
const replacedValue = value.replace(regex, (match, p1, p2, p3) => {
const stars = p2.replace(/./g, this.replaceContent)
return p1 + stars + p3
})
return replacedValue
},
changeTagShowValue(value) {
if (this.replaceShowPassword && this.replaceRule) {
value = this.matchRule(value)
}
return value
},
handleShowPassword() {
this.isCheckShowPassword = !this.isCheckShowPassword
}
}
}
@@ -159,7 +116,6 @@ export default {
&>>> .el-tag {
margin-top: 3px;
font-family: sans-serif !important;
}
&>>> .el-autocomplete {
@@ -184,13 +140,4 @@ export default {
.filter-field >>> .el-input__inner {
height: 26px;
}
.show-password {
display: inherit;
padding-right: 6px;
cursor: pointer;
&:hover {
color: #999999;
}
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<span>
{{ text }}
</span>
</template>
<script>
export default {
name: 'Text',
props: {
text: {
type: String,
default: ''
}
},
methods: {
onClick() {
window.open(this.href)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -23,7 +23,7 @@
</template>
<script>
import Select2 from './Select2.vue'
import Select2 from './Select2'
import { hasUUID } from '@/utils/common'
export default {

View File

@@ -1,12 +1,12 @@
<template>
<div>
<input ref="upLoadFile" :accept="accept" style="display: none" type="file" @change="Onchange">
<input ref="upLoadFile" :accept="accept" type="file" style="display: none" @change="Onchange">
<el-button size="mini" @click.native.stop="onUpLoad">
{{ this.$t('common.SelectFile') }}
</el-button>
<span>{{ fileName }}</span>
<div v-if="tip !== ''" class="help-block">{{ tip }}</div>
<input v-model="value" hidden type="text" v-on="$listeners">
<div v-if="tip !== ''">{{ tip }}</div>
<input v-model="value" type="text" hidden v-on="$listeners">
<div>
<img :src="preview" v-bind="$attrs">
</div>

Some files were not shown because too many files have changed in this diff Show More