58 changed files with 3866 additions and 3897 deletions
@ -1,131 +1,131 @@ |
|||||
<template> |
<template> |
||||
<el-scrollbar v-show="show&&showMembers.length" ref="scrollBox" class="group-member-choose" |
<el-scrollbar v-show="show && showMembers.length" ref="scrollBox" class="group-member-choose" |
||||
:style="{'left':pos.x+'px','top':pos.y-300+'px'}"> |
:style="{ 'left': pos.x + 'px', 'top': pos.y - 300 + 'px' }"> |
||||
<div v-for="(member,idx) in showMembers" :key="member.id"> |
<div v-for="(member, idx) in showMembers" :key="member.id"> |
||||
<chat-group-member :member="member" :height="40" :active='activeIdx==idx' |
<chat-group-member :member="member" :height="40" :active='activeIdx == idx' |
||||
@click.native="onSelectMember(member)"></chat-group-member> |
@click.native="onSelectMember(member)"></chat-group-member> |
||||
</div> |
</div> |
||||
</el-scrollbar> |
</el-scrollbar> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import ChatGroupMember from "./ChatGroupMember.vue"; |
import ChatGroupMember from "./ChatGroupMember.vue"; |
||||
export default { |
export default { |
||||
name: "chatAtBox", |
name: "chatAtBox", |
||||
components: { |
components: { |
||||
ChatGroupMember |
ChatGroupMember |
||||
|
}, |
||||
|
props: { |
||||
|
searchText: { |
||||
|
type: String, |
||||
|
default: "" |
||||
}, |
}, |
||||
props: { |
ownerId: { |
||||
searchText: { |
type: Number, |
||||
type: String, |
}, |
||||
default: "" |
members: { |
||||
}, |
type: Array |
||||
ownerId: { |
} |
||||
type: Number, |
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
pos: { |
||||
|
x: 0, |
||||
|
y: 0 |
||||
}, |
}, |
||||
members: { |
activeIdx: 0, |
||||
type: Array |
showMembers: [] |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
init() { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = 0; |
||||
|
this.showMembers = []; |
||||
|
let userId = this.$store.state.userStore.userInfo.id; |
||||
|
let name = "全体成员"; |
||||
|
if (this.ownerId == userId && name.startsWith(this.searchText)) { |
||||
|
this.showMembers.push({ |
||||
|
userId: -1, |
||||
|
showNickName: name |
||||
|
}) |
||||
} |
} |
||||
|
this.members.forEach((m) => { |
||||
|
if (m.userId != userId && !m.quit && m.showNickName.startsWith(this.searchText)) { |
||||
|
this.showMembers.push(m); |
||||
|
} |
||||
|
}) |
||||
|
this.activeIdx = this.showMembers.length > 0 ? 0 : -1; |
||||
}, |
}, |
||||
data() { |
open(pos) { |
||||
return { |
this.show = true; |
||||
show: false, |
this.pos = pos; |
||||
pos: { |
this.init(); |
||||
x: 0, |
|
||||
y: 0 |
|
||||
}, |
|
||||
activeIdx: 0, |
|
||||
showMembers: [] |
|
||||
}; |
|
||||
}, |
}, |
||||
methods: { |
close() { |
||||
init() { |
this.show = false; |
||||
this.$refs.scrollBox.wrap.scrollTop = 0; |
}, |
||||
this.showMembers = []; |
moveUp() { |
||||
let userId = this.$store.state.userStore.userInfo.id; |
if (this.activeIdx > 0) { |
||||
let name = "全体成员"; |
this.activeIdx--; |
||||
if (this.ownerId == userId && name.startsWith(this.searchText)) { |
this.scrollToActive() |
||||
this.showMembers.push({ |
|
||||
userId: -1, |
|
||||
showNickName: name |
|
||||
}) |
|
||||
} |
|
||||
this.members.forEach((m) => { |
|
||||
if (m.userId != userId && !m.quit && m.showNickName.startsWith(this.searchText)) { |
|
||||
this.showMembers.push(m); |
|
||||
} |
|
||||
}) |
|
||||
this.activeIdx = this.showMembers.length > 0 ? 0: -1; |
|
||||
}, |
|
||||
open(pos) { |
|
||||
this.show = true; |
|
||||
this.pos = pos; |
|
||||
this.init(); |
|
||||
}, |
|
||||
close() { |
|
||||
this.show = false; |
|
||||
}, |
|
||||
moveUp() { |
|
||||
if (this.activeIdx > 0) { |
|
||||
this.activeIdx--; |
|
||||
this.scrollToActive() |
|
||||
} |
|
||||
}, |
|
||||
moveDown() { |
|
||||
if (this.activeIdx < this.showMembers.length - 1) { |
|
||||
this.activeIdx++; |
|
||||
this.scrollToActive() |
|
||||
} |
|
||||
}, |
|
||||
select() { |
|
||||
if (this.activeIdx >= 0) { |
|
||||
this.onSelectMember(this.showMembers[this.activeIdx]) |
|
||||
} |
|
||||
this.close(); |
|
||||
}, |
|
||||
scrollToActive() { |
|
||||
if (this.activeIdx * 35 - this.$refs.scrollBox.wrap.clientHeight > this.$refs.scrollBox.wrap.scrollTop) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop += 140; |
|
||||
if (this.$refs.scrollBox.wrap.scrollTop > this.$refs.scrollBox.wrap.scrollHeight) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop = this.$refs.scrollBox.wrap.scrollHeight |
|
||||
} |
|
||||
} |
|
||||
if (this.activeIdx * 35 < this.$refs.scrollBox.wrap.scrollTop) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop -= 140; |
|
||||
if (this.$refs.scrollBox.wrap.scrollTop < 0) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop = 0; |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
onSelectMember(member) { |
|
||||
this.$emit("select", member); |
|
||||
this.show = false; |
|
||||
} |
} |
||||
}, |
}, |
||||
computed: { |
moveDown() { |
||||
isOwner() { |
if (this.activeIdx < this.showMembers.length - 1) { |
||||
return this.$store.state.userStore.userInfo.id == this.ownerId; |
this.activeIdx++; |
||||
|
this.scrollToActive() |
||||
} |
} |
||||
}, |
}, |
||||
watch: { |
select() { |
||||
searchText: { |
if (this.activeIdx >= 0) { |
||||
handler(newText, oldText) { |
this.onSelectMember(this.showMembers[this.activeIdx]) |
||||
this.init(); |
} |
||||
|
this.close(); |
||||
|
}, |
||||
|
scrollToActive() { |
||||
|
if (this.activeIdx * 35 - this.$refs.scrollBox.wrap.clientHeight > this.$refs.scrollBox.wrap.scrollTop) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop += 140; |
||||
|
if (this.$refs.scrollBox.wrap.scrollTop > this.$refs.scrollBox.wrap.scrollHeight) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = this.$refs.scrollBox.wrap.scrollHeight |
||||
|
} |
||||
|
} |
||||
|
if (this.activeIdx * 35 < this.$refs.scrollBox.wrap.scrollTop) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop -= 140; |
||||
|
if (this.$refs.scrollBox.wrap.scrollTop < 0) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = 0; |
||||
} |
} |
||||
} |
} |
||||
|
}, |
||||
|
onSelectMember(member) { |
||||
|
this.$emit("select", member); |
||||
|
this.show = false; |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isOwner() { |
||||
|
return this.$store.state.userStore.userInfo.id == this.ownerId; |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
searchText: { |
||||
|
handler(newText, oldText) { |
||||
|
this.init(); |
||||
|
} |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.group-member-choose { |
.group-member-choose { |
||||
position: fixed; |
position: fixed; |
||||
width: 200px; |
width: 200px; |
||||
height: 300px; |
height: 300px; |
||||
//border: 1px solid #53a0e79c; |
//border: 1px solid #53a0e79c; |
||||
//border-radius: 5px; |
//border-radius: 5px; |
||||
background-color: #fff; |
background-color: #fff; |
||||
box-shadow: var(--im-box-shadow); |
box-shadow: var(--im-box-shadow); |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,139 +1,138 @@ |
|||||
<template> |
<template> |
||||
<el-dialog class="chat-record" title="语音录制" :visible.sync="visible" width="600px" :before-close="onClose"> |
<el-dialog class="chat-record" title="语音录制" :visible.sync="visible" width="600px" :before-close="onClose"> |
||||
<div v-show="mode=='RECORD'"> |
<div v-show="mode == 'RECORD'"> |
||||
<div class="tip">{{stateTip}}</div> |
<div class="tip">{{ stateTip }}</div> |
||||
<div>时长: {{state=='STOP'?0:parseInt(rc.duration)}}s</div> |
<div>时长: {{ state == 'STOP' ? 0 : parseInt(rc.duration) }}s</div> |
||||
</div> |
</div> |
||||
<audio v-show="mode=='PLAY'" :src="url" controls ref="audio" @ended="onStopAudio()"></audio> |
<audio v-show="mode == 'PLAY'" :src="url" controls ref="audio" @ended="onStopAudio()"></audio> |
||||
<el-divider content-position="center"></el-divider> |
<el-divider content-position="center"></el-divider> |
||||
<el-row class="btn-group"> |
<el-row class="btn-group"> |
||||
<el-button round type="primary" v-show="state=='STOP'" @click="onStartRecord()">开始录音</el-button> |
<el-button round type="primary" v-show="state == 'STOP'" @click="onStartRecord()">开始录音</el-button> |
||||
<el-button round type="warning" v-show="state=='RUNNING'" @click="onPauseRecord()">暂停录音</el-button> |
<el-button round type="warning" v-show="state == 'RUNNING'" @click="onPauseRecord()">暂停录音</el-button> |
||||
<el-button round type="primary" v-show="state=='PAUSE'" @click="onResumeRecord()">继续录音</el-button> |
<el-button round type="primary" v-show="state == 'PAUSE'" @click="onResumeRecord()">继续录音</el-button> |
||||
<el-button round type="danger" v-show="state=='RUNNING'||state=='PAUSE'" @click="onCompleteRecord()"> |
<el-button round type="danger" v-show="state == 'RUNNING' || state == 'PAUSE'" @click="onCompleteRecord()"> |
||||
结束录音</el-button> |
结束录音</el-button> |
||||
<el-button round type="success" v-show="state=='COMPLETE' && mode!='PLAY'" @click="onPlayAudio()">播放录音 |
<el-button round type="success" v-show="state == 'COMPLETE' && mode != 'PLAY'" @click="onPlayAudio()">播放录音 |
||||
</el-button> |
</el-button> |
||||
<el-button round type="warning" v-show="state=='COMPLETE' && mode=='PLAY'" @click="onStopAudio()">停止播放 |
<el-button round type="warning" v-show="state == 'COMPLETE' && mode == 'PLAY'" @click="onStopAudio()">停止播放 |
||||
</el-button> |
</el-button> |
||||
<el-button round type="primary" v-show="state=='COMPLETE'" @click="onRestartRecord()">重新录音</el-button> |
<el-button round type="primary" v-show="state == 'COMPLETE'" @click="onRestartRecord()">重新录音</el-button> |
||||
<el-button round type="primary" v-show="state=='COMPLETE'" @click="onSendRecord()">立即发送</el-button> |
<el-button round type="primary" v-show="state == 'COMPLETE'" @click="onSendRecord()">立即发送</el-button> |
||||
</el-row> |
</el-row> |
||||
</el-dialog> |
</el-dialog> |
||||
|
|
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import Recorder from 'js-audio-recorder'; |
import Recorder from 'js-audio-recorder'; |
||||
|
|
||||
export default { |
export default { |
||||
name: 'chatRecord', |
name: 'chatRecord', |
||||
props: { |
props: { |
||||
visible: { |
visible: { |
||||
type: Boolean |
type: Boolean |
||||
} |
} |
||||
}, |
}, |
||||
data() { |
data() { |
||||
return { |
return { |
||||
rc: new Recorder(), |
rc: new Recorder(), |
||||
audio: new Audio(), |
audio: new Audio(), |
||||
state: 'STOP', // STOP、RUNNING、PAUSE、COMPLETE |
state: 'STOP', // STOP、RUNNING、PAUSE、COMPLETE |
||||
stateTip: "未开始", |
stateTip: "未开始", |
||||
mode: 'RECORD', // RECORD 、PLAY |
mode: 'RECORD', // RECORD 、PLAY |
||||
duration: 0, |
duration: 0, |
||||
url: "" |
url: "" |
||||
} |
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onClose() { |
||||
|
// 关闭前清除数据 |
||||
|
this.rc.destroy(); |
||||
|
this.rc = new Recorder(); |
||||
|
this.audio.pause(); |
||||
|
this.mode = 'RECORD'; |
||||
|
this.state = 'STOP'; |
||||
|
this.stateTip = '未开始'; |
||||
|
this.$emit("close"); |
||||
}, |
}, |
||||
methods: { |
onStartRecord() { |
||||
onClose() { |
this.rc.start().then((stream) => { |
||||
// 关闭前清除数据 |
|
||||
this.rc.destroy(); |
|
||||
this.rc = new Recorder(); |
|
||||
this.audio.pause(); |
|
||||
this.mode = 'RECORD'; |
|
||||
this.state = 'STOP'; |
|
||||
this.stateTip = '未开始'; |
|
||||
this.$emit("close"); |
|
||||
}, |
|
||||
onStartRecord() { |
|
||||
this.rc.start().then((stream) => { |
|
||||
this.state = 'RUNNING'; |
|
||||
this.stateTip = "正在录音..."; |
|
||||
}).catch(error => { |
|
||||
this.$message.error(error); |
|
||||
}); |
|
||||
|
|
||||
|
|
||||
}, |
|
||||
onPauseRecord() { |
|
||||
this.rc.pause(); |
|
||||
this.state = 'PAUSE'; |
|
||||
this.stateTip = "已暂停录音"; |
|
||||
}, |
|
||||
onResumeRecord() { |
|
||||
this.rc.resume(); |
|
||||
this.state = 'RUNNING'; |
this.state = 'RUNNING'; |
||||
this.stateTip = "正在录音..."; |
this.stateTip = "正在录音..."; |
||||
}, |
}).catch(error => { |
||||
onCompleteRecord() { |
this.$message.error(error); |
||||
this.rc.pause(); |
}); |
||||
this.state = 'COMPLETE'; |
|
||||
this.stateTip = "已结束录音"; |
|
||||
}, |
|
||||
onPlayAudio() { |
|
||||
let wav = this.rc.getWAVBlob(); |
|
||||
let url = URL.createObjectURL(wav); |
|
||||
this.$refs.audio.src = url; |
|
||||
this.$refs.audio.play(); |
|
||||
this.mode = 'PLAY'; |
|
||||
}, |
|
||||
onStopAudio() { |
|
||||
this.$refs.audio.pause(); |
|
||||
this.mode = 'RECORD'; |
|
||||
}, |
|
||||
onRestartRecord() { |
|
||||
this.rc.destroy(); |
|
||||
this.rc = new Recorder() |
|
||||
this.rc.start(); |
|
||||
this.state = 'RUNNING'; |
|
||||
this.mode = 'RECORD'; |
|
||||
this.stateTip = "正在录音..."; |
|
||||
}, |
|
||||
onSendRecord() { |
|
||||
let wav = this.rc.getWAVBlob(); |
|
||||
let name = new Date().getDate() + '.wav'; |
|
||||
var formData = new window.FormData() |
|
||||
formData.append('file', wav, name); |
|
||||
this.$http({ |
|
||||
url: '/file/upload', |
|
||||
data: formData, |
|
||||
method: 'post', |
|
||||
headers: { |
|
||||
'Content-Type': 'multipart/form-data' |
|
||||
} |
|
||||
}).then((url) => { |
|
||||
let data = { |
|
||||
duration: parseInt(this.rc.duration), |
|
||||
url: url |
|
||||
} |
|
||||
this.$emit("send", data); |
|
||||
this.onClose(); |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
}, |
||||
|
onPauseRecord() { |
||||
|
this.rc.pause(); |
||||
|
this.state = 'PAUSE'; |
||||
|
this.stateTip = "已暂停录音"; |
||||
|
}, |
||||
|
onResumeRecord() { |
||||
|
this.rc.resume(); |
||||
|
this.state = 'RUNNING'; |
||||
|
this.stateTip = "正在录音..."; |
||||
|
}, |
||||
|
onCompleteRecord() { |
||||
|
this.rc.pause(); |
||||
|
this.state = 'COMPLETE'; |
||||
|
this.stateTip = "已结束录音"; |
||||
|
}, |
||||
|
onPlayAudio() { |
||||
|
let wav = this.rc.getWAVBlob(); |
||||
|
let url = URL.createObjectURL(wav); |
||||
|
this.$refs.audio.src = url; |
||||
|
this.$refs.audio.play(); |
||||
|
this.mode = 'PLAY'; |
||||
|
}, |
||||
|
onStopAudio() { |
||||
|
this.$refs.audio.pause(); |
||||
|
this.mode = 'RECORD'; |
||||
|
}, |
||||
|
onRestartRecord() { |
||||
|
this.rc.destroy(); |
||||
|
this.rc = new Recorder() |
||||
|
this.rc.start(); |
||||
|
this.state = 'RUNNING'; |
||||
|
this.mode = 'RECORD'; |
||||
|
this.stateTip = "正在录音..."; |
||||
|
}, |
||||
|
onSendRecord() { |
||||
|
let wav = this.rc.getWAVBlob(); |
||||
|
let name = new Date().getDate() + '.wav'; |
||||
|
var formData = new window.FormData() |
||||
|
formData.append('file', wav, name); |
||||
|
this.$http({ |
||||
|
url: '/file/upload', |
||||
|
data: formData, |
||||
|
method: 'post', |
||||
|
headers: { |
||||
|
'Content-Type': 'multipart/form-data' |
||||
|
} |
||||
|
}).then((url) => { |
||||
|
let data = { |
||||
|
duration: parseInt(this.rc.duration), |
||||
|
url: url |
||||
|
} |
||||
|
this.$emit("send", data); |
||||
|
this.onClose(); |
||||
|
}) |
||||
|
} |
||||
} |
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
.chat-record { |
.chat-record { |
||||
|
|
||||
.tip { |
.tip { |
||||
font-size: 18px; |
font-size: 18px; |
||||
} |
} |
||||
|
|
||||
.btn-group { |
.btn-group { |
||||
margin-bottom: 20px; |
margin-bottom: 20px; |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,103 +1,102 @@ |
|||||
<template> |
<template> |
||||
<el-upload :action="'#'" :http-request="onFileUpload" :accept="fileTypes==null?'':fileTypes.join(',')" :show-file-list="false" |
<el-upload :action="'#'" :http-request="onFileUpload" :accept="fileTypes == null ? '' : fileTypes.join(',')" |
||||
:disabled="disabled" :before-upload="beforeUpload" :multiple="true"> |
:show-file-list="false" :disabled="disabled" :before-upload="beforeUpload" :multiple="true"> |
||||
<slot></slot> |
<slot></slot> |
||||
</el-upload> |
</el-upload> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: "fileUpload", |
name: "fileUpload", |
||||
data() { |
data() { |
||||
return { |
return { |
||||
loading: null, |
loading: null, |
||||
uploadHeaders: { |
uploadHeaders: { |
||||
"accessToken": sessionStorage.getItem('accessToken') |
"accessToken": sessionStorage.getItem('accessToken') |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
action: { |
||||
|
type: String, |
||||
|
required: false |
||||
}, |
}, |
||||
props: { |
fileTypes: { |
||||
action: { |
type: Array, |
||||
type: String, |
default: null |
||||
required: false |
|
||||
}, |
|
||||
fileTypes: { |
|
||||
type: Array, |
|
||||
default: null |
|
||||
}, |
|
||||
maxSize: { |
|
||||
type: Number, |
|
||||
default: null |
|
||||
}, |
|
||||
showLoading: { |
|
||||
type: Boolean, |
|
||||
default: false |
|
||||
}, |
|
||||
disabled: { |
|
||||
type: Boolean, |
|
||||
default: false |
|
||||
} |
|
||||
}, |
}, |
||||
methods: { |
maxSize: { |
||||
onFileUpload(file) { |
type: Number, |
||||
// 展示加载条 |
default: null |
||||
if (this.showLoading) { |
}, |
||||
this.loading = this.$loading({ |
showLoading: { |
||||
lock: true, |
type: Boolean, |
||||
text: '正在上传...', |
default: false |
||||
spinner: 'el-icon-loading', |
}, |
||||
background: 'rgba(0, 0, 0, 0.7)' |
disabled: { |
||||
}); |
type: Boolean, |
||||
} |
default: false |
||||
let formData = new FormData() |
} |
||||
formData.append('file', file.file) |
}, |
||||
this.$http({ |
methods: { |
||||
url: this.action, |
onFileUpload(file) { |
||||
data: formData, |
// 展示加载条 |
||||
method: 'post', |
if (this.showLoading) { |
||||
headers: { |
this.loading = this.$loading({ |
||||
'Content-Type': 'multipart/form-data' |
lock: true, |
||||
} |
text: '正在上传...', |
||||
}).then((data) => { |
spinner: 'el-icon-loading', |
||||
this.$emit("success", data, file.file); |
background: 'rgba(0, 0, 0, 0.7)' |
||||
}).catch((e) => { |
}); |
||||
this.$emit("fail", e, file.file); |
} |
||||
}).finally(() => { |
let formData = new FormData() |
||||
this.loading && this.loading.close(); |
formData.append('file', file.file) |
||||
}) |
this.$http({ |
||||
}, |
url: this.action, |
||||
beforeUpload(file) { |
data: formData, |
||||
// 校验文件类型 |
method: 'post', |
||||
if (this.fileTypes && this.fileTypes.length > 0) { |
headers: { |
||||
let fileType = file.type; |
'Content-Type': 'multipart/form-data' |
||||
let t = this.fileTypes.find((t) => t.toLowerCase() === fileType); |
|
||||
if (t === undefined) { |
|
||||
this.$message.error(`文件格式错误,请上传以下格式的文件:${this.fileTypes.join("、")}`); |
|
||||
return false; |
|
||||
} |
|
||||
} |
} |
||||
// 校验大小 |
}).then((data) => { |
||||
if (this.maxSize && file.size > this.maxSize) { |
this.$emit("success", data, file.file); |
||||
this.$message.error(`文件大小不能超过 ${this.fileSizeStr}!`); |
}).catch((e) => { |
||||
|
this.$emit("fail", e, file.file); |
||||
|
}).finally(() => { |
||||
|
this.loading && this.loading.close(); |
||||
|
}) |
||||
|
}, |
||||
|
beforeUpload(file) { |
||||
|
// 校验文件类型 |
||||
|
if (this.fileTypes && this.fileTypes.length > 0) { |
||||
|
let fileType = file.type; |
||||
|
let t = this.fileTypes.find((t) => t.toLowerCase() === fileType); |
||||
|
if (t === undefined) { |
||||
|
this.$message.error(`文件格式错误,请上传以下格式的文件:${this.fileTypes.join("、")}`); |
||||
return false; |
return false; |
||||
} |
} |
||||
this.$emit("before", file); |
|
||||
return true; |
|
||||
} |
} |
||||
}, |
// 校验大小 |
||||
computed: { |
if (this.maxSize && file.size > this.maxSize) { |
||||
fileSizeStr() { |
this.$message.error(`文件大小不能超过 ${this.fileSizeStr}!`); |
||||
if (this.maxSize > 1024 * 1024) { |
return false; |
||||
return Math.round(this.maxSize / 1024 / 1024) + "M"; |
} |
||||
} |
this.$emit("before", file); |
||||
if (this.maxSize > 1024) { |
return true; |
||||
return Math.round(this.maxSize / 1024) + "KB"; |
} |
||||
} |
}, |
||||
return this.maxSize + "B"; |
computed: { |
||||
|
fileSizeStr() { |
||||
|
if (this.maxSize > 1024 * 1024) { |
||||
|
return Math.round(this.maxSize / 1024 / 1024) + "M"; |
||||
|
} |
||||
|
if (this.maxSize > 1024) { |
||||
|
return Math.round(this.maxSize / 1024) + "KB"; |
||||
} |
} |
||||
|
return this.maxSize + "B"; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style></style> |
||||
</style> |
|
||||
@ -1,70 +1,70 @@ |
|||||
<template> |
<template> |
||||
<div class="group-member"> |
<div class="group-member"> |
||||
<head-image :id="member.userId" :name="member.showNickName" |
<head-image :id="member.userId" :name="member.showNickName" :url="member.headImage" :size="38" |
||||
:url="member.headImage" :size="38" |
:online="member.online"> |
||||
:online="member.online" > |
<div v-if="showDel" @click.stop="onDelete()" class="btn-kick el-icon-error"></div> |
||||
<div v-if="showDel" @click.stop="onDelete()" class="btn-kick el-icon-error"></div> |
|
||||
</head-image> |
</head-image> |
||||
<div class="member-name">{{member.showNickName}}</div> |
<div class="member-name">{{ member.showNickName }}</div> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import HeadImage from "../common/HeadImage.vue"; |
import HeadImage from "../common/HeadImage.vue"; |
||||
export default{ |
export default { |
||||
name: "groupMember", |
name: "groupMember", |
||||
components:{HeadImage}, |
components: { HeadImage }, |
||||
data(){ |
data() { |
||||
return {}; |
return {}; |
||||
|
}, |
||||
|
props: { |
||||
|
member: { |
||||
|
type: Object, |
||||
|
required: true |
||||
}, |
}, |
||||
props:{ |
showDel: { |
||||
member:{ |
type: Boolean, |
||||
type: Object, |
default: false |
||||
required: true |
} |
||||
}, |
}, |
||||
showDel:{ |
methods: { |
||||
type: Boolean, |
onDelete() { |
||||
default: false |
this.$emit("del", this.member); |
||||
} |
|
||||
}, |
|
||||
methods:{ |
|
||||
onDelete(){ |
|
||||
this.$emit("del",this.member); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
.group-member{ |
.group-member { |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
align-items: center; |
align-items: center; |
||||
width: 50px; |
width: 50px; |
||||
.member-name { |
|
||||
font-size: 12px; |
|
||||
text-align: center; |
|
||||
width: 100%; |
|
||||
height: 30px; |
|
||||
line-height: 30px; |
|
||||
white-space: nowrap; |
|
||||
text-overflow:ellipsis; |
|
||||
overflow:hidden |
|
||||
} |
|
||||
|
|
||||
.btn-kick { |
.member-name { |
||||
display: none; |
font-size: 12px; |
||||
position: absolute; |
text-align: center; |
||||
right: -8px; |
width: 100%; |
||||
top: -8px; |
height: 30px; |
||||
color: darkred; |
line-height: 30px; |
||||
font-size: 20px; |
white-space: nowrap; |
||||
cursor: pointer; |
text-overflow: ellipsis; |
||||
} |
overflow: hidden |
||||
|
} |
||||
|
|
||||
&:hover .btn-kick{ |
.btn-kick { |
||||
display: block; |
display: none; |
||||
color: #ce1818; |
position: absolute; |
||||
} |
right: -8px; |
||||
|
top: -8px; |
||||
|
color: darkred; |
||||
|
font-size: 20px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
&:hover .btn-kick { |
||||
|
display: block; |
||||
|
color: #ce1818; |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,116 +1,118 @@ |
|||||
<template> |
<template> |
||||
<el-dialog title="是否加入通话?" :visible.sync="isShow" width="400px"> |
<el-dialog title="是否加入通话?" :visible.sync="isShow" width="400px"> |
||||
<div class="rtc-group-join"> |
<div class="rtc-group-join"> |
||||
<div class="host-info"> |
<div class="host-info"> |
||||
<head-image :name="rtcInfo.host.nickName" :url="rtcInfo.host.headImage" :size="80"></head-image> |
<head-image :name="rtcInfo.host.nickName" :url="rtcInfo.host.headImage" :size="80"></head-image> |
||||
<div class="host-text">{{'发起人:'+rtcInfo.host.nickName}}</div> |
<div class="host-text">{{ '发起人:' + rtcInfo.host.nickName }}</div> |
||||
|
</div> |
||||
|
<div class="users-info"> |
||||
|
<div>{{ rtcInfo.userInfos.length + '人正在通话中' }}</div> |
||||
|
<div class="user-list"> |
||||
|
<div class="user-item" v-for="user in rtcInfo.userInfos" :key="user.id"> |
||||
|
<head-image :url="user.headImage" :name="user.nickName" :size="40"> |
||||
|
</head-image> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
</div> |
</div> |
||||
<div class="users-info"> |
<span slot="footer" class="dialog-footer"> |
||||
<div>{{rtcInfo.userInfos.length+'人正在通话中'}}</div> |
<el-button @click="onCancel()">取 消</el-button> |
||||
<div class="user-list"> |
<el-button type="primary" @click="onOk()">确 定</el-button> |
||||
<div class="user-item" v-for="user in rtcInfo.userInfos" :key="user.id"> |
</span> |
||||
<head-image :url="user.headImage" :name="user.nickName" :size="40"> |
|
||||
</head-image> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<span slot="footer" class="dialog-footer"> |
|
||||
<el-button @click="onCancel()">取 消</el-button> |
|
||||
<el-button type="primary" @click="onOk()">确 定</el-button> |
|
||||
</span> |
|
||||
</el-dialog> |
</el-dialog> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import HeadImage from '@/components/common/HeadImage' |
import HeadImage from '@/components/common/HeadImage' |
||||
|
|
||||
export default{ |
export default { |
||||
name: "rtcGroupJoin", |
name: "rtcGroupJoin", |
||||
components:{ |
components: { |
||||
HeadImage |
HeadImage |
||||
}, |
}, |
||||
data() { |
data() { |
||||
return { |
return { |
||||
isShow: false, |
isShow: false, |
||||
rtcInfo: { |
rtcInfo: { |
||||
host:{}, |
host: {}, |
||||
userInfos:[] |
userInfos: [] |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
groupId: { |
||||
|
type: Number |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
open(rtcInfo) { |
||||
|
this.rtcInfo = rtcInfo; |
||||
|
this.isShow = true; |
||||
}, |
}, |
||||
props: { |
onOk() { |
||||
groupId: { |
this.isShow = false; |
||||
type: Number |
let userInfos = this.rtcInfo.userInfos; |
||||
|
let mine = this.$store.state.userStore.userInfo; |
||||
|
if (!userInfos.find((user) => user.id == mine.id)) { |
||||
|
// 加入自己的信息 |
||||
|
userInfos.push({ |
||||
|
id: mine.id, |
||||
|
nickName: mine.nickName, |
||||
|
headImage: mine.headImageThumb, |
||||
|
isCamera: false, |
||||
|
isMicroPhone: true |
||||
|
}) |
||||
} |
} |
||||
}, |
let rtcInfo = { |
||||
methods: { |
isHost: false, |
||||
open(rtcInfo) { |
groupId: this.groupId, |
||||
this.rtcInfo = rtcInfo; |
inviterId: mine.id, |
||||
this.isShow = true; |
userInfos: userInfos |
||||
}, |
|
||||
onOk() { |
|
||||
this.isShow = false; |
|
||||
let userInfos = this.rtcInfo.userInfos; |
|
||||
let mine = this.$store.state.userStore.userInfo; |
|
||||
if(!userInfos.find((user)=>user.id==mine.id)){ |
|
||||
// 加入自己的信息 |
|
||||
userInfos.push({ |
|
||||
id: mine.id, |
|
||||
nickName: mine.nickName, |
|
||||
headImage: mine.headImageThumb, |
|
||||
isCamera: false, |
|
||||
isMicroPhone: true |
|
||||
}) |
|
||||
} |
|
||||
let rtcInfo = { |
|
||||
isHost: false, |
|
||||
groupId: this.groupId, |
|
||||
inviterId: mine.id, |
|
||||
userInfos: userInfos |
|
||||
} |
|
||||
// 通过home.vue打开多人视频窗口 |
|
||||
this.$eventBus.$emit("openGroupVideo", rtcInfo); |
|
||||
|
|
||||
}, |
|
||||
onCancel(){ |
|
||||
this.isShow = false; |
|
||||
} |
} |
||||
|
// 通过home.vue打开多人视频窗口 |
||||
|
this.$eventBus.$emit("openGroupVideo", rtcInfo); |
||||
|
|
||||
|
}, |
||||
|
onCancel() { |
||||
|
this.isShow = false; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.rtc-group-join { |
.rtc-group-join { |
||||
height: 260px; |
height: 260px; |
||||
|
padding: 10px; |
||||
|
|
||||
|
.host-info { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
font-size: 16px; |
||||
padding: 10px; |
padding: 10px; |
||||
.host-info { |
height: 100px; |
||||
display: flex; |
align-items: center; |
||||
flex-direction: column; |
|
||||
font-size: 16px; |
.host-text { |
||||
padding: 10px; |
margin-top: 5px; |
||||
height: 100px; |
|
||||
align-items: center; |
|
||||
|
|
||||
.host-text{ |
|
||||
margin-top: 5px; |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
.users-info { |
|
||||
font-size: 16px; |
.users-info { |
||||
margin-top: 20px; |
font-size: 16px; |
||||
.user-list { |
margin-top: 20px; |
||||
display: flex; |
|
||||
padding: 5px 5px; |
.user-list { |
||||
height: 90px; |
display: flex; |
||||
flex-wrap: wrap; |
padding: 5px 5px; |
||||
justify-content: center; |
height: 90px; |
||||
|
flex-wrap: wrap; |
||||
.user-item{ |
justify-content: center; |
||||
padding: 2px; |
|
||||
} |
.user-item { |
||||
|
padding: 2px; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,72 +1,72 @@ |
|||||
import Vue from 'vue' |
import Vue from 'vue' |
||||
|
|
||||
// v-dialogDrag: 弹窗拖拽
|
// v-dialogDrag: 弹窗拖拽
|
||||
Vue.directive('dialogDrag', { |
Vue.directive('dialogDrag', { |
||||
bind (el, binding, vnode, oldVnode) { |
bind(el, binding, vnode, oldVnode) { |
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header') |
const dialogHeaderEl = el.querySelector('.el-dialog__header') |
||||
const dragDom = el.querySelector('.el-dialog') |
const dragDom = el.querySelector('.el-dialog') |
||||
dialogHeaderEl.style.cursor = 'move' |
dialogHeaderEl.style.cursor = 'move' |
||||
|
|
||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) |
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) |
||||
|
|
||||
dialogHeaderEl.onmousedown = (e) => { |
dialogHeaderEl.onmousedown = (e) => { |
||||
// 鼠标按下,计算当前元素距离可视区的距离
|
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft |
const disX = e.clientX - dialogHeaderEl.offsetLeft |
||||
const disY = e.clientY - dialogHeaderEl.offsetTop |
const disY = e.clientY - dialogHeaderEl.offsetTop |
||||
const screenWidth = document.body.clientWidth; // body当前宽度
|
const screenWidth = document.body.clientWidth; // body当前宽度
|
||||
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
|
||||
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
const dragDomheight = dragDom.offsetHeight; // 对话框高度
|
||||
const minDragDomLeft = dragDom.offsetLeft; |
const minDragDomLeft = dragDom.offsetLeft; |
||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; |
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth; |
||||
const minDragDomTop = dragDom.offsetTop; |
const minDragDomTop = dragDom.offsetTop; |
||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; |
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; |
||||
|
|
||||
// 获取到的值带px 正则匹配替换
|
// 获取到的值带px 正则匹配替换
|
||||
let styL, styT |
let styL, styT |
||||
|
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (sty.left.includes('%')) { |
if (sty.left.includes('%')) { |
||||
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100) |
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100) |
||||
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) |
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) |
||||
} else { |
} else { |
||||
styL = +sty.left.replace(/\px/g, '') |
styL = +sty.left.replace(/\px/g, '') |
||||
styT = +sty.top.replace(/\px/g, '') |
styT = +sty.top.replace(/\px/g, '') |
||||
} |
} |
||||
|
|
||||
document.onmousemove = function (e) { |
document.onmousemove = function (e) { |
||||
// 获取body的页面可视宽高
|
// 获取body的页面可视宽高
|
||||
// var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
|
// var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
|
||||
// var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
|
// var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
|
||||
|
|
||||
// 通过事件委托,计算移动的距离
|
// 通过事件委托,计算移动的距离
|
||||
var l = e.clientX - disX |
var l = e.clientX - disX |
||||
var t = e.clientY - disY |
var t = e.clientY - disY |
||||
|
|
||||
// 边界处理
|
// 边界处理
|
||||
if (-l > minDragDomLeft) { |
if (-l > minDragDomLeft) { |
||||
l = -minDragDomLeft; |
l = -minDragDomLeft; |
||||
} else if (l > maxDragDomLeft) { |
} else if (l > maxDragDomLeft) { |
||||
l = maxDragDomLeft; |
l = maxDragDomLeft; |
||||
} |
} |
||||
if (-t > minDragDomTop) { |
if (-t > minDragDomTop) { |
||||
t = -minDragDomTop; |
t = -minDragDomTop; |
||||
} else if (t > maxDragDomTop) { |
} else if (t > maxDragDomTop) { |
||||
t = maxDragDomTop; |
t = maxDragDomTop; |
||||
} |
} |
||||
// 移动当前元素
|
// 移动当前元素
|
||||
dragDom.style.left = `${l + styL}px` |
dragDom.style.left = `${l + styL}px` |
||||
dragDom.style.top = `${t + styT}px` |
dragDom.style.top = `${t + styT}px` |
||||
|
|
||||
// 将此时的位置传出去
|
// 将此时的位置传出去
|
||||
// binding.value({x:e.pageX,y:e.pageY})
|
// binding.value({x:e.pageX,y:e.pageY})
|
||||
} |
} |
||||
|
|
||||
document.onmouseup = function (e) { |
document.onmouseup = function (e) { |
||||
document.onmousemove = null |
document.onmousemove = null |
||||
document.onmouseup = null |
document.onmouseup = null |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
}) |
}) |
||||
|
|||||
|
Before Width: | Height: | Size: 132 KiB |
Loading…
Reference in new issue