项目仓库
本项目是为开发一套容器化的开发、运行、测试环境,用以支持Web开发、程序设计等课程的实验教学。
代码内容均为我和肖同学共同完成。
设计并且编写主界面
主界面内容:
view/coding-view.vue
import { FileType } from "../fileziper";
<!--login-coding-project-page-->
<template>
<el-header>
<topmenu :activeIndex=activeIndex @on-index-change="onIndexChange"></topmenu>
</el-header>
<el-main v-show="activeIndex == '5'">
<el-header>
<el-row :gutter="20">
<el-col :span="3">
<el-input v-model="invitationCode" placeholder="组织邀请码" :style="{width:'200px'} "/>
</el-col>
<el-col :span="6">
<el-button text @click="addOrganization()">添加组织</el-button>
</el-col>
<el-col :span="6" />
<el-col :span="6" />
</el-row>
</el-header>
<el-main>
<el-table :data="organizationData" border style="width: 100%">
<el-table-column prop="lesson" label="课程" width="510px" />
<el-table-column prop="class" label="班级" width="510px" />
<el-table-column width="110px">
<template #default="scope">
<el-button text size="small" @click="deleteOrganization(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</el-main>
<el-main v-show="activeIndex == '4'">
<el-row class="tac">
<el-col :span="5">
<side ref="sideRef" @set-file-context="setFileContext" @detele-file="removeTab" @rename="rename" />
</el-col>
<el-col :span="1"></el-col>
<el-col :span="18">
<div v-show="editableTabsValue != '-1'">
<el-tabs v-model="editableTabsValue" type="card" class="demo-tabs" closable @tab-remove="removeTab"
@tab-click="clickTab">
<el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name">
</el-tab-pane>
</el-tabs>
<code-editor-vue ref="cmRef" @on-change="onCodeChange"></code-editor-vue>
</div>
<div v-show="editableTabsValue == '-1'">
<el-result title="点击打开文件">
<template #icon>
<el-icon :size="280">
<files />
</el-icon>
</template>
</el-result>
</div>
<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>
<el-main v-show="activeIndex == '3'">
<div class="login-wrap">
<el-form class="login-container">
<el-upload class="upload-demo" drag action="#" multiple accept=".zip" :http-request="handleUpload">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
拖拽文件或
<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">只支持zip压缩包</div>
</template>
</el-upload>
</el-form>
</div>
</el-main>
<el-main v-show="activeIndex == '2'">
<el-tabs v-model="activeProjectTabName" class="demo-tabs" @tab-click="handleProjectTabClick">
<el-tab-pane label="课程任务" name="1">
<el-main>
<el-dialog v-model="choseProjectFormVisible" title="选择项目" width="30%">
<el-form>
<el-form-item label="项目名称" label-width="100px">
<el-cascader
v-model="choseProject"
placeholder="项目搜索"
:options="projectData"
filterable
:props="choseProjectProps"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="choseProjectFormVisible = false">取消</el-button>
<el-button type="primary" @click="submitProject">确认</el-button>
</span>
</template>
</el-dialog>
<el-table :data="homeworkTableData" border style="width: 100%">
<el-table-column prop="lesson" label="课程" width="180" />
<el-table-column prop="date" label="截止日期" sortable width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-icon>
<timer />
</el-icon>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="mission" label="任务简介" />
<el-table-column prop="finish" label="任务进度" width="100"/>
<el-table-column fixed="right" width="200">
<template #default="scope">
<el-button text size="small" @click="checkDetail(scope.row.missionDetail)">查看详情</el-button>
<el-button text size="small" @click="choseProjectFormVisible=true">提交作业</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</el-tab-pane>
<el-tab-pane label="课程通知" name="2">
<el-table :data="noticeTableData" border style="width: 100%">
<el-table-column prop="className" label="课程" width="180" />
<el-table-column prop="date" label="发布日期" sortable width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-icon>
<timer />
</el-icon>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="detail" label="通知内容" />
<el-table-column fixed="right" width="180">
<template #default="scope">
<el-button text size="small" @click="checkDetail(scope.row.detail)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="我的项目" name="3">
<div v-show="!showDetail">
<el-header>
<el-button @click="addProject()">
添加<el-icon class="el-icon--right">
<Plus />
</el-icon>
</el-button>
<el-button @click="deleteProject()">
删除<el-icon class="el-icon--right">
<Delete />
</el-icon>
</el-button>
<el-button @click="downloadProject()">
下载<el-icon class="el-icon--right">
<Bottom />
</el-icon>
</el-button>
</el-header>
<el-main>
<el-dialog v-model="createProjectFormVisible" title="添加项目" width="30%">
<el-form :model="createProjectForm">
<el-form-item label="项目名称" label-width="100px">
<el-input v-model="createProjectForm.name" autocomplete="off" />
</el-form-item>
<el-form-item label="项目简介" label-width="100px">
<el-input type="textarea" v-model="createProjectForm.description" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="createProjectFormVisible = false">取消</el-button>
<el-button type="primary" @click="confirmAddProject">确认</el-button>
</span>
</template>
</el-dialog>
<el-table ref="projectTableRef" :data="projectData" border style="width: 100%"
@selection-change="handleProjectSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="projectName" label="项目名字" width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-icon>
<folder />
</el-icon>
<span style="margin-left: 10px">{{ scope.row.projectName }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="date" label="上次更新" width="180" />
<el-table-column prop="introduction" label="项目简介" />
<el-table-column fixed="right" width="220">
<template #default="scope">
<el-button text size="small" @click="showProjectDetail(scope.row)">项目详情</el-button>
<el-button text size="small" @click="editProject(scope.raw)">编辑项目</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</div>
<div v-show="showDetail">
<el-main>
<el-page-header content="项目详情" @back="closeProjectDetail" />
<el-container>
<el-main>
<el-table :data="selectedProjectDetail?.files" style="width: 100%">
<el-table-column prop="name" label="文件夹名字" width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-icon>
<folder />
</el-icon>
<el-button type="text" style="margin-left: 10px">{{ scope.row.name }}</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="date" label="上次更新" width="180" />
<el-table-column prop="introduction" label="简介" />
<el-table-column fixed="right" width="120">
<template #default="scope">
<el-button text size="small" @click="deleteProjecFolder(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-main>
<el-divider />
<h6>项目输出日志</h6>
<div class="demo-radius">
<el-input type="textarea" class="example-demonstration">{{selectedProjectDetail?.outputlog}}</el-input>
</div>
</el-main>
</el-main>
<el-aside width="300px">
<el-row>
<el-col :span="2">
<el-divider direction="vertical" style="height: 800px" />
</el-col>
<el-col :span="22">
<el-form label-width="120px">
<el-form-item size="large" label="项目介绍">
</el-form-item>
<el-input type="textarea" class="example-demonstration">{{selectedProjectDetail?.introduction}}</el-input>
</el-form>
</el-col>
</el-row>
</el-aside>
</el-container>
</el-main>
</div>
</el-tab-pane>
</el-tabs>
</el-main>
</template>
<script lang="ts" setup>
import { ref, watch, reactive } from "vue";
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 { useLoginStore } from '@/stores/store';
import { Files, UploadFilled } from '@element-plus/icons-vue';
import { request } from "@/network/request";
import { ElMessage, ElMessageBox } from "element-plus";
import type { TabsPaneContext, ElTable } from 'element-plus';
import { Folder, Timer, Delete, Bottom, Plus } from '@element-plus/icons-vue';
import type { ElTree } from 'element-plus'
import Node from "element-plus/es/components/tree/src/model/node";
import { h } from 'vue';
const activeIndex = ref('2');
const lstore = useLoginStore();
const onIndexChange = (idx: string) => {
activeIndex.value = idx;
}
const getFormDate = () => {
const Dates = new Date();
//年份
const Year: number = Dates.getFullYear();
//月份下标是0-11
const Months: any = (Dates.getMonth() + 1) < 10 ? '0' + (Dates.getMonth() + 1) : (Dates.getMonth() + 1);
//具体的天数
const Day: any = Dates.getDate() < 10 ? '0' + Dates.getDate() : Dates.getDate();
return Year + '-' + Months + '-' + Day;
}
//上传中心
const upload = (param: FormData) => {
request('/weblab/submit/submitByZip', param, lstore.getToken)
.then(res => {
if (res.status == 200 && res.data.msg == 'success') {
console.log(res);
ElMessage({
showClose: true,
message: '上传成功',
type: 'success',
center: true,
grouping: true
})
}
})
.catch(error => {
console.log(error);
})
}
const handleUpload = (file: any) => {
let param = new FormData();
param.append('file', file.file);
upload(param);
}
//编辑中心
const sideRef = ref();
const cmRef = ref();
const editableTabsValue = ref('-1');
interface tab {
title: string,
name: string
}
const editableTabs = ref<tab[]>([]);
const addTab = (name: string, id: string) => {
editableTabs.value.push({
title: name,
name: id,
})
editableTabsValue.value = id;
}
const removeTab = (id: string) => {
const tabs = editableTabs.value
let activeName = editableTabsValue.value
if (activeName === id) {
tabs.forEach((tab, index) => {
if (tab.name === id) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
const code = sideRef.value.getCode(activeName);
cmRef.value.setCode(code.code, code.type);
}
}
})
}
editableTabsValue.value = activeName
editableTabs.value = tabs.filter((tab) => tab.name !== id)
if (editableTabs.value.length == 0)
editableTabsValue.value = '-1';
}
const clickTab = (pane: TabsPaneContext, ev: Event) => {
const code = sideRef.value.getCode(pane.props.name);
cmRef.value.setCode(code.code, code.type);
}
const rename = (id: string, new_name: string) => {
const tabs = editableTabs.value
tabs.forEach((tab, index) => {
if (tab.name === id) {
tab.title = new_name;
}
})
}
const setFileContext = (name: string, id: string, code: string | undefined, type: FileType) => {
cmRef.value.setCode(code, type);
const tabs = editableTabs.value
let flag = false;
tabs.forEach((tab, index) => {
if (tab.name == id) {
editableTabsValue.value = id;
flag = true;
}
})
if (!flag) {
addTab(name, id);
}
}
const onCodeChange = (value: string) => {
sideRef.value.setCode(editableTabsValue.value, value);
}
const saveZIP = async () => {
ziper.setProjectName(sideRef.value.getProjectName());
ziper.updateProject(sideRef.value.getData());
const param: any = await ziper.uploadFile();
if (param != undefined)
upload(param);
}
//组织中心
const invitationCode = ref('');
const organizationData = ref([
{
lesson: '项目',
class: '软一',
},
{
lesson: '实训',
class: '软一',
},
])
const addOrganization = () => {
ElMessageBox.alert('是否加入班级XXX', '加入班级', {
confirmButtonText: 'OK',
// callback: (action: Action) => {
// ElMessage({
// type: 'info',
// message: `action: ${action}`,
// })
// },
})
}
const deleteOrganization = (val: any) => {
ElMessageBox.confirm('将删除该课程,继续?', 'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
const targetIdx = organizationData.value.findIndex((d) => d == val);
organizationData.value.splice(targetIdx, 1);
})
}
//项目中心
//课程任务
const choseProjectFormVisible = ref(false);
const choseProjectProps={
label:'projectName',
value:'projectName'
}
const choseProject=ref('');
const homeworkTableData = ref<hwDataIF[]>([
{
lesson: '项目实训',
mission: '任务简介',
date: '2022-05-15',
missionDetail: '用vue3+springboot完成一个简单的前后端分离项目',
finish:'已完成'
},
{
lesson: '项目实训',
mission: '任务简介',
date: '2022-06-15',
missionDetail: '用vue3+springboot完成一个简单的前后端分离项目',
finish:'未完成'
},
])
//查看作业详情
const checkDetail = (detail: string) => {
ElMessageBox.alert(detail, '详情', {
confirmButtonText: 'OK',
})
}
//提交项目
const submitProject=()=>{
choseProjectFormVisible.value = false
console.log(choseProject.value)
}
//课程通知
interface noticetDataIF {
className: string,
date: string,
detail: string
}
const noticeTableData=ref<noticetDataIF[]>([
{
className:'项目实训',
date:'2022-5-24',
detail:'请同学们与6-30前提交作业'
}
]);
//我的项目
const showDetail = ref(false);
const activeProjectTabName = ref('1');
const createProjectFormVisible = ref(false);
const createProjectForm = reactive({
name: '',
description: ''
})
const projectTableRef = ref<InstanceType<typeof ElTable>>()
const selectedProject = ref<projectDataIF[]>();
const selectedProjectDetail = ref<projectDetailIF>();
//任务数据
interface hwDataIF {
lesson: string,
mission: string,
date: string,
missionDetail: string,
finish:string
}
//项目数据
//项目详情内容
interface projectFilesIF {
name: string,
date: string,
introduction: string
}
interface projectDetailIF {
outputlog: string,
introduction: string,
files: projectFilesIF[]
}
interface projectDataIF {
projectName: string,
introduction: string,
date: string,
detail: projectDetailIF
}
const projectData = ref<projectDataIF[]>([
{
projectName: '项目实训',
introduction: '项目实训简介',
date: '2022-05-15',
detail: {
outputlog: '输出日志',
introduction: '项目介绍',
files: [
{
name: 'src',
introduction: 'src',
date: '2022-05-15',
},
{
name: 'views',
introduction: 'views',
date: '2022-05-15',
},
]
}
},
{
projectName: 'web',
introduction: 'web简介',
date: '2022-05-15',
detail: {
outputlog: '输出日志',
introduction: '项目介绍',
files: [
{
name: 'src',
introduction: 'src',
date: '2022-05-15',
},
{
name: 'views',
introduction: 'views',
date: '2022-05-15',
},
]
}
},
])
//编辑项目
const editProject = (data: projectDataIF) => {
activeIndex.value = '4'
}
//查看项目详情
const showProjectDetail = (data: projectDataIF) => {
const idx = projectData.value.findIndex(item => item == data);
selectedProjectDetail.value = projectData.value[idx].detail;
showDetail.value = true;
}
const closeProjectDetail = () => {
showDetail.value = false;
}
//删除项目内容
const deleteProjecFolder = (index: number) => {
ElMessageBox.confirm('将删除该文件,继续?', 'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
selectedProjectDetail.value?.files.splice(index, 1);
})
}
//添加项目
const addProject = () => {
createProjectFormVisible.value = true;
}
const confirmAddProject = () => {
createProjectFormVisible.value = false;
projectData.value.push({
projectName: createProjectForm.name,
introduction: createProjectForm.description,
date: getFormDate(),
detail: {
outputlog: '',
introduction: '',
files: []
}
})
}
//删除项目
const deleteProject = () => {
ElMessageBox.confirm('将删除选中的项目,继续?', 'Warning',
{
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
projectData.value = projectData.value.filter((x) => !selectedProject.value!.some((item) => x.projectName === item.projectName));
projectTableRef.value!.clearSelection();
})
}
//下载项目
const downloadProject = () => {
projectTableRef.value!.clearSelection();
}
const handleProjectTabClick = (pane: TabsPaneContext, ev: Event) => {
// console.log(pane);
}
const handleProjectSelectionChange = (val: projectDataIF[]) => {
selectedProject.value = val;
}
</script>
<style>
.grid-content {
border-radius: 4px;
min-height: 10px;
}
.demo-tabs>.el-tabs__content {
padding: 0px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
.title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
.demo-radius {
height: 800px;
width: 90%;
border: 1px solid #b7b7ba;
border-radius: 10px;
margin-top: 20px;
}
.example-demonstration {
margin: 1rem;
}
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>