mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 20:32:44 +08:00
编排新增文件/文件夹操作,获取文件图标
This commit is contained in:
parent
75b9fdff80
commit
dcaa00823b
@ -1,6 +1,7 @@
|
|||||||
const quickcomposer = {
|
const quickcomposer = {
|
||||||
textProcessor: require("./quickcomposer/textProcessor"),
|
textProcessor: require("./quickcomposer/textProcessor"),
|
||||||
simulate: require("./quickcomposer/simulate"),
|
simulate: require("./quickcomposer/simulate"),
|
||||||
|
file: require("./quickcomposer/file"),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = quickcomposer;
|
module.exports = quickcomposer;
|
||||||
|
5
plugin/lib/quickcomposer/file/index.js
Normal file
5
plugin/lib/quickcomposer/file/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const operation = require("./operation");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
operation: operation.operation,
|
||||||
|
};
|
320
plugin/lib/quickcomposer/file/operation.js
Normal file
320
plugin/lib/quickcomposer/file/operation.js
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
const fs = require("fs").promises;
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件读取操作
|
||||||
|
* @param {Object} config 配置对象
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
if (readMode === "all") {
|
||||||
|
return await fs.readFile(filePath, { encoding, flag });
|
||||||
|
} else {
|
||||||
|
// 指定位置读取
|
||||||
|
const fileHandle = await fs.open(filePath, flag);
|
||||||
|
try {
|
||||||
|
const buffer = Buffer.alloc(length);
|
||||||
|
await fileHandle.read(buffer, 0, length, start);
|
||||||
|
await fileHandle.close();
|
||||||
|
return encoding ? buffer.toString(encoding) : buffer;
|
||||||
|
} catch (error) {
|
||||||
|
await fileHandle.close();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件写入操作
|
||||||
|
* @param {Object} config 配置对象
|
||||||
|
* @param {string} config.filePath 文件路径
|
||||||
|
* @param {string} config.content 写入内容
|
||||||
|
* @param {string} config.encoding 编码方式
|
||||||
|
* @param {string} config.flag 写入标志
|
||||||
|
* @param {string|number} config.mode 文件权限
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function write(config) {
|
||||||
|
const { filePath, content, encoding, flag, mode } = config;
|
||||||
|
|
||||||
|
// 确保目录存在
|
||||||
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||||
|
// 将字符串模式转换为八进制数字
|
||||||
|
const modeNum = parseInt(mode, 8);
|
||||||
|
await fs.writeFile(filePath, content, { encoding, flag, mode: modeNum });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件删除操作
|
||||||
|
*/
|
||||||
|
async function delete_(config) {
|
||||||
|
const { filePath, recursive, force, targetType } = 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 });
|
||||||
|
} else {
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === "ENOENT") {
|
||||||
|
if (!force) throw new Error("文件或目录不存在");
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件管理操作
|
||||||
|
*/
|
||||||
|
async function manage(config) {
|
||||||
|
const {
|
||||||
|
filePath,
|
||||||
|
manageOperation,
|
||||||
|
newPath,
|
||||||
|
mode,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
recursive,
|
||||||
|
targetType,
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
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 (recursive && stats.isDirectory()) {
|
||||||
|
const walk = async (dir) => {
|
||||||
|
const files = await fs.readdir(dir);
|
||||||
|
for (const file of files) {
|
||||||
|
const curPath = path.join(dir, file);
|
||||||
|
const stat = await fs.lstat(curPath);
|
||||||
|
await fs.chmod(curPath, parseInt(mode, 8));
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
await walk(curPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await fs.chmod(filePath, parseInt(mode, 8));
|
||||||
|
await walk(filePath);
|
||||||
|
} else {
|
||||||
|
await fs.chmod(filePath, parseInt(mode, 8));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "chown":
|
||||||
|
if (recursive && stats.isDirectory()) {
|
||||||
|
await fs.chown(filePath, uid, gid);
|
||||||
|
const walk = async (dir) => {
|
||||||
|
const files = await fs.readdir(dir);
|
||||||
|
for (const file of files) {
|
||||||
|
const curPath = path.join(dir, file);
|
||||||
|
const stat = await fs.lstat(curPath);
|
||||||
|
await fs.chown(curPath, uid, gid);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
await walk(curPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await fs.chown(filePath, uid, gid);
|
||||||
|
await walk(filePath);
|
||||||
|
} else {
|
||||||
|
await fs.chown(filePath, uid, gid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`不支持的操作类型: ${manageOperation}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出目录内容
|
||||||
|
* @param {Object} config 配置对象
|
||||||
|
* @param {string} config.filePath 目录路径
|
||||||
|
* @param {boolean} config.recursive 是否递归列出子目录
|
||||||
|
* @param {boolean} config.showHidden 是否显示隐藏文件
|
||||||
|
* @returns {Promise<Array>} 文件列表
|
||||||
|
*/
|
||||||
|
async function list(config) {
|
||||||
|
const { filePath, recursive, showHidden } = config;
|
||||||
|
|
||||||
|
if (recursive) {
|
||||||
|
const result = [];
|
||||||
|
const walk = async (dir) => {
|
||||||
|
const files = await fs.readdir(dir);
|
||||||
|
for (const file of files) {
|
||||||
|
if (!showHidden && file.startsWith(".")) continue;
|
||||||
|
const curPath = path.join(dir, file);
|
||||||
|
const stat = await fs.lstat(curPath);
|
||||||
|
result.push({
|
||||||
|
path: curPath,
|
||||||
|
isDirectory: stat.isDirectory(),
|
||||||
|
isFile: stat.isFile(),
|
||||||
|
isSymbolicLink: stat.isSymbolicLink(),
|
||||||
|
});
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
await walk(curPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await walk(filePath);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
const files = await fs.readdir(filePath);
|
||||||
|
return Promise.all(
|
||||||
|
files
|
||||||
|
.filter((file) => showHidden || !file.startsWith("."))
|
||||||
|
.map(async (file) => {
|
||||||
|
const curPath = path.join(filePath, file);
|
||||||
|
const stat = await fs.lstat(curPath);
|
||||||
|
return {
|
||||||
|
path: curPath,
|
||||||
|
isDirectory: stat.isDirectory(),
|
||||||
|
isFile: stat.isFile(),
|
||||||
|
isSymbolicLink: stat.isSymbolicLink(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件或目录状态
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === "ENOENT") {
|
||||||
|
return {
|
||||||
|
exists: false,
|
||||||
|
...(statMode === "exists" && {
|
||||||
|
isFile: false,
|
||||||
|
isDirectory: false,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一的文件操作入口
|
||||||
|
*/
|
||||||
|
async function operation(config) {
|
||||||
|
if (!config || typeof config !== "object") {
|
||||||
|
throw new Error("配置参数必须是一个对象");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { operation } = config;
|
||||||
|
if (!operation) {
|
||||||
|
throw new Error("缺少必要的 operation 参数");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (operation) {
|
||||||
|
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 delete_(config);
|
||||||
|
case "manage":
|
||||||
|
return await manage(config);
|
||||||
|
default:
|
||||||
|
throw new Error(`不支持的操作类型: ${operation}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
list,
|
||||||
|
stat,
|
||||||
|
delete: delete_,
|
||||||
|
manage,
|
||||||
|
operation,
|
||||||
|
};
|
@ -238,4 +238,14 @@ export default defineComponent({
|
|||||||
.command-composer :deep(.q-checkbox__inner) {
|
.command-composer :deep(.q-checkbox__inner) {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 暗黑模式下的标签栏背景颜色 */
|
||||||
|
.body--dark .command-composer :deep(.q-tab),
|
||||||
|
.body--dark .command-composer :deep(.q-tab-panel) {
|
||||||
|
background-color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .command-composer :deep(.q-tab--inactive) {
|
||||||
|
opacity: 2;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
class="text-weight-medium"
|
class="text-weight-medium q-px-lg"
|
||||||
v-html="highlightText(command.label)"
|
v-html="highlightText(command.label)"
|
||||||
/>
|
/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
@ -415,10 +415,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .q-tab,
|
|
||||||
.body--dark .q-tab-panel {
|
|
||||||
background-color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保下拉按钮内容垂直居中 */
|
/* 确保下拉按钮内容垂直居中 */
|
||||||
.codec-dropdown :deep(.q-btn__content) {
|
.codec-dropdown :deep(.q-btn__content) {
|
||||||
|
@ -448,11 +448,6 @@ export default defineComponent({
|
|||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .q-tab,
|
|
||||||
.body--dark .q-tab-panel {
|
|
||||||
background-color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保下拉按钮内容垂直居中 */
|
/* 确保下拉按钮内容垂直居中 */
|
||||||
.codec-dropdown :deep(.q-btn__content) {
|
.codec-dropdown :deep(.q-btn__content) {
|
||||||
min-height: unset;
|
min-height: unset;
|
||||||
|
699
src/components/composer/file/FileOperationEditor.vue
Normal file
699
src/components/composer/file/FileOperationEditor.vue
Normal file
@ -0,0 +1,699 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-operation-editor">
|
||||||
|
<!-- 操作类型选择 -->
|
||||||
|
<div class="tabs-container">
|
||||||
|
<q-tabs
|
||||||
|
v-model="operation"
|
||||||
|
dense
|
||||||
|
class="text-grey"
|
||||||
|
active-color="primary"
|
||||||
|
indicator-color="primary"
|
||||||
|
align="justify"
|
||||||
|
narrow-indicator
|
||||||
|
inline-label
|
||||||
|
>
|
||||||
|
<q-tab name="read" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="file_open" size="16px" />
|
||||||
|
<div class="q-ml-xs">读取</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
<q-tab name="write" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="save" size="16px" />
|
||||||
|
<div class="q-ml-xs">写入</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
<q-tab name="list" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="folder_open" size="16px" />
|
||||||
|
<div class="q-ml-xs">列目录</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
<q-tab name="stat" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="info" size="16px" />
|
||||||
|
<div class="q-ml-xs">状态</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
<q-tab name="delete" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="delete" size="16px" />
|
||||||
|
<div class="q-ml-xs">删除</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
<q-tab name="manage" no-caps>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="settings" size="16px" />
|
||||||
|
<div class="q-ml-xs">管理</div>
|
||||||
|
</div>
|
||||||
|
</q-tab>
|
||||||
|
</q-tabs>
|
||||||
|
<q-separator />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件路径输入 -->
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<VariableInput
|
||||||
|
v-model="filePath"
|
||||||
|
label="文件路径"
|
||||||
|
:command="{ icon: 'folder' }"
|
||||||
|
class="col-grow"
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
icon="folder_open"
|
||||||
|
class="self-center"
|
||||||
|
@click="selectFile"
|
||||||
|
>
|
||||||
|
<q-tooltip>选择文件</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 读取操作配置 -->
|
||||||
|
<template v-if="operation === 'read'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<q-select
|
||||||
|
v-model="encoding"
|
||||||
|
:options="encodingOptions"
|
||||||
|
label="编码"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="readMode"
|
||||||
|
:options="readModeOptions"
|
||||||
|
label="读取模式"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="readFlag"
|
||||||
|
:options="readFlagOptions"
|
||||||
|
label="读取标志"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<VariableInput
|
||||||
|
v-if="readMode === 'start'"
|
||||||
|
v-model="start"
|
||||||
|
label="起始位置"
|
||||||
|
:command="{ icon: 'first_page', inputType: 'number' }"
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
<VariableInput
|
||||||
|
v-if="readMode === 'start'"
|
||||||
|
v-model="length"
|
||||||
|
label="读取长度"
|
||||||
|
:command="{ icon: 'last_page', inputType: 'number' }"
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 写入操作配置 -->
|
||||||
|
<template v-if="operation === 'write'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<q-select
|
||||||
|
v-model="encoding"
|
||||||
|
:options="encodingOptions"
|
||||||
|
label="编码"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="writeMode"
|
||||||
|
:options="writeModeOptions"
|
||||||
|
label="写入模式"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="writeFlag"
|
||||||
|
:options="writeFlagOptions"
|
||||||
|
label="文件权限"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<VariableInput
|
||||||
|
v-model="content"
|
||||||
|
label="写入内容"
|
||||||
|
:command="{ icon: 'edit' }"
|
||||||
|
class="col-12"
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 删除操作配置 -->
|
||||||
|
<template v-if="operation === 'delete'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<q-select
|
||||||
|
v-model="targetType"
|
||||||
|
:options="targetTypeOptions"
|
||||||
|
label="目标类型"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="recursive"
|
||||||
|
label="递归删除"
|
||||||
|
v-if="targetType === 'directory'"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
<q-checkbox v-model="force" label="强制删除" dense class="col-grow" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 管理操作配置 -->
|
||||||
|
<template v-if="operation === 'manage'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<q-select
|
||||||
|
v-model="targetType"
|
||||||
|
:options="targetTypeOptions"
|
||||||
|
label="目标类型"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="manageOperation"
|
||||||
|
:options="manageOperationOptions"
|
||||||
|
label="操作类型"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 重命名操作 -->
|
||||||
|
<template v-if="manageOperation === 'rename'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<VariableInput
|
||||||
|
v-model="newPath"
|
||||||
|
label="新路径"
|
||||||
|
:command="{ icon: 'drive_file_rename_outline' }"
|
||||||
|
class="col-grow"
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 修改权限操作 -->
|
||||||
|
<template v-if="manageOperation === 'chmod'">
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-caption q-mb-xs">所有者</div>
|
||||||
|
<q-option-group
|
||||||
|
v-model="ownerMode"
|
||||||
|
:options="permissionOptions"
|
||||||
|
type="checkbox"
|
||||||
|
inline
|
||||||
|
dense
|
||||||
|
@update:model-value="updateMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-caption q-mb-xs">用户组</div>
|
||||||
|
<q-option-group
|
||||||
|
v-model="groupMode"
|
||||||
|
:options="permissionOptions"
|
||||||
|
type="checkbox"
|
||||||
|
inline
|
||||||
|
dense
|
||||||
|
@update:model-value="updateMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="text-caption q-mb-xs">其他用户</div>
|
||||||
|
<q-option-group
|
||||||
|
v-model="otherMode"
|
||||||
|
:options="permissionOptions"
|
||||||
|
type="checkbox"
|
||||||
|
inline
|
||||||
|
dense
|
||||||
|
@update:model-value="updateMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md items-center q-mt-xs">
|
||||||
|
<div style="font-size: 12px">权限值: {{ mode }}</div>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="recursive"
|
||||||
|
label="递归修改"
|
||||||
|
v-if="targetType === 'directory'"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 修改所有者操作 -->
|
||||||
|
<template v-if="manageOperation === 'chown'">
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<VariableInput
|
||||||
|
v-model="uid"
|
||||||
|
label="用户ID"
|
||||||
|
:command="{ icon: 'person', inputType: 'number' }"
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
<VariableInput
|
||||||
|
v-model="gid"
|
||||||
|
label="组ID"
|
||||||
|
:command="{ icon: 'group', inputType: 'number' }"
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="recursive"
|
||||||
|
label="递归修改"
|
||||||
|
v-if="targetType === 'directory'"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 列目录操作配置 -->
|
||||||
|
<template v-if="operation === 'list'">
|
||||||
|
<div class="row q-gutter-sm q-px-xs">
|
||||||
|
<q-checkbox
|
||||||
|
v-model="recursive"
|
||||||
|
label="递归列出子目录"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="showHidden"
|
||||||
|
label="显示隐藏文件"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 状态操作配置 -->
|
||||||
|
<template v-if="operation === 'stat'">
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<q-select
|
||||||
|
v-model="targetType"
|
||||||
|
:options="targetTypeOptions"
|
||||||
|
label="目标类型"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="statMode"
|
||||||
|
:options="statModeOptions"
|
||||||
|
label="检查类型"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
class="col-grow"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:model-value="updateConfig"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="followSymlinks"
|
||||||
|
label="跟随符号链接"
|
||||||
|
v-if="statMode === 'status'"
|
||||||
|
dense
|
||||||
|
class="col-grow"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import VariableInput from "../ui/VariableInput.vue";
|
||||||
|
import { formatJsonVariables } from "js/composer/formatString";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "FileOperationEditor",
|
||||||
|
components: { VariableInput },
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
operation: "read",
|
||||||
|
filePath: "",
|
||||||
|
encoding: "utf8",
|
||||||
|
readMode: "all",
|
||||||
|
writeMode: "w",
|
||||||
|
content: "",
|
||||||
|
start: 0,
|
||||||
|
length: 1024,
|
||||||
|
recursive: false,
|
||||||
|
force: false,
|
||||||
|
showHidden: false,
|
||||||
|
followSymlinks: false,
|
||||||
|
manageOperation: "rename",
|
||||||
|
newPath: "",
|
||||||
|
mode: "644",
|
||||||
|
uid: "",
|
||||||
|
gid: "",
|
||||||
|
readFlag: "r",
|
||||||
|
writeFlag: "666",
|
||||||
|
ownerMode: ["read", "write"],
|
||||||
|
groupMode: ["read"],
|
||||||
|
otherMode: ["read"],
|
||||||
|
permissionOptions: [
|
||||||
|
{ label: "读取(r)", value: "read" },
|
||||||
|
{ label: "写入(w)", value: "write" },
|
||||||
|
{ label: "执行(x)", value: "execute" },
|
||||||
|
],
|
||||||
|
|
||||||
|
encodingOptions: [
|
||||||
|
{ label: "UTF-8", value: "utf8" },
|
||||||
|
{ label: "ASCII", value: "ascii" },
|
||||||
|
{ label: "Base64", value: "base64" },
|
||||||
|
{ label: "二进制", value: "binary" },
|
||||||
|
{ label: "十六进制", value: "hex" },
|
||||||
|
],
|
||||||
|
readModeOptions: [
|
||||||
|
{ label: "全部读取", value: "all" },
|
||||||
|
{ label: "指定位置读取", value: "start" },
|
||||||
|
],
|
||||||
|
writeModeOptions: [
|
||||||
|
{ label: "覆盖写入", value: "w" },
|
||||||
|
{ label: "追加写入", value: "a" },
|
||||||
|
{ label: "读写", value: "w+" },
|
||||||
|
{ label: "追加读写", value: "a+" },
|
||||||
|
],
|
||||||
|
manageOperationOptions: [
|
||||||
|
{ label: "重命名/移动", value: "rename" },
|
||||||
|
{ label: "修改权限", value: "chmod" },
|
||||||
|
{ label: "修改所有者", value: "chown" },
|
||||||
|
],
|
||||||
|
readFlagOptions: [
|
||||||
|
{ label: "只读", value: "r" },
|
||||||
|
{ label: "读写", value: "r+" },
|
||||||
|
{ label: "同步读取", value: "rs" },
|
||||||
|
{ label: "同步读写", value: "rs+" },
|
||||||
|
],
|
||||||
|
writeFlagOptions: [
|
||||||
|
{ label: "默认权限", value: "666" },
|
||||||
|
{ label: "只读", value: "444" },
|
||||||
|
{ label: "可执行", value: "755" },
|
||||||
|
{ label: "完全控制", value: "777" },
|
||||||
|
],
|
||||||
|
targetType: "",
|
||||||
|
targetTypeOptions: [
|
||||||
|
{ label: "文件", value: "file" },
|
||||||
|
{ label: "文件夹", value: "directory" },
|
||||||
|
],
|
||||||
|
statMode: "exists",
|
||||||
|
statModeOptions: [
|
||||||
|
{ label: "检查是否存在", value: "exists" },
|
||||||
|
{ label: "获取详细状态", value: "status" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
switch (this.operation) {
|
||||||
|
case "list":
|
||||||
|
this.targetType = "directory";
|
||||||
|
break;
|
||||||
|
case "stat":
|
||||||
|
case "delete":
|
||||||
|
case "manage":
|
||||||
|
this.targetType = "file";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateConfig() {
|
||||||
|
let config = {
|
||||||
|
operation: this.operation,
|
||||||
|
filePath: this.filePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义需要处理的变量字段
|
||||||
|
const variableFields = [
|
||||||
|
"filePath",
|
||||||
|
...(this.operation === "read" && this.readMode === "start"
|
||||||
|
? ["start", "length"]
|
||||||
|
: []),
|
||||||
|
...(this.operation === "write" ? ["content"] : []),
|
||||||
|
...(this.operation === "manage" && this.manageOperation === "rename"
|
||||||
|
? ["newPath"]
|
||||||
|
: []),
|
||||||
|
...(this.operation === "manage" && this.manageOperation === "chmod"
|
||||||
|
? ["mode"]
|
||||||
|
: []),
|
||||||
|
...(this.operation === "manage" && this.manageOperation === "chown"
|
||||||
|
? ["uid", "gid"]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
|
||||||
|
let code = "";
|
||||||
|
switch (this.operation) {
|
||||||
|
case "read":
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
encoding: this.encoding,
|
||||||
|
readMode: this.readMode,
|
||||||
|
flag: this.readFlag,
|
||||||
|
...(this.readMode === "start" && {
|
||||||
|
start: this.start,
|
||||||
|
length: this.length,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
code = `quickcomposer.file.operation(${formatJsonVariables(
|
||||||
|
config,
|
||||||
|
variableFields
|
||||||
|
)})`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "write":
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
encoding: this.encoding,
|
||||||
|
flag: this.writeMode,
|
||||||
|
mode: this.writeFlag,
|
||||||
|
content: this.content,
|
||||||
|
};
|
||||||
|
code = `quickcomposer.file.operation(${formatJsonVariables(
|
||||||
|
config,
|
||||||
|
variableFields
|
||||||
|
)})`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "delete":
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
recursive: this.recursive,
|
||||||
|
force: this.force,
|
||||||
|
};
|
||||||
|
code = `quickcomposer.file.operation(${formatJsonVariables(
|
||||||
|
config,
|
||||||
|
variableFields
|
||||||
|
)})`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "manage":
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
manageOperation: this.manageOperation,
|
||||||
|
...(this.manageOperation === "rename" && { newPath: this.newPath }),
|
||||||
|
...(this.manageOperation === "chmod" && {
|
||||||
|
mode: this.mode,
|
||||||
|
recursive: this.recursive,
|
||||||
|
}),
|
||||||
|
...(this.manageOperation === "chown" && {
|
||||||
|
uid: this.uid,
|
||||||
|
gid: this.gid,
|
||||||
|
recursive: this.recursive,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
code = `quickcomposer.file.operation(${formatJsonVariables(
|
||||||
|
config,
|
||||||
|
variableFields
|
||||||
|
)})`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "stat":
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
targetType: this.targetType,
|
||||||
|
statMode: this.statMode,
|
||||||
|
...(this.statMode === "status" && {
|
||||||
|
followSymlinks: this.followSymlinks,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
code = `quickcomposer.file.operation(${formatJsonVariables(
|
||||||
|
config,
|
||||||
|
variableFields
|
||||||
|
)})`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit("update:modelValue", code);
|
||||||
|
},
|
||||||
|
|
||||||
|
async selectFile() {
|
||||||
|
const result = window.utools.showOpenDialog({
|
||||||
|
title: "选择文件",
|
||||||
|
properties: [
|
||||||
|
this.operation === "list" ||
|
||||||
|
(["delete", "manage", "stat"].includes(this.operation) &&
|
||||||
|
this.targetType === "directory")
|
||||||
|
? "openDirectory"
|
||||||
|
: "openFile",
|
||||||
|
"showHiddenFiles",
|
||||||
|
],
|
||||||
|
buttonLabel: "选择",
|
||||||
|
});
|
||||||
|
console.log(result);
|
||||||
|
if (result && result[0]) {
|
||||||
|
this.filePath = result[0];
|
||||||
|
this.updateConfig();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateMode() {
|
||||||
|
const modeMap = {
|
||||||
|
read: 4,
|
||||||
|
write: 2,
|
||||||
|
execute: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateMode = (perms) => {
|
||||||
|
return perms.reduce((sum, perm) => sum + modeMap[perm], 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ownerValue = calculateMode(this.ownerMode);
|
||||||
|
const groupValue = calculateMode(this.groupMode);
|
||||||
|
const otherValue = calculateMode(this.otherMode);
|
||||||
|
|
||||||
|
this.mode = `${ownerValue}${groupValue}${otherValue}`;
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
operation() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
encoding() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
readMode() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
writeMode() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
recursive() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
force() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
manageOperation() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
start() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
length() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
mode() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
uid() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
gid() {
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
targetType: {
|
||||||
|
handler(newType) {
|
||||||
|
this.filePath = "";
|
||||||
|
this.updateConfig();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-operation-editor {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container .q-tabs {
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container .q-tab {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-grow {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.col-grow {
|
||||||
|
flex: 1 1 calc(50% - 8px);
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -467,9 +467,3 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.body--dark .q-tab,
|
|
||||||
.body--dark .q-tab-panel {
|
|
||||||
background-color: #303133;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -131,11 +131,6 @@ export default defineComponent({
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 暗色模式 */
|
|
||||||
.body--dark .q-tab,
|
|
||||||
.body--dark .q-tab-panel {
|
|
||||||
background-color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 调整面板内边距和布局 */
|
/* 调整面板内边距和布局 */
|
||||||
.ubrowser-panels :deep(.q-tab-panel) {
|
.ubrowser-panels :deep(.q-tab-panel) {
|
||||||
|
@ -49,3 +49,8 @@ export const SymmetricCryptoEditor = defineAsyncComponent(() =>
|
|||||||
export const AsymmetricCryptoEditor = defineAsyncComponent(() =>
|
export const AsymmetricCryptoEditor = defineAsyncComponent(() =>
|
||||||
import("components/composer/crypto/AsymmetricCryptoEditor.vue")
|
import("components/composer/crypto/AsymmetricCryptoEditor.vue")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// File Components
|
||||||
|
export const FileOperationEditor = defineAsyncComponent(() =>
|
||||||
|
import("components/composer/file/FileOperationEditor.vue")
|
||||||
|
);
|
||||||
|
@ -4,8 +4,15 @@ export const fileCommands = {
|
|||||||
defaultOpened: true,
|
defaultOpened: true,
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
value: "open",
|
value: "quickcomposer.file.operation",
|
||||||
label: "打开文件/文件夹/软件",
|
label: "文件/文件夹操作",
|
||||||
|
component: "FileOperationEditor",
|
||||||
|
desc: "文件和文件夹的读写、删除、重命名等操作",
|
||||||
|
isAsync: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "utools.shellOpenItem",
|
||||||
|
label: "默认程序打开",
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
key: "path",
|
key: "path",
|
||||||
@ -17,8 +24,8 @@ export const fileCommands = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "locate",
|
value: "utools.shellShowItemInFolder",
|
||||||
label: "在文件管理器中定位文件",
|
label: "文件管理器中显示",
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
key: "path",
|
key: "path",
|
||||||
@ -29,5 +36,18 @@ export const fileCommands = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "utools.getFileIcon",
|
||||||
|
label: "获取文件图标",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
key: "path",
|
||||||
|
label: "文件或软件的绝对路径",
|
||||||
|
type: "input",
|
||||||
|
defaultValue: "",
|
||||||
|
icon: "folder_open",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@ export const networkCommands = {
|
|||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
value: "visit",
|
value: "visit",
|
||||||
label: "用默认浏览器打开网址",
|
label: "默认浏览器打开网址",
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
key: "url",
|
key: "url",
|
||||||
@ -18,7 +18,7 @@ export const networkCommands = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "utools.ubrowser.goto",
|
value: "utools.ubrowser.goto",
|
||||||
label: "用ubrowser打开网址",
|
label: "ubrowser打开网址",
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
key: "url",
|
key: "url",
|
||||||
|
133
src/js/composer/customComponentGuide.js
Normal file
133
src/js/composer/customComponentGuide.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* Custom Component Creation Guide
|
||||||
|
* 自定义组件创建指南
|
||||||
|
*/
|
||||||
|
const customComponentGuide = {
|
||||||
|
description: "创建自定义命令组件的完整流程",
|
||||||
|
steps: {
|
||||||
|
"1. Backend Interface": {
|
||||||
|
location: "plugin/lib/quickcomposer/xxx/yyy.js",
|
||||||
|
description: "创建具体功能实现",
|
||||||
|
requirements: {
|
||||||
|
functionDefinition: "使用独立函数而非对象方法",
|
||||||
|
asyncHandling: "使用 async/await 处理异步操作",
|
||||||
|
errorHandling: "合理的错误捕获和提示",
|
||||||
|
paramValidation: "检查必要参数是否存在",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"2. Interface Export": {
|
||||||
|
location: "plugin/lib/quickcomposer/xxx/index.js",
|
||||||
|
description: "导出接口给quickcomposer使用",
|
||||||
|
examples: {
|
||||||
|
singleFunction: "module.exports = { operation }",
|
||||||
|
multipleFunctions: "module.exports = { ...encoder, ...hash }",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"3. Interface Registration": {
|
||||||
|
location: "plugin/lib/quickcomposer.js",
|
||||||
|
description: "将接口注册到quickcomposer对象",
|
||||||
|
format: "quickcomposer.xxx = require('./quickcomposer/xxx')",
|
||||||
|
},
|
||||||
|
"4. Component Development": {
|
||||||
|
location: "src/components/composer/xxx/YourComponent.vue",
|
||||||
|
basicStructure: {
|
||||||
|
template: "组件模板,使用quasar组件库",
|
||||||
|
script: "组件逻辑,使用Vue3 defineComponent",
|
||||||
|
style: "组件样式,建议使用scoped",
|
||||||
|
},
|
||||||
|
keyPoints: {
|
||||||
|
variableInput: {
|
||||||
|
scenarios: [
|
||||||
|
"需要支持变量输入的文本框",
|
||||||
|
"数字输入框(设置inputType='number')",
|
||||||
|
"需要自动处理引号的输入",
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
vModel: "双向绑定值",
|
||||||
|
command: "配置图标和输入类型",
|
||||||
|
label: "输入框标签",
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
description: "需要监听的事件",
|
||||||
|
list: ["@update:model-value='updateConfig' - 监听值变化并更新代码"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selectInput: {
|
||||||
|
description: "选择框组件",
|
||||||
|
component: "q-select",
|
||||||
|
props: {
|
||||||
|
"v-model": "双向绑定值",
|
||||||
|
options: "选项列表",
|
||||||
|
label: "标签文本",
|
||||||
|
"emit-value": "true - 使用选项的value作为值",
|
||||||
|
"map-options": "true - 启用选项映射",
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
"@update:model-value": "必须监听此事件以触发代码更新",
|
||||||
|
},
|
||||||
|
tips: "所有影响代码生成的输入组件都必须在值变化时触发updateConfig",
|
||||||
|
},
|
||||||
|
codeGeneration: {
|
||||||
|
tool: "使用formatJsonVariables处理变量",
|
||||||
|
params: {
|
||||||
|
config: "完整的配置对象",
|
||||||
|
variableFields: "需要处理的字段列表",
|
||||||
|
},
|
||||||
|
formats: {
|
||||||
|
objectParams: {
|
||||||
|
description: "当参数是对象时的处理方式",
|
||||||
|
example:
|
||||||
|
"`quickcomposer.xxx.yyy(${formatJsonVariables(config, variableFields)})`",
|
||||||
|
reference: "参考 AxiosConfigEditor.vue",
|
||||||
|
},
|
||||||
|
simpleParams: {
|
||||||
|
description: "当参数是简单值时的处理方式",
|
||||||
|
example: "`${functionName}(${args.join(',')})`",
|
||||||
|
reference: "参考 MultiParams.vue",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"5. Component Registration": {
|
||||||
|
location: "src/js/composer/cardComponents.js",
|
||||||
|
description: "使用defineAsyncComponent注册组件",
|
||||||
|
format:
|
||||||
|
"export const YourComponent = defineAsyncComponent(() => import('path/to/component'))",
|
||||||
|
},
|
||||||
|
"6. Command Configuration": {
|
||||||
|
location: "src/js/composer/commands/xxxCommands.js",
|
||||||
|
requiredFields: {
|
||||||
|
value: "quickcomposer.xxx.yyy",
|
||||||
|
label: "显示名称",
|
||||||
|
component: "组件名称",
|
||||||
|
},
|
||||||
|
optionalFields: {
|
||||||
|
desc: "命令描述",
|
||||||
|
isAsync: "是否异步命令",
|
||||||
|
isControlFlow: "是否控制流命令",
|
||||||
|
allowEmptyArgv: "是否允许空参数",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
variableHandling: {
|
||||||
|
description: "VariableInput 值的处理方式取决于参数类型",
|
||||||
|
cases: {
|
||||||
|
objectCase:
|
||||||
|
"当值在对象中时,使用 formatJsonVariables 处理,如 AxiosConfigEditor",
|
||||||
|
simpleCase: "当值是直接参数时,直接使用值本身,如 MultiParams",
|
||||||
|
},
|
||||||
|
tips: "formatJsonVariables 主要用于处理对象中的变量,避免对简单参数使用,以免产生不必要的引号",
|
||||||
|
},
|
||||||
|
asyncCommand: "后端使用异步函数时,命令配置需要设置isAsync: true",
|
||||||
|
componentStructure: "参考现有组件的实现方式,保持一致的代码风格",
|
||||||
|
errorHandling: "前后端都需要适当的错误处理和提示",
|
||||||
|
typeChecking: "确保所有参数都有适当的类型检查",
|
||||||
|
},
|
||||||
|
examples: {
|
||||||
|
simpleComponent: "RegexEditor - 单一功能的组件",
|
||||||
|
complexComponent: "AxiosConfigEditor - 多功能、多配置的组件",
|
||||||
|
controlComponent: "ConditionalJudgment - 流程控制组件",
|
||||||
|
},
|
||||||
|
};
|
32
项目说明.json
32
项目说明.json
@ -15,6 +15,7 @@
|
|||||||
"isControlFlow": "可选,是否是流程控制命令",
|
"isControlFlow": "可选,是否是流程控制命令",
|
||||||
"commandChain": "可选,命令链,流程控制命令使用",
|
"commandChain": "可选,命令链,流程控制命令使用",
|
||||||
"allowEmptyArgv": "可选,是否允许空参数",
|
"allowEmptyArgv": "可选,是否允许空参数",
|
||||||
|
"isAsync": "可选,是否是异步命令",
|
||||||
"config": {
|
"config": {
|
||||||
"描述": "可选,命令的配置,用来在MultiParams组件中显示,是一个数组,每个元素是一个对象",
|
"描述": "可选,命令的配置,用来在MultiParams组件中显示,是一个数组,每个元素是一个对象",
|
||||||
"配置项属性": {
|
"配置项属性": {
|
||||||
@ -47,6 +48,9 @@
|
|||||||
"导出组件": "导出所有注册的组件供CommandComposer使用"
|
"导出组件": "导出所有注册的组件供CommandComposer使用"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"customComponentGuide.js": {
|
||||||
|
"描述": "自定义组件创建指南"
|
||||||
|
},
|
||||||
"formatString.js": {
|
"formatString.js": {
|
||||||
"描述": "处理JSON字符串中的值,主要处理来自VariableInput的字段,最后返回字符串",
|
"描述": "处理JSON字符串中的值,主要处理来自VariableInput的字段,最后返回字符串",
|
||||||
"主要功能": {
|
"主要功能": {
|
||||||
@ -72,6 +76,34 @@
|
|||||||
"描述": "存放Vue组件",
|
"描述": "存放Vue组件",
|
||||||
"composer": {
|
"composer": {
|
||||||
"描述": "存放可视化编排相关的组件",
|
"描述": "存放可视化编排相关的组件",
|
||||||
|
"file": {
|
||||||
|
"描述": "存放文件操作相关的组件",
|
||||||
|
"组件列表": {
|
||||||
|
"FileOperationEditor.vue": {
|
||||||
|
"描述": "文件操作编辑器组件",
|
||||||
|
"主要功能": {
|
||||||
|
"文件读取": {
|
||||||
|
"编码支持": "UTF-8、ASCII、Base64、二进制、十六进制",
|
||||||
|
"读取模式": "全部读取、指定位置读取",
|
||||||
|
"参数配置": "起始位置、读取长度"
|
||||||
|
},
|
||||||
|
"文件写入": {
|
||||||
|
"编码支持": "UTF-8、ASCII、Base64、二进制、十六进制",
|
||||||
|
"写入模式": "覆盖写入、追加写入",
|
||||||
|
"内容输入": "支持变量和文本输入"
|
||||||
|
},
|
||||||
|
"文件删除": {
|
||||||
|
"删除选项": "递归删除、强制删除"
|
||||||
|
},
|
||||||
|
"文件管理": {
|
||||||
|
"重命名/移动": "支持文件重命名和移动",
|
||||||
|
"权限管理": "支持修改文件权限,支持递归修改",
|
||||||
|
"所有者管理": "支持修改文件所有者和组,支持递归修改"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
"描述": "基础UI组件",
|
"描述": "基础UI组件",
|
||||||
"组件列表": {
|
"组件列表": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user