|
|
@@ -2,11 +2,25 @@ |
|
|
|
<div class="archive-view"> |
|
|
|
<div class="view-left"> |
|
|
|
<div class="view-left-title">从成果中选择交付物</div> |
|
|
|
<div class="view-left-button">查看接收方消息</div> |
|
|
|
<div class="view-left-button" @click="receiverModalVisible = true">查看接收方消息</div> |
|
|
|
<el-collapse v-model="collapseActiveKeys" class="panel-wrapper"> |
|
|
|
<el-collapse-item class="panel" title="项目成果" name="1"> |
|
|
|
<div class="list-item "><span>显示所有勾选文件(xx/xx)</span></div> |
|
|
|
<div class="list-item list-item-active"><span>节点名称</span></div> |
|
|
|
<div |
|
|
|
class="list-item" |
|
|
|
:class="{ 'list-item-active': tabKey === 'all' }" |
|
|
|
@click.stop="filterArchive()" |
|
|
|
> |
|
|
|
<span>显示所有勾选文件{{selectedKeyCount ? ` (${selectedKeyCount}/${fileTotalCount})`:''}}</span> |
|
|
|
</div> |
|
|
|
<div |
|
|
|
class="list-item" |
|
|
|
v-for="folder in currentArchiveTreeList.filter(i => i.nodeType === 'folder')" |
|
|
|
:class="{ 'list-item-active': tabKey === folder.id }" |
|
|
|
:key="folder.id" |
|
|
|
@click.stop="filterArchive(folder)" |
|
|
|
> |
|
|
|
<span>{{folder.folderName}}</span> |
|
|
|
</div> |
|
|
|
</el-collapse-item> |
|
|
|
<el-collapse-item class="panel" title="历史提交记录" name="2"> |
|
|
|
<div |
|
|
@@ -14,6 +28,7 @@ |
|
|
|
:key="commit.id" |
|
|
|
class="list-item" |
|
|
|
:class="{ 'list-item-active': tabKey === commit.id }" |
|
|
|
@click.stop="checkCommitHistory(commit)" |
|
|
|
> |
|
|
|
{{commit.createTime | formatTime}} |
|
|
|
</div> |
|
|
@@ -22,32 +37,130 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="view-right"> |
|
|
|
<el-button type="primary" class="view-right-button">点击认证并交付所有勾选文件</el-button> |
|
|
|
<el-button type="primary" class="view-right-button" :disabled="!selectedKeyCount" @click.stop="authModalVisible = true">点击认证并交付所有勾选文件</el-button> |
|
|
|
<div class="table-like"> |
|
|
|
<div class="table-header"> |
|
|
|
<div class="table-th">由工作成果提交的文件</div> |
|
|
|
<div class="table-th">版本号</div> |
|
|
|
<div class="table-th">种类</div> |
|
|
|
<div class="table-th">文件类型</div> |
|
|
|
<div class="table-th">创建人</div> |
|
|
|
<div class="table-th">提交时间</div> |
|
|
|
<div class="table-th">操作</div> |
|
|
|
</div> |
|
|
|
<item-list /> |
|
|
|
<template |
|
|
|
v-for="node in rightPanelItemList" |
|
|
|
> |
|
|
|
<list-file-item |
|
|
|
:file="node" |
|
|
|
:key="node.id" |
|
|
|
v-if="node.nodeType === 'file'" |
|
|
|
:readOnly="readOnly" |
|
|
|
:checked="selectedKeyMap[node.id]" |
|
|
|
@check="toggleCheck" |
|
|
|
/> |
|
|
|
<div v-else :key="node.id" class="table-node"> |
|
|
|
<div class="table-node-header"> |
|
|
|
<div class="table-node-header-th">{{node.folderName}}</div> |
|
|
|
<div class="table-node-header-extra" v-show="!readOnly"> |
|
|
|
<el-checkbox class="table-node-header-checkbox" @change="toggleCheck(node)" /> |
|
|
|
全选 |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<item-list |
|
|
|
:list="node.children" |
|
|
|
:readOnly="readOnly" |
|
|
|
:selectedKeyMap="selectedKeyMap" |
|
|
|
@check="toggleCheck" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<el-dialog |
|
|
|
:visible.sync="authModalVisible" |
|
|
|
width="460px" |
|
|
|
top="30vh" |
|
|
|
class="auth-modal" |
|
|
|
> |
|
|
|
<template #title> |
|
|
|
<div class="auth-title">账户认证</div> |
|
|
|
<div class="auth-hint">请输入您的账户密码,一旦交付将不可撤回!</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<el-input |
|
|
|
v-model="authInpVal" |
|
|
|
placeholder="请输入密码" |
|
|
|
type="password" |
|
|
|
auto-complete="new-password" |
|
|
|
@keyup.enter.native="doCommit" |
|
|
|
/> |
|
|
|
<div slot="footer" class="dialog-footer"> |
|
|
|
<el-button @click="authModalVisible = false">取 消</el-button> |
|
|
|
<el-button :disabled="!authInpVal" type="primary" @click="doCommit" >认证并提交交付物</el-button> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
<el-dialog |
|
|
|
:visible.sync="receiverModalVisible" |
|
|
|
width="660px" |
|
|
|
class="receiver-modal" |
|
|
|
> |
|
|
|
<div class="template-summary"> |
|
|
|
<top-header |
|
|
|
class="set-header-position header-wrap" |
|
|
|
theme="" |
|
|
|
:onBackBtnClick="returnToDistWorkClick" |
|
|
|
backBtnTitle="接收方信息" |
|
|
|
:showUserCenter="false" |
|
|
|
:showBackBtn="true" |
|
|
|
> |
|
|
|
<div slot="right" class="edit-template-btn" @click.stop="receiverModalVisible = false">关闭</div> |
|
|
|
</top-header> |
|
|
|
<div class="summary-wrap"> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">企业名称:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">项目名称:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">关联模板名称:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">对方项目负责人:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">对接模板节点:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
<div class="item"> |
|
|
|
<div class="item-title">对接节点负责人:</div> |
|
|
|
<div class="item-value"></div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import ItemList from './item-list.vue'; |
|
|
|
import * as services from '../../service'; |
|
|
|
import { firstCharToLowerCase } from '@/utils/tool'; |
|
|
|
import { firstCharToLowerCase, firstCharToUpperCase, notify } from '@/utils/tool'; |
|
|
|
import moment from 'dayjs'; |
|
|
|
import { VirtualFolder } from '../../helper'; |
|
|
|
import ListFileItem from './list-file-item.vue'; |
|
|
|
import { wrapErrorHint } from '@/utils/request'; |
|
|
|
import TopHeader from '@/components/app-header/components/top-header.vue'; |
|
|
|
|
|
|
|
export default { |
|
|
|
components: { ItemList }, |
|
|
|
components: { ItemList, ListFileItem, TopHeader }, |
|
|
|
props: { |
|
|
|
currentNodeFolder: Object, |
|
|
|
projectId: String, |
|
|
|
}, |
|
|
|
filters: { |
|
|
|
formatTime(v) { |
|
|
@@ -55,22 +168,74 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
data() { |
|
|
|
const accountName = this.$store.state.accountName; |
|
|
|
return { |
|
|
|
accountName, |
|
|
|
userId: sessionStorage.userId, // 当前用户id |
|
|
|
collapseActiveKeys: ['1', '2'], |
|
|
|
selectedItemKeys: [], // 选中 |
|
|
|
readonly: false, // 查看历史提交记录时改为true |
|
|
|
// selectedItemKeys: [], // 选中 |
|
|
|
readOnly: false, // 查看历史提交记录时改为true |
|
|
|
commitHisList: [], |
|
|
|
tabKey: 'all', // 左侧菜单选中key |
|
|
|
currentArchivePlainList: [], |
|
|
|
currentArchiveTreeList: [], |
|
|
|
rightPanelItemList: [], |
|
|
|
selectedKeyMap: {}, // 选中的文件的hash |
|
|
|
|
|
|
|
// 认证弹窗 |
|
|
|
authModalVisible: false, |
|
|
|
authInpVal: '', |
|
|
|
|
|
|
|
// |
|
|
|
receiverModalVisible: false, |
|
|
|
receiverInfo: {}, |
|
|
|
}; |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
selectedKeyCount() { |
|
|
|
return Object.values(this.selectedKeyMap).filter(i => i).length; |
|
|
|
}, |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
// 1. 查询节点文件 |
|
|
|
this.fetchFolderFiles(); |
|
|
|
// 2. 查询历史提交记录 |
|
|
|
this.fetchArchiveHistory(); |
|
|
|
}, |
|
|
|
watch: { |
|
|
|
authModalVisible(v) { |
|
|
|
if(!v) { |
|
|
|
this.authInpVal = ''; |
|
|
|
} |
|
|
|
}, |
|
|
|
receiverModalVisible(flag) { |
|
|
|
if(flag) { |
|
|
|
this.fetchReceiverInfo(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// |
|
|
|
toggleCheck(node) { |
|
|
|
const tempMap = this.selectedKeyMap; |
|
|
|
if(node.nodeType === 'file') { |
|
|
|
this.selectedKeyMap = { ...tempMap, [node.id]: !tempMap[node.id] } |
|
|
|
} else { // folder |
|
|
|
const relationKeys = []; |
|
|
|
let checkFlag = true; |
|
|
|
recursionForEach(node.children, (fileNode) => { |
|
|
|
relationKeys.push(fileNode.id); |
|
|
|
checkFlag = checkFlag && tempMap[fileNode.id]; |
|
|
|
}); |
|
|
|
const stateMap = relationKeys.reduce((h, key) => (h[key] = !checkFlag, h), {}); |
|
|
|
this.selectedKeyMap = { ...tempMap, ...stateMap }; |
|
|
|
} |
|
|
|
}, |
|
|
|
filterArchive(folder) { |
|
|
|
this.tabKey = !folder ? 'all' : folder.id; |
|
|
|
this.readOnly = false; |
|
|
|
this.rightPanelItemList = !folder ? this.currentArchiveTreeList : this.currentArchiveTreeList.filter(i => i.id === folder.id); |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 查询当前文件夹内容 |
|
|
|
*/ |
|
|
@@ -78,35 +243,108 @@ export default { |
|
|
|
const nodeFolder = this.currentNodeFolder; |
|
|
|
const userId = this.userId; |
|
|
|
if(!nodeFolder || !nodeFolder.id) return; |
|
|
|
// 先清空当前的文件队列 在这清空队列会有闪烁问题,得放在切换显示文件夹内容的地方触发 |
|
|
|
// this.uploadFileList = []; |
|
|
|
// this.workFileList = []; |
|
|
|
// this.workSubFolderList = []; |
|
|
|
// this.cooperationFileList = []; |
|
|
|
// todo 接口去除公共文件夹配置 |
|
|
|
const folderResInfo = await services.fetchFolderFileList(nodeFolder.id, userId, nodeFolder.id); |
|
|
|
console.log(folderResInfo); |
|
|
|
|
|
|
|
if(!folderResInfo) return; |
|
|
|
// 快速切换节点的时候 |
|
|
|
if(this.currentNodeFolder.id !== nodeFolder.id) return; |
|
|
|
// this.workFileList = folderResInfo.file; |
|
|
|
// this.subFolderMap = { ...this.subFolderMap, ...folderResInfo.folderMap }; |
|
|
|
// this.resolveUploadFileList(this.workFileList); |
|
|
|
// this.workSubFolderList = folderResInfo.folder; |
|
|
|
// this.tempWorkSubFolderList = this.tempWorkSubFolderList.filter(tempFolder => !folderResInfo.folder.some(folder => folder.id === tempFolder.id)); |
|
|
|
// this.cooperationFileList = folderResInfo.coordinationFiles; |
|
|
|
|
|
|
|
// this.coopSubFolderList = this.dealWithCoopFiles(this.cooperationFileList); |
|
|
|
// this.resolveUploadFileList(this.cooperationFileList); |
|
|
|
|
|
|
|
this.currentArchiveTreeList = resolveFileListToTree(folderResInfo.deliverables); |
|
|
|
this.fileTotalCount = folderResInfo.deliverables.length; |
|
|
|
this.currentArchivePlainList = folderResInfo.deliverables; |
|
|
|
|
|
|
|
this.rightPanelItemList = this.currentArchiveTreeList; |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 查看提交历史记录 |
|
|
|
*/ |
|
|
|
async fetchArchiveHistory() { |
|
|
|
const res = await this.$fetchApi('file/queryProjDeliverablesSubmitByDeliverablesFolderId', { deliverablesFolderId: this.currentNodeFolder.id }); |
|
|
|
console.log(res); |
|
|
|
this.commitHisList = (res.Data || []).map(firstCharToLowerCase); |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 查看历史提交详情 |
|
|
|
*/ |
|
|
|
async checkCommitHistory(commit) { |
|
|
|
this.tabKey = commit.id; |
|
|
|
const res = await this.$fetchApi('file/queryProjDeliverablesSubmitDetailByDeliverablesSubmitId', { deliverablesSubmitId: commit.id }); |
|
|
|
this.rightPanelItemList = resolveFileListToTree((res.Data || []).map(firstCharToLowerCase)); |
|
|
|
this.readOnly = true; |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 提交新的交付物 |
|
|
|
*/ |
|
|
|
async doCommit() { |
|
|
|
const res = await this.$fetchApi('authentication/passwordCheck', { password: this.authInpVal, userName: this.accountName }); |
|
|
|
wrapErrorHint(res); |
|
|
|
if(res.Code) { return; } |
|
|
|
const selectedKeyMap = this.selectedKeyMap; |
|
|
|
const commitFileList = this.currentArchivePlainList.filter(file => selectedKeyMap[file.id]); |
|
|
|
const commitRes = await this.$fetchApi('file/submitDeliverables', { |
|
|
|
DeliverablesFolderId: this.currentNodeFolder.id, |
|
|
|
ProjId: this.projectId, |
|
|
|
DeliverablesFile: commitFileList.map(firstCharToUpperCase), |
|
|
|
}); |
|
|
|
wrapErrorHint(commitRes); |
|
|
|
console.log(commitRes); |
|
|
|
if(commitRes.Code) { return; } |
|
|
|
notify.success('交付成功'); |
|
|
|
this.authModalVisible = false; |
|
|
|
this.selectedKeyMap = {}; |
|
|
|
this.fetchArchiveHistory(); |
|
|
|
// this.removeLink(); |
|
|
|
}, |
|
|
|
/** |
|
|
|
* 查看接收方信息 |
|
|
|
*/ |
|
|
|
async fetchReceiverInfo() { |
|
|
|
|
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function recursionForEach(list, f) { |
|
|
|
list.forEach(iNode => { |
|
|
|
if(iNode.nodeType === 'file') { |
|
|
|
f(iNode); |
|
|
|
return; |
|
|
|
} |
|
|
|
recursionForEach(iNode.children, f); |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
function resolveFileListToTree(fileList) { |
|
|
|
const folderMap = {}; // [key: folderPath]: folder |
|
|
|
const headList = []; |
|
|
|
fileList.forEach(file => { |
|
|
|
file.nodeType = 'file'; |
|
|
|
file.nodeFolderName = file.nodeName; |
|
|
|
// delete file.nodeName; |
|
|
|
if(!file.nodeFolderName) { headList.push(file); return; } |
|
|
|
const filePath = `${file.nodeFolderName}${file.relativePath ? `/${file.relativePath}`: ''}`; |
|
|
|
generateFolder(folderMap, filePath); |
|
|
|
const folder = folderMap[filePath]; |
|
|
|
folder.children.push(file); |
|
|
|
}); |
|
|
|
|
|
|
|
return headList.concat(Object.values(folderMap).filter(folder => !folder.parentFolderPath)); |
|
|
|
} |
|
|
|
|
|
|
|
function generateFolder(folderMap, filePath) { |
|
|
|
const paths = filePath.split('/'); |
|
|
|
paths.forEach((_, idx) => { |
|
|
|
const fullRelativePath = paths.slice(0, idx + 1).join('/'); |
|
|
|
if(folderMap[fullRelativePath]) return; |
|
|
|
const folder = new VirtualFolder(fullRelativePath); |
|
|
|
folder.children = []; |
|
|
|
folder.nodeType = 'folder'; |
|
|
|
const parentRelativePath = folder.parentFolderPath; |
|
|
|
folderMap[fullRelativePath] = folder; |
|
|
|
if(folderMap[parentRelativePath]) { |
|
|
|
folderMap[parentRelativePath].children.push(folder); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<style lang="less" scoped> |
|
|
@@ -114,6 +352,12 @@ export default { |
|
|
|
height: 100%; |
|
|
|
padding: 0 40px 0 48px; |
|
|
|
@leftViewWidth: 312px; |
|
|
|
// 修改该页面下所有checkbox样式 |
|
|
|
// ::v-deep .el-checkbox__input:not(.is-checked) .el-checkbox__inner { |
|
|
|
// background-color: #cbcbce; |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
.view-left { |
|
|
|
display: inline-block; |
|
|
|
vertical-align: top; |
|
|
@@ -194,6 +438,29 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.table-node-header { |
|
|
|
display: flex; |
|
|
|
flex-direction: row; |
|
|
|
justify-content: space-between; |
|
|
|
height: 44px; |
|
|
|
line-height: 44px; |
|
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.2); |
|
|
|
font-size: 16px; |
|
|
|
&-checkbox { |
|
|
|
margin-left: 10px; |
|
|
|
::v-deep .el-checkbox__inner { |
|
|
|
width: 20px; |
|
|
|
height: 20px; |
|
|
|
} |
|
|
|
} |
|
|
|
&-th { |
|
|
|
flex: 918; |
|
|
|
} |
|
|
|
&-extra { |
|
|
|
flex: 90; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -258,7 +525,75 @@ export default { |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.auth-modal { |
|
|
|
::v-deep .el-dialog__body { |
|
|
|
padding-top: 0; |
|
|
|
} |
|
|
|
.auth-title { |
|
|
|
margin-bottom: 8px; |
|
|
|
} |
|
|
|
.auth-hint { |
|
|
|
font-size: 16px; |
|
|
|
color: #32323c; |
|
|
|
text-align: center; |
|
|
|
margin-bottom: 20px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.receiver-modal { |
|
|
|
::v-deep .el-dialog__header { |
|
|
|
display: none; |
|
|
|
} |
|
|
|
::v-deep .el-dialog__body { |
|
|
|
padding: 0; |
|
|
|
padding-bottom: 20px; |
|
|
|
} |
|
|
|
.set-header-position { |
|
|
|
position: relative !important; |
|
|
|
color: #32323c !important; |
|
|
|
z-index: 99; |
|
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.2); |
|
|
|
::v-deep .app-header-content { |
|
|
|
padding-left: 24px !important; |
|
|
|
} |
|
|
|
} |
|
|
|
.edit-template-btn{ |
|
|
|
position: relative; |
|
|
|
left: 24px; |
|
|
|
cursor: pointer; |
|
|
|
padding: 0 16px; |
|
|
|
height: 32px; |
|
|
|
line-height: 32px; |
|
|
|
margin: 9px 0; |
|
|
|
border-radius: 8px; |
|
|
|
background-color: rgba(120, 80, 255, 1); |
|
|
|
color: rgba(245, 245, 247, 100); |
|
|
|
font-size: 14px; |
|
|
|
text-align: center; |
|
|
|
font-family: PingFangSC-Regular; |
|
|
|
} |
|
|
|
.item { |
|
|
|
display: flex; |
|
|
|
width: 100%; |
|
|
|
margin: 14px 0; |
|
|
|
font-size: 14px; |
|
|
|
font-family: PingFangSC-Regular; |
|
|
|
color: #32323C; |
|
|
|
.item-title { |
|
|
|
color: #62492f; |
|
|
|
width: 24%; |
|
|
|
text-align: right; |
|
|
|
} |
|
|
|
.item-value { |
|
|
|
margin: 0 0 0 12px; |
|
|
|
&:not(.cover-img-wrap) { |
|
|
|
flex: 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style> |