feat: 新增查看文件历史版本

This commit is contained in:
kuaifan 2022-04-25 05:04:21 +08:00
parent ab66b70485
commit ebb3ec6784
5 changed files with 345 additions and 8 deletions

View File

@ -439,6 +439,7 @@ class FileController extends AbstractController
* @apiParam {String} down 直接下载
* - no: 浏览(默认)
* - yes: 下载office文件直接下载
* @apiParam {Number} [history_id] 读取历史记录ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@ -449,6 +450,7 @@ class FileController extends AbstractController
$id = Request::input('id');
$down = Request::input('down', 'no');
$only_update_at = Request::input('only_update_at', 'no');
$history_id = intval(Request::input('history_id'));
//
if (Base::isNumber($id)) {
User::auth();
@ -470,7 +472,11 @@ class FileController extends AbstractController
]);
}
//
$content = FileContent::whereFid($file->id)->orderByDesc('id')->first();
$builder = FileContent::whereFid($file->id);
if ($history_id > 0) {
$builder->whereId($history_id);
}
$content = $builder->orderByDesc('id')->first();
return FileContent::formatContent($file, $content?->content, $down == 'yes');
}
@ -767,6 +773,36 @@ class FileController extends AbstractController
});
}
/**
* @api {get} api/file/content/history 08. 获取内容历史
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup file
* @apiName content__history
*
* @apiParam {Number} id 文件ID
*
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:100
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function content__history()
{
$id = Request::input('id');
//
$file = File::permissionFind(intval($id));
//
$data = FileContent::select(['id', 'size', 'userid', 'created_at'])
->whereFid($file->id)
->orderByDesc('id')
->paginate(Base::getPaginate(100, 20));
return Base::retSuccess('success', $data);
}
/**
* @api {get} api/file/share 12. 获取共享信息
*

View File

@ -43,7 +43,22 @@
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
<Button v-if="!file.only_view" :disabled="equalContent" :loading="loadSave > 0" class="header-button" size="small" type="primary" @click="handleClick('save')">{{$L('保存')}}</Button>
<template v-if="!file.only_view">
<div class="header-icons">
<ETooltip :content="$L('文件链接')">
<div class="header-icon" @click="handleClick('link')"><i class="taskfont">&#xe785;</i></div>
</ETooltip>
<EPopover v-model="historyShow" trigger="click">
<div class="file-content-history">
<FileHistory :value="historyShow" :fileId="fileId" @on-select="handleHistory"/>
</div>
<ETooltip slot="reference" :disabled="historyShow" :content="$L('历史版本')">
<div class="header-icon"><i class="taskfont">&#xe71d;</i></div>
</ETooltip>
</EPopover>
</div>
<Button :disabled="equalContent" :loading="loadSave > 0" class="header-button" size="small" type="primary" @click="handleClick('save')">{{$L('保存')}}</Button>
</template>
</div>
<div v-if="contentDetail" class="content-body">
<template v-if="file.type=='document'">
@ -57,6 +72,31 @@
</div>
</template>
<div v-if="contentLoad" class="content-load"><Loading/></div>
<!--文件链接-->
<Modal
v-model="linkShow"
:title="$L('文件链接')"
:mask-closable="false">
<div>
<Input ref="linkInput" v-model="linkData.url" type="textarea" :rows="3" @on-focus="linkFocus" readonly/>
<div class="form-tip" style="padding-top:6px">{{$L('可通过此链接浏览文件。')}}</div>
</div>
<div slot="footer" class="adaption">
<Button type="default" @click="linkShow=false">{{$L('取消')}}</Button>
<Poptip
confirm
placement="bottom"
style="margin-left:8px"
@on-ok="linkGet(true)"
transfer>
<div slot="title">
<p><strong>{{$L('注意:刷新将导致原来的链接失效!')}}</strong></p>
</div>
<Button type="primary" :loading="linkLoad > 0">{{$L('刷新')}}</Button>
</Poptip>
</div>
</Modal>
</div>
</template>
@ -64,6 +104,7 @@
import Vue from 'vue'
import Minder from '../../../components/Minder'
import {mapState} from "vuex";
import FileHistory from "./FileHistory";
Vue.use(Minder)
const MDEditor = () => import('../../../components/MDEditor/index');
@ -74,7 +115,7 @@ const Drawio = () => import('../../../components/Drawio');
export default {
name: "FileContent",
components: {AceEditor, TEditor, MDEditor, OnlyOffice, Drawio},
components: {FileHistory, AceEditor, TEditor, MDEditor, OnlyOffice, Drawio},
props: {
value: {
type: Boolean,
@ -103,6 +144,12 @@ export default {
editUser: [],
loadPreview: true,
linkShow: false,
linkData: {},
linkLoad: 0,
historyShow: false,
}
},
@ -114,7 +161,7 @@ export default {
window.__onBeforeUnload = () => {
if (!this.equalContent) {
$A.modalConfirm({
content: '修改的内容尚未保存,真的要放弃修改吗?',
content: '修改的内容尚未保存,确定要放弃修改吗?',
cancelText: '取消',
okText: '放弃',
onOk: () => {
@ -139,6 +186,9 @@ export default {
this.ready = true;
this.editUser = [this.userId];
this.getContent();
} else {
this.linkShow = false;
this.historyShow = false;
}
},
immediate: true,
@ -224,7 +274,7 @@ export default {
}
},
getContent() {
getContent(history_id = 0) {
if (this.fileId === 0) {
this.contentDetail = {};
this.updateBak();
@ -241,10 +291,13 @@ export default {
url: 'file/content',
data: {
id: this.fileId,
history_id: history_id
},
}).then(({data}) => {
this.contentDetail = data.content;
this.updateBak();
if (!history_id) {
this.updateBak();
}
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
@ -259,13 +312,21 @@ export default {
handleClick(act) {
switch (act) {
case "link":
this.linkData = {
id: this.fileId
};
this.linkShow = true;
this.linkGet()
break;
case "saveBefore":
if (!this.equalContent && this.loadSave == 0) {
this.handleClick('save');
} else {
$A.messageWarning('没有任何修改!');
}
return;
break;
case "save":
if (this.file.only_view) {
@ -296,6 +357,58 @@ export default {
}
},
handleHistory(item) {
this.historyShow = false;
if (!this.equalContent) {
$A.modalConfirm({
content: '修改的内容尚未保存,确定要读取历史记录吗?',
cancelText: '取消',
okText: '确定',
onOk: () => {
this.getContent(item.id)
}
});
} else {
this.getContent(item.id)
}
},
linkGet(refresh) {
this.linkLoad++;
this.$store.dispatch("call", {
url: 'file/link',
data: {
id: this.linkData.id,
refresh: refresh === true ? 'yes' : 'no'
},
}).then(({data}) => {
this.linkData = Object.assign(data, {
id: this.linkData.id
});
this.linkCopy();
}).catch(({msg}) => {
this.linkShow = false
$A.modalError(msg);
}).finally(_ => {
this.linkLoad--;
});
},
linkCopy() {
if (!this.linkData.url) {
return;
}
this.$copyText(this.linkData.url).then(() => {
$A.messageSuccess(this.$L('复制成功!'));
}, () => {
$A.messageError(this.$L('复制失败!'));
});
},
linkFocus() {
this.$refs.linkInput.focus({cursor:'all'});
},
exportMenu(act) {
switch (this.file.type) {
case 'mind':

View File

@ -0,0 +1,169 @@
<template>
<div class="file-history">
<Table
:width="460"
:max-height="windowHeight - 180"
:columns="columns"
:data="list"
:loading="loadIng > 0"
:no-data-text="$L(noText)"
stripe/>
<Page
v-if="total > pageSize"
:total="total"
:current="page"
:page-size="pageSize"
:disabled="loadIng > 0"
:simple="true"
@on-change="setPage"
@on-page-size-change="setPageSize"/>
</div>
</template>
<style lang="scss" scoped>
.file-history {
.ivu-page {
margin-top: 12px;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>
<script>
import {mapState} from "vuex";
export default {
name: "FileHistory",
props: {
value: {
type: Boolean,
default: false
},
fileId: {
type: Number,
default: 0
},
},
data() {
return {
loadIng: 0,
columns: [
{
title: this.$L('日期'),
key: 'created_at',
width: 168,
}, {
title: this.$L('创建人'),
width: 120,
render: (h, {row}) => {
return h('UserAvatar', {
props: {
showName: true,
size: 22,
userid: row.userid,
}
})
}
}, {
title: this.$L('大小'),
key: 'size',
width: 80,
render: (h, {row}) => {
return h('AutoTip', $A.bytesToSize(row.size));
}
}, {
title: this.$L('操作'),
align: 'center',
width: 100,
render: (h, {row, column}) => {
const vNodes = [
h('div', {
style: {
fontSize: '13px',
cursor: 'pointer',
color: '#8bcf70',
},
on: {
'click': () => {
this.$emit('on-select', row)
}
},
}, this.$L('读取')),
];
return h('TableAction', {
props: {
column: column
}
}, vNodes);
}
}
],
list: [],
page: 1,
pageSize: 10,
total: 0,
noText: ''
}
},
mounted() {
},
watch: {
value: {
handler(val) {
if (val) {
this.setPage(1);
}
},
immediate: true,
},
},
computed: {
...mapState(['windowHeight'])
},
methods: {
getLists() {
if (this.fileId === 0) {
return;
}
this.loadIng++;
this.$store.dispatch("call", {
url: 'file/content/history',
data: {
id: this.fileId,
page: Math.max(this.page, 1),
pagesize: Math.max($A.runNum(this.pageSize), 10),
},
}).then(({data}) => {
this.page = data.current_page;
this.total = data.total;
this.list = data.data;
this.noText = '没有相关的数据';
}).catch(() => {
this.noText = '数据加载失败';
}).finally(_ => {
this.loadIng--;
})
},
setPage(page) {
this.page = page;
this.getLists();
},
setPageSize(pageSize) {
this.page = 1;
this.pageSize = pageSize;
this.getLists();
},
}
}
</script>

View File

@ -1529,7 +1529,7 @@ export default {
return
}
$A.modalConfirm({
content: '文件尚未保存,是否放弃修改',
content: '修改的内容尚未保存,确定要放弃修改吗',
cancelText: '取消',
okText: '放弃',
onOk: () => {

View File

@ -92,6 +92,25 @@
font-size: 12px !important;
}
}
.header-icons {
margin-left: -4px;
margin-right: 16px;
display: flex;
align-items: center;
justify-content: center;
.header-icon {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 100%;
color: #777777;
> i {
font-size: 20px;
}
}
}
.header-button {
font-size: 12px;
margin-right: 24px;