文本文件添加预览编辑等

This commit is contained in:
junleea 2025-05-21 14:25:26 +08:00
parent 7b09c0d95b
commit 48289fafc6
9 changed files with 559 additions and 11 deletions

145
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.3", "axios": "^1.6.3",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"codemirror": "^6.0.1",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"cropperjs": "^2.0.0", "cropperjs": "^2.0.0",
"d3": "^7.9.0", "d3": "^7.9.0",
@ -37,6 +38,7 @@
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vditor": "^3.11.0", "vditor": "^3.11.0",
"vue": "^3.4.5", "vue": "^3.4.5",
"vue-codemirror": "^6.1.1",
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",
@ -250,6 +252,79 @@
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
"integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="
}, },
"node_modules/@codemirror/autocomplete": {
"version": "6.18.6",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/commands": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.27.0",
"@lezer/common": "^1.1.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
"integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/lint": {
"version": "6.8.5",
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.35.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/search": {
"version": "6.5.11",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.36.8",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz",
"integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==",
"dependencies": {
"@codemirror/state": "^6.5.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@cropper/element": { "node_modules/@cropper/element": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz",
@ -556,6 +631,32 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@lezer/common": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
},
"node_modules/@lezer/highlight": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
},
"node_modules/@mermaid-js/parser": { "node_modules/@mermaid-js/parser": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz",
@ -1598,6 +1699,20 @@
"tiny-emitter": "^2.0.0" "tiny-emitter": "^2.0.0"
} }
}, },
"node_modules/codemirror": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/commands": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/lint": "^6.0.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
}
},
"node_modules/codepage": { "node_modules/codepage": {
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
@ -1684,6 +1799,11 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
},
"node_modules/cropperjs": { "node_modules/cropperjs": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz", "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz",
@ -4943,6 +5063,11 @@
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
},
"node_modules/stylis": { "node_modules/stylis": {
"version": "4.3.6", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
@ -5373,6 +5498,21 @@
} }
} }
}, },
"node_modules/vue-codemirror": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-6.1.1.tgz",
"integrity": "sha512-rTAYo44owd282yVxKtJtnOi7ERAcXTeviwoPXjIc6K/IQYUsoDkzPvw/JDFtSP6T7Cz/2g3EHaEyeyaQCKoDMg==",
"dependencies": {
"@codemirror/commands": "6.x",
"@codemirror/language": "6.x",
"@codemirror/state": "6.x",
"@codemirror/view": "6.x"
},
"peerDependencies": {
"codemirror": "6.x",
"vue": "3.x"
}
},
"node_modules/vue-cropper": { "node_modules/vue-cropper": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz", "resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz",
@ -5480,6 +5620,11 @@
"typescript": "*" "typescript": "*"
} }
}, },
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
},
"node_modules/web-worker": { "node_modules/web-worker": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz",

View File

@ -14,6 +14,7 @@
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.3", "axios": "^1.6.3",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"codemirror": "^6.0.1",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"cropperjs": "^2.0.0", "cropperjs": "^2.0.0",
"d3": "^7.9.0", "d3": "^7.9.0",
@ -37,6 +38,7 @@
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vditor": "^3.11.0", "vditor": "^3.11.0",
"vue": "^3.4.5", "vue": "^3.4.5",
"vue-codemirror": "^6.1.1",
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
"vue-echarts": "^6.6.9", "vue-echarts": "^6.6.9",

View File

@ -47,3 +47,27 @@ export const DelUserFileService = (Data) => {
} }
}) })
} }
export const FindFileContentService = (Data) => {
const params = new URLSearchParams();
for (let key in Data) {
params.append(key, Data[key])
}
return request.post('/file/find_file_content', params,{
headers: {
'token': Data.token, //token
}
})
}
export const UpdateFileContentService = (Data) => {
const params = new URLSearchParams();
for (let key in Data) {
params.append(key, Data[key])
}
return request.post('/file/update_file_content', params,{
headers: {
'token': Data.token, //token
}
})
}

View File

