457 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="ready" class="file-content">
<iframe v-if="isPreview" ref="myPreview" class="preview-iframe" :src="previewUrl"></iframe>
<template v-else>
<div v-show="!['word', 'excel', 'ppt'].includes(file.type)" class="edit-header">
<div class="header-title">
<EPopover v-if="!equalContent" v-model="unsaveTip" class="file-unsave-tip">
<div class="task-detail-delete-file-popover">
<p>{{$L('未保存当前修改内容?')}}</p>
<div class="buttons">
<Button size="small" type="text" @click="unSaveGive">{{$L('放弃')}}</Button>
<Button size="small" type="primary" @click="onSaveSave">{{$L('保存')}}</Button>
</div>
</div>
<span slot="reference">[{{$L('未保存')}}*]</span>
</EPopover>
{{formatName(file)}}
</div>
<div class="header-user">
<ul>
<li v-for="(userid, index) in editUser" :key="index" v-if="index <= 10">
<UserAvatar :userid="userid" :size="28" :border-witdh="2"/>
</li>
<li v-if="editUser.length > 10" class="more">{{editUser.length > 99 ? '99+' : editUser.length}}</li>
</ul>
</div>
<div v-if="file.type=='document' && contentDetail" class="header-hint">
<ButtonGroup size="small" shape="circle">
<Button :type="`${contentDetail.type=='md'?'primary':'default'}`" @click="$set(contentDetail, 'type', 'md')">{{$L('MD编辑器')}}</Button>
<Button :type="`${contentDetail.type!='md'?'primary':'default'}`" @click="$set(contentDetail, 'type', 'text')">{{$L('文本编辑器')}}</Button>
</ButtonGroup>
</div>
<div v-if="file.type=='mind'" class="header-hint">
{{$L('选中节点按enter键添加同级节点tab键添加子节点')}}
</div>
<Dropdown v-if="file.type=='mind'"
trigger="click"
class="header-hint"
@on-click="exportMenu">
<a href="javascript:void(0)">{{$L('导出')}}<Icon type="ios-arrow-down"></Icon></a>
<DropdownMenu slot="list">
<DropdownItem name="png">{{$L('导出PNG图片')}}</DropdownItem>
<DropdownItem name="pdf">{{$L('导出PDF文件')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
<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'">
<MDEditor v-if="contentDetail.type=='md'" v-model="contentDetail.content" height="100%"/>
<TEditor v-else v-model="contentDetail.content" height="100%" @editorSave="handleClick('saveBefore')"/>
</template>
<Drawio v-else-if="file.type=='drawio'" ref="myFlow" v-model="contentDetail" :title="file.name" @saveData="handleClick('saveBefore')"/>
<Minder v-else-if="file.type=='mind'" ref="myMind" v-model="contentDetail" @saveData="handleClick('saveBefore')"/>
<AceEditor v-else-if="['code', 'txt'].includes(file.type)" v-model="contentDetail.content" :ext="file.ext" @saveData="handleClick('saveBefore')"/>
<OnlyOffice v-else-if="['word', 'excel', 'ppt'].includes(file.type)" v-model="contentDetail" :documentKey="documentKey"/>
<div v-if="historyShow" class="content-mask"></div>
</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>
<script>
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');
const TEditor = () => import('../../../components/TEditor');
const AceEditor = () => import('../../../components/AceEditor');
const OnlyOffice = () => import('../../../components/OnlyOffice');
const Drawio = () => import('../../../components/Drawio');
export default {
name: "FileContent",
components: {FileHistory, AceEditor, TEditor, MDEditor, OnlyOffice, Drawio},
props: {
value: {
type: Boolean,
default: false
},
file: {
type: Object,
default: () => {
return {};
}
},
},
data() {
return {
ready: false,
loadSave: 0,
loadContent: 0,
unsaveTip: false,
contentDetail: null,
contentBak: {},
editUser: [],
loadPreview: true,
linkShow: false,
linkData: {},
linkLoad: 0,
historyShow: false,
}
},
mounted() {
document.addEventListener('keydown', this.keySave)
window.addEventListener('message', this.handleMessage)
//
if (this.$isSubElectron) {
window.__onBeforeUnload = () => {
if (!this.equalContent) {
$A.modalConfirm({
content: '修改的内容尚未保存,确定要放弃修改吗?',
cancelText: '取消',
okText: '放弃',
onOk: () => {
this.$Electron.sendMessage('windowDestroy');
}
});
return true
}
}
}
},
beforeDestroy() {
document.removeEventListener('keydown', this.keySave)
window.removeEventListener('message', this.handleMessage)
},
watch: {
value: {
handler(val) {
if (val) {
this.ready = true;
this.editUser = [this.userId];
this.getContent();
} else {
this.linkShow = false;
this.historyShow = false;
}
},
immediate: true,
},
wsMsg: {
handler(info) {
const {type, action, data} = info;
switch (type) {
case 'path':
if (data.path == '/single/file/' + this.fileId) {
this.editUser = data.userids;
}
break;
case 'file':
if (action == 'content') {
if (this.value && data.id == this.fileId) {
$A.modalConfirm({
title: "更新提示",
content: '团队成员(' + info.nickname + ')更新了内容,<br/>更新时间:' + $A.formatDate("Y-m-d H:i:s", info.time) + '。<br/><br/>点击【确定】加载最新内容。',
onOk: () => {
this.getContent();
}
});
}
}
break;
}
},
deep: true,
},
},
computed: {
...mapState(['wsMsg', 'userId']),
fileId() {
return this.file.id || 0
},
equalContent() {
return this.contentBak == $A.jsonStringify(this.contentDetail);
},
contentLoad() {
return this.loadContent > 0 || this.previewLoad;
},
isPreview() {
return this.contentDetail && this.contentDetail.preview === true;
},
previewLoad() {
return this.isPreview && this.loadPreview === true;
},
previewUrl() {
if (this.isPreview) {
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.contentDetail.url))
} else {
return '';
}
},
},
methods: {
handleMessage (event) {
const data = event.data;
switch (data.act) {
case 'ready':
this.loadPreview = false;
break
}
},
keySave(e) {
if (this.value && e.keyCode === 83) {
if (e.metaKey || e.ctrlKey) {
e.preventDefault();
this.onSaveSave();
}
}
},
getContent(history_id = 0) {
if (this.fileId === 0) {
this.contentDetail = {};
this.updateBak();
return;
}
if (['word', 'excel', 'ppt'].includes(this.file.type)) {
this.contentDetail = $A.cloneJSON(this.file);
this.updateBak();
return;
}
this.loadSave++;
this.loadContent++;
this.$store.dispatch("call", {
url: 'file/content',
data: {
id: this.fileId,
history_id: history_id
},
}).then(({data}) => {
this.contentDetail = data.content;
if (!history_id) {
this.updateBak();
}
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.loadSave--;
this.loadContent--;
})
},
updateBak() {
this.contentBak = $A.jsonStringify(this.contentDetail);
},
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('没有任何修改!');
}
break;
case "save":
if (this.file.only_view) {
return;
}
this.updateBak();
this.loadSave++;
this.$store.dispatch("call", {
url: 'file/content/save',
method: 'post',
data: {
id: this.fileId,
content: this.contentBak
},
}).then(({data, msg}) => {
$A.messageSuccess(msg);
this.$store.dispatch("saveFile", {
id: this.fileId,
size: data.size,
});
}).catch(({msg}) => {
$A.modalError(msg);
this.getContent();
}).finally(_ => {
this.loadSave--;
})
break;
}
},
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':
this.$refs.myMind.exportHandle(act == 'pdf' ? 1 : 0, this.file.name);
break;
}
},
unSaveGive() {
this.getContent();
this.unsaveTip = false;
},
onSaveSave() {
this.handleClick('save');
this.unsaveTip = false;
},
documentKey() {
return new Promise(resolve => {
this.$store.dispatch("call", {
url: 'file/content',
data: {
id: this.fileId,
only_update_at: 'yes'
},
}).then(({data}) => {
resolve($A.Date(data.update_at, true))
}).catch(() => {
resolve(0)
});
})
},
formatName(file) {
let {name, ext} = file;
if (ext != '') {
name += "." + ext;
}
return name;
},
}
}
</script>