前言
本项目是为开发一套容器化的开发、运行、测试环境,用以支持Web开发、程序设计等课程的实验教学。
完善文件压缩类
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
enum FileType {
root,
folder,
ts,
js,
html,
css,
md,
txt,
}
const fileTypes = function (type: string): FileType | undefined {
switch (type) {
case "folder":
return FileType.folder;
case "ts":
return FileType.ts;
case "js":
return FileType.js;
case "html":
return FileType.html;
case "css":
return FileType.css;
case "md":
return FileType.md;
case "txt":
return FileType.txt;
default:
return undefined
}
}
class Ziper {
projName: string = 'web';
zip: any;
constructor() {
this.zip = new JSZip();
}
setProjectName(name: string) {
this.projName = name;
}
updateProject(data: any, parent = this.zip) {
for (var item in data) {
if (data[item].type == FileType.folder) {
let pa = this._addFolder(parent, data[item].name)
this.updateProject(data[item].children, pa);
} else {
this._addFile(parent, data[item].name, data[item].value);
}
}
}
_addFile(parent: any, name: string, value: string) {
parent.file(name, value);
}
_addFolder(parent: any, name: string) {
return parent.folder(name);
}
_saveZIP() {
let projName = this.projName;
this.zip.generateAsync({ type: "blob" })
.then(function (blob: any) {
saveAs(blob, projName);
});
}
}
const ziper = new Ziper();
export { ziper, FileType, fileTypes };
实现代码编辑器与文件管理器的通信
编辑器里:
const setCode=(new_code:string,type:FileType)=>{
code.value=new_code;
switch(type){
case FileType.js:
cmOptions.value.mode="text/javascript";
break;
case FileType.ts:
cmOptions.value.mode="text/typescript";
break;
case FileType.md:
cmOptions.value.mode="text/x-markdown";
break;
case FileType.css:
cmOptions.value.mode="text/css";
break;
case FileType.html:
cmOptions.value.mode="text/html"
break;
}
}
defineExpose({setCode});
const emit = defineEmits(['onChange']);
文件管理器:
const LeftClick = function (data: Tree) {
visible.value = false;
if (data.type != FileType.folder && data.type != FileType.root) {
selectedData.value = data;
emit("setFileContext", selectedData.value.value, selectedData.value.type);
}
}
const emit = defineEmits(['setFileContext']);
const setCode = (value: string) => {
if (selectedData.value != null) {
console.log(selectedData.value.name);
selectedData.value.value = value;
}
}
defineExpose({ setCode })
coding页面:
const setFileContext = (code: string, type: FileType) => {
cmRef.value.setCode(code, type);
}
const onCodeChange = (value: string) => {
sideRef.value.setCode(value);
}
目前的效果
进一步完善
在一开始没有选择文件时显示选择文件来打开编辑器
<code-editor-vue ref="cmRef" @on-change="onCodeChange" v-show="selected"></code-editor-vue>
<el-result title="open file to edit" v-show="!selected">
<template #icon>
<el-icon :size="250">
<files />
</el-icon>
</template>
</el-result>
const store = useFileStore();
const sideRef = ref();
const cmRef = ref();
const selected = computed({
get: () => store.getSelected,
set: (val) => {
selected.value = val;
},
})
pinia中
const useFileStore = defineStore({
id: "file",
state: () => ({
selected: false,
}),
getters: {
getSelected(state) {
return state.selected;
},
},
actions: {
setSelected(s: boolean) {
this.selected = s;
}
},
});
文件管理器栏
const LeftClick = function (data: Tree) {
visible.value = false;
if (data.type != FileType.folder && data.type != FileType.root) {
selectedData.value = data;
store.setSelected(true);
emit("setFileContext", selectedData.value.value, selectedData.value.type);
}
}
const Delete = function () {
ElMessageBox.confirm('You will delete the file,continue?', 'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
const parent = targetNode.value?.parent
const children: Tree[] = parent?.data.children || parent?.data
const index = children.findIndex((d) => d.id === targetData.value?.id)
children.splice(index, 1)
dataSource.value = [...dataSource.value]
selectedData.value=undefined;
store.setSelected(false);
})
.catch(() => {
})
}
将所有文件保存为zip
点击按钮
const Submit = function () {
emit('SaveZIP')
}
const emit = defineEmits(['SaveZIP']);
文件管理栏
const getData = () => {
return dataSource.value[0].children;
}
const getProjectName = () => {
return dataSource.value[0].name;
}
coding页面
const saveZIP = () => {
ziper.setProjectName(sideRef.value.getProjectName());
ziper.updateProject(sideRef.value.getData());
ziper._saveZIP();
}
最终效果
完整代码
CodeEditor.vue
<template>
<CodeMirror ref="cmRef" class="code-mirror" :value="code" :options="cmOptions" :border="true" :height="400"
@change="onChange" @input="onInput" @ready="onReady" autofocus KeepCursorInEnd />
</template>
<script setup lang="ts">
import CodeMirror from "codemirror-editor-vue3"
// language
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/vue/vue.js";
import "codemirror/mode/markdown/markdown.js";
import "codemirror/mode/css/css.js";
import "codemirror/mode/htmlmixed/htmlmixed.js"
import type { Editor, EditorConfiguration } from "codemirror";
// theme css
import "codemirror/theme/rubyblue.css";
import "codemirror/theme/panda-syntax.css";
import "codemirror/theme/darcula.css";
import "codemirror/theme/idea.css";
import "codemirror/theme/eclipse.css";
// require active-line.js
import "codemirror/addon/selection/active-line.js";
// styleSelectedText
import "codemirror/addon/selection/mark-selection.js";
import "codemirror/addon/search/searchcursor.js";
// hint
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/selection/active-line.js";
// highlightSelectionMatches
import "codemirror/addon/scroll/annotatescrollbar.js";
import "codemirror/addon/search/matchesonscrollbar.js";
import "codemirror/addon/search/searchcursor.js";
import "codemirror/addon/search/match-highlighter.js";
// keyMap
import "codemirror/mode/clike/clike.js";
import "codemirror/addon/edit/matchbrackets.js";
import "codemirror/addon/comment/comment.js";
import "codemirror/addon/dialog/dialog.js";
import "codemirror/addon/dialog/dialog.css";
import "codemirror/addon/search/searchcursor.js";
import "codemirror/addon/search/search.js";
import "codemirror/keymap/sublime.js";
// foldGutter
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/xml-fold.js";
import { onMounted, ref, type Ref } from "vue";
import { FileType } from "@/fileziper";
const cminstance = ref<Editor>();
const cmOptions = ref<EditorConfiguration>({
mode: "text/javascript", // 语言模式 java:text/x-java js:text/javascript ts:text/typescript css:text/css html:text/html md:text/x-markdown
tabSize: 4,
theme: "darcula", // 主题
lineNumbers: true, // 显示行号
styleSelectedText: true,
smartIndent: true, // 智能缩进
indentUnit: 4, // 智能缩进单位为4个空格长度
foldGutter: true, // 启用行槽中的代码折叠
styleActiveLine: true, // 显示选中行的样式
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true },
matchBrackets: true,
showCursorWhenSelecting: true,
extraKeys: { Ctrl: "autocomplete" },
autofocus: true,
lineWrapping: true,
});
var code = ref();
function onChange(value: string) {
}
function onInput(value: string) {
emit('onChange', value);
}
function onReady(cm: Editor) {
cminstance.value = cm;
}
onMounted(() => {
// code.value = "let b=20;";
})
const setCode = (new_code: string, type: FileType) => {
// code.value = new_code;
cminstance.value!.setValue(new_code);
switch (type) {
case FileType.js:
cmOptions.value.mode = "text/javascript";
break;
case FileType.ts:
cmOptions.value.mode = "text/typescript";
break;
case FileType.md:
cmOptions.value.mode = "text/x-markdown";
break;
case FileType.css:
cmOptions.value.mode = "text/css";
break;
case FileType.html:
cmOptions.value.mode = "text/html"
break;
case FileType.txt:
cmOptions.value.mode = "text";
break;
}
cminstance.value!.refresh();
}
defineExpose({ setCode });
const emit = defineEmits(['onChange']);
</script>
<style>
.code-mirror {
font-size: 16px !important;
line-height: 150%;
text-align: left !important;
}
</style>
coding-view.vue
import { FileType } from "../fileziper";
<!--login-coding-page-->
<template>
<el-header>
<topmenu :activeIndex="'2'"></topmenu>
</el-header>
<el-main>
<el-row class="tac">
<el-col :span="5">
<side ref="sideRef" @set-file-context="setFileContext" />
</el-col>
<el-col :span="1"></el-col>
<el-col :span="18">
<code-editor-vue ref="cmRef" @on-change="onCodeChange" v-show="selected"></code-editor-vue>
<el-result title="open file to edit" v-show="!selected">
<template #icon>
<el-icon :size="250">
<files />
</el-icon>
</template>
</el-result>
<div class="grid-content"></div>
<el-divider content-position="center">
<btns @save-z-i-p="saveZIP"></btns>
</el-divider>
<div class="grid-content"></div>
<ResultsDisplay></ResultsDisplay>
</el-col>
</el-row>
</el-main>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { RouterLink, RouterView } from "vue-router";
import CodeEditorVue from "../components/CodeEditor.vue";
import ResultsDisplay from "../components/ResultsDisplay.vue"
import btns from "../layout/btns.vue"
import topmenu from "../layout/topmenu.vue";
import side from "../layout/sidecolumn.vue"
import { ziper } from "../fileziper"
import type { FileType } from "../fileziper";
import { useFileStore } from '@/stores/store';
import { Files } from '@element-plus/icons-vue';
const store = useFileStore();
const sideRef = ref();
const cmRef = ref();
const selected = computed({
get: () => store.getSelected,
set: (val) => {
selected.value = val;
},
})
const setFileContext = (code: string | undefined, type: FileType) => {
cmRef.value.setCode(code, type);
}
const onCodeChange = (value: string) => {
sideRef.value.setCode(value);
}
const saveZIP = () => {
ziper.setProjectName(sideRef.value.getProjectName());
ziper.updateProject(sideRef.value.getData());
ziper._saveZIP();
}
</script>
<style>
.grid-content {
border-radius: 4px;
min-height: 10px;
}
</style>
store.ts
import { defineStore } from "pinia";
const useLoginStore = defineStore({
id: "login",
state: () => ({
isLogin: Number(localStorage.getItem('isLogin') || '0'),
userId: localStorage.getItem('userId') || '',
userPassword: localStorage.getItem('userPassword') || '',
token: localStorage.getItem('token') || '',
}),
getters: {
getIsLogin(state) {
return (state.isLogin == 1) ? true : false;
},
getUserId(state) {
return state.userId;
},
getUserPassword(state) {
return state.userPassword;
},
getToken(state) {
return state.token;
},
},
actions: {
userLogin(id: string, pwd: string, token: string) {
this.isLogin = 1;
this.userId = id;
this.userPassword = pwd;
this.token = token;
localStorage.setItem('isLogin', '1');
localStorage.setItem('userId', id);
localStorage.setItem('userPassword', pwd);
localStorage.setItem('token', token);
},
userLogout() {
this.isLogin = 0;
this.userId = '';
this.userPassword = '';
this.token = '';
localStorage.removeItem('isLogin');
localStorage.removeItem('userId');
localStorage.removeItem('userPassword');
localStorage.removeItem('token');
},
// userRegister(id: string, pwd: string) {
// localStorage.setItem('userId', id);
// localStorage.setItem('userPassword', pwd);
// this.userId = id;
// this.userPassword = pwd;
// },
updateUserPassword(newPass: string) {
this.userPassword = newPass;
localStorage.setItem('userPassword', newPass);
},
},
});
const useFileStore = defineStore({
id: "file",
state: () => ({
selected: false,
}),
getters: {
getSelected(state) {
return state.selected;
},
},
actions: {
setSelected(s: boolean) {
this.selected = s;
}
},
});
export { useLoginStore, useFileStore };
sidecolumn.vue
<template>
<div class="custom-tree-container">
<el-menu default-active="2" class="el-menu-vertical-demo">
<el-input v-model="query" placeholder="Please enter keyword" style="width: 220px" />
<el-tree ref="treeRef" :data="dataSource" :props="props" :filter-node-method="filterMethod" :height="720"
node-key="id" default-expand-all @node-contextmenu="RightClick" @node-click="LeftClick" highlight-current>
<template #default="{ node, data }">
<el-icon>
<folder v-if="data.type == FileType.folder || data.type == FileType.root"></folder>
<document v-else></document>
</el-icon>
<span>{{ node.label }}</span>
</template>
</el-tree>
<el-card v-show="visible" id="menu" shadow="always"
:style="{ position: 'fixed', left: mouseX + 'px', top: mouseY + 'px', zIndex: '999', cursor: 'pointer' }">
<div>
<el-button type="text" @click="AddFoloder()"
v-show="targetData?.type === FileType.folder || targetData?.type === FileType.root">add new folder
</el-button>
</div>
<div>
<el-button type="text" @click="AddFile()"
v-show="targetData?.type === FileType.folder || targetData?.type === FileType.root">add new file
</el-button>
</div>
<div>
<el-button type="text" @click="Delete()" v-show="targetData?.type != FileType.root">delete</el-button>
</div>
<div>
<el-button type="text" @click="Rename()">rename</el-button>
</div>
</el-card>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import type { ElTree } from 'element-plus'
import type Node from 'element-plus/es/components/tree/src/model/node'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fileTypes, FileType } from '../fileziper';
import { Folder, Document } from '@element-plus/icons-vue';
import { useFileStore } from '@/stores/store';
const store = useFileStore();
interface Tree {
id: string
name: string
type: FileType
children: Tree[]
value?: string
}
const visible = ref(false)
const query = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()
const targetData = ref<Tree>()
const targetNode = ref<Node>()
const selectedData = ref<Tree>()
const totalId = ref(0);
const mouseX = ref(0)
const mouseY = ref(0)
const props = {
value: 'id',
label: 'name',
children: 'children',
}
const dataSource = ref<Tree[]>
([
{
id: `${totalId.value++}`, name: 'Web', type: FileType.root, children:
[
{
id: `${totalId.value++}`, name: 'src', type: FileType.folder, children:
[
{
id: `${totalId.value++}`, name: 'main.js', type: FileType.ts, children: [], value: ''
},
{
id: `${totalId.value++}`, name: 'main.html', type: FileType.html, children: [], value: ''
},
{
id: `${totalId.value++}`, name: 'main.css', type: FileType.css, children: [], value: ''
},
{
id: `${totalId.value++}`, name: 'README.md', type: FileType.md, children: [], value: ''
}
]
},
{
id: `${totalId.value++}`, name: 'public', type: FileType.folder, children:
[
{
id: `${totalId.value++}`, name: 'README.md', type: FileType.md, children: [], value: ''
}
]
}
]
}
])
watch(query, (val) => {
treeRef.value!.filter(val)
})
const filterMethod = (query: string, data: Tree) => {
if (!query) return true;
return data.name!.includes(query)
}
const RightClick = function (e: PointerEvent, data: Tree, node: Node) {
visible.value = true;
targetData.value = data;
targetNode.value = node;
mouseX.value = e.clientX;
mouseY.value = e.clientY;
document.addEventListener('click', cancelRightClick)
}
const LeftClick = function (data: Tree) {
visible.value = false;
if (data.type != FileType.folder && data.type != FileType.root) {
selectedData.value = data;
store.setSelected(true);
emit("setFileContext", selectedData.value.value, selectedData.value.type);
}
}
const cancelRightClick = function () {
visible.value = false;
document.removeEventListener('click', cancelRightClick);
}
const AddFile = () => {
ElMessageBox.prompt('Please input folder of file(name.type)', 'Add new file', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern:
/[\w!#$%&'*+/=?^_`{|}~-]+(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
inputErrorMessage: 'Please input file(name.type)',
})
.then(({ value }) => {
const val = value.split(".");
let type = val[val.length - 1];
let fileType = fileTypes(type);
if (fileType != undefined) {
targetData.value!.children.push({
id: `${totalId.value++}`,
name: value,
type: fileType,
children: [],
value: ''
})
dataSource.value = [...dataSource.value]
} else {
ElMessage({
type: 'error',
message: `not support this type`,
})
}
// ElMessage({
// type: 'success',
// message: `Your file is:${value}`,
// })
})
.catch(() => {
})
}
const AddFoloder = () => {
ElMessageBox.prompt('Please input folder', 'Add new folder', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern:
/[\w!#$%&'*+/=?^_`{|}~-]?/,
inputErrorMessage: 'Please input folder',
})
.then(({ value }) => {
targetData.value!.children.push({
id: `${totalId.value++}`,
name: value,
type: FileType.folder,
children: []
})
dataSource.value = [...dataSource.value]
// ElMessage({
// type: 'success',
// message: `Your folder is:${value}`,
// })
})
.catch(() => {
})
}
const Delete = function () {
ElMessageBox.confirm('You will delete the file,continue?', 'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
const parent = targetNode.value?.parent
const children: Tree[] = parent?.data.children || parent?.data
const index = children.findIndex((d) => d.id === targetData.value?.id)
children.splice(index, 1)
dataSource.value = [...dataSource.value]
selectedData.value = undefined;
store.setSelected(false);
})
.catch(() => {
})
}
const Rename = function () {
const isFolder = targetData.value?.type === FileType.folder || targetData.value?.type === FileType.root;
ElMessageBox.prompt('Please input new name', 'Rename', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern: isFolder
? /[\w!#$%&'*+/=?^_`{|}~-]?/ : /[\w!#$%&'*+/=?^_`{|}~-]+(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
inputErrorMessage: 'Please input correct name',
})
.then(({ value }) => {
let fileType;
if (!isFolder) {
const val = value.split(".");
let type = val[val.length - 1];
fileType = fileTypes(type);
} else
fileType = FileType.folder;
if (fileType != undefined) {
targetData.value!.name = value;
dataSource.value = [...dataSource.value]
} else {
ElMessage({
type: 'error',
message: `not support this type`,
})
}
// ElMessage({
// type: 'success',
// message: `Your folder is:${value}`,
// })
})
.catch(() => {
})
}
const emit = defineEmits(['setFileContext']);
const setCode = (value: string) => {
if (selectedData.value != null) {
selectedData.value.value = value;
}
}
const getData = () => {
return dataSource.value[0].children;
}
const getProjectName = () => {
return dataSource.value[0].name;
}
defineExpose({ setCode, getData, getProjectName })
</script>
<style>
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>
btns.vue
<template>
<div class="flex">
<el-space wrap :size="size">
<el-button size="large" type="primary" @click="Submit">提交</el-button>
<!-- <el-button type="primary">-->
<!-- Upload<el-icon class="el-icon--right"><Upload /></el-icon>-->
<!-- </el-button>-->
</el-space>
<el-button size="large" type="primary">刷新</el-button>
</div>
</template>
<script setup lang="ts">
import { Edit, Share, Delete, Search, Upload } from '@element-plus/icons-vue'
import { ref } from 'vue'
const size = ref(20)
const Submit = function () {
emit('SaveZIP')
}
const emit = defineEmits(['SaveZIP']);
</script>
<style scoped>
</style>
fileziper.ts
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
enum FileType {
root,
folder,
ts,
js,
html,
css,
md,
txt,
}
const fileTypes = function (type: string): FileType | undefined {
switch (type) {
case "folder":
return FileType.folder;
case "ts":
return FileType.ts;
case "js":
return FileType.js;
case "html":
return FileType.html;
case "css":
return FileType.css;
case "md":
return FileType.md;
case "txt":
return FileType.txt;
default:
return undefined
}
}
class Ziper {
projName: string = 'web';
zip: any;
constructor() {
this.zip = new JSZip();
}
setProjectName(name: string) {
this.projName = name;
}
updateProject(data: any, parent = this.zip) {
for (var item in data) {
if (data[item].type == FileType.folder) {
let pa = this._addFolder(parent, data[item].name)
this.updateProject(data[item].children, pa);
} else {
this._addFile(parent, data[item].name, data[item].value);
}
}
}
_addFile(parent: any, name: string, value: string) {
parent.file(name, value);
}
_addFolder(parent: any, name: string) {
return parent.folder(name);
}
_saveZIP() {
let projName = this.projName;
this.zip.generateAsync({ type: "blob" })
.then(function (blob: any) {
saveAs(blob, projName);
});
}
}
const ziper = new Ziper();
export { ziper, FileType, fileTypes };
修改文件命名相同问题
const AddFile = () => {
ElMessageBox.prompt('Please input folder of file(name.type)', 'Add new file', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern:
/[\w!#$%&'*+/=?^_`{|}~-]+(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
inputErrorMessage: 'Please input file(name.type)',
})
.then(({ value }) => {
const val = value.split(".");
let type = val[val.length - 1];
let fileType = fileTypes(type);
if (fileType != undefined) {
if (targetData.value != null) {
for (let i = 0; i < targetData.value.children.length; i++) {
if (fileType == targetData.value.children[i].type&&value == targetData.value.children[i].name)
throw false;
}
}
targetData.value!.children.push({
id: `${totalId.value++}`,
name: value,
type: fileType,
children: [],
value: ''
})
dataSource.value = [...dataSource.value]
} else {
ElMessage({
type: 'error',
message: `not support this type`,
})
}
// ElMessage({
// type: 'success',
// message: `Your file is:${value}`,
// })
})
.catch(() => {
ElMessage({
type: 'error',
message: "file already exist",
})
})
}
const AddFoloder = () => {
ElMessageBox.prompt('Please input folder', 'Add new folder', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern:
/[\w!#$%&'*+/=?^_`{|}~-]?/,
inputErrorMessage: 'Please input folder',
})
.then(({ value }) => {
if (targetData.value != null) {
for (let i = 0; i < targetData.value.children.length; i++) {
if (targetData.value.children[i].type == FileType.folder && value == targetData.value.children[i].name)
throw false;
}
}
targetData.value!.children.push({
id: `${totalId.value++}`,
name: value,
type: FileType.folder,
children: []
})
dataSource.value = [...dataSource.value]
// ElMessage({
// type: 'success',
// message: `Your folder is:${value}`,
// })
})
.catch(() => {
ElMessage({
type: 'error',
message: "folder already exist",
})
})
}
const Rename = function () {
const isFolder = targetData.value?.type === FileType.folder || targetData.value?.type === FileType.root;
ElMessageBox.prompt('Please input new name', 'Rename', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputPattern: isFolder
? /[\w!#$%&'*+/=?^_`{|}~-]?/ : /[\w!#$%&'*+/=?^_`{|}~-]+(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
inputErrorMessage: 'Please input correct name',
})
.then(({ value }) => {
let fileType;
const pa = targetNode.value?.parent.data.children;
if (!isFolder) {
const val = value.split(".");
let type = val[val.length - 1];
fileType = fileTypes(type);
for (let i = 0; i < pa.length; i++) {
if (value == pa[i].name && fileType == pa[i].type)
throw false;
}
} else {
for (let i = 0; i < pa.length; i++) {
if (pa[i].type == FileType.folder && value == pa[i].name)
throw false;
}
fileType = FileType.folder;
}
if (fileType != undefined) {
targetData.value!.name = value;
dataSource.value = [...dataSource.value]
} else {
ElMessage({
type: 'error',
message: `not support this type`,
})
}
// ElMessage({
// type: 'success',
// message: `Your folder is:${value}`,
// })
})
.catch(() => {
})
}