diff --git a/frontend/src/components/content_value/ContentPubsub.vue b/frontend/src/components/content_value/ContentPubsub.vue
index 5e36b335..e71b5d06 100644
--- a/frontend/src/components/content_value/ContentPubsub.vue
+++ b/frontend/src/components/content_value/ContentPubsub.vue
@@ -15,9 +15,10 @@ import Checked from '@/components/icons/Checked.vue'
import Bottom from '@/components/icons/Bottom.vue'
import IconButton from '@/components/common/IconButton.vue'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
+import Save from '@/components/icons/Save.vue'
const themeVars = useThemeVars()
-
+const datetimeFormat = 'YYYY-MM-DD HH:mm:ss.SSS';
const browserStore = useBrowserStore()
const i18n = useI18n()
const props = defineProps({
@@ -44,6 +45,10 @@ const publishData = reactive({
const tableRef = ref(null)
+const getFormattedDateTime = (timestamp) => {
+ return dayjs(timestamp).format(datetimeFormat);
+}
+
const columns = computed(() => [
{
title: () => i18n.t('pubsub.time'),
@@ -52,7 +57,7 @@ const columns = computed(() => [
align: 'center',
titleAlign: 'center',
render: ({ timestamp }, index) => {
- return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss.SSS')
+ return getFormattedDateTime(timestamp);
},
},
{
@@ -181,6 +186,47 @@ const onCleanLog = () => {
data.list = []
}
+const onSave = async () => {
+ if (data.list.length == 0) {
+ $message.error("no messages");
+ return;
+ }
+
+ const headers = Object.keys(data.list[0]);
+
+ const csv = [
+ headers.join(','),
+ ...data.list.map(row =>
+ headers.map(header => `${String(header == 'timestamp' ? getFormattedDateTime(dayjs(row[header])) : row[header] ?? '')}`).join(',')
+ )
+ ].join('\n');
+
+ try
+ {
+ const handle = await window.showSaveFilePicker({
+ suggestedName: 'download.csv',
+ types: [
+ {
+ description: 'Csv file',
+ accept: {
+ 'text/plain': ['.csv']
+ }
+ }
+ ]
+ });
+
+ const writable = await handle.createWritable();
+ await writable.write(csv);
+ await writable.close();
+ }
+ catch (error) {
+ if (error.name === 'AbortError') {
+ return;
+ }
+ throw error;
+ }
+}
+
const onPublish = async () => {
if (isEmpty(publishData.channel)) {
return
@@ -245,6 +291,13 @@ const onPublish = async () => {
stroke-width="3.5"
t-tooltip="monitor.always_show_last"
@click="data.autoShowLast = !data.autoShowLast" />
+