mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-06 13:04:10 +08:00
完善编排的文件操作模块
This commit is contained in:
parent
3cec2f94f2
commit
9bcea1e575
@ -126,6 +126,4 @@ async function archive(
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
archive,
|
||||
};
|
||||
module.exports = archive;
|
||||
|
@ -7,23 +7,22 @@ const path = require("path");
|
||||
* @param {string} config.filePath 文件路径
|
||||
* @param {string} config.encoding 编码方式
|
||||
* @param {string} config.readMode 读取模式
|
||||
* @param {string} config.flag 读取标志
|
||||
* @param {number} [config.start] 起始位置
|
||||
* @param {number} [config.length] 读取长度
|
||||
* @returns {Promise<string|Buffer>} 文件内容
|
||||
*/
|
||||
async function read(config) {
|
||||
const { filePath, encoding, readMode, flag, start, length } = config;
|
||||
const { filePath, encoding, readMode, start, length } = config;
|
||||
|
||||
if (readMode === "all") {
|
||||
return await fs.readFile(filePath, { encoding, flag });
|
||||
return await fs.readFile(filePath, { encoding });
|
||||
} else if (
|
||||
readMode === "start" &&
|
||||
typeof start === "number" &&
|
||||
typeof length === "number"
|
||||
) {
|
||||
// 指定位置读取
|
||||
const fileHandle = await fs.open(filePath, flag || "r");
|
||||
const fileHandle = await fs.open(filePath, "r");
|
||||
try {
|
||||
const buffer = Buffer.alloc(length);
|
||||
const { bytesRead } = await fileHandle.read(buffer, 0, length, start);
|
||||
@ -39,12 +38,11 @@ async function read(config) {
|
||||
// 按行读取,暂时使用全部读取然后分行的方式
|
||||
const content = await fs.readFile(filePath, {
|
||||
encoding: encoding || "utf8",
|
||||
flag,
|
||||
});
|
||||
return content.split(/\r?\n/);
|
||||
} else {
|
||||
// 默认使用全部读取
|
||||
return await fs.readFile(filePath, { encoding, flag });
|
||||
return await fs.readFile(filePath, { encoding });
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,20 +83,12 @@ async function write(config) {
|
||||
* 文件删除操作
|
||||
*/
|
||||
async function remove(config) {
|
||||
const { filePath, recursive, force, targetType } = config;
|
||||
const { filePath, recursive, force } = config;
|
||||
|
||||
// 检查文件是否存在
|
||||
try {
|
||||
const stats = await fs.lstat(filePath);
|
||||
|
||||
// 检查目标类型
|
||||
if (targetType === "file" && !stats.isFile()) {
|
||||
throw new Error("目标不是文件");
|
||||
}
|
||||
if (targetType === "directory" && !stats.isDirectory()) {
|
||||
throw new Error("目标不是目录");
|
||||
}
|
||||
|
||||
// 执行删除操作
|
||||
if (stats.isDirectory()) {
|
||||
await fs.rm(filePath, { recursive, force });
|
||||
@ -115,39 +105,16 @@ async function remove(config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件管理操作
|
||||
* 文件权限操作
|
||||
*/
|
||||
async function manage(config) {
|
||||
const {
|
||||
filePath,
|
||||
manageOperation,
|
||||
newPath,
|
||||
mode,
|
||||
uid,
|
||||
gid,
|
||||
recursive,
|
||||
targetType,
|
||||
} = config;
|
||||
async function permission(config) {
|
||||
const { filePath, operationType, mode, uid, gid, recursive } = config;
|
||||
|
||||
// 检查文件是否存在
|
||||
const stats = await fs.lstat(filePath);
|
||||
try {
|
||||
// 检查文件是否存在
|
||||
const stats = await fs.lstat(filePath);
|
||||
|
||||
// 检查目标类型
|
||||
if (targetType === "file" && !stats.isFile()) {
|
||||
throw new Error("目标不是文件");
|
||||
}
|
||||
if (targetType === "directory" && !stats.isDirectory()) {
|
||||
throw new Error("目标不是目录");
|
||||
}
|
||||
|
||||
switch (manageOperation) {
|
||||
case "rename":
|
||||
// 确保目标目录存在
|
||||
await fs.mkdir(path.dirname(newPath), { recursive: true });
|
||||
await fs.rename(filePath, newPath);
|
||||
break;
|
||||
|
||||
case "chmod":
|
||||
if (operationType === "chmod") {
|
||||
if (recursive && stats.isDirectory()) {
|
||||
const walk = async (dir) => {
|
||||
const files = await fs.readdir(dir);
|
||||
@ -166,9 +133,7 @@ async function manage(config) {
|
||||
} else {
|
||||
await fs.chmod(filePath, parseInt(mode, 8));
|
||||
}
|
||||
break;
|
||||
|
||||
case "chown":
|
||||
} else if (operationType === "chown") {
|
||||
if (recursive && stats.isDirectory()) {
|
||||
await fs.chown(filePath, uid, gid);
|
||||
const walk = async (dir) => {
|
||||
@ -188,10 +153,71 @@ async function manage(config) {
|
||||
} else {
|
||||
await fs.chown(filePath, uid, gid);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
throw new Error(`不支持的操作类型: ${operationType}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") {
|
||||
throw new Error("文件或目录不存在");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`不支持的操作类型: ${manageOperation}`);
|
||||
/**
|
||||
* 文件复制移动操作
|
||||
*/
|
||||
async function transfer(config) {
|
||||
const { filePath, transferOperation, newPath } = config;
|
||||
|
||||
// 检查文件是否存在
|
||||
try {
|
||||
const stats = await fs.lstat(filePath);
|
||||
|
||||
// 确保目标目录存在
|
||||
await fs.mkdir(path.dirname(newPath), { recursive: true });
|
||||
if (transferOperation === "copy") {
|
||||
const processBar = await quickcommand.showProcessBar({
|
||||
text: "复制中...",
|
||||
});
|
||||
if (stats.isDirectory()) {
|
||||
// 复制目录
|
||||
const copyDir = async (src, dest) => {
|
||||
await fs.mkdir(dest, { recursive: true });
|
||||
const entries = await fs.readdir(src);
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry);
|
||||
const destPath = path.join(dest, entry);
|
||||
const entryStat = await fs.lstat(srcPath);
|
||||
if (entryStat.isDirectory()) {
|
||||
await copyDir(srcPath, destPath);
|
||||
} else {
|
||||
await fs.copyFile(srcPath, destPath);
|
||||
}
|
||||
quickcommand.updateProcessBar({ text: entry }, processBar);
|
||||
}
|
||||
};
|
||||
await copyDir(filePath, newPath);
|
||||
} else {
|
||||
// 复制文件
|
||||
await fs.copyFile(filePath, newPath);
|
||||
}
|
||||
processBar.close();
|
||||
} else if (transferOperation === "rename") {
|
||||
const processBar = await quickcommand.showProcessBar({
|
||||
text: "处理中...",
|
||||
});
|
||||
await fs.rename(filePath, newPath);
|
||||
processBar.close();
|
||||
} else {
|
||||
throw new Error(`不支持的操作类型: ${transferOperation}`);
|
||||
}
|
||||
} catch (error) {
|
||||
processBar?.close();
|
||||
if (error.code === "ENOENT") {
|
||||
throw new Error("文件或目录不存在");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,69 +272,49 @@ async function list(config) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param {number} bytes 字节数
|
||||
* @returns {string} 格式化后的文件大小
|
||||
*/
|
||||
function formatBytes(bytes) {
|
||||
const units = ["B", "KB", "MB", "GB", "TB"];
|
||||
let unitIndex = 0;
|
||||
while (bytes >= 1024 && unitIndex < units.length - 1) {
|
||||
bytes /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return `${bytes.toFixed(2)} ${units[unitIndex]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件或目录状态
|
||||
* @param {Object} config 配置对象
|
||||
* @param {string} config.filePath 路径
|
||||
* @param {string} config.targetType 目标类型
|
||||
* @param {string} config.statMode 检查类型
|
||||
* @param {boolean} [config.followSymlinks] 是否跟随符号链接
|
||||
* @returns {Promise<Object>} 状态信息
|
||||
*/
|
||||
async function stat(config) {
|
||||
const { filePath, targetType, statMode, followSymlinks } = config;
|
||||
const { filePath, followSymlinks } = config;
|
||||
|
||||
try {
|
||||
const statFn = followSymlinks ? fs.stat : fs.lstat;
|
||||
const stats = await statFn(filePath);
|
||||
|
||||
// 检查目标类型是否匹配
|
||||
if (targetType === "file" && !stats.isFile()) {
|
||||
throw new Error("目标不是文件");
|
||||
}
|
||||
if (targetType === "directory" && !stats.isDirectory()) {
|
||||
throw new Error("目标不是目录");
|
||||
}
|
||||
|
||||
// 根据检查类型返回不同的信息
|
||||
if (statMode === "exists") {
|
||||
return {
|
||||
exists: true,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
};
|
||||
} else if (statMode === "status") {
|
||||
return {
|
||||
exists: true,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
isSymbolicLink: stats.isSymbolicLink(),
|
||||
size: stats.size,
|
||||
mode: stats.mode,
|
||||
uid: stats.uid,
|
||||
gid: stats.gid,
|
||||
accessTime: stats.atime,
|
||||
modifyTime: stats.mtime,
|
||||
changeTime: stats.ctime,
|
||||
birthTime: stats.birthtime,
|
||||
};
|
||||
} else {
|
||||
// 默认返回基本信息
|
||||
return {
|
||||
exists: true,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
isSymbolicLink: stats.isSymbolicLink(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
exists: true,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
isSymbolicLink: stats.isSymbolicLink(),
|
||||
humanReadSize: formatBytes(stats.size),
|
||||
...stats,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") {
|
||||
return {
|
||||
exists: false,
|
||||
...(statMode === "exists" && {
|
||||
isFile: false,
|
||||
isDirectory: false,
|
||||
}),
|
||||
isFile: false,
|
||||
isDirectory: false,
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
@ -319,39 +325,28 @@ async function stat(config) {
|
||||
* 统一的文件操作入口
|
||||
*/
|
||||
async function operation(config) {
|
||||
if (!config || typeof config !== "object") {
|
||||
throw new Error("配置参数必须是一个对象");
|
||||
}
|
||||
const { operation: op } = config;
|
||||
|
||||
const { operation } = config;
|
||||
if (!operation) {
|
||||
throw new Error("缺少必要的 operation 参数");
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
switch (op) {
|
||||
case "read":
|
||||
return await read(config);
|
||||
case "write":
|
||||
return await write(config);
|
||||
case "list":
|
||||
return await list(config);
|
||||
case "stat":
|
||||
return await stat(config);
|
||||
case "delete":
|
||||
return await remove(config);
|
||||
case "manage":
|
||||
return await manage(config);
|
||||
case "stat":
|
||||
return await stat(config);
|
||||
case "permission":
|
||||
return await permission(config);
|
||||
case "transfer":
|
||||
return await transfer(config);
|
||||
default:
|
||||
throw new Error(`不支持的操作类型: ${operation}`);
|
||||
throw new Error(`不支持的操作类型: ${op}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
read,
|
||||
write,
|
||||
list,
|
||||
stat,
|
||||
remove,
|
||||
manage,
|
||||
operation,
|
||||
};
|
||||
|
@ -43,10 +43,16 @@
|
||||
<div class="q-ml-xs">删除</div>
|
||||
</div>
|
||||
</q-tab>
|
||||
<q-tab name="manage" no-caps>
|
||||
<q-tab name="transfer" no-caps>
|
||||
<div class="row items-center no-wrap">
|
||||
<q-icon name="settings" size="16px" />
|
||||
<div class="q-ml-xs">管理</div>
|
||||
<q-icon name="drive_file_move" size="16px" />
|
||||
<div class="q-ml-xs">复制移动</div>
|
||||
</div>
|
||||
</q-tab>
|
||||
<q-tab name="permission" no-caps>
|
||||
<div class="row items-center no-wrap">
|
||||
<q-icon name="security" size="16px" />
|
||||
<div class="q-ml-xs">权限</div>
|
||||
</div>
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
@ -64,10 +70,7 @@
|
||||
dialog: {
|
||||
options: {
|
||||
title: '选择文件',
|
||||
properties: [
|
||||
shouldSelectDirectory ? 'openDirectory' : 'openFile',
|
||||
'showHiddenFiles',
|
||||
],
|
||||
properties: ['openDirectory', 'openFile', 'showHiddenFiles'],
|
||||
},
|
||||
},
|
||||
}"
|
||||
@ -100,25 +103,13 @@
|
||||
map-options
|
||||
@update:model-value="updateArgvs('readMode', $event)"
|
||||
/>
|
||||
<q-select
|
||||
v-model="argvs.readFlag"
|
||||
:options="readFlagOptions"
|
||||
label="读取标志"
|
||||
dense
|
||||
options-dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('readFlag', $event)"
|
||||
/>
|
||||
<NumberInput
|
||||
v-if="argvs.readMode === 'start'"
|
||||
:model-value="argvs.start"
|
||||
@update:model-value="updateArgvs('start', $event)"
|
||||
label="起始位置"
|
||||
icon="first_page"
|
||||
class="col-grow"
|
||||
class="col"
|
||||
/>
|
||||
<NumberInput
|
||||
v-if="argvs.readMode === 'start'"
|
||||
@ -126,7 +117,7 @@
|
||||
@update:model-value="updateArgvs('length', $event)"
|
||||
label="读取长度"
|
||||
icon="last_page"
|
||||
class="col-grow"
|
||||
class="col"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -186,7 +177,7 @@
|
||||
@update:model-value="updateArgvs('content', $event)"
|
||||
label="写入内容"
|
||||
icon="edit"
|
||||
class="col-12"
|
||||
class="col-grow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -194,80 +185,87 @@
|
||||
<!-- 删除操作配置 -->
|
||||
<template v-if="argvs.operation === 'delete'">
|
||||
<div class="row q-gutter-sm">
|
||||
<q-select
|
||||
v-model="argvs.targetType"
|
||||
:options="targetTypeOptions"
|
||||
label="目标类型"
|
||||
dense
|
||||
filled
|
||||
options-dense
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('targetType', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
<CheckButton
|
||||
v-model="argvs.recursive"
|
||||
label="递归删除"
|
||||
v-if="argvs.targetType === 'directory'"
|
||||
dense
|
||||
class="col-grow"
|
||||
class="col"
|
||||
@update:model-value="updateArgvs('recursive', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
<CheckButton
|
||||
v-model="argvs.force"
|
||||
label="强制删除"
|
||||
dense
|
||||
class="col-grow"
|
||||
class="col"
|
||||
@update:model-value="updateArgvs('force', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 管理操作配置 -->
|
||||
<template v-if="argvs.operation === 'manage'">
|
||||
<!-- 列目录操作配置 -->
|
||||
<template v-if="argvs.operation === 'list'">
|
||||
<div class="row q-gutter-sm">
|
||||
<q-select
|
||||
v-model="argvs.targetType"
|
||||
:options="targetTypeOptions"
|
||||
label="目标类型"
|
||||
dense
|
||||
options-dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('targetType', $event)"
|
||||
<CheckButton
|
||||
v-model="argvs.recursive"
|
||||
label="递归列出子目录"
|
||||
class="col"
|
||||
@update:model-value="updateArgvs('recursive', $event)"
|
||||
/>
|
||||
<q-select
|
||||
v-model="argvs.manageOperation"
|
||||
:options="manageOperationOptions"
|
||||
label="管理操作"
|
||||
dense
|
||||
options-dense
|
||||
filled
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('manageOperation', $event)"
|
||||
<CheckButton
|
||||
v-model="argvs.showHidden"
|
||||
label="显示隐藏文件"
|
||||
class="col"
|
||||
@update:model-value="updateArgvs('showHidden', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 重命名操作 -->
|
||||
<template v-if="argvs.manageOperation === 'rename'">
|
||||
<div class="row q-gutter-sm">
|
||||
<!-- 状态操作配置 -->
|
||||
<template v-if="argvs.operation === 'stat'">
|
||||
<div class="row q-gutter-sm">
|
||||
<CheckButton
|
||||
v-model="argvs.followSymlinks"
|
||||
label="跟随符号链接"
|
||||
@update:model-value="updateArgvs('followSymlinks', $event)"
|
||||
class="col-grow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 复制移动操作配置 -->
|
||||
<template v-if="argvs.operation === 'transfer'">
|
||||
<div class="row q-gutter-sm">
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
:model-value="argvs.newPath"
|
||||
@update:model-value="updateArgvs('newPath', $event)"
|
||||
label="新路径"
|
||||
label="目标路径"
|
||||
icon="drive_file_rename_outline"
|
||||
class="col-grow"
|
||||
class="col-6"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="col">
|
||||
<ButtonGroup
|
||||
v-model="argvs.transferOperation"
|
||||
:options="transferOperationOptions"
|
||||
height="36px"
|
||||
@update:model-value="updateArgvs('transferOperation', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 权限操作配置 -->
|
||||
<template v-if="argvs.operation === 'permission'">
|
||||
<div class="row q-gutter-sm">
|
||||
<ButtonGroup
|
||||
v-model="argvs.operationType"
|
||||
class="col"
|
||||
:options="operationTypeOptions"
|
||||
@update:model-value="updateArgvs('operationType', $event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 修改权限操作 -->
|
||||
<template v-if="argvs.manageOperation === 'chmod'">
|
||||
<template v-if="argvs.operationType === 'chmod'">
|
||||
<div class="row q-gutter-sm">
|
||||
<q-select
|
||||
v-model="argvs.mode"
|
||||
@ -290,11 +288,9 @@
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<q-checkbox
|
||||
<CheckButton
|
||||
v-model="argvs.recursive"
|
||||
label="递归修改"
|
||||
v-if="argvs.targetType === 'directory'"
|
||||
dense
|
||||
class="col-grow"
|
||||
@update:model-value="updateArgvs('recursive', $event)"
|
||||
/>
|
||||
@ -302,7 +298,7 @@
|
||||
</template>
|
||||
|
||||
<!-- 修改所有者操作 -->
|
||||
<template v-if="argvs.manageOperation === 'chown'">
|
||||
<template v-if="argvs.operationType === 'chown'">
|
||||
<div class="row q-gutter-sm">
|
||||
<NumberInput
|
||||
:model-value="argvs.uid"
|
||||
@ -318,75 +314,15 @@
|
||||
icon="group"
|
||||
class="col-grow"
|
||||
/>
|
||||
<q-checkbox
|
||||
<CheckButton
|
||||
v-model="argvs.recursive"
|
||||
label="递归修改"
|
||||
v-if="argvs.targetType === 'directory'"
|
||||
dense
|
||||
class="col-grow"
|
||||
@update:model-value="updateArgvs('recursive', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 列目录操作配置 -->
|
||||
<template v-if="argvs.operation === 'list'">
|
||||
<div class="row q-gutter-sm q-px-xs">
|
||||
<q-checkbox
|
||||
v-model="argvs.recursive"
|
||||
label="递归列出子目录"
|
||||
dense
|
||||
class="col-grow"
|
||||
@update:model-value="updateArgvs('recursive', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
v-model="argvs.showHidden"
|
||||
label="显示隐藏文件"
|
||||
dense
|
||||
class="col-grow"
|
||||
@update:model-value="updateArgvs('showHidden', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 状态操作配置 -->
|
||||
<template v-if="argvs.operation === 'stat'">
|
||||
<div class="row q-gutter-sm">
|
||||
<q-select
|
||||
v-model="argvs.targetType"
|
||||
:options="targetTypeOptions"
|
||||
label="目标类型"
|
||||
dense
|
||||
filled
|
||||
options-dense
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('targetType', $event)"
|
||||
/>
|
||||
<q-select
|
||||
v-model="argvs.statMode"
|
||||
:options="statModeOptions"
|
||||
label="检查类型"
|
||||
dense
|
||||
filled
|
||||
options-dense
|
||||
class="col-grow"
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateArgvs('statMode', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
v-model="argvs.followSymlinks"
|
||||
label="跟随符号链接"
|
||||
v-if="argvs.statMode === 'status'"
|
||||
@update:model-value="updateArgvs('followSymlinks', $event)"
|
||||
dense
|
||||
class="col-grow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -394,7 +330,9 @@
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/composer/common/VariableInput.vue";
|
||||
import NumberInput from "components/composer/common/NumberInput.vue";
|
||||
import { stringifyArgv, parseFunction } from "js/composer/formatString";
|
||||
import ButtonGroup from "components/composer/common/ButtonGroup.vue";
|
||||
import CheckButton from "components/composer/common/CheckButton.vue";
|
||||
import { stringifyArgv } from "js/composer/formatString";
|
||||
import { newVarInputVal } from "js/composer/varInputValManager";
|
||||
|
||||
// 静态选项数据
|
||||
@ -418,11 +356,6 @@ const READ_MODE_OPTIONS = [
|
||||
{ label: "按行读取", value: "line" },
|
||||
];
|
||||
|
||||
const READ_FLAG_OPTIONS = [
|
||||
{ label: "同步读取", value: "sync" },
|
||||
{ label: "异步读取", value: "async" },
|
||||
];
|
||||
|
||||
const WRITE_MODE_OPTIONS = [
|
||||
{ label: "覆盖写入", value: "write" },
|
||||
{ label: "追加写入", value: "append" },
|
||||
@ -435,20 +368,19 @@ const WRITE_FLAG_OPTIONS = [
|
||||
{ label: "777", value: "777", hint: "所有人读写执行" },
|
||||
];
|
||||
|
||||
const STAT_MODE_OPTIONS = [
|
||||
{ label: "检查存在", value: "exists" },
|
||||
{ label: "完整状态", value: "status" },
|
||||
];
|
||||
|
||||
const TARGET_TYPE_OPTIONS = [
|
||||
{ label: "文件", value: "file" },
|
||||
{ label: "目录", value: "directory" },
|
||||
];
|
||||
|
||||
const MANAGE_OPERATION_OPTIONS = [
|
||||
{ label: "重命名", value: "rename" },
|
||||
{ label: "修改权限", value: "chmod" },
|
||||
{ label: "修改所有者", value: "chown" },
|
||||
const OPERATION_TYPE_OPTIONS = [
|
||||
{ label: "修改权限", value: "chmod", icon: "lock" },
|
||||
{ label: "修改所有者", value: "chown", icon: "person" },
|
||||
];
|
||||
|
||||
const TRANSFER_OPERATION_OPTIONS = [
|
||||
{ label: "移动/重命名", value: "rename", icon: "drive_file_rename_outline" },
|
||||
{ label: "复制", value: "copy", icon: "content_copy" },
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
@ -456,6 +388,8 @@ export default defineComponent({
|
||||
components: {
|
||||
VariableInput,
|
||||
NumberInput,
|
||||
ButtonGroup,
|
||||
CheckButton,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
@ -468,44 +402,34 @@ export default defineComponent({
|
||||
return {
|
||||
encodingOptions: ENCODING_OPTIONS,
|
||||
readModeOptions: READ_MODE_OPTIONS,
|
||||
readFlagOptions: READ_FLAG_OPTIONS,
|
||||
writeModeOptions: WRITE_MODE_OPTIONS,
|
||||
writeFlagOptions: WRITE_FLAG_OPTIONS,
|
||||
statModeOptions: STAT_MODE_OPTIONS,
|
||||
targetTypeOptions: TARGET_TYPE_OPTIONS,
|
||||
manageOperationOptions: MANAGE_OPERATION_OPTIONS,
|
||||
operationTypeOptions: OPERATION_TYPE_OPTIONS,
|
||||
transferOperationOptions: TRANSFER_OPERATION_OPTIONS,
|
||||
defaultArgvs: {
|
||||
operation: "read",
|
||||
filePath: newVarInputVal("str"),
|
||||
encoding: "utf8",
|
||||
readMode: "all",
|
||||
readFlag: "async",
|
||||
start: 0,
|
||||
length: 100,
|
||||
targetType: "file",
|
||||
writeMode: "write",
|
||||
writeFlag: "644",
|
||||
statMode: "exists",
|
||||
followSymlinks: false,
|
||||
followSymlinks: true,
|
||||
mode: "644",
|
||||
recursive: false,
|
||||
force: false,
|
||||
showHidden: false,
|
||||
manageOperation: "rename",
|
||||
operationType: "chmod",
|
||||
transferOperation: "rename",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
argvs() {
|
||||
return (
|
||||
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
|
||||
);
|
||||
},
|
||||
shouldSelectDirectory() {
|
||||
return (
|
||||
this.argvs.operation === "list" ||
|
||||
(this.argvs.targetType === "directory" &&
|
||||
["delete", "manage", "stat"].includes(this.argvs.operation))
|
||||
);
|
||||
return this.modelValue.argvs || this.defaultArgvs;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -519,7 +443,6 @@ export default defineComponent({
|
||||
switch (argvs.operation) {
|
||||
case "read":
|
||||
params.encoding = argvs.encoding;
|
||||
params.readFlag = argvs.readFlag;
|
||||
params.readMode = argvs.readMode;
|
||||
if (argvs.readMode === "start") {
|
||||
params.start = Number(argvs.start) || 0;
|
||||
@ -537,32 +460,33 @@ export default defineComponent({
|
||||
case "list":
|
||||
params.targetType = "directory";
|
||||
params.recursive = argvs.recursive;
|
||||
params.showHidden = argvs.showHidden;
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
params.targetType = argvs.targetType;
|
||||
params.force = argvs.force;
|
||||
params.recursive =
|
||||
argvs.recursive && argvs.targetType === "directory";
|
||||
break;
|
||||
|
||||
case "manage":
|
||||
params.targetType = argvs.targetType;
|
||||
params.manageOperation = argvs.manageOperation;
|
||||
if (argvs.manageOperation === "rename") {
|
||||
params.newPath = argvs.newPath;
|
||||
} else {
|
||||
if (argvs.mode) params.mode = argvs.mode;
|
||||
if (argvs.uid) params.uid = argvs.uid;
|
||||
if (argvs.gid) params.gid = argvs.gid;
|
||||
}
|
||||
params.recursive = argvs.recursive;
|
||||
break;
|
||||
|
||||
case "stat":
|
||||
params.targetType = argvs.targetType;
|
||||
params.statMode = argvs.statMode;
|
||||
params.followSymlinks = argvs.followSymlinks;
|
||||
break;
|
||||
|
||||
case "permission":
|
||||
params.operationType = argvs.operationType;
|
||||
if (argvs.operationType === "chmod") {
|
||||
params.mode = argvs.mode;
|
||||
} else {
|
||||
params.uid = argvs.uid;
|
||||
params.gid = argvs.gid;
|
||||
}
|
||||
params.recursive = argvs.recursive;
|
||||
break;
|
||||
|
||||
case "transfer":
|
||||
params.transferOperation = argvs.transferOperation;
|
||||
params.newPath = argvs.newPath;
|
||||
break;
|
||||
}
|
||||
|
||||
return `${this.modelValue.value}(${stringifyArgv(params)})`;
|
||||
@ -610,52 +534,6 @@ export default defineComponent({
|
||||
this.argvs.mode = `${ownerValue}${groupValue}${otherValue}`;
|
||||
this.updateArgvs();
|
||||
},
|
||||
parseCodeToArgvs(code) {
|
||||
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
|
||||
if (!code) return argvs;
|
||||
|
||||
try {
|
||||
const variableFormatPaths = [
|
||||
"arg0.filePath",
|
||||
"arg0.content",
|
||||
"arg0.newPath",
|
||||
];
|
||||
const result = parseFunction(code, { variableFormatPaths });
|
||||
let params = result.argvs[0];
|
||||
|
||||
// 根据不同操作类型处理特定参数
|
||||
switch (params.operation) {
|
||||
case "read":
|
||||
if (params.readMode === "start") {
|
||||
params.start = Number(params.start) || 0;
|
||||
params.length = Number(params.length) || 100;
|
||||
}
|
||||
break;
|
||||
|
||||
case "write":
|
||||
// 将 flag 转换回 writeMode
|
||||
params.writeMode = params.flag === "a" ? "append" : "write";
|
||||
// 将 mode 转换回 writeFlag
|
||||
params.writeFlag = params.mode;
|
||||
break;
|
||||
|
||||
case "list":
|
||||
params.targetType = "directory";
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
case "manage":
|
||||
case "stat":
|
||||
// 这些操作的参数可以直接使用
|
||||
break;
|
||||
}
|
||||
|
||||
return params;
|
||||
} catch (e) {
|
||||
console.error("解析文件操作参数失败:", e);
|
||||
}
|
||||
return argvs;
|
||||
},
|
||||
updateModelValue(argvs) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
@ -670,19 +548,11 @@ export default defineComponent({
|
||||
write: "写入",
|
||||
list: "列目录",
|
||||
delete: "删除",
|
||||
manage: "管理",
|
||||
stat: "状态",
|
||||
stat: "获取状态",
|
||||
transfer: "复制移动",
|
||||
permission: "设置权限",
|
||||
};
|
||||
const findOptionsLabel = (options, value) => {
|
||||
return options.find((option) => option.value === value)?.label || value;
|
||||
};
|
||||
let operationInfo =
|
||||
argvs.operation === "manage"
|
||||
? findOptionsLabel(MANAGE_OPERATION_OPTIONS, argvs.manageOperation) +
|
||||
" "
|
||||
: argvs.operation === "stat"
|
||||
? findOptionsLabel(STAT_MODE_OPTIONS, argvs.statMode) + " "
|
||||
: operationDict[argvs.operation] + " ";
|
||||
let operationInfo = operationDict[argvs.operation] + " ";
|
||||
return operationInfo + argvs.filePath.value;
|
||||
},
|
||||
},
|
||||
|
@ -382,7 +382,7 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
argvs() {
|
||||
return this.modelValue.argvs;
|
||||
return this.modelValue.argvs || this.defaultArgvs;
|
||||
},
|
||||
hasRequestData() {
|
||||
return ["PUT", "POST", "PATCH"].includes(this.argvs.method);
|
||||
|
127
src/plugins/monaco/types/quickcomposer.d.ts
vendored
127
src/plugins/monaco/types/quickcomposer.d.ts
vendored
@ -507,89 +507,58 @@ interface quickcomposerApi {
|
||||
file: {
|
||||
/**
|
||||
* 文件操作
|
||||
* @param config 操作配置
|
||||
*/
|
||||
operation: {
|
||||
/**
|
||||
* 统一的文件操作入口
|
||||
* @param config 操作配置
|
||||
*/
|
||||
operation(config: {
|
||||
/** 操作类型:read-读取,write-写入,list-列表,stat-状态,delete-删除,manage-管理 */
|
||||
operation: "read" | "write" | "list" | "stat" | "delete" | "manage";
|
||||
/** 文件路径 */
|
||||
filePath: string;
|
||||
/** 读取操作配置 */
|
||||
readOptions?: {
|
||||
/** 编码方式 */
|
||||
encoding?: BufferEncoding;
|
||||
/** 起始位置 */
|
||||
start?: number;
|
||||
/** 结束位置 */
|
||||
end?: number;
|
||||
};
|
||||
/** 写入操作配置 */
|
||||
writeOptions?: {
|
||||
/** 写入内容 */
|
||||
content: string;
|
||||
/** 编码方式 */
|
||||
encoding?: BufferEncoding;
|
||||
/** 写入模式:overwrite-覆盖,append-追加 */
|
||||
writeMode?: "overwrite" | "append";
|
||||
/** 文件权限 */
|
||||
writeFlag?: string;
|
||||
};
|
||||
/** 列表操作配置 */
|
||||
listOptions?: {
|
||||
/** 是否递归 */
|
||||
recursive?: boolean;
|
||||
/** 是否包含隐藏文件 */
|
||||
includeHidden?: boolean;
|
||||
};
|
||||
/** 管理操作配置 */
|
||||
manageOptions?: {
|
||||
/** 目标类型:file-文件,directory-目录 */
|
||||
targetType?: "file" | "directory";
|
||||
/** 管理操作类型:rename-重命名,chmod-修改权限,chown-修改所有者 */
|
||||
manageOperation?: "rename" | "chmod" | "chown";
|
||||
/** 新路径(重命名时使用) */
|
||||
newPath?: string;
|
||||
/** 权限模式(chmod时使用) */
|
||||
mode?: string | number;
|
||||
/** 用户ID(chown时使用) */
|
||||
uid?: number;
|
||||
/** 组ID(chown时使用) */
|
||||
gid?: number;
|
||||
/** 是否递归操作 */
|
||||
recursive?: boolean;
|
||||
};
|
||||
}): Promise<any>;
|
||||
};
|
||||
operation(config: {
|
||||
/** 操作类型:read-读取,write-写入,list-列表,stat-状态,delete-删除,permission-权限,transfer-复制移动 */
|
||||
operation:
|
||||
| "read"
|
||||
| "write"
|
||||
| "list"
|
||||
| "stat"
|
||||
| "delete"
|
||||
| "permission"
|
||||
| "transfer";
|
||||
/** 文件路径 */
|
||||
filePath: string;
|
||||
/** 读取操作配置 */
|
||||
encoding?: BufferEncoding;
|
||||
readMode?: "all" | "start" | "line";
|
||||
start?: number;
|
||||
length?: number;
|
||||
/** 写入操作配置 */
|
||||
content?: string;
|
||||
flag?: "w" | "a";
|
||||
mode?: string | number;
|
||||
/** 列表操作配置 */
|
||||
recursive?: boolean;
|
||||
showHidden?: boolean;
|
||||
/** 权限操作配置 */
|
||||
operationType?: "chmod" | "chown";
|
||||
uid?: number;
|
||||
gid?: number;
|
||||
/** 复制移动操作配置 */
|
||||
transferOperation?: "copy" | "rename";
|
||||
newPath?: string;
|
||||
/** 删除操作配置 */
|
||||
force?: boolean;
|
||||
/** 状态操作配置 */
|
||||
followSymlinks?: boolean;
|
||||
}): Promise<any>;
|
||||
|
||||
/**
|
||||
* 文件归档
|
||||
* 文件归档操作
|
||||
* @param operation 操作类型:compress-压缩,extract-解压
|
||||
* @param format 归档格式:zip, tar, gzip
|
||||
* @param source 源文件/文件夹路径
|
||||
* @param destination 目标路径
|
||||
*/
|
||||
archive: {
|
||||
/**
|
||||
* 文件归档操作
|
||||
* @param config 归档配置
|
||||
*/
|
||||
archive(config: {
|
||||
/** 操作类型:compress-压缩,extract-解压 */
|
||||
operation: "compress" | "extract";
|
||||
/** 归档格式:zip, tar, gzip */
|
||||
format: "zip" | "tar" | "gzip";
|
||||
/** 源文件/文件夹路径 */
|
||||
sourcePath: string | string[];
|
||||
/** 目标路径 */
|
||||
targetPath: string;
|
||||
/** 压缩级别(1-9) */
|
||||
level?: number;
|
||||
/** 是否保持目录结构 */
|
||||
preserveStructure?: boolean;
|
||||
/** 密码保护(仅zip格式支持) */
|
||||
password?: string;
|
||||
}): Promise<void>;
|
||||
};
|
||||
archive(
|
||||
operation: "compress" | "extract",
|
||||
format: "zip" | "tar" | "gzip",
|
||||
source: string | string[],
|
||||
destination: string
|
||||
): Promise<void>;
|
||||
};
|
||||
system: {
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user