优化ArrayDict/DictEditor参数传递,修复formatString处理Array时的BUG

This commit is contained in:
fofolee 2025-01-10 18:54:11 +08:00
parent 41b3501945
commit 5e70c4c9a0
10 changed files with 372 additions and 371 deletions

View File

@ -6,7 +6,6 @@ const quickcomposer = {
network: require("./quickcomposer/network"), network: require("./quickcomposer/network"),
coding: require("./quickcomposer/coding"), coding: require("./quickcomposer/coding"),
math: require("./quickcomposer/math"), math: require("./quickcomposer/math"),
ui: require("./quickcomposer/ui"),
audio: require("./quickcomposer/audio"), audio: require("./quickcomposer/audio"),
image: require("./quickcomposer/image"), image: require("./quickcomposer/image"),
}; };

View File

@ -1,50 +0,0 @@
const showSaveDialog = (
title,
defaultPath,
buttonLabel,
message,
extensions,
properties
) => {
return window.utools.showSaveDialog({
title,
defaultPath,
buttonLabel,
message,
properties,
filters: [
{
name: "文件",
extensions,
},
],
});
};
const showOpenDialog = (
title,
defaultPath,
buttonLabel,
message,
extensions,
properties
) => {
return window.utools.showOpenDialog({
title,
defaultPath,
buttonLabel,
message,
properties,
filters: [
{
name: "文件",
extensions,
},
],
});
};
module.exports = {
showSaveDialog,
showOpenDialog,
};

View File

@ -1,6 +0,0 @@
const { showSaveDialog, showOpenDialog } = require("./dialog");
module.exports = {
showSaveDialog,
showOpenDialog,
};

View File

