|
|
@@ -0,0 +1,363 @@
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <el-row>
|
|
|
+ <h2>{{fileReview.title}}</h2>
|
|
|
+ <el-tag class="status-tag" size="medium" v-if="fileReview.status === 'Planning'">计划中</el-tag>
|
|
|
+ <el-tag class="status-tag" size="medium" v-if="fileReview.status === 'Inspecting'">审查中</el-tag>
|
|
|
+ <el-tag class="status-tag" size="medium" v-if="fileReview.status === 'Rework'">修改中</el-tag>
|
|
|
+ <el-tag class="status-tag" size="medium" v-if="fileReview.status === 'Completed'">已结束</el-tag>
|
|
|
+ </el-row>
|
|
|
+ <el-row class="status-information">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ <div v-if="fileReview.status === 'Planning' && userRole === 'WORKER'">目前正处于计划阶段,您可以更改递交审查的文件列表</div>
|
|
|
+ <div v-if="fileReview.status === 'Planning' && userRole === 'CREATOR'">目前正处于计划阶段,您可以更改递交审查的文件列表,确认无误后请点击递交审查进入下一阶段</div>
|
|
|
+ <div v-if="fileReview.status === 'Planning' && userRole === 'REVIEWER'">目前正处于计划阶段,您可以预览需评审文件,但暂时无法发表评论</div>
|
|
|
+ <div v-if="fileReview.status === 'Inspecting' && userRole === 'WORKER'">目前正处于评审阶段,请等待所有评审人评审完成</div>
|
|
|
+ <div v-if="fileReview.status === 'Inspecting' && userRole === 'CREATOR'">目前正处于评审阶段,请等待所有评审人评审完成</div>
|
|
|
+ <div v-if="fileReview.status === 'Inspecting' && userRole === 'REVIEWER' && !hasInspected">目前正处于评审阶段,请提出您的评审意见,并根据结果选择返工或是结束</div>
|
|
|
+ <div v-if="fileReview.status === 'Inspecting' && userRole === 'REVIEWER' && hasInspected">目前正处于评审阶段,您已提交了您的评审结果,请等待其他人评审结束</div>
|
|
|
+ <div v-if="fileReview.status === 'Rework' && userRole === 'WORKER'">目前正处于返工阶段,请根据评审意见完成修改</div>
|
|
|
+ <div v-if="fileReview.status === 'Rework' && userRole === 'CREATOR'">目前正处于返工阶段,请根据评审意见完成修改,修改完毕后请点击递交审查进入下一阶段</div>
|
|
|
+ <div v-if="fileReview.status === 'Rework' && userRole === 'REVIEWER'">目前正处于返工阶段,请等待修改完毕</div>
|
|
|
+ <div v-if="fileReview.status === 'Completed'">代码已通过评审</div>
|
|
|
+ </el-row>
|
|
|
+ <el-row>
|
|
|
+ <h3>需评审文件列表</h3>
|
|
|
+ <div class="button-plate">
|
|
|
+ <el-button type="primary" size="small" v-if="showUpdateFileList()" @click="updateFileList">更新文件列表</el-button>
|
|
|
+ <el-button type="success" size="small" v-if="showMoveToInspect()" @click="submitToInspect()">递交审查</el-button>
|
|
|
+ <el-button type="primary" size="small" v-if="showSubmitToRework()" @click="submitToRework()" :disabled="hasInspected">递交返工</el-button>
|
|
|
+ <el-button type="success" size="small" v-if="showSubmitToRework()" @click="submitToComplete()" :disabled="hasInspected">结束评审</el-button>
|
|
|
+ </div>
|
|
|
+ </el-row>
|
|
|
+ <el-tree :load="loadNode" :props="fileListProps" lazy show-checkbox ref="fileTree" node-key="path" @node-click="clickTreeNode"></el-tree>
|
|
|
+ <ElTabs v-model="activeTab" style="width: 90%">
|
|
|
+ <ElTabPane :label="'代码展示'" name="codePlate">
|
|
|
+ <div class="file-headline">{{fileName}}</div>
|
|
|
+ <div class="code-plate">
|
|
|
+ <el-row v-for="(line, index) in code.split('\n')" :key="index">
|
|
|
+ <el-col :span="1" class="line-number">
|
|
|
+ <button @click="clickLine(index+1)">{{index+1}}</button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="23">
|
|
|
+ <highlightjs language="java" :code="line" v-if="line !== ''" />
|
|
|
+ <highlightjs language="java" :code="' '" v-if="line === ''"/>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-drawer v-model="showAddCommentDrawer" >
|
|
|
+ <template #title><h4>添加评论</h4></template>
|
|
|
+ <el-form ref="addComment" :model="addCommentForm" :rules="addCommentRules">
|
|
|
+ <el-form-item label="选择类型">
|
|
|
+ <el-select v-model="addCommentForm.commentType">
|
|
|
+ <el-option label="普通" value="Comment"/>
|
|
|
+ <el-option label="警告" value="Warning"/>
|
|
|
+ <el-option label="严重错误" value="Error"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="填写意见" prop="content">
|
|
|
+ <el-input type="textarea" :autosize="{minRows:3, maxRows: 5}" v-model="addCommentForm.content"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item @click="addComment()">
|
|
|
+ <el-button type="success">创建</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+ </ElTabPane>
|
|
|
+ <ElTabPane :label="'评审意见'" name="commentList">
|
|
|
+ <div v-if="commentList.length!==0">
|
|
|
+ <div class="comment-line" v-for="item in commentList" :key="item.id">
|
|
|
+ <el-row>
|
|
|
+ <el-tag size="mini" effect="plain" type="warning" v-if="item.commentType==='Warning'">Warning</el-tag>
|
|
|
+ <el-tag size="mini" effect="plain" type="danger" v-if="item.commentType==='Error'">Error</el-tag>
|
|
|
+ at <span class="path">{{item.content.split('\t')[0]}}</span>, Line <span class="path"> {{item.content.split('\t')[1]}}</span>
|
|
|
+ </el-row>
|
|
|
+ <el-row>{{item.content.split('\t')[2]}}</el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ElTabPane>
|
|
|
+ </ElTabs>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script>
|
|
|
+import 'highlight.js/lib/common';
|
|
|
+import 'highlight.js/styles/base16/dracula.css';
|
|
|
+import hljsVuePlugin from '@highlightjs/vue-plugin';
|
|
|
+import { CodeReviewAPI } from '../../api';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'FileReviewDetail.vue',
|
|
|
+ components: { highlightjs: hljsVuePlugin.component },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ activeTab: 'codePlate',
|
|
|
+ showAddCommentDrawer: false,
|
|
|
+ fileReviewId: -1,
|
|
|
+ fileReview: {
|
|
|
+ id: -1,
|
|
|
+ projectId: -1,
|
|
|
+ branch: '',
|
|
|
+ creatorId: -1,
|
|
|
+ reviewerList: [],
|
|
|
+ fileList: [],
|
|
|
+ createTime: '',
|
|
|
+ status: '',
|
|
|
+ },
|
|
|
+ userRole: '',
|
|
|
+ fileListProps: {
|
|
|
+ label: 'name',
|
|
|
+ children: 'children',
|
|
|
+ isLeaf: 'leaf',
|
|
|
+ },
|
|
|
+ fileList: [{
|
|
|
+ name: 'file1',
|
|
|
+ path: '',
|
|
|
+ }],
|
|
|
+ nodeTreeRootConfig: {
|
|
|
+ nodeLevel0: {},
|
|
|
+ resolveLevel0: {},
|
|
|
+ },
|
|
|
+ fileName: '',
|
|
|
+ code: 'const i = 100;\nvar adc = true;\n',
|
|
|
+ commentList: [{
|
|
|
+ id: -1,
|
|
|
+ creatorId: -1,
|
|
|
+ creatorName: 'ShanShan',
|
|
|
+ content: 'main.java\t13\tthe veriable i should have an another name',
|
|
|
+ commentType: 'Error',
|
|
|
+ }],
|
|
|
+ addCommentForm: {
|
|
|
+ codeReviewId: -1,
|
|
|
+ creatorId: -1,
|
|
|
+ creatorName: '',
|
|
|
+ commentType: 'Comment',
|
|
|
+ content: '',
|
|
|
+ replyId: -1,
|
|
|
+ },
|
|
|
+ selectedLine: {
|
|
|
+ path: '',
|
|
|
+ line: -1,
|
|
|
+ },
|
|
|
+ addCommentRules: {
|
|
|
+ content: [{ required: true, message: '请输入评审意见', trigger: 'blur' }],
|
|
|
+ },
|
|
|
+ hasInspected: false,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async loadNode(node, resolve) {
|
|
|
+ if (node.level === 0) {
|
|
|
+ this.nodeTreeRootConfig = {
|
|
|
+ nodeLevel0: node,
|
|
|
+ resolveLevel0: resolve,
|
|
|
+ };
|
|
|
+ return resolve(this.fileList);
|
|
|
+ }
|
|
|
+ if (node.level > 0) {
|
|
|
+ const subTree = await CodeReviewAPI.getSubTree(this.fileReview.projectId, this.fileReview.branch, node.data.path);
|
|
|
+ return resolve(subTree);
|
|
|
+ }
|
|
|
+ return resolve([]);
|
|
|
+ },
|
|
|
+ setUserRole() {
|
|
|
+ if (this.currentUserId === this.fileReview.creatorId) this.userRole = 'CREATOR';
|
|
|
+ else if (this.fileReview.reviewerList.includes(this.currentUserId)) this.userRole = 'REVIEWER';
|
|
|
+ else this.userRole = 'WORKER';
|
|
|
+ },
|
|
|
+ showUpdateFileList() {
|
|
|
+ return this.fileReview.status === 'Planning' && (this.userRole === 'WORKER' || this.userRole === 'CREATOR');
|
|
|
+ },
|
|
|
+ updateFileList() {
|
|
|
+ this.fileReview.fileList = this.$refs.fileTree.getCheckedKeys();
|
|
|
+ CodeReviewAPI.updateFileList(this.fileReview.id, this.fileReview.fileList).then(
|
|
|
+ (res) => {
|
|
|
+ this.$message({
|
|
|
+ message: '文件列表更新成功',
|
|
|
+ type: 'success',
|
|
|
+ });
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ showMoveToInspect() {
|
|
|
+ return (this.fileReview.status === 'Planning' || this.fileReview.status === 'Rework') && this.userRole === 'CREATOR';
|
|
|
+ },
|
|
|
+ submitToInspect() {
|
|
|
+ CodeReviewAPI.submitToInspection(this.fileReview.id).then(
|
|
|
+ (res) => {
|
|
|
+ this.$message({ message: '递交审查成功', type: 'success' });
|
|
|
+ this.fileReview.status = 'Inspecting';
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ showSubmitToRework() {
|
|
|
+ return this.fileReview.status === 'Inspecting' && this.userRole === 'REVIEWER';
|
|
|
+ },
|
|
|
+ async submitToRework() {
|
|
|
+ this.fileReview.status = await CodeReviewAPI.submitToRework(this.fileReview.id, this.currentUserId);
|
|
|
+ this.$message({ message: '递交结果成功', type: 'success' });
|
|
|
+ this.hasInspected = true;
|
|
|
+ },
|
|
|
+ async submitToComplete() {
|
|
|
+ this.fileReview.status = await CodeReviewAPI.submitToComplete(this.fileReview.id, this.currentUserId);
|
|
|
+ this.$message({ message: '递交结果成功', type: 'success' });
|
|
|
+ this.hasInspected = true;
|
|
|
+ },
|
|
|
+ async clickTreeNode(data, node, obj) {
|
|
|
+ if (node.isLeaf) {
|
|
|
+ if (this.fileReview.fileList.includes(data.path)) {
|
|
|
+ this.fileName = data.path;
|
|
|
+ this.code = await CodeReviewAPI.getFileContent(this.fileReview.projectId, this.fileReview.branch, this.fileName);
|
|
|
+ } else {
|
|
|
+ this.$message('选中的文件不在评审列表中');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ clickLine(lineNum) {
|
|
|
+ if (this.userRole === 'REVIEWER' && this.fileReview.status === 'Inspecting' && !this.hasInspected) {
|
|
|
+ this.selectedLine.path = this.fileName;
|
|
|
+ this.selectedLine.line = lineNum;
|
|
|
+ this.showAddCommentDrawer = true;
|
|
|
+ }
|
|
|
+ // this.selectedLine.path = this.fileName;
|
|
|
+ // this.selectedLine.line = lineNum;
|
|
|
+ // this.showAddCommentDrawer = true;
|
|
|
+ },
|
|
|
+ addComment() {
|
|
|
+ this.$refs.addComment.validate(
|
|
|
+ (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ this.addCommentForm.content = `${this.selectedLine.path}\t${this.selectedLine.line}\t${this.addCommentForm.content}`;
|
|
|
+ CodeReviewAPI.createCommentForFileReview(this.addCommentForm).then(
|
|
|
+ async (res) => {
|
|
|
+ this.$message({ message: '创建评审意见成功', type: 'success' });
|
|
|
+ this.$refs.addComment.resetFields();
|
|
|
+ this.commentList = await CodeReviewAPI.getFileReviewComment(this.fileReview.id);
|
|
|
+ this.showAddCommentDrawer = false;
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ getFirstFile() {
|
|
|
+ let ret = '';
|
|
|
+ this.fileReview.fileList.forEach((file) => {
|
|
|
+ if (file.includes('.')) ret = file;
|
|
|
+ });
|
|
|
+ return ret;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ currentUserId() {
|
|
|
+ return this.$store.state.user.id;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ async beforeCreate() {
|
|
|
+ this.fileReviewId = this.$route.query.fileReviewId;
|
|
|
+ this.fileReview = await CodeReviewAPI.getFileReviewDetail(this.fileReviewId);
|
|
|
+ this.fileList = await CodeReviewAPI.getRepositoryTree(this.fileReview.projectId, this.fileReview.branch);
|
|
|
+ this.nodeTreeRootConfig.nodeLevel0.childNodes = [];
|
|
|
+ this.loadNode(this.nodeTreeRootConfig.nodeLevel0, this.nodeTreeRootConfig.resolveLevel0);
|
|
|
+ this.$refs.fileTree.setCheckedKeys(this.fileReview.fileList);
|
|
|
+ this.setUserRole();
|
|
|
+ this.fileName = this.getFirstFile();
|
|
|
+ this.code = await CodeReviewAPI.getFileContent(this.fileReview.projectId, this.fileReview.branch, this.fileName);
|
|
|
+ this.commentList = await CodeReviewAPI.getFileReviewComment(this.fileReview.id);
|
|
|
+ this.addCommentForm.codeReviewId = this.fileReview.id;
|
|
|
+ this.addCommentForm.creatorId = this.currentUserId;
|
|
|
+ this.hasInspected = await CodeReviewAPI.checkInspected(this.fileReview.id, this.currentUserId);
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.container {
|
|
|
+ margin: 20px 50px;
|
|
|
+ > * {
|
|
|
+ margin-top: 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.status-tag {
|
|
|
+ margin-left:25px;
|
|
|
+}
|
|
|
+.status-information {
|
|
|
+ color: #909399;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+.button-plate {
|
|
|
+ position: absolute;
|
|
|
+ right: 10%;
|
|
|
+ z-index: 10;
|
|
|
+}
|
|
|
+.el-tree {
|
|
|
+ border-style: solid;
|
|
|
+ border-width: 1px;
|
|
|
+ border-color: gainsboro;
|
|
|
+ border-radius: 5px;
|
|
|
+ width: 90%;
|
|
|
+}
|
|
|
+.file-headline {
|
|
|
+ width: 90%;
|
|
|
+ height: 24px;
|
|
|
+ padding: 5px;
|
|
|
+ font-size: 19px;
|
|
|
+}
|
|
|
+.code-plate {
|
|
|
+ width: 100%;
|
|
|
+ max-height: 450px;
|
|
|
+ margin-top: 0px;
|
|
|
+ overflow-y: auto;
|
|
|
+ .el-row {
|
|
|
+ height: 30px;
|
|
|
+ .line-number {
|
|
|
+ border-right: solid 1px #909399;
|
|
|
+ button {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ padding: 5px;
|
|
|
+ border-width: 0px;
|
|
|
+ text-align: right;
|
|
|
+ background-color: #282936;
|
|
|
+ color: #909399;
|
|
|
+ font-family: Consolas, Monaco, monospace;
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ :focus-within {
|
|
|
+ background-color: #535353;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.comment-line {
|
|
|
+ padding: 5px 10px;
|
|
|
+ border-bottom: solid 1px gainsboro;
|
|
|
+ .el-row {
|
|
|
+ padding: 2px;
|
|
|
+ .el-tag {
|
|
|
+ margin-right: 20px;
|
|
|
+ }
|
|
|
+ .path {
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.el-form {
|
|
|
+ padding: 12px;
|
|
|
+}
|
|
|
+::v-deep {
|
|
|
+ .el-tree {
|
|
|
+ .el-tree-node__content {
|
|
|
+ height: 35px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .code-plate {
|
|
|
+ code {
|
|
|
+ padding-top: 5px;
|
|
|
+ padding-bottom: 5px;
|
|
|
+ height: 20px;
|
|
|
+ font-family: Consolas, Monaco, monospace;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|