添加红包功能

This commit is contained in:
bobobobo
2026-01-07 00:52:23 +08:00
parent 95b46d5cf4
commit 1c021cdd21
12 changed files with 1542 additions and 1346 deletions

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767718029420" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16788" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M169.467181 98.184183c0-19.775392 16.210193-35.794228 36.021401-35.794228l611.364055 0c19.898189 0 36.021401 15.981996 36.021401 35.794228l0 827.598888c0 19.776416-16.211217 35.827997-36.021401 35.827997L205.489606 961.611068c-19.898189 0-36.021401-15.980973-36.021401-35.827997L169.468205 98.184183zM510.732635 609.966219l-93.224218-154.097634c-3.8814-6.462179-9.0624-9.061376-14.683421-9.061376-3.8814 0-7.762801 1.299599-11.644201 3.477194-6.480598 3.441379-11.222599 9.484002-11.222599 16.402575 0 3.0208 0.860601 6.0416 3.0208 9.063423l73.798796 114.368794-59.994396 0c-9.485025 0-14.665001 4.743024-14.665001 14.684444 0 9.905604 5.179976 14.645558 14.665001 14.645558l78.119195 0 16.403598 25.467021 0 20.302395-94.944396 0c-9.94142 0-14.683421 4.321422-14.683421 14.226003 0 9.94142 5.180999 14.682397 14.683421 14.682397l94.944396 0 0 76.397994c0 11.661597 7.341199 17.704221 19.424398 17.704221 11.661597 0 19.424398-6.0416 19.424398-17.704221L530.154987 694.128037l94.522794 0c9.0624 0 14.683421-4.740978 14.683421-14.682397 0-10.327206-4.742001-14.226003-14.683421-14.226003l-94.522794 0 0-19.847024 15.981996-25.922393 78.980819 0c9.061376 0 14.665001-4.740978 14.665001-15.103999 0-9.905604-5.163603-14.226003-14.665001-14.226003l-60.434417 0 73.377194-114.368794c2.160199-3.477194 3.0208-6.499018 3.0208-9.519818 0-6.462179-4.320399-12.0832-10.783601-15.94618-3.8814-2.600221-8.201799-3.89982-12.0832-3.89982-5.619998 0-11.223622 2.599198-15.105023 9.484002L510.732635 609.966219zM738.891032 225.338165c-1.036609-14.858406-13.944594-26.063609-28.785603-25.00858-1.335415 0.069585-2.599198 0.350994-3.828188 0.667196l-3.653203 1.052982-0.088004-0.069585c-49.176003 21.321608-102.549607 35.090193-159.066809 39.059598-77.749782 5.443989-152.375409-8.043187-219.74682-36.038798l-0.034792 0.034792-1.966795-0.632403-0.229221 0.562818c-0.069585-0.035816-0.140193-0.070608-0.192382-0.070608l0.034792-0.596588-4.89959-1.581008-4.864798-1.546216c-2.38942-0.772596-6.936993-0.772596-6.990205-0.772596l-2.248204 0.210801c-14.858406 1.01819-24.060999 14.366196-23.042809 29.225625 0.456395 6.462179 3.40761 12.258185 7.762801 16.685008 0.034792 0.069585 0.106424 0.105401 0.141216 0.140193 0.667196 0.667196 1.562589 1.054005 2.282996 1.650593 4.742001 2.916423 13.944594 6.568602 13.944594 6.568602 74.5366 30.80561 157.397796 45.874817 243.315608 39.868009 62.08501-4.356214 121.184013-19.002796 175.312818-42.256406l-0.122797-0.316202C732.534254 247.960395 739.71684 237.352803 738.891032 225.338165z" fill="#7D7D7D" p-id="16789"></path></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -3,17 +3,12 @@
<Navigation :title="title">
<template #left>
<div @click="back">
<Icon
:file="backSVG"
/>
<Icon :file="backSVG" />
</div>
</template>
<template #right>
<div @click="onNavigationBarButtonTap">
<Icon
v-if="isGroup"
:file="More"
/>
<Icon v-if="isGroup" :file="More" />
</div>
</template>
</Navigation>
@@ -21,78 +16,78 @@
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from '../../../adapter-vue';
import {
TUIStore,
StoreName,
IConversationModel,
TUITranslateService,
} from '@tencentcloud/chat-uikit-engine-lite';
import { onLoad } from '@dcloudio/uni-app';
import Navigation from '../../common/Navigation/index.vue';
import Icon from '../../common/Icon.vue';
import More from '../../../assets/icon/more.svg';
import backSVG from '../../../assets/icon/back.svg';
import { onMounted, onUnmounted, ref } from '../../../adapter-vue'
import {
TUIStore,
StoreName,
IConversationModel,
TUITranslateService
} from '@tencentcloud/chat-uikit-engine-lite'
import { onLoad } from '@dcloudio/uni-app'
import Navigation from '../../common/Navigation/index.vue'
import Icon from '../../common/Icon.vue'
import More from '../../../assets/icon/more.svg'
import backSVG from '../../../assets/icon/back.svg'
const emits = defineEmits(['openGroupManagement']);
const props = defineProps(['isGroup']);
const emits = defineEmits(['openGroupManagement'])
const props = defineProps(['isGroup'])
const currentConversation = ref<IConversationModel>();
const typingStatus = ref(false);
const title = ref('');
const currentConversation = ref<IConversationModel>()
const typingStatus = ref(false)
const title = ref('')
const setChatHeaderContent = (content: string) => {
title.value = content || '云通信 IM';
};
const onNavigationBarButtonTap = () => {
if (props.isGroup) {
emits('openGroupManagement');
const setChatHeaderContent = (content: string) => {
title.value = content || '云通信 IM'
}
};
onMounted(() => {
TUIStore.watch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdated,
});
TUIStore.watch(StoreName.CHAT, {
typingStatus: onTypingStatusUpdated,
});
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdated,
});
TUIStore.unwatch(StoreName.CHAT, {
typingStatus: onTypingStatusUpdated,
});
});
onLoad(() => {
setChatHeaderContent(currentConversation.value?.getShowName());
});
function onCurrentConversationUpdated(conversation: IConversationModel) {
currentConversation.value = conversation;
if (!typingStatus.value) {
setChatHeaderContent(currentConversation?.value?.getShowName());
const onNavigationBarButtonTap = () => {
if (props.isGroup) {
emits('openGroupManagement')
}
}
}
function onTypingStatusUpdated(status: boolean) {
typingStatus.value = status;
if (typingStatus.value) {
setChatHeaderContent(TUITranslateService.t('TUIChat.对方正在输入'));
} else {
setChatHeaderContent(currentConversation.value?.getShowName());
onMounted(() => {
TUIStore.watch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdated
})
TUIStore.watch(StoreName.CHAT, {
typingStatus: onTypingStatusUpdated
})
})
onUnmounted(() => {
TUIStore.unwatch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdated
})
TUIStore.unwatch(StoreName.CHAT, {
typingStatus: onTypingStatusUpdated
})
})
onLoad(() => {
setChatHeaderContent(currentConversation.value?.getShowName())
})
function onCurrentConversationUpdated(
conversation: IConversationModel
) {
currentConversation.value = conversation
if (!typingStatus.value) {
setChatHeaderContent(currentConversation?.value?.getShowName())
}
}
}
function back() {
uni.navigateBack();
}
function onTypingStatusUpdated(status: boolean) {
typingStatus.value = status
if (typingStatus.value) {
setChatHeaderContent(TUITranslateService.t('TUIChat.对方正在输入'))
} else {
setChatHeaderContent(currentConversation.value?.getShowName())
}
}
function back() {
uni.navigateBack()
}
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