@ -96,7 +96,7 @@ export default defineComponent({
}, },
saveFlow() { saveFlow() {
const flow = window.lodashM.cloneDeep(this.commandFlow); const flow = window.lodashM.cloneDeep(this.commandFlow);
const uselessProps = ["config", "argvs", "label", "type"]; const uselessProps = ["config", "argvs", "label", "component", "subCommands", "options", "defaultValue"];
// //
flow.forEach((cmd) => { flow.forEach((cmd) => {
for (const props of uselessProps) { for (const props of uselessProps) {

View File

@ -6,25 +6,21 @@
:model-value="isCollapse" :model-value="isCollapse"
> >
<div class="array-editor"> <div class="array-editor">
<div v-for="(item, index) in items" :key="index" class="row items-center"> <div v-for="(row, index) in rows" :key="index" class="row items-center">
<template v-if="optionsKeys.length"> <template v-if="columns">
<div <div
v-for="key in optionsKeys" v-for="column in processedColumns"
:key="key.value" :key="column.key"
:class="[ :class="[
key.width ? `col-${key.width}` : 'col', column.width ? `col-${column.width}` : 'col',
optionsKeys.length > 1 ? 'q-pr-sm' : '', Object.keys(columns).length > 1 ? 'q-pr-sm' : '',
]" ]"
> >
<VariableInput <VariableInput
:model-value="item[key.value] || key.defaultValue" :model-value="row[column.key]"
:label="key.label" v-bind="column"
:no-icon="true"
:placeholder="key.placeholder"
:options="key.options"
:disable-toggle-type="key.disableToggleType"
@update:model-value=" @update:model-value="
(val) => updateItemKeyValue(index, key.value, val) (val) => updateColumn(index, column.key, val)
" "
/> />
</div> </div>
@ -32,38 +28,32 @@
<template v-else> <template v-else>
<div class="col"> <div class="col">
<VariableInput <VariableInput
:model-value="item" :model-value="row"
:label="`${label || '项目'} ${index + 1}`" v-bind="$attrs"
:icon="icon || 'code'" @update:model-value="(val) => updateValue(index, val)"
:placeholder="placeholder"
:options="{
items: options.items,
}"
:disable-toggle-type="disableToggleType"
@update:model-value="(val) => updateItemValue(index, val)"
/> />
</div> </div>
</template> </template>
<div class="col-auto"> <div class="col-auto">
<div class="btn-container"> <div class="btn-container">
<template v-if="items.length === 1"> <template v-if="rows.length === 1">
<q-btn <q-btn
flat flat
dense dense
size="sm" size="sm"
icon="add" icon="add"
class="center-btn" class="center-btn"
@click="addItem" @click="addRow"
/> />
</template> </template>
<template v-else-if="index === items.length - 1"> <template v-else-if="index === rows.length - 1">
<q-btn <q-btn
flat flat
dense dense
size="sm" size="sm"
icon="remove" icon="remove"
class="top-btn" class="top-btn"
@click="removeItem(index)" @click="removeRow(index)"
/> />
<q-btn <q-btn
flat flat
@ -71,7 +61,7 @@
size="sm" size="sm"
icon="add" icon="add"
class="bottom-btn" class="bottom-btn"
@click="addItem" @click="addRow"
/> />
</template> </template>
<template v-else> <template v-else>
@ -81,7 +71,7 @@
size="sm" size="sm"
icon="remove" icon="remove"
class="center-btn" class="center-btn"
@click="removeItem(index)" @click="removeRow(index)"
/> />
</template> </template>
</div> </div>
@ -94,63 +84,62 @@
<script> <script>
/** /**
* 数组编辑器组件 * 数组编辑器组件
* @description 支持数组和多键对象数组的编辑 * @description 支持单数组和对象数组的编辑
* *
* @property {Array} modelValue - 绑定的数组值 * @property {Array} modelValue - 绑定的数组值
* @property {String} label - 输入框标签 * @property {Object} columns - 列配置可选
* @property {String} icon - 输入框图标
* @property {Object} options - 配置选项
* *
* @property {String[]} [options.keys] - 多键对象模式的键名列表 * // 1. columns
* @property {String[]} [options.keys.value] - 元素为对象时对象的键名 * //
* @property {String[]} [options.keys.label] - 对应varInput的label
* @property {String[]} [options.keys.placeholder] - 对应varInput的placeholder
* @property {String[]} [options.keys.defaultValue] - 对应varInput的defaultValue
* @property {Object} [options.keys.options] - 对应varInput的options
*
* @property {String[]} [options.items] - 下拉选择模式的选项列表
* @property {Object} [options.defaultValue] - 初始化时默认的值决定显示几个元素对应元素内容
* @example
* //
* [ * [
* newVarInputVal("str", "张三") * newVarInputVal('str', '选项1'),
* newVarInputVal('str', '选项2')
* ] * ]
* *
* // * 属性透传
* options.keys = ['name', 'age', 'email'] * ArrayEditor属性全部透传给VariableInput
* *
* options.keys= [ * 初始值
* { * 使用defaultValue属性设置初始值
* label: "姓名", * 使用defaultRowValue属性设置新增行时的初始值
* value: "name", *
* placeholder: "姓名", * // 2. columns
* columns = {
* name: {
* label: '姓名',
* placeholder: '请输入姓名',
* width: 6,
* options: { * options: {
* items: ["张三", "李四", "王五"], * items: ['张三', '李四', '王五']
* multiSelect: true,
* }, * },
* defaultValue: newVarInputVal('str', '张三')
* },
* age: {
* label: '年龄',
* placeholder: '请输入年龄',
* width: 4,
* defaultValue: newVarInputVal('str', '18')
* } * }
* ] * }
* * //
* [ * [
* { * {
* name: newVarInputVal("str", "张三"), * name: newVarInputVal('str', '张三'),
* age: newVarInputVal("str", "18"), * age: newVarInputVal('str', '18')
* email: newVarInputVal("str", "zhangsan@example.com")
* } * }
* ] * ]
* *
* // * 属性透传
* options.items = ['选项1', '选项2', '选项3'] * columns的每一个对象的值的属性全部透传给VariableInput
* [ *
* newVarInputVal("str", "选项1"), * 初始值
* newVarInputVal("str", "选项2"), * 使用defaultValue属性设置初始的每一行
* newVarInputVal("str", "选项3") * columns的每一个对象的defaultValue属性设置新增行时的初始值
* ]
*/ */
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import VariableInput from "components/composer/common/VariableInput.vue"; import VariableInput from "./VariableInput.vue";
import { newVarInputVal } from "js/composer/varInputValManager"; import { newVarInputVal } from "js/composer/varInputValManager";
import BorderLabel from "components/composer/common/BorderLabel.vue"; import BorderLabel from "./BorderLabel.vue";
export default defineComponent({ export default defineComponent({
name: "ArrayEditor", name: "ArrayEditor",
@ -164,9 +153,17 @@ export default defineComponent({
required: true, required: true,
default: () => [], default: () => [],
}, },
label: { columns: {
type: String, type: Object,
default: "", default: null,
},
defaultValue: {
type: [Object, Array],
default: null,
},
defaultRowValue: {
type: [Object, String],
default: null,
}, },
topLabel: { topLabel: {
type: String, type: String,
@ -180,103 +177,89 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
options: {
type: Object,
default: () => ({}),
},
placeholder: {
type: String,
default: "",
},
disableToggleType: {
type: Boolean,
default: false,
},
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],
computed: { computed: {
items() { rows() {
return this.modelValue.length ? this.modelValue : this.initializeItems(); return this.modelValue.length ? this.modelValue : this.initializeRow();
}, },
optionsKeys() { processedColumns() {
return ( if (!this.columns) return null;
this.options?.keys?.map((key) => {
return { return Object.entries(this.columns).map(([key, config]) => ({
...key, key,
value: key.value || key, ...config,
label: key.label || key, width: config.width || null,
}; defaultValue: config.defaultValue || newVarInputVal("str"),
}) || [] }));
);
}, },
}, },
methods: { methods: {
initializeItems() { initializeRow() {
if (this.optionsKeys?.length) { if (!this.columns) {
const item = {}; // 使 defaultValue VarInputVal
this.optionsKeys.forEach((key) => { return this.defaultValue || [newVarInputVal("str")];
item[key] = newVarInputVal("str");
});
return [item];
} }
return [newVarInputVal("str")]; //
}, if (this.defaultValue) {
/** return [this.defaultValue];
* 添加新的数组项
* 根据配置创建相应的数据结构
*/
addItem() {
let newItems = [];
if (this.options.keys) {
const newItem = {};
this.options.keys.forEach((key) => {
newItem[key] = newVarInputVal("str");
});
newItems = [...this.items, newItem];
} else {
newItems = [...this.items, newVarInputVal("str")];
} }
this.$emit("update:modelValue", newItems);
const row = {};
Object.entries(this.columns).forEach(([key, config]) => {
row[key] = config.defaultValue || newVarInputVal("str");
});
return [row];
}, },
/** getNewRowValue() {
* 移除指定索引的数组项 if (!this.columns) {
* 如果移除后数组为空则创建一个新的空项 // 使 defaultRowValue VarInputVal
*/ return this.defaultRowValue || newVarInputVal("str");
removeItem(index) { }
const newItems = [...this.items];
newItems.splice(index, 1); //
if (newItems.length === 0) { if (this.defaultRowValue) {
if (this.options.keys) { return this.defaultRowValue;
const newItem = {}; }
this.options.keys.forEach((key) => {
newItem[key] = newVarInputVal("str"); const row = {};
}); Object.entries(this.columns).forEach(([key, config]) => {
newItems.push(newItem); row[key] = config.defaultValue || newVarInputVal("str");
} else { });
newItems.push(newVarInputVal("str")); return row;
},
addRow() {
const newRows = [...this.rows, this.getNewRowValue()];
this.$emit("update:modelValue", newRows);
},
removeRow(index) {
const newRows = [...this.rows];
newRows.splice(index, 1);
if (newRows.length === 0) {
newRows.push(this.initializeRow()[0]);
}
this.$emit("update:modelValue", newRows);
},
updateValue(index, value) {
const newRows = [...this.rows];
newRows[index] = value;
this.$emit("update:modelValue", newRows);
},
updateColumn(index, columnKey, value) {
//
const newRows = this.rows.map((row, i) => {
//
if (i === index) {
return {
...row,
[columnKey]: value,
};
} }
} return row;
this.$emit("update:modelValue", newItems); });
},
/** this.$emit("update:modelValue", newRows);
* 更新单值模式下的值
*/
updateItemValue(index, value) {
const newItems = [...this.items];
newItems[index] = value;
this.$emit("update:modelValue", newItems);
},
/**
* 更新多键模式下指定键的值
*/
updateItemKeyValue(index, key, value) {
const newItems = [...this.items];
newItems[index] = {
...newItems[index],
[key]: value,
};
this.$emit("update:modelValue", newItems);
}, },
}, },
}); });

View File

@ -157,7 +157,7 @@ import BorderLabel from "components/composer/common/BorderLabel.vue";
* } * }
* *
* // * //
* options.items = ['User-Agent', 'Content-Type', 'Accept'] * options.optionKeys = ['User-Agent', 'Content-Type', 'Accept']
* { * {
* "User-Agent": newVarInputVal("str", "Mozilla/5.0"), * "User-Agent": newVarInputVal("str", "Mozilla/5.0"),
* "Content-Type": newVarInputVal("str", "text/html"), * "Content-Type": newVarInputVal("str", "text/html"),

View File

@ -130,23 +130,8 @@
<template v-if="argvs.optionType === 'json'"> <template v-if="argvs.optionType === 'json'">
<ArrayEditor <ArrayEditor
:model-value="argvs.selects" :model-value="argvs.selects"
:options="{ :columns="arrayEditorColumns"
keys: [ :default-row-value="arrayEditorDefaultRowValue"
{
value: 'id',
label: 'id',
width: 3,
},
{
value: 'title',
label: '标题',
},
{
value: 'description',
label: '描述',
},
],
}"
@update:model-value="updateArgvs('selects', $event)" @update:model-value="updateArgvs('selects', $event)"
> >
<template #header> <template #header>
@ -178,8 +163,7 @@ import VariableInput from "../common/VariableInput.vue";
import ArrayEditor from "../common/ArrayEditor.vue"; import ArrayEditor from "../common/ArrayEditor.vue";
import OperationCard from "../common/OperationCard.vue"; import OperationCard from "../common/OperationCard.vue";
import { parseFunction, stringifyArgv } from "js/composer/formatString"; import { parseFunction, stringifyArgv } from "js/composer/formatString";
import { newVarInputVal, isVarInputVal} from "js/composer/varInputValManager"; import { newVarInputVal, isVarInputVal } from "js/composer/varInputValManager";
const jsonDefaultSelects = new Array(3).fill().map((_, index) => ({ const jsonDefaultSelects = new Array(3).fill().map((_, index) => ({
id: newVarInputVal("var", index), id: newVarInputVal("var", index),
@ -226,6 +210,28 @@ export default defineComponent({
showCancelButton: false, showCancelButton: false,
closeOnSelect: true, closeOnSelect: true,
}, },
arrayEditorDefaultRowValue: {
id: newVarInputVal("var"),
title: newVarInputVal("str"),
description: newVarInputVal("str"),
},
arrayEditorColumns: {
id: {
label: "id",
width: 3,
noIcon: true,
},
title: {
label: "标题",
width: 4,
noIcon: true,
},
description: {
label: "描述",
width: 4,
noIcon: true,
},
},
}; };
}, },
computed: { computed: {

View File

@ -335,6 +335,7 @@ export const dataCommands = {
icon: "memory", icon: "memory",
width: 12, width: 12,
defaultValue: [newVarInputVal("var")], defaultValue: [newVarInputVal("var")],
defaultRowValue: newVarInputVal("var"),
disableToggleType: true, disableToggleType: true,
}, },
{ {

View File

@ -1,5 +1,109 @@
import { newVarInputVal } from "js/composer/varInputValManager"; import { newVarInputVal } from "js/composer/varInputValManager";
const SAVE_DIALOG_PROPERTIES = {
component: "CheckGroup",
icon: "settings",
label: "选项",
width: 12,
options: [
{ label: "显示隐藏文件", value: "showHiddenFiles" },
{ label: "允许创建文件夹Mac", value: "createDirectory" },
{
label: "将.App作为目录Mac",
value: "treatPackageAsDirectory",
},
{
label: "显示覆盖确认Linux",
value: "showOverwriteConfirmation",
},
{ label: "不添加到最近Win", value: "dontAddToRecent" },
],
};
const OPEN_DIALOG_PROPERTIES = {
...SAVE_DIALOG_PROPERTIES,
options: [
{ label: "选择文件", value: "openFile" },
{ label: "选择文件夹", value: "openDirectory" },
{ label: "允许多选", value: "multiSelections" },
{ label: "显示隐藏文件", value: "showHiddenFiles" },
{ label: "提示新建路径Win", value: "promptToCreate" },
{ label: "不添加到最近Win", value: "dontAddToRecent" },
{ label: "允许创建文件夹Mac", value: "createDirectory" },
{ label: "不解析符号链接Mac", value: "noResolveAliases" },
{
label: "将.App作为目录Mac",
value: "treatPackageAsDirectory",
},
],
};
const DIALOG_CONFIG = {
options: {
title: {
label: "标题",
component: "VariableInput",
icon: "title",
width: 6,
},
defaultPath: {
label: "默认路径",
component: "VariableInput",
icon: "folder",
width: 6,
},
buttonLabel: {
label: "按钮文本",
component: "VariableInput",
icon: "text_fields",
width: 6,
},
message: {
label: "提示信息",
component: "VariableInput",
icon: "info",
width: 6,
},
filters: {
topLabel: "过滤器",
component: "ArrayEditor",
icon: "filter_list",
width: 12,
defaultRowValue: [newVarInputVal("str"), newVarInputVal("var", "")],
columns: {
name: {
label: "文件类型",
noIcon: true,
width: 4,
},
extensions: {
label: "扩展名",
noIcon: true,
width: 7,
disableToggleType: true,
options: {
items: ["*", "jpg", "png", "gif", "txt", "json", "exe"],
multiSelect: true,
},
},
},
},
},
defaultValue: {
title: newVarInputVal("str", "请选择"),
defaultPath: newVarInputVal("str"),
buttonLabel: newVarInputVal("str", "选择"),
message: newVarInputVal("str", "请选择"),
filters: [
{
name: newVarInputVal("str", "file"),
extensions: newVarInputVal("var", '["*"]'),
},
],
properties: ["openFile", "showHiddenFiles"],
},
};
export const uiCommands = { export const uiCommands = {
label: "用户交互", label: "用户交互",
icon: "web", icon: "web",
@ -19,6 +123,7 @@ export const uiCommands = {
newVarInputVal("str", "是"), newVarInputVal("str", "是"),
newVarInputVal("str", "否"), newVarInputVal("str", "否"),
], ],
defaultRowValue: newVarInputVal("str"),
}, },
], ],
}, },
@ -33,17 +138,13 @@ export const uiCommands = {
label: "输入框", label: "输入框",
component: "ArrayEditor", component: "ArrayEditor",
width: 12, width: 12,
options: { columns: {
keys: [ label: {
{ label: "标签",
label: "标签", },
value: "label", value: {
}, label: "默认值",
{ },
label: "默认值",
value: "value",
},
],
}, },
defaultValue: [ defaultValue: [
{ {
@ -163,106 +264,43 @@ export const uiCommands = {
], ],
}, },
{ {
value: "quickcomposer.ui.showOpenDialog", value: "utools.showOpenDialog",
label: "文件选择框", label: "文件选择框",
desc: "显示一个文件选择框,返回选择的文件路径", desc: "显示一个文件选择框,返回选择的文件路径",
outputVariable: "filePaths", outputVariable: "filePaths",
saveOutput: true, saveOutput: true,
config: [
{
label: "标题",
component: "VariableInput",
defaultValue: newVarInputVal("str", "请选择文件"),
width: 6,
},
{
label: "默认路径",
component: "VariableInput",
defaultValue: newVarInputVal("str"),
width: 6,
placeholder: "默认打开的路径",
},
{
label: "按钮文本",
component: "VariableInput",
defaultValue: newVarInputVal("str", "选择"),
width: 3,
},
{
label: "提示信息",
component: "VariableInput",
defaultValue: newVarInputVal("str"),
width: 3,
placeholder: "对话框底部的提示信息",
defaultValue: newVarInputVal("str", "请选择"),
},
{
label: "扩展名",
component: "VariableInput",
width: 6,
options: {
items: ["*", "jpg", "png", "gif", "txt", "json", "exe"],
multiSelect: true,
},
defaultValue: newVarInputVal("var", '["*"]'),
disableToggleType: true,
},
],
subCommands: [ subCommands: [
{ {
value: "quickcomposer.ui.showOpenDialog", value: "utools.showOpenDialog",
label: "打开文件对话框", label: "打开文件对话框",
desc: "打开文件对话框", desc: "打开文件对话框",
icon: "folder_open", icon: "folder_open",
config: [ config: [
{ {
label: "选择选项", label: "选项",
component: "CheckGroup", component: "OptionEditor",
icon: "settings", defaultValue: DIALOG_CONFIG.defaultValue,
width: 12, options: {
options: [ ...DIALOG_CONFIG.options,
{ label: "选择文件", value: "openFile" }, properties: OPEN_DIALOG_PROPERTIES,
{ label: "选择文件夹", value: "openDirectory" }, },
{ label: "允许多选", value: "multiSelections" },
{ label: "显示隐藏文件", value: "showHiddenFiles" },
{ label: "提示新建路径Win", value: "promptToCreate" },
{ label: "不添加到最近Win", value: "dontAddToRecent" },
{ label: "允许创建文件夹Mac", value: "createDirectory" },
{ label: "不解析符号链接Mac", value: "noResolveAliases" },
{
label: "将.App作为目录Mac",
value: "treatPackageAsDirectory",
},
],
defaultValue: ["openFile", "showHiddenFiles"],
}, },
], ],
}, },
{ {
value: "quickcomposer.ui.showSaveDialog", value: "utools.showSaveDialog",
label: "保存文件对话框", label: "保存文件对话框",
desc: "保存文件对话框", desc: "保存文件对话框",
icon: "save", icon: "save",
config: [ config: [
{ {
label: "选择选项", label: "选项",
component: "CheckGroup", component: "OptionEditor",
icon: "settings", defaultValue: DIALOG_CONFIG.defaultValue,
width: 12, options: {
options: [ ...DIALOG_CONFIG.options,
{ label: "显示隐藏文件", value: "showHiddenFiles" }, properties: SAVE_DIALOG_PROPERTIES,
{ label: "允许创建文件夹Mac", value: "createDirectory" }, },
{
label: "将.App作为目录Mac",
value: "treatPackageAsDirectory",
},
{
label: "显示覆盖确认Linux",
value: "showOverwriteConfirmation",
},
{ label: "不添加到最近Win", value: "dontAddToRecent" },
],
defaultValue: ["showHiddenFiles"],
}, },
], ],
}, },

View File

@ -6,69 +6,101 @@ import {
} from "./varInputValManager"; } from "./varInputValManager";
/** /**
* 递归移除对象中的空值 * 处理单个值返回格式化后的字符串
* @param {Object} obj 要处理的对象
* @returns {Object} 处理后的对象
*/ */
const removeEmptyValues = (obj) => { const processValue = (value, parentPath = "") => {
return window.lodashM.omitBy(obj, (value) => { if (!value) return value;
// 如果value是VariableInput的输出则取其value值
const realValue = isVarInputVal(value) ? value.value : value; if (typeof value === "object") {
if (window.lodashM.isNil(realValue) || realValue === "") return true; if (isVarInputVal(value)) {
// 如果value是对象并且不是VariableInput的输出则递归移除空值 return stringifyVarInputVal(value);
if (typeof value === "object" && !isVarInputVal(value)) }
return window.lodashM.isEmpty(removeEmptyValues(value)); return processObject(value, parentPath);
return false; }
});
return typeof value === "string" ? `"${value}"` : value;
};
/**
* 格式化带缩进的值
*/
const formatWithIndent = (value, indent, isLast = true) => {
return `${indent}${value}${isLast ? "" : ","}`;
}; };
/** /**
* 递归处理对象的值并格式化成字符串 * 递归处理对象的值并格式化成字符串
* @param {Object} obj 要处理的对象
* @param {string} parentPath 父路径(用于缩进)
* @returns {string} 处理后的字符串
*/ */
const processObject = (obj, parentPath = "") => { const processObject = (obj, parentPath = "") => {
// 移除空值 // 移除空值
obj = removeEmptyValues(obj); obj = removeEmptyValues(obj);
// 如果是 VariableInput 的输出,直接用 stringifyVarInputVal 处理
if (isVarInputVal(obj)) { if (isVarInputVal(obj)) {
return stringifyVarInputVal(obj); return stringifyVarInputVal(obj);
} }
let result = "{\n"; const indentLevel = parentPath.split(".").length;
const indent = " ".repeat(indentLevel + 1);
const closingIndent = " ".repeat(indentLevel);
// 处理数组
if (Array.isArray(obj)) {
if (obj.length === 0) return "[]";
const items = obj.map((item, index) =>
formatWithIndent(
processValue(item, parentPath + " "),
indent,
index === obj.length - 1
)
);
return `[\n${items.join("\n")}\n${closingIndent}]`;
}
// 处理对象
const entries = Object.entries(obj); const entries = Object.entries(obj);
if (entries.length === 0) return "{}";
entries.forEach(([key, value], index) => { const items = entries.map(([key, value], index) =>
let valueStr = ""; formatWithIndent(
`"${key}": ${processValue(value, parentPath + " ")}`,
indent,
index === entries.length - 1
)
);
// 处理对象类型 return `{\n${items.join("\n")}\n${closingIndent}}`;
if (value && typeof value === "object") { };
// 如果是 VariableInput 的输出,直接用 stringifyVarInputVal 处理
if (isVarInputVal(value)) { /**
valueStr = stringifyVarInputVal(value); * 递归移除对象中的空值
} else { */
valueStr = processObject(value, parentPath + " "); const removeEmptyValues = (obj) => {
} const isEmptyValue = (value) => {
} const realValue = isVarInputVal(value) ? value.value : value;
// 处理其他类型 return window.lodashM.isNil(realValue) || realValue === "";
else if (value && typeof value === "string") { };
valueStr = `"${value}"`;
} else { const processObjectValue = (value) => {
valueStr = value; if (typeof value === "object" && !isVarInputVal(value)) {
return removeEmptyValues(value);
} }
return value;
};
// 添加缩进 if (Array.isArray(obj)) {
const indent = " ".repeat(parentPath.split(".").length + 1); return obj.filter((value) => !isEmptyValue(value)).map(processObjectValue);
result += `${indent}"${key}": ${valueStr}`; }
if (index < entries.length - 1) result += ",";
result += "\n";
});
// 闭合括号的缩进 return window.lodashM.omitBy(
const closingIndent = " ".repeat(parentPath.split(".").length); obj,
result += `${closingIndent}}`; (value) =>
return result; isEmptyValue(value) ||
(typeof value === "object" &&
!isVarInputVal(value) &&
window.lodashM.isEmpty(removeEmptyValues(value)))
);
}; };
/** /**
@ -264,9 +296,7 @@ export const parseFunction = (functionStr, options = {}) => {
: node.value; : node.value;
// null // null
case "NullLiteral": case "NullLiteral":
return shouldUseVariableFormat return shouldUseVariableFormat ? newVarInputVal("var", "null") : null;
? newVarInputVal("var", "null")
: null;
case "Identifier": case "Identifier":
// 标识符(变量)总是不带引号的 // 标识符(变量)总是不带引号的
return shouldUseVariableFormat return shouldUseVariableFormat