2025-01-06 09:43:48 +08:00

722 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="buffer-editor">
<!-- 操作类型选择 -->
<div class="operation-cards">
<div
v-for="op in operations"
:key="op.name"
:class="['operation-card', { active: argvs.operation === op.name }]"
@click="updateArgvs('operation', op.name)"
:data-value="op.name"
>
<q-icon
:name="op.icon"
size="16px"
:color="argvs.operation === op.name ? 'primary' : 'grey'"
/>
<div class="text-caption">{{ op.label }}</div>
</div>
</div>
<!-- 操作配置 -->
<div class="operation-options q-mt-sm">
<div class="options-container">
<!-- 创建 Buffer -->
<div v-if="argvs.operation === 'from'" class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.data"
@update:model-value="(val) => updateArgvs('data', val)"
label="数据"
icon="text_fields"
class="col"
/>
<q-select
:model-value="argvs.encoding"
@update:model-value="(val) => updateArgvs('encoding', val)"
:options="encodings"
label="编码"
dense
filled
emit-value
map-options
options-dense
class="col-3"
/>
</div>
<!-- 转换为字符串 -->
<div
v-if="argvs.operation === 'toString'"
class="column q-col-gutter-sm"
>
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
icon="memory"
/>
<div class="row q-col-gutter-sm">
<div class="col-12 col-sm-4">
<q-select
:model-value="argvs.encoding"
@update:model-value="(val) => updateArgvs('encoding', val)"
:options="encodings"
label="编码"
dense
filled
emit-value
map-options
options-dense
/>
</div>
<div class="col-12 col-sm-4">
<NumberInput
:model-value="argvs.start"
@update:model-value="(val) => updateArgvs('start', val)"
label="起始位置"
/>
</div>
<div class="col-12 col-sm-4">
<NumberInput
:model-value="argvs.end"
@update:model-value="(val) => updateArgvs('end', val)"
label="结束位置"
/>
</div>
</div>
</div>
<!-- 写入数据 -->
<div v-if="argvs.operation === 'write'" class="column q-col-gutter-sm">
<div class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
icon="memory"
class="col"
/>
<VariableInput
:model-value="argvs.string"
@update:model-value="(val) => updateArgvs('string', val)"
label="要写入的字符串"
icon="edit"
class="col"
/>
</div>
<div class="row q-col-gutter-sm">
<div class="col-4">
<NumberInput
:model-value="argvs.offset"
@update:model-value="(val) => updateArgvs('offset', val)"
label="偏移量"
/>
</div>
<div class="col-4">
<NumberInput
:model-value="argvs.length"
@update:model-value="(val) => updateArgvs('length', val)"
label="长度"
/>
</div>
<div class="col-4">
<q-select
:model-value="argvs.encoding"
@update:model-value="(val) => updateArgvs('encoding', val)"
:options="encodings"
label="编码"
dense
filled
emit-value
map-options
options-dense
/>
</div>
</div>
</div>
<!-- 填充数据 -->
<div v-if="argvs.operation === 'fill'" class="column q-col-gutter-sm">
<div class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
icon="memory"
class="col"
/>
<VariableInput
:model-value="argvs.value"
@update:model-value="(val) => updateArgvs('value', val)"
label="填充值"
icon="format_color_fill"
class="col"
/>
</div>
<div class="row q-col-gutter-sm">
<div class="col-4">
<NumberInput
:model-value="argvs.offset"
@update:model-value="(val) => updateArgvs('offset', val)"
label="起始位置"
/>
</div>
<div class="col-12 col-sm-4">
<NumberInput
:model-value="argvs.end"
@update:model-value="(val) => updateArgvs('end', val)"
label="结束位置"
/>
</div>
<div class="col-12 col-sm-4">
<q-select
:model-value="argvs.encoding"
@update:model-value="(val) => updateArgvs('encoding', val)"
:options="encodings"
label="编码"
dense
filled
emit-value
map-options
options-dense
/>
</div>
</div>
</div>
<!-- 复制数据 -->
<div v-if="argvs.operation === 'copy'" class="column q-col-gutter-sm">
<div class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.source"
@update:model-value="(val) => updateArgvs('source', val)"
label="源Buffer"
icon="content_copy"
class="col"
/>
<VariableInput
:model-value="argvs.target"
@update:model-value="(val) => updateArgvs('target', val)"
label="目标Buffer"
icon="save"
class="col"
/>
</div>
<div class="row q-col-gutter-sm">
<div class="col-4">
<NumberInput
:model-value="argvs.targetStart"
@update:model-value="(val) => updateArgvs('targetStart', val)"
label="目标起始位置"
/>
</div>
<div class="col-12 col-sm-4">
<NumberInput
:model-value="argvs.sourceStart"
@update:model-value="(val) => updateArgvs('sourceStart', val)"
label="源起始位置"
/>
</div>
<div class="col-12 col-sm-4">
<NumberInput
:model-value="argvs.sourceEnd"
@update:model-value="(val) => updateArgvs('sourceEnd', val)"
label="源结束位置"
/>
</div>
</div>
</div>
<!-- 比较数据 -->
<div
v-if="argvs.operation === 'compare'"
class="column q-col-gutter-sm"
>
<div class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buf1"
@update:model-value="(val) => updateArgvs('buf1', val)"
label="Buffer 1"
icon="memory"
class="col"
/>
<VariableInput
:model-value="argvs.buf2"
@update:model-value="(val) => updateArgvs('buf2', val)"
label="Buffer 2"
icon="memory"
class="col"
/>
</div>
</div>
<!-- 连接 Buffer -->
<div v-if="argvs.operation === 'concat'" class="column q-gutter-sm">
<ArrayEditor
:model-value="argvs.buffers"
@update:model-value="(val) => updateArgvs('buffers', val)"
label="Buffer"
icon="memory"
/>
<NumberInput
:model-value="argvs.totalLength"
@update:model-value="(val) => updateArgvs('totalLength', val)"
label="总长度可选"
/>
</div>
<!-- 查找数据 -->
<div
v-if="argvs.operation === 'indexOf'"
class="column q-col-gutter-sm"
>
<div class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
icon="memory"
class="col"
/>
<VariableInput
:model-value="argvs.value"
@update:model-value="(val) => updateArgvs('value', val)"
label="要查找的值"
icon="search"
class="col"
/>
</div>
<div class="row q-col-gutter-sm">
<div class="col-12 col-sm-6">
<NumberInput
:model-value="argvs.byteOffset"
@update:model-value="(val) => updateArgvs('byteOffset', val)"
label="起始位置"
/>
</div>
<div class="col-12 col-sm-6">
<q-select
:model-value="argvs.encoding"
@update:model-value="(val) => updateArgvs('encoding', val)"
:options="encodings"
label="编码"
dense
filled
emit-value
map-options
options-dense
/>
</div>
</div>
</div>
<!-- 切片数据 -->
<div v-if="argvs.operation === 'slice'" class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
icon="memory"
class="col"
/>
<NumberInput
:model-value="argvs.start"
@update:model-value="(val) => updateArgvs('start', val)"
label="起始位置"
class="col"
/>
<NumberInput
:model-value="argvs.end"
@update:model-value="(val) => updateArgvs('end', val)"
label="结束位置"
class="col"
/>
</div>
<!-- 交换字节序 -->
<div v-if="argvs.operation === 'swap'" class="row q-col-gutter-sm">
<VariableInput
:model-value="argvs.buffer"
@update:model-value="(val) => updateArgvs('buffer', val)"
label="Buffer"
class="col"
icon="memory"
/>
<q-select
:model-value="argvs.size"
@update:model-value="(val) => updateArgvs('size', val)"
:options="swapSizes"
label="字节大小"
class="col-3"
dense
filled
emit-value
map-options
options-dense
/>
</div>
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import { stringifyArgv, parseFunction } from "js/composer/formatString";
import VariableInput from "components/composer/ui/VariableInput.vue";
import NumberInput from "components/composer/ui/NumberInput.vue";
import ArrayEditor from "components/composer/ui/ArrayEditor.vue";
export default defineComponent({
name: "BufferEditor",
components: {
VariableInput,
NumberInput,
ArrayEditor,
},
props: {
modelValue: {
type: Object,
required: true,
},
},
emits: ["update:modelValue"],
data() {
return {
operations: [
{ name: "from", label: "创建Buffer", icon: "add_box" },
{ name: "toString", label: "转换字符串", icon: "text_fields" },
{ name: "write", label: "写入数据", icon: "edit" },
{ name: "fill", label: "填充数据", icon: "format_color_fill" },
{ name: "copy", label: "复制数据", icon: "content_copy" },
{ name: "compare", label: "比较数据", icon: "compare" },
{ name: "concat", label: "连接Buffer", icon: "merge" },
{ name: "indexOf", label: "查找数据", icon: "search" },
{ name: "slice", label: "切片数据", icon: "content_cut" },
{ name: "swap", label: "交换字节序", icon: "swap_horiz" },
],
encodings: [
{ label: "UTF-8", value: "utf8" },
{ label: "UTF-16LE", value: "utf16le" },
{ label: "Latin1", value: "latin1" },
{ label: "Base64", value: "base64" },
{ label: "Hex", value: "hex" },
{ label: "ASCII", value: "ascii" },
{ label: "Binary", value: "binary" },
{ label: "UCS-2", value: "ucs2" },
],
swapSizes: [
{ label: "16", value: 16 },
{ label: "32", value: 32 },
{ label: "64", value: 64 },
],
defaultArgvs: {
operation: "from",
data: {
value: "",
isString: true,
__varInputVal__: true,
},
buffer: {
value: "",
isString: false,
__varInputVal__: true,
},
encoding: "utf8",
start: 0,
end: 0,
string: {
value: "",
isString: true,
__varInputVal__: true,
},
offset: 0,
length: 0,
value: {
value: "",
isString: true,
__varInputVal__: true,
},
source: {
value: "",
isString: false,
__varInputVal__: true,
},
target: {
value: "",
isString: false,
__varInputVal__: true,
},
targetStart: 0,
sourceStart: 0,
sourceEnd: 0,
buf1: {
value: "",
isString: false,
__varInputVal__: true,
},
buf2: {
value: "",
isString: false,
__varInputVal__: true,
},
buffers: [
{
value: "",
isString: false,
__varInputVal__: true,
},
],
totalLength: undefined,
byteOffset: 0,
size: 16,
},
};
},
computed: {
argvs: {
get() {
return (
this.modelValue.argvs ||
this.parseCodeToArgvs(this.modelValue.code) || {
...this.defaultArgvs,
}
);
},
set(value) {
this.updateModelValue(value);
},
},
},
methods: {
generateCode(argvs = this.argvs) {
switch (argvs.operation) {
case "from":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.data
)}, "${argvs.encoding}")`;
case "toString":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, "${argvs.encoding}", ${argvs.start}, ${argvs.end})`;
case "write":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, ${stringifyArgv(argvs.string)}, ${argvs.offset}, ${
argvs.length
}, "${argvs.encoding}")`;
case "fill":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, ${stringifyArgv(argvs.value)}, ${argvs.offset}, ${argvs.end}, "${
argvs.encoding
}")`;
case "copy":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.source
)}, ${stringifyArgv(argvs.target)}, ${argvs.targetStart}, ${
argvs.sourceStart
}, ${argvs.sourceEnd})`;
case "compare":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buf1
)}, ${stringifyArgv(argvs.buf2)})`;
case "concat":
const buffersStr = argvs.buffers
.map((buf) => stringifyArgv(buf))
.join(", ");
return `${this.modelValue.value}.${argvs.operation}([${buffersStr}]${
argvs.totalLength !== undefined ? `, ${argvs.totalLength}` : ""
})`;
case "indexOf":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, ${stringifyArgv(argvs.value)}, ${argvs.byteOffset}, "${
argvs.encoding
}")`;
case "slice":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, ${argvs.start}, ${argvs.end})`;
case "swap":
return `${this.modelValue.value}.${argvs.operation}(${stringifyArgv(
argvs.buffer
)}, ${argvs.size})`;
default:
return `${this.modelValue.value}.${argvs.operation}()`;
}
},
parseCodeToArgvs(code) {
if (!code) return null;
try {
// 定义需要使用variable格式的路径
const variableFormatPaths = ["arg0", "arg0[*]"];
const subFunc = code.match(/buffer\.(\w+)\((.*)\)/);
switch (subFunc[1]) {
case "write":
case "fill":
case "copy":
case "compare":
case "indexOf":
variableFormatPaths.push("arg1");
break;
}
// 使用 parseFunction 解析代码
const result = parseFunction(code, { variableFormatPaths });
if (!result) return this.defaultArgvs;
const operation = result.name.split(".").pop();
const args = result.args;
const newArgvs = {
...this.defaultArgvs,
operation,
};
switch (operation) {
case "from":
newArgvs.data = args[0];
newArgvs.encoding = args[1]?.value || "utf8";
break;
case "toString":
newArgvs.buffer = args[0];
newArgvs.encoding = args[1]?.value || "utf8";
newArgvs.start = args[2] ?? 0;
newArgvs.end = args[3] ?? 0;
break;
case "write":
newArgvs.buffer = args[0];
newArgvs.string = args[1];
newArgvs.offset = args[2] ?? 0;
newArgvs.length = args[3] ?? 0;
newArgvs.encoding = args[4]?.value || "utf8";
break;
case "fill":
newArgvs.buffer = args[0];
newArgvs.value = args[1];
newArgvs.offset = args[2] ?? 0;
newArgvs.end = args[3] ?? 0;
newArgvs.encoding = args[4]?.value || "utf8";
break;
case "copy":
newArgvs.source = args[0];
newArgvs.target = args[1];
newArgvs.targetStart = args[2] ?? 0;
newArgvs.sourceStart = args[3] ?? 0;
newArgvs.sourceEnd = args[4] ?? 0;
break;
case "compare":
newArgvs.buf1 = args[0];
newArgvs.buf2 = args[1];
break;
case "concat":
if (Array.isArray(args[0])) {
newArgvs.buffers = args[0];
}
newArgvs.totalLength = args[1];
break;
case "indexOf":
newArgvs.buffer = args[0];
newArgvs.value = args[1];
newArgvs.byteOffset = args[2] ?? 0;
newArgvs.encoding = args[3]?.value || "utf8";
break;
case "slice":
newArgvs.buffer = args[0];
newArgvs.start = args[1] ?? 0;
newArgvs.end = args[2] ?? 0;
break;
case "swap":
newArgvs.buffer = args[0];
newArgvs.size = args[1] ?? 16;
break;
}
return newArgvs;
} catch (e) {
console.error("解析Buffer参数失败:", e);
return this.defaultArgvs;
}
},
updateArgvs(key, value) {
this.argvs = {
...this.argvs,
[key]: value,
};
},
getSummary(argvs) {
const op = this.operations.find(
(op) => op.name === argvs.operation
)?.label;
return op;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
summary: this.getSummary(argvs),
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
watch: {
"argvs.operation": {
immediate: true,
handler(newVal) {
this.$nextTick(() => {
document
.querySelector(`.operation-card[data-value="${newVal}"]`)
?.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "nearest",
});
});
},
},
},
});
</script>
<style scoped>
.buffer-editor {
display: flex;
flex-direction: column;
}
.options-container {
min-height: 32px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>