背景
前面使用低代码配置,把实体配置、库表和模式化的代码生成出来了,实际上是用平台帮忙开发人员把“体力活”给干了。接下来,就需要在此基础上进行个性化逻辑的开发。
文档操作
新增混合实体
因为很多操作同时涉及到文件和文件夹,如显示、移动、拷贝,因此定义一个新的实体对象PopDocument,作为文件夹和文件的混合对象。
package tech.abc.edoc.edoc.entity;
import lombok.Data;
import tech.abc.platform.common.base.BaseEntity;
import java.time.LocalDateTime;
/**
* 文件夹与文件混合对象
*
* @author wqliu
* @date 2021-03-07
*/
@Data
public class PopDocument extends BaseEntity {
// region 公用属性
/**
* 上级标识
*/
private String parentId;
/**
* 名称
*/
private String name;
/**
* 对象类型 FOLDER 文件夹 DOCUMENT 文档
*/
private String objectType;
//endregion
// region 文件夹专有属性
//endregion
// region 文档专有属性
/**
* 大小
*/
private String size;
/**
* 文件类型
*/
private String type;
/**
* 阅读次数
*/
private Integer readCount;
/**
* 下载次数
*/
private Integer downloadCount;
/**
* 上传时间
*/
private LocalDateTime uploadTime;
/**
* 状态
*/
private String status;
/**
* 锁定人标识
*/
private String lockId;
/**
* 锁定时间
*/
private LocalDateTime lockTime;
//endregion
}
混合显示
在文档库菜单对应的视图中,调整平台自动生成的代码,使用自定义的getChildren方法,一次性把左边树节点对应的文件夹下属的所有文件夹和文件都展现出来,实现效果如下:
实现混合查询服务方法
public List<PopDocument> getChildren(String id, SortInfo sortInfo) {
Folder entity = getEntity(id);
//权限检查
folderPermissionService.checkPermission(entity.getId(), DocumentPermissionItemEnum.VIEW_FOLDER.name());
//获取子文件夹
List<PopDocument> folderList=getFolderList(id,sortInfo);
folderList.stream().forEach(x->{
x.setObjectType(PopDocumentTypeEnum.FOLDER.name());
x.setType("文件夹");
});
//获取文档
List<PopDocument> documentList=getDocumentList(id,sortInfo);
documentList.stream().forEach(x->x.setObjectType(PopDocumentTypeEnum.DOCUMENT.name()));
//合并
folderList.addAll(documentList);
//返回
return folderList;
}
根据文件夹标识,先查询文件夹,后查询文件,然后合并查询结果,并且附加了排序处理。
调整前端调用
loadData() {
return new Promise((resolve) => {
if (this.parentId) {
this.loading = true
this.$api.edoc.folder
.getChildren(this.parentId, this.sortInfo)
.then((res) => {
this.tableData = res.data
resolve()
})
.finally(() => {
this.loading = false
this.currentId = this.$constant.NO_ITEM_SELECTED
})
}
})
}
写一个loadData方法,覆盖掉mixin中的默认加载数据的操作(分页查询文件夹下的文档列表)。
隐藏分页控件
分页控件来自于mixin,如要去除需要做的调整比较多,这里可以简单化处理,直接使用v-show=false隐藏。
混合处理
加载动态右键菜单
通过前面的改造,列表页面混合显示文件夹下的文件夹和文件,在列表页面点击右键菜单时,需要根据类型来显示不同的右键菜单。
// 表格右键菜单
tableContextMenuShow(row, column, mouseEvent) {
mouseEvent.preventDefault()
this.selectedRow = row
this.currentId = row.id
if (row.objectType === this.$constant.POP_DOCUMENT_TYPE_FOLDER) {
// 文件夹
this.$api.edoc.folderPermission.getFolderPermissionForUser(row.id).then((res) => {
this.setFolderContextMenu(res.data, mouseEvent)
})
} else if (row.objectType === this.$constant.POP_DOCUMENT_TYPE_DOCUMENT) {
// 文档
this.$api.edoc.folderPermission.getDocumentPermissionForUser(row.id).then((res) => {
this.setTableContextMenu(res.data, mouseEvent)
})
}
}
迁移文件夹相关操作
文件夹的操作在folder实体的树视图中实现了,需要迁移到document列表视图里,菜单项对应的操作,需要进行调整,比如原先是直接刷新树,现在需要触发事件,传递到外层的treeList视图,然后再调用tree相关操作,完成树的刷新。同时,需要注意的是,部分操作,如文件夹重命名,仅仅刷新树还不够,需要调用下list.vue的loadData方法,刷新列表数据,因为树节点变更时才会自动刷新列表数据,而重命名这种场景,选中的节点并没有发生变更,所以需要手动调用刷新。与之类似的操作还有删除文件夹。
list.vue
// 新建文件夹后回调
folderCreateCallback() {
this.$emit('refresh')
},
// 更名文件夹后回调
folderRenameCallback(data) {
this.$emit('refresh')
this.loadData()
},
treeList.vue
<template>
<el-container class="layout-container" style="height: 100%">
<el-aside width="200px">
<TreeView ref="treeView" @change-selected="changeNode" />
</el-aside>
<el-main>
<ListView :common-param="commonParam" @refresh="refresh" />
</el-main>
</el-container>
</template>
refresh() {
this.$refs.treeView.load()
}
tree.vue
load() {
return new Promise((resolve) => {
this.api.tree().then((res) => {
this.treeData = res.data
// 如没有默认选中节点
if (!this.currentId || this.currentId === '') {
// 默认设置根节点
this.currentId = this.treeData[0].id
this.currentName = this.treeData[0].label
// 设置根节点默认展开
this.cacheTreeExpandedKeys.push(this.treeData[0].id)
// 手工触发选择节点改变
this.$emit('change-selected', this.treeData[0].id, this.treeData[0].label)
}
})
})
}
实现文件右键操作
参照文件夹右键操作,实现文件的右键操作。
右键菜单定义如下:
<!-- 文档右键菜单 -->
<el-menu
v-show="tableContextMenu.visible"
ref="tableContextMenu"
:style="{
width: '150px',
left: tableContextMenu.left + 'px',
top: tableContextMenu.top + 'px',
position: 'fixed',
cursor: 'pointer',
'z-index': 9999
}"
popper-append-to-body
@mouseleave="tableContextMenu.visible = false"
@select="tableContextMenuSelect"
>
<el-menu-item
v-if="documentPermissionList.includes($constant.PREVIEW_DOCUMENT)"
:index="$constant.PREVIEW_DOCUMENT"
>
<el-icon><View /></el-icon>
<template #title>预览</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.DOWNLOAD_DOCUMENT)"
:index="$constant.DOWNLOAD_DOCUMENT"
>
<el-icon><Download /></el-icon>
<template #title>下载</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.RENAME_DOCUMENT)"
:index="$constant.RENAME_DOCUMENT"
>
<el-icon><Edit /></el-icon>
<template #title>更名</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.UPDATE_DOCUMENT)"
:index="$constant.UPDATE_DOCUMENT"
>
<el-icon><Edit /></el-icon>
<template #title>更新</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.SHARE_DOCUMENT)"
:index="$constant.SHARE_DOCUMENT"
>
<el-icon><Share /></el-icon>
<template #title>分享</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.LOCK_DOCUMENT)"
:index="$constant.LOCK_DOCUMENT"
>
<el-icon><Lock /></el-icon>
<template #title>锁定</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.UNLOCK_DOCUMENT)"
:index="$constant.UNLOCK_DOCUMENT"
>
<el-icon><Unlock /></el-icon>
<template #title>解锁</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.REMOVE_DOCUMENT)"
:index="$constant.REMOVE_DOCUMENT"
>
<el-icon><DocumentRemove /></el-icon>
<template #title>删除</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.COPY_DOCUMENT)"
:index="$constant.COPY_DOCUMENT"
>
<el-icon><CopyDocument /></el-icon>
<template #title>复制</template>
</el-menu-item>
<el-menu-item
v-if="documentPermissionList.includes($constant.MOVE_DOCUMENT)"
:index="$constant.MOVE_DOCUMENT"
>
<el-icon><Position /></el-icon>
<template #title>移动</template>
</el-menu-item>
<el-menu-item
class="h-5"
v-if="documentPermissionList.includes($constant.VIEW_DOCUMENT_VERSION)"
:index="$constant.VIEW_DOCUMENT_VERSION"
>
<el-icon><Flag /></el-icon>
<template #title>查看版本</template>
</el-menu-item>
<el-menu-item
class="h-5"
v-if="documentPermissionList.includes($constant.RESTORE_DOCUMENT_VERSION)"
:index="$constant.RESTORE_DOCUMENT_VERSION"
>
<el-icon><Flag /></el-icon>
<template #title>恢复版本</template>
</el-menu-item>
<el-menu-item v-if="documentPermissionList.length === 0" :index="$constant.NO_MENU_AVAILABLE">
<el-icon><Close /></el-icon>
<template #title>无可用菜单</template>
</el-menu-item>
</el-menu>
挂载右键菜单
<el-table
v-loading="loading"
:data="tableData"
style="width: 100%"
highlight-current-row
border
:default-sort="{ prop: sortInfo.sort_field, order: sortInfo.sort_sortType }"
@row-contextmenu="tableContextMenuShow"
@sort-change="sortChange"
@current-change="rowChange"
@selection-change="selectionChange"
@row-dblclick="rowDoubleClick"
>
实现文档功能项
预览
通过集成第三方文件预览组件kkfileview,来实现的最终的预览。
<template>
<el-dialog title="预览" v-model="visible" append-to-body fullscreen @close="visible = false">
<iframe
id="iframeId"
:src="url"
frameborder="0"
style="width: 98%; height: 95%; margin-top: 10px"
scrolling="auto"
></iframe>
</el-dialog>
</template>
<script>
export default {
data() {
return {
url: '',
visible: false
}
},
methods: {
show(id, name) {
this.$api.edoc.document.getToken(id).then((res) => {
// 获取访问令牌
const token = res.data
// 拼接文档访问路径
let previewUrl = import.meta.env.VITE_BASE_URL + '/edoc/document/getStream'
previewUrl = previewUrl + '?fullfilename=' + name + '&token=' + token
// 调用预览服务
this.url =
import.meta.env.VITE_DOCUMENT_PREVIEW_URL +
encodeURIComponent(this.$base64Util.encode(previewUrl))
this.visible = true
})
},
showVersion(id, version, name) {
this.$api.edoc.document.getToken(id).then((res) => {
// 获取访问令牌
const token = res.data
// 拼接文档访问路径
let previewUrl = import.meta.env.VITE_BASE_URL + '/edoc/document/getStreamVersion'
previewUrl =
previewUrl + '?fullfilename=' + name + '&token=' + token + '&version=' + version
// 调用预览服务
this.url =
import.meta.env.VITE_DOCUMENT_PREVIEW_URL +
encodeURIComponent(this.$base64Util.encode(previewUrl))
this.visible = true
})
}
}
}
</script>
<style lang="scss"></style>
下载
调用标准的下载操作即可。
// 下载
downloadDocument(row) {
this.api.download(row.id)
}
更名
因为常用,把更名操作单独拿出来作为独立操作。
<template>
<Dialog title="更名" v-model="visible" width="300px">
<el-form
ref="form"
:model="entityData"
:rules="rules"
label-width="60px"
label-position="right"
style="width: 90%; margin: 0px auto"
>
<!--表单区域 -->
<el-form-item label="名称" prop="name">
<el-input v-model="entityData.name" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="save" v-permission="pageCode + 'rename'">保存</el-button>
<el-button @click="close">关闭</el-button>
</template>
</Dialog>
</template>
<script>
import { modifyMixin } from '@/mixin/modifyMixin.js'
const MODULE_CODE = 'edoc'
const ENTITY_TYPE = 'document'
export default {
name: ENTITY_TYPE + '-modify',
mixins: [modifyMixin],
data() {
return {
entityType: ENTITY_TYPE,
moduleCode: MODULE_CODE,
// eslint-disable-next-line no-eval
api: eval('this.$api.' + MODULE_CODE + '.' + ENTITY_TYPE),
pageCode: MODULE_CODE + ':' + ENTITY_TYPE + ':',
entityData: {},
rules: {
//前端验证规则
name: [{ required: true, message: '【名称】不能为空', trigger: 'blur' }]
}
}
},
methods: {
// 重写保存方法
saveData() {
this.api
.rename(this.entityData.id, this.entityData.name)
.then((res) => {
if (this.afterSave) {
this.afterSave()
}
this.$emit('refresh')
this.close()
})
.finally(() => {
this.loading = false
})
}
}
}
</script>
<style></style>
在修改视图的基础上调整,页面上只保留名称单一属性,同时重写保存数据的方法,由默认的modify更换为rename。
更新
更新比较麻烦,需要多方改造。
前端上传文件的vue组件,需要调整,合并文件块操作增加文档标识、版本号、版本标记的处理,通过新加属性appendData接收,并传入到文件块合并的调用操作的参数中。
fileSuccess(rootFile, file) {
if (file.chunks.length > 1) {
//构造文件信息
const param = {
identifier: file.uniqueIdentifier,
filename: file.name,
moduleCode: this.moduleCode,
entityType: this.entityType,
entityId: this.entityId,
type: file.fileType,
totalSize: file.size,
...this.appendData
}
// 合并文件块
// eslint-disable-next-line no-eval
eval(this.mergeChunkApi)
.mergeChunks(param)
.then(() => {
if (!this.showSuccessFiles) {
// 移除已上传成功的文件
this.$refs.uploader.uploader.removeFile(file)
}
})
} else {
if (!this.showSuccessFiles) {
// 不分块,移除已上传成功的文件
this.$refs.uploader.uploader.removeFile(file)
}
}
}
后端需要扩展原有的附件上传功能,增加三个跟版本相关的属性,用于合并文件块时的逻辑处理。
package tech.abc.platform.oss.entity;
import lombok.Data;
import tech.abc.platform.common.base.BaseVO;
/**
* 文件 实体
* 匹配前端simple-uploader控件
*
* @author wqliu
* @date 2023-11-27
*/
@Data
public class FileInfo {
/**
* 文件标识
*/
private String identifier;
/**
* 文件名
*/
private String filename;
/**
* 模块编码
*/
private String moduleCode;
/**
* 实体类型
*/
private String entityType;
/**
* 实体标识
*/
private String entityId;
/**
* 文件类型
*/
private String type;
/**
* 总大小
*/
private Long totalSize;
// 附加文档标识、版本号、版本标记,用于文档更新时合并文件块处理
/**
* 文档标识
*/
private String documentId;
/**
* 文档版本
*/
private String documentVersion;
/**
* 版本标记
*/
private String versionTag;
}
服务层进行判断是上传文档还是更新版本,走两个逻辑处理分支。
public String mergeChunks(FileInfo fileInfo) {
// 合并文件
objectStoreService.mergeChunks(fileInfo);
if(StringUtils.isBlank(fileInfo.getDocumentId())){
// 正常上传,生成附件信息
return create(fileInfo);
}else{
// 版本更新
return modify(fileInfo);
}
}
分享
分享是在预览的基础上又往前走了一步。
内部处理的关键点有两个,一是调用后端,生成有时效限制的令牌,用于文件预览的身份认证;二是调用剪贴板的复制功能,方便用户复制网址分享。
代码实现如下:
<template>
<el-dialog
title="分享文档"
v-model="visible"
:close-on-click-modal="false"
append-to-body
destroy-on-close
width="400px"
@close="close"
>
<el-form
ref="form"
:model="entityData"
:rules="rules"
label-width="120px"
label-position="right"
style="width: 90%; margin-top: 10px"
>
<!--表单区域 -->
<el-form-item label="有效时长(小时)" prop="validHours">
<el-input v-model="entityData.validHours" />
</el-form-item>
<el-form-item label="共享地址">
<el-input v-model="shareUrl" readonly />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="close">关闭</el-button>
<el-button type="primary" @click="copy" :data-clipboard-text="shareUrl" class="clipboardObj"
>复制</el-button
>
</template>
</el-dialog>
</template>
<script>
import Clipboard from 'clipboard'
export default {
data() {
return {
loading: false,
visible: false,
folderId: '',
entityData: {
documentId: '',
validHours: 24
},
rules: {
validHours: [{ required: true, message: '请输入文档共享时长', trigger: 'blur' }]
},
shareUrl: '',
documentName: ''
}
},
methods: {
show(documentId, documentName) {
this.documentName = documentName
this.entityData.documentId = documentId
this.generateShareUrl()
this.visible = true
},
// 关闭
close() {
// 清理数据
this.entityData = {
documentId: '',
validHours: 24
}
this.visible = false
},
generateShareUrl() {
if (!this.entityData.validHours) {
this.$message.warning('请输入文档共享时长')
return
}
return this.$api.edoc.document
.getTokenWithTime(this.entityData.documentId, this.entityData.validHours)
.then((res) => {
const token = res.data
// 拼接文档访问路径
let previewUrl = import.meta.env.VITE_BASE_URL + '/edoc/document/getStream'
previewUrl = previewUrl + '?fullfilename=' + this.documentName + '&token=' + token
// 生成访问地址
this.shareUrl =
import.meta.env.VITE_DOCUMENT_PREVIEW_URL +
encodeURIComponent(this.$base64Util.encode(previewUrl))
})
},
// 复制
copy() {
// 此处需要重新调用一遍生成,主要在于用户修改共享时长后立即点击复制按钮,此时令牌尚在请求处理中,会造成不一致
this.generateShareUrl().then(() => {
// 复制
let clipboard = new Clipboard('.clipboardObj')
clipboard.on('success', (e) => {
this.$message({
type: 'info',
message: '复制成功'
})
// 释放内存
clipboard.destroy()
})
clipboard.on('error', (e) => {
// 不支持复制
this.$message({
type: 'warning',
message: '复制失败'
})
// 释放内存
clipboard.destroy()
})
})
}
}
}
</script>
<style lang="scss"></style>
锁定
锁定的逻辑是首先判断下状态,如当前为锁定,给予提示,当前用户可以通过线下沟通的方式与当前锁定人确认是否可以加锁,直接变更锁定人,这么设计是考虑到某些情况下虽然加了锁,但并未进行线下文档修改操作,因此原锁定人无需登录系统执行解锁操作;如果未加锁,则执行加锁操作。
// 锁定文档
lockDocument(row) {
if (row.status === 'LOCKED') {
this.$confirm('该文档当前是锁定状态, 是否将锁定人更新为您?', '确认', {
type: 'warning'
})
.then(() => {
this.api.lock(row.id).then(() => {
this.loadData()
})
})
.catch(() => {
this.$message.info('已取消')
})
} else {
this.$confirm('此操作将锁定该文档, 是否继续?', '确认', {
type: 'warning'
})
.then(() => {
this.api.lock(row.id).then(() => {
this.loadData()
})
})
.catch(() => {
this.$message.info('已取消')
})
}
},
解锁
解锁操作则比较简单,直接解锁或提示未锁定。
// 解锁文档
unlockDocument(row) {
if (row.status === 'LOCKED') {
this.$confirm('该操作将解除对文档的锁定,是否继续?', '确认', {
type: 'warning'
})
.then(() => {
this.api.unlock(row.id).then(() => {
this.loadData()
})
})
.catch(() => {
this.$message.info('已取消')
})
} else {
this.$message.info('当前文档未锁定,无需解锁')
}
},
删除
标准的移除操作。
// 移除文档
removeDocument(row) {
this.$confirm('此操作将删除该文档, 是否继续?', '确认', {
type: 'warning'
})
.then(() => {
this.api.remove(row.id).then(() => {
this.loadData()
})
})
.catch(() => {
this.$message.info('已取消')
})
}
复制
其实无论是文件夹复制,还是文档复制,重要操作都是选择目标文件夹,因此复用了一个功能页面。
<CopyDocumentPage ref="copyDocument" @confirm="copyDocumentCallback" />
import CopyFolder from '../folder/copy.vue'
// 加page后缀是因为防止与图标组件命名冲突
import CopyDocumentPage from '../folder/copy.vue'
在复制回调方法中,进行实际的逻辑处理。
// 复制文档
copyDocument() {
this.$refs.copyDocument.show(this.parentId)
},
// 复制文档后回调
copyDocumentCallback(parentId) {
this.api.copy(this.currentId, parentId).then(() => {
this.loadData()
})
}
移动
移动文档操作,实际跟复制文件夹和复制文档一样,都是需要选择目标文件夹,在回调操作中执行差异化逻辑就行了,因此使用的都是文件夹选择操作,为了更符合业务含义,将folder目录下的move.vue重命名为select.vue.
// 移动文档
moveDocument() {
this.$refs.moveDocument.show(this.parentId)
},
// 移动文档后回调
moveDocumentCallback(parentId) {
this.api.move(this.currentId, parentId).then(() => {
this.loadData()
})
}
查看版本
查看版本调用的是文档版本实体的列表操作,在列表视图的基础上做了改造,需要以对话框的方式打开,去除查询条件区域,按创建时间降序排序即可
附带了预览、下载和恢复版本三个操作,具体代码实现如下:
<template>
<Dialog title="文档版本" v-model="visible" width="800px" @close="close">
<el-card style="width: 100%">
<div style="margin-top: 0; margin-bottom: 10px; float: right">
<ColumnsController :value="columnList" :tableKey="tableKey" />
</div>
<el-table :data="tableData" style="width: 100%" highlight-current-row border>
<el-table-column type="selection" width="55" />
<el-table-column
v-for="(item, index) in showCols"
:key="index"
:label="item.label"
:prop="item.prop"
:show-overflow-tooltip="item.showOverflowTooltip"
:width="item.width"
:formatter="item.formatFunc"
:sortable="item.sortable"
/>
<el-table-column fixed="right" label="操作" width="250">
<template #default="scope">
<el-button
v-permission="pageCode + 'preview'"
type="primary"
@click="preview(scope.row)"
>预览</el-button
>
<el-button
v-permission="pageCode + 'download'"
type="primary"
@click="download(scope.row)"
>下载</el-button
>
<el-button
v-permission="pageCode + 'restore'"
type="primary"
@click="restore(scope.row)"
>恢复</el-button
>
</template>
</el-table-column>
</el-table>
</el-card>
<PreviewDocument ref="previewDocument" />
</Dialog>
</template>
<script lang="ts">
import ColumnsController from '@/components/abc/ColumnsController/index.vue'
import PreviewDocument from '../document/preview.vue'
import { Dialog } from '@/components/abc/Dialog'
const MODULE_CODE = 'edoc'
const ENTITY_TYPE = 'documentVersion'
export default {
components: { Dialog, ColumnsController, PreviewDocument },
mixins: [],
data() {
return {
entityType: ENTITY_TYPE,
moduleCode: MODULE_CODE,
// eslint-disable-next-line no-eval
api: eval('this.$api.' + MODULE_CODE + '.' + ENTITY_TYPE),
pageCode: MODULE_CODE + ':' + ENTITY_TYPE + ':',
// 排序信息
sortInfo: {
sort_field: 'id',
sort_sortType: 'descending'
},
columnList: [
{
prop: 'documentVersion',
label: '文档版本',
show: true,
showOverflowTooltip: true,
sortable: false
},
{
prop: 'versionTag',
label: '版本标记',
show: true,
showOverflowTooltip: true,
sortable: false
},
{
prop: 'uploadUserName',
label: '创建人',
show: true,
showOverflowTooltip: true
},
{
prop: 'createTime',
label: '创建时间',
show: true,
showOverflowTooltip: true,
sortable: false
}
],
queryCondition: {
//默认值处理
},
tableData: [],
visible: false,
documentName: '',
documentId: ''
}
},
computed: {
showCols() {
return this.columnList.filter((item) => item.show)
},
tableKey() {
const { path } = this.$route
return `${path}/table`
}
},
methods: {
show(documentId, documentName) {
this.documentId = documentId
this.documentName = documentName
this.loadData()
},
loadData() {
this.api.getList(this.documentId).then((res) => {
this.tableData = res.data
this.visible = true
})
},
preview(row) {
this.$refs.previewDocument.showVersion(row.documentId, row.documentVersion, this.documentName)
},
download(row) {
this.$api.edoc.document.downloadVersion(row.documentId, row.documentVersion)
},
restore(row) {
this.$confirm('该操作将当前版本设置为最新版本,是否继续?', '确认', {
type: 'warning'
})
.then(() => {
this.$api.edoc.document.restore(row.documentId, row.documentVersion).then(() => {
this.loadData()
})
})
.catch(() => {
this.$message.info('已取消')
})
},
close() {
this.visible = false
}
}
}
</script>
开发平台资料
平台名称:一二三应用开发平台
平台简介:企业级通用低代码应用开发平台,免费全开源可商用
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
应用系统资料
应用名称:一二三文档管理系统
应用简介: 企事业单位一站式文档管理系统,让组织内文档管理有序,协作高效、安全可控
设计文档:csdn专栏
开源地址:Gitee
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!