feat(core): micropython 板卡文件管理 右键菜单添加 上传 和 下载 选项
This commit is contained in:
@@ -11,7 +11,7 @@ class FS {
|
||||
|
||||
async createFile(filePath) {}
|
||||
|
||||
async readFile(filePath) {}
|
||||
async readFile(filePath, encoding = 'utf8') {}
|
||||
|
||||
async writeFile(filePath, data) {}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ goog.require('layui');
|
||||
goog.require('Mixly.PageBase');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.HTMLTemplate');
|
||||
goog.require('Mixly.DragV');
|
||||
goog.require('Mixly.StatusBar');
|
||||
@@ -17,6 +18,7 @@ const {
|
||||
PageBase,
|
||||
Env,
|
||||
Msg,
|
||||
Debug,
|
||||
HTMLTemplate,
|
||||
DragV,
|
||||
StatusBar,
|
||||
@@ -142,6 +144,141 @@ class StatusBarAmpy extends PageBase {
|
||||
fileTreeMenu.add({
|
||||
weight: 14,
|
||||
id: 'sep5',
|
||||
data: '---------'
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 15,
|
||||
id: 'upload-folder',
|
||||
preconditionFn: ($trigger) => {
|
||||
let type = $trigger.attr('type');
|
||||
if (type === 'file') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
data: {
|
||||
isHtmlName: true,
|
||||
name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFolder'], ''),
|
||||
callback: async (_, { $trigger }) => {
|
||||
this.#fileTree_.showProgress();
|
||||
try {
|
||||
const fp = await window.showDirectoryPicker();
|
||||
if (!fp) {
|
||||
this.#fileTree_.hideProgress();
|
||||
return;
|
||||
}
|
||||
const type = $trigger.attr('type');
|
||||
let folderPath = '/';
|
||||
if (type !== 'root') {
|
||||
folderPath = $trigger.attr('id');
|
||||
}
|
||||
const fs = this.#fileTree_.getFS();
|
||||
try {
|
||||
for await (const [key, value] of fp.entries()) {
|
||||
if (value.kind !== 'file') {
|
||||
continue;
|
||||
}
|
||||
const file = await value.getFile();
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer);
|
||||
if (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
this.#fileTree_.refreshFolder(folderPath);
|
||||
} catch (_) { }
|
||||
this.#fileTree_.hideProgress();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 16,
|
||||
id: 'upload-file',
|
||||
preconditionFn: ($trigger) => {
|
||||
let type = $trigger.attr('type');
|
||||
if (type === 'file') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
data: {
|
||||
isHtmlName: true,
|
||||
name: Menu.getItem(Msg.Lang['statusbar.ampy.uploadFile'], ''),
|
||||
callback: async (_, { $trigger }) => {
|
||||
this.#fileTree_.showProgress();
|
||||
try {
|
||||
const [ fp ] = await window.showOpenFilePicker({
|
||||
multiple: false
|
||||
});
|
||||
if (!fp) {
|
||||
this.#fileTree_.hideProgress();
|
||||
return;
|
||||
}
|
||||
const type = $trigger.attr('type');
|
||||
let folderPath = '/';
|
||||
if (type !== 'root') {
|
||||
folderPath = $trigger.attr('id');
|
||||
}
|
||||
const file = await fp.getFile();
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const fs = this.#fileTree_.getFS();
|
||||
const [error,] = await fs.writeFile(path.join(folderPath, file.name), arrayBuffer);
|
||||
if (error) {
|
||||
Debug.error(error);
|
||||
} else {
|
||||
this.#fileTree_.refreshFolder(folderPath);
|
||||
}
|
||||
} catch (_) { }
|
||||
this.#fileTree_.hideProgress();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 17,
|
||||
id: 'download',
|
||||
preconditionFn: ($trigger) => {
|
||||
const type = $trigger.attr('type');
|
||||
if (type === 'file') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
data: {
|
||||
isHtmlName: true,
|
||||
name: Menu.getItem(Msg.Lang['statusbar.ampy.download'], ''),
|
||||
callback: async (_, { $trigger }) => {
|
||||
const filePath = $trigger.attr('id');
|
||||
this.#fileTree_.showProgress();
|
||||
const fp = await window.showSaveFilePicker({
|
||||
suggestedName: path.basename(filePath)
|
||||
});
|
||||
if (!fp) {
|
||||
this.#fileTree_.hideProgress();
|
||||
return;
|
||||
}
|
||||
const fs = this.#fileTree_.getFS();
|
||||
const [error, result] = await fs.readFile(filePath, 'buffer');
|
||||
if (error) {
|
||||
Debug.error(error);
|
||||
} else {
|
||||
const writer = await fp.createWritable();
|
||||
await writer.write(result);
|
||||
await writer.close();
|
||||
}
|
||||
this.#fileTree_.hideProgress();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 18,
|
||||
id: 'sep6',
|
||||
preconditionFn: ($trigger) => {
|
||||
const selectedNodeId = this.#fileTree_.getSelectedNodeId();
|
||||
let type = $trigger.attr('type');
|
||||
@@ -155,7 +292,7 @@ class StatusBarAmpy extends PageBase {
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 15,
|
||||
weight: 19,
|
||||
id: 'refresh',
|
||||
preconditionFn: ($trigger) => {
|
||||
const selectedNodeId = this.#fileTree_.getSelectedNodeId();
|
||||
@@ -187,7 +324,7 @@ class StatusBarAmpy extends PageBase {
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 16,
|
||||
weight: 20,
|
||||
id: 'sep6',
|
||||
preconditionFn: ($trigger) => {
|
||||
let type = $trigger.attr('type');
|
||||
@@ -197,7 +334,7 @@ class StatusBarAmpy extends PageBase {
|
||||
});
|
||||
|
||||
fileTreeMenu.add({
|
||||
weight: 17,
|
||||
weight: 21,
|
||||
id: 'exit',
|
||||
preconditionFn: ($trigger) => {
|
||||
let type = $trigger.attr('type');
|
||||
|
||||
@@ -957,6 +957,7 @@
|
||||
"Mixly.PageBase",
|
||||
"Mixly.Env",
|
||||
"Mixly.Msg",
|
||||
"Mixly.Debug",
|
||||
"Mixly.HTMLTemplate",
|
||||
"Mixly.DragV",
|
||||
"Mixly.StatusBar",
|
||||
|
||||
@@ -54,12 +54,16 @@ class AmpyFS extends FS {
|
||||
return [error, stdout];
|
||||
}
|
||||
|
||||
async readFile(filePath) {
|
||||
async readFile(filePath, encoding = 'utf8') {
|
||||
let stdout = '', error = null;
|
||||
try {
|
||||
const output = await this.#ampy_.get(this.#port_, this.#baud_, filePath);
|
||||
stdout = output.stdout;
|
||||
if (encoding = 'utf8') {
|
||||
stdout = this.#decoder_.decode(this.#ampy_.unhexlify(stdout));
|
||||
} else {
|
||||
stdout = this.#ampy_.unhexlify(stdout);
|
||||
}
|
||||
} catch (e) {
|
||||
error = e;
|
||||
Debug.error(error);
|
||||
@@ -71,6 +75,9 @@ class AmpyFS extends FS {
|
||||
let stdout = '', error = null;
|
||||
try {
|
||||
const startFilePath = path.join(Env.clientPath, 'temp/temp');
|
||||
if (data.constructor.name === 'ArrayBuffer') {
|
||||
data = Buffer.from(data);
|
||||
}
|
||||
await fs_extra.outputFile(startFilePath, data);
|
||||
const output = await this.#ampy_.put(this.#port_, this.#baud_, startFilePath, filePath);
|
||||
stdout = output.stdout;
|
||||
|
||||
@@ -54,8 +54,8 @@ class AmpyExt extends Ampy {
|
||||
return this.exec(port, this.render('ls', { port, baud, folderPath }));
|
||||
}
|
||||
|
||||
async get(port, baud, filePath) {
|
||||
return this.exec(port, this.render('get', { port, baud, filePath }));
|
||||
async get(port, baud, filePath, encoding = 'utf8') {
|
||||
return this.exec(port, this.render('get', { port, baud, filePath, encoding }));
|
||||
}
|
||||
|
||||
async mkdir(port, baud, folderPath) {
|
||||
|
||||
@@ -72,12 +72,12 @@ class AmpyFS extends FS {
|
||||
return [error, stdout];
|
||||
}
|
||||
|
||||
async readFile(filePath) {
|
||||
async readFile(filePath, encoding = 'utf8') {
|
||||
let stdout = '', error = null, ampy = null;
|
||||
try {
|
||||
ampy = await this.getAmpy();
|
||||
await ampy.enter();
|
||||
stdout = await ampy.get(filePath);
|
||||
stdout = await ampy.get(filePath, encoding);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
Debug.error(error);
|
||||
|
||||
@@ -211,7 +211,7 @@ class AmpyExt extends Ampy {
|
||||
this.#active_ = false;
|
||||
}
|
||||
|
||||
async get(filename, timeout = 5000) {
|
||||
async get(filename, encoding = 'utf8', timeout = 5000) {
|
||||
if (!this.isActive()) {
|
||||
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
|
||||
}
|
||||
@@ -222,7 +222,11 @@ class AmpyExt extends Ampy {
|
||||
if (dataError) {
|
||||
return '';
|
||||
}
|
||||
if (encoding === 'utf8') {
|
||||
return this.#device_.decode(this.unhexlify(data));
|
||||
} else {
|
||||
return this.unhexlify(data);
|
||||
}
|
||||
}
|
||||
|
||||
async put(filename, data, timeout = 5000) {
|
||||
|
||||
@@ -57,8 +57,7 @@ File.open = async () => {
|
||||
const fileConfig = {
|
||||
multiple: false,
|
||||
types: File.getFileTypes(filters),
|
||||
excludeAcceptAllOption: true,
|
||||
multiple: false,
|
||||
excludeAcceptAllOption: true
|
||||
};
|
||||
try {
|
||||
const [ obj ] = await window.showOpenFilePicker(fileConfig);
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
"statusbar.ampy.refresh": "Refresh",
|
||||
"statusbar.ampy.exit": "Exit",
|
||||
"statusbar.ampy.cannotEdit": "This file type does not support editing",
|
||||
"statusbar.ampy.uploadFile": "Upload file",
|
||||
"statusbar.ampy.uploadFolder": "Upload folder",
|
||||
"statusbar.ampy.download": "Download",
|
||||
"statusbar.dropdownMenu.noOptions": "No options",
|
||||
"statusbar.fs.newMapFolder": "Create a new mapping directory",
|
||||
"statusbar.fs.localFolderNotExist": "Local mapping directory does not exist",
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
"statusbar.ampy.refresh": "刷新",
|
||||
"statusbar.ampy.exit": "退出",
|
||||
"statusbar.ampy.cannotEdit": "该文件类型不支持编辑",
|
||||
"statusbar.ampy.uploadFile": "上传文件",
|
||||
"statusbar.ampy.uploadFolder": "上传文件夹",
|
||||
"statusbar.ampy.download": "下载",
|
||||
"statusbar.dropdownMenu.noOptions": "无选项",
|
||||
"statusbar.fs.newMapFolder": "新建映射目录",
|
||||
"statusbar.fs.localFolderNotExist": "本地映射目录不存在",
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
"statusbar.ampy.refresh": "刷新",
|
||||
"statusbar.ampy.exit": "退出",
|
||||
"statusbar.ampy.cannotEdit": "該文件類型不支援編輯",
|
||||
"statusbar.ampy.uploadFile": "上傳檔案",
|
||||
"statusbar.ampy.uploadFolder": "上傳資料夾",
|
||||
"statusbar.ampy.download": "下載",
|
||||
"statusbar.dropdownMenu.noOptions": "無選項",
|
||||
"statusbar.fs.newMapFolder": "新映射目錄",
|
||||
"statusbar.fs.localFolderNotExist": "本機映射目錄不存在",
|
||||
|
||||
Reference in New Issue
Block a user