更新组件和API,新增NImage支持,优化文件上传功能,调整主题颜色,删除不必要的图片,改进用户界面和交互体验。
1
components.d.ts
vendored
@ -52,6 +52,7 @@ declare module 'vue' {
|
|||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
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']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NoticeEditor: typeof import('./src/components/group/manage/NoticeEditor.vue')['default']
|
NoticeEditor: typeof import('./src/components/group/manage/NoticeEditor.vue')['default']
|
||||||
|
@ -25,3 +25,7 @@ export const ServeRefreshToken = () => {
|
|||||||
export const ServeForgetPassword = (data) => {
|
export const ServeForgetPassword = (data) => {
|
||||||
return post('/api/v1/auth/forget', data)
|
return post('/api/v1/auth/forget', data)
|
||||||
}
|
}
|
||||||
|
// 获取用户信息服务
|
||||||
|
export const GetUserInfo = (data) => {
|
||||||
|
return post('/api/v1/users/info', data)
|
||||||
|
}
|
@ -45,10 +45,12 @@ export const ServeFindFriendApplyNum = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 搜索用户信息服务接口
|
// 搜索用户信息服务接口
|
||||||
|
// export const ServeSearchUser = (data) => {
|
||||||
|
// return get('/api/v1/contact/detail', data)
|
||||||
|
// }
|
||||||
export const ServeSearchUser = (data) => {
|
export const ServeSearchUser = (data) => {
|
||||||
return get('/api/v1/contact/detail', data)
|
return post('/api/v1/users/info', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索用户信息服务接口
|
// 搜索用户信息服务接口
|
||||||
export const ServeContactGroupList = (data) => {
|
export const ServeContactGroupList = (data) => {
|
||||||
return get('/api/v1/contact/group/list', data)
|
return get('/api/v1/contact/group/list', data)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// 默认主题
|
// 默认主题
|
||||||
html {
|
html {
|
||||||
--im-primary-color: #1890ff;
|
--im-primary-color: #462AA0;
|
||||||
--im-bg-color: #ffffff;
|
--im-bg-color: #ffffff;
|
||||||
--line-border-color: #f5f5f5;
|
--line-border-color: #f5f5f5;
|
||||||
--border-color: #eeeaea;
|
--border-color: #eeeaea;
|
||||||
|
BIN
src/assets/image/excel-icon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/excel-text.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
src/assets/image/file-icon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/file-text.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/pdf-text.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/image/ppt-icon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/ppt-text.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/image/word-icon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/word-text.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
@ -1,7 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { fileFormatSize } from '@/utils/strings'
|
import { fileFormatSize } from '@/utils/strings'
|
||||||
import { download, getFileNameSuffix } from '@/utils/functions'
|
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
import { useUploadsStore } from '@/store'
|
||||||
|
import pptText from '@/assets/image/ppt-text.png'
|
||||||
|
import excelText from '@/assets/image/excel-text.png'
|
||||||
|
import wordText from '@/assets/image/word-text.png'
|
||||||
|
import pdfText from '@/assets/image/pdf-text.png'
|
||||||
|
import fileText from '@/assets/image/file-text.png'
|
||||||
|
|
||||||
// 定义组件属性
|
// 定义组件属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -22,54 +27,76 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 控制文件上传时的播放状态
|
const uploadsStore = useUploadsStore()
|
||||||
const isPlaying = ref(false)
|
const isPlaying = ref(false)
|
||||||
|
|
||||||
/**
|
// 文件类型配置
|
||||||
* 切换播放状态
|
const fileTypes = {
|
||||||
* 在上传过程中可以暂停/继续
|
PDF: { icon: pdfText, color: '#DE4E4E', type: 'PDF' },
|
||||||
*/
|
PPT: { icon: pptText, color: '#B74B2B', type: 'PPT' },
|
||||||
const togglePlay = () => {
|
EXCEL: { icon: excelText, color: '#3C7F4B', type: 'EXCEL' },
|
||||||
isPlaying.value = !isPlaying.value
|
WORD: { icon: wordText, color: '#2750B2', type: 'WORD' },
|
||||||
console.log('播放状态:', isPlaying.value ? '播放中' : '暂停')
|
DEFAULT: { icon: fileText, color: '#747474', type: '文件' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Excel文件扩展名映射
|
||||||
* 从文件URL中提取并返回大写的文件扩展名
|
const EXCEL_EXTENSIONS = ['XLS', 'XLSX', 'CSV']
|
||||||
* @param {string} url - 文件的URL或名称
|
// Word文件扩展名映射
|
||||||
* @returns {string} 大写的文件扩展名
|
const WORD_EXTENSIONS = ['DOC', 'DOCX', 'RTF', 'DOT', 'DOTX']
|
||||||
*/
|
// PPT文件扩展名映射
|
||||||
function getFileExtensionUpperCase(url) {
|
const PPT_EXTENSIONS = ['PPT', 'PPTX', 'PPS', 'PPSX']
|
||||||
// 从URL提取文件名
|
|
||||||
const fileName = url.split('/').pop()
|
// 获取文件类型信息
|
||||||
// 提取扩展名并转换为大写
|
const fileInfo = computed(() => {
|
||||||
return fileName.split('.').pop().toUpperCase()
|
const extension = getFileExtension(props.extra.name)
|
||||||
|
if (EXCEL_EXTENSIONS.includes(extension)) {
|
||||||
|
return fileTypes.EXCEL
|
||||||
|
}
|
||||||
|
if (WORD_EXTENSIONS.includes(extension)) {
|
||||||
|
return fileTypes.WORD
|
||||||
|
}
|
||||||
|
if (PPT_EXTENSIONS.includes(extension)) {
|
||||||
|
return fileTypes.PPT
|
||||||
|
}
|
||||||
|
return fileTypes[extension] || fileTypes.DEFAULT
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
function getFileExtension(filename) {
|
||||||
|
const parts = filename.split('.')
|
||||||
|
return parts.length > 1 ? parts.pop().toUpperCase() : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换播放状态
|
||||||
|
const togglePlay = () => {
|
||||||
|
isPlaying.value = !isPlaying.value
|
||||||
|
|
||||||
|
if (props.extra.is_uploading && props.extra.upload_id) {
|
||||||
|
const action = isPlaying.value ? 'resumeUpload' : 'pauseUpload'
|
||||||
|
uploadsStore[action](props.extra.upload_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算SVG圆环进度条的参数
|
// 计算SVG圆环进度条的参数
|
||||||
const radius = 9 // 圆环半径
|
const radius = 9
|
||||||
const circumference = computed(() => 2 * Math.PI * radius) // 计算圆周长
|
const circumference = computed(() => 2 * Math.PI * radius)
|
||||||
// 根据上传百分比计算描边偏移量
|
|
||||||
const strokeDashoffset = computed(() =>
|
const strokeDashoffset = computed(() =>
|
||||||
circumference.value * (1 - props.extra.percentage / 100)
|
circumference.value * (1 - (props.extra.percentage || 0) / 100)
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-243px bg-#fff rounded-8px shadow-md px-14px pointer">
|
<div class="file-message">
|
||||||
<!-- 文件头部信息 -->
|
<!-- 文件头部信息 -->
|
||||||
<div class="flex py-14px pr-5px justify-between w-full" style="border-bottom: 1px solid #EEEEEE;">
|
<div class="file-header">
|
||||||
<!-- 文件名 -->
|
<!-- 文件名 -->
|
||||||
<div class="text-#1A1A1A text-14px">{{ extra.name }}</div>
|
<div class="file-name">{{ extra.name }}</div>
|
||||||
<!-- 文件图标区域 -->
|
<!-- 文件图标区域 -->
|
||||||
<div class="relative">
|
<div class="file-icon-container">
|
||||||
<img class="w-47.91px h-47.91px" src="@/assets/image/file-paper-line@2x.png" alt="文件图标">
|
<img class="file-icon" :src="fileInfo.icon" alt="文件图标">
|
||||||
<!-- 文件扩展名显示 - 非上传状态 -->
|
|
||||||
<div v-if="!extra.is_uploading" class="absolute top-11px left-16px text-#DE4E4E text-10px font-bold">
|
|
||||||
{{ getFileExtensionUpperCase(extra.name) }}
|
|
||||||
</div>
|
|
||||||
<!-- 上传进度圆环 - 上传状态 -->
|
<!-- 上传进度圆环 - 上传状态 -->
|
||||||
<div v-else class="absolute top-9px left-16px w-20px h-20px">
|
<div v-if="extra.is_uploading" class="progress-overlay">
|
||||||
<div class="circle-progress-container" @click="togglePlay">
|
<div class="circle-progress-container" @click="togglePlay">
|
||||||
<svg class="circle-progress" width="20" height="20" viewBox="0 0 20 20">
|
<svg class="circle-progress" width="20" height="20" viewBox="0 0 20 20">
|
||||||
<!-- 底色圆环 -->
|
<!-- 底色圆环 -->
|
||||||
@ -87,7 +114,7 @@ const strokeDashoffset = computed(() =>
|
|||||||
cy="10"
|
cy="10"
|
||||||
r="9"
|
r="9"
|
||||||
fill="transparent"
|
fill="transparent"
|
||||||
stroke="#D54C4B"
|
:stroke="fileInfo.color"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
:stroke-dasharray="circumference"
|
:stroke-dasharray="circumference"
|
||||||
:stroke-dashoffset="strokeDashoffset"
|
:stroke-dashoffset="strokeDashoffset"
|
||||||
@ -95,15 +122,13 @@ const strokeDashoffset = computed(() =>
|
|||||||
class="progress-circle"
|
class="progress-circle"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 暂停图标 - 播放中显示 -->
|
<!-- 暂停/播放图标 -->
|
||||||
<g v-if="isPlaying" class="pause-icon transform-rotate-90">
|
<g v-if="isPlaying" class="pause-icon">
|
||||||
<rect x="7" y="5" width="2" height="10" fill="#D54C4B" />
|
<rect x="7" y="5" width="2" height="10" :fill="fileInfo.color" />
|
||||||
<rect x="11" y="5" width="2" height="10" fill="#D54C4B" />
|
<rect x="11" y="5" width="2" height="10" :fill="fileInfo.color" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<!-- 播放图标 - 暂停时显示 -->
|
|
||||||
<g v-else class="play-icon">
|
<g v-else class="play-icon">
|
||||||
<rect x="6" y="6" width="8" height="8" fill="#D54C4B" />
|
<rect x="6" y="6" width="8" height="8" :fill="fileInfo.color" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@ -111,11 +136,66 @@ const strokeDashoffset = computed(() =>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 文件大小信息 -->
|
<!-- 文件大小信息 -->
|
||||||
<div class="text-#747474 text-12px pt-5px pb-11px">{{ fileFormatSize(extra.size) }}</div>
|
<div class="file-size">{{ fileFormatSize(extra.size) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.file-message {
|
||||||
|
width: 243px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 0 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-header {
|
||||||
|
display: flex;
|
||||||
|
padding: 14px 5px 14px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px solid #EEEEEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
color: #1A1A1A;
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-overlay {
|
||||||
|
background-color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 11px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-size {
|
||||||
|
color: #747474;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 5px 0 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.circle-progress-container {
|
.circle-progress-container {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -136,7 +216,7 @@ const strokeDashoffset = computed(() =>
|
|||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transform-rotate-90 {
|
.pause-icon {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -100,7 +100,7 @@ async function onPlay() {
|
|||||||
function pauseUpload(e) {
|
function pauseUpload(e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (props.extra.is_uploading && props.extra.upload_id) {
|
if (props.extra.is_uploading && props.extra.upload_id) {
|
||||||
uploadsStore.pauseVideoUpload(props.extra.upload_id)
|
uploadsStore.pauseUpload(props.extra.upload_id)
|
||||||
isPaused.value = true
|
isPaused.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ function resumeUpload(e) {
|
|||||||
console.log('resumeUpload')
|
console.log('resumeUpload')
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (props.extra.is_uploading && props.extra.upload_id) {
|
if (props.extra.is_uploading && props.extra.upload_id) {
|
||||||
uploadsStore.resumeVideoUpload(props.extra.upload_id)
|
uploadsStore.resumeUpload(props.extra.upload_id)
|
||||||
isPaused.value = false
|
isPaused.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ function retryUpload(e) {
|
|||||||
uploadFailed.value = false
|
uploadFailed.value = false
|
||||||
|
|
||||||
// 恢复上传
|
// 恢复上传
|
||||||
uploadsStore.resumeVideoUpload(props.extra.upload_id)
|
uploadsStore.resumeUpload(props.extra.upload_id)
|
||||||
message.success('正在重新上传视频...')
|
message.success('正在重新上传视频...')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { NModal, NInput, NScrollbar, NCheckbox, NTabs, NTab } from 'naive-ui'
|
|||||||
import { Search, Delete } from '@icon-park/vue-next'
|
import { Search, Delete } from '@icon-park/vue-next'
|
||||||
import { ServeGetContacts } from '@/api/contact'
|
import { ServeGetContacts } from '@/api/contact'
|
||||||
import { ServeGetGroups } from '@/api/group'
|
import { ServeGetGroups } from '@/api/group'
|
||||||
|
import XNModal from '@/components/x-naive-ui/x-n-modal/index.vue'
|
||||||
const emit = defineEmits(['close', 'on-submit'])
|
const emit = defineEmits(['close', 'on-submit'])
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
@ -130,20 +130,12 @@ onLoad()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-modal
|
<x-n-modal
|
||||||
v-model:show="isShowBox"
|
v-model:show="isShowBox"
|
||||||
preset="card"
|
|
||||||
title="选择联系人"
|
title="选择联系人"
|
||||||
class="modal-radius"
|
class="modal-radius"
|
||||||
style="max-width: 650px; height: 550px"
|
style="width: 997px; height: 740px;background-color: #F9F9FD"
|
||||||
:on-after-leave="onMaskClick"
|
:on-after-leave="onMaskClick"
|
||||||
:segmented="{
|
|
||||||
content: true,
|
|
||||||
footer: true
|
|
||||||
}"
|
|
||||||
:content-style="{
|
|
||||||
padding: 0
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<section class="el-container launch-box">
|
<section class="el-container launch-box">
|
||||||
<aside class="el-aside bdr-r" style="width: 240px">
|
<aside class="el-aside bdr-r" style="width: 240px">
|
||||||
@ -154,6 +146,7 @@ onLoad()
|
|||||||
<n-tab name="2"> 群聊 </n-tab>
|
<n-tab name="2"> 群聊 </n-tab>
|
||||||
<!-- <n-tab name="企业"> 企业 </n-tab> -->
|
<!-- <n-tab name="企业"> 企业 </n-tab> -->
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<header class="el-header sub-header">
|
<header class="el-header sub-header">
|
||||||
@ -256,7 +249,7 @@ onLoad()
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</x-n-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, reactive } from 'vue'
|
import { ref, computed, reactive } from 'vue'
|
||||||
import { NIcon, NModal, NButton, NInput, NDropdown, NPopover,NImage } from 'naive-ui'
|
|
||||||
import { CloseOne, Male, Female, SendOne } from '@icon-park/vue-next'
|
import { CloseOne, Male, Female, SendOne } from '@icon-park/vue-next'
|
||||||
import { ServeSearchUser } from '@/api/contact'
|
import { ServeSearchUser } from '@/api/contact'
|
||||||
import { ServeCreateContact } from '@/api/contact'
|
import { ServeCreateContact } from '@/api/contact'
|
||||||
@ -8,11 +7,10 @@ import { ServeContactGroupList, ServeContactMoveGroup, ServeEditContactRemark }
|
|||||||
import { useTalkStore } from '@/store'
|
import { useTalkStore } from '@/store'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import xNModal from '@/components/x-naive-ui/x-n-modal/index.vue'
|
import xNModal from '@/components/x-naive-ui/x-n-modal/index.vue'
|
||||||
|
import { NSkeleton } from 'naive-ui'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const talkStore = useTalkStore()
|
const talkStore = useTalkStore()
|
||||||
|
|
||||||
const emit = defineEmits(['update:show', 'update:uid', 'updateRemark'])
|
const emit = defineEmits(['update:show', 'update:uid', 'updateRemark'])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -26,7 +24,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const isOpenFrom = ref(false)
|
const isOpenFrom = ref(false)
|
||||||
const state: any = reactive({
|
const userInfo: any = ref({
|
||||||
id: 0,
|
id: 0,
|
||||||
avatar: '',
|
avatar: '',
|
||||||
gender: 0,
|
gender: 0,
|
||||||
@ -43,26 +41,26 @@ const editCardPopover: any = ref(false)
|
|||||||
const modelRemark = ref('')
|
const modelRemark = ref('')
|
||||||
|
|
||||||
const options = ref<any>([])
|
const options = ref<any>([])
|
||||||
const groupName = computed(() => {
|
// const groupName = computed(() => {
|
||||||
const item = options.value.find((item: any) => {
|
// const item = options.value.find((item: any) => {
|
||||||
return item.key == state.group_id
|
// return item.key == state.group_id
|
||||||
})
|
// })
|
||||||
|
|
||||||
if (item) {
|
// if (item) {
|
||||||
return item.label
|
// return item.label
|
||||||
}
|
// }
|
||||||
|
|
||||||
return '未设置分组'
|
// return '未设置分组'
|
||||||
})
|
// })
|
||||||
|
|
||||||
const onLoadData = () => {
|
const onLoadData = () => {
|
||||||
ServeSearchUser({
|
ServeSearchUser({
|
||||||
user_id: props.uid
|
erp_user_id: props.uid
|
||||||
}).then(({ code, data }) => {
|
}).then(({ code, data }) => {
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
Object.assign(state, data)
|
userInfo.value = data
|
||||||
|
|
||||||
modelRemark.value = state.remark
|
// modelRemark.value = state.remark
|
||||||
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
} else {
|
} else {
|
||||||
@ -70,15 +68,15 @@ const onLoadData = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ServeContactGroupList().then((res) => {
|
// ServeContactGroupList().then((res) => {
|
||||||
if (res.code == 200) {
|
// if (res.code == 200) {
|
||||||
let items = res.data.items || []
|
// let items = res.data.items || []
|
||||||
options.value = []
|
// options.value = []
|
||||||
for (const iter of items) {
|
// for (const iter of items) {
|
||||||
options.value.push({ label: iter.name, key: iter.id })
|
// options.value.push({ label: iter.name, key: iter.id })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onToTalk = () => {
|
const onToTalk = () => {
|
||||||
@ -86,84 +84,84 @@ const onToTalk = () => {
|
|||||||
emit('update:show', false)
|
emit('update:show', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onJoinContact = () => {
|
// const onJoinContact = () => {
|
||||||
if (!state.text.length) {
|
// if (!state.text.length) {
|
||||||
return window['$message'].info('备注信息不能为空')
|
// return window['$message'].info('备注信息不能为空')
|
||||||
}
|
// }
|
||||||
|
|
||||||
ServeCreateContact({
|
// ServeCreateContact({
|
||||||
friend_id: props.uid,
|
// friend_id: props.uid,
|
||||||
remark: state.text
|
// remark: state.text
|
||||||
}).then((res) => {
|
// }).then((res) => {
|
||||||
if (res.code == 200) {
|
// if (res.code == 200) {
|
||||||
isOpenFrom.value = false
|
// isOpenFrom.value = false
|
||||||
window['$message'].success('申请发送成功')
|
// window['$message'].success('申请发送成功')
|
||||||
} else {
|
// } else {
|
||||||
window['$message'].error(res.message)
|
// window['$message'].error(res.message)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
const onChangeRemark = () => {
|
// const onChangeRemark = () => {
|
||||||
ServeEditContactRemark({
|
// ServeEditContactRemark({
|
||||||
friend_id: props.uid,
|
// friend_id: props.uid,
|
||||||
remark: modelRemark.value
|
// remark: modelRemark.value
|
||||||
}).then(({ code, message }) => {
|
// }).then(({ code, message }) => {
|
||||||
if (code == 200) {
|
// if (code == 200) {
|
||||||
editCardPopover.value.setShow(false)
|
// editCardPopover.value.setShow(false)
|
||||||
window['$message'].success('备注成功')
|
// window['$message'].success('备注成功')
|
||||||
state.remark = modelRemark.value
|
// state.remark = modelRemark.value
|
||||||
|
|
||||||
emit('updateRemark', {
|
// emit('updateRemark', {
|
||||||
user_id: props.uid,
|
// user_id: props.uid,
|
||||||
remark: modelRemark.value
|
// remark: modelRemark.value
|
||||||
})
|
// })
|
||||||
} else {
|
// } else {
|
||||||
window['$message'].error(message)
|
// window['$message'].error(message)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
const handleSelectGroup = (value) => {
|
// const handleSelectGroup = (value) => {
|
||||||
ServeContactMoveGroup({
|
// ServeContactMoveGroup({
|
||||||
user_id: props.uid,
|
// user_id: props.uid,
|
||||||
group_id: value
|
// group_id: value
|
||||||
}).then(({ code, message }) => {
|
// }).then(({ code, message }) => {
|
||||||
if (code == 200) {
|
// if (code == 200) {
|
||||||
state.group_id = value
|
// state.group_id = value
|
||||||
window['$message'].success('分组修改成功')
|
// window['$message'].success('分组修改成功')
|
||||||
} else {
|
// } else {
|
||||||
window['$message'].error(message)
|
// window['$message'].error(message)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
const reset = () => {
|
// const reset = () => {
|
||||||
loading.value = true
|
// loading.value = true
|
||||||
|
|
||||||
Object.assign(state, {
|
// Object.assign(state, {
|
||||||
id: 0,
|
// id: 0,
|
||||||
avatar: '',
|
// avatar: '',
|
||||||
gender: 0,
|
// gender: 0,
|
||||||
mobile: '',
|
// mobile: '',
|
||||||
motto: '',
|
// motto: '',
|
||||||
nickname: '',
|
// nickname: '',
|
||||||
remark: '',
|
// remark: '',
|
||||||
email: '',
|
// email: '',
|
||||||
status: 1,
|
// status: 1,
|
||||||
text: ''
|
// text: ''
|
||||||
})
|
// })
|
||||||
|
|
||||||
isOpenFrom.value = false
|
// isOpenFrom.value = false
|
||||||
}
|
// }
|
||||||
|
|
||||||
const onUpdate = (value) => {
|
// const onUpdate = (value) => {
|
||||||
if (!value) {
|
// if (!value) {
|
||||||
setTimeout(reset, 100)
|
// setTimeout(reset, 100)
|
||||||
}
|
// }
|
||||||
|
|
||||||
emit('update:show', value)
|
// emit('update:show', value)
|
||||||
}
|
// }
|
||||||
|
|
||||||
const onAfterEnter = () => {
|
const onAfterEnter = () => {
|
||||||
onLoadData()
|
onLoadData()
|
||||||
@ -171,201 +169,80 @@ const onAfterEnter = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<x-n-modal content-style="padding:0;" :closable="false" class="w-311px min-h-445px" style="border-radius: 10px;overflow:hidden;" :show="show" :on-update:show="onUpdate" :on-after-enter="onAfterEnter">
|
<x-n-modal content-style="padding:0;" :closable="false" class="w-311px min-h-445px" style="border-radius: 10px;overflow:hidden;" :show="show" :on-after-enter="onAfterEnter">
|
||||||
<div class="section relative px-7px pt-82px pb-20px" v-loading="loading">
|
<div class="section relative px-7px pt-82px pb-20px">
|
||||||
<div class="absolute top-9px right-7px pointer" @click="onUpdate(false)" >
|
<div class="absolute top-9px right-7px pointer z-10" @click="emit('update:show', false)">
|
||||||
<img class="w-20px h-20px" src="@/assets/image/close.png" alt="">
|
<img class="w-20px h-20px" src="@/assets/image/close.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex py-10px bg-#fff px-16px rounded-4px items-center mb-10px">
|
|
||||||
<div class="w-59px h-59px rounded-8px mr-12px overflow-hidden">
|
<template v-if="loading">
|
||||||
<n-image width="59" :src="state.avatar" >
|
<div class="flex py-10px bg-#fff px-16px rounded-4px items-center mb-10px">
|
||||||
|
<div class="w-59px h-59px rounded-8px mr-12px overflow-hidden">
|
||||||
</n-image>
|
<n-skeleton circle height="59px" width="59px" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full">
|
||||||
|
<n-skeleton text style="width: 80%; margin-bottom: 5px;" />
|
||||||
|
<n-skeleton text style="width: 60%;" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-#fff rounded-4px mb-20px">
|
||||||
|
<div class="flex px-15px py-9px" v-for="i in 6" :key="i">
|
||||||
|
<n-skeleton text style="width: 30%; margin-right: 10px;" />
|
||||||
|
<n-skeleton text style="width: 60%;" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-#000 text-16px mb-5px">{{ state.nickname }}</div>
|
<n-skeleton text style="width: 100%; height: 42px; border-radius: 4px;" />
|
||||||
<div class="text-#ACACAC text-12px">工号:{{ state.job_num }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<div class="bg-#fff rounded-4px mb-20px">
|
|
||||||
<div class="flex px-15px py-9px">
|
<template v-else>
|
||||||
<div class="text-#000 text-12px w-84px">公司别</div>
|
<div class="flex py-10px bg-#fff px-16px rounded-4px items-center mb-10px">
|
||||||
<div class="text-#747474 text-12px">江苏泰丰文化传播股份有限公司</div>
|
<div class="w-59px h-59px rounded-8px mr-12px overflow-hidden">
|
||||||
</div>
|
<n-image width="59" :src="userInfo.avatar" >
|
||||||
<div class="flex px-15px py-9px">
|
|
||||||
<div class="text-#000 text-12px w-84px">主管</div>
|
|
||||||
<div class="text-#747474 text-12px">江苏泰丰文化传播股份有限公司</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex px-15px py-9px">
|
|
||||||
<div class="text-#000 text-12px w-84px">部门</div>
|
|
||||||
<div class="text-#747474 text-12px">江苏泰丰文化传播股份有限公司</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex px-15px py-9px">
|
|
||||||
<div class="text-#000 text-12px w-84px">手机号</div>
|
|
||||||
<div class="text-#747474 text-12px">{{ state.mobile }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex px-15px py-9px">
|
|
||||||
<div class="text-#000 text-12px w-84px">岗位</div>
|
|
||||||
<div class="text-#747474 text-12px">江苏泰丰文化传播股份有限公司</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex px-15px py-9px">
|
|
||||||
<div class="text-#000 text-12px w-84px">入职日期</div>
|
|
||||||
<div class="text-#747474 text-12px">江苏泰丰文化传播股份有限公司</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<n-button block color="#EEE9F8" text-color="#46299D" @click="onToTalk">
|
|
||||||
<div class="flex items-center justify-center py-11px">
|
|
||||||
<img class="w-19.8px h-20px mr-15px" src="@/assets/image/faxi@2x.png" alt="">
|
|
||||||
<span>发送消息</span>
|
|
||||||
</div>
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
<!-- <section class="el-container is-vertical">
|
|
||||||
<header class="el-header header">
|
|
||||||
<im-avatar
|
|
||||||
class="avatar"
|
|
||||||
:size="100"
|
|
||||||
:src="state.avatar"
|
|
||||||
:username="state.remark || state.nickname"
|
|
||||||
:font-size="30"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="gender" v-show="state.gender > 0">
|
</n-image>
|
||||||
<n-icon v-if="state.gender == 1" :component="Male" color="#508afe" />
|
|
||||||
<n-icon v-if="state.gender == 2" :component="Female" color="#ff5722" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div class="close" @click="onUpdate(false)">
|
<div class="text-#000 text-16px mb-5px">{{ userInfo.nickname }}</div>
|
||||||
<close-one theme="outline" size="22" fill="#fff" :strokeWidth="2" />
|
<div class="text-#ACACAC text-12px">工号:{{ userInfo.job_num }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="nickname text-ellipsis">
|
<div class="bg-#fff rounded-4px mb-20px">
|
||||||
{{ state.remark || state.nickname || '未设置昵称' }}
|
<div class="flex px-15px py-9px">
|
||||||
|
<div class="text-#000 text-12px w-84px">公司别</div>
|
||||||
|
<div class="text-#747474 text-12px">{{ userInfo.company_name }}</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
<div class="flex px-15px py-9px">
|
||||||
|
<div class="text-#000 text-12px w-84px">主管</div>
|
||||||
<main class="el-main main me-scrollbar me-scrollbar-thumb">
|
<div class="text-#747474 text-12px">{{ userInfo.leaders?.map(x=>x.user_name)?.join(',') }}</div>
|
||||||
<div class="motto">
|
|
||||||
{{ state.motto || '编辑个签,展示我的独特态度。' }}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex px-15px py-9px">
|
||||||
<div class="infos">
|
<div class="text-#000 text-12px w-84px">部门</div>
|
||||||
<div class="info-item">
|
<div class="text-#747474 text-12px">{{ userInfo.erp_dept_position?.map(x=>x.department_name)?.join(',') }}</div>
|
||||||
<span class="name">工号 :</span>
|
|
||||||
<span class="text">{{ state.job_num}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="name">手机 :</span>
|
|
||||||
<span class="text">{{ state.mobile }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="name">昵称 :</span>
|
|
||||||
<span class="text text-ellipsis">{{ state.nickname || '-' }} </span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="name">性别 :</span>
|
|
||||||
<span class="text">{{
|
|
||||||
state.gender == 1 ? '男' : state.gender == 2 ? '女' : '未知'
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item" v-if="state.friend_status == 2">
|
|
||||||
<span class="name">备注 :</span>
|
|
||||||
<n-popover trigger="click" placement="top-start" ref="editCardPopover">
|
|
||||||
<template #trigger>
|
|
||||||
<span class="text edit pointer text-ellipsis">
|
|
||||||
{{ state.remark || '未设置' }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #header> 设置备注 </template>
|
|
||||||
|
|
||||||
<div style="display: flex">
|
|
||||||
<n-input
|
|
||||||
type="text"
|
|
||||||
placeholder="请填写备注"
|
|
||||||
:autofocus="true"
|
|
||||||
maxlength="10"
|
|
||||||
v-model:value="modelRemark"
|
|
||||||
@keydown.enter="onChangeRemark"
|
|
||||||
/>
|
|
||||||
<n-button
|
|
||||||
type="primary"
|
|
||||||
text-color="#ffffff"
|
|
||||||
class="mt-l5"
|
|
||||||
@click="onChangeRemark"
|
|
||||||
>
|
|
||||||
确定
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</n-popover>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="name">邮箱 :</span>
|
|
||||||
<span class="text">{{ state.email || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item" v-if="state.friend_status == 2">
|
|
||||||
<span class="name">分组 :</span>
|
|
||||||
<n-dropdown
|
|
||||||
trigger="click"
|
|
||||||
placement="top-start"
|
|
||||||
:show-arrow="true"
|
|
||||||
:options="options"
|
|
||||||
@select="handleSelectGroup"
|
|
||||||
>
|
|
||||||
<span class="text edit pointer">{{ groupName }}</span>
|
|
||||||
</n-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="name">入职时间 :</span>
|
|
||||||
<span class="text">{{ state.enter_date}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
<div class="flex px-15px py-9px">
|
||||||
|
<div class="text-#000 text-12px w-84px">手机号</div>
|
||||||
<footer v-if="state.friend_status == 2" class="el-footer footer bdr-t flex-center">
|
<div class="text-#747474 text-12px">{{ userInfo.tel_num }}</div>
|
||||||
<n-button
|
</div>
|
||||||
round
|
<div class="flex px-15px py-9px">
|
||||||
block
|
<div class="text-#000 text-12px w-84px">岗位</div>
|
||||||
type="primary"
|
<div class="text-#747474 text-12px">{{ userInfo.erp_dept_position?.map(x=>x.position_name)?.join(',') }}</div>
|
||||||
text-color="#ffffff"
|
</div>
|
||||||
@click="onToTalk"
|
<div class="flex px-15px py-9px">
|
||||||
style="width: 91%"
|
<div class="text-#000 text-12px w-84px">入职日期</div>
|
||||||
>
|
<div class="text-#747474 text-12px">{{ userInfo.enter_date }}</div>
|
||||||
<template #icon>
|
</div>
|
||||||
<n-icon :component="SendOne" />
|
</div>
|
||||||
</template>
|
<div>
|
||||||
发送消息
|
<n-button block color="#EEE9F8" text-color="#46299D" @click="onToTalk">
|
||||||
|
<div class="flex items-center justify-center py-11px">
|
||||||
|
<img class="w-19.8px h-20px mr-15px" src="@/assets/image/faxi@2x.png" alt="">
|
||||||
|
<span>发送消息</span>
|
||||||
|
</div>
|
||||||
</n-button>
|
</n-button>
|
||||||
</footer>
|
</div>
|
||||||
|
</template>
|
||||||
<footer v-else-if="state.friend_status == 1" class="el-footer footer bdr-t flex-center">
|
|
||||||
<template v-if="isOpenFrom">
|
|
||||||
<n-input
|
|
||||||
type="text"
|
|
||||||
placeholder="请填写申请备注"
|
|
||||||
v-model:value="state.text"
|
|
||||||
@keydown.enter="onJoinContact"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<n-button type="primary" text-color="#ffffff" class="mt-l5" @click="onJoinContact">
|
|
||||||
确定
|
|
||||||
</n-button>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<n-button
|
|
||||||
type="primary"
|
|
||||||
text-color="#ffffff"
|
|
||||||
block
|
|
||||||
round
|
|
||||||
style="width: 91%"
|
|
||||||
@click="isOpenFrom = true"
|
|
||||||
>
|
|
||||||
添加好友
|
|
||||||
</n-button>
|
|
||||||
</template>
|
|
||||||
</footer>
|
|
||||||
</section> -->
|
|
||||||
</div>
|
</div>
|
||||||
</x-n-modal>
|
</x-n-modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -80,26 +80,26 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
return this.items.find((item) => item.client_upload_id === clientUploadId)
|
return this.items.find((item) => item.client_upload_id === clientUploadId)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 暂停文件上传
|
// // 暂停文件上传
|
||||||
pauseUpload(uploadId: string) {
|
// pauseUpload(uploadId: string) {
|
||||||
const item = this.findItem(uploadId)
|
// const item = this.findItem(uploadId)
|
||||||
if (!item) return
|
// if (!item) return
|
||||||
|
|
||||||
item.is_paused = true
|
// item.is_paused = true
|
||||||
console.log(`暂停上传: ${uploadId}`)
|
// console.log(`暂停上传: ${uploadId}`)
|
||||||
},
|
// },
|
||||||
|
|
||||||
// 恢复文件上传
|
// 恢复文件上传
|
||||||
resumeUpload(uploadId: string) {
|
// resumeUpload(uploadId: string) {
|
||||||
const item = this.findItem(uploadId)
|
// const item = this.findItem(uploadId)
|
||||||
if (!item) return
|
// if (!item) return
|
||||||
|
|
||||||
item.is_paused = false
|
// item.is_paused = false
|
||||||
console.log(`恢复上传: ${uploadId}`)
|
// console.log(`恢复上传: ${uploadId}`)
|
||||||
|
|
||||||
// 继续上传
|
// // 继续上传
|
||||||
this.triggerUpload(uploadId)
|
// this.triggerUpload(uploadId)
|
||||||
},
|
// },
|
||||||
|
|
||||||
// 发送上传消息
|
// 发送上传消息
|
||||||
async sendUploadMessage(item: any) {
|
async sendUploadMessage(item: any) {
|
||||||
@ -114,7 +114,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 初始化视频上传(使用分片上传方式)
|
// 初始化上传(使用分片上传方式)
|
||||||
async initUploadFile(
|
async initUploadFile(
|
||||||
file: File,
|
file: File,
|
||||||
talkType: number,
|
talkType: number,
|
||||||
@ -306,16 +306,16 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 暂停视频上传
|
|
||||||
pauseVideoUpload(clientUploadId: string) {
|
pauseUpload(clientUploadId: string) {
|
||||||
const item = this.findItemByClientId(clientUploadId)
|
const item = this.findItemByClientId(clientUploadId)
|
||||||
if (!item) return
|
if (!item) return
|
||||||
|
|
||||||
item.is_paused = true
|
item.is_paused = true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 恢复视频上传
|
// 恢复上传
|
||||||
resumeVideoUpload(clientUploadId: string) {
|
resumeUpload(clientUploadId: string) {
|
||||||
const item = this.findItemByClientId(clientUploadId)
|
const item = this.findItemByClientId(clientUploadId)
|
||||||
if (!item) return
|
if (!item) return
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { ServeFindFriendApplyNum } from '@/api/contact'
|
|||||||
import { ServeGroupApplyUnread } from '@/api/group'
|
import { ServeGroupApplyUnread } from '@/api/group'
|
||||||
import { delAccessToken } from '@/utils/auth'
|
import { delAccessToken } from '@/utils/auth'
|
||||||
import { storage } from '@/utils/storage'
|
import { storage } from '@/utils/storage'
|
||||||
|
import { GetUserInfo } from '@/api/auth'
|
||||||
interface UserStoreState {
|
interface UserStoreState {
|
||||||
uid: number
|
uid: number
|
||||||
nickname: string
|
nickname: string
|
||||||
@ -35,7 +35,7 @@ export const useUserStore = defineStore('user', {
|
|||||||
online: false, // 在线状态
|
online: false, // 在线状态
|
||||||
isQiye: false,
|
isQiye: false,
|
||||||
isContactApply: false,
|
isContactApply: false,
|
||||||
isGroupApply: false
|
isGroupApply: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {},
|
getters: {},
|
||||||
|
@ -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'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b89574a563986daa80674dd774ef18032ee6016a202902c95452e1e81931358d4d3cb7f0db0c6fc66f406f57e411cb1e2aeb77318f7c36b2b61f48c4c645d27920f05c204fe133ab9bfa481e9c1ae2e384c'
|
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d228655b2039673c1108d29c13b75d1fc2bfd3d1071f49b461822d6c2a320ae711492ac514d1a31043c8b7f2d10ff9e3869928844485d0ed575b936311797f3446e4a6c7ee1d9a63aba9445bc8b41c89143'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,7 +13,8 @@ import SkipBottom from './SkipBottom.vue'
|
|||||||
import { ITalkRecord } from '@/types/chat'
|
import { ITalkRecord } from '@/types/chat'
|
||||||
import { EditorConst } from '@/constant/event-bus'
|
import { EditorConst } from '@/constant/event-bus'
|
||||||
import { useInject, useTalkRecord, useUtil } from '@/hooks'
|
import { useInject, useTalkRecord, useUtil } from '@/hooks'
|
||||||
|
import { ExclamationCircleFilled } from '@ant-design/icons-vue';
|
||||||
|
import { useUserStore } from '@/store'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
uid: {
|
uid: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -39,17 +40,20 @@ const { useMessage } = useUtil()
|
|||||||
const { dropdown, showDropdownMenu, closeDropdownMenu } = useMenu()
|
const { dropdown, showDropdownMenu, closeDropdownMenu } = useMenu()
|
||||||
const { showUserInfoModal } = useInject()
|
const { showUserInfoModal } = useInject()
|
||||||
const dialogueStore = useDialogueStore()
|
const dialogueStore = useDialogueStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
// const showUserInfoModal = (uid: number) => {
|
||||||
|
// userStore.getUserInfo(uid)
|
||||||
|
// }
|
||||||
watch(() => records, (newValue, oldValue) => {
|
watch(() => records, (newValue, oldValue) => {
|
||||||
console.log(newValue);
|
console.log(newValue);
|
||||||
|
|
||||||
},{deep:true,immediate:true})
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
// 置底按钮
|
// 置底按钮
|
||||||
const skipBottom = ref(false)
|
const skipBottom = ref(false)
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
console.log(records.value,'records.value');
|
console.log(records.value, 'records.value');
|
||||||
},1000)
|
}, 1000)
|
||||||
// 是否显示消息时间
|
// 是否显示消息时间
|
||||||
const isShowTalkTime = (index: number, datetime: string) => {
|
const isShowTalkTime = (index: number, datetime: string) => {
|
||||||
if (datetime == undefined) {
|
if (datetime == undefined) {
|
||||||
@ -276,11 +280,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div
|
<div id="imChatPanel" class="me-scrollbar me-scrollbar-thumb talk-container" @scroll="onPanelScroll($event)">
|
||||||
id="imChatPanel"
|
|
||||||
class="me-scrollbar me-scrollbar-thumb talk-container"
|
|
||||||
@scroll="onPanelScroll($event)"
|
|
||||||
>
|
|
||||||
<!-- 数据加载状态栏 -->
|
<!-- 数据加载状态栏 -->
|
||||||
<div class="load-toolbar pointer">
|
<div class="load-toolbar pointer">
|
||||||
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
||||||
@ -288,88 +288,54 @@ onMounted(() => {
|
|||||||
<span v-else class="no-more"> 没有更多消息了 </span>
|
<span v-else class="no-more"> 没有更多消息了 </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="message-item" v-for="(item, index) in records" :key="item.msg_id" :id="item.msg_id">
|
||||||
class="message-item"
|
|
||||||
v-for="(item, index) in records"
|
|
||||||
:key="item.msg_id"
|
|
||||||
:id="item.msg_id"
|
|
||||||
>
|
|
||||||
<!-- 系统消息 -->
|
<!-- 系统消息 -->
|
||||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||||
<component
|
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" />
|
||||||
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
|
||||||
:extra="item.extra"
|
|
||||||
:data="item"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 撤回消息 -->
|
<!-- 撤回消息 -->
|
||||||
<div v-else-if="item.is_revoke == 1" class="message-box">
|
<div v-else-if="item.is_revoke == 1" class="message-box">
|
||||||
<revoke-message
|
<revoke-message :login_uid="uid" :user_id="item.user_id" :nickname="item.nickname" :talk_type="item.talk_type"
|
||||||
:login_uid="uid"
|
:datetime="item.created_at" />
|
||||||
:user_id="item.user_id"
|
|
||||||
:nickname="item.nickname"
|
|
||||||
:talk_type="item.talk_type"
|
|
||||||
:datetime="item.created_at"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-else class="message-box record-box" :class="{
|
||||||
v-else
|
'direction-rt': item.float == 'right',
|
||||||
class="message-box record-box"
|
'multi-select': dialogueStore.isOpenMultiSelect,
|
||||||
:class="{
|
'multi-select-check': item.isCheck
|
||||||
'direction-rt': item.float == 'right',
|
}">
|
||||||
'multi-select': dialogueStore.isOpenMultiSelect,
|
|
||||||
'multi-select-check': item.isCheck
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<!-- 多选按钮 -->
|
<!-- 多选按钮 -->
|
||||||
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
|
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
|
||||||
<n-checkbox
|
<n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" />
|
||||||
size="small"
|
|
||||||
:checked="item.isCheck"
|
|
||||||
@update:checked="item.isCheck = !item.isCheck"
|
|
||||||
/>
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 头像信息 -->
|
<!-- 头像信息 -->
|
||||||
|
|
||||||
<aside class="avatar-column">
|
<aside class="avatar-column">
|
||||||
<im-avatar
|
<im-avatar class="pointer" :src="item.avatar" :size="42" :username="item.nickname"
|
||||||
class="pointer"
|
@click="showUserInfoModal(item.erp_user_id)" />
|
||||||
:src="item.avatar"
|
|
||||||
:size="42"
|
|
||||||
:username="item.nickname"
|
|
||||||
@click="showUserInfoModal(item.user_id)"
|
|
||||||
/>
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 主体信息 -->
|
<!-- 主体信息 -->
|
||||||
<main class="main-column">
|
<main class="main-column">
|
||||||
<div class="talk-title">
|
<div class="talk-title">
|
||||||
<span
|
<span class="nickname pointer" v-show="talk_type == 2 && item.float == 'left'"
|
||||||
class="nickname pointer"
|
@click="onClickNickname(item)">
|
||||||
v-show="talk_type == 2 && item.float == 'left'"
|
|
||||||
@click="onClickNickname(item)"
|
|
||||||
>
|
|
||||||
<span class="at">@</span>{{ item.nickname }}
|
<span class="at">@</span>{{ item.nickname }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ parseTime(item.created_at, '{y}/{m}/{d} {h}:{i}') }}</span>
|
<span>{{ parseTime(item.created_at, '{y}/{m}/{d} {h}:{i}') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="talk-content" :class="{ pointer: dialogueStore.isOpenMultiSelect }" @click="onRowClick(item)">
|
||||||
class="talk-content"
|
|
||||||
:class="{ pointer: dialogueStore.isOpenMultiSelect }"
|
|
||||||
@click="onRowClick(item)"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
|
||||||
:extra="item.extra"
|
|
||||||
:data="item"
|
|
||||||
:max-width="true"
|
|
||||||
:source="'panel'"
|
|
||||||
@contextmenu.prevent="onContextMenu($event, item)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item"
|
||||||
|
:max-width="true" :source="'panel'" @contextmenu.prevent="onContextMenu($event, item)" />
|
||||||
|
<div class="mr-10px"> <n-button text style="font-size: 20px">
|
||||||
|
<n-icon color="#CF3050">
|
||||||
|
<ExclamationCircleFilled />
|
||||||
|
</n-icon>
|
||||||
|
</n-button></div>
|
||||||
<!-- <div class="talk-tools">
|
<!-- <div class="talk-tools">
|
||||||
<template v-if="talk_type == 1 && item.float == 'right'">
|
<template v-if="talk_type == 1 && item.float == 'right'">
|
||||||
<loading
|
<loading
|
||||||
@ -385,19 +351,11 @@ onMounted(() => {
|
|||||||
<span v-show="item.send_status != 1"> 已送达 </span>
|
<span v-show="item.send_status != 1"> 已送达 </span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<n-icon
|
<n-icon class="more-tools pointer" :component="MoreThree" @click="onContextMenu($event, item)" />
|
||||||
class="more-tools pointer"
|
</div> -->
|
||||||
:component="MoreThree"
|
|
||||||
@click="onContextMenu($event, item)"
|
|
||||||
/>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-if="item.extra.reply" class="talk-reply pointer" @click="onJumpMessage(item.extra?.reply?.msg_id)">
|
||||||
v-if="item.extra.reply"
|
|
||||||
class="talk-reply pointer"
|
|
||||||
@click="onJumpMessage(item.extra?.reply?.msg_id)"
|
|
||||||
>
|
|
||||||
<n-icon :component="ToTop" size="14" class="icon-top" />
|
<n-icon :component="ToTop" size="14" class="icon-top" />
|
||||||
<span class="ellipsis">
|
<span class="ellipsis">
|
||||||
回复 {{ item.extra?.reply?.nickname }}:
|
回复 {{ item.extra?.reply?.nickname }}:
|
||||||
@ -418,15 +376,8 @@ onMounted(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 右键菜单 -->
|
<!-- 右键菜单 -->
|
||||||
<n-dropdown
|
<n-dropdown :show="dropdown.show" :x="dropdown.x" :y="dropdown.y" style="width: 142px;" :options="dropdown.options"
|
||||||
:show="dropdown.show"
|
@select="onContextMenuHandle" @clickoutside="closeDropdownMenu" />
|
||||||
:x="dropdown.x"
|
|
||||||
:y="dropdown.y"
|
|
||||||
style="width: 142px;"
|
|
||||||
:options="dropdown.options"
|
|
||||||
@select="onContextMenuHandle"
|
|
||||||
@clickoutside="closeDropdownMenu"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -451,6 +402,7 @@ onMounted(() => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 38px;
|
line-height: 38px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
.no-more {
|
.no-more {
|
||||||
color: #b9b3b3;
|
color: #b9b3b3;
|
||||||
}
|
}
|
||||||
@ -528,6 +480,7 @@ onMounted(() => {
|
|||||||
color: var(--im-text-color);
|
color: var(--im-text-color);
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
.at {
|
.at {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -144,34 +144,32 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
dialogueStore.talk.username,
|
dialogueStore.talk.username,
|
||||||
uploadId,
|
uploadId,
|
||||||
async (percentage) => {
|
async (percentage) => {
|
||||||
console.log('percentage', percentage)
|
|
||||||
// 更新消息进度的回调
|
|
||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
dialogueStore.updateUploadProgress(uploadId, percentage)
|
||||||
},
|
},
|
||||||
async (videoData) => {
|
async () => {
|
||||||
console.log('videoData', videoData)
|
dialogueStore.batchDelDialogueRecord([uploadId])
|
||||||
// 上传完成后的回调
|
// console.log('videoData', videoData)
|
||||||
if (videoData.code != 0) return
|
// // 上传完成后的回调
|
||||||
|
|
||||||
|
// // 更新临时消息为最终消息
|
||||||
|
// dialogueStore.completeUpload(uploadId, {
|
||||||
|
// url: videoData.data.ori_url,
|
||||||
|
// cover: videoData.data.cover_url
|
||||||
|
// })
|
||||||
|
|
||||||
// 更新临时消息为最终消息
|
// // 上传成功后,发送正式消息给服务端
|
||||||
dialogueStore.completeUpload(uploadId, {
|
// let finalMessage = {
|
||||||
url: videoData.data.ori_url,
|
// type: 'video',
|
||||||
cover: videoData.data.cover_url
|
// url: videoData.data.ori_url,
|
||||||
})
|
|
||||||
|
|
||||||
// 上传成功后,发送正式消息给服务端
|
|
||||||
let finalMessage = {
|
|
||||||
type: 'video',
|
|
||||||
url: videoData.data.ori_url,
|
|
||||||
|
|
||||||
size: data.size
|
// size: data.size
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 发送真实消息到服务端
|
// // 发送真实消息到服务端
|
||||||
onSendMessage(finalMessage, () => {
|
// onSendMessage(finalMessage, () => {
|
||||||
// 上传成功且消息发送成功后,删除临时消息
|
// // 上传成功且消息发送成功后,删除临时消息
|
||||||
dialogueStore.batchDelDialogueRecord([uploadId])
|
// dialogueStore.batchDelDialogueRecord([uploadId])
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -218,10 +216,8 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
async (percentage) => {
|
async (percentage) => {
|
||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
dialogueStore.updateUploadProgress(uploadId, percentage)
|
||||||
},
|
},
|
||||||
async (data) => {
|
async () => {
|
||||||
console.log('data', data)
|
dialogueStore.batchDelDialogueRecord([uploadId])
|
||||||
// 上传完成后的回调
|
|
||||||
if (data.code != 0) return
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|