Skip to content

Commit

Permalink
Merge pull request #321 from emqx/develop
Browse files Browse the repository at this point in the history
Release v1.3.4
  • Loading branch information
CrazyWisdom authored Oct 14, 2020
2 parents a50bbe8 + 54bc0c9 commit 553f987
Show file tree
Hide file tree
Showing 24 changed files with 878 additions and 95 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "MQTTX",
"version": "1.3.3",
"version": "1.3.4",
"description": "MQTT desktop client",
"author": "EMQ X Team",
"scripts": {
Expand All @@ -26,7 +26,7 @@
"lowdb": "^1.0.0",
"moment": "^2.24.0",
"monaco-editor": "^0.20.0",
"mqtt": "^4.1.0",
"mqtt": "^4.2.1",
"vue": "^2.6.10",
"vue-class-component": "^7.0.2",
"vue-click-outside": "^1.1.0",
Expand Down
7 changes: 7 additions & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createProtocol, installVueDevtools } from 'vue-cli-plugin-electron-buil
import db from './database/index'
import updateChecker from './main/updateChecker'
import getMenuTemplate from './main/getMenuTemplate'
import saveFile from './main/saveFile'

interface WindowSizeModel {
width: number
Expand Down Expand Up @@ -32,6 +33,12 @@ function handleIpcMessages() {
ipcMain.on('checkUpdate', () => {
updateChecker(false)
})
ipcMain.on('exportData', (event: any, ...args: string[]) => {
const [filename, content, type] = args
if (win) {
saveFile(win, filename, content, type)
}
})
}

function createWindow() {
Expand Down
5 changes: 4 additions & 1 deletion src/components/Contextmenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default class Contextmenu extends Vue {
display: block;
&:hover {
color: var(--color-main-green);
background: var(--color-second-green);
background: var(--color-thrid-green);
}
&.danger {
color: var(--color-second-red);
Expand All @@ -57,6 +57,9 @@ export default class Contextmenu extends Vue {
i {
margin-right: 5px;
}
.icon-delete {
margin-right: 4px;
}
}
}
</style>
131 changes: 131 additions & 0 deletions src/components/ExportData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<template>
<my-dialog
:title="$t('connections.exportData')"
:visible.sync="showDialog"
class="export-data"
width="350px"
@confirm="exportData"
@close="resetData"
@keyupEnter="exportData"
>
<el-form ref="form" label-position="left" label-width="190px" :model="record">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="$t('connections.exportFormat')" prop="exportFormat">
<el-select size="small" v-model="record.exportFormat">
<el-option v-for="(format, index) in ['JSON']" :key="index" :value="format"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item class="swtich-item" :label="$t('connections.allConnections')" prop="allConnections">
<el-tooltip
v-if="connection"
placement="top"
:effect="theme !== 'light' ? 'light' : 'dark'"
:open-delay="500"
:content="$t('connections.allConnectionsTips')"
>
<a href="javascript:;" class="icon-tip">
<i class="el-icon-question"></i>
</a>
</el-tooltip>
<el-switch v-model="record.allConnections" :disabled="!connection"></el-switch>
</el-form-item>
</el-col>
</el-row>
</el-form>
</my-dialog>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { Getter } from 'vuex-class'
import { ipcRenderer } from 'electron'
import { loadConnections } from '@/utils/api/connection'
import MyDialog from './MyDialog.vue'
import { ConnectionModel } from '@/views/connections/types'
type ExportFormat = 'JSON'
interface ExportForm {
exportFormat: ExportFormat
allConnections: boolean
}
@Component({
components: {
MyDialog,
},
})
export default class ExportData extends Vue {
@Getter('currentTheme') private theme!: Theme
@Prop({ default: undefined }) public connection!: ConnectionModel
@Prop({ default: false }) public visible!: boolean
private showDialog: boolean = this.visible
private record: ExportForm = {
exportFormat: 'JSON',
allConnections: false,
}
@Watch('visible')
private onChildChanged(val: boolean) {
this.showDialog = val
}
private exportData() {
switch (this.record.exportFormat) {
case 'JSON':
this.exportJSONData()
break
default:
break
}
}
private async exportJSONData() {
let filename = this.$t('connections.allConnections')
let content = ''
if (!this.record.allConnections) {
filename = this.connection.name
content = JSON.stringify(this.connection, null, 2)
ipcRenderer.send('exportData', filename, content, 'json')
} else {
const connections: ConnectionModel[] | [] = await loadConnections()
content = JSON.stringify(connections, null, 2)
ipcRenderer.send('exportData', 'data', content, 'json')
}
ipcRenderer.on('saved', () => {
this.$message.success(`${filename} ${this.$t('common.exportSuccess')}`)
this.resetData()
})
}
private resetData() {
this.showDialog = false
this.$emit('update:visible', false)
ipcRenderer.removeAllListeners('saved')
}
private created() {
this.record.allConnections = !this.connection ? true : false
}
}
</script>

<style lang="scss">
.export-data {
.el-dialog__body {
padding-bottom: 0px;
.el-tooltip.icon-tip {
position: absolute;
right: 195px;
font-size: 16px;
color: var(--color-text-tips);
}
.swtich-item {
.el-form-item__content {
text-align: right;
}
}
}
}
</style>
174 changes: 174 additions & 0 deletions src/components/ImportData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<template>
<my-dialog
:title="$t('connections.importData')"
:visible.sync="showDialog"
class="import-data"
width="350px"
@confirm="importData"
@close="resetData"
>
<el-form ref="form" label-position="left" label-width="130px" :model="record">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="$t('connections.importFormat')" prop="importFormat">
<el-select size="small" v-model="record.importFormat">
<el-option v-for="(format, index) in ['JSON']" :key="index" :value="format"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="22">
<el-form-item :label="$t('connections.importFile')" prop="cert">
<el-tooltip placement="top" :effect="theme !== 'light' ? 'light' : 'dark'" :open-delay="500">
<div slot="content" v-html="$t('connections.importConnectionsTip')">
{{ $t('connections.importConnectionsTip') }}
</div>
<a href="javascript:;" class="icon-tip">
<i class="el-icon-question"></i>
</a>
</el-tooltip>
<el-input size="small" v-model="record.filePath"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<a href="javascript:;" class="icon-upload" @click="getFileData">
<i class="el-icon-folder-opened"></i>
</a>
</el-col>
</el-row>
</el-form>
</my-dialog>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { Getter } from 'vuex-class'
import fs from 'fs'
import { remote } from 'electron'
import { isArray } from 'lodash'
import { importConnections } from '@/utils/api/connection'
import { ConnectionModel } from '@/views/connections/types'
import MyDialog from './MyDialog.vue'
type ImportFormat = 'JSON'
interface ImportForm {
importFormat: ImportFormat
filePath: string
fileContent: ConnectionModel[]
}
@Component({
components: {
MyDialog,
},
})
export default class ImportData extends Vue {
@Getter('currentTheme') private theme!: Theme
@Prop({ default: false }) public visible!: boolean
private showDialog: boolean = this.visible
private record: ImportForm = {
importFormat: 'JSON',
filePath: '',
fileContent: [],
}
@Watch('visible')
private onChildChanged(val: boolean) {
this.showDialog = val
}
private getFileData() {
remote.dialog.showOpenDialog(
{
properties: ['openFile'],
filters: [{ name: '', extensions: ['json'] }],
},
(files) => {
if (files) {
const filePath = files[0]
fs.readFile(filePath, 'utf-8', (err, data) => {
if (err) {
this.$message.error(`${this.$t('connections.readFileErr')}${err.message}`)
return
}
try {
const _data = JSON.parse(data)
const fileContent = isArray(_data) ? _data : [_data]
const res = this.verifyFileContent(fileContent)
if (!res) {
this.$message.error(`${this.$t('connections.fileContentRequired')}`)
return
}
this.record.filePath = filePath
this.record.fileContent = fileContent
} catch (err) {
this.$message.error(err.toString())
}
})
}
},
)
}
private verifyFileContent(data: ConnectionModel[]) {
const hasRequiredItem = (oneConnection: ConnectionModel): boolean => {
const { clientId, name, host, port, ssl, certType, ca } = oneConnection
return !(!clientId || !name || !host || !port || (ssl && !certType) || (certType === 'self' && !ca))
}
return data.every(hasRequiredItem)
}
private importData() {
if (!this.record.fileContent.length) {
this.$message.error(`${this.$t('connections.uploadFileTip')}`)
return
}
switch (this.record.importFormat) {
case 'JSON':
this.importJSONData()
break
default:
break
}
}
private async importJSONData() {
const importDataResult = await importConnections(this.record.fileContent)
if (importDataResult === 'ok') {
this.$message.success(`${this.$t('common.importSuccess')}`)
this.$emit('updateData')
this.resetData()
} else {
this.$message.error(importDataResult)
}
}
private resetData() {
this.showDialog = false
this.$emit('update:visible', false)
this.record = {
importFormat: 'JSON',
filePath: '',
fileContent: [],
}
}
}
</script>

<style lang="scss">
.import-data {
.el-dialog__body {
padding-bottom: 0px;
.el-tooltip.icon-tip {
position: absolute;
right: 195px;
font-size: 16px;
color: var(--color-text-tips);
}
}
.icon-upload {
display: inline-block;
margin-top: 9px;
}
.el-col-2 {
padding-left: 5px !important;
}
}
</style>
6 changes: 5 additions & 1 deletion src/components/MsgLeftItem.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="msg-left-item">
<span class="topic-color" :style="{ height: topicColorHeight, background: currentTopicColor }"></span>
<div ref="leftPayload" class="left-payload payload">
<div ref="leftPayload" class="left-payload payload" @contextmenu.prevent="customMenu($event)">
<p class="left-info">
<span class="topic">Topic: {{ topic }}</span>
<span class="qos">QoS: {{ qos }}</span>
Expand Down Expand Up @@ -40,6 +40,10 @@ export default class MsgLeftItem extends Vue {
}
}
private customMenu(event: MouseEvent) {
this.$emit('showmenu', this.payload, event)
}
private mounted() {
const leftPayloadDom = this.$refs.leftPayload as LeftPayloadDOM
this.topicColorHeight = `${leftPayloadDom.offsetHeight - 6}px`
Expand Down
Loading

0 comments on commit 553f987

Please # to comment.