mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-25 20:08:12 +00:00
feat: 新增查看文件历史版本
This commit is contained in:
parent
ab66b70485
commit
ebb3ec6784
@ -439,6 +439,7 @@ class FileController extends AbstractController
|
|||||||
* @apiParam {String} down 直接下载
|
* @apiParam {String} down 直接下载
|
||||||
* - no: 浏览(默认)
|
* - no: 浏览(默认)
|
||||||
* - yes: 下载(office文件直接下载)
|
* - yes: 下载(office文件直接下载)
|
||||||
|
* @apiParam {Number} [history_id] 读取历史记录ID
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
@ -449,6 +450,7 @@ class FileController extends AbstractController
|
|||||||
$id = Request::input('id');
|
$id = Request::input('id');
|
||||||
$down = Request::input('down', 'no');
|
$down = Request::input('down', 'no');
|
||||||
$only_update_at = Request::input('only_update_at', 'no');
|
$only_update_at = Request::input('only_update_at', 'no');
|
||||||
|
$history_id = intval(Request::input('history_id'));
|
||||||
//
|
//
|
||||||
if (Base::isNumber($id)) {
|
if (Base::isNumber($id)) {
|
||||||
User::auth();
|
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');
|
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. 获取共享信息
|
* @api {get} api/file/share 12. 获取共享信息
|
||||||
*
|
*
|
||||||
|
|||||||
@ -43,7 +43,22 @@
|
|||||||
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
|
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</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"></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"></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>
|
||||||
<div v-if="contentDetail" class="content-body">
|
<div v-if="contentDetail" class="content-body">
|
||||||
<template v-if="file.type=='document'">
|
<template v-if="file.type=='document'">
|
||||||
@ -57,6 +72,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="contentLoad" class="content-load"><Loading/></div>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -64,6 +104,7 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Minder from '../../../components/Minder'
|
import Minder from '../../../components/Minder'
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
|
import FileHistory from "./FileHistory";
|
||||||
Vue.use(Minder)
|
Vue.use(Minder)
|
||||||
|
|
||||||
const MDEditor = () => import('../../../components/MDEditor/index');
|
const MDEditor = () => import('../../../components/MDEditor/index');
|
||||||
@ -74,7 +115,7 @@ const Drawio = () => import('../../../components/Drawio');
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "FileContent",
|
name: "FileContent",
|
||||||
components: {AceEditor, TEditor, MDEditor, OnlyOffice, Drawio},
|
components: {FileHistory, AceEditor, TEditor, MDEditor, OnlyOffice, Drawio},
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -103,6 +144,12 @@ export default {
|
|||||||
editUser: [],
|
editUser: [],
|
||||||
|
|
||||||
loadPreview: true,
|
loadPreview: true,
|
||||||
|
|
||||||
|
linkShow: false,
|
||||||
|
linkData: {},
|
||||||
|
linkLoad: 0,
|
||||||
|
|
||||||
|
historyShow: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -114,7 +161,7 @@ export default {
|
|||||||
window.__onBeforeUnload = () => {
|
window.__onBeforeUnload = () => {
|
||||||
if (!this.equalContent) {
|
if (!this.equalContent) {
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
content: '修改的内容尚未保存,真的要放弃修改吗?',
|
content: '修改的内容尚未保存,确定要放弃修改吗?',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
okText: '放弃',
|
okText: '放弃',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
@ -139,6 +186,9 @@ export default {
|
|||||||
this.ready = true;
|
this.ready = true;
|
||||||
this.editUser = [this.userId];
|
this.editUser = [this.userId];
|
||||||
this.getContent();
|
this.getContent();
|
||||||
|
} else {
|
||||||
|
this.linkShow = false;
|
||||||
|
this.historyShow = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@ -224,7 +274,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getContent() {
|
getContent(history_id = 0) {
|
||||||
if (this.fileId === 0) {
|
if (this.fileId === 0) {
|
||||||
this.contentDetail = {};
|
this.contentDetail = {};
|
||||||
this.updateBak();
|
this.updateBak();
|
||||||
@ -241,10 +291,13 @@ export default {
|
|||||||
url: 'file/content',
|
url: 'file/content',
|
||||||
data: {
|
data: {
|
||||||
id: this.fileId,
|
id: this.fileId,
|
||||||
|
history_id: history_id
|
||||||
},
|
},
|
||||||
}).then(({data}) => {
|
}).then(({data}) => {
|
||||||
this.contentDetail = data.content;
|
this.contentDetail = data.content;
|
||||||
this.updateBak();
|
if (!history_id) {
|
||||||
|
this.updateBak();
|
||||||
|
}
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
}).finally(_ => {
|
}).finally(_ => {
|
||||||
@ -259,13 +312,21 @@ export default {
|
|||||||
|
|
||||||
handleClick(act) {
|
handleClick(act) {
|
||||||
switch (act) {
|
switch (act) {
|
||||||
|
case "link":
|
||||||
|
this.linkData = {
|
||||||
|
id: this.fileId
|
||||||
|
};
|
||||||
|
this.linkShow = true;
|
||||||
|
this.linkGet()
|
||||||
|
break;
|
||||||
|
|
||||||
case "saveBefore":
|
case "saveBefore":
|
||||||
if (!this.equalContent && this.loadSave == 0) {
|
if (!this.equalContent && this.loadSave == 0) {
|
||||||
this.handleClick('save');
|
this.handleClick('save');
|
||||||
} else {
|
} else {
|
||||||
$A.messageWarning('没有任何修改!');
|
$A.messageWarning('没有任何修改!');
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
|
|
||||||
case "save":
|
case "save":
|
||||||
if (this.file.only_view) {
|
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) {
|
exportMenu(act) {
|
||||||
switch (this.file.type) {
|
switch (this.file.type) {
|
||||||
case 'mind':
|
case 'mind':
|
||||||
|
|||||||
169
resources/assets/js/pages/manage/components/FileHistory.vue
Normal file
169
resources/assets/js/pages/manage/components/FileHistory.vue
Normal 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>
|
||||||
@ -1529,7 +1529,7 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
content: '文件尚未保存,是否放弃修改?',
|
content: '修改的内容尚未保存,确定要放弃修改吗?',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
okText: '放弃',
|
okText: '放弃',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
|
|||||||
@ -92,6 +92,25 @@
|
|||||||
font-size: 12px !important;
|
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 {
|
.header-button {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user