Compare commits
3 Commits
6361aa9ac3
...
49fbeead9a
Author | SHA1 | Date | |
---|---|---|---|
49fbeead9a | |||
cefc3d67dc | |||
57daf0f0f8 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,4 +7,4 @@
|
||||
/idea/
|
||||
.vscode/
|
||||
/unpackage/
|
||||
.hbuilderx/launch.json
|
||||
.hbuilderx/launch.json
|
6
App.vue
6
App.vue
@ -266,8 +266,8 @@ body {
|
||||
// html,
|
||||
// body,
|
||||
// page {
|
||||
// width: 100% !important;
|
||||
// height: 100% !important;
|
||||
// width: 100% ;
|
||||
// height: 100% ;
|
||||
// overflow: hidden;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
60
TUIKit/assets/styles/common.scss
Normal file
60
TUIKit/assets/styles/common.scss
Normal file
@ -0,0 +1,60 @@
|
||||
body, div, ul, ol, dt, dd, li, dl, h1, h2, h3, h4, p {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-style:normal;
|
||||
|
||||
/* font:12px/22px"\5B8B\4F53",Arial,Helvetica,sans-serif; */
|
||||
}
|
||||
|
||||
ol, ul, li {
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
img {
|
||||
border:0;
|
||||
vertical-align:middle;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
body{
|
||||
height: 100% important;
|
||||
color:#000;
|
||||
background:#FFF;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear:both;
|
||||
height:1px;
|
||||
width:100%;
|
||||
overflow:hidden;
|
||||
margin-top:-1px;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
input:focus, input:active, textarea:focus, textarea:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.chat-aside {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 0;
|
||||
box-sizing: border-box;
|
||||
width: 360px !important;
|
||||
border-radius: 8px 0 0 8px;
|
||||
z-index: 9999;
|
||||
max-height: calc(100% - 50px);
|
||||
}
|
314
TUIKit/components/TUIChat/index.vue
Normal file
314
TUIKit/components/TUIChat/index.vue
Normal file
@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<div class="chat" :style="{height:fn+'px'}">
|
||||
<div :style="{height:'100%'}" :class="['tui-chat', !isPC && 'tui-chat-h5']">
|
||||
<div
|
||||
v-if="!currentConversationID"
|
||||
:class="['tui-chat-default', !isPC && 'tui-chat-h5-default']"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<div
|
||||
v-if="currentConversationID"
|
||||
:class="['tui-chat', !isPC && 'tui-chat-h5']"
|
||||
>
|
||||
<ChatHeader
|
||||
:class="[
|
||||
'tui-chat-header',
|
||||
!isPC && 'tui-chat-H5-header',
|
||||
isUniFrameWork && 'tui-chat-uniapp-header',
|
||||
]"
|
||||
:isGroup="isGroup"
|
||||
:headerExtensionList="headerExtensionList"
|
||||
@closeChat="closeChat"
|
||||
@openGroupManagement="handleGroup"
|
||||
/>
|
||||
<Forward @toggleMultipleSelectMode="toggleMultipleSelectMode" />
|
||||
<MessageList
|
||||
ref="messageListRef"
|
||||
:class="['tui-chat-message-list', !isPC && 'tui-chat-h5-message-list']"
|
||||
:isGroup="isGroup"
|
||||
:groupID="groupID"
|
||||
:isNotInGroup="isNotInGroup"
|
||||
:isMultipleSelectMode="isMultipleSelectMode"
|
||||
@handleEditor="handleEditor"
|
||||
@closeInputToolBar="() => changeToolbarDisplayType('none')"
|
||||
@toggleMultipleSelectMode="toggleMultipleSelectMode"
|
||||
/>
|
||||
<div
|
||||
v-if="isNotInGroup"
|
||||
:class="{
|
||||
'tui-chat-leave-group': true,
|
||||
'tui-chat-leave-group-mobile': isMobile,
|
||||
}"
|
||||
>
|
||||
{{ leaveGroupReasonText }}
|
||||
</div>
|
||||
<MultipleSelectPanel
|
||||
v-else-if="isMultipleSelectMode"
|
||||
@oneByOneForwardMessage="oneByOneForwardMessage"
|
||||
@mergeForwardMessage="mergeForwardMessage"
|
||||
@toggleMultipleSelectMode="toggleMultipleSelectMode"
|
||||
/>
|
||||
<template v-else>
|
||||
<MessageInputToolbar
|
||||
v-if="isInputToolbarShow"
|
||||
:class="[
|
||||
'tui-chat-message-input-toolbar',
|
||||
!isPC && 'tui-chat-h5-message-input-toolbar',
|
||||
isUniFrameWork && 'tui-chat-uni-message-input-toolbar'
|
||||
]"
|
||||
:displayType="inputToolbarDisplayType"
|
||||
@insertEmoji="insertEmoji"
|
||||
@changeToolbarDisplayType="changeToolbarDisplayType"
|
||||
@scrollToLatestMessage="scrollToLatestMessage"
|
||||
/>
|
||||
<MessageInput
|
||||
ref="messageInputRef"
|
||||
:class="[
|
||||
'tui-chat-message-input',
|
||||
!isPC && 'tui-chat-h5-message-input',
|
||||
isUniFrameWork && 'tui-chat-uni-message-input',
|
||||
isWeChat && 'tui-chat-wx-message-input',
|
||||
]"
|
||||
:enableAt="featureConfig.InputMention"
|
||||
:isMuted="false"
|
||||
:muteText="TUITranslateService.t('TUIChat.您已被管理员禁言')"
|
||||
:placeholder="TUITranslateService.t('TUIChat.请输入消息')"
|
||||
:inputToolbarDisplayType="inputToolbarDisplayType"
|
||||
@changeToolbarDisplayType="changeToolbarDisplayType"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<!-- Group Management -->
|
||||
<div
|
||||
v-if="!isNotInGroup && !isApp && isUniFrameWork && isGroup && headerExtensionList.length > 0"
|
||||
class="group-profile"
|
||||
@click="handleGroup"
|
||||
>
|
||||
{{ headerExtensionList[0].text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, computed } from '../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
TUIConversationService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUICore, { TUIConstants, ExtensionInfo } from '@tencentcloud/tui-core';
|
||||
import ChatHeader from './chat-header/index.vue';
|
||||
import MessageList from './message-list/index.vue';
|
||||
import MessageInput from './message-input/index.vue';
|
||||
import MultipleSelectPanel from './mulitple-select-panel/index.vue';
|
||||
import Forward from './forward/index.vue';
|
||||
import MessageInputToolbar from './message-input-toolbar/index.vue';
|
||||
import { isPC, isWeChat, isUniFrameWork, isMobile, isApp } from '../../utils/env';
|
||||
import { ToolbarDisplayType } from '../../interface';
|
||||
import TUIChatConfig from './config';
|
||||
// @Start uniapp use Chat only
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app';
|
||||
import { initChat, logout } from './entry-chat-only.ts';
|
||||
|
||||
onLoad((options) => {
|
||||
initChat(options);
|
||||
});
|
||||
|
||||
onUnload(() => {
|
||||
// Whether logout is decided by yourself when the page is unloaded. The default is false.
|
||||
logout(false).then(() => {
|
||||
// Handle success result from promise.then when you set true.
|
||||
}).catch(() => {
|
||||
// handle error
|
||||
});
|
||||
});
|
||||
// @End uniapp use Chat only
|
||||
|
||||
const emits = defineEmits(['closeChat']);
|
||||
const fn = uni.getSystemInfoSync().windowHeight
|
||||
const groupID = ref(undefined);
|
||||
const isGroup = ref(false);
|
||||
const isNotInGroup = ref(false);
|
||||
const notInGroupReason = ref<number>();
|
||||
const currentConversationID = ref();
|
||||
const isMultipleSelectMode = ref(false);
|
||||
const inputToolbarDisplayType = ref<ToolbarDisplayType>('none');
|
||||
const messageInputRef = ref();
|
||||
const messageListRef = ref<InstanceType<typeof MessageList>>();
|
||||
const headerExtensionList = ref<ExtensionInfo[]>([]);
|
||||
const featureConfig = TUIChatConfig.getFeatureConfig();
|
||||
const ht = uni.getSystemInfoSync().windowHeight
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdate,
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdate,
|
||||
});
|
||||
reset();
|
||||
});
|
||||
|
||||
const isInputToolbarShow = computed<boolean>(() => {
|
||||
return isUniFrameWork ? inputToolbarDisplayType.value !== 'none' : true;
|
||||
});
|
||||
|
||||
const leaveGroupReasonText = computed<string>(() => {
|
||||
let text = '';
|
||||
switch (notInGroupReason.value) {
|
||||
case 4:
|
||||
text = TUITranslateService.t('TUIChat.您已被管理员移出群聊');
|
||||
break;
|
||||
case 5:
|
||||
text = TUITranslateService.t('TUIChat.该群聊已被解散');
|
||||
break;
|
||||
case 8:
|
||||
text = TUITranslateService.t('TUIChat.您已退出该群聊');
|
||||
break;
|
||||
default:
|
||||
text = TUITranslateService.t('TUIChat.您已退出该群聊');
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
});
|
||||
|
||||
const reset = () => {
|
||||
TUIConversationService.switchConversation('');
|
||||
};
|
||||
|
||||
const closeChat = (conversationID: string) => {
|
||||
emits('closeChat', conversationID);
|
||||
reset();
|
||||
};
|
||||
|
||||
const insertEmoji = (emojiObj: object) => {
|
||||
messageInputRef.value?.insertEmoji(emojiObj);
|
||||
};
|
||||
|
||||
const handleEditor = (message: IMessageModel, type: string) => {
|
||||
if (!message || !type) return;
|
||||
switch (type) {
|
||||
case 'reference':
|
||||
// todo
|
||||
break;
|
||||
case 'reply':
|
||||
// todo
|
||||
break;
|
||||
case 'reedit':
|
||||
if (message?.payload?.text) {
|
||||
messageInputRef?.value?.reEdit(message?.payload?.text);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleGroup = () => {
|
||||
headerExtensionList.value[0].listener.onClicked({ groupID: groupID.value });
|
||||
};
|
||||
|
||||
function changeToolbarDisplayType(type: ToolbarDisplayType) {
|
||||
inputToolbarDisplayType.value = inputToolbarDisplayType.value === type ? 'none' : type;
|
||||
if (inputToolbarDisplayType.value !== 'none' && isUniFrameWork) {
|
||||
uni.$emit('scroll-to-bottom');
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToLatestMessage() {
|
||||
messageListRef.value?.scrollToLatestMessage();
|
||||
}
|
||||
|
||||
function toggleMultipleSelectMode(visible?: boolean) {
|
||||
isMultipleSelectMode.value = visible === undefined ? !isMultipleSelectMode.value : visible;
|
||||
}
|
||||
|
||||
function mergeForwardMessage() {
|
||||
messageListRef.value?.mergeForwardMessage();
|
||||
}
|
||||
|
||||
function oneByOneForwardMessage() {
|
||||
messageListRef.value?.oneByOneForwardMessage();
|
||||
}
|
||||
|
||||
function updateUIUserNotInGroup(conversation: IConversationModel) {
|
||||
if (conversation?.operationType > 0) {
|
||||
headerExtensionList.value = [];
|
||||
isNotInGroup.value = true;
|
||||
/**
|
||||
* 4 - be removed from the group
|
||||
* 5 - group is dismissed
|
||||
* 8 - quit group
|
||||
*/
|
||||
notInGroupReason.value = conversation?.operationType;
|
||||
} else {
|
||||
isNotInGroup.value = false;
|
||||
notInGroupReason.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function onCurrentConversationUpdate(conversation: IConversationModel) {
|
||||
updateUIUserNotInGroup(conversation);
|
||||
// return when currentConversation is null
|
||||
if (!conversation) {
|
||||
return;
|
||||
}
|
||||
// return when currentConversationID.value is the same as conversation.conversationID.
|
||||
if (currentConversationID.value === conversation?.conversationID) {
|
||||
return;
|
||||
}
|
||||
|
||||
isGroup.value = false;
|
||||
let conversationType = TUIChatEngine.TYPES.CONV_C2C;
|
||||
const conversationID = conversation.conversationID;
|
||||
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
|
||||
conversationType = TUIChatEngine.TYPES.CONV_GROUP;
|
||||
isGroup.value = true;
|
||||
groupID.value = conversationID.replace(TUIChatEngine.TYPES.CONV_GROUP, '');
|
||||
}
|
||||
|
||||
headerExtensionList.value = [];
|
||||
isMultipleSelectMode.value = false;
|
||||
// Initialize chatType
|
||||
TUIChatConfig.setChatType(conversationType);
|
||||
// While converstaion change success, notify callkit and roomkit、or other components.
|
||||
TUICore.notifyEvent(TUIConstants.TUIChat.EVENT.CHAT_STATE_CHANGED, TUIConstants.TUIChat.EVENT_SUB_KEY.CHAT_OPENED, { groupID: groupID.value });
|
||||
// The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
|
||||
TUICore.callService({
|
||||
serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
|
||||
method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
|
||||
params: { conversationID: conversationID },
|
||||
});
|
||||
// When open chat in room, close main chat ui and reset theme.
|
||||
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.ROOM) {
|
||||
if (TUIChatConfig.getFeatureConfig(TUIConstants.TUIChat.FEATURE.InputVoice) === true) {
|
||||
TUIChatConfig.setTheme('light');
|
||||
currentConversationID.value = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Get chat header extensions
|
||||
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.GROUP) {
|
||||
headerExtensionList.value = TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.CHAT_HEADER.EXT_ID);
|
||||
}
|
||||
TUIStore.update(StoreName.CUSTOM, 'activeConversation', conversationID);
|
||||
currentConversationID.value = conversationID;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" src="./style/index.scss">
|
||||
.chat{
|
||||
width: 100% !important;
|
||||
height: 100vh !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
243
TUIKit/components/TUIChat/message-input/index.vue
Normal file
243
TUIKit/components/TUIChat/message-input/index.vue
Normal file
@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<div :class="['message-input', !isPC && 'message-input-h5']">
|
||||
<div class="audio-main-content-line">
|
||||
<MessageInputAudio
|
||||
v-if="(isWeChat || isApp) && isRenderVoice"
|
||||
:class="{
|
||||
'message-input-wx-audio-open': displayType === 'audio',
|
||||
}"
|
||||
:isEnableAudio="displayType === 'audio'"
|
||||
@changeDisplayType="changeDisplayType"
|
||||
/>
|
||||
<MessageInputEditor
|
||||
v-show="displayType === 'editor'"
|
||||
ref="editor"
|
||||
class="message-input-editor"
|
||||
:placeholder="props.placeholder"
|
||||
:isMuted="props.isMuted"
|
||||
:muteText="props.muteText"
|
||||
:enableInput="props.enableInput"
|
||||
:enableAt="props.enableAt"
|
||||
:enableTyping="props.enableTyping"
|
||||
:isGroup="isGroup"
|
||||
@onTyping="onTyping"
|
||||
@onAt="onAt"
|
||||
@onFocus="onFocus"
|
||||
/>
|
||||
<MessageInputAt
|
||||
v-if="props.enableAt"
|
||||
ref="messageInputAtRef"
|
||||
@insertAt="insertAt"
|
||||
@onAtListOpen="onAtListOpen"
|
||||
/>
|
||||
<Icon
|
||||
v-if="isRenderEmojiPicker"
|
||||
class="icon icon-face"
|
||||
:file="faceIcon"
|
||||
:size="'23px'"
|
||||
:hotAreaSize="'3px'"
|
||||
@onClick="changeToolbarDisplayType('emojiPicker')"
|
||||
/>
|
||||
<Icon
|
||||
v-if="isRenderMore"
|
||||
class="icon icon-more"
|
||||
:file="moreIcon"
|
||||
:size="'23px'"
|
||||
:hotAreaSize="'3px'"
|
||||
@onClick="changeToolbarDisplayType('tools')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<MessageQuote
|
||||
:style="{minWidth: 0}"
|
||||
:displayType="displayType"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, watch, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import MessageInputEditor from './message-input-editor.vue';
|
||||
import MessageInputAt from './message-input-at/index.vue';
|
||||
import MessageInputAudio from './message-input-audio.vue';
|
||||
import MessageQuote from './message-input-quote/index.vue';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import faceIcon from '../../../assets/icon/face-uni.png';
|
||||
import moreIcon from '../../../assets/icon/more-uni.png';
|
||||
import { isPC, isH5, isWeChat, isApp } from '../../../utils/env';
|
||||
import { sendTyping } from '../utils/sendMessage';
|
||||
import { ToolbarDisplayType, InputDisplayType } from '../../../interface';
|
||||
import TUIChatConfig from '../config';
|
||||
|
||||
interface IProps {
|
||||
placeholder: string;
|
||||
isMuted?: boolean;
|
||||
muteText?: string;
|
||||
enableInput?: boolean;
|
||||
enableAt?: boolean;
|
||||
enableTyping?: boolean;
|
||||
replyOrReference?: Record<string, any>;
|
||||
inputToolbarDisplayType: ToolbarDisplayType;
|
||||
}
|
||||
interface IEmits {
|
||||
(e: 'changeToolbarDisplayType', displayType: ToolbarDisplayType): void;
|
||||
}
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
placeholder: 'this is placeholder',
|
||||
replyOrReference: () => ({}),
|
||||
isMuted: true,
|
||||
muteText: '',
|
||||
enableInput: true,
|
||||
enableAt: true,
|
||||
enableTyping: true,
|
||||
inputToolbarDisplayType: 'none',
|
||||
});
|
||||
|
||||
const editor = ref();
|
||||
const messageInputAtRef = ref();
|
||||
const currentConversation = ref<IConversationModel>();
|
||||
const isGroup = ref<boolean>(false);
|
||||
const displayType = ref<InputDisplayType>('editor');
|
||||
const featureConfig = TUIChatConfig.getFeatureConfig();
|
||||
const isRenderVoice = ref<boolean>(featureConfig.InputVoice);
|
||||
const isRenderEmojiPicker = ref<boolean>(featureConfig.InputEmoji || featureConfig.InputStickers);
|
||||
const isRenderMore = ref<boolean>(featureConfig.InputImage || featureConfig.InputVideo || featureConfig.InputEvaluation || featureConfig.InputQuickReplies);
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
});
|
||||
|
||||
watch(() => props.inputToolbarDisplayType, (newVal: ToolbarDisplayType) => {
|
||||
if (newVal !== 'none') {
|
||||
changeDisplayType('editor');
|
||||
}
|
||||
});
|
||||
|
||||
function changeDisplayType(display: InputDisplayType) {
|
||||
displayType.value = display;
|
||||
if (display === 'audio') {
|
||||
emits('changeToolbarDisplayType', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
function changeToolbarDisplayType(displayType: ToolbarDisplayType) {
|
||||
emits('changeToolbarDisplayType', displayType);
|
||||
}
|
||||
|
||||
const onTyping = (inputContentEmpty: boolean, inputBlur: boolean) => {
|
||||
sendTyping(inputContentEmpty, inputBlur);
|
||||
};
|
||||
|
||||
const onAt = (show: boolean) => {
|
||||
messageInputAtRef?.value?.toggleAtList(show);
|
||||
};
|
||||
|
||||
const onFocus = () => {
|
||||
// if (isH5) {
|
||||
emits('changeToolbarDisplayType', 'none');
|
||||
// }
|
||||
// 新增隐藏加号控制显示内容的逻辑
|
||||
// emits('changeToolbarDisplayType', 'none');
|
||||
};
|
||||
|
||||
const insertEmoji = (emoji: any) => {
|
||||
editor?.value?.addEmoji && editor?.value?.addEmoji(emoji);
|
||||
};
|
||||
|
||||
const insertAt = (atInfo: any) => {
|
||||
editor?.value?.insertAt && editor?.value?.insertAt(atInfo);
|
||||
};
|
||||
|
||||
const onAtListOpen = () => {
|
||||
editor?.value?.blur && editor?.value?.blur();
|
||||
};
|
||||
|
||||
const reEdit = (content: any) => {
|
||||
editor?.value?.resetEditor();
|
||||
editor?.value?.setEditorContent(content);
|
||||
};
|
||||
|
||||
function onCurrentConversationUpdated(conversation: IConversationModel) {
|
||||
currentConversation.value = conversation;
|
||||
isGroup.value = currentConversation.value?.type === TUIChatEngine.TYPES.CONV_GROUP;
|
||||
}
|
||||
|
||||
function onQuoteMessageUpdated(options?: { message: IMessageModel; type: string }) {
|
||||
// switch text input mode when there is a quote message
|
||||
if (options?.message && options?.type === 'quote') {
|
||||
changeDisplayType('editor');
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
insertEmoji,
|
||||
reEdit,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../../assets/styles/common";
|
||||
|
||||
:not(not) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
background: #ebf0f6;
|
||||
|
||||
&-h5 {
|
||||
padding: 10px 10px 15px;
|
||||
}
|
||||
|
||||
&-editor {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
&-wx-audio-open {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-main-content-line {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
285
TUIKit/components/TUIChat/message-input/message-input-editor.vue
Normal file
285
TUIKit/components/TUIChat/message-input/message-input-editor.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'message-input-container': true,
|
||||
'message-input-container-h5': !isPC,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="props.isMuted"
|
||||
class="message-input-mute"
|
||||
>
|
||||
{{ props.muteText }}
|
||||
</div>
|
||||
<input
|
||||
id="editor"
|
||||
ref="inputRef"
|
||||
v-model="inputText"
|
||||
:adjust-position="true"
|
||||
cursor-spacing="20"
|
||||
confirm-type="send"
|
||||
:confirm-hold="true"
|
||||
maxlength="140"
|
||||
type="text"
|
||||
placeholder-class="input-placeholder"
|
||||
class="message-input-area"
|
||||
:placeholder="props.placeholder"
|
||||
auto-blur
|
||||
@confirm="handleSendMessage"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
@focus="onFocus"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import { TUIStore, StoreName, IConversationModel, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import DraftManager from '../utils/conversationDraft';
|
||||
import { transformTextWithEmojiNamesToKeys } from '../emoji-config';
|
||||
import { isPC } from '../../../utils/env';
|
||||
import { sendMessages } from '../utils/sendMessage';
|
||||
import { ISendMessagePayload } from '../../../interface';
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'this is placeholder',
|
||||
},
|
||||
replayOrReferenceMessage: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
},
|
||||
isMuted: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
muteText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
enableInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
enableAt: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
enableTyping: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isGroup: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['onTyping', 'onFocus', 'onAt']);
|
||||
const inputText = ref('');
|
||||
const inputRef = ref();
|
||||
const inputBlur = ref(true);
|
||||
const inputContentEmpty = ref(true);
|
||||
const allInsertedAtInfo = new Map();
|
||||
const currentConversation = ref<IConversationModel>();
|
||||
const currentConversationID = ref<string>('');
|
||||
const currentQuoteMessage = ref<{ message: IMessageModel; type: string }>();
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
|
||||
uni.$on('insert-emoji', (data) => {
|
||||
inputText.value += data?.emoji?.name;
|
||||
});
|
||||
|
||||
uni.$on('send-message-in-emoji-picker', () => {
|
||||
handleSendMessage();
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (currentConversationID.value) {
|
||||
DraftManager.setStore(currentConversationID.value, inputText.value, inputText.value, currentQuoteMessage.value);
|
||||
}
|
||||
|
||||
uni.$off('insertEmoji');
|
||||
uni.$off('send-message-in-emoji-picker');
|
||||
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
|
||||
reset();
|
||||
});
|
||||
|
||||
const handleSendMessage = () => {
|
||||
const messageList = getEditorContent();
|
||||
resetEditor();
|
||||
sendMessages(messageList as any, currentConversation.value!);
|
||||
};
|
||||
|
||||
const insertAt = (atInfo: any) => {
|
||||
if (!allInsertedAtInfo?.has(atInfo?.id)) {
|
||||
allInsertedAtInfo?.set(atInfo?.id, atInfo?.label);
|
||||
}
|
||||
inputText.value += atInfo?.label;
|
||||
};
|
||||
|
||||
const getEditorContent = () => {
|
||||
let text = inputText.value;
|
||||
text = transformTextWithEmojiNamesToKeys(text);
|
||||
const atUserList: string[] = [];
|
||||
allInsertedAtInfo?.forEach((value: string, key: string) => {
|
||||
if (text?.includes('@' + value)) {
|
||||
atUserList.push(key);
|
||||
}
|
||||
});
|
||||
const payload: ISendMessagePayload = {
|
||||
text,
|
||||
};
|
||||
if (atUserList?.length) {
|
||||
payload.atUserList = atUserList;
|
||||
}
|
||||
return [
|
||||
{
|
||||
type: 'text',
|
||||
payload,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const resetEditor = () => {
|
||||
inputText.value = '';
|
||||
inputContentEmpty.value = true;
|
||||
allInsertedAtInfo?.clear();
|
||||
};
|
||||
|
||||
const setEditorContent = (content: any) => {
|
||||
inputText.value = content;
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
inputBlur.value = true;
|
||||
};
|
||||
|
||||
const onFocus = (e: any) => {
|
||||
inputBlur.value = false;
|
||||
emits('onFocus', e?.detail?.height);
|
||||
};
|
||||
|
||||
const isEditorContentEmpty = () => {
|
||||
inputContentEmpty.value = inputText?.value?.length ? false : true;
|
||||
};
|
||||
|
||||
const onInput = (e: any) => {
|
||||
// uni-app recognizes mention messages
|
||||
const text = e?.detail?.value;
|
||||
isEditorContentEmpty();
|
||||
if (props.isGroup && (text.endsWith('@') || text.endsWith('@\n'))) {
|
||||
// TUIGlobal?.hideKeyboard();
|
||||
emits('onAt', true);
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [inputContentEmpty.value, inputBlur.value],
|
||||
(newVal: any, oldVal: any) => {
|
||||
if (newVal !== oldVal) {
|
||||
emits('onTyping', inputContentEmpty.value, inputBlur.value);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
function onCurrentConversationUpdated(conversation: IConversationModel) {
|
||||
const prevConversationID = currentConversationID.value;
|
||||
currentConversation.value = conversation;
|
||||
currentConversationID.value = conversation?.conversationID;
|
||||
if (prevConversationID !== currentConversationID.value) {
|
||||
if (prevConversationID) {
|
||||
DraftManager.setStore(
|
||||
prevConversationID,
|
||||
inputText.value,
|
||||
inputText.value,
|
||||
currentQuoteMessage.value,
|
||||
);
|
||||
}
|
||||
resetEditor();
|
||||
if (currentConversationID.value) {
|
||||
DraftManager.getStore(currentConversationID.value, setEditorContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onQuoteMessageUpdated(options?: { message: IMessageModel; type: string }) {
|
||||
currentQuoteMessage.value = options;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
inputBlur.value = true;
|
||||
currentConversation.value = null;
|
||||
currentConversationID.value = '';
|
||||
currentQuoteMessage.value = null;
|
||||
resetEditor();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
insertAt,
|
||||
resetEditor,
|
||||
setEditorContent,
|
||||
getEditorContent,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
|
||||
.message-input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 3px 10px 10px;
|
||||
overflow: hidden;
|
||||
|
||||
&-h5 {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 7px 0 7px 10px;
|
||||
font-size: 16px !important;
|
||||
max-height: 86px;
|
||||
}
|
||||
|
||||
.message-input-mute {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-input-area {
|
||||
flex: 1;
|
||||
overflow-y: scroll;
|
||||
min-height: 25px;
|
||||
}
|
||||
}
|
||||
</style>
|
430
TUIKit/components/TUIContact/contact-info/index.vue
Normal file
430
TUIKit/components/TUIContact/contact-info/index.vue
Normal file
@ -0,0 +1,430 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{height: '100%'}"
|
||||
v-if="typeof contactInfoData === 'object' && Object.keys(contactInfoData).length"
|
||||
:class="['tui-contact-info', !isPC && 'tui-contact-info-h5']"
|
||||
>
|
||||
<div
|
||||
v-if="!isPC"
|
||||
:class="[
|
||||
'tui-contact-info-header',
|
||||
!isPC && 'tui-contact-info-h5-header',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-header-icon',
|
||||
!isPC && 'tui-contact-info-h5-header-icon',
|
||||
]"
|
||||
@click="resetContactSearchingUIData"
|
||||
>
|
||||
<Icon :file="backSVG" />
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-header-title',
|
||||
!isPC && 'tui-contact-info-h5-header-title',
|
||||
]"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIContact.添加好友/群聊") }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['tui-contact-info-basic', !isPC && 'tui-contact-info-h5-basic']">
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-basic-text',
|
||||
!isPC && 'tui-contact-info-h5-basic-text',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-basic-text-name',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-name',
|
||||
]"
|
||||
>
|
||||
{{ generateContactInfoName(contactInfoData) }}
|
||||
</div>
|
||||
<div
|
||||
v-for="item in contactInfoBasicList"
|
||||
:key="item.label"
|
||||
:class="[
|
||||
'tui-contact-info-basic-text-other',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-other',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
`${TUITranslateService.t(`TUIContact.${item.label}`)}:
|
||||
${item.data}`
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
:class="[
|
||||
'tui-contact-info-basic-avatar',
|
||||
!isPC && 'tui-contact-info-h5-basic-avatar',
|
||||
]"
|
||||
:src="generateAvatar(contactInfoData)"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="contactInfoMoreList[0]"
|
||||
:class="['tui-contact-info-more', !isPC && 'tui-contact-info-h5-more']"
|
||||
>
|
||||
<div
|
||||
v-for="item in contactInfoMoreList"
|
||||
:key="item.key"
|
||||
:class="[
|
||||
'tui-contact-info-more-item',
|
||||
!isPC && 'tui-contact-info-h5-more-item',
|
||||
item.labelPosition === CONTACT_INFO_LABEL_POSITION.TOP
|
||||
? 'tui-contact-info-more-item-top'
|
||||
: 'tui-contact-info-more-item-left',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-label',
|
||||
!isPC && 'tui-contact-info-h5-more-item-label',
|
||||
]"
|
||||
>
|
||||
{{ `${TUITranslateService.t(`TUIContact.${item.label}`)}` }}
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
v-if="!item.editing"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text-data',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-data',
|
||||
]"
|
||||
>
|
||||
{{ item.data }}
|
||||
</div>
|
||||
<div
|
||||
v-if="item.editable"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text-icon',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-icon',
|
||||
]"
|
||||
@click="setEditing(item)"
|
||||
>
|
||||
<Icon
|
||||
:file="editSVG"
|
||||
width="14px"
|
||||
height="14px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.INPUT"
|
||||
v-model="item.data"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-input',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-input',
|
||||
]"
|
||||
type="text"
|
||||
@confirm="onContactInfoEmitSubmit(item)"
|
||||
@keyup.enter="onContactInfoEmitSubmit(item)"
|
||||
>
|
||||
<textarea
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.TEXTAREA"
|
||||
v-model="item.data"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-textarea',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-textarea',
|
||||
]"
|
||||
confirm-type="done"
|
||||
/>
|
||||
<div
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH"
|
||||
@click="onContactInfoEmitSubmit(item)"
|
||||
>
|
||||
<SwitchBar :value="item.data" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-button',
|
||||
!isPC && 'tui-contact-info-h5-button',
|
||||
]"
|
||||
>
|
||||
<button
|
||||
v-for="item in contactInfoButtonList"
|
||||
:key="item.key"
|
||||
:class="[
|
||||
'tui-contact-info-button-item',
|
||||
!isPC && 'tui-contact-info-h5-button-item',
|
||||
item.type === CONTACT_INFO_BUTTON_TYPE.CANCEL
|
||||
? `tui-contact-info-button-item-cancel`
|
||||
: `tui-contact-info-button-item-submit`,
|
||||
]"
|
||||
@click="onContactInfoButtonClicked(item)"
|
||||
>
|
||||
{{ TUITranslateService.t(`TUIContact.${item.label}`) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IGroupModel,
|
||||
Friend,
|
||||
FriendApplication,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, computed, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import { isPC } from '../../../utils/env';
|
||||
|
||||
import {
|
||||
generateAvatar,
|
||||
generateContactInfoName,
|
||||
generateContactInfoBasic,
|
||||
isFriend,
|
||||
isApplicationType,
|
||||
} from '../utils/index';
|
||||
import {
|
||||
contactMoreInfoConfig,
|
||||
contactButtonConfig,
|
||||
} from './contact-info-config';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import editSVG from '../../../assets/icon/edit.svg';
|
||||
import backSVG from '../../../assets/icon/back.svg';
|
||||
import SwitchBar from '../../common/SwitchBar/index.vue';
|
||||
import {
|
||||
IBlackListUserItem,
|
||||
IContactInfoMoreItem,
|
||||
IContactInfoButton,
|
||||
} from '../../../interface';
|
||||
import {
|
||||
CONTACT_INFO_LABEL_POSITION,
|
||||
CONTACT_INFO_MORE_EDIT_TYPE,
|
||||
CONTACT_INFO_BUTTON_TYPE,
|
||||
} from '../../../constant';
|
||||
import { deepCopy } from '../../TUIChat/utils/utils';
|
||||
|
||||
type IContactInfoType = IGroupModel | Friend | FriendApplication | IBlackListUserItem;
|
||||
|
||||
const emits = defineEmits(['switchConversation']);
|
||||
|
||||
const contactInfoData = ref<IContactInfoType>({} as IContactInfoType);
|
||||
const contactInfoBasicList = ref<Array<{ label: string; data: string }>>([]);
|
||||
const contactInfoMoreList = ref<IContactInfoMoreItem[]>([]);
|
||||
const contactInfoButtonList = ref<IContactInfoButton[]>([]);
|
||||
|
||||
const setEditing = (item: any) => {
|
||||
item.editing = true;
|
||||
};
|
||||
|
||||
const isGroup = computed((): boolean =>
|
||||
(contactInfoData.value as IGroupModel)?.groupID ? true : false,
|
||||
);
|
||||
|
||||
const isApplication = computed((): boolean => {
|
||||
return isApplicationType(contactInfoData?.value);
|
||||
});
|
||||
|
||||
// is both friend, if is group type always false
|
||||
const isBothFriend = ref<boolean>(false);
|
||||
|
||||
// is group member, including ordinary member, admin, group owner
|
||||
const isGroupMember = computed((): boolean => {
|
||||
return (contactInfoData.value as IGroupModel)?.selfInfo?.userID ? true : false;
|
||||
});
|
||||
|
||||
// is in black list, if is group type always false
|
||||
const isInBlackList = computed((): boolean => {
|
||||
return (
|
||||
!isGroup.value
|
||||
&& blackList.value?.findIndex(
|
||||
(item: IBlackListUserItem) =>
|
||||
item?.userID === (contactInfoData.value as IBlackListUserItem)?.userID,
|
||||
) >= 0
|
||||
);
|
||||
});
|
||||
|
||||
const blackList = ref<IBlackListUserItem[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
});
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
});
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
});
|
||||
});
|
||||
|
||||
const resetContactInfoUIData = () => {
|
||||
contactInfoData.value = {} as IContactInfoType;
|
||||
contactInfoBasicList.value = [];
|
||||
contactInfoMoreList.value = [];
|
||||
contactInfoButtonList.value = [];
|
||||
};
|
||||
|
||||
const resetContactSearchingUIData = () => {
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {});
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactSearchingStatus', false);
|
||||
TUIGlobal?.closeSearching && TUIGlobal?.closeSearching();
|
||||
};
|
||||
|
||||
const onContactInfoEmitSubmit = (item: any) => {
|
||||
item.editSubmitHandler
|
||||
&& item.editSubmitHandler({
|
||||
item,
|
||||
contactInfoData: contactInfoData.value,
|
||||
isBothFriend: isBothFriend.value,
|
||||
isInBlackList: isInBlackList.value,
|
||||
});
|
||||
};
|
||||
|
||||
const onContactInfoButtonClicked = (item: any) => {
|
||||
item.onClick
|
||||
&& item.onClick({
|
||||
contactInfoData: contactInfoData.value,
|
||||
contactInfoMoreList: contactInfoMoreList.value,
|
||||
});
|
||||
if (
|
||||
item.key === 'enterGroupConversation'
|
||||
|| item.key === 'enterC2CConversation'
|
||||
) {
|
||||
emits('switchConversation', contactInfoData.value);
|
||||
resetContactSearchingUIData();
|
||||
}
|
||||
};
|
||||
|
||||
const generateMoreInfo = async () => {
|
||||
if (!isApplication.value) {
|
||||
if (
|
||||
(!isGroup.value && !isBothFriend.value && !isInBlackList.value)
|
||||
|| (isGroup.value
|
||||
&& !isGroupMember.value
|
||||
&& (contactInfoData.value as IGroupModel)?.type !== TUIChatEngine?.TYPES?.GRP_AVCHATROOM)
|
||||
) {
|
||||
contactMoreInfoConfig.setWords.data = '';
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setWords);
|
||||
}
|
||||
if (!isGroup.value && !isInBlackList.value) {
|
||||
contactMoreInfoConfig.setRemark.data
|
||||
= (contactInfoData.value as Friend)?.remark || '';
|
||||
contactMoreInfoConfig.setRemark.editing = false;
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setRemark);
|
||||
}
|
||||
if (!isGroup.value && (isBothFriend.value || isInBlackList.value)) {
|
||||
contactMoreInfoConfig.blackList.data = isInBlackList.value || false;
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.blackList);
|
||||
}
|
||||
} else {
|
||||
contactMoreInfoConfig.displayWords.data
|
||||
= (contactInfoData.value as FriendApplication)?.wording || '';
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.displayWords);
|
||||
}
|
||||
};
|
||||
|
||||
const generateButton = () => {
|
||||
if (isInBlackList.value) {
|
||||
return;
|
||||
}
|
||||
if (isApplication.value) {
|
||||
if (
|
||||
(contactInfoData.value as FriendApplication)?.type
|
||||
=== TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
) {
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.refuseFriendApplication,
|
||||
);
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.acceptFriendApplication,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (isGroup.value && isGroupMember.value) {
|
||||
switch ((contactInfoData.value as IGroupModel)?.selfInfo?.role) {
|
||||
case 'Owner':
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.dismissGroup);
|
||||
break;
|
||||
default:
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.quitGroup);
|
||||
break;
|
||||
}
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterGroupConversation,
|
||||
);
|
||||
} else if (!isGroup.value && isBothFriend.value) {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.deleteFriend);
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterC2CConversation,
|
||||
);
|
||||
} else {
|
||||
if (isGroup.value) {
|
||||
contactInfoButtonList?.value?.push(
|
||||
(contactInfoData.value as IGroupModel)?.type === TUIChatEngine?.TYPES?.GRP_AVCHATROOM
|
||||
? contactButtonConfig.joinAVChatGroup
|
||||
: contactButtonConfig.joinGroup,
|
||||
);
|
||||
} else {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.addFriend);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
|
||||
blackList.value = userBlacklist;
|
||||
}
|
||||
|
||||
async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
||||
if (
|
||||
contactInfoData.value
|
||||
&& contactInfo
|
||||
&& JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
resetContactInfoUIData();
|
||||
// deep clone
|
||||
contactInfoData.value = deepCopy(contactInfo) || {};
|
||||
if (!contactInfoData.value || Object.keys(contactInfoData.value)?.length === 0) {
|
||||
return;
|
||||
}
|
||||
contactInfoBasicList.value = generateContactInfoBasic(
|
||||
contactInfoData.value,
|
||||
);
|
||||
isBothFriend.value = await isFriend(contactInfoData.value);
|
||||
generateMoreInfo();
|
||||
generateButton();
|
||||
if (contactInfo.infoKeyList) {
|
||||
contactInfoMoreList.value = contactInfo.infoKeyList.map((key: string) => {
|
||||
return (contactMoreInfoConfig as any)[key];
|
||||
});
|
||||
}
|
||||
if (contactInfo.btnKeyList) {
|
||||
contactInfoButtonList.value = contactInfo.btnKeyList.map((key: string) => {
|
||||
return (contactButtonConfig as any)[key];
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
138
TUIKit/components/TUIContact/index.vue
Normal file
138
TUIKit/components/TUIContact/index.vue
Normal file
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<SelectFriend v-if="isShowSelectFriend" />
|
||||
<div
|
||||
v-else-if="isShowContactList"
|
||||
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
||||
>
|
||||
<div :class="['tui-contact-left', !isPC && 'tui-contact-h5-left']" :style="{height:ht+'px'}">
|
||||
<!-- <ContactSearch /> -->
|
||||
<ContactList :class="['tui-contact-left-list', !isPC && 'tui-contact-h5-left-list']" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isShowContactInfo"
|
||||
:class="['tui-contact-right', !isPC && 'tui-contact-h5-right']"
|
||||
:style="{height:ht+'px'}"
|
||||
>
|
||||
<ContactInfo @switchConversation="switchConversation" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, watchEffect,defineProps } from '../../adapter-vue';
|
||||
import { isPC, isUniFrameWork } from '../../utils/env';
|
||||
const ht = uni.getSystemInfoSync().windowHeight
|
||||
import SelectFriend from './select-friend/index.vue';
|
||||
import ContactSearch from './contact-search/index.vue';
|
||||
import ContactList from './contact-list/index.vue';
|
||||
import ContactInfo from './contact-info/index.vue';
|
||||
|
||||
const emits = defineEmits(['switchConversation']);
|
||||
|
||||
const props = defineProps({
|
||||
// web/h5 single page application display format, uniapp please ignore
|
||||
displayType: {
|
||||
type: String,
|
||||
default: 'contactList', // "contactList" / "selectFriend"
|
||||
require: false,
|
||||
|
||||
},
|
||||
stu: {
|
||||
type: Number, // 这里可以根据实际类型修改,如 String, Number, Object 等
|
||||
require: true,
|
||||
}
|
||||
});
|
||||
|
||||
const displayTypeRef = ref<string>(props.displayType || 'contactList');
|
||||
const isShowSelectFriend = ref(false);
|
||||
const isShowContactList = ref(true);
|
||||
const isShowContactInfo = ref(true);
|
||||
const isstu=ref(props.stu);
|
||||
|
||||
watchEffect(() => {
|
||||
isShowContactList.value = props?.displayType !== 'selectFriend';
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
isShowSelectFriendComponent: (data: any) => {
|
||||
if (!isUniFrameWork && props?.displayType === 'selectFriend') {
|
||||
isShowSelectFriend.value = data;
|
||||
isShowContactList.value = false;
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
isShowSelectFriend.value = true;
|
||||
if (isUniFrameWork) {
|
||||
displayTypeRef.value = 'selectFriend';
|
||||
TUIGlobal?.hideTabBar();
|
||||
}
|
||||
} else {
|
||||
isShowSelectFriend.value = false;
|
||||
if (isUniFrameWork) {
|
||||
displayTypeRef.value = props.displayType;
|
||||
TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
|
||||
}
|
||||
}
|
||||
},
|
||||
currentContactInfo: (contactInfo: any) => {
|
||||
isShowContactInfo.value = isPC || (contactInfo && typeof contactInfo === 'object' && Object.keys(contactInfo)?.length > 0);
|
||||
},
|
||||
});
|
||||
|
||||
const switchConversation = (data: any) => {
|
||||
isUniFrameWork
|
||||
&& TUIGlobal?.navigateTo({
|
||||
url: '/TUIKit/components/TUIChat/index',
|
||||
});
|
||||
emits('switchConversation', data);
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/styles/common";
|
||||
|
||||
.tui-contact {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
&-left {
|
||||
min-width: 285px;
|
||||
flex: 0 0 24%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-right {
|
||||
border-left: 1px solid #f4f5f9;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.tui-contact-h5 {
|
||||
position: relative;
|
||||
|
||||
&-left,
|
||||
&-right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-right {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&-left {
|
||||
&-list {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
139
TUIKit/components/TUIContact/indexsea.vue
Normal file
139
TUIKit/components/TUIContact/indexsea.vue
Normal file
@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<SelectFriend v-if="isShowSelectFriend" />
|
||||
<div
|
||||
v-else-if="isShowContactList"
|
||||
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
||||
>
|
||||
<div :class="['tui-contact-left', !isPC && 'tui-contact-h5-left']" :style="{height:ht+'px'}" >
|
||||
<ContactSearch />
|
||||
<ContactList :class="['tui-contact-left-list', !isPC && 'tui-contact-h5-left-list']" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isShowContactInfo"
|
||||
:class="['tui-contact-right', !isPC && 'tui-contact-h5-right']"
|
||||
:style="{height:ht+'px'}"
|
||||
>
|
||||
<ContactInfo @switchConversation="switchConversation" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, watchEffect,defineProps } from '../../adapter-vue';
|
||||
import { isPC, isUniFrameWork } from '../../utils/env';
|
||||
|
||||
import SelectFriend from './select-friend/index.vue';
|
||||
import ContactSearch from './contact-search/index.vue';
|
||||
import ContactList from './contact-list/indexsea.vue';
|
||||
import ContactInfo from './contact-info/index.vue';
|
||||
|
||||
const emits = defineEmits(['switchConversation']);
|
||||
const ht = uni.getSystemInfoSync().windowHeight
|
||||
const props = defineProps({
|
||||
// web/h5 single page application display format, uniapp please ignore
|
||||
displayType: {
|
||||
type: String,
|
||||
default: 'contactList', // "contactList" / "selectFriend"
|
||||
require: false,
|
||||
|
||||
},
|
||||
stu: {
|
||||
type: Number, // 这里可以根据实际类型修改,如 String, Number, Object 等
|
||||
require: true,
|
||||
}
|
||||
});
|
||||
|
||||
const displayTypeRef = ref<string>(props.displayType || 'contactList');
|
||||
const isShowSelectFriend = ref(false);
|
||||
const isShowContactList = ref(true);
|
||||
const isShowContactInfo = ref(true);
|
||||
const isstu=ref(props.stu);
|
||||
|
||||
watchEffect(() => {
|
||||
isShowContactList.value = props?.displayType !== 'selectFriend';
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
isShowSelectFriendComponent: (data: any) => {
|
||||
if (!isUniFrameWork && props?.displayType === 'selectFriend') {
|
||||
isShowSelectFriend.value = data;
|
||||
isShowContactList.value = false;
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
isShowSelectFriend.value = true;
|
||||
if (isUniFrameWork) {
|
||||
displayTypeRef.value = 'selectFriend';
|
||||
TUIGlobal?.hideTabBar();
|
||||
}
|
||||
} else {
|
||||
isShowSelectFriend.value = false;
|
||||
if (isUniFrameWork) {
|
||||
displayTypeRef.value = props.displayType;
|
||||
TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
|
||||
}
|
||||
}
|
||||
},
|
||||
currentContactInfo: (contactInfo: any) => {
|
||||
isShowContactInfo.value = isPC || (contactInfo && typeof contactInfo === 'object' && Object.keys(contactInfo)?.length > 0);
|
||||
},
|
||||
});
|
||||
|
||||
const switchConversation = (data: any) => {
|
||||
isUniFrameWork
|
||||
&& TUIGlobal?.navigateTo({
|
||||
url: '/TUIKit/components/TUIChat/index',
|
||||
});
|
||||
emits('switchConversation', data);
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/styles/common";
|
||||
|
||||
.tui-contact {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
&-left {
|
||||
min-width: 285px;
|
||||
flex: 0 0 24%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-right {
|
||||
border-left: 1px solid #f4f5f9;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.tui-contact-h5 {
|
||||
position: relative;
|
||||
|
||||
&-left,
|
||||
&-right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-right {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&-left {
|
||||
&-list {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
23
api/home.js
23
api/home.js
@ -12,6 +12,17 @@ export function getAdvertisement() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 像后端传递经纬度
|
||||
*/
|
||||
export function postAdvertisementjw(data) {
|
||||
return http.request({
|
||||
url: "/member/address/location",
|
||||
method: Method.POST,
|
||||
header: { "content-type": "application/x-www-form-urlencoded" },
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -50,7 +61,17 @@ export function getFloorData() {
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地生活楼层数据
|
||||
* @param client_type
|
||||
* @param page_type
|
||||
*/
|
||||
export function getFloorgetBendi() {
|
||||
return http.request({
|
||||
url: `/other/pageData/getBendi?clientType=H5`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 获取获取首页分类数据
|
||||
*/
|
||||
|
@ -13,7 +13,7 @@ import {http, Method} from '@/utils/request.js';
|
||||
*/
|
||||
export function getStoreList(params) {
|
||||
return http.request({
|
||||
url: '/store',
|
||||
url: '/store/store',
|
||||
method: Method.GET,
|
||||
params,
|
||||
});
|
||||
|
@ -284,9 +284,11 @@ export default {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const storedSte = uni.getStorageSync('ste');
|
||||
let data = {
|
||||
skuId: this.goodsDetail.id,
|
||||
num: this.num,
|
||||
ste:storedSte
|
||||
};
|
||||
|
||||
if (val == "cart") {
|
||||
|
@ -7,7 +7,7 @@ export default {
|
||||
shareLink: "https://m-b2b2c.pickmall.cn", //分享地址,也就是在h5中默认的复制地址
|
||||
appid: "wx6f10f29075dc1b0b", //小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态)
|
||||
appSecret: "6dfbe0c72380dce5d49d65b3c91059b1", //可在 manifest.json 查看
|
||||
aMapKey: "d649892b3937a5ad20b76dacb2bcb5bd", //在高德中申请web端key
|
||||
aMapKey: "AOHBZ-VCEL3-XX73N-O623U-FMTP6-ASBTD", //在腾讯的中申请web端key
|
||||
scanAuthNavigation:['https://m-b2b2c.pickmall.cn/'], //扫码认证跳转域名配置 会根据此处配置的路由进行跳转
|
||||
iosAppId:"id1564638363", //AppStore的应用地址id 具体在分享->拷贝链接中查看
|
||||
logo:"https://lilishop-oss.oss-cn-beijing.aliyuncs.com/4c864e133c2944efad1f7282ac8a3b9e.png", //logo地址
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name" : "wzj4",
|
||||
"appid" : "__UNI__6DB512D",
|
||||
"description" : "",
|
||||
"appid" : "__UNI__1F0975C",
|
||||
"description" : "admin",
|
||||
"versionName" : "4.0.0",
|
||||
"versionCode" : 4000049,
|
||||
"transformPx" : false,
|
||||
|
2084
pages.json
2084
pages.json
File diff suppressed because it is too large
Load Diff
@ -541,13 +541,15 @@ export default {
|
||||
// #ifdef APP-PLUS
|
||||
client = "APP";
|
||||
// #endif
|
||||
|
||||
const storedSte = uni.getStorageSync('ste');
|
||||
let submit = {
|
||||
client,
|
||||
way: this.routerVal.way,
|
||||
remark: this.remarkVal,
|
||||
parentOrderSn: "",
|
||||
ste:storedSte
|
||||
};
|
||||
console.log(submit,'===')
|
||||
// 如果是拼团并且当前用户不是团长
|
||||
this.routerVal.parentOrder && this.routerVal.parentOrder.orderSn
|
||||
? (submit.parentOrderSn = this.routerVal.parentOrder.orderSn)
|
||||
|
@ -788,7 +788,7 @@ export default {
|
||||
id: this.productId,
|
||||
};
|
||||
storage.setCartBackbtn(obj);
|
||||
uni.switchTab({
|
||||
uni.navigateTo({
|
||||
url: "/pages/tabbar/cart/cartList",
|
||||
});
|
||||
},
|
||||
|
@ -63,6 +63,7 @@ export default {
|
||||
},
|
||||
|
||||
tostorePage(val) {
|
||||
console.log(val);
|
||||
uni.navigateTo({
|
||||
url: "../product/shopPage?id=" + val.storeId,
|
||||
});
|
||||
|
@ -7,21 +7,22 @@
|
||||
<div class="wrapper" v-if="storeList.length!=0">
|
||||
<div class="store-item" @click="handleClickStore(item)" v-for="(item,index) in storeList" :key="index">
|
||||
<div>
|
||||
<u-image shape="circle" width="100" height="100" :src="item.storeLogo">
|
||||
<u-image shape="square" width="100" height="100" :src="item.storeLogo">
|
||||
</u-image>
|
||||
</div>
|
||||
<div class="store-msg">
|
||||
<div class="store-name">
|
||||
{{item.storeName}}
|
||||
<div> {{item.storeName}}</div>
|
||||
<div class="typeof" >{{item.selfOperated?'自营':'非自营'}}</div>
|
||||
</div>
|
||||
<div class="goods-num">
|
||||
商品 {{item.goodsNum}}
|
||||
商品 {{item.goodsNum}} <span class="line">|</span> <span class="store-collection">收藏 {{item.collectionNum}}</span>
|
||||
</div>
|
||||
<div class="flex store-distance">
|
||||
<div>
|
||||
<span class="store-score">{{item.serviceScore | unitPrice}}</span>
|
||||
<span class="line">|</span>
|
||||
<span class="store-collection">收藏 {{item.collectionNum}}</span>
|
||||
<template v-for="i in 5">
|
||||
<u-icon :name="i <= item.serviceScore ? 'star-fill' : 'star'" color="#FF0000" size="30"></u-icon>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -113,11 +114,19 @@ export default {
|
||||
padding: 24rpx;
|
||||
}
|
||||
.store-msg {
|
||||
width: 100%;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.store-name {
|
||||
font-weight: bold;
|
||||
font-size: 30rpx;
|
||||
font-size: 35rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.typeof{
|
||||
border: 1px solid red;
|
||||
color: red;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
.goods-num,
|
||||
.store-collection {
|
||||
|
@ -1,7 +1,23 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<!-- 楼层装修组件 -->
|
||||
<tpl ref="tpl" />
|
||||
<u-navbar class="navbar" :is-back="false" :is-fixed="false">
|
||||
<div class="tab-container">
|
||||
<div
|
||||
v-for="(item, index) in list1"
|
||||
:key="index"
|
||||
:class="{ active: currentTabIndex === index }"
|
||||
@click="click(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</u-navbar>
|
||||
<!-- 商城 -->
|
||||
<tpl ref="tpl" v-if="currentTabIndex == 1" :ste="1" />
|
||||
<!-- 本地生活 -->
|
||||
<tpl ref="tpl" v-if="currentTabIndex == 0" :ste="0" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -13,21 +29,55 @@ export default {
|
||||
background: {
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
list1: [
|
||||
{
|
||||
name: "本地生活",
|
||||
},
|
||||
{
|
||||
name: "商城",
|
||||
},
|
||||
],
|
||||
currentTabIndex: 0,
|
||||
};
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.$refs.tpl.init();
|
||||
|
||||
uni.stopPullDownRefresh();
|
||||
},
|
||||
components: {
|
||||
tpl,
|
||||
},
|
||||
methods: {
|
||||
click(item) {
|
||||
this.currentTabIndex = this.list1.indexOf(item);
|
||||
},
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.$refs.tpl.init();
|
||||
uni.stopPullDownRefresh();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper{
|
||||
.tab-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
// background-color: #f0f0f0;
|
||||
// padding: 0 16rpx 0 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
.tab-container div {
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.tab-container div.active {
|
||||
border-bottom: 2px solid #F50505;
|
||||
color:#F50505;
|
||||
background-color: transparent;
|
||||
}
|
||||
.wrapper {
|
||||
// background: red;
|
||||
}
|
||||
.navbar{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
103
pages/tabbar/home/template/Talent.vue
Normal file
103
pages/tabbar/home/template/Talent.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="con_view">
|
||||
<div class="-item-tilte">
|
||||
<img class="image-mode" style="height: 150px" :src="model.imgs" />
|
||||
<span> {{ model.name }}</span>
|
||||
</div>
|
||||
<div class="sea" @click="clickSwiper">更多></div>
|
||||
</div>
|
||||
<!-- <div>自己代码编辑</div> -->
|
||||
<div class="menu-list" style="overflow-x: auto; white-space: nowrap; scrollbar-width: none; -ms-overflow-style: none;">
|
||||
<div class="menu-item" v-for="(item, index) in res.list" :key="index">
|
||||
<div>
|
||||
<img class="menu-img" :src="item.img" alt="" @click="tostorePage(item)" />
|
||||
</div>
|
||||
<!-- <div class="menu-title">{{ item.title }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
export default {
|
||||
title: "达人店",
|
||||
props: ["res","model"],
|
||||
watch: {
|
||||
res: {
|
||||
handler(newValue, oldValue) {
|
||||
this.$set(this, "res", newValue);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
//跳转个人店铺
|
||||
tostorePage(val) {
|
||||
uni.navigateTo({
|
||||
url: "/pages/product/shopPage?id=" + val.url.id,
|
||||
});
|
||||
},
|
||||
//跳转店铺
|
||||
clickSwiper(index) {
|
||||
uni.navigateTo({
|
||||
url: "/pages/product/shopList",
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.carousel,
|
||||
.image-mode {
|
||||
width: 30px;
|
||||
height: 30px !important;
|
||||
}
|
||||
.layout{
|
||||
background: url('@/static/tanback.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.con_view {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.-item-tilte {
|
||||
display: flex;
|
||||
& > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
font-weight: 800;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.sea {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
}
|
||||
.menu-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// flex-wrap: wrap;
|
||||
|
||||
> .menu-item {
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
}
|
||||
.menu-img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
.menu-title {
|
||||
font-size: 12px;
|
||||
}
|
||||
.menu-list::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
20
pages/tabbar/home/template/tpl_activity.vue
Normal file
20
pages/tabbar/home/template/tpl_activity.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<img :src="res.list[0].img" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
title: "活动",
|
||||
props: ["res"],
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.layout{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,38 +1,261 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="search" @click="handleSearch">
|
||||
<u-icon name="search"></u-icon>
|
||||
{{ res.list[0].title }}
|
||||
<div class="addres" @click="showPicker" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
{{ cityOrCounty == "" ? "请选择" : cityOrCounty }}
|
||||
</div>
|
||||
<div class="search">{{ res.list[0].title }}</div>
|
||||
<div class="shop" @click="toshop"></div>
|
||||
<m-city
|
||||
:provinceData="list"
|
||||
headTitle="区域选择"
|
||||
ref="cityPicker"
|
||||
@funcValue="getpickerParentValue"
|
||||
pickerSize="3"
|
||||
>
|
||||
</m-city>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import config from "@/config/config";
|
||||
import { postAdvertisementjw } from "@/api/home.js";
|
||||
import gkcity from "@/components/m-city/m-city.vue";
|
||||
import { Icon } from "uview-ui";
|
||||
export default {
|
||||
title:"搜索栏",
|
||||
components: {
|
||||
"m-city": gkcity,
|
||||
Icon,
|
||||
},
|
||||
title: "搜索栏",
|
||||
props: ["res"],
|
||||
data() {
|
||||
return {
|
||||
location: null,
|
||||
// 确保默认值正确读取
|
||||
cityOrCounty: uni.getStorageSync("cityOrCounty") || "",
|
||||
form: {},
|
||||
list: [
|
||||
{
|
||||
id: "",
|
||||
localName: "请选择",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
// 添加本地存储的经纬度
|
||||
storedLat: uni.getStorageSync("latitude") || null,
|
||||
storedLon: uni.getStorageSync("longitude") || null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载时,若本地有存储数据就使用
|
||||
if (this.storedLat && this.storedLon) {
|
||||
this.form.lat = this.storedLat;
|
||||
this.form.lon = this.storedLon;
|
||||
this.getAdvertisementByLocation();
|
||||
}
|
||||
// 确保地区名称也被读取
|
||||
this.cityOrCounty = uni.getStorageSync("cityOrCounty") || "";
|
||||
// this.getLocation();
|
||||
},
|
||||
methods: {
|
||||
toshop() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/tabbar/cart/cartList",
|
||||
});
|
||||
},
|
||||
// 三级地址联动回调
|
||||
async getpickerParentValue(e) {
|
||||
// 将需要绑定的地址设置为空,并赋值
|
||||
this.form.consigneeAddressIdPath = [];
|
||||
this.form.consigneeAddressPath = [];
|
||||
let name = "";
|
||||
|
||||
e.forEach((item, index) => {
|
||||
if (item.id) {
|
||||
// 遍历数据
|
||||
this.form.consigneeAddressIdPath.push(item.id);
|
||||
this.form.consigneeAddressPath.push(item.localName);
|
||||
name += item.localName;
|
||||
this.form.___path = name;
|
||||
}
|
||||
if (index == e.length - 1) {
|
||||
//如果是最后一个
|
||||
let _town = item.children.filter((_child) => {
|
||||
return _child.id == item.id;
|
||||
});
|
||||
|
||||
this.form.lat = _town[0].center.split(",")[1];
|
||||
this.form.lon = _town[0].center.split(",")[0];
|
||||
}
|
||||
});
|
||||
|
||||
const jwobj = {
|
||||
longitude: Number(this.form.lon),
|
||||
latitude: Number(this.form.lat),
|
||||
};
|
||||
|
||||
const { data } = await postAdvertisementjw(jwobj);
|
||||
if (data.success) {
|
||||
// 确保保存的城市名称正确
|
||||
this.cityOrCounty = this.form.consigneeAddressPath[2];
|
||||
uni.setStorageSync("cityOrCounty", this.cityOrCounty);
|
||||
// 存储经纬度
|
||||
uni.setStorageSync("longitude", this.form.lon);
|
||||
uni.setStorageSync("latitude", this.form.lat);
|
||||
// 更新本地存储的经纬度
|
||||
this.storedLat = this.form.lat;
|
||||
this.storedLon = this.form.lon;
|
||||
}
|
||||
},
|
||||
// 根据经纬度获取广告信息
|
||||
async getAdvertisementByLocation() {
|
||||
const jwobj = {
|
||||
longitude: Number(this.storedLon),
|
||||
latitude: Number(this.storedLat),
|
||||
};
|
||||
const { data } = await postAdvertisementjw(jwobj);
|
||||
if (data.success) {
|
||||
// 更新页面内容
|
||||
}
|
||||
},
|
||||
showPicker() {
|
||||
this.$refs.cityPicker.show();
|
||||
},
|
||||
handleSearch() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/navigation/search/searchPage",
|
||||
});
|
||||
},
|
||||
// async getLocation() {
|
||||
// const permision = await import("@/js_sdk/wa-permission/permission.js");
|
||||
// // #ifdef APP-PLUS
|
||||
// if (plus.os.name == "iOS") {
|
||||
// if (permision.judgeIosPermissionLocation()) {
|
||||
// this.getLocationInfo();
|
||||
// } else {
|
||||
// this.refuseLocation();
|
||||
// }
|
||||
// } else {
|
||||
// const result = await permision.requestAndroidPermission(
|
||||
// "android.permission.ACCESS_FINE_LOCATION"
|
||||
// );
|
||||
// if (result == 1) {
|
||||
// this.getLocationInfo();
|
||||
// } else {
|
||||
// this.refuseLocation();
|
||||
// }
|
||||
// }
|
||||
// // #endif
|
||||
// // #ifndef APP-PLUS
|
||||
// this.getLocationInfo();
|
||||
// // #endif
|
||||
// },
|
||||
// getLocationInfo() {
|
||||
// uni.getLocation({
|
||||
// type: "wgs84",
|
||||
// success: async (resss) => {
|
||||
// this.location = resss;
|
||||
// console.log("当前位置:", resss);
|
||||
// const jwobj = {
|
||||
// latitude: resss.latitude,
|
||||
// longitude: resss.longitude,
|
||||
// };
|
||||
// const { data } = await postAdvertisementjw(jwobj);
|
||||
// if (data.success) {
|
||||
// uni.request({
|
||||
// url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${resss.latitude},${resss.longitude}&key=${config.aMapKey}`,
|
||||
// success: (res) => {
|
||||
// console.log("腾讯地图API接口返回信息", res);
|
||||
// this.cityOrCounty = res.data.result.address_component.district || res.data.result.address_component.city;
|
||||
// uni.setStorageSync('cityOrCounty', this.cityOrCounty);
|
||||
// // 存储经纬度
|
||||
// uni.setStorageSync('longitude', resss.longitude);
|
||||
// uni.setStorageSync('latitude', resss.latitude);
|
||||
// // 更新本地存储的经纬度
|
||||
// this.storedLat = resss.latitude;
|
||||
// this.storedLon = resss.longitude;
|
||||
// },
|
||||
// fail: (error) => {
|
||||
// uni.showToast({
|
||||
// title: "获取位置信息失败222腾讯",
|
||||
// icon: "none"
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// fail: (err) => {
|
||||
// console.error("获取位置失败:", err);
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// refuseLocation() {
|
||||
// uni.showModal({
|
||||
// title: "温馨提示",
|
||||
// content: "您已拒绝定位,请开启",
|
||||
// confirmText: "去设置",
|
||||
// success(res) {
|
||||
// if (res.confirm) {
|
||||
// //打开授权设置
|
||||
// // #ifndef MP-WEIXIN
|
||||
// uni.getSystemInfo({
|
||||
// success(res) {
|
||||
// if (res.platform == "ios") {
|
||||
// //IOS
|
||||
// plus.runtime.openURL("app-settings://");
|
||||
// } else if (res.platform == "android") {
|
||||
// //安卓
|
||||
// let main = plus.android.runtimeMainActivity();
|
||||
// let Intent = plus.android.importClass(
|
||||
// "android.content.Intent"
|
||||
// );
|
||||
// let mIntent = new Intent("android.settings.ACTION_SETTINGS");
|
||||
// main.startActivity(mIntent);
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
// // #endif
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
|
||||
.search {
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
width: 68%;
|
||||
height: 32px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ededed;
|
||||
}
|
||||
|
||||
.layout {
|
||||
background: #fff;
|
||||
padding: 0 16rpx;
|
||||
// background: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: transparent;
|
||||
}
|
||||
.addres {
|
||||
// width: 15%;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.layout {
|
||||
// background: #fff;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.shop {
|
||||
width: 10%;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
background: url("@/static/shop.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<!-- uni 中不能使用 vue component 所以用if判断每个组件 -->
|
||||
<div v-for="(item,index) in pageData.list" :key="index">
|
||||
<!-- 搜索栏,如果在楼层装修顶部则会自动浮动,否则不浮动 -->
|
||||
<u-navbar class="navbar" v-if="item.type == 'search'" :is-back="false" :is-fixed="index ===1 ? false : true">
|
||||
<div class="navbar" v-if="item.type == 'search'" :is-back="false" :is-fixed="false">
|
||||
<search style="width:100%" :res="item.options" />
|
||||
<!-- #ifndef H5 -->
|
||||
<!-- 扫码功能 不兼容h5 详情文档: https://uniapp.dcloud.io/api/system/barcode?id=scancode -->
|
||||
@ -11,21 +11,24 @@
|
||||
<u-icon name="scan" @click="scan()" color="#666" size="50"></u-icon>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
</u-navbar>
|
||||
</div>
|
||||
<carousel v-if="item.type == 'carousel'" :res="item.options" />
|
||||
<titleLayout v-if="item.type == 'title'" :res="item.options" />
|
||||
<leftOneRightTwo v-if="item.type == 'leftOneRightTwo'" :res="item.options" />
|
||||
<leftTwoRightOne v-if="item.type == 'leftTwoRightOne'" :res="item.options" />
|
||||
<topOneBottomTwo v-if="item.type == 'topOneBottomTwo'" :res="item.options" />
|
||||
<topTwoBottomOne v-if="item.type == 'topTwoBottomOne'" :res="item.options" />
|
||||
<flexThree v-if="item.type == 'flexThree'" :res="item.options" />
|
||||
|
||||
|
||||
<flexThree v-if="item.type == 'flexThree'" :res="item.options" />
|
||||
<flexFive v-if="item.type == 'flexFive'" :res="item.options" />
|
||||
<flexFour v-if="item.type == 'flexFour'" :res="item.options" />
|
||||
<flexTwo v-if="item.type == 'flexTwo'" :res="item.options" />
|
||||
<textPicture v-if="item.type == 'textPicture'" :res="item.options" />
|
||||
<menuLayout v-if="item.type == 'menu'" :res="item.options" />
|
||||
<flexOne v-if="item.type == 'flexOne'" :res="item.options" />
|
||||
|
||||
<activity v-if="item.type == 'activity'" :res="item.options" />
|
||||
<Talent v-if="item.type == 'talent'" :res="item.options" :model="item" />
|
||||
<goods v-if="item.type == 'goods'" :res="item.options" />
|
||||
|
||||
<group v-if="item.type == 'group'" :res="item.options" />
|
||||
@ -56,8 +59,10 @@ import tpl_menu from "@/pages/tabbar/home/template/tpl_menu"; //五列菜单模
|
||||
import tpl_search from "@/pages/tabbar/home/template/tpl_search"; //搜索栏
|
||||
import tpl_group from "@/pages/tabbar/home/template/tpl_group"; //
|
||||
import tpl_goods from "@/pages/tabbar/home/template/tpl_goods"; //商品分类以及分类中的商品
|
||||
import Talent from "@/pages/tabbar/home/template/Talent"; //达人店
|
||||
import tpl_activity from "@/pages/tabbar/home/template/tpl_activity"; //活动
|
||||
// 结束引用组件
|
||||
import { getFloorData } from "@/api/home"; //获取楼层装修接口
|
||||
import { getFloorData ,getFloorgetBendi} from "@/api/home"; //获取楼层装修接口
|
||||
import permision from "@/js_sdk/wa-permission/permission.js"; //权限工具类
|
||||
import config from "@/config/config";
|
||||
// TODO 后续开发
|
||||
@ -90,11 +95,23 @@ export default {
|
||||
flexOne: tpl_flex_one,
|
||||
goods: tpl_goods,
|
||||
group: tpl_group,
|
||||
Talent,
|
||||
activity: tpl_activity,
|
||||
// spike: tpl_spike,
|
||||
// joinGroup: tpl_join_group,
|
||||
// integral: tpl_integral,
|
||||
},
|
||||
|
||||
props: {
|
||||
ste: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
ste(val) {
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
// #ifdef MP-WEIXIN
|
||||
@ -102,18 +119,32 @@ export default {
|
||||
uni.showShareMenu({ withShareTicket: true });
|
||||
// #endif
|
||||
},
|
||||
|
||||
// provide() {
|
||||
// return {
|
||||
// ste: this.ste,
|
||||
// };
|
||||
// },
|
||||
methods: {
|
||||
/**
|
||||
* 实例化首页数据楼层
|
||||
*/
|
||||
init() {
|
||||
uni.setStorageSync('ste', this.ste); // 存储 ste 的值
|
||||
this.pageData = "";
|
||||
getFloorData().then((res) => {
|
||||
if (this.ste == 1) {
|
||||
getFloorData().then((res) => {
|
||||
if (res.data.success) {
|
||||
this.pageData = JSON.parse(res.data.result.pageData);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
getFloorgetBendi().then((res) => {
|
||||
if (res.data.success) {
|
||||
this.pageData = JSON.parse(res.data.result.pageData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
@ -128,7 +159,6 @@ export default {
|
||||
uni.scanCode({
|
||||
success: function (res) {
|
||||
let path = encodeURIComponent(res.result);
|
||||
|
||||
// WX_CODE 为小程序码
|
||||
if (res.scanType == "WX_CODE") {
|
||||
console.log(res)
|
||||
@ -208,16 +238,26 @@ export default {
|
||||
this.seacnCode();
|
||||
// #endif
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wrapper{
|
||||
height: auto;
|
||||
overflow-y: auto; /* 当内容超出时显示垂直滚动条 */
|
||||
height: 100%;
|
||||
}
|
||||
.navbar{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background:white;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.navbar-right {
|
||||
padding: 0 16rpx 0 0;
|
||||
// padding: 0 16rpx 0 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,3 +1,289 @@
|
||||
<template>
|
||||
<div>im</div>
|
||||
</template>
|
||||
<div style="height: 100%">
|
||||
<div class="jolkp">
|
||||
<div class="fan" :style="{ marginRight: statue == 0 ? '56%' : '70%' }">
|
||||
<span @click="hui(0)">消息</span>
|
||||
</div>
|
||||
<!-- <TUIContact v-else-if="statue==1" :stu="2" /> -->
|
||||
<div @click="hui(1)" class="jolkp_h" v-if="statue == 0"></div>
|
||||
<div @click="fnkiopl" class="jolkp_l"></div>
|
||||
<!-- 弹出框 -->
|
||||
<div v-if="isPopupVisible" class="popup">
|
||||
<ul>
|
||||
<li @click="handleAddFriend">添加好友/群聊</li>
|
||||
<li @click="handleMenu()">发起群聊</li>
|
||||
<li>扫一扫</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="three">
|
||||
<div class="con">
|
||||
<div class="three_div1"></div>
|
||||
<div class="title">点赞</div>
|
||||
</div>
|
||||
<div class="con">
|
||||
<div class="three_div2"></div>
|
||||
<div class="title">关注</div>
|
||||
</div>
|
||||
|
||||
<div class="con">
|
||||
<div class="three_div3"></div>
|
||||
<div class="title">评论</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 搜索添加 -->
|
||||
<TUIContactsea v-if="statue == 2" />
|
||||
<!-- 联系人 -->
|
||||
<TUIContact v-else-if="statue == 1" style="height: 100%;" />
|
||||
<SelectFriend v-else-if="statue == 3" @con="confn" />
|
||||
<SelectFriendqlioa v-else-if="statue == 4" @concen="concen" />
|
||||
<!-- 会话 -->
|
||||
<TUIConversation style="height: 100%;" v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入组件
|
||||
import TUIConversation from "@/TUIKit/components/TUIConversation/index";
|
||||
import TUIContact from "@/TUIKit/components/TUIContact/index";
|
||||
import TUIContactsea from "@/TUIKit/components/TUIContact/indexsea";
|
||||
import { TUILogin } from "@tencentcloud/tui-core";
|
||||
import ContactSearch from "@/TUIKit/components/TUIContact/contact-search/index.vue";
|
||||
import { TUIChatKit } from "@/TUIKit";
|
||||
import SelectFriend from "@/TUIKit/components/TUIContact/select-friend/index.vue";
|
||||
import SelectFriendqlioa from "@/TUIKit/components/TUIGroup/index.vue";
|
||||
import TUICore, { ExtensionInfo, TUIConstants } from "@tencentcloud/tui-core";
|
||||
import storage from "@/utils/storage.js";
|
||||
import { getUserimInfo } from "@/api/members";
|
||||
TUIChatKit.init();
|
||||
let vueVersion = 2;
|
||||
// vueVersion = 3;
|
||||
|
||||
export default {
|
||||
// 注册组件
|
||||
components: {
|
||||
TUIConversation,
|
||||
TUIContact,
|
||||
TUIContactsea,
|
||||
ContactSearch,
|
||||
SelectFriend,
|
||||
SelectFriendqlioa,
|
||||
TUIChatKit,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statue: 0, //1联系人 2添加好友/群聊 0对话记录 3建群 4群名称
|
||||
isPopupVisible: false, // 控制弹出框的显示与隐藏
|
||||
isq: false,
|
||||
};
|
||||
},
|
||||
created() {},
|
||||
onShow() {
|
||||
// 每次进入页面时调用的方法
|
||||
//获取用户信息
|
||||
getUserimInfo()
|
||||
.then(({ data }) => {
|
||||
if (data.code == 200) {
|
||||
const par = data.result;
|
||||
TUILogin.login({
|
||||
SDKAppID: par.sdkAppId,
|
||||
userID: par.userID,
|
||||
userSig: par.userSig,
|
||||
useUploadPlugin: true, // If you need to send rich media messages, please set to true.
|
||||
framework: `vue${vueVersion}`, // framework used vue2 / vue3
|
||||
});
|
||||
} else {
|
||||
// 接口返回非 200 状态码,跳转登录页面
|
||||
uni.navigateTo({
|
||||
url: "/pages/passport/login",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 请求失败,跳转登录页面
|
||||
uni.navigateTo({
|
||||
url: "/pages/passport/login",
|
||||
});
|
||||
});
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
//完成跳转群名
|
||||
confn() {
|
||||
this.statue = 4;
|
||||
},
|
||||
concen() {
|
||||
this.statue = 0;
|
||||
this.isPopupVisible = false;
|
||||
},
|
||||
hui(index) {
|
||||
this.statue = index;
|
||||
},
|
||||
fnkiopl() {
|
||||
this.isPopupVisible = !this.isPopupVisible;
|
||||
},
|
||||
handleMenu() {
|
||||
const fn = [
|
||||
...TUICore.getExtensionList(
|
||||
TUIConstants.TUISearch.EXTENSION.SEARCH_MORE.EXT_ID
|
||||
),
|
||||
];
|
||||
const item = fn[1];
|
||||
const {
|
||||
listener = {
|
||||
onClicked: () => {},
|
||||
},
|
||||
} = item;
|
||||
listener?.onClicked?.(item);
|
||||
this.statue = 3;
|
||||
},
|
||||
// 处理添加好友/群聊点击事件
|
||||
handleAddFriend() {
|
||||
this.statue = 2;
|
||||
this.isPopupVisible = false;
|
||||
},
|
||||
|
||||
// // 打开会话列表
|
||||
// openConversationList() {
|
||||
// uni.navigateTo({
|
||||
// url: '/TUIKit/components/TUIConversation/index'
|
||||
// });
|
||||
// },
|
||||
// // 打开联系人
|
||||
// openContact() {
|
||||
// uni.navigateTo({
|
||||
// url: '/TUIKit/components/TUIContact/index'
|
||||
// });
|
||||
// },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
uni-page-body,
|
||||
html,
|
||||
body,
|
||||
page {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.jolkp {
|
||||
height: 66px;
|
||||
display: flex;
|
||||
font-size: 15px;
|
||||
justify-content: end;
|
||||
/* 设置背景图 */
|
||||
background-image: url("@/static/im/Rectangle.png");
|
||||
/* 让背景图覆盖整个元素 */
|
||||
background-size: cover;
|
||||
/* 背景图不重复 */
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.jolkp_l {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-image: url("@/static/im/Frame.png");
|
||||
/* 让背景图覆盖整个元素 */
|
||||
background-size: cover;
|
||||
/* 背景图不重复 */
|
||||
background-repeat: no-repeat;
|
||||
margin-top: 35px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.jolkp_h {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-image: url("@/static/im/user.png");
|
||||
/* 让背景图覆盖整个元素 */
|
||||
background-size: cover;
|
||||
/* 背景图不重复 */
|
||||
background-repeat: no-repeat;
|
||||
margin-top: 35px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
/* 调整弹出框的位置 */
|
||||
right: 5px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.popup ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.popup li {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popup li:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.fan {
|
||||
width: 10%;
|
||||
height: "100%";
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.three {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background: rgb(255, 255, 255);
|
||||
}
|
||||
.con{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.three_div1 {
|
||||
height: 3rem;
|
||||
width: 40%;
|
||||
background-image: url("@/static/im/kf.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.three_div2 {
|
||||
width: 40%;
|
||||
height: 3rem;
|
||||
background-image: url("@/static/im/hy.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.three_div3 {
|
||||
height: 3rem;
|
||||
background-image: url("@/static/im/da.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
BIN
static/shop.png
Normal file
BIN
static/shop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
static/tabbar/bei.png
Normal file
BIN
static/tabbar/bei.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 496 KiB |
BIN
static/tabbar/shop.png
Normal file
BIN
static/tabbar/shop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
static/tanback.png
Normal file
BIN
static/tanback.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
Loading…
x
Reference in New Issue
Block a user