mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
RbPreview
This commit is contained in:
parent
bd47c5b70c
commit
92071ad0e1
|
@ -78,7 +78,8 @@
|
|||
"RbHighbar": true,
|
||||
"ApprovalSubmitForm": true,
|
||||
"ConfigList": true,
|
||||
"ConfigFormDlg": true
|
||||
"ConfigFormDlg": true,
|
||||
"RbPreview": true
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -21,7 +21,8 @@
|
|||
"javascriptreact",
|
||||
"html"
|
||||
],
|
||||
"prettier.eslintIntegration": true
|
||||
"prettier.eslintIntegration": true,
|
||||
"workbench.editor.enablePreview": false
|
||||
}
|
||||
// node and eslint(-g)
|
||||
// Plugins: Beautify and ESLint
|
|
@ -22,6 +22,7 @@ import cn.devezhao.commons.CodecUtils;
|
|||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import com.rebuild.server.helper.QiniuCloud;
|
||||
import com.rebuild.server.helper.SysConfiguration;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.web.BaseControll;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
@ -98,6 +99,13 @@ public class FileDownloader extends BaseControll {
|
|||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "make-url", method = RequestMethod.GET)
|
||||
public void makeUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String filePath = getParameterNotNull(request, "url");
|
||||
String privateUrl = QiniuCloud.instance().url(filePath);
|
||||
writeSuccess(response, JSONUtils.toJSONObject("private_url", privateUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
|
|
|
@ -936,6 +936,7 @@ i.split.ui-draggable-dragging {
|
|||
line-height: 1;
|
||||
cursor: default;
|
||||
height: 30px;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.column-imgs>a img {
|
||||
|
@ -959,7 +960,7 @@ i.split.ui-draggable-dragging {
|
|||
|
||||
.column-files ul>li>a {
|
||||
color: #404040;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.column-checkbox {
|
||||
|
@ -2226,11 +2227,12 @@ form {
|
|||
|
||||
.search-models a {
|
||||
background: #eee;
|
||||
color: #777;
|
||||
color: #888 !important;
|
||||
border-radius: 99px;
|
||||
padding: 4px 9px;
|
||||
max-width: 120px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.search-models a+a {
|
||||
|
@ -2241,4 +2243,110 @@ form {
|
|||
.search-models a.active {
|
||||
background: #4285f4;
|
||||
color: #fff !important
|
||||
}
|
||||
|
||||
.preview-modal {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1099;
|
||||
background-color: rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
.preview-modal .preview-header {
|
||||
background: rgba(0, 0, 0, 1);
|
||||
color: #fff;
|
||||
padding: 0 25px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.preview-modal .preview-header .float-left>h5,
|
||||
.preview-modal .preview-header .float-right a {
|
||||
margin: 0;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.preview-modal .preview-header .float-right a {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.preview-modal .preview-header .float-right a:hover {
|
||||
background: #888;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body,
|
||||
.preview-modal .preview-body>div,
|
||||
.preview-modal .container .iframe,
|
||||
.preview-modal .preview-body iframe {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body iframe {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .img-zoom {
|
||||
text-align: center;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .img-zoom img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .op-box {
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
width: 160px;
|
||||
left: 50%;
|
||||
margin-left: -80px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
height: 50px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
color: rgba(255, 255, 255, .8);
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .op-box a.arrow,
|
||||
.preview-modal .preview-body .op-box span {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .op-box a.arrow {
|
||||
width: 50px;
|
||||
font-size: 21px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .op-box a.arrow:hover {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.preview-modal .preview-body .op-box span {
|
||||
font-size: 1rem;
|
||||
color: #fff;
|
||||
width: 60px;
|
||||
}
|
|
@ -21,7 +21,8 @@ body {
|
|||
.view-header {
|
||||
padding: 15px 20px;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #e3e3e3;
|
||||
height: 61px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
|
@ -328,10 +328,10 @@ CellRenders.addRender('IMAGE', function (v, s, k) {
|
|||
v = JSON.parse(v || '[]')
|
||||
return <td key={k} className="td-min">
|
||||
<div style={s} className="column-imgs" title={v.length + ' 个图片'}>
|
||||
{v.map((item) => {
|
||||
{v.map((item, idx) => {
|
||||
let imgUrl = rb.baseUrl + '/filex/img/' + item
|
||||
let imgName = $fileCutName(item)
|
||||
return <a key={'k-' + item} href={'#!/Preview/' + item} title={imgName}><img src={imgUrl + '?imageView2/2/w/100/interlace/1/q/100'} /></a>
|
||||
return <a key={'k-' + item} title={imgName} onClick={() => RbPreview.create(v, idx)}><img src={imgUrl + '?imageView2/2/w/100/interlace/1/q/100'} /></a>
|
||||
})}</div></td>
|
||||
})
|
||||
CellRenders.addRender('FILE', function (v, s, k) {
|
||||
|
@ -340,7 +340,7 @@ CellRenders.addRender('FILE', function (v, s, k) {
|
|||
<ul className="list-unstyled" title={v.length + ' 个文件'}>
|
||||
{v.map((item) => {
|
||||
let fileName = $fileCutName(item)
|
||||
return <li key={'k-' + item} className="text-truncate"><a href={'#!/Preview/' + item} title={fileName}>{fileName}</a></li>
|
||||
return <li key={'k-' + item} className="text-truncate"><a title={fileName} onClick={() => RbPreview.create(item)}>{fileName}</a></li>
|
||||
})}</ul>
|
||||
</div></td>
|
||||
})
|
||||
|
|
|
@ -582,14 +582,15 @@ class RbFormImage extends RbFormElement {
|
|||
)
|
||||
}
|
||||
renderViewElement() {
|
||||
if (this.state.value.length === 0) {
|
||||
const value = this.state.value
|
||||
if (value.length === 0) {
|
||||
return <div className="form-control-plaintext"><span className="text-muted">无</span></div>
|
||||
}
|
||||
return (<div className="img-field">
|
||||
{this.state.value.map((item) => {
|
||||
{value.map((item, idx) => {
|
||||
let itemUrl = rb.baseUrl + '/filex/img/' + item
|
||||
let fileName = $fileCutName(item)
|
||||
return <span key={'img-' + item}><a title={fileName} onClick={this.clickPreview.bind(this, itemUrl)} className="img-thumbnail img-upload zoom-in" href={itemUrl} target="_blank" rel="noopener noreferrer"><img src={itemUrl + '?imageView2/2/w/100/interlace/1/q/100'} /></a></span>
|
||||
return <span key={'img-' + item}><a title={fileName} onClick={() => (parent || window).RbPreview.create(value, idx)} className="img-thumbnail img-upload zoom-in"><img src={itemUrl + '?imageView2/2/w/100/interlace/1/q/100'} /></a></span>
|
||||
})}
|
||||
</div>)
|
||||
}
|
||||
|
@ -665,11 +666,9 @@ class RbFormFile extends RbFormImage {
|
|||
}
|
||||
return (<div className="file-field">
|
||||
{this.state.value.map((item) => {
|
||||
let itemUrl = rb.baseUrl + '/filex/download/' + item
|
||||
let fileName = $fileCutName(item)
|
||||
let fileExt = $fileExtName(fileName)
|
||||
return <a key={'file-' + item} title={fileName} onClick={this.clickPreview.bind(this, itemUrl)} className="img-thumbnail" href={itemUrl} target="_blank" rel="noopener noreferrer">
|
||||
<i className="file-icon" data-type={fileExt} /><span>{fileName}</span>
|
||||
return <a key={'file-' + item} title={fileName} onClick={() => (parent || window).RbPreview.create(item)} className="img-thumbnail">
|
||||
<i className="file-icon" data-type={$fileExtName(fileName)} /><span>{fileName}</span>
|
||||
</a>
|
||||
})}
|
||||
</div>)
|
||||
|
@ -1174,4 +1173,116 @@ class DeleteConfirm extends RbAlert {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ~~ 图片/文档预览
|
||||
const TYPE_DOCS = ['.doc', '.docx', '.rtf', '.xsl', '.xslx', '.ppt', '.pptx']
|
||||
const TYPE_IMGS = ['.jpg', '.jpeg', '.gif', '.png', '.bmp']
|
||||
class RbPreview extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { currentIndex: props.currentIndex || 0 }
|
||||
}
|
||||
|
||||
render() {
|
||||
let currentUrl = this.props.urls[this.state.currentIndex]
|
||||
let fileName = $fileCutName(currentUrl)
|
||||
return <React.Fragment>
|
||||
<div className="preview-modal" ref={(c) => this._dlg = c}>
|
||||
<div className="preview-header">
|
||||
<div className="float-left"><h5>{fileName}</h5></div>
|
||||
<div className="float-right">
|
||||
<a target="_blank" rel="noopener noreferrer" href={`${rb.baseUrl}/filex/download/${currentUrl}?attname=${fileName}`}><i className="zmdi zmdi-download"></i></a>
|
||||
<a onClick={this.hide}><i className="zmdi zmdi-close"></i></a>
|
||||
</div>
|
||||
<div className="clearfix"></div>
|
||||
</div>
|
||||
<div className="preview-body" onClick={this.hide}>
|
||||
{this.__isimg(currentUrl) && this.renderImgs()}
|
||||
{this.__isdoc(currentUrl) && this.renderDocs()}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
renderDocs() {
|
||||
return (<div className="container">
|
||||
<div className="iframe" onClick={this.__stopEvent}>
|
||||
<iframe frameBorder="0" scrolling="no" src={this.state.previewUrl || ''}></iframe>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
renderImgs() {
|
||||
return (<React.Fragment>
|
||||
<div className="img-zoom">
|
||||
<div className="must-center" onClick={this.__stopEvent}><img src={`${rb.baseUrl}/filex/img/${this.props.urls[this.state.currentIndex]}`} /></div>
|
||||
</div>
|
||||
{this.props.urls.length > 1 && <div className="op-box">
|
||||
<a className="arrow float-left" onClick={this.__previmg}><i className="zmdi zmdi-chevron-left" /></a>
|
||||
<span>{this.state.currentIndex + 1} / {this.props.urls.length}</span>
|
||||
<a className="arrow float-right" onClick={this.__nextimg}><i className="zmdi zmdi-chevron-right" /></a>
|
||||
</div>
|
||||
}
|
||||
</React.Fragment>)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.__isdoc) {
|
||||
$.get(`${rb.baseUrl}/filex/make-url?url=${this.props.urls[this.state.currentIndex]}`, (res) => {
|
||||
let previewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${$encode(res.data.private_url)}`
|
||||
this.setState({ previewUrl: previewUrl })
|
||||
})
|
||||
}
|
||||
|
||||
this.__modalOpen = $(document.body).hasClass('modal-open')
|
||||
if (!this.__modalOpen) $(document.body).addClass('modal-open')
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (!this.__modalOpen) $(document.body).removeClass('modal-open')
|
||||
}
|
||||
|
||||
__isimg(url) {
|
||||
url = url.toLowerCase()
|
||||
for (let i = 0; i < TYPE_IMGS.length; i++) {
|
||||
if (url.endsWith(TYPE_IMGS[i])) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
__isdoc(url) {
|
||||
url = url.toLowerCase()
|
||||
for (let i = 0; i < TYPE_DOCS.length; i++) {
|
||||
if (url.endsWith(TYPE_DOCS[i])) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
__previmg = (e) => {
|
||||
this.__stopEvent(e)
|
||||
if (this.state.currentIndex <= 0) return
|
||||
this.setState({ currentIndex: this.state.currentIndex - 1 })
|
||||
}
|
||||
__nextimg = (e) => {
|
||||
this.__stopEvent(e)
|
||||
if (this.state.currentIndex + 1 >= this.props.urls.length) return
|
||||
this.setState({ currentIndex: this.state.currentIndex + 1 })
|
||||
}
|
||||
__stopEvent = (e) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
hide = () => {
|
||||
$unmount($(this._dlg).parent(), 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} urls string or array of URL
|
||||
* @param {*} index
|
||||
*/
|
||||
static create(urls, index) {
|
||||
if (!urls) return
|
||||
// if (parent && parent.RbPreview) {
|
||||
// parent.RbPreview.create(urls, index)
|
||||
// return
|
||||
// }
|
||||
if (typeof urls === 'string') urls = [urls]
|
||||
renderRbcomp(<RbPreview urls={urls} currentIndex={index || 0} />)
|
||||
}
|
||||
}
|
|
@ -98,6 +98,7 @@ var __initNavs = function () {
|
|||
e.stopPropagation()
|
||||
currsntSubnav = _this
|
||||
_this.find('a').eq(0).tooltip('hide')
|
||||
$('.left-sidebar-scroll').perfectScrollbar('update')
|
||||
})
|
||||
$('.sidebar-elements li.parent .sub-menu').click(function (e) {
|
||||
e.stopPropagation()
|
||||
|
@ -180,6 +181,7 @@ var __loadMessages = function () {
|
|||
// Global search
|
||||
var __globalSearch = function () {
|
||||
$('.sidebar-elements li').each((idx, item) => {
|
||||
if (idx > 40) return false
|
||||
let id = $(item).attr('id')
|
||||
if (id && id.startsWith('nav_entity-') && id !== 'nav_entity-$PARENT$') {
|
||||
let $a = $(item).find('a')
|
||||
|
|
Loading…
Reference in a new issue