更新组件声明,新增NDropdown支持;调整ForwardRecord.vue中的模态框逻辑,优化文件点击事件处理,修复ContactModal.vue中的数据提交逻辑,更新路由配置以支持新页面。
This commit is contained in:
parent
b65f38f02e
commit
579fed2e69
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -52,6 +52,7 @@ declare module 'vue' {
|
|||||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
|
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
NImage: typeof import('naive-ui')['NImage']
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
|
@ -21,7 +21,7 @@ html {
|
|||||||
// message
|
// message
|
||||||
--im-message-bg-color: #f7f7f7;
|
--im-message-bg-color: #f7f7f7;
|
||||||
--im-message-border-color: #efeff5;
|
--im-message-border-color: #efeff5;
|
||||||
--im-message-left-bg-color: #F4F4FC;
|
--im-message-left-bg-color: #fff;
|
||||||
--im-message-left-text-color: #333;
|
--im-message-left-text-color: #333;
|
||||||
--im-message-right-bg-color: #46299D;
|
--im-message-right-bg-color: #46299D;
|
||||||
--im-message-right-text-color: #fff;
|
--im-message-right-text-color: #fff;
|
||||||
|
@ -5,22 +5,21 @@ import { ServeGetForwardRecords } from '@/api/chat'
|
|||||||
import { MessageComponents } from '@/constant/message'
|
import { MessageComponents } from '@/constant/message'
|
||||||
import { ITalkRecord } from '@/types/chat'
|
import { ITalkRecord } from '@/types/chat'
|
||||||
import { useInject } from '@/hooks'
|
import { useInject } from '@/hooks'
|
||||||
|
import customModal from '@/components/common/customModal.vue'
|
||||||
const emit = defineEmits(['close'])
|
import { voiceToText } from '@/api/chat.js'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
msgId: {
|
msgId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const isShow=defineModel<boolean>('show')
|
||||||
const { showUserInfoModal } = useInject()
|
const { showUserInfoModal } = useInject()
|
||||||
const isShow = ref(true)
|
|
||||||
const items = ref<ITalkRecord[]>([])
|
const items = ref<ITalkRecord[]>([])
|
||||||
const title = ref('会话记录')
|
const title = ref('会话记录')
|
||||||
|
|
||||||
const onMaskClick = () => {
|
const onMaskClick = () => {
|
||||||
emit('close')
|
isShow.value=false
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLoadData = () => {
|
const onLoadData = () => {
|
||||||
@ -30,18 +29,92 @@ const onLoadData = () => {
|
|||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
items.value = res.data.items || []
|
items.value = res.data.items || []
|
||||||
|
|
||||||
title.value = `会话记录(${items.value.length})`
|
// title.value = `会话记录(${items.value.length})`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const dropdown=ref({
|
||||||
|
show:false,
|
||||||
|
x:'',
|
||||||
|
y:'',
|
||||||
|
options:[] as any,
|
||||||
|
item:{} as ITalkRecord,
|
||||||
|
})
|
||||||
|
const onConvertText =async (data: ITalkRecord) => {
|
||||||
|
data.is_convert_text = 1
|
||||||
|
const res = await voiceToText({msgId:data.msg_id,voiceUrl:data.extra.url})
|
||||||
|
if(res.code == 200){
|
||||||
|
data.extra.content = res.data.convText
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onloseConvertText=(data: ITalkRecord)=>{
|
||||||
|
data.is_convert_text = 0
|
||||||
|
}
|
||||||
|
const evnets = {
|
||||||
|
convertText: onConvertText,
|
||||||
|
closeConvertText:onloseConvertText
|
||||||
|
}
|
||||||
|
|
||||||
|
const onContextMenuHandle=(key:string)=>{
|
||||||
|
evnets[key] && evnets[key](dropdown.value.item)
|
||||||
|
closeDropdownMenu()
|
||||||
|
}
|
||||||
|
const closeDropdownMenu=()=>{
|
||||||
|
dropdown.value.show=false
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
onLoadData()
|
onLoadData()
|
||||||
})
|
})
|
||||||
|
const onContextMenu = (e:any,item: ITalkRecord) => {
|
||||||
|
dropdown.value.show=true
|
||||||
|
|
||||||
|
dropdown.value.x=e.clientX
|
||||||
|
dropdown.value.y=e.clientY
|
||||||
|
if(item.is_convert_text === 1){
|
||||||
|
dropdown.value.options=[{ label: '关闭转文字', key: 'closeConvertText' }]
|
||||||
|
}else{
|
||||||
|
dropdown.value.options=[{ label: '转文字', key: 'convertText' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown.value.item=item
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-modal
|
<customModal :closable="false" customCloseBtn v-model:show="isShow" :title="title" style="width: 997px;background-color: #F9F9FD;" :on-after-leave="onMaskClick">
|
||||||
|
<template #content>
|
||||||
|
<div class="main-box bg-#fff me-scrollbar me-scrollbar-thumb">
|
||||||
|
<Loading v-if="items.length === 0" />
|
||||||
|
|
||||||
|
<div v-for="item in items" :key="item.msg_id" class="message-item">
|
||||||
|
<div class="left-box pointer" @click="showUserInfoModal(item.user_id)">
|
||||||
|
<im-avatar :src="item.avatar" :size="38" :username="item.nickname" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-box">
|
||||||
|
<div class="msg-header">
|
||||||
|
<span class="name">{{ item.nickname }}</span>
|
||||||
|
<span class="time"> {{ item.created_at }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<component
|
||||||
|
@contextmenu.prevent="onContextMenu($event,item)"
|
||||||
|
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||||
|
:extra="item.extra"
|
||||||
|
:data="item"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 右键菜单 -->
|
||||||
|
<n-dropdown :show="dropdown.show" :x="dropdown.x" :y="dropdown.y" style="width: 142px;" :options="dropdown.options"
|
||||||
|
@select="onContextMenuHandle" @clickoutside="closeDropdownMenu" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</customModal>
|
||||||
|
<!-- <n-modal
|
||||||
v-model:show="isShow"
|
v-model:show="isShow"
|
||||||
preset="card"
|
preset="card"
|
||||||
:title="title"
|
:title="title"
|
||||||
@ -80,7 +153,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-modal>
|
</n-modal> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -94,10 +167,10 @@ onMounted(() => {
|
|||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding: 5px 15px;
|
padding: 24px 42px;
|
||||||
|
|
||||||
.left-box {
|
.left-box {
|
||||||
width: 30px;
|
width: 38px;
|
||||||
display: flex;
|
display: flex;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
@ -86,12 +86,10 @@ const strokeDashoffset = computed(() =>
|
|||||||
|
|
||||||
// 处理文件点击事件
|
// 处理文件点击事件
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
console.log('props.extra', props.extra);
|
|
||||||
|
|
||||||
window.open(
|
window.open(
|
||||||
`http://localhost:5500/?url=${props.extra.path}`,
|
`${window.location.origin}/office?url=${props.extra.path}`,
|
||||||
'_blank',
|
'_blank',
|
||||||
'width=800,height=600,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
'width=1200,height=900,left=200,top=200,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -33,7 +33,7 @@ const onClick = () => {
|
|||||||
<span>转发:聊天会话记录 ({{ extra.msg_ids.length }}条)</span>
|
<span>转发:聊天会话记录 ({{ extra.msg_ids.length }}条)</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ForwardRecord v-if="isShowRecord" :msg-id="data.msg_id" @close="isShowRecord = false" />
|
<ForwardRecord v-model:show="isShowRecord" :msg-id="data.msg_id" @close="isShowRecord = false" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ const { showUserInfoModal } = useInject()
|
|||||||
<template>
|
<template>
|
||||||
<div class="im-message-sys-text">
|
<div class="im-message-sys-text">
|
||||||
<div class="sys-text">
|
<div class="sys-text">
|
||||||
|
|
||||||
<a @click="showUserInfoModal(extra.owner_id)">
|
<a @click="showUserInfoModal(extra.owner_id)">
|
||||||
{{ extra.owner_name }}
|
{{ extra.owner_name }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -127,11 +127,12 @@ const onCancel = () => {
|
|||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
let data = checkedFilter.value.map((item: any) => {
|
let data = checkedFilter.value.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
receiver_id: item.receiver_id,
|
||||||
type: item.type
|
talk_type: item.talk_type
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
console.log('data', data);
|
||||||
|
console.log('checkedFilter.value', checkedFilter.value);
|
||||||
emit('on-submit', data)
|
emit('on-submit', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,11 @@ const routes = [
|
|||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: '404 NotFound',
|
name: '404 NotFound',
|
||||||
component: () => import('@/views/other/not-found.vue')
|
component: () => import('@/views/other/not-found.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/office',
|
||||||
|
name: 'office',
|
||||||
|
component: () => import('@/views/office/index.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export function isLoggedIn() {
|
|||||||
*/
|
*/
|
||||||
export function getAccessToken() {
|
export function getAccessToken() {
|
||||||
// return storage.get(AccessToken) || ''
|
// return storage.get(AccessToken) || ''
|
||||||
return JSON.parse(localStorage.getItem('token'))||'6e0b0a00c35d0e1613d8f9ce2303067e46a0c2d9941c959848ccf7c91e7eb0fe14690f485ae6b5e932196267c2af8a6089bda35a715c44ad565de15114eb2c82fddcba958dd35b80cd0980f7d6fa4dde04e3c5f1407e39ac1073432c4e9dc0031afbe520e8a6528eeba7e79cf2c97f4c6e07e4a466460ee50fc28b2f05ca5215db9ea522a4d76911dbdd5d863c107f1cb073da0c94091ce81c59c9bd2faba1de9552a38a8c9ac69bb4a8d562b41b127a1e92468d4b8a5749adf7c899a65c748940eda0d6c8834cdd8995b527ab6f56cf9a6ede9de78ce7938190030708b576238d62f018abb0d363ee4cb4c1a8235487c20938760bece6caaf5f3573f888d15a0f9e79c4b09bd5214c704c135be67de9b475e24addc92d662768128eef05ddc67d68d8c0f16b5293888508c2f7f87bd0766fa7609726c18f814f04551f6f54d7'
|
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22201760cb2a37a6703ccf06c9d5a5752e0c3798b552c606065aa0079caa2737c431030be4871e931d3dcb27c239d70b4ba5f2e533a36a0b9b784388409ccd3ad8c62c1de535f4fd865b4f3bf37d260ccc'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,20 +39,20 @@ const onMultiDelete = () => {
|
|||||||
dialogueStore.ApiDeleteRecord(msgIds)
|
dialogueStore.ApiDeleteRecord(msgIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onContactModal = (data: { id: number; type: number }[]) => {
|
const onContactModal = (data: { receiver_id: number; talk_type: number }[]) => {
|
||||||
let msg_ids = dialogueStore.selectItems.map((item: any) => item.msg_id)
|
let msg_ids = dialogueStore.selectItems.map((item: any) => item.msg_id)
|
||||||
|
|
||||||
let user_ids: number[] = []
|
let user_ids: number[] = []
|
||||||
let group_ids: number[] = []
|
let group_ids: number[] = []
|
||||||
|
|
||||||
for (let o of data) {
|
for (let o of data) {
|
||||||
if (o.type == 1) {
|
if (o.talk_type == 1) {
|
||||||
user_ids.push(o.id)
|
user_ids.push(o.receiver_id)
|
||||||
} else {
|
} else {
|
||||||
group_ids.push(o.id)
|
group_ids.push(o.receiver_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('user_ids',user_ids)
|
||||||
dialogueStore.ApiForwardRecord({
|
dialogueStore.ApiForwardRecord({
|
||||||
mode: forwardMode.value,
|
mode: forwardMode.value,
|
||||||
message_ids: msg_ids,
|
message_ids: msg_ids,
|
||||||
|
99
src/views/office/index.vue
Normal file
99
src/views/office/index.vue
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div id="office-div" ></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// 动态加载 OnlyOffice API 脚本(防止重复加载)
|
||||||
|
function loadScript(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (document.querySelector(`script[src="${src}"]`)) {
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.type = 'text/javascript'
|
||||||
|
script.src = src
|
||||||
|
script.onload = resolve
|
||||||
|
script.onerror = reject
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件类型
|
||||||
|
function getDocumentTypes(url) {
|
||||||
|
const extension = url.split('.').pop().toLowerCase()
|
||||||
|
const types = {
|
||||||
|
'docx': { fileType: 'docx', documentType: 'word' },
|
||||||
|
'doc': { fileType: 'doc', documentType: 'word' },
|
||||||
|
'xlsx': { fileType: 'xlsx', documentType: 'cell' },
|
||||||
|
'xls': { fileType: 'xls', documentType: 'cell' },
|
||||||
|
'pptx': { fileType: 'pptx', documentType: 'slide' },
|
||||||
|
'ppt': { fileType: 'ppt', documentType: 'slide' },
|
||||||
|
'pdf': { fileType: 'pdf', documentType: 'word' }
|
||||||
|
}
|
||||||
|
return types[extension] || { fileType: 'docx', documentType: 'word' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态插入 CSP meta
|
||||||
|
function insertCSPMeta() {
|
||||||
|
if (!document.querySelector('meta[http-equiv="Content-Security-Policy"]')) {
|
||||||
|
const meta = document.createElement('meta')
|
||||||
|
meta.httpEquiv = 'Content-Security-Policy'
|
||||||
|
meta.content = 'upgrade-insecure-requests'
|
||||||
|
document.head.appendChild(meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
insertCSPMeta()
|
||||||
|
await loadScript('https://onlyoffice.fontree.cn/web-apps/apps/api/documents/api.js')
|
||||||
|
const url = route.query.url
|
||||||
|
if (!url) {
|
||||||
|
alert('请提供文档 URL 参数')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const fileName = url.split('/').pop()
|
||||||
|
const { fileType, documentType } = getDocumentTypes(url)
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
new window.DocsAPI.DocEditor('office-div', {
|
||||||
|
document: {
|
||||||
|
fileType,
|
||||||
|
key: `doc_${fileName}_${Date.now()}`,
|
||||||
|
title: fileName,
|
||||||
|
url
|
||||||
|
},
|
||||||
|
documentType,
|
||||||
|
editorConfig: {
|
||||||
|
mode: 'view',
|
||||||
|
lang: 'zh-CN',
|
||||||
|
user: {
|
||||||
|
id: 'user_' + Date.now(),
|
||||||
|
name: '访客用户'
|
||||||
|
},
|
||||||
|
customization: {
|
||||||
|
chat: false,
|
||||||
|
commentAuthorOnly: false,
|
||||||
|
compactToolbar: true,
|
||||||
|
hideRightMenu: false,
|
||||||
|
compatibility: true,
|
||||||
|
showReviewChanges: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
iframe[name="frameEditor"] {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100vh !important;
|
||||||
|
min-height: 100vh !important;
|
||||||
|
border: none !important;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user