View File

@@ -3,7 +3,7 @@
:class="[
'message-input-toolbar',
'message-input-toolbar-h5',
'message-input-toolbar-uni',
'message-input-toolbar-uni'
]"
>
<div v-if="props.displayType === 'emojiPicker'">
@@ -20,18 +20,17 @@
:class="[
'message-input-toolbar-list',
'message-input-toolbar-h5-list',
'message-input-toolbar-uni-list',
'message-input-toolbar-uni-list'
]"
>
<CameraUpload
v-if="featureConfig.InputCamera"
/>
<AlbumUpload
v-if="featureConfig.InputAlbum"
/>
<CameraUpload v-if="featureConfig.InputCamera" />
<AlbumUpload v-if="featureConfig.InputAlbum" />
<template v-if="currentExtensionList.length > 0">
<div
v-for="(extension, index) in currentExtensionList.slice(0, slicePos)"
v-for="(extension, index) in currentExtensionList.slice(
0,
slicePos
)"
:key="index"
>
<ToolbarItemContainer
@@ -64,6 +63,7 @@
v-if="featureConfig.InputEvaluation"
@onDialogPopupShowOrHide="handleSwiperDotShow"
/>
<RedEnvelope />
</template>
</swiper-item>
<swiper-item
@@ -71,11 +71,13 @@
:class="[
'message-input-toolbar-list',
'message-input-toolbar-h5-list',
'message-input-toolbar-uni-list',
'message-input-toolbar-uni-list'
]"
>
<div
v-for="(extension, index) in currentExtensionList.slice(slicePos)"
v-for="(extension, index) in currentExtensionList.slice(
slicePos
)"
:key="index"
>
<ToolbarItemContainer
@@ -117,188 +119,217 @@
</div>
</template>
<script setup lang="ts">
import TUIChatEngine, {
IConversationModel,
TUIStore,
StoreName,
TUIReportService,
} from '@tencentcloud/chat-uikit-engine-lite';
import TUICore, { ExtensionInfo, TUIConstants } from '@tencentcloud/tui-core-lite';
import { ref, onUnmounted, onMounted } from '../../../adapter-vue';
import AlbumUpload from './album-upload/index.vue';
import CameraUpload from './camera-upload/index.vue';
import Evaluate from './evaluate/index.vue';
import Words from './words/index.vue';
import ToolbarItemContainer from './toolbar-item-container/index.vue';
import EmojiPickerDialog from './emoji-picker/emoji-picker-dialog.vue';
import UserSelector from './user-selector/index.vue';
import TUIChatConfig from '../config';
import { enableSampleTaskStatus } from '../../../utils/enableSampleTaskStatus';
import { ToolbarDisplayType } from '../../../interface';
import OfflinePushInfoManager, { PUSH_SCENE } from '../offlinePushInfoManager/index';
import TUIChatEngine, {
IConversationModel,
TUIStore,
StoreName,
TUIReportService
} from '@tencentcloud/chat-uikit-engine-lite'
import TUICore, {
ExtensionInfo,
TUIConstants
} from '@tencentcloud/tui-core-lite'
import { ref, onUnmounted, onMounted } from '../../../adapter-vue'
import AlbumUpload from './album-upload/index.vue'
import CameraUpload from './camera-upload/index.vue'
import Evaluate from './evaluate/index.vue'
import RedEnvelope from './red-envelope/index.vue'
import Words from './words/index.vue'
import ToolbarItemContainer from './toolbar-item-container/index.vue'
import EmojiPickerDialog from './emoji-picker/emoji-picker-dialog.vue'
import UserSelector from './user-selector/index.vue'
import TUIChatConfig from '../config'
import { enableSampleTaskStatus } from '../../../utils/enableSampleTaskStatus'
import { ToolbarDisplayType } from '../../../interface'
import OfflinePushInfoManager, {
PUSH_SCENE
} from '../offlinePushInfoManager/index'
interface IProps {
displayType: ToolbarDisplayType;
}
const props = withDefaults(defineProps<IProps>(), {
});
const emits = defineEmits(['changeToolbarDisplayType']);
const currentConversation = ref<IConversationModel>();
const isGroup = ref<boolean>(false);
const selectorShowType = ref<string>('');
const userSelectorRef = ref();
const currentUserSelectorExtension = ref<ExtensionInfo | null>();
const currentExtensionList = ref<ExtensionInfo[]>([]);
const isSwiperIndicatorDotsEnable = ref<boolean>(false);
const featureConfig = TUIChatConfig.getFeatureConfig();
const neededCountFirstPage = ref<number>(8);
const slicePos = ref<number>(0);
const computeToolbarPaging = () => {
if (featureConfig.InputAlbum && featureConfig.InputCamera) {
neededCountFirstPage.value -= 2;
} else if (featureConfig.InputAlbum || featureConfig.InputCamera) {
neededCountFirstPage.value -= 1;
interface IProps {
displayType: ToolbarDisplayType
}
slicePos.value = neededCountFirstPage.value;
neededCountFirstPage.value -= currentExtensionList.value.length;
const props = withDefaults(defineProps<IProps>(), {})
if (neededCountFirstPage.value === 1) {
isSwiperIndicatorDotsEnable.value = (featureConfig.InputEvaluation && featureConfig.InputQuickReplies);
} else if (neededCountFirstPage.value < 1) {
isSwiperIndicatorDotsEnable.value = featureConfig.InputEvaluation || featureConfig.InputQuickReplies;
}
};
const emits = defineEmits(['changeToolbarDisplayType'])
onMounted(() => {
TUIStore.watch(StoreName.CUSTOM, {
activeConversation: onActiveConversationUpdate,
});
});
const currentConversation = ref<IConversationModel>()
const isGroup = ref<boolean>(false)
const selectorShowType = ref<string>('')
const userSelectorRef = ref()
const currentUserSelectorExtension = ref<ExtensionInfo | null>()
const currentExtensionList = ref<ExtensionInfo[]>([])
const isSwiperIndicatorDotsEnable = ref<boolean>(false)
const featureConfig = TUIChatConfig.getFeatureConfig()
const neededCountFirstPage = ref<number>(8)
const slicePos = ref<number>(0)
onUnmounted(() => {
TUIStore.unwatch(StoreName.CUSTOM, {
activeConversation: onActiveConversationUpdate,
});
});
const onActiveConversationUpdate = (conversationID: string) => {
if (!conversationID) {
return;
}
if (conversationID !== currentConversation.value?.conversationID) {
getExtensionList();
computeToolbarPaging();
currentConversation.value = TUIStore.getData(StoreName.CONV, 'currentConversation');
isGroup.value = conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP);
}
};
const getExtensionList = () => {
const chatType = TUIChatConfig.getChatType();
const params: Record<string, boolean | string> = { chatType };
// Backward compatibility: When callkit does not have chatType judgment, use filterVoice and filterVideo to filter
if (chatType === TUIConstants.TUIChat.TYPE.CUSTOMER_SERVICE) {
params.filterVoice = true;
params.filterVideo = true;
enableSampleTaskStatus('customerService');
}
// uni-app build ios app has null in last index need to filter
currentExtensionList.value = [
...TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID, params),
].filter((extension: ExtensionInfo) => {
if (extension?.data?.name === 'search') {
return featureConfig.MessageSearch;
const computeToolbarPaging = () => {
if (featureConfig.InputAlbum && featureConfig.InputCamera) {
neededCountFirstPage.value -= 2
} else if (featureConfig.InputAlbum || featureConfig.InputCamera) {
neededCountFirstPage.value -= 1
}
return true;
});
reportExtension(currentExtensionList.value);
};
function reportExtension(extensionList: ExtensionInfo[]) {
extensionList.forEach((extension: ExtensionInfo) => {
const _name = extension?.data?.name;
if (_name === 'voiceCall') {
TUIReportService.reportFeature(203, 'voice-call');
} else if (_name === 'videoCall') {
TUIReportService.reportFeature(203, 'video-call');
} else if (_name === 'quickRoom') {
TUIReportService.reportFeature(204);
slicePos.value = neededCountFirstPage.value
neededCountFirstPage.value -= currentExtensionList.value.length
if (neededCountFirstPage.value === 1) {
isSwiperIndicatorDotsEnable.value =
featureConfig.InputEvaluation && featureConfig.InputQuickReplies
} else if (neededCountFirstPage.value < 1) {
isSwiperIndicatorDotsEnable.value =
featureConfig.InputEvaluation || featureConfig.InputQuickReplies
}
});
}
// handle extensions onclick
const onExtensionClick = (extension: ExtensionInfo) => {
// uniapp vue2 build wx lose listener proto
const extensionModel = currentExtensionList.value.find(
targetExtension => targetExtension?.data?.name === extension?.data?.name,
);
switch (extensionModel?.data?.name) {
case 'voiceCall':
onCallExtensionClicked(extensionModel, 1);
break;
case 'videoCall':
onCallExtensionClicked(extensionModel, 2);
break;
case 'search':
extensionModel?.listener?.onClicked?.();
break;
default:
break;
}
};
const onCallExtensionClicked = (extension: ExtensionInfo, callType: number) => {
selectorShowType.value = extension?.data?.name;
if (currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_C2C) {
extension?.listener?.onClicked?.({
userIDList: [currentConversation?.value?.conversationID?.slice(3)],
type: callType,
onMounted(() => {
TUIStore.watch(StoreName.CUSTOM, {
activeConversation: onActiveConversationUpdate
})
})
onUnmounted(() => {
TUIStore.unwatch(StoreName.CUSTOM, {
activeConversation: onActiveConversationUpdate
})
})
const onActiveConversationUpdate = (conversationID: string) => {
if (!conversationID) {
return
}
if (conversationID !== currentConversation.value?.conversationID) {
getExtensionList()
computeToolbarPaging()
currentConversation.value = TUIStore.getData(
StoreName.CONV,
'currentConversation'
)
isGroup.value = conversationID.startsWith(
TUIChatEngine.TYPES.CONV_GROUP
)
}
}
const getExtensionList = () => {
const chatType = TUIChatConfig.getChatType()
const params: Record<string, boolean | string> = { chatType }
// Backward compatibility: When callkit does not have chatType judgment, use filterVoice and filterVideo to filter
if (chatType === TUIConstants.TUIChat.TYPE.CUSTOMER_SERVICE) {
params.filterVoice = true
params.filterVideo = true
enableSampleTaskStatus('customerService')
}
// uni-app build ios app has null in last index need to filter
currentExtensionList.value = [
...TUICore.getExtensionList(
TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID,
params
)
].filter((extension: ExtensionInfo) => {
if (extension?.data?.name === 'search') {
return featureConfig.MessageSearch
}
return true
})
reportExtension(currentExtensionList.value)
}
function reportExtension(extensionList: ExtensionInfo[]) {
extensionList.forEach((extension: ExtensionInfo) => {
const _name = extension?.data?.name
if (_name === 'voiceCall') {
TUIReportService.reportFeature(203, 'voice-call')
} else if (_name === 'videoCall') {
TUIReportService.reportFeature(203, 'video-call')
} else if (_name === 'quickRoom') {
TUIReportService.reportFeature(204)
}
})
}
// handle extensions onclick
const onExtensionClick = (extension: ExtensionInfo) => {
// uniapp vue2 build wx lose listener proto
const extensionModel = currentExtensionList.value.find(
targetExtension =>
targetExtension?.data?.name === extension?.data?.name
)
switch (extensionModel?.data?.name) {
case 'voiceCall':
onCallExtensionClicked(extensionModel, 1)
break
case 'videoCall':
onCallExtensionClicked(extensionModel, 2)
break
case 'search':
extensionModel?.listener?.onClicked?.()
break
default:
break
}
}
const onCallExtensionClicked = (
extension: ExtensionInfo,
callType: number
) => {
selectorShowType.value = extension?.data?.name
if (
currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_C2C
) {
extension?.listener?.onClicked?.({
userIDList: [
currentConversation?.value?.conversationID?.slice(3)
],
type: callType,
callParams: {
offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(
PUSH_SCENE.CALL
)
}
})
} else if (isGroup.value) {
currentUserSelectorExtension.value = extension
userSelectorRef?.value?.toggleShow &&
userSelectorRef.value.toggleShow(true)
}
}
const genExtensionIcon = (extension: any) => extension?.icon
const genExtensionText = (extension: any) => extension?.text
const onUserSelectorSubmit = (selectedInfo: any) => {
currentUserSelectorExtension.value?.listener?.onClicked?.({
...selectedInfo,
callParams: {
offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(PUSH_SCENE.CALL),
},
});
} else if (isGroup.value) {
currentUserSelectorExtension.value = extension;
userSelectorRef?.value?.toggleShow && userSelectorRef.value.toggleShow(true);
offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(
PUSH_SCENE.CALL
)
}
})
currentUserSelectorExtension.value = null
}
};
const genExtensionIcon = (extension: any) => extension?.icon;
const genExtensionText = (extension: any) => extension?.text;
const onUserSelectorCancel = () => {
currentUserSelectorExtension.value = null
}
const onUserSelectorSubmit = (selectedInfo: any) => {
currentUserSelectorExtension.value?.listener?.onClicked?.({
...selectedInfo,
callParams: {
offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(PUSH_SCENE.CALL),
},
});
currentUserSelectorExtension.value = null;
};
const onUserSelectorCancel = () => {
currentUserSelectorExtension.value = null;
};
const handleSwiperDotShow = (showStatus: boolean) => {
isSwiperIndicatorDotsEnable.value = (neededCountFirstPage.value <= 1 && !showStatus);
emits('changeToolbarDisplayType', showStatus ? 'dialog' : 'tools');
};
const handleSwiperDotShow = (showStatus: boolean) => {
isSwiperIndicatorDotsEnable.value =
neededCountFirstPage.value <= 1 && !showStatus
emits('changeToolbarDisplayType', showStatus ? 'dialog' : 'tools')
}
</script>
<script lang="ts">
export default {
options: {
styleIsolation: 'shared',
},
};
export default {
options: {
styleIsolation: 'shared'
}
}
</script>
<style lang="scss">
@import '../../../assets/styles/common';
@import './style/uni';
@import '../../../assets/styles/common';
@import './style/uni';
</style>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import ToolbarItemContainer from '../toolbar-item-container/index.vue'
import custom from '../../../../assets/icon/red-packet.svg'
import { isUniFrameWork } from '../../../../utils/env'
const evaluateIcon = custom;
</script>
<template>
<ToolbarItemContainer
:iconFile="evaluateIcon"
:iconWidth="isUniFrameWork ? '26px' : '20px'"
:iconHeight="isUniFrameWork ? '26px' : '20px'"
title="红包"
>
<view>弹出窗口</view>
</ToolbarItemContainer>
</template>
<style scoped lang="scss"></style>

View File

@@ -5,172 +5,175 @@ import TUIChatEngine, {
TUIUserService,
TUITranslateService,
AddFriendParams,
JoinGroupParams,
} from '@tencentcloud/chat-uikit-engine-lite';
import { TUIGlobal } from '@tencentcloud/universal-api';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
JoinGroupParams
} from '@tencentcloud/chat-uikit-engine-lite'
import { TUIGlobal } from '@tencentcloud/universal-api'
import { Toast, TOAST_TYPE } from '../../common/Toast/index'
import { deleteImGroup, quitImGroup } from '../../../../api/tui-kit'
import { switchTab } from '../../../../utils/router'
export const generateAvatar = (item: any): string => {
return (
item?.avatar
|| item?.profile?.avatar
|| (item?.groupID && 'https://web.sdk.qcloud.com/im/assets/images/Public.svg')
|| 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
);
};
item?.avatar ||
item?.profile?.avatar ||
(item?.groupID &&
'https://web.sdk.qcloud.com/im/assets/images/Public.svg') ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
)
}
export const generateName = (item: any): string => {
return (
item?.remark
|| item?.name
|| item?.profile?.nick
|| item?.nick
|| item?.groupID
|| item?.userID
|| ''
);
};
item?.remark ||
item?.name ||
item?.profile?.nick ||
item?.nick ||
item?.groupID ||
item?.userID ||
''
)
}
export const generateContactInfoName = (item: any): string => {
return (
item?.name
|| item?.profile?.nick
|| item?.nick
|| item?.groupID
|| item?.userID
|| ''
);
};
item?.name ||
item?.profile?.nick ||
item?.nick ||
item?.groupID ||
item?.userID ||
''
)
}
// Parse the basic information display content of the contactInfo module
// Group information display: group ID group type
// User information display: user ID personal signature
export const generateContactInfoBasic = (
contactInfo: any,
): any[] => {
export const generateContactInfoBasic = (contactInfo: any): any[] => {
const res = [
{
label: contactInfo?.groupID ? '群ID' : 'ID',
data: contactInfo?.groupID || contactInfo?.userID || '',
},
];
data: contactInfo?.groupID || contactInfo?.userID || ''
}
]
if (!isApplicationType(contactInfo)) {
res.push({
label: contactInfo?.groupID ? '群类型' : '个性签名',
data: contactInfo?.type || contactInfo?.profile?.selfSignature || '',
});
data: contactInfo?.type || contactInfo?.profile?.selfSignature || ''
})
}
return res;
};
return res
}
export const isApplicationType = (info: any) => {
return (
info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|| info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
);
};
info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME ||
info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
)
}
export const isFriend = (info: any): Promise<boolean> => {
return new Promise((resolve, reject) => {
if (info?.groupID || !info?.userID) {
resolve(false);
return;
resolve(false)
return
}
if (info?.addTime) {
resolve(true);
return;
resolve(true)
return
}
TUIFriendService.checkFriend({
userIDList: [info?.userID],
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH,
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH
})
.then((res: any) => {
switch (res?.data?.successUserIDList[0]?.relation) {
// No friend relationship: A does not have B in his friend list, and B does not have A in his friend list
case TUIChatEngine.TYPES.SNS_TYPE_NO_RELATION:
resolve(false);
break;
resolve(false)
break
// Single-item friend: A has B in his friend list, but B does not have A in his friend list
case TUIChatEngine.TYPES.SNS_TYPE_A_WITH_B:
resolve(false);
break;
resolve(false)
break
// Single-item friend: A does not have B in his friend list, but B has A in his friend list
case TUIChatEngine.TYPES.SNS_TYPE_B_WITH_A:
resolve(false);
break;
resolve(false)
break
// Two-way friendship
case TUIChatEngine.TYPES.SNS_TYPE_BOTH_WAY:
resolve(true);
break;
resolve(true)
break
default:
resolve(false);
break;
resolve(false)
break
}
})
.catch((error: any) => {
console.warn('checkFriend error', error);
reject(error);
});
});
};
console.warn('checkFriend error', error)
reject(error)
})
})
}
// Change friends remark
export const updateFriendRemark = (userID: string, remark: string) => {
// eslint-disable-next-line no-control-regex
if (remark?.replace(/[^\u0000-\u00ff]/g, 'aa')?.length > 96) {
Toast({
message: TUITranslateService.t('TUIContact.修改备注失败: 备注长度不得超过 96 字节'),
type: TOAST_TYPE.ERROR,
});
return;
message: TUITranslateService.t(
'TUIContact.修改备注失败: 备注长度不得超过 96 字节'
),
type: TOAST_TYPE.ERROR
})
return
}
TUIFriendService.updateFriend({
userID,
remark,
remark
})
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.修改备注成功'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
})
.catch((error: any) => {
console.warn('update friend remark failed:', error);
console.warn('update friend remark failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.修改备注失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Delete one friend
export const deleteFriend = (userID: string) => {
TUIFriendService.deleteFriend({
userIDList: [userID],
type: TUIChatEngine.TYPES.SNS_DELETE_TYPE_BOTH,
type: TUIChatEngine.TYPES.SNS_DELETE_TYPE_BOTH
})
.then((res: any) => {
const { successUserIDList } = res.data;
const { successUserIDList } = res.data
if (successUserIDList[0].userID === userID) {
Toast({
message: TUITranslateService.t('TUIContact.删除好友成功'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
} else {
Toast({
message: TUITranslateService.t('TUIContact.删除好友失败'),
type: TOAST_TYPE.ERROR,
});
type: TOAST_TYPE.ERROR
})
}
})
.catch((error: any) => {
console.warn('delete friend failed:', error);
console.warn('delete friend failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.删除好友失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Add friend
export const addFriend = (params: AddFriendParams) => {
@@ -178,54 +181,54 @@ export const addFriend = (params: AddFriendParams) => {
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.申请已发送'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
})
.catch((error: any) => {
console.warn('delete friend failed:', error);
console.warn('delete friend failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.申请发送失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Enter conversation
export const enterConversation = (item: any) => {
const conversationID = item?.groupID
? `GROUP${item?.groupID}`
: `C2C${item?.userID}`;
: `C2C${item?.userID}`
TUIConversationService.switchConversation(conversationID).catch(
(error: any) => {
console.warn('switch conversation failed:', error);
console.warn('switch conversation failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.进入会话失败'),
type: TOAST_TYPE.ERROR,
});
},
);
};
type: TOAST_TYPE.ERROR
})
}
)
}
// Accept friend application
export const acceptFriendApplication = (userID: string) => {
TUIFriendService.acceptFriendApplication({
userID,
type: TUIChatEngine.TYPES.SNS_APPLICATION_AGREE_AND_ADD,
type: TUIChatEngine.TYPES.SNS_APPLICATION_AGREE_AND_ADD
})
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.添加好友成功'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
})
.catch((error: any) => {
console.warn('accept friend application failed:', error);
console.warn('accept friend application failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.同意好友申请失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Refuse friend application
export const refuseFriendApplication = (userID: string) => {
@@ -233,127 +236,164 @@ export const refuseFriendApplication = (userID: string) => {
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.拒绝成功'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
})
.catch((error: any) => {
console.warn('accept friend application failed:', error);
console.warn('accept friend application failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.拒绝好友申请失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Dismiss group
export const dismissGroup = (groupID: string) => {
TUIGroupService.dismissGroup(groupID)
deleteImGroup(groupID)
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.解散群聊成功'),
type: TOAST_TYPE.SUCCESS,
});
TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch();
type: TOAST_TYPE.SUCCESS
})
TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch()
switchTab('/TUIKit/components/TUIConversation/index')
})
.catch((error: any) => {
console.warn('dismiss group failed:', error);
console.warn('dismiss group failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.解散群聊失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
// TUIGroupService.dismissGroup(groupID)
// .then(() => {
// Toast({
// message: TUITranslateService.t('TUIContact.解散群聊成功'),
// type: TOAST_TYPE.SUCCESS,
// });
// TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch();
// })
// .catch((error: any) => {
// console.warn('dismiss group failed:', error);
// Toast({
// message: TUITranslateService.t('TUIContact.解散群聊失败'),
// type: TOAST_TYPE.ERROR,
// });
// });
}
// Quit group
export const quitGroup = (groupID: string) => {
TUIGroupService.quitGroup(groupID)
console.log('222')
quitImGroup(groupID)
.then(() => {
Toast({
message: TUITranslateService.t('TUIContact.退出群组成功'),
type: TOAST_TYPE.SUCCESS,
});
type: TOAST_TYPE.SUCCESS
})
switchTab('/TUIKit/components/TUIConversation/index')
})
.catch((error: any) => {
console.warn('quit group failed:', error);
console.warn('quit group failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.退出群组失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
// TUIGroupService.quitGroup(groupID)
// .then(() => {
// Toast({
// message: TUITranslateService.t('TUIContact.退出群组成功'),
// type: TOAST_TYPE.SUCCESS,
// });
// })
// .catch((error: any) => {
// console.warn('quit group failed:', error);
// Toast({
// message: TUITranslateService.t('TUIContact.退出群组失败'),
// type: TOAST_TYPE.ERROR,
// });
// });
}
// Join group
export const joinGroup = (groupID: string, applyMessage?: string) => {
TUIGroupService.joinGroup({
groupID,
applyMessage,
applyMessage
} as JoinGroupParams)
.then((imResponse: { data: { status?: string } }) => {
switch (imResponse?.data?.status) {
case TUIChatEngine.TYPES.JOIN_STATUS_WAIT_APPROVAL: // Wait for administrator approval
Toast({
message: TUITranslateService.t('TUIContact.等待管理员同意'),
type: TOAST_TYPE.SUCCESS,
});
break;
type: TOAST_TYPE.SUCCESS
})
break
case TUIChatEngine.TYPES.JOIN_STATUS_SUCCESS: // Join group successfully
Toast({
message: TUITranslateService.t('TUIContact.加群成功'),
type: TOAST_TYPE.SUCCESS,
});
break;
type: TOAST_TYPE.SUCCESS
})
break
case TUIChatEngine.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // Already in the group
Toast({
message: TUITranslateService.t('TUIContact.您已是群成员'),
type: TOAST_TYPE.SUCCESS,
});
break;
type: TOAST_TYPE.SUCCESS
})
break
default:
break;
break
}
})
.catch((error: any) => {
console.warn('join group failed:', error);
console.warn('join group failed:', error)
Toast({
message: '申请入群失败',
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Add to blacklist
export const addToBlacklist = (userID: string, successCallBack?: () => void) => {
export const addToBlacklist = (
userID: string,
successCallBack?: () => void
) => {
TUIUserService.addToBlacklist({
userIDList: [userID],
userIDList: [userID]
})
.then(() => {
successCallBack && successCallBack();
successCallBack && successCallBack()
})
.catch((error: any) => {
console.warn('add to blacklist failed:', error);
console.warn('add to blacklist failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.加入黑名单失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}
// Remove from Blacklist
export const removeFromBlacklist = (
userID: string,
successCallBack?: () => void,
successCallBack?: () => void
) => {
TUIUserService.removeFromBlacklist({
userIDList: [userID],
userIDList: [userID]
})
.then(() => {
successCallBack && successCallBack();
successCallBack && successCallBack()
})
.catch((error: any) => {
console.warn('remove from blacklist failed:', error);
console.warn('remove from blacklist failed:', error)
Toast({
message: TUITranslateService.t('TUIContact.移除黑名单失败'),
type: TOAST_TYPE.ERROR,
});
});
};
type: TOAST_TYPE.ERROR
})
})
}

View File

@@ -6,7 +6,7 @@
<label class="group-list-item-label">
{{ TUITranslateService.t('TUIGroup.群头像') }}
</label>
<Avatar :url="groupInfo.profile.avatar" />
<Avatar :url="groupAvatar" @click="selectAvatar" />
</li>
<ul>
<li
@@ -158,21 +158,12 @@
import Server from '../server'
import { IUserProfile } from '../../../interface'
import { createImGroup } from '../../../../api/tui-kit'
import { chooseImage } from '../../../../utils/media'
import { uploadSingleFile } from '../../../../utils/uploadFile'
const TUIGroupServer = Server.getInstance()
const TUIConstants = TUIGroupServer.constants
/** 自定义数据 */
const cbPopupShow = ref(null)
const applyJoinOption = ref('NeedPermission')
const applyJoinOptionName = computed(() => {
const data = groupJoinTypeConfig.find(
v => v.value === applyJoinOption.value
)
return data?.label || ''
})
// =======
const groupInfo = reactive<any>({
profile: {
groupID: '',
@@ -195,6 +186,19 @@
isEdit: false
})
/** 自定义数据 */
const cbPopupShow = ref(null)
const applyJoinOption = ref('NeedPermission')
// 群头像
const groupAvatar = ref(groupInfo.profile.avatar)
const applyJoinOptionName = computed(() => {
const data = groupJoinTypeConfig.find(
v => v.value === applyJoinOption.value
)
return data?.label || ''
})
// =======
watchEffect(() => {
const params = TUIGroupServer.getOnCallParams(
TUIConstants.TUIGroup.SERVICE.METHOD.CREATE_GROUP
@@ -247,35 +251,69 @@
}
}
/** 选择群头像 */
const selectAvatar = async () => {
const paths = await chooseImage({ count: 1 })
groupAvatar.value = await uploadSingleFile(paths[0], {
url: '/api/common/admin/upload/up/single'
})
}
const createGroup = async (options: any) => {
console.log('确认创建==', options)
// const data = {
// currentMemberCount:
// }
// createImGroup()
return
try {
options.memberList = options.memberList.map(
(item: IUserProfile) => {
return { userID: item.userID }
}
)
if (options.type === TUIChatEngine.TYPES.GRP_COMMUNITY) {
delete options.groupID
}
const res = await TUIGroupService.createGroup(options)
const { type } = res.data.group
if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
await TUIGroupService.joinGroup({
groupID: res.data.group.groupID,
applyMessage: ''
const data = {
applyJoinOption: applyJoinOption.value,
faceUrl: groupAvatar.value,
groupName: options.name,
groupType: options.type,
memberList: options.memberList.map((item: IUserProfile) => {
return { memberAccount: item.userID }
})
}
handleCompleteCreate(res.data.group)
const res = await createImGroup(data)
const e = res.data
// const type = e.groupType
// if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
// await TUIGroupService.joinGroup({
// groupID: e.groupID,
// applyMessage: ''
// })
// }
const newRes = await TUIGroupService.getGroupProfile({
groupID: e.groupId
})
handleCompleteCreate(newRes.data.group)
Toast({
message: TUITranslateService.t('TUIGroup.群组创建成功'),
type: TOAST_TYPE.SUCCESS
})
// ========= 原本逻辑
// options.memberList = options.memberList.map(
// (item: IUserProfile) => {
// return { userID: item.userID }
// }
// )
// if (options.type === TUIChatEngine.TYPES.GRP_COMMUNITY) {
// delete options.groupID
// }
// const res = await TUIGroupService.createGroup(options)
// console.log(res)
// const { type } = res.data.group
// if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
// await TUIGroupService.joinGroup({
// groupID: res.data.group.groupID,
// applyMessage: ''
// })
// }
// handleCompleteCreate(res.data.group)
// Toast({
// message: TUITranslateService.t('TUIGroup.群组创建成功'),
// type: TOAST_TYPE.SUCCESS
// })
} catch (err: any) {
Toast({
message: err.message,

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,10 @@
<div class="group-info" @click="toggleEditStatus">
<Avatar
useSkeletonAnimation
:url="groupProfile.avatar ||
'https://web.sdk.qcloud.com/im/demo/TUIkit/web/img/constomer.svg'"
:url="
groupProfile.avatar ||
'https://web.sdk.qcloud.com/im/demo/TUIkit/web/img/constomer.svg'
"
size="40px"
/>
<div class="group-details">
@@ -16,28 +18,26 @@
<div
v-if="isEdit"
:class="{
'edit-h5': isMobile,
'edit-h5': isMobile
}"
>
<main class="edit-h5-main">
<header
v-if="!isPC"
class="edit-h5-header"
>
<header v-if="!isPC" class="edit-h5-header">
<aside class="left">
<h1 class="title">{{ TUITranslateService.t(`TUIGroup.修改群聊名称`) }}</h1>
<span class="subtitle">{{
TUITranslateService.t(
`TUIGroup.修改群聊名称后,将在群内通知其他成员`
)
}}</span>
<h1 class="title">
{{ TUITranslateService.t(`TUIGroup.修改群聊名称`) }}
</h1>
<span class="subtitle">
{{
TUITranslateService.t(
`TUIGroup.修改群聊名称后,将在群内通知其他成员`
)
}}
</span>
</aside>
<span
class="close"
@click="toggleEditStatus"
>{{
TUITranslateService.t(`关闭`)
}}</span>
<span class="close" @click="toggleEditStatus">
{{ TUITranslateService.t(`关闭`) }}
</span>
</header>
<div class="input-box">
<input
@@ -46,24 +46,17 @@
v-model="inputGroupName"
class="input"
type="text"
>
<span
v-if="!isPC"
class="tip"
>{{
TUITranslateService.t(
`TUIGroup.仅限中文、字母、数字和下划线2-20个字`
)
}}</span>
/>
<span v-if="!isPC" class="tip">
{{
TUITranslateService.t(
`TUIGroup.仅限中文字母数字和下划线2-20个字`
)
}}
</span>
</div>
<footer
v-if="!isPC"
class="edit-h5-footer"
>
<button
class="btn"
@click="updateProfile"
>
<footer v-if="!isPC" class="edit-h5-footer">
<button class="btn" @click="updateProfile">
{{ TUITranslateService.t(`确认`) }}
</button>
</footer>
@@ -73,226 +66,226 @@
</template>
<script lang="ts" setup>
import { watchEffect, ref, nextTick, watch } from '../../../adapter-vue';
import {
TUITranslateService,
IGroupModel,
} from '@tencentcloud/chat-uikit-engine-lite';
import Avatar from "../../common/Avatar/index.vue";
import Icon from '../../common/Icon.vue';
import rightIcon from '../../../assets/icon/right-icon.svg';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
import { isMobile, isPC } from '../../../utils/env';
import { watchEffect, ref, nextTick, watch } from '../../../adapter-vue'
import {
TUITranslateService,
IGroupModel
} from '@tencentcloud/chat-uikit-engine-lite'
import Avatar from '../../common/Avatar/index.vue'
import Icon from '../../common/Icon.vue'
import rightIcon from '../../../assets/icon/right-icon.svg'
import { Toast, TOAST_TYPE } from '../../common/Toast/index'
import { isMobile, isPC } from '../../../utils/env'
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
isAuthor: {
type: Boolean,
default: false,
},
});
const props = defineProps({
data: {
type: Object,
default: () => ({})
},
isAuthor: {
type: Boolean,
default: false
}
})
const groupProfile = ref<IGroupModel>({});
const inputGroupName = ref('');
const isEdit = ref(false);
const nameInputRef = ref<HTMLInputElement>(null);
const groupProfile = ref<IGroupModel>({})
const inputGroupName = ref('')
const isEdit = ref(false)
const nameInputRef = ref<HTMLInputElement>(null)
watchEffect(() => {
groupProfile.value = props.data;
});
watchEffect(() => {
groupProfile.value = props.data
})
const emit = defineEmits(['update']);
const updateProfile = () => {
if (!inputGroupName.value) {
Toast({
message: TUITranslateService.t('TUIGroup.群名称不能为空'),
type: TOAST_TYPE.ERROR,
});
} else {
if (inputGroupName.value !== groupProfile.value.name) {
emit('update', { key: 'name', value: inputGroupName.value });
groupProfile.value.name = inputGroupName.value;
inputGroupName.value = '';
const emit = defineEmits(['update'])
const updateProfile = () => {
if (!inputGroupName.value) {
Toast({
message: TUITranslateService.t('TUIGroup.群名称修改成功'),
type: TOAST_TYPE.SUCCESS,
});
message: TUITranslateService.t('TUIGroup.群名称不能为空'),
type: TOAST_TYPE.ERROR
})
} else {
if (inputGroupName.value !== groupProfile.value.name) {
emit('update', { key: 'name', value: inputGroupName.value })
groupProfile.value.name = inputGroupName.value
inputGroupName.value = ''
Toast({
message: TUITranslateService.t('TUIGroup.群名称修改成功'),
type: TOAST_TYPE.SUCCESS
})
}
toggleEditStatus()
}
toggleEditStatus();
}
};
const toggleEditStatus = () => {
if (props.isAuthor) {
isEdit.value = !isEdit.value;
}
if (isEdit.value) {
inputGroupName.value = groupProfile.value.name;
}
};
watch(
() => isEdit.value,
(newVal: boolean) => {
if (newVal) {
nextTick(() => {
nameInputRef.value?.focus();
});
const toggleEditStatus = () => {
if (props.isAuthor) {
isEdit.value = !isEdit.value
}
},
);
if (isEdit.value) {
inputGroupName.value = groupProfile.value.name
}
}
watch(
() => isEdit.value,
(newVal: boolean) => {
if (newVal) {
nextTick().then(() => {
nameInputRef.value?.focus()
})
}
}
)
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
@import '../../../assets/styles/common';
.group-name {
padding: 14px 20px;
font-weight: 400;
font-size: 14px;
color: #000;
display: flex;
flex-direction: column;
}
.group-info {
display: flex;
gap: 10px;
align-items: center;
}
.group-details {
min-width: 0;
flex: 1;
display: flex;
gap: 6px;
flex-direction: column;
.name {
font-size: 16px;
line-height: 18px;
font-weight: 500;
display: flex;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.ID {
font-size: 12px;
line-height: 14px;
font-weight: 400;
color: #888;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
}
.input-box {
display: flex;
.input {
flex: 1;
border: 1px solid #e8e8e9;
border-radius: 4px;
padding: 4px 16px;
.group-name {
padding: 14px 20px;
font-weight: 400;
font-size: 14px;
color: #000;
opacity: 0.6;
}
}
.space-top {
border-top: 10px solid #f4f5f9;
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.edit-h5-main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
width: 80vw;
.input-box {
flex-direction: column;
padding: 18px 0;
.input {
background: #f8f8f8;
padding: 10px 12px;
}
.tip {
font-size: 12px;
color: #888;
padding-top: 8px;
}
}
}
&-header {
display: flex;
flex-direction: column;
}
.group-info {
display: flex;
gap: 10px;
align-items: center;
justify-content: space-between;
}
h1 {
font-family: PingFang SC;
.group-details {
min-width: 0;
flex: 1;
display: flex;
gap: 6px;
flex-direction: column;
.name {
font-size: 16px;
line-height: 18px;
font-weight: 500;
font-size: 22px;
line-height: 26px;
display: flex;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.subtitle {
color: #888;
}
.close {
font-family: PingFangSC-Regular;
.ID {
font-size: 12px;
line-height: 14px;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
color: #888;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
}
&-footer {
.input-box {
display: flex;
.btn {
.input {
flex: 1;
border: none;
background: #147aff;
border-radius: 5px;
font-family: PingFangSC-Regular;
border: 1px solid #e8e8e9;
border-radius: 4px;
padding: 4px 16px;
font-weight: 400;
font-size: 16px;
color: #fff;
letter-spacing: 0;
line-height: 27px;
padding: 8px 0;
font-size: 14px;
color: #000;
opacity: 0.6;
}
}
&:disabled {
opacity: 0.3;
.space-top {
border-top: 10px solid #f4f5f9;
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.edit-h5-main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
width: 80vw;
.input-box {
flex-direction: column;
padding: 18px 0;
.input {
background: #f8f8f8;
padding: 10px 12px;
}
.tip {
font-size: 12px;
color: #888;
padding-top: 8px;
}
}
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
h1 {
font-family: PingFang SC;
font-weight: 500;
font-size: 22px;
line-height: 26px;
}
.subtitle {
color: #888;
}
.close {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
}
}
&-footer {
display: flex;
.btn {
flex: 1;
border: none;
background: #147aff;
border-radius: 5px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #fff;
letter-spacing: 0;
line-height: 27px;
padding: 8px 0;
&:disabled {
opacity: 0.3;
}
}
}
}
}
</style>

View File

@@ -1,10 +1,42 @@
import http from '@/utils/request'
/** 创建群组 */
export const createImGroup = data => {
/**
* 创建/修改群组
* @param {*} data
* @param {*} method post 创建 put 修改
* @returns
*/
export const createImGroup = (data, method = 'post') => {
return http({
url: '/api/service/imGroup',
method: 'post',
method,
data
})
}
/** 删除群 */
export const deleteImGroup = groupId => {
return http({
url: `/api/service/imGroup/remove`,
method: 'post',
data: { groupId }
})
}
/** 群成员退出群 */
export const quitImGroup = groupId => {
return http({
url: `/api/service/imGroupMember/leave`,
method: 'post',
data: { groupId }
})
}
/** 删除群组成员 */
export const deleteImGroupMember = (groupId, memberId) => {
return http({
url: `/api/service/imGroup/removeMember`,
method: 'post',
data: { groupId, memberId }
})
}

View File

@@ -11,6 +11,7 @@
url: '/pages/my-index/wallet/index'
},
{ name: '我的团队', icon: 'team', url: '/pages/my-index/my-team' },
{ name: '群创建直播', icon: 'videocam', url: '' },
{
name: '会议记录',
icon: 'meeting',
@@ -100,12 +101,19 @@
@click="item.url && navigateTo(item.url)"
>
<view class="item-name">
<uni-icons
v-if="item.icon === 'videocam'"
type="videocam"
size="64rpx"
color="#1b38b9"
></uni-icons>
<image
v-else
:src="`/static/images/my-index/${item.icon}.png`"
mode="heightFix"
class="icon"
></image>
<text>{{ item.name }}</text>
<text class="text-box">{{ item.name }}</text>
</view>
<image
src="/static/images/public/right-arrow.png"
@@ -248,9 +256,9 @@
align-items: center;
.icon {
height: 64rpx;
margin-right: 16rpx;
}
text {
.text-box {
margin-left: 16rpx;
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 32rpx;

View File

@@ -62,7 +62,6 @@ export const useUserStore = defineStore('user', () => {
* 登录腾讯 IM
*/
const loginTencentIM = async () => {
console.log(11111111111111111111111)
await TUILogin.login({
SDKAppID: tencentUserSig.value.sdkappID,
userID: tencentUserSig.value.userId,

View File

@@ -3,7 +3,7 @@
* @param {Object} options - 透传给 uni.chooseImage 的配置
* @returns {Promise<Array<string>>} 返回选中的临时图片路径数组
*/
export function chooseImage(options = {}) {
export const chooseImage = (options = {}) => {
// 默认配置
const defaultOptions = {
count: 1,