@ -59,9 +59,13 @@
<el-button type="danger" size="small" :icon="Link" @click="handlePush(row)" v-if="item.operate.push.link"> <el-button type="danger" size="small" :icon="Link" @click="handlePush(row)" v-if="item.operate.push.link">
{{item.operate.push.label}} {{item.operate.push.label}}
</el-button> </el-button>
<el-button type="danger" size="small" :icon="Link" @click="genOperate(row)" v-if="item.operate.gen.show"> <el-button type="danger" size="small" :icon="Link" @click="genOperate(row,item.operate)" v-if="item.operate.gen.show">
{{item.operate.gen.label}} {{item.operate.gen.label}}
</el-button> </el-button>
<el-button type="danger" size="small" :icon="Link" @click="genOperateV2(row,item.operate.genv2)" v-if="item.operate.genv2?.show">
{{item.operate.genv2.label}}
</el-button>
</template> </template>
<span v-else-if="item.formatter"> <span v-else-if="item.formatter">
{{ item.formatter(row[item.prop]) }} {{ item.formatter(row[item.prop]) }}
@ -155,6 +159,10 @@ const props = defineProps({
type: Function, type: Function,
default: () => { } default: () => { }
}, },
genOperateV2: {
type: Function,
default: () => { }
},
}) })
let { let {

View File

@ -342,8 +342,6 @@ import mermaidPlugin from "@agoose77/markdown-it-mermaid";
import "katex/dist/katex.min.css"; import "katex/dist/katex.min.css";
import Vditor from 'vditor'; import Vditor from 'vditor';
import 'vditor/dist/index.css'; import 'vditor/dist/index.css';
import { json } from "d3";
import { pa } from "element-plus/es/locale";
interface Message { interface Message {

View File

@ -3,7 +3,7 @@
<TableSearch :query="query" :options="searchOpt" :search="handleSearch" :refresh="getData" /> <TableSearch :query="query" :options="searchOpt" :search="handleSearch" :refresh="getData" />
<div class="container"> <div class="container">
<TableCustom :columns="columns" :tableData="tableData" :total="page.total" :viewFunc="handleView" <TableCustom :columns="columns" :tableData="tableData" :total="page.total" :viewFunc="handleView"
:delFunc="handleDelete" :changePage="changePage" :currentPage="page.index" :editFunc="handleEdit" :genOperate="handleGenOperate" :refresh="getData"> :delFunc="handleDelete" :changePage="changePage" :currentPage="page.index" :editFunc="handleEdit" :genOperate="handleGenOperate" :refresh="getData" :genOperateV2="handleGenOperateV2">
<template #toolbarBtn> <template #toolbarBtn>
<el-button type="warning" :icon="CirclePlusFilled" @click="visible_add = true">上传文件</el-button> <el-button type="warning" :icon="CirclePlusFilled" @click="visible_add = true">上传文件</el-button>
</template> </template>
@ -21,6 +21,17 @@
<el-dialog title="查看详情" v-model="visible1" width="700px" destroy-on-close> <el-dialog title="查看详情" v-model="visible1" width="700px" destroy-on-close>
<TableDetail :data="viewData"></TableDetail> <TableDetail :data="viewData"></TableDetail>
</el-dialog> </el-dialog>
<div>
<transition name="fade">
<div
v-if="code_edit"
class="mask"
@click.self="code_edit = false"
></div>
</transition>
<slideCodeEdit v-model:content="code_edit_content" :fileID="edit_file_id" :show="code_edit" :fileName="edit_file_name" @close="closeFileEdit" @update="updateFileContent($event)"/>
</div>
</div> </div>
<el-dialog <el-dialog
@ -41,14 +52,14 @@ import { CirclePlusFilled } from '@element-plus/icons-vue';
import { Session } from '@/types/session'; import { Session } from '@/types/session';
import {FindUserFileService} from "@/api/file"; import {FindUserFileService} from "@/api/file";
import {UpdateUserFileService} from "@/api/file"; import {UpdateUserFileService} from "@/api/file";
import {DelUserFileService} from "@/api/file"; import {DelUserFileService, FindFileContentService} from "@/api/file";
import { File,fileUrl,fileUrl2 } from '@/types/file'; import { File,fileUrl,fileUrl2 } from '@/types/file';
import TableCustom from '@/components/table-custom.vue'; import TableCustom from '@/components/table-custom.vue';
import TableDetail from '@/components/table-detail.vue'; import TableDetail from '@/components/table-detail.vue';
import TableSearch from '@/components/table-search.vue'; import TableSearch from '@/components/table-search.vue';
import UploadFile from '@/components/upload-file.vue'; import UploadFile from '@/components/upload-file.vue';
import { FormOption, FormOptionList } from '@/types/form-option'; import { FormOption, FormOptionList } from '@/types/form-option';
import { pa } from 'element-plus/es/locale'; import slideCodeEdit from './slide-code-edit.vue';
const page = reactive({ const page = reactive({
@ -60,6 +71,10 @@ const tableData = ref<File[]>([]);
const allData = ref<File[]>([]); const allData = ref<File[]>([]);
const downloadFileVisible = ref(false); const downloadFileVisible = ref(false);
const downloadFileUrl = ref(''); const downloadFileUrl = ref('');
const code_edit = ref(false);
const code_edit_content = ref('');
const edit_file_name = ref('');
const edit_file_id = ref(0);
// //
const query = reactive({ const query = reactive({
@ -106,8 +121,7 @@ let columns = ref([
{ prop: "UploadType", label: "上传类型",width: 100}, { prop: "UploadType", label: "上传类型",width: 100},
{ prop: 'CreatedAt', label: '创建时间',type: 'date',width: 220 }, { prop: 'CreatedAt', label: '创建时间',type: 'date',width: 220 },
{ prop: 'UpdatedAt', label: '更新时间',type: 'date',width: 220 }, { prop: 'UpdatedAt', label: '更新时间',type: 'date',width: 220 },
{ prop: 'operator', label: '操作' , operate: { view: false, edit: true, delete: true,push: {link: false,label:"继续该会话"} ,gen: {show: true,label:"下载文件"}}, { prop: 'operator', label: '操作' , operate: { view: false, edit: true, delete: true,push: {link: false,label:"继续该会话"} ,gen: {show: true,label:"下载文件"}, genv2:{type:"file_edit", label:"编辑文件内容", show:true}},align: 'center', fixed: 'right' },
align: 'center', fixed: 'right' },
]) ])
const getData = async () => { const getData = async () => {
@ -252,6 +266,34 @@ const handleGenOperate = async (row: File) => {
downloadFile(row); downloadFile(row);
} }
const editFileType= {'txt':true,'json':true,'md':true,'py':true,'java':true,'cpp':true,'go':true,'js':true,'html':true,'css':true}
const handleGenOperateV2 = async (row: File, content: any) => {
if (content.type == 'download'){
console.log("gen row:", row);
//
downloadFile(row);
}else if (content.type == 'file_edit'){
//
let fileName = row.UserFileName;
//.
let fileType = fileName.split(".")[1];
console.log("fileType:", fileType);
if(editFileType[fileType] == undefined){
ElMessage.error("不支持的文件类型:"+fileType+",该文件类型不支持在线编辑,请下载后编辑");
return;
}
//
await FindFileContent(row);
code_edit.value = true;
}else{
ElMessage.error("操作类型错误");
}
}
const downloadFile = async (row: File) => { const downloadFile = async (row: File) => {
const fileUrl_ = fileUrl2+row.file_store_name +"?token=" + localStorage.getItem('token'); const fileUrl_ = fileUrl2+row.file_store_name +"?token=" + localStorage.getItem('token');
//<a> //<a>
@ -291,6 +333,56 @@ const handleDelete = async (row: File) => {
console.log(e); console.log(e);
} }
} }
const FindFileContent = async (row: File) => {
let req={
token: localStorage.getItem('token'),
user_file_id: row.ID,
};
edit_file_name.value = row.UserFileName;
edit_file_id.value = row.ID;
try{
let result = await FindFileContentService(req);
if(result["code"] === 0){
//console.log("file content:", result["data"][0]);
code_edit_content.value = result["data"][0]["FileContent"];
console.log("code_edit_content:", code_edit_content.value);
}else{
ElMessage.error("删除失败");
}
}catch(e){
console.log(e);
}
}
const closeFileEdit = () => {
code_edit.value = false;
code_edit_content.value = '';
}
const updateFileContent = async (value) => {
console.log("file content:", value);
}
</script> </script>
<style scoped></style> <style scoped>
.mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -38,7 +38,6 @@ import TableCustom from '@/components/table-custom.vue';
import TableDetail from '@/components/table-detail.vue'; import TableDetail from '@/components/table-detail.vue';
import TableSearch from '@/components/table-search.vue'; import TableSearch from '@/components/table-search.vue';
import { FormOption, FormOptionList } from '@/types/form-option'; import { FormOption, FormOptionList } from '@/types/form-option';
import { pa } from 'element-plus/es/locale';
// //

View File

@ -0,0 +1,152 @@
<template>
<transition name="slide">
<div v-if="show" class="side-panel">
<button @click="handleClose" class="close-btn"></button>
<el-button type="primary" @click="UpdateFileCOntent">保存修改</el-button>
<div class="content">
<h2>{{ fileName }}</h2>
<textarea
ref="textareaRef"
v-model="localContent"
cols="40"
rows="10"
spellcheck="false"
></textarea>
</div>
</div>
</transition>
</template>
<script setup lang="ts">
import { ref, watch, nextTick } from "vue";
import { UpdateFileContentService } from "@/api/file";
import { ElMessage } from "element-plus";
const props = defineProps({
content: {
type: String,
default: "",
},
show: {
type: Boolean,
default: false,
},
fileName: {
type: String,
default: "编辑代码",
},
fileID: {
type: Number,
default: 0,
},
});
const emit = defineEmits<{
(e: "close"): void;
}>();
const textareaRef = ref<HTMLTextAreaElement | null>(null);
const localContent = ref(props.content);
// props.content
watch(
() => props.content,
(newVal) => {
localContent.value = newVal;
}
);
// textarea
watch(
() => props.show,
async (newVal) => {
if (newVal) {
await nextTick();
textareaRef.value?.focus();
}
}
);
const handleClose = () => {
emit("close");
};
const UpdateFileCOntent = async () => {
let req = {
token: localStorage.getItem("token"),
file_id: props.fileID,
file_content: localContent.value,
};
try {
let result = await UpdateFileContentService(req);
if (result["code"] === 0) {
ElMessage.success("修改成功");
} else {
ElMessage.error("修改失败");
}
} catch (e) {
console.log(e);
}
};
</script>
<style scoped lang="css">
.side-panel {
position: fixed;
top: 0;
right: 0;
width: 50%;
height: 100%;
background: white;
z-index: 1001;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
padding: 20px;
display: flex;
flex-direction: column;
}
.close-btn {
position: absolute;
top: 10px;
right: 10px;
font-size: 24px;
background: none;
border: none;
cursor: pointer;
transition: color 0.2s;
z-index: 1;
}
.close-btn:hover {
color: #666;
}
.content {
flex: 1;
display: flex;
flex-direction: column;
margin-top: 20px;
}
textarea {
flex: 1;
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
font-family: monospace;
font-size: 14px;
line-height: 1.5;
tab-size: 2;
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from,
.slide-leave-to {
transform: translateX(100%);
}
</style>

130
yarn.lock
View File

@ -87,6 +87,72 @@
resolved "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz" resolved "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz"
integrity sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== integrity sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==
"@codemirror/autocomplete@^6.0.0":
version "6.18.6"
resolved "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz"
integrity sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.17.0"
"@lezer/common" "^1.0.0"
"@codemirror/commands@^6.0.0", "@codemirror/commands@6.x":
version "6.8.1"
resolved "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz"
integrity sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.4.0"
"@codemirror/view" "^6.27.0"
"@lezer/common" "^1.1.0"
"@codemirror/language@^6.0.0", "@codemirror/language@6.x":
version "6.11.0"
resolved "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz"
integrity sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.23.0"
"@lezer/common" "^1.1.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"
"@codemirror/lint@^6.0.0":
version "6.8.5"
resolved "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz"
integrity sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.35.0"
crelt "^1.0.5"
"@codemirror/search@^6.0.0":
version "6.5.11"
resolved "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz"
integrity sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/state@^6.0.0", "@codemirror/state@^6.4.0", "@codemirror/state@^6.5.0", "@codemirror/state@6.x":
version "6.5.2"
resolved "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz"
integrity sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==
dependencies:
"@marijn/find-cluster-break" "^1.0.0"
"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.35.0", "@codemirror/view@6.x":
version "6.36.8"
resolved "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz"
integrity sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==
dependencies:
"@codemirror/state" "^6.5.0"
style-mod "^4.1.0"
w3c-keyname "^2.2.4"
"@cropper/element-canvas@^2.0.0": "@cropper/element-canvas@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz" resolved "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz"
@ -287,6 +353,30 @@
"@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@lezer/common@^1.0.0", "@lezer/common@^1.1.0":
version "1.2.3"
resolved "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz"
integrity sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==
"@lezer/highlight@^1.0.0":
version "1.2.1"
resolved "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz"
integrity sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/lr@^1.0.0":
version "1.4.2"
resolved "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz"
integrity sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==
dependencies:
"@lezer/common" "^1.0.0"
"@marijn/find-cluster-break@^1.0.0":
version "1.0.2"
resolved "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz"
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
"@mermaid-js/parser@^0.4.0": "@mermaid-js/parser@^0.4.0":
version "0.4.0" version "0.4.0"
resolved "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz" resolved "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz"
@ -1049,6 +1139,19 @@ clipboard@^2.0.11:
select "^1.1.2" select "^1.1.2"
tiny-emitter "^2.0.0" tiny-emitter "^2.0.0"
codemirror@^6.0.1, codemirror@6.x:
version "6.0.1"
resolved "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz"
integrity sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
codepage@~1.15.0: codepage@~1.15.0:
version "1.15.0" version "1.15.0"
resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz" resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz"
@ -1145,6 +1248,11 @@ crc-32@~1.2.0, crc-32@~1.2.1:
resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz"
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
crelt@^1.0.5:
version "1.0.6"
resolved "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz"
integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==
cropperjs@^1.5.6: cropperjs@^1.5.6:
version "1.6.2" version "1.6.2"
resolved "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz" resolved "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz"
@ -3122,6 +3230,11 @@ strip-literal@^1.0.0:
dependencies: dependencies:
acorn "^8.10.0" acorn "^8.10.0"
style-mod@^4.0.0, style-mod@^4.1.0:
version "4.1.2"
resolved "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz"
integrity sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==
stylis@^4.1.2, stylis@^4.1.3, stylis@^4.3.6: stylis@^4.1.2, stylis@^4.1.3, stylis@^4.3.6:
version "4.3.6" version "4.3.6"
resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz"
@ -3348,6 +3461,16 @@ vscode-uri@~3.0.8:
resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz" resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz"
integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==
vue-codemirror@^6.1.1:
version "6.1.1"
resolved "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-6.1.1.tgz"
integrity sha512-rTAYo44owd282yVxKtJtnOi7ERAcXTeviwoPXjIc6K/IQYUsoDkzPvw/JDFtSP6T7Cz/2g3EHaEyeyaQCKoDMg==
dependencies:
"@codemirror/commands" "6.x"
"@codemirror/language" "6.x"
"@codemirror/state" "6.x"
"@codemirror/view" "6.x"
vue-cropper@1.1.1: vue-cropper@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz" resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.1.1.tgz"
@ -3404,7 +3527,7 @@ vue-tsc@^0.38.4:
dependencies: dependencies:
"@volar/vue-typescript" "0.38.9" "@volar/vue-typescript" "0.38.9"
vue@*, "vue@^2.6.12 || ^3.1.1", "vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0", vue@^3.0.5, vue@^3.2.0, vue@^3.2.25, vue@^3.2.47, vue@^3.3.4, vue@^3.4.5, vue@>=3.0.0, "vue@2 || 3", vue@3.4.21: vue@*, "vue@^2.6.12 || ^3.1.1", "vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0", vue@^3.0.5, vue@^3.2.0, vue@^3.2.25, vue@^3.2.47, vue@^3.3.4, vue@^3.4.5, vue@>=3.0.0, "vue@2 || 3", vue@3.4.21, vue@3.x:
version "3.4.21" version "3.4.21"
resolved "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz" resolved "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz"
integrity sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA== integrity sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==
@ -3415,6 +3538,11 @@ vue@*, "vue@^2.6.12 || ^3.1.1", "vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0
"@vue/server-renderer" "3.4.21" "@vue/server-renderer" "3.4.21"
"@vue/shared" "3.4.21" "@vue/shared" "3.4.21"
w3c-keyname@^2.2.4:
version "2.2.8"
resolved "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz"
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
web-worker@^1.2.0: web-worker@^1.2.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz" resolved "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz"