perf: 优化文件历史记录展示
This commit is contained in:
parent
388cf75633
commit
34a21327a2
@ -471,4 +471,13 @@ export default {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 获取office文件历史
|
||||||
|
getOfficeHistoryList: function(params) {
|
||||||
|
return request({
|
||||||
|
url: '/history/office/list',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,223 +1,468 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<el-popover
|
||||||
<el-popover
|
ref="historyPopover"
|
||||||
v-if="hasHistoryVersion"
|
:value="historyListPopoverVisible"
|
||||||
style="margin-right: 15px"
|
style="margin-right: 15px"
|
||||||
:visible-arrow="false"
|
:visible-arrow="false"
|
||||||
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' history-popover'"
|
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' version-timeline-popover'"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@show="popoverShow"
|
width="400"
|
||||||
@hide="popoverHide"
|
trigger="manual"
|
||||||
v-model="historyListPopoverVisible"
|
@show="popoverShow"
|
||||||
trigger="manual">
|
@hide="popoverHide"
|
||||||
<el-table :data="fileHistoryList" height="300" row-class-name="file-history-row" :class="lightTheme? 'file-history-list' : 'dark-file-history-list'">
|
>
|
||||||
<el-table-column show-overflow-tooltip width="200" property="metadata.filename" label="文件名"></el-table-column>
|
<div :class="lightTheme ? 'version-history-timeline' : 'dark-version-history-timeline'">
|
||||||
<el-table-column width="190" property="metadata.time" label="版本日期"></el-table-column>
|
<div class="scrollbar-wrapper" @scroll="handleScroll">
|
||||||
<el-table-column label="操作" width="210">
|
<div class="timeline-container-scrollbar">
|
||||||
<template slot-scope="scope">
|
<div v-if="loading && fileHistoryList.length === 0" class="timeline-loading">加载中...</div>
|
||||||
<div style="display: flex">
|
<div v-if="!loading && fileHistoryList.length === 0 && finished" class="timeline-empty">暂无历史版本</div>
|
||||||
<el-button round
|
<div v-for="(item, index) in fileHistoryList" :key="item.id" class="timeline-item">
|
||||||
size="mini"
|
<div class="timeline-icon">
|
||||||
:class="lightTheme?'':'dark-button'"
|
<el-avatar :src="imageUrl + item.metadata.operator" size="medium" icon="el-icon-user-solid"></el-avatar>
|
||||||
@click="viewHistoryFile(scope.row, true, false)" :loading="historyOperationLoading">预览</el-button>
|
|
||||||
<el-popover
|
|
||||||
placement="bottom"
|
|
||||||
width="310"
|
|
||||||
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' delete-' + scope.row.id"
|
|
||||||
style="margin: 0 10px;"
|
|
||||||
:visible-arrow="false"
|
|
||||||
trigger="click">
|
|
||||||
<p class="el-popconfirm__main">
|
|
||||||
<i class="el-popconfirm__icon el-icon-question" style="color: rgb(255, 153, 0);"></i>
|
|
||||||
{{'确定要恢复到'+ scope.row.metadata.time +'吗?当前内容将被覆盖!'}}
|
|
||||||
</p>
|
|
||||||
<div style="text-align: right; margin: 0">
|
|
||||||
<el-button round size="mini" type="text" @click="cancelRecovery(scope.row.id)">取消</el-button>
|
|
||||||
<el-button round type="primary" size="mini" @click="confirmRecoveryHistoryFile(scope.row)">确定</el-button>
|
|
||||||
</div>
|
|
||||||
<el-button round slot="reference" size="mini" :id="'recoveryVersionBtn-'+scope.row.id" type="warning" :loading="historyOperationLoading">恢复</el-button>
|
|
||||||
</el-popover>
|
|
||||||
<el-popover
|
|
||||||
placement="bottom"
|
|
||||||
width="310"
|
|
||||||
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' delete-' + scope.row.id"
|
|
||||||
:visible-arrow="false"
|
|
||||||
trigger="click">
|
|
||||||
<p class="el-popconfirm__main">
|
|
||||||
<i class="el-popconfirm__icon el-icon-question" style="color: rgb(255, 153, 0);"></i>
|
|
||||||
{{'确定要删除'+ scope.row.metadata.time +'吗?这将永久删除该历史版本!'}}
|
|
||||||
</p>
|
|
||||||
<div style="text-align: right; margin: 0">
|
|
||||||
<el-button round size="mini" type="text" @click="cancelDelete(scope.row.id)">取消</el-button>
|
|
||||||
<el-button round type="primary" size="mini" @click="confirmDeleteHistoryFile(scope.row.id)">确定</el-button>
|
|
||||||
</div>
|
|
||||||
<el-button round slot="reference" size="mini" :id="'deleteVersionBtn-'+scope.row.id" type="danger" :loading="historyOperationLoading">删除</el-button>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div class="timeline-line" v-if="index !== fileHistoryList.length - 1"></div>
|
||||||
</el-table-column>
|
<div class="timeline-content">
|
||||||
</el-table>
|
<div class="timeline-header">
|
||||||
|
<span class="timeline-date">{{ item.metadata.time }}</span>
|
||||||
|
<span class="version-filename" :title="item.metadata.filename">{{ item.metadata.filename }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-second-line">
|
||||||
|
<div><span class="timeline-operator">{{ item.metadata.operator }}</span></div>
|
||||||
|
<div class="timeline-actions">
|
||||||
|
<el-button round size="mini" :class="lightTheme ? '' : 'dark-button'"
|
||||||
|
@click="viewHistoryFile(item, true, false)" :loading="historyOperationLoading">预览
|
||||||
|
</el-button>
|
||||||
|
|
||||||
<el-button round slot="reference" :style="versionBtnStyle" ref="versionBtn" :size="buttonSize" :class="lightTheme?' history-version-btn':'dark-button history-version-btn'" @click="showOrHidePopover">历史版本</el-button>
|
<el-popover placement="bottom" width="310"
|
||||||
</el-popover>
|
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' recovery-' + item.id"
|
||||||
</div>
|
:visible-arrow="false" trigger="click">
|
||||||
|
<p class="el-popconfirm__main"><i class="el-popconfirm__icon el-icon-question"
|
||||||
|
style="color: rgb(255, 153, 0);"></i>{{ '确定要恢复到' + item.metadata.time + '吗?当前内容将被覆盖!'
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div style="text-align: right; margin: 0">
|
||||||
|
<el-button round size="mini" type="text" @click="cancelRecovery(item.id)">取消</el-button>
|
||||||
|
<el-button round type="primary" size="mini" @click="confirmRecoveryHistoryFile(item)">确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-button round slot="reference" size="mini" :id="'recoveryVersionBtn-'+item.id" type="warning"
|
||||||
|
:loading="historyOperationLoading">恢复
|
||||||
|
</el-button>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<el-popover placement="bottom" width="310"
|
||||||
|
:popper-class="(lightTheme ? 'file-history-popover' : 'dark-file-history-popover') + ' delete-' + item.id"
|
||||||
|
:visible-arrow="false" trigger="click">
|
||||||
|
<p class="el-popconfirm__main"><i class="el-popconfirm__icon el-icon-question"
|
||||||
|
style="color: rgb(255, 153, 0);"></i>{{ '确定要删除' + item.metadata.time + '吗?这将永久删除该历史版本!'
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div style="text-align: right; margin: 0">
|
||||||
|
<el-button round size="mini" type="text" @click="cancelDelete(item.id)">取消</el-button>
|
||||||
|
<el-button round type="primary" size="mini" @click="confirmDeleteHistoryFile(item.id)">确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-button round slot="reference" size="mini" :id="'deleteVersionBtn-'+item.id" type="danger"
|
||||||
|
:loading="historyOperationLoading">删除
|
||||||
|
</el-button>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="loading && fileHistoryList.length > 0" class="timeline-loading-more">加载中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
slot="reference"
|
||||||
|
round
|
||||||
|
:style="versionBtnStyle"
|
||||||
|
:size="buttonSize"
|
||||||
|
:class="lightTheme ? ' history-version-btn' : 'dark-button history-version-btn'"
|
||||||
|
@click="showOrHidePopover"
|
||||||
|
>
|
||||||
|
历史版本
|
||||||
|
</el-button>
|
||||||
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import historyApi from "@/api/file-history"
|
import historyApi from '@/api/file-history'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "history-popover",
|
name: 'history-popover',
|
||||||
props: {
|
props: {
|
||||||
hasHistoryVersion: {
|
hasHistoryVersion: {
|
||||||
type: Object,
|
type: Object, // 注意:这里是 Object,虽然通常用 Boolean,但为保持兼容性,维持原样
|
||||||
default: function () {
|
default: function() {
|
||||||
return {metadata: {}}
|
return { metadata: {} }
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
lightTheme: {
|
lightTheme: { type: Boolean, default: true },
|
||||||
type: Boolean,
|
historyListPopoverVisible: { type: Boolean, default: false },
|
||||||
default: true
|
historyOperationLoading: { type: Boolean, default: false },
|
||||||
},
|
saved: { type: Boolean, default: true },
|
||||||
historyListPopoverVisible: {
|
buttonSize: { type: String, default: 'mini' },
|
||||||
type: Boolean,
|
transparent: { type: Boolean, default: false },
|
||||||
default: false
|
},
|
||||||
},
|
data() {
|
||||||
historyOperationLoading: {
|
return {
|
||||||
type: Boolean,
|
imageUrl: `${process.env.VUE_APP_BASE_API}/view/thumbnail/user/`,
|
||||||
default: false
|
fileHistoryList: [],
|
||||||
},
|
loading: false,
|
||||||
saved: {
|
finished: false,
|
||||||
type: Boolean,
|
historyPageIndex: 1,
|
||||||
default: true
|
historyPageSize: 20,
|
||||||
},
|
loadApiFunction: null,
|
||||||
buttonSize: {
|
loadApiParams: {},
|
||||||
type: String,
|
|
||||||
default: 'mini'
|
|
||||||
},
|
|
||||||
transparent: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
versionBtnStyle() {
|
versionBtnStyle() {
|
||||||
if (this.transparent) {
|
if (this.transparent) {
|
||||||
return 'background: #00000000;border: 0;'
|
return 'background: #00000000;border: 0;'
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
}
|
return ''
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fileHistoryList: [],
|
|
||||||
recoveryHistoryListenerMap: new Map(),
|
|
||||||
deleteHistoryListenerMap: new Map(),
|
|
||||||
historyPageIndex: 1,
|
|
||||||
historyPageSize: 50
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
historyListPopoverVisible(value) {
|
historyListPopoverVisible(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
document.getElementById('app').addEventListener('click', this.onGlobalClick)
|
document.getElementById('app').addEventListener('click', this.onGlobalClick)
|
||||||
} else {
|
} else {
|
||||||
// destroy elementListeners
|
|
||||||
document.getElementById('app').removeEventListener('click', this.onGlobalClick)
|
document.getElementById('app').removeEventListener('click', this.onGlobalClick)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
loadHistoryList(id) {
|
loadHistoryList(id) {
|
||||||
if (!this.$store.state.user.token || !this.$store.state.user.name) {
|
if (!this.$store.state.user.token || !this.$store.state.user.name) return
|
||||||
return
|
this.resetState()
|
||||||
|
this.loadApiFunction = historyApi.fileHistoryList
|
||||||
|
this.loadApiParams = { fileId: id, username: this.$store.state.user.name }
|
||||||
|
this._internalLoadHistory()
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
loadHistoryPathList(pathname) {
|
||||||
|
if (!this.$store.getters.token) return
|
||||||
|
this.resetState()
|
||||||
|
this.loadApiFunction = historyApi.fileHistoryPathList
|
||||||
|
this.loadApiParams = { path: encodeURI(pathname), username: this.$store.state.user.name }
|
||||||
|
this._internalLoadHistory()
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- 内部方法 ---
|
||||||
|
|
||||||
|
_internalLoadHistory(isLoadMore = false) {
|
||||||
|
if (this.loading || (isLoadMore && this.finished)) return
|
||||||
|
|
||||||
|
if (!isLoadMore) {
|
||||||
|
this.historyPageIndex = 1
|
||||||
|
this.fileHistoryList = []
|
||||||
|
} else {
|
||||||
|
this.historyPageIndex++
|
||||||
}
|
}
|
||||||
historyApi.fileHistoryList({fileId: id, username: this.$store.state.user.name, pageIndex: this.historyPageIndex, pageSize: this.historyPageSize}).then((res) => {
|
|
||||||
if (res.count && res.count > 0) {
|
this.loading = true
|
||||||
this.fileHistoryList = res.data
|
const params = {
|
||||||
this.$emit('update:hasHistoryVersion', true)
|
...this.loadApiParams,
|
||||||
} else {
|
pageIndex: this.historyPageIndex,
|
||||||
this.$emit('update:hasHistoryVersion', false)
|
pageSize: this.historyPageSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadApiFunction(params).then(res => {
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
this.fileHistoryList.push(...res.data)
|
||||||
}
|
}
|
||||||
|
this.finished = this.fileHistoryList.length >= res.count
|
||||||
|
|
||||||
|
if (this.loadApiFunction === historyApi.fileHistoryList) {
|
||||||
|
this.$emit('update:hasHistoryVersion', res.count > 0)
|
||||||
|
} else {
|
||||||
|
this.$emit('loadHistoryPathListSuccess', { res, pathname: this.loadApiParams.path })
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('加载历史版本失败', err)
|
||||||
|
if (isLoadMore) this.historyPageIndex--
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadHistoryPathList(pathname) {
|
|
||||||
if (!this.$store.getters.token) {
|
resetState() {
|
||||||
return
|
this.fileHistoryList = []
|
||||||
|
this.historyPageIndex = 1
|
||||||
|
this.finished = false
|
||||||
|
this.loading = false
|
||||||
|
const scrollWrapper = this.$el ? this.$el.querySelector('.scrollbar-wrapper') : null
|
||||||
|
if (scrollWrapper) scrollWrapper.scrollTop = 0
|
||||||
|
},
|
||||||
|
|
||||||
|
handleScroll(e) {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||||
|
if (scrollHeight - scrollTop - clientHeight < 100) {
|
||||||
|
this._internalLoadHistory(true)
|
||||||
}
|
}
|
||||||
historyApi.fileHistoryPathList({path: encodeURI(pathname), username: this.$store.state.user.name, pageIndex: this.historyPageIndex, pageSize: this.historyPageSize}).then((res) => {
|
|
||||||
this.fileHistoryList = res.data
|
|
||||||
this.$emit('loadHistoryPathListSuccess', {res: res, pathname: pathname})
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
onGlobalClick(event) {
|
onGlobalClick(event) {
|
||||||
let eventClass = event.target.getAttribute('class') || event.target.parentElement.getAttribute('class')
|
let eventClass = event.target.getAttribute('class') || (event.target.parentElement ? event.target.parentElement.getAttribute('class') : '')
|
||||||
if (eventClass && eventClass.indexOf('history-version-btn') > -1) {
|
if (eventClass && eventClass.includes('history-version-btn')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.historyListPopoverVisible) {
|
if (this.historyListPopoverVisible) {
|
||||||
this.$emit('update:historyListPopoverVisible', false)
|
this.$emit('update:historyListPopoverVisible', false)
|
||||||
}
|
}
|
||||||
if (event.target.getAttribute("data-tab") === "file") {
|
// 这段逻辑非常特定,必须保留
|
||||||
|
if (event.target.getAttribute('data-tab') === 'file') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let parentDoc = document.querySelector('.component-only-office')
|
let parentDoc = document.querySelector('.component-only-office')
|
||||||
let doc = parentDoc.getElementsByTagName('iframe')[0].contentWindow.document
|
if (parentDoc) {
|
||||||
doc.getElementById('fm-btn-history').style.display = 'none'
|
let doc = parentDoc.getElementsByTagName('iframe')[0].contentWindow.document
|
||||||
|
let btn = doc.getElementById('fm-btn-history')
|
||||||
|
if (btn) btn.style.display = 'none'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
popoverShow() {
|
popoverShow() {
|
||||||
this.$emit('update:historyListPopoverVisible', true)
|
this.$emit('update:historyListPopoverVisible', true)
|
||||||
},
|
},
|
||||||
showOrHidePopover() {
|
|
||||||
if (this.historyListPopoverVisible) {
|
|
||||||
this.$emit('update:historyListPopoverVisible', false)
|
|
||||||
} else {
|
|
||||||
this.$emit('update:historyListPopoverVisible', true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
popoverHide() {
|
popoverHide() {
|
||||||
this.$emit('update:historyListPopoverVisible', false)
|
this.$emit('update:historyListPopoverVisible', false)
|
||||||
},
|
},
|
||||||
viewHistoryFile(historyInfo, diff, recovery) {
|
showOrHidePopover() {
|
||||||
this.$emit('viewHistoryFile', {historyInfo: historyInfo, diff: diff, recovery: recovery})
|
this.$emit('update:historyListPopoverVisible', !this.historyListPopoverVisible)
|
||||||
},
|
},
|
||||||
|
viewHistoryFile(historyInfo, diff, recovery) {
|
||||||
|
this.$emit('viewHistoryFile', { historyInfo, diff, recovery })
|
||||||
|
},
|
||||||
|
|
||||||
cancelDelete(id) {
|
cancelDelete(id) {
|
||||||
document.getElementById('deleteVersionBtn-'+id).click()
|
document.getElementById('deleteVersionBtn-' + id).click()
|
||||||
},
|
},
|
||||||
cancelRecovery(id) {
|
cancelRecovery(id) {
|
||||||
document.getElementById('recoveryVersionBtn-'+id).click()
|
document.getElementById('recoveryVersionBtn-' + id).click()
|
||||||
},
|
},
|
||||||
|
|
||||||
confirmRecoveryHistoryFile(historyInfo) {
|
confirmRecoveryHistoryFile(historyInfo) {
|
||||||
if (!this.saved) {
|
if (!this.saved) {
|
||||||
this.$message({type: 'info', message: "请先保存当前修改的内容"})
|
this.$message({ type: 'info', message: '请先保存当前修改的内容' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.cancelRecovery(historyInfo.id)
|
this.cancelRecovery(historyInfo.id)
|
||||||
this.$emit('update:historyOperationLoading', true)
|
this.$emit('update:historyOperationLoading', true)
|
||||||
historyApi.recoveryHistory({id: historyInfo.id}).then((res) => {
|
historyApi.recoveryHistory({ id: historyInfo.id }).then((res) => {
|
||||||
this.$emit('recoverySuccess', {historyInfo: historyInfo, result: res})
|
this.$emit('recoverySuccess', { historyInfo, result: res })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
confirmDeleteHistoryFile(id) {
|
confirmDeleteHistoryFile(id) {
|
||||||
this.cancelDelete(id)
|
this.cancelDelete(id)
|
||||||
this.$emit('update:historyOperationLoading', true)
|
this.$emit('update:historyOperationLoading', true)
|
||||||
historyApi.deleteHistory({id: id}).then(() => {
|
historyApi.deleteHistory({ id: id }).then(() => {
|
||||||
this.$emit('update:historyOperationLoading', false)
|
this.$emit('update:historyOperationLoading', false)
|
||||||
this.$message({message: '删除成功',type: 'success'})
|
this.$message({ message: '删除成功', type: 'success' })
|
||||||
let deleteIndex = this.fileHistoryList.findIndex(fileHistory => fileHistory.id === id)
|
let deleteIndex = this.fileHistoryList.findIndex(fileHistory => fileHistory.id === id)
|
||||||
this.fileHistoryList.splice(deleteIndex, 1)
|
this.fileHistoryList.splice(deleteIndex, 1)
|
||||||
if (this.fileHistoryList.length < 1) {
|
if (this.fileHistoryList.length < 1) {
|
||||||
this.$emit('update:hasHistoryVersion', false)
|
this.$emit('update:hasHistoryVersion', false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style>
|
||||||
|
/* 非 Scoped 样式 */
|
||||||
|
.file-history-popover {
|
||||||
|
padding: 0 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background-color: rgba(255, 255, 255, .75);
|
||||||
|
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
|
||||||
|
.el-popconfirm {
|
||||||
|
width: 310px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-file-history-popover {
|
||||||
|
background-color: rgba(46, 46, 46, .75);
|
||||||
|
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
color: #b7b7b7;
|
||||||
|
border: 1px solid #2e2e30;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.version-history-timeline {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-version-history-timeline {
|
||||||
|
.timeline-date {
|
||||||
|
color: #b7b7b7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-icon {
|
||||||
|
background: #3e3e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-operator {
|
||||||
|
color: #b7b7b7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-filename {
|
||||||
|
color: #b7b7b7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-line {
|
||||||
|
background: #3e3e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-button {
|
||||||
|
background: #3e3e3e;
|
||||||
|
border: 1px solid #3e3e3e;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-button:hover {
|
||||||
|
color: #409EFF;
|
||||||
|
background-color: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-avatar {
|
||||||
|
background: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-wrapper {
|
||||||
|
height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-wrapper::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-wrapper::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(180, 180, 180, 0.48);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-container-scrollbar {
|
||||||
|
padding: 10px 15px 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-icon {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-line {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
top: 45px;
|
||||||
|
bottom: -35px;
|
||||||
|
width: 1px;
|
||||||
|
background-color: #e1e4e8;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
margin-left: 16px;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-date {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #24292e;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-filename {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #586069;
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-width: 120px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-second-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-operator {
|
||||||
|
color: #586069;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-actions .el-button {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-loading, .timeline-empty, .timeline-loading-more {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
padding: 20px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-popconfirm__main {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -236,9 +236,9 @@ export default {
|
|||||||
.timeline-line {
|
.timeline-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 20px; /* (Avatar宽度 / 2) */
|
left: 20px; /* (Avatar宽度 / 2) */
|
||||||
top: 45px; /* 大约是 Avatar 下方开始 */
|
top: 50px; /* 大约是 Avatar 下方开始 */
|
||||||
/* bottom: 0; */ /* 不再使用 bottom */
|
/* bottom: 0; */ /* 不再使用 bottom */
|
||||||
height: calc(100% - 40px); /* 从 Avatar 下方到底部 */
|
height: calc(100% - 30px); /* 从 Avatar 下方到底部 */
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background-color: #e1e4e8;
|
background-color: #e1e4e8;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@ -211,22 +211,43 @@ export default {
|
|||||||
this.docEditorConfig.document.key = key ? key : this.fileKey
|
this.docEditorConfig.document.key = key ? key : this.fileKey
|
||||||
this.docEditor = new DocsAPI.DocEditor(this.id, this.docEditorConfig)
|
this.docEditor = new DocsAPI.DocEditor(this.id, this.docEditorConfig)
|
||||||
},
|
},
|
||||||
onRequestHistoryData(event, historyInfo, url) {
|
onRequestHistoryData(event, historyInfo, historyUrl) {
|
||||||
let historyConfig = {
|
let historyConfig;
|
||||||
"fileType": this.fileType,
|
if (historyInfo && historyUrl){
|
||||||
"key": `${new Date(historyInfo.metadata.time).getTime()}-${SparkMD5.hash(this.file.id)}`,
|
historyConfig = {
|
||||||
"url": url,
|
"fileType": this.fileType,
|
||||||
"version": historyInfo.metadata.time,
|
"key": `${new Date(historyInfo.metadata.time).getTime()}-${SparkMD5.hash(this.file.id)}`,
|
||||||
|
"url": historyUrl,
|
||||||
|
"version": historyInfo.metadata.time,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
historyInfo = this.fileHistoryDateList.find(historyInfo => historyInfo.version === event.data)
|
||||||
|
console.log('historyInfo', historyInfo)
|
||||||
|
historyUrl = window.location.origin + fileConfig.previewHistoryUrl(historyInfo.key, this.$store.state.user.name, this.$store.state.user.token)
|
||||||
|
historyConfig = {
|
||||||
|
"fileType": this.fileType,
|
||||||
|
"key": historyInfo.key,
|
||||||
|
"url": historyUrl,
|
||||||
|
"version": historyInfo.version,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
api.getOfficeJwt(historyConfig).then(res => {
|
api.getOfficeJwt(historyConfig).then(res => {
|
||||||
historyConfig.token = res.data
|
historyConfig.token = res.data
|
||||||
this.docEditor.setHistoryData(historyConfig)
|
this.docEditor.setHistoryData(historyConfig)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
onRequestHistoryClose() {
|
||||||
|
console.log('onRequestHistoryClose')
|
||||||
|
this.reloadDocument()
|
||||||
|
},
|
||||||
onRequestHistory() {
|
onRequestHistory() {
|
||||||
this.docEditor.refreshHistory({
|
api.getOfficeHistoryList({fileId: this.file.id, pageSize: 100, pageIndex: 1}).then(res => {
|
||||||
"currentVersion": 'currentVersion',
|
const currentVersion = res.data.length + 1
|
||||||
"history": []
|
this.fileHistoryDateList = res.data
|
||||||
|
this.docEditor.refreshHistory({
|
||||||
|
"currentVersion": currentVersion,
|
||||||
|
"history": this.fileHistoryDateList
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getType(type) {
|
getType(type) {
|
||||||
@ -345,6 +366,7 @@ export default {
|
|||||||
"onDocumentStateChange": this.onDocumentStateChange,
|
"onDocumentStateChange": this.onDocumentStateChange,
|
||||||
"onRequestHistory": this.onRequestHistory,
|
"onRequestHistory": this.onRequestHistory,
|
||||||
"onRequestHistoryData": this.onRequestHistoryData,
|
"onRequestHistoryData": this.onRequestHistoryData,
|
||||||
|
"onRequestHistoryClose": this.onRequestHistoryClose,
|
||||||
}
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.titleObserver = null
|
this.titleObserver = null
|
||||||
|
|||||||
@ -83,6 +83,9 @@
|
|||||||
<el-form-item label="位置:" class="details-position">
|
<el-form-item label="位置:" class="details-position">
|
||||||
<span><a :href="pathUrl" v-tooltip="{content: '文件所在位置', placement: 'bottom'}">{{ filepath }}</a></span>
|
<span><a :href="pathUrl" v-tooltip="{content: '文件所在位置', placement: 'bottom'}">{{ filepath }}</a></span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="SHA265:">
|
||||||
|
<span> {{ file.etag }}</span>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="上传时间:">
|
<el-form-item label="上传时间:">
|
||||||
<span>{{ file.uploadDate }}</span>
|
<span>{{ file.uploadDate }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|||||||
@ -376,48 +376,6 @@ input {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-history-popover {
|
|
||||||
.el-popconfirm {
|
|
||||||
width: 310px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dark-file-history-popover {
|
|
||||||
background-color: #202021;
|
|
||||||
color: #b7b7b7;
|
|
||||||
border: 1px solid #2e2e30;
|
|
||||||
.el-popconfirm {
|
|
||||||
width: 310px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-file-history-list.el-table--enable-row-hover {
|
|
||||||
.el-table__body tr:hover>td {
|
|
||||||
background-color: #2e2e30;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-file-history-list.el-table {
|
|
||||||
background-color: #202021;
|
|
||||||
tr,th,td {
|
|
||||||
background-color: #202021;
|
|
||||||
color: #b7b7b7;
|
|
||||||
border-bottom: 1px solid #3e3e3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-button {
|
|
||||||
background: #3e3e3e;
|
|
||||||
border: 1px solid #3e3e3e;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
.dark-button:hover {
|
|
||||||
color: #409EFF;
|
|
||||||
background-color: #181818;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dark-file-history-list.el-table::before {
|
|
||||||
background-color: #3e3e3e;
|
|
||||||
}
|
|
||||||
kbd {
|
kbd {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user