perf: 聊天消息附件支持预览
This commit is contained in:
parent
08372facd7
commit
705d7f3da0
@ -329,6 +329,56 @@ class DialogController extends AbstractController
|
||||
return Base::retSuccess('success', $read ?: []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/detail 08. 消息详情
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__detail
|
||||
*
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__detail()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$msg_id = intval(Request::input('msg_id'));
|
||||
//
|
||||
$dialogMsg = WebSocketDialogMsg::whereId($msg_id)->first();
|
||||
if (empty($dialogMsg)) {
|
||||
return Base::retError("文件不存在");
|
||||
}
|
||||
$data = $dialogMsg->toArray();
|
||||
//
|
||||
if ($data['type'] == 'file') {
|
||||
$codeExt = ['txt'];
|
||||
$fillExt = ['jpg', 'jpeg', 'png', 'gif'];
|
||||
$msg = Base::json2array($dialogMsg->getRawOriginal('msg'));
|
||||
$filePath = public_path($msg['path']);
|
||||
if (in_array($msg['ext'], $codeExt) && $msg['size'] < 2 * 1024 * 1024) {
|
||||
// 文本代码,限制2M内的文件
|
||||
$data['content'] = file_get_contents($filePath);
|
||||
$data['file_mode'] = 1;
|
||||
} else {
|
||||
// 支持预览
|
||||
if (in_array($msg['ext'], $fillExt)) {
|
||||
$url = Base::fillUrl($msg['path']);
|
||||
} else {
|
||||
$url = 'http://' . env('APP_IPPR') . '.3/' . $msg['path'];
|
||||
}
|
||||
$data['url'] = base64_encode($url);
|
||||
$data['file_mode'] = 2;
|
||||
}
|
||||
}
|
||||
//
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/download 08. 文件下载
|
||||
*
|
||||
|
@ -64,7 +64,7 @@
|
||||
"stylus-loader": "^6.2.0",
|
||||
"tinymce": "^5.10.2",
|
||||
"tui-calendar-hi": "^1.15.1-5",
|
||||
"view-design-hi": "^4.7.0-8",
|
||||
"view-design-hi": "^4.7.0-10",
|
||||
"vue": "^2.6.14",
|
||||
"vue-clipboard2": "^0.3.3",
|
||||
"vue-emoji-picker": "^1.0.3",
|
||||
|
2
public/css/iview.css
vendored
2
public/css/iview.css
vendored
File diff suppressed because one or more lines are too long
6
resources/assets/js/app.js
vendored
6
resources/assets/js/app.js
vendored
@ -11,7 +11,11 @@ import Language from './language/index'
|
||||
import store from './store/index'
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(ViewUI);
|
||||
Vue.use(ViewUI, {
|
||||
modal: {
|
||||
checkEscClose: true
|
||||
}
|
||||
});
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(Language);
|
||||
|
||||
|
@ -1,16 +1,18 @@
|
||||
<template>
|
||||
<div class="dialog-view" :data-id="msgData.id">
|
||||
<div :class="`dialog-view ${msgData.type}`" :data-id="msgData.id">
|
||||
|
||||
<!--文本-->
|
||||
<div v-if="msgData.type === 'text'" class="dialog-content">
|
||||
<pre class="no-dark-mode" v-html="textMsg(msgData.msg.text)"></pre>
|
||||
<pre class="no-dark-mode">{{textMsg(msgData.msg.text)}}</pre>
|
||||
</div>
|
||||
|
||||
<!--等待-->
|
||||
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
|
||||
|
||||
<!--文件-->
|
||||
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
|
||||
<div class="dialog-file" @click="downFile">
|
||||
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb"/>
|
||||
<div class="dialog-file">
|
||||
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb" @click="viewFile"/>
|
||||
<div v-else class="file-box">
|
||||
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
||||
<div class="file-info">
|
||||
@ -19,7 +21,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-file-menu">
|
||||
<div class="file-menu-warp"></div>
|
||||
<div class="file-menu-icon">
|
||||
<Icon @click="viewFile" type="md-eye" />
|
||||
<Icon @click="downFile" type="md-arrow-round-down" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--未知-->
|
||||
<div v-else class="dialog-content unknown">{{$L("未知的消息类型")}}</div>
|
||||
|
||||
@ -138,8 +148,7 @@ export default {
|
||||
if (!text) {
|
||||
return ""
|
||||
}
|
||||
text = text.trim().replace(/(\n\x20*){3,}/g, "<br/><br/>");
|
||||
text = text.trim().replace(/\n/g, "<br/>");
|
||||
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
|
||||
return text;
|
||||
},
|
||||
|
||||
@ -167,6 +176,25 @@ export default {
|
||||
return {};
|
||||
},
|
||||
|
||||
viewFile() {
|
||||
if (this.$Electron) {
|
||||
this.$Electron.ipcRenderer.send('windowRouter', {
|
||||
title: `${this.msgData.msg.name} (${$A.bytesToSize(this.msgData.msg.size)})`,
|
||||
titleFixed: true,
|
||||
name: 'msgview-' + this.msgData.id,
|
||||
path: "/single/msgview/" + this.msgData.id,
|
||||
force: false,
|
||||
config: {
|
||||
parent: null,
|
||||
width: Math.min(window.screen.availWidth, 1440),
|
||||
height: Math.min(window.screen.availHeight, 900),
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.open($A.apiUrl(`../single/msgview/${this.msgData.id}`))
|
||||
}
|
||||
},
|
||||
|
||||
downFile() {
|
||||
$A.modalConfirm({
|
||||
title: '下载文件',
|
||||
|
130
resources/assets/js/pages/single/msgview.vue
Normal file
130
resources/assets/js/pages/single/msgview.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="single-msgview">
|
||||
<PageTitle :title="title"/>
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<template v-else>
|
||||
<AceEditor v-if="isCode" v-model="codeContent" :ext="codeExt" class="view-editor" readOnly/>
|
||||
<iframe v-else-if="isPreview" class="preview-iframe" :src="previewUrl"></iframe>
|
||||
<div v-else class="no-support">{{$L('不支持单独查看此消息')}}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.single-msgview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.preview-iframe,
|
||||
.ace_editor,
|
||||
.no-support {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.preview-iframe {
|
||||
background: 0 0;
|
||||
float: none;
|
||||
max-width: none;
|
||||
}
|
||||
.view-editor,
|
||||
.no-support {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
<script>
|
||||
import AceEditor from "../../components/AceEditor";
|
||||
|
||||
export default {
|
||||
components: {AceEditor},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
|
||||
msgDetail: {},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
//
|
||||
},
|
||||
watch: {
|
||||
'$route': {
|
||||
handler() {
|
||||
this.getInfo();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
const {msg} = this.msgDetail;
|
||||
if (msg && msg.name) {
|
||||
return msg.name;
|
||||
}
|
||||
return "Loading..."
|
||||
},
|
||||
isCode() {
|
||||
return this.msgDetail.type == 'file' && this.msgDetail.file_mode == 1;
|
||||
},
|
||||
codeContent() {
|
||||
if (this.isCode) {
|
||||
return this.msgDetail.content;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
codeExt() {
|
||||
if (this.isCode) {
|
||||
return this.msgDetail.msg.ext;
|
||||
}
|
||||
return 'txt'
|
||||
},
|
||||
isPreview() {
|
||||
return this.msgDetail.type == 'file' && this.msgDetail.file_mode == 2;
|
||||
},
|
||||
previewUrl() {
|
||||
if (this.isPreview) {
|
||||
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.msgDetail.url))
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getInfo() {
|
||||
let msg_id = $A.runNum(this.$route.params.id);
|
||||
if (msg_id <= 0) {
|
||||
return;
|
||||
}
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/detail',
|
||||
data: {
|
||||
msg_id,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
this.loadIng--;
|
||||
this.msgDetail = data;
|
||||
}).catch(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.modalError({
|
||||
content: msg,
|
||||
onOk: () => {
|
||||
if (this.$Electron) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
resources/assets/js/routes.js
vendored
5
resources/assets/js/routes.js
vendored
@ -85,6 +85,11 @@ export default [
|
||||
path: '/single/task/:id',
|
||||
component: () => import('./pages/single/task.vue'),
|
||||
},
|
||||
{
|
||||
name: 'single-msgview',
|
||||
path: '/single/msgview/:id',
|
||||
component: () => import('./pages/single/msgview.vue'),
|
||||
},
|
||||
{
|
||||
name: 'login',
|
||||
path: '/login',
|
||||
|
@ -45,6 +45,7 @@
|
||||
line-height: 22px;
|
||||
max-width: 100%;
|
||||
.ivu-tag {
|
||||
flex-shrink: 0;
|
||||
margin: 0 6px 0 0;
|
||||
padding: 0 5px;
|
||||
&.ivu-tag-success {
|
||||
@ -110,19 +111,50 @@
|
||||
height: 30px;
|
||||
}
|
||||
.dialog-view {
|
||||
max-width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 0 0 0 8px;
|
||||
position: relative;
|
||||
&.text {
|
||||
max-width: 70%;
|
||||
}
|
||||
.dialog-content {
|
||||
color: #333333;
|
||||
background-color: #F4F5F7;
|
||||
padding: 8px;
|
||||
min-width: 32px;
|
||||
border-radius: 6px 6px 6px 0;
|
||||
.dialog-file {
|
||||
cursor: pointer;
|
||||
.dialog-file-menu {
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -8px;
|
||||
.file-menu-warp {
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
}
|
||||
.file-menu-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
> i {
|
||||
flex: 1;
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #777;
|
||||
}
|
||||
&+i {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> pre {
|
||||
display: block;
|
||||
@ -133,6 +165,11 @@
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
&:hover {
|
||||
.dialog-file-menu {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
display: flex;
|
||||
.common-loading {
|
||||
@ -187,6 +224,7 @@
|
||||
overflow: hidden;
|
||||
.file-img {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
&.unknown {
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user