RbPreview

This commit is contained in:
devezhao 2019-08-30 16:51:13 +08:00
parent bd47c5b70c
commit 92071ad0e1
8 changed files with 247 additions and 15 deletions

View file

@ -78,7 +78,8 @@
"RbHighbar": true,
"ApprovalSubmitForm": true,
"ConfigList": true,
"ConfigFormDlg": true
"ConfigFormDlg": true,
"RbPreview": true
},
"rules": {
"strict": 0,

View file

@ -21,7 +21,8 @@
"javascriptreact",
"html"
],
"prettier.eslintIntegration": true
"prettier.eslintIntegration": true,
"workbench.editor.enablePreview": false
}
// node and eslint(-g)
// Plugins: Beautify and ESLint

View file

@ -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));
}
/**
* 文件下载
*

View file

@ -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;
}

View file

@ -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;

View file

@ -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>
})

View file

@ -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} />)
}
}

View file

@ -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')