-
@@ -170,7 +175,11 @@ export default defineComponent({
},
getSummary(argvs) {
// 虽然header里对溢出做了处理,但是这里截断主要是为了节省存储空间
- return argvs
+ const funcNameLabel = this.localCommand.functionSelector?.options.find(
+ (option) => option.value === this.functionName
+ )?.label;
+ const subFeature = funcNameLabel ? `${funcNameLabel} ` : "";
+ const allArgvs = argvs
.map((item) =>
item?.hasOwnProperty("__varInputVal__")
? window.lodashM.truncate(item.value, {
@@ -179,8 +188,8 @@ export default defineComponent({
})
: item
)
- .filter((item) => item != null)
- .join("、");
+ .filter((item) => item != null && item != "");
+ return `${subFeature}${allArgvs.join(",")}`;
},
updateModelValue(functionName, argvs) {
this.$emit("update:modelValue", {
@@ -201,6 +210,23 @@ export default defineComponent({
this.updateModelValue(this.functionName, this.defaultArgvs);
}
},
+ watch: {
+ functionName: {
+ immediate: true,
+ handler(newVal) {
+ // 当操作卡片改变时,确保它在视图中可见
+ this.$nextTick(() => {
+ document
+ .querySelector(`.operation-card[data-value="${newVal}"]`)
+ ?.scrollIntoView({
+ behavior: "smooth",
+ block: "nearest",
+ inline: "nearest",
+ });
+ });
+ },
+ },
+ },
});
@@ -213,12 +239,56 @@ export default defineComponent({
}
.flex-item {
- min-width: 100px; /* 设置最小宽度以确保内容可读性 */
+ min-width: 100px;
}
@media (max-width: 600px) {
.flex-item {
- flex: 1 1 100% !important; /* 在小屏幕上强制换行 */
+ flex: 1 1 100% !important;
}
}
+
+.operation-cards {
+ display: flex;
+ align-items: center;
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
+ padding: 1px;
+ gap: 8px;
+ border-radius: 8px;
+}
+
+.operation-cards::-webkit-scrollbar {
+ display: none;
+}
+
+.operation-card {
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ border: 1px solid transparent;
+ border-radius: 6px;
+ min-width: 72px;
+ padding: 2px 0;
+ background: rgba(0, 0, 0, 0.05);
+}
+
+.body--dark .operation-card {
+ background: rgba(0, 0, 0, 0.05);
+}
+
+.operation-card:hover {
+ background: var(--q-primary-opacity-5);
+ transform: translateY(-1px);
+ border: 1px solid var(--q-primary-opacity-10);
+}
+
+.operation-card.active {
+ border-color: var(--q-primary);
+ background: var(--q-primary-opacity-5);
+}
+
+.body--dark .operation-card.active {
+ border-color: var(--q-primary-opacity-50);
+}
diff --git a/src/css/composer.css b/src/css/composer.css
new file mode 100644
index 0000000..61f1b9c
--- /dev/null
+++ b/src/css/composer.css
@@ -0,0 +1,146 @@
+/* 操作卡片样式 */
+.command-composer .operation-cards {
+ display: flex;
+ align-items: center;
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
+ padding: 1px;
+ gap: 8px;
+ border-radius: 8px;
+}
+
+.command-composer .operation-cards::-webkit-scrollbar {
+ display: none;
+}
+
+.command-composer .operation-card {
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ border: 1px solid transparent;
+ border-radius: 6px;
+ min-width: 72px;
+ max-height: 36px;
+ background: rgba(0, 0, 0, 0.05);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.command-composer .operation-card:hover {
+ background: var(--q-primary-opacity-5);
+ transform: translateY(-1px);
+ border: 1px solid var(--q-primary-opacity-10);
+}
+
+.command-composer .operation-card.active {
+ border-color: var(--q-primary);
+ background: var(--q-primary-opacity-5);
+}
+
+/* 暗色模式适配 */
+.body--dark .command-composer .operation-card {
+ background: rgba(255, 255, 255, 0.03);
+}
+
+.body--dark .command-composer .operation-card.active {
+ border-color: var(--q-primary-opacity-50);
+}
+
+/* 滚动美化 */
+.command-composer .q-scrollarea__thumb {
+ width: 2px;
+ opacity: 0.4;
+ transition: opacity 0.3s ease;
+}
+
+.command-composer .q-scrollarea__thumb:hover {
+ opacity: 0.8;
+}
+
+/* 布局更加紧凑 */
+/* 输入框高度及字体 */
+.command-composer .q-field--filled:not(.q-textarea) .q-field__control,
+.command-composer .q-field--filled:not(.q-textarea) .q-field__control>*,
+.command-composer .q-field--filled:not(.q-field--labeled):not(.q-textarea) .q-field__native {
+ max-height: 36px !important;
+ min-height: 36px !important;
+}
+
+.command-composer .q-field--filled .q-field__control,
+.command-composer .q-field--filled .q-field__control>*,
+.command-composer .q-field--filled .q-field__native {
+ border-radius: 5px;
+ font-size: 12px;
+}
+
+/* 输入框图标大小 */
+.command-composer .q-field--filled .q-field__control .q-icon {
+ font-size: 18px;
+}
+
+/* 输入框标签字体大小,占位时的位置 */
+.command-composer .q-field--filled .q-field__label {
+ font-size: 11px;
+ top: 11px;
+}
+
+/* 输入框标签悬浮的位置 */
+.command-composer .q-field--filled .q-field--float .q_field__label {
+ transform: translateY(-35%) scale(0.7);
+}
+
+/* 去除filled输入框边框 */
+.command-composer .q-field--filled .q-field__control:before {
+ border: none;
+}
+
+/* 去除filled输入框下划线 */
+.command-composer .q-field--filled .q-field__control:after {
+ height: 0;
+ border-bottom: none;
+}
+
+/* 输入框背景颜色及内边距 */
+.command-composer .q-field--filled .q-field__control {
+ background: rgba(0, 0, 0, 0.03);
+ padding: 0 8px;
+}
+
+/* 输入框聚焦时的背景颜色 */
+.command-composer .q-field--filled.q-field--highlighted .q-field__control {
+ background: rgba(0, 0, 0, 0.03);
+}
+
+/* 暗黑模式下的输入框背景颜色 */
+.body--dark .command-composer .q-field--filled .q-field__control {
+ background: rgba(255, 255, 255, 0.04);
+}
+
+/* 暗黑模式下输入框聚焦时的背景颜色 */
+.body--dark .command-composer .q-field--filled.q-field--highlighted .q-field__control {
+ background: rgba(255, 255, 255, 0.08);
+}
+
+/* checkbox/toggle大小及字体 */
+.command-composer .q-checkbox__label,
+.command-composer .q-toggle__label {
+ font-size: 12px;
+}
+
+.command-composer .q-checkbox__inner,
+.command-composer .q-toggle__inner {
+ font-size: 28px;
+ margin: 4px 0px;
+}
+
+/* 暗黑模式下的标签栏背景颜色 */
+.body--dark .command-composer .q-tab,
+.body--dark .command-composer .q-tab-panel {
+ background-color: #303133;
+}
+
+.body--dark .command-composer .q-tab--inactive {
+ opacity: 2;
+}
diff --git a/src/js/composer/cardComponents.js b/src/js/composer/cardComponents.js
index bf5dd93..3ccf045 100644
--- a/src/js/composer/cardComponents.js
+++ b/src/js/composer/cardComponents.js
@@ -65,5 +65,17 @@ export const OsEditor = defineAsyncComponent(() =>
);
export const PathEditor = defineAsyncComponent(() =>
- import("src/components/composer/system/PathEditor.vue")
+ import("components/composer/system/PathEditor.vue")
+);
+export const ZlibEditor = defineAsyncComponent(() =>
+ import("components/composer/file/ZlibEditor.vue")
+);
+export const UrlEditor = defineAsyncComponent(() =>
+ import("components/composer/network/UrlEditor.vue")
+);
+export const DnsEditor = defineAsyncComponent(() =>
+ import("components/composer/network/DnsEditor.vue")
+);
+export const BufferEditor = defineAsyncComponent(() =>
+ import("components/composer/developer/BufferEditor.vue")
);
diff --git a/src/js/composer/commands/developerCommands.js b/src/js/composer/commands/developerCommands.js
new file mode 100644
index 0000000..57161d6
--- /dev/null
+++ b/src/js/composer/commands/developerCommands.js
@@ -0,0 +1,14 @@
+export const developerCommands = {
+ label: "开发相关",
+ icon: "code",
+ defaultOpened: true,
+ commands: [
+ {
+ value: "quickcomposer.developer.buffer",
+ label: "Buffer操作",
+ desc: "Buffer创建、转换和操作",
+ component: "BufferEditor",
+ icon: "memory",
+ },
+ ],
+};
diff --git a/src/js/composer/commands/fileCommands.js b/src/js/composer/commands/fileCommands.js
index d015636..42ee0e1 100644
--- a/src/js/composer/commands/fileCommands.js
+++ b/src/js/composer/commands/fileCommands.js
@@ -46,5 +46,13 @@ export const fileCommands = {
},
],
},
+ {
+ value: "quickcomposer.file.zlib",
+ label: "压缩解压",
+ desc: "使用 zlib 进行数据压缩和解压",
+ component: "ZlibEditor",
+ icon: "compress",
+ isAsync: true,
+ },
],
};
diff --git a/src/js/composer/commands/index.js b/src/js/composer/commands/index.js
index de35771..014e01e 100644
--- a/src/js/composer/commands/index.js
+++ b/src/js/composer/commands/index.js
@@ -6,6 +6,7 @@ import { textCommands } from "./textCommands";
import { otherCommands } from "./otherCommands";
import { simulateCommands } from "./simulateCommands";
import { controlCommands } from "./controlCommands";
+import { developerCommands } from "./developerCommands";
export const commandCategories = [
fileCommands,
@@ -16,4 +17,5 @@ export const commandCategories = [
controlCommands,
otherCommands,
simulateCommands,
+ developerCommands,
];
diff --git a/src/js/composer/commands/networkCommands.js b/src/js/composer/commands/networkCommands.js
index 0712231..52b4884 100644
--- a/src/js/composer/commands/networkCommands.js
+++ b/src/js/composer/commands/networkCommands.js
@@ -44,5 +44,20 @@ export const networkCommands = {
isAsync: true,
icon: "http",
},
+ {
+ value: "quickcomposer.network.url",
+ label: "URL操作",
+ desc: "URL解析、格式化和参数处理",
+ component: "UrlEditor",
+ icon: "link",
+ },
+ {
+ value: "quickcomposer.network.dns",
+ label: "DNS操作",
+ desc: "DNS解析和查询",
+ component: "DnsEditor",
+ icon: "dns",
+ isAsync: true,
+ },
],
};
diff --git a/src/js/composer/commands/textCommands.js b/src/js/composer/commands/textCommands.js
index ec04872..e803102 100644
--- a/src/js/composer/commands/textCommands.js
+++ b/src/js/composer/commands/textCommands.js
@@ -22,28 +22,42 @@ export const textCommands = {
{
label: "Base64编码",
value: "quickcomposer.text.base64Encode",
+ icon: "title",
},
{
label: "Base64解码",
value: "quickcomposer.text.base64Decode",
+ icon: "title",
},
{
label: "十六进制编码",
value: "quickcomposer.text.hexEncode",
+ icon: "code",
},
{
label: "十六进制解码",
value: "quickcomposer.text.hexDecode",
+ icon: "code",
+ },
+ {
+ label: "URL编码",
+ value: "quickcomposer.text.urlEncode",
+ icon: "link",
+ },
+ {
+ label: "URL解码",
+ value: "quickcomposer.text.urlDecode",
+ icon: "link",
},
- { label: "URL编码", value: "quickcomposer.text.urlEncode" },
- { label: "URL解码", value: "quickcomposer.text.urlDecode" },
{
label: "HTML编码",
value: "quickcomposer.text.htmlEncode",
+ icon: "html",
},
{
label: "HTML解码",
value: "quickcomposer.text.htmlDecode",
+ icon: "html",
},
],
width: 3,
@@ -75,11 +89,31 @@ export const textCommands = {
functionSelector: {
selectLabel: "哈希算法",
options: [
- { label: "MD5", value: "quickcomposer.text.md5Hash" },
- { label: "SHA1", value: "quickcomposer.text.sha1Hash" },
- { label: "SHA256", value: "quickcomposer.text.sha256Hash" },
- { label: "SHA512", value: "quickcomposer.text.sha512Hash" },
- { label: "SM3", value: "quickcomposer.text.sm3Hash" },
+ {
+ label: "MD5",
+ value: "quickcomposer.text.md5Hash",
+ icon: "functions",
+ },
+ {
+ label: "SHA1",
+ value: "quickcomposer.text.sha1Hash",
+ icon: "functions",
+ },
+ {
+ label: "SHA256",
+ value: "quickcomposer.text.sha256Hash",
+ icon: "functions",
+ },
+ {
+ label: "SHA512",
+ value: "quickcomposer.text.sha512Hash",
+ icon: "functions",
+ },
+ {
+ label: "SM3",
+ value: "quickcomposer.text.sm3Hash",
+ icon: "functions",
+ },
],
},
width: 3,
diff --git a/src/js/composer/formatString.js b/src/js/composer/formatString.js
index 412f547..3b2f2f3 100644
--- a/src/js/composer/formatString.js
+++ b/src/js/composer/formatString.js
@@ -150,12 +150,16 @@ const isPathMatched = (path, patterns) => {
const regexPattern = pattern
// 先处理 **,将其转换为特殊标记
.replace(/\*\*/g, "###DOUBLEWILDCARD###")
+ // 处理数组索引通配符 [*]
+ .replace(/\[\*\]/g, "###ARRAYINDEX###")
// 处理普通的 *
- .replace(/\*/g, "[^/.]+")
+ .replace(/\*/g, "[^/.\\[\\]]+")
// 转义特殊字符
- .replace(/[.]/g, "\\$&")
+ .replace(/[.[\]]/g, "\\$&")
// 还原 ** 为正则表达式
- .replace(/###DOUBLEWILDCARD###/g, ".*");
+ .replace(/###DOUBLEWILDCARD###/g, ".*")
+ // 还原数组索引通配符
+ .replace(/###ARRAYINDEX###/g, "\\[\\d+\\]");
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
@@ -188,25 +192,28 @@ const isPathMatched = (path, patterns) => {
* - arg1.data.* - 匹配data下的所有直接子属性
* - arg2.params.** - 匹配params下的所有属性(包括嵌套)
*
- * 3. 通配符:
- * - * - 匹配单个层级的任意字符(不包含点号)
- * - ** - 匹配任意层级(包含点号)
+ * 3. 数组索引:
+ * - arg0[0] - 匹配数组的第一个元素
+ * - arg0[*] - 匹配数组的任意元素
+ * - arg0[*].name - 匹配数组任意元素的name属性
+ * - arg0[*].** - 匹配数组任意元素的所有属性(包括嵌套)
*
- * 4. 排除规则:
+ * 4. 通配符:
+ * - * - 匹配单个层级的任意字符(不包含点号和方括号)
+ * - ** - 匹配任意层级(包含点号)
+ * - [*] - 匹配任意数组索引
+ *
+ * 5. 排除规则:
* - !pattern - 排除匹配的路径
* - 排除优先级高于包含
*
- * 5. 示例:
+ * 6. 示例:
* - arg0 - 匹配第一个参数
* - arg*.headers.** - 匹配任意参数中headers下的所有属性
* - arg*.data.* - 匹配任意参数中data下的直接子属性
+ * - arg0[*] - 匹配第一个参数的所有数组元素
+ * - arg0[*].name - 匹配第一个参数数组中所有元素的name属性
* - !arg*.headers.Content-Type - 排除所有参数中的Content-Type头
- * - arg*.headers.Accept* - 匹配所有以Accept开头的头部
- *
- * 6. 使用建议:
- * - 优先使用精确匹配(arg0, arg1.data)
- * - 使用通配符时注意层级(* vs **)
- * - 合理使用排除规则避免过度匹配
*
* @returns {Object} 解析结果,包含函数名和参数数组
*/