Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): support data export #1649

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions web/src/components/ExportData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<template>
<my-dialog
:title="$t('connections.exportData')"
:visible.sync="showDialog"
class="export-data"
width="350px"
:confirmLoading="confirmLoading"
@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 { getAllData } from '@/utils/api/setting'
import { Getter } from 'vuex-class'
import MyDialog from './MyDialog.vue'

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 confirmLoading: boolean = false
private record: ExportForm = {
exportFormat: 'JSON',
allConnections: false,
}

@Watch('visible')
private onVisibleChanged(val: boolean) {
this.showDialog = val
}

private exportData() {
switch (this.record.exportFormat) {
case 'JSON':
this.exportJSONData()
break
default:
break
}
}

private exportDiffFormatData(content: string, format: ExportFormat) {
const filename = 'data'
const fileExt = format.toLowerCase()
if (!this.record.allConnections) {
// TODO: export single connection
}
const blob = new Blob([content], { type: 'text/plain' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `${filename}.${fileExt}`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
this.$message.success(this.$tc('common.exportSuccess'))
this.resetData()
}

private async getContent() {
let data: $TSFixed
if (!this.record.allConnections) {
// TODO: export single connection
} else {
data = getAllData()
}
return data
}

private async getStringifyContent() {
const connections = await this.getContent()
return JSON.stringify(connections, null, 2)
}

private exportJSONData() {
this.confirmLoading = true
this.getStringifyContent()
.then((content) => {
this.exportDiffFormatData(content, 'JSON')
})
.catch((err) => {
this.$message.error(err.toString())
})
.finally(() => {
this.confirmLoading = false
})
}

private resetData() {
this.showDialog = false
this.$emit('update:visible', false)
}

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>
5 changes: 5 additions & 0 deletions web/src/lang/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ export default {
en: 'Copy Failed',
ja: 'コピーが失敗しました',
},
exportSuccess: {
zh: '导出成功',
en: 'exported successfully',
ja: 'のエクスポートが成功しました',
},
version: {
zh: '版本:',
en: 'Version: ',
Expand Down
20 changes: 20 additions & 0 deletions web/src/lang/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,26 @@ export default {
en: 'Comma separator (,) is also used when setting aliases for multiple topics',
ja: '複数のトピックにエイリアスを設定する場合は、カンマ区切り(,)も使用されます。',
},
allConnections: {
zh: '全部数据',
en: 'All connections',
ja: '全ての接続',
},
allConnectionsTips: {
zh: '当打开此开关后,可以导出全部数据',
en: 'When this switch is turned on, all Connections data can be exported',
ja: 'このスイッチをオンにすると、全ての接続のデータをエクスポートすることができます',
},
exportData: {
zh: '导出数据',
en: 'Export Data',
ja: 'エクスポート',
},
exportFormat: {
zh: '导出数据格式',
en: 'Export format',
ja: 'エクスポートフォーマット',
},
metaTips: {
zh: '仅在 MQTT 5.0 中启用',
en: 'Enabled only with MQTT 5.0',
Expand Down
4 changes: 4 additions & 0 deletions web/src/utils/api/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const setSettings = (key: string, value: string | boolean | number): stri
return db.set<string | boolean | number>(key, value)
}

export const getAllData = (): $TSFixed => {
return db.read().value()
}

export const cleanHistoryData = (): void => {
db.set('connections', [])
db.set('headersHistory', [])
Expand Down
25 changes: 25 additions & 0 deletions web/src/views/settings/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,23 @@
<div class="settings-title">{{ $t('settings.advanced') }}</div>
<el-divider></el-divider>

<el-row class="settings-item" type="flex" justify="space-between" align="middle">
<el-col :span="20">
<label>{{ $t('settings.dataBackup') }}</label>
</el-col>
<el-col :span="4">
<el-button
class="data-manager-btn"
type="primary"
size="mini"
icon="el-icon-printer"
@click="handleExportData"
>
</el-button>
</el-col>
</el-row>
<el-divider></el-divider>

<el-row class="settings-item" type="flex" justify="space-between" align="middle">
<el-col :span="20">
<label>{{ $t('settings.historyCleanup') }}</label>
Expand All @@ -179,6 +196,7 @@
</el-row>
<el-divider></el-divider>

<ExportData :visible.sync="showExportData" />
<ClearUpHistoryData :visible.sync="showHistoryData" />
</div>
</div>
Expand All @@ -187,10 +205,12 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { Getter, Action } from 'vuex-class'
import ExportData from '@/components/ExportData.vue'
import ClearUpHistoryData from '@/components/ClearUpHistoryData.vue'

@Component({
components: {
ExportData,
ClearUpHistoryData,
},
})
Expand Down Expand Up @@ -223,6 +243,7 @@ export default class Settings extends Vue {
{ label: 'Night', value: 'night' },
]

private showExportData = false
private showHistoryData = false

private handleSelectChange(type: 'lang' | 'theme', value: string | number | boolean): void {
Expand Down Expand Up @@ -256,6 +277,10 @@ export default class Settings extends Vue {
this.actionAutoScrollInterval({ autoScrollInterval: value })
}

private handleExportData() {
this.showExportData = true
}

private handleCleanupHistoryData() {
this.showHistoryData = true
}
Expand Down
Loading