Skip to content

Commit

Permalink
feat(web): 支持文件(夹)拖拽放置,文件(夹)的创建,删除
Browse files Browse the repository at this point in the history
  • Loading branch information
enncy committed Mar 1, 2022
1 parent fb1b7c9 commit 705cc21
Show file tree
Hide file tree
Showing 11 changed files with 502 additions and 216 deletions.
4 changes: 0 additions & 4 deletions packages/app/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ async function openWindow() {
const win = createWindow();

if (!app.isPackaged) {
/**
* using `mode` options to prevent issue : {@link https://github.com/electron/electron/issues/32702}
*/
await win.loadURL("http://localhost:3000");
win.webContents.openDevTools({ mode: "detach" });
} else {
await win.loadFile("./public/index.html");
}
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/tasks/remote.register.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { ipcMain, app, dialog, BrowserWindow } = require("electron");
const { ipcMain, app, dialog, BrowserWindow, clipboard } = require("electron");
const Logger = require("../logger");

const { autoLaunch } = require("./auto.launch");
Expand Down Expand Up @@ -40,4 +40,5 @@ exports.remoteRegister = function (win) {
registerRemoteEvent("methods", {
autoLaunch,
});
registerRemoteEvent("logger", Logger("render"));
};
24 changes: 24 additions & 0 deletions packages/web/src/assets/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,27 @@ html,
background: rgba(0, 0, 0, 0.1);
box-shadow: inset006pxrgba(0, 0, 0, 0.3);
}
/** 通知栏样式 */
.ant-notification {
/** 主容器 */
/** 底部按钮 */
}
.ant-notification .anticon {
transform: translate(-0.5px, -6px);
}
.ant-notification .ant-notification-notice {
padding: 12px;
}
.ant-notification .ant-notification-notice-btn {
margin-top: 8px;
}
.ant-notification .ant-notification-notice-with-icon .ant-notification-notice-description {
margin-left: 0;
font-size: 12px;
}
.ant-notification .ant-notification-notice-with-icon .ant-notification-notice-message {
margin-left: 36px;
}
.ant-notification .ant-notification-notice-description {
overflow: auto;
}
29 changes: 29 additions & 0 deletions packages/web/src/assets/less/common.less
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,32 @@ html,
background: rgba(0, 0, 0, 0.1);
box-shadow: inset006pxrgba(0, 0, 0, 0.3);
}

/** 通知栏样式 */
.ant-notification {
.anticon {
transform: translate(-0.5px, -6px);
}

/** 主容器 */
.ant-notification-notice{
padding: 12px;
}

/** 底部按钮 */
.ant-notification-notice-btn{
margin-top: 8px;
}

.ant-notification-notice-with-icon .ant-notification-notice-description {
margin-left: 0;
font-size: 12px;
}
.ant-notification-notice-with-icon .ant-notification-notice-message {
margin-left: 36px;
}

.ant-notification-notice-description {
overflow: auto;
}
}
92 changes: 69 additions & 23 deletions packages/web/src/components/file/File.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,55 @@
import { Stats } from "fs";
import { store } from "../../store";

const fs = require("fs") as typeof import("fs");
const path = require("path") as typeof import("path");

export { fs, path };

/** 文件状态 */
export interface FileStats {
createTime: number;
modifyTime: number;
/** 是否为文件夹 */
isDirectory: boolean;
/** 已被删除 */
removed: boolean;
/** 是否选中 */
selected: boolean;
/** 重命名中 */
renaming: boolean;
}

/**
* 文件对象
*/
export interface File {
/** 文件名 */
title: string;
key: string;
slots: {
icon: string;
};

stat?: FileStats;
path?: string;
content?: string;
/** 文件信息 */
stat: FileStats;
/** 文件路径 */
path: string;
/** 父目录 */
parent: string;
/** 文件内容 */
content: string;
/** 子文件 */
children?: File[];
}

export interface FileStats {
isDirectory: boolean;
createTime: number;
modifyTime: number;
}

/**
* 获取可用文件名
* @param name 名字模板, 例如 新建文件夹($count)
* @param rootPath 父目录
* @param name 名字模板, 例如 新建文件夹($count) , $count - 序号占位符
*/
export function validFileName(name: string) {
export function validFileName(rootPath: string, name: string) {
if (!name.includes("$count")) throw "名字模板未带有序号占位符 - $count";
let count = 0;
let p = "";
while (true) {
p = path.resolve(store.workspace, name.replace("($count)", count++ === 0 ? "" : `(${count})`));
p = path.resolve(rootPath, name.replace("($count)", count++ === 0 ? "" : `(${count})`));
if (!fs.existsSync(p)) {
break;
}
Expand All @@ -42,43 +58,73 @@ export function validFileName(name: string) {
}

/**
* 创建文件(夹)对象
* 创建文件(夹)对象,到文件树
*
* @param filePath 文件(夹)路径
* @param title 覆盖文件显示名
* @returns
*/
export function createFile(filePath: string, title?: string): File {
export function createFileNode(rootPath: string, filePath?: string): File {
filePath = filePath || rootPath;
const stat = fs.statSync(filePath);
let children;
let content;
let content = "";
let icon;
if (stat.isDirectory()) {
icon = "dir";
children = fs
.readdirSync(filePath)
.map((childFilePath) => createFile(path.resolve(filePath, childFilePath)))
.map((childFilePath) => createFileNode(rootPath, path.resolve(filePath || rootPath, childFilePath)))
.filter((f) => !!f);
} else {
icon = "file";
content = fs.readFileSync(filePath).toString();
}

title = title || path.basename(filePath);
let title = path.basename(filePath);
let p = path.dirname(filePath);
return {
title,
key: filePath,
key: filePath
.replace(rootPath + "\\", "")
.split("\\")
.join("-"),
slots: {
icon,
},
stat: {
isDirectory: stat.isDirectory(),
createTime: stat.birthtimeMs,
modifyTime: stat.ctimeMs,
removed: false,
selected: false,
renaming: true,
},
content,
path: p,
parent: p,
path: filePath,
children,
};
}

/**
* 提供文件遍历操作
* @param files 文件源
* @param handlers 处理器
*/
export function loopFiles(files: File[], ...handlers: { (files: File[]): File[] }[]) {
for (const handler of handlers) {
files = handler(files);
}

for (const file of files) {
if (file.children) {
for (const handler of handlers) {
file.children = handler(file.children);
}
loopFiles(file.children, ...handlers);
}
}

return files;
}
53 changes: 49 additions & 4 deletions packages/web/src/components/file/FileMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,38 @@
import { ref, reactive, toRefs, Ref } from "vue";
import { MenuItem } from "../menus";
import { FileStats } from "./File";
import { createFileNode, File, fs, path, validFileName } from "./File";
import Menus from "../menus/Menus.vue";
const { shell } = require("electron");
interface FileMenuProps {
stat: FileStats;
file: File;
rootPath: string;
}
const props = defineProps<FileMenuProps>();
const { stat } = toRefs(props);
const emits = defineEmits<{
(e: "update:file", file: File): void;
(e: "fileChange", file: File): void;
}>();
const { file, rootPath } = toRefs(props);
const dirMenus: MenuItem[] = [
{
title: "新建文件夹",
icon: "icon-wenjianjia",
onClick() {
createFile("新建文件夹($count)", (newFilePath) => {
fs.mkdirSync(newFilePath);
});
},
},
{
title: "新建OCS文件",
icon: "icon-file-text",
onClick() {
createFile("新建OCS文件($count).ocs", (newFilePath) => {
fs.writeFileSync(newFilePath, "{}");
});
},
},
];
Expand All @@ -37,14 +53,28 @@ const baseMenus: MenuItem[] = [
divide: true,
title: "打开文件位置",
icon: "icon-location",
onClick() {
shell.showItemInFolder(file.value.path);
},
},
{
title: "删除",
icon: "icon-delete",
onClick() {
fs.unlinkSync(file.value.path);
file.value.stat.removed = true;
emits("update:file", file.value);
emits("fileChange", file.value);
},
},
{
title: "重命名",
icon: "icon-redo",
onClick() {
file.value.stat.renaming = true;
emits("update:file", file.value);
emits("fileChange", file.value);
},
},
{
title: "属性",
Expand All @@ -54,7 +84,7 @@ const baseMenus: MenuItem[] = [
const menus: Ref<MenuItem[]> = ref(baseMenus);
if (stat.value.isDirectory) {
if (file.value.stat?.isDirectory) {
let mes = menus.value;
mes[0].divide = true;
menus.value = dirMenus.concat(...mes);
Expand All @@ -63,6 +93,21 @@ if (stat.value.isDirectory) {
mes[0].divide = true;
menus.value = fileMenus.concat(...mes);
}
/**
* 创建文件(夹)到本地,并且添加到文件树中
* @param newFileNameFormate 文件格式化字符串 {@link validFileName}
* @param handler 文件路径结果处理
*/
function createFile(newFileNameFormate: string, handler: (newFilePath: string) => void) {
let _file = file.value;
let newFilePath = validFileName(_file.path, newFileNameFormate);
handler(newFilePath);
_file.children = _file.children || [];
_file.children.push(createFileNode(rootPath.value, newFilePath));
emits("update:file", _file);
emits("fileChange", _file);
}
</script>

<style scope lang="less"></style>
Loading

0 comments on commit 705cc21

Please sign in to comment.