运行结果界面添加格式化按钮,将树状的对象格式为JSON字符串展示,添加转表格的按钮,将符合条件的对象转为表格字符串展示

This commit is contained in:
fofolee 2025-01-14 16:21:35 +08:00
parent 601df1f1e3
commit ab5f90ea51
3 changed files with 195 additions and 25 deletions

View File

@ -39,6 +39,7 @@
:textbtn="!enableHtml" :textbtn="!enableHtml"
:imagebtn="!enableHtml && isDataUrl" :imagebtn="!enableHtml && isDataUrl"
@showImg="showBase64Img" @showImg="showBase64Img"
@updateResult="runResult = $event"
:style="{ :style="{
height: headerHeight + 'px', height: headerHeight + 'px',
}" }"

View File

@ -22,7 +22,12 @@
v-else-if="typeof item === 'undefined'" v-else-if="typeof item === 'undefined'"
v-text="'undefined'" v-text="'undefined'"
/> />
<pre class="result" v-text="item" v-else /> <pre
class="result"
:class="{ wrapLine: !isTable }"
v-text="item"
v-else
/>
</div> </div>
</div> </div>
</div> </div>
@ -78,6 +83,10 @@ export default {
) )
: ""; : "";
}, },
// ---
isTable() {
return this.runResult?.[0]?.split("\n")?.[1]?.includes("---");
},
}, },
mounted() { mounted() {
this.frameInit(); this.frameInit();
@ -135,16 +144,22 @@ export default {
.text { .text {
font-family: Consolas, Monaco, "Courier New"; font-family: Consolas, Monaco, "Courier New";
} }
.result { .result {
white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
max-width: 100%; max-width: 100%;
margin: 0; margin: 0;
} }
.result.wrapLine {
white-space: pre-wrap;
}
.undefined { .undefined {
color: #999; color: #999;
font-style: italic; font-style: italic;
} }
iframe { iframe {
width: 100%; width: 100%;
display: block; display: block;

View File

@ -1,13 +1,32 @@
<template> <template>
<q-btn-group :stretch="stretch" class="text-primary"> <q-btn-group :stretch="stretch" class="text-primary result-menu">
<q-btn <q-btn
icon="image" icon="image"
label="转图片" label="转图片"
@click="dataUrlToImg" @click="dataUrlToImg"
v-show="imagebtn" v-show="imagebtn"
dense dense
size="sm" size="sm"
></q-btn> ></q-btn>
<q-btn
icon="data_object"
@click="updateStringifyResult"
v-show="textbtn && hasObject"
:label="!dense ? '格式化' : ''"
dense
size="sm"
>
<q-tooltip v-if="!dense">格式化JSON</q-tooltip>
</q-btn>
<q-btn
icon="pivot_table_chart"
@click="updateTableResult"
v-show="textbtn && hasTable"
:label="!dense ? '转表格' : ''"
dense
size="sm"
>
</q-btn>
<q-btn <q-btn
icon="content_paste" icon="content_paste"
@click="copyResult" @click="copyResult"
@ -36,45 +55,180 @@ export default {
closebtn: Boolean, closebtn: Boolean,
runResult: Array, runResult: Array,
}, },
emits: ["updateResult"],
computed: { computed: {
content() { /**
return this.getContent(); * 处理运行结果数据同时完成以下任务
* 1. 查找符合表格显示条件的数组数据数组元素都是对象
* 2. 格式化所有数据项对象转JSON处理null等特殊情况
*
* @returns {Object} 处理后的数据对象
* - items: 格式化后的所有数据项数组每项包含:
* - raw: 原始数据
* - formatted: 格式化后的字符串
* - tableData: 找到的第一个可转换为表格的数组如果没有则为null
*/
processedData() {
//
if (!this.runResult) return { items: [], tableData: null };
let tableData = null;
const items = this.runResult.map((item, index) => {
// 1.
//
if (!tableData && Array.isArray(item) && item.length > 0) {
// null
const isValidTable = item.every(
(elem) =>
typeof elem === "object" && elem !== null && !Array.isArray(elem)
);
//
if (isValidTable) {
tableData = item;
}
}
// 2.
// 2.1
if (typeof item !== "object") {
return { raw: item, formatted: item };
}
// 2.2 null "null"
if (item === null) {
return { raw: null, formatted: "null" };
}
// 2.3
try {
// JSON
return {
raw: item,
formatted: JSON.stringify(item, null, 2),
};
} catch (error) {
// JSON使toString()
return {
raw: item,
formatted: item.toString(),
};
}
});
//
return { items, tableData };
},
formattedItems() {
return this.processedData.items;
},
validTableData() {
return this.processedData.tableData;
},
hasObject() {
return this.formattedItems.some(
(item) => typeof item.raw === "object" && item.raw !== null
);
},
hasTable() {
return this.validTableData !== null;
}, },
}, },
methods: { methods: {
copyResult() { copyResult() {
window.utools.copyText(this.content); window.utools.copyText(this.getFormattedContent());
quickcommand.showMessageBox("已复制到剪贴板"); quickcommand.showMessageBox("已复制到剪贴板");
}, },
sendResult() { sendResult() {
window.utools.hideMainWindowTypeString(this.content); window.utools.hideMainWindowTypeString(this.getFormattedContent());
}, },
dataUrlToImg() { dataUrlToImg() {
let imgs = this.content const imagePattern = /data:image\/.*?;base64,.*/g;
.match(/data:image\/.*?;base64,.*/g) const imageUrls = this.getFormattedContent()
.match(imagePattern)
?.map((dataUrl) => `<img src="${dataUrl}"><br>`); ?.map((dataUrl) => `<img src="${dataUrl}"><br>`);
if (!imgs) return quickcommand.showMessageBox("dataUrl 格式不正确!");
this.$emit("showImg", imgs); if (!imageUrls) {
return quickcommand.showMessageBox("dataUrl 格式不正确!");
}
this.$emit("showImg", imageUrls);
}, },
saveResult() { saveResult() {
window.saveFile(this.content, { const saveOptions = {
defaultPath: "quickcommand-result.txt", defaultPath: "quickcommand-result.txt",
filters: [{ name: "txt", extensions: ["txt"] }], filters: [{ name: "txt", extensions: ["txt"] }],
}); };
window.saveFile(this.getFormattedContent(), saveOptions);
}, },
getContent() { getFormattedContent() {
let content = this.runResult.map((item) => { return this.formattedItems.map((item) => item.formatted).join("\n");
if (typeof item === "object") { },
try { updateStringifyResult() {
return JSON.stringify(item, null, 2); this.updateResult([this.getFormattedContent()]);
} catch (e) { },
return item.toString(); updateTableResult() {
} const tableData = this.validTableData;
} if (!tableData) return;
return item;
const headers = [
...new Set(tableData.flatMap((obj) => Object.keys(obj))),
];
if (!headers.length) return;
//
const columnWidths = headers.map((header) => {
const maxDataWidth = Math.max(
header.length,
...tableData.map((obj) => {
const value = obj[header];
return value === undefined || value === null
? 0
: String(value).replace(/\n/g, "\\n").length;
})
);
return maxDataWidth;
}); });
return content.join("\n");
//
const createAlignedRow = (cells) =>
"| " +
cells
.map((cell, index) => {
const padding = " ".repeat(
Math.max(0, columnWidths[index] - String(cell).length)
);
return String(cell) + padding;
})
.join(" | ") +
" |";
const rows = [
//
createAlignedRow(headers),
//
"| " +
headers.map((_, i) => "-".repeat(columnWidths[i])).join(" | ") +
" |",
//
...tableData.map((obj) =>
createAlignedRow(
headers.map((header) => {
const value = obj[header];
return value === undefined || value === null
? ""
: String(value).replace(/\n/g, "\\n");
})
)
),
];
this.updateResult([rows.join("\n")]);
},
updateResult(result) {
this.$emit("updateResult", result);
}, },
}, },
}; };
</script> </script>
<style scoped>
.result-menu {
gap: 4px;
}
</style>