Skip to content

Commit

Permalink
feat(web): 添加文件解析,使用懒加载进行文件的显示
Browse files Browse the repository at this point in the history
  • Loading branch information
enncy committed Mar 7, 2022
1 parent 6f4554f commit 0da0f02
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 95 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"electron-store": "^8.0.1",
"fs-extra": "^10.0.1",
"get-port": "^5.1.1",
"jsonlint": "^1.6.3",
"nan": "^2.15.0",
"playwright": "^1.18.1",
"ws": "^8.5.0"
Expand Down
73 changes: 73 additions & 0 deletions packages/web/src/components/CodeHighlight.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<pre
class="highlight"
ref="code"
><code v-html="render(code, lang,[[errorLine - 1,'error']])" ></code></pre>
</template>

<script setup lang="ts">
import { ref, toRefs, computed } from "vue";
import hljs from "highlight.js";
import jsonLanguage from "highlight.js/lib/languages/json";
import "highlight.js/styles/atom-one-light.css";
hljs.registerLanguage("json", jsonLanguage);
interface CodeHighlightProps {
code: string;
lang: string;
errorLine?: number;
}
const props = withDefaults(defineProps<CodeHighlightProps>(), {
errorLine: -1,
});
const { code, errorLine } = toRefs(props);
/**
* 渲染代码高亮
* @param content 内容
* @param language 语言
* @param styleList Array<[number,class]> 元素类列表, number - 行号
*/
function render(content: string, language: string, styleList: any[]) {
let html = hljs.highlight(content, { language }).value;
console.log(styleList);
return Array.from(html.match(/(.*?)\n/g) || [])
.map(
(s, i) =>
`<div class="${
styleList.find((s) => s[0] === i)?.[1] || ""
}"><line>${i}</line>${s}</div>`
)
.join("");
}
</script>

<style scope lang="less">
.highlight {
text-align: left;
height: 100%;
.error {
background-color: #ff000030;
}
.info {
background-color: #70b2df30;
}
}
line {
color: #97979790;
left: 10px;
width: 30px;
margin-right: 4px;
display: inline-block;
border-right: 1px solid #97979790;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
</style>
60 changes: 0 additions & 60 deletions packages/web/src/components/JSONEditor.vue

This file was deleted.

4 changes: 1 addition & 3 deletions packages/web/src/components/file/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface FileNode {
/** 文件名 */
title: string;
uid: string;
key: string;

slots: {
icon: string;
};
Expand All @@ -40,8 +40,6 @@ export interface FileNode {
path: string;
/** 父目录 */
parent: string;
/** 文件内容 */
content: string;
/** 子文件 */
children?: FileNode[];
}
Expand Down
94 changes: 69 additions & 25 deletions packages/web/src/components/file/File.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<template v-if="content !== undefined">
<template v-if="options !== undefined">
<div class="file">
<div class="form-header text-start border-bottom">
<div class="file-title">
Expand Down Expand Up @@ -39,7 +39,7 @@

<div class="form-container">
<template v-if="showSource">
<JSONEditor :json="JSON.stringify(content, null, 4)" />
<CodeHighlight lang="json" :code="content"></CodeHighlight>
</template>

<template v-else>
Expand All @@ -50,7 +50,7 @@
<a-switch
checked-children="开启"
un-checked-children="关闭"
v-model:checked="content.launchOptions.headless"
v-model:checked="options.launchOptions.headless"
/>
</span>
</div>
Expand All @@ -67,7 +67,7 @@
<div class="form">
<label>浏览器路径</label>
<a-input
v-model:value="content.launchOptions.executablePath"
v-model:value="options.launchOptions.executablePath"
placeholder="浏览器路径"
/>
</div>
Expand All @@ -76,7 +76,7 @@
<label>登录类型</label>
<a-select
style="width: 100%"
v-model:value="content.scripts[0].name"
v-model:value="options.scripts[0].name"
@change="onScriptChange"
show-search
>
Expand All @@ -96,13 +96,13 @@
<label> {{ item.title }} </label>
<template v-if="item.type === 'text'">
<a-input
v-model:value="(content.scripts[0].options as any)[item.name]"
v-model:value="(options.scripts[0].options as any)[item.name]"
:placeholder="'输入' + item.title"
/>
</template>
<template v-if="item.type === 'password'">
<a-input-password
v-model:value="(content.scripts[0].options as any)[item.name]"
v-model:value="(options.scripts[0].options as any)[item.name]"
:placeholder="'输入' + item.title"
/>
</template>
Expand All @@ -115,26 +115,45 @@
<Transition name="fade">
<div v-show="showTerminal" class="h-100 iterminal overflow-hidden">
<span> 控制台 </span>
<Terminal class="h-100" :running="file.stat.running" :file="file" />
<Terminal
class="h-100"
:running="file.stat.running"
:file="file"
:options="options"
/>
</div>
</Transition>
</div>
</template>
<template v-else>文件格式错误</template>
<template v-else>
<div class="error-page">
<div class="error-message">
<p>解析文件时第 {{ errorLine }} 行发生错误:</p>
<pre>{{ error }}</pre>

<CodeHighlight
class="json-editor border rounded"
lang="json"
:code="content"
:error-line="errorLine"
></CodeHighlight>
</div>
</div>
</template>
</template>

<script setup lang="ts">
import { toRefs, computed, ref, watch } from "vue";
import { FileNode, fs, path } from "./File";
import { scriptForms, Form } from ".";
import JSONEditor from "../JSONEditor.vue";
import CodeHighlight from "../CodeHighlight.vue";
import { LaunchScriptsOptions } from "@ocsjs/scripts";
import { debounce } from "../../utils/index";
import Terminal from "../terminal/Terminal.vue";
import Card from "../Card.vue";
import { store } from "../../store";
import { notify } from "../../utils/notify";
import Icon from "../Icon.vue";
const jsonlint = require("jsonlint");
const { scriptNames } = require("@ocsjs/scripts");
interface FormCreateProps {
Expand All @@ -144,11 +163,21 @@ const props = withDefaults(defineProps<FormCreateProps>(), {});
const { file } = toRefs(props);
/** 文件内容 */
const content = ref<LaunchScriptsOptions>();
const content = ref(fs.readFileSync(file.value.path).toString());
/** 解析内容 */
const options = ref<LaunchScriptsOptions>();
/** 错误信息 */
const error = ref("");
/** 错误行 */
const errorLine = ref(0);
try {
content.value = JSON.parse(file.value.content);
options.value = jsonlint.parse(content.value);
} catch (e) {
notify("文件格式错误", e, "file", { type: "error", copy: true });
error.value = (e as Error).message;
errorLine.value = parseInt(
error.value.match(/Parse error on line (\d+):/)?.[1] || "0"
);
console.log(error.value);
}
/** 是否显示源码 */
Expand All @@ -161,11 +190,11 @@ const openIncognito = ref(false);
setUserDataDir();
if (content.value) {
if (options.value) {
watch(openIncognito, () => {
if (content.value) {
if (options.value) {
if (openIncognito.value) {
content.value.userDataDir = "";
options.value.userDataDir = "";
} else {
setUserDataDir();
}
Expand All @@ -174,10 +203,9 @@ if (content.value) {
/** 监听文件更新 */
watch(
content.value,
options.value,
debounce(() => {
const value = JSON.stringify(content.value, null, 4);
file.value.content = value;
fs.writeFileSync(file.value.path, value);
}, 500)
);
Expand All @@ -187,8 +215,8 @@ if (content.value) {
* 解析第一个 script 内容,根据 script 的名字进行解析,并生成表单
*/
const loginTypeForms = computed(() => {
if (content.value) {
const target = scriptForms[content.value.scripts[0].name] as Form<any>;
if (options.value) {
const target = scriptForms[options.value.scripts[0].name] as Form<any>;
const keys = Reflect.ownKeys(target);
return keys.map((key) => ({
name: key,
Expand All @@ -200,14 +228,14 @@ const loginTypeForms = computed(() => {
/** 登录脚本名更新时,重置options内容 */
function onScriptChange() {
if (content.value) {
content.value.scripts[0].options = {};
if (options.value) {
options.value.scripts[0].options = {};
}
}
function setUserDataDir() {
if (content.value) {
content.value.userDataDir = path.join(
if (options.value) {
options.value.userDataDir = path.join(
store["user-data-path"],
"scriptUserData",
file.value.uid
Expand All @@ -217,6 +245,22 @@ function setUserDataDir() {
</script>

<style scope lang="less">
.error-page {
width: 100%;
padding-top: 50px;
text-align: left;
.error-message {
padding: 4px;
width: fit-content;
margin: 0 auto;
.json-editor {
width: 100%;
}
}
}
.file {
height: 100%;
display: grid;
Expand Down
1 change: 1 addition & 0 deletions packages/web/src/components/file/FileTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@drop="onDrop"
@select="onSelect"
:autoExpandParent="false"
:replaceFields="{ children: 'children', title: 'title', key: 'path' }"
>
<template #switcherIcon>
<Icon type="icon-down" />
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/components/project/ProjectNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function onSelect(
Project.opened.value.forEach((file) => (file.stat.show = false));
/** 寻找打开过的文件 */
const openedFile = Project.opened.value.find((f) => f.key === file.key);
const openedFile = Project.opened.value.find((f) => f.uid === file.uid);
/** 如果该文件之前打开过 */
if (openedFile) {
openedFile.stat.show = true;
Expand Down
Loading

0 comments on commit 0da0f02

Please sign in to comment.