提现功能需要添加
This commit is contained in:
19
App.vue
19
App.vue
@@ -1,15 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
||||||
import { useTokenStore } from './stores/token'
|
|
||||||
import { reLaunch } from './utils/router'
|
import { reLaunch } from './utils/router'
|
||||||
import { useAuthUser } from './composables/useAuthUser'
|
import { useAuthUser } from './composables/useAuthUser'
|
||||||
|
import { useUserStore } from './stores/user'
|
||||||
|
|
||||||
|
import { TUIChatKit } from './TUIKit';
|
||||||
|
TUIChatKit.init();
|
||||||
|
|
||||||
const { token } = useAuthUser()
|
const { token } = useAuthUser()
|
||||||
|
const { loginTencentIM } = useUserStore()
|
||||||
/** 静默登录逻辑 */
|
/** 静默登录逻辑 */
|
||||||
const silentLogin = async () => {
|
const silentLogin = async () => {
|
||||||
console.log(token.value, '==')
|
console.log(token.value, '==')
|
||||||
if (token.value) {
|
if (token.value) {
|
||||||
reLaunch('/pages/news-list/news-list')
|
loginTencentIM()
|
||||||
|
reLaunch('/TUIKit/components/TUIConversation/index')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,4 +39,14 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/*每个页面公共css */
|
/*每个页面公共css */
|
||||||
@import './styles/global.scss';
|
@import './styles/global.scss';
|
||||||
|
|
||||||
|
/* common css for page */
|
||||||
|
// uni-page-body,
|
||||||
|
// html,
|
||||||
|
// body,
|
||||||
|
// page {
|
||||||
|
// width: 100% !important;
|
||||||
|
// height: 100% !important;
|
||||||
|
// overflow: hidden;
|
||||||
|
// }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="chat" :style="{marginBottom: keywordHight + 'px'}">
|
<div class="chat" :style="{ marginBottom: keywordHight + 'px' }">
|
||||||
<div :class="['tui-chat', !isPC && 'tui-chat-h5']">
|
<div :class="['tui-chat', !isPC && 'tui-chat-h5']">
|
||||||
<div
|
<div
|
||||||
v-if="!currentConversationID"
|
v-if="!currentConversationID"
|
||||||
@@ -20,7 +20,10 @@
|
|||||||
<Forward @toggleMultipleSelectMode="toggleMultipleSelectMode" />
|
<Forward @toggleMultipleSelectMode="toggleMultipleSelectMode" />
|
||||||
<MessageList
|
<MessageList
|
||||||
ref="messageListRef"
|
ref="messageListRef"
|
||||||
:class="['tui-chat-message-list', !isPC && 'tui-chat-h5-message-list']"
|
:class="[
|
||||||
|
'tui-chat-message-list',
|
||||||
|
!isPC && 'tui-chat-h5-message-list'
|
||||||
|
]"
|
||||||
:isGroup="isGroup"
|
:isGroup="isGroup"
|
||||||
:groupID="groupID"
|
:groupID="groupID"
|
||||||
:isNotInGroup="isNotInGroup"
|
:isNotInGroup="isNotInGroup"
|
||||||
@@ -33,7 +36,7 @@
|
|||||||
v-if="isNotInGroup"
|
v-if="isNotInGroup"
|
||||||
:class="{
|
:class="{
|
||||||
'tui-chat-leave-group': true,
|
'tui-chat-leave-group': true,
|
||||||
'tui-chat-leave-group-mobile': isMobile,
|
'tui-chat-leave-group-mobile': isMobile
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ leaveGroupReasonText }}
|
{{ leaveGroupReasonText }}
|
||||||
@@ -63,7 +66,7 @@
|
|||||||
'tui-chat-message-input',
|
'tui-chat-message-input',
|
||||||
!isPC && 'tui-chat-h5-message-input',
|
!isPC && 'tui-chat-h5-message-input',
|
||||||
isUniFrameWork && 'tui-chat-uni-message-input',
|
isUniFrameWork && 'tui-chat-uni-message-input',
|
||||||
isWeChat && 'tui-chat-wx-message-input',
|
isWeChat && 'tui-chat-wx-message-input'
|
||||||
]"
|
]"
|
||||||
:enableAt="featureConfig.InputMention"
|
:enableAt="featureConfig.InputMention"
|
||||||
:isMuted="false"
|
:isMuted="false"
|
||||||
@@ -78,232 +81,275 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from '../../adapter-vue';
|
import {
|
||||||
import TUIChatEngine, {
|
ref,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
computed
|
||||||
|
} from '../../adapter-vue'
|
||||||
|
import TUIChatEngine, {
|
||||||
TUITranslateService,
|
TUITranslateService,
|
||||||
TUIConversationService,
|
TUIConversationService,
|
||||||
TUIStore,
|
TUIStore,
|
||||||
StoreName,
|
StoreName,
|
||||||
IMessageModel,
|
IMessageModel,
|
||||||
IConversationModel,
|
IConversationModel
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import TUICore, { TUIConstants, ExtensionInfo } from '@tencentcloud/tui-core-lite';
|
import TUICore, {
|
||||||
import ChatHeader from './chat-header/index.vue';
|
TUIConstants,
|
||||||
import MessageList from './message-list/index.vue';
|
ExtensionInfo
|
||||||
import MessageInput from './message-input/index.vue';
|
} from '@tencentcloud/tui-core-lite'
|
||||||
import MultipleSelectPanel from './mulitple-select-panel/index.vue';
|
import ChatHeader from './chat-header/index.vue'
|
||||||
import Forward from './forward/index.vue';
|
import MessageList from './message-list/index.vue'
|
||||||
import MessageInputToolbar from './message-input-toolbar/index.vue';
|
import MessageInput from './message-input/index.vue'
|
||||||
import { isPC, isWeChat, isUniFrameWork, isMobile, isApp } from '../../utils/env';
|
import MultipleSelectPanel from './mulitple-select-panel/index.vue'
|
||||||
import { ToolbarDisplayType } from '../../interface';
|
import Forward from './forward/index.vue'
|
||||||
import TUIChatConfig from './config';
|
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
|
// @Start uniapp use Chat only
|
||||||
import { onLoad, onUnload } from '@dcloudio/uni-app';
|
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||||
import { initChat, logout } from './entry-chat-only.ts';
|
import { initChat, logout } from './entry-chat-only.ts'
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad(options => {
|
||||||
initChat(options);
|
initChat(options)
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnload(() => {
|
onUnload(() => {
|
||||||
// Whether logout is decided by yourself when the page is unloaded. The default is false.
|
// Whether logout is decided by yourself when the page is unloaded. The default is false.
|
||||||
logout(false).then(() => {
|
logout(false)
|
||||||
|
.then(() => {
|
||||||
// Handle success result from promise.then when you set true.
|
// Handle success result from promise.then when you set true.
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(() => {
|
||||||
// handle error
|
// handle error
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
// @End uniapp use Chat only
|
// @End uniapp use Chat only
|
||||||
|
|
||||||
const emits = defineEmits(['closeChat']);
|
const emits = defineEmits(['closeChat'])
|
||||||
|
|
||||||
const groupID = ref(undefined);
|
const groupID = ref(undefined)
|
||||||
const isGroup = ref(false);
|
const isGroup = ref(false)
|
||||||
const isNotInGroup = ref(false);
|
const isNotInGroup = ref(false)
|
||||||
const notInGroupReason = ref<number>();
|
const notInGroupReason = ref<number>()
|
||||||
const currentConversationID = ref();
|
const currentConversationID = ref()
|
||||||
const isMultipleSelectMode = ref(false);
|
const isMultipleSelectMode = ref(false)
|
||||||
const inputToolbarDisplayType = ref<ToolbarDisplayType>('none');
|
const inputToolbarDisplayType = ref<ToolbarDisplayType>('none')
|
||||||
const messageInputRef = ref();
|
const messageInputRef = ref()
|
||||||
const messageListRef = ref<InstanceType<typeof MessageList>>();
|
const messageListRef = ref<InstanceType<typeof MessageList>>()
|
||||||
const headerExtensionList = ref<ExtensionInfo[]>([]);
|
const headerExtensionList = ref<ExtensionInfo[]>([])
|
||||||
const featureConfig = TUIChatConfig.getFeatureConfig();
|
const featureConfig = TUIChatConfig.getFeatureConfig()
|
||||||
const keywordHight = ref(0);
|
const keywordHight = ref(0)
|
||||||
|
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
const screenHeight = systemInfo.screenHeight;
|
const screenHeight = systemInfo.screenHeight
|
||||||
|
|
||||||
const windowResizeCallback = (res) => {
|
const windowResizeCallback = res => {
|
||||||
const value = screenHeight - res.size.windowHeight;
|
const value = screenHeight - res.size.windowHeight
|
||||||
if (value > 0 && inputToolbarDisplayType.value !== 'dialog') {
|
if (value > 0 && inputToolbarDisplayType.value !== 'dialog') {
|
||||||
inputToolbarDisplayType.value = 'none';
|
inputToolbarDisplayType.value = 'none'
|
||||||
}
|
}
|
||||||
uni.$emit('scroll-to-bottom');
|
uni.$emit('scroll-to-bottom')
|
||||||
keywordHight.value = value;
|
keywordHight.value = value
|
||||||
};
|
}
|
||||||
uni.onWindowResize(windowResizeCallback);
|
uni.onWindowResize(windowResizeCallback)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.CONV, {
|
TUIStore.watch(StoreName.CONV, {
|
||||||
currentConversation: onCurrentConversationUpdate,
|
currentConversation: onCurrentConversationUpdate
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.CONV, {
|
TUIStore.unwatch(StoreName.CONV, {
|
||||||
currentConversation: onCurrentConversationUpdate,
|
currentConversation: onCurrentConversationUpdate
|
||||||
});
|
})
|
||||||
reset();
|
reset()
|
||||||
});
|
})
|
||||||
|
|
||||||
const isInputToolbarShow = computed<boolean>(() => {
|
const isInputToolbarShow = computed<boolean>(() => {
|
||||||
return isUniFrameWork ? inputToolbarDisplayType.value !== 'none' : true;
|
return isUniFrameWork
|
||||||
});
|
? inputToolbarDisplayType.value !== 'none'
|
||||||
|
: true
|
||||||
|
})
|
||||||
|
|
||||||
const leaveGroupReasonText = computed<string>(() => {
|
const leaveGroupReasonText = computed<string>(() => {
|
||||||
let text = '';
|
let text = ''
|
||||||
switch (notInGroupReason.value) {
|
switch (notInGroupReason.value) {
|
||||||
case 4:
|
case 4:
|
||||||
text = TUITranslateService.t('TUIChat.您已被管理员移出群聊');
|
text = TUITranslateService.t('TUIChat.您已被管理员移出群聊')
|
||||||
break;
|
break
|
||||||
case 5:
|
case 5:
|
||||||
text = TUITranslateService.t('TUIChat.该群聊已被解散');
|
text = TUITranslateService.t('TUIChat.该群聊已被解散')
|
||||||
break;
|
break
|
||||||
case 8:
|
case 8:
|
||||||
text = TUITranslateService.t('TUIChat.您已退出该群聊');
|
text = TUITranslateService.t('TUIChat.您已退出该群聊')
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
text = TUITranslateService.t('TUIChat.您已退出该群聊');
|
text = TUITranslateService.t('TUIChat.您已退出该群聊')
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
return text;
|
return text
|
||||||
});
|
})
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
TUIConversationService.switchConversation('');
|
TUIConversationService.switchConversation('')
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeChat = (conversationID: string) => {
|
const closeChat = (conversationID: string) => {
|
||||||
emits('closeChat', conversationID);
|
emits('closeChat', conversationID)
|
||||||
reset();
|
reset()
|
||||||
};
|
}
|
||||||
|
|
||||||
const insertEmoji = (emojiObj: object) => {
|
const insertEmoji = (emojiObj: object) => {
|
||||||
messageInputRef.value?.insertEmoji(emojiObj);
|
messageInputRef.value?.insertEmoji(emojiObj)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleEditor = (message: IMessageModel, type: string) => {
|
const handleEditor = (message: IMessageModel, type: string) => {
|
||||||
if (!message || !type) return;
|
if (!message || !type) return
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'reference':
|
case 'reference':
|
||||||
// todo
|
// todo
|
||||||
break;
|
break
|
||||||
case 'reply':
|
case 'reply':
|
||||||
// todo
|
// todo
|
||||||
break;
|
break
|
||||||
case 'reedit':
|
case 'reedit':
|
||||||
if (message?.payload?.text) {
|
if (message?.payload?.text) {
|
||||||
messageInputRef?.value?.reEdit(message?.payload?.text);
|
messageInputRef?.value?.reEdit(message?.payload?.text)
|
||||||
}
|
}
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
break;
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleGroup = () => {
|
const handleGroup = () => {
|
||||||
headerExtensionList.value[0].listener.onClicked({ groupID: groupID.value });
|
headerExtensionList.value[0].listener.onClicked({
|
||||||
};
|
groupID: groupID.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function changeToolbarDisplayType(type: ToolbarDisplayType) {
|
function changeToolbarDisplayType(type: ToolbarDisplayType) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
inputToolbarDisplayType.value = inputToolbarDisplayType.value === type ? 'none' : type;
|
inputToolbarDisplayType.value =
|
||||||
|
inputToolbarDisplayType.value === type ? 'none' : type
|
||||||
if (inputToolbarDisplayType.value !== 'none' && isUniFrameWork) {
|
if (inputToolbarDisplayType.value !== 'none' && isUniFrameWork) {
|
||||||
uni.$emit('scroll-to-bottom');
|
uni.$emit('scroll-to-bottom')
|
||||||
}
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToLatestMessage() {
|
function scrollToLatestMessage() {
|
||||||
messageListRef.value?.scrollToLatestMessage();
|
messageListRef.value?.scrollToLatestMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMultipleSelectMode(visible?: boolean) {
|
function toggleMultipleSelectMode(visible?: boolean) {
|
||||||
isMultipleSelectMode.value = visible === undefined ? !isMultipleSelectMode.value : visible;
|
isMultipleSelectMode.value =
|
||||||
}
|
visible === undefined ? !isMultipleSelectMode.value : visible
|
||||||
|
}
|
||||||
|
|
||||||
function mergeForwardMessage() {
|
function mergeForwardMessage() {
|
||||||
messageListRef.value?.mergeForwardMessage();
|
messageListRef.value?.mergeForwardMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
function oneByOneForwardMessage() {
|
function oneByOneForwardMessage() {
|
||||||
messageListRef.value?.oneByOneForwardMessage();
|
messageListRef.value?.oneByOneForwardMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUIUserNotInGroup(conversation: IConversationModel) {
|
function updateUIUserNotInGroup(conversation: IConversationModel) {
|
||||||
if (conversation?.operationType > 0) {
|
if (conversation?.operationType > 0) {
|
||||||
headerExtensionList.value = [];
|
headerExtensionList.value = []
|
||||||
isNotInGroup.value = true;
|
isNotInGroup.value = true
|
||||||
/**
|
/**
|
||||||
* 4 - be removed from the group
|
* 4 - be removed from the group
|
||||||
* 5 - group is dismissed
|
* 5 - group is dismissed
|
||||||
* 8 - quit group
|
* 8 - quit group
|
||||||
*/
|
*/
|
||||||
notInGroupReason.value = conversation?.operationType;
|
notInGroupReason.value = conversation?.operationType
|
||||||
} else {
|
} else {
|
||||||
isNotInGroup.value = false;
|
isNotInGroup.value = false
|
||||||
notInGroupReason.value = undefined;
|
notInGroupReason.value = undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onCurrentConversationUpdate(conversation: IConversationModel) {
|
function onCurrentConversationUpdate(conversation: IConversationModel) {
|
||||||
updateUIUserNotInGroup(conversation);
|
updateUIUserNotInGroup(conversation)
|
||||||
// return when currentConversation is null
|
// return when currentConversation is null
|
||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
// return when currentConversationID.value is the same as conversation.conversationID.
|
// return when currentConversationID.value is the same as conversation.conversationID.
|
||||||
if (currentConversationID.value === conversation?.conversationID) {
|
if (currentConversationID.value === conversation?.conversationID) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroup.value = false;
|
isGroup.value = false
|
||||||
let conversationType = TUIChatEngine.TYPES.CONV_C2C;
|
let conversationType = TUIChatEngine.TYPES.CONV_C2C
|
||||||
const conversationID = conversation.conversationID;
|
const conversationID = conversation.conversationID
|
||||||
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
|
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
|
||||||
conversationType = TUIChatEngine.TYPES.CONV_GROUP;
|
conversationType = TUIChatEngine.TYPES.CONV_GROUP
|
||||||
isGroup.value = true;
|
isGroup.value = true
|
||||||
groupID.value = conversationID.replace(TUIChatEngine.TYPES.CONV_GROUP, '');
|
groupID.value = conversationID.replace(
|
||||||
|
TUIChatEngine.TYPES.CONV_GROUP,
|
||||||
|
''
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
headerExtensionList.value = [];
|
headerExtensionList.value = []
|
||||||
isMultipleSelectMode.value = false;
|
isMultipleSelectMode.value = false
|
||||||
// Initialize chatType
|
// Initialize chatType
|
||||||
TUIChatConfig.setChatType(conversationType);
|
TUIChatConfig.setChatType(conversationType)
|
||||||
// While converstaion change success, notify callkit and roomkit、or other components.
|
// 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 });
|
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.
|
// The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
|
||||||
TUICore.callService({
|
TUICore.callService({
|
||||||
serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
|
serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
|
||||||
method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
|
method:
|
||||||
params: { conversationID: conversationID },
|
TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD
|
||||||
});
|
.ACTIVE_CONVERSATION,
|
||||||
|
params: { conversationID: conversationID }
|
||||||
|
})
|
||||||
// When open chat in room, close main chat ui and reset theme.
|
// When open chat in room, close main chat ui and reset theme.
|
||||||
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.ROOM) {
|
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.ROOM) {
|
||||||
if (TUIChatConfig.getFeatureConfig(TUIConstants.TUIChat.FEATURE.InputVoice) === true) {
|
if (
|
||||||
TUIChatConfig.setTheme('light');
|
TUIChatConfig.getFeatureConfig(
|
||||||
currentConversationID.value = '';
|
TUIConstants.TUIChat.FEATURE.InputVoice
|
||||||
return;
|
) === true
|
||||||
|
) {
|
||||||
|
TUIChatConfig.setTheme('light')
|
||||||
|
currentConversationID.value = ''
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Get chat header extensions
|
// Get chat header extensions
|
||||||
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.GROUP) {
|
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.GROUP) {
|
||||||
headerExtensionList.value = TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.CHAT_HEADER.EXT_ID);
|
headerExtensionList.value = TUICore.getExtensionList(
|
||||||
|
TUIConstants.TUIChat.EXTENSION.CHAT_HEADER.EXT_ID
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TUIStore.update(
|
||||||
|
StoreName.CUSTOM,
|
||||||
|
'activeConversation',
|
||||||
|
conversationID
|
||||||
|
)
|
||||||
|
currentConversationID.value = conversationID
|
||||||
}
|
}
|
||||||
TUIStore.update(StoreName.CUSTOM, 'activeConversation', conversationID);
|
|
||||||
currentConversationID.value = conversationID;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss" src="./style/index.scss"></style>
|
<style scoped lang="scss" src="./style/index.scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
uni-page-body,
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
page {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
@import '../../../assets/styles/common';
|
@import '../../../assets/styles/common';
|
||||||
@import './web';
|
@import './web';
|
||||||
@import './h5';
|
@import './h5';
|
||||||
|
|||||||
@@ -26,64 +26,84 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { deepCopy } from '../../../TUIChat/utils/utils';
|
import { deepCopy } from '../../../TUIChat/utils/utils'
|
||||||
import ContactListItem from '../contact-list-item/index.vue';
|
import ContactListItem from '../contact-list-item/index.vue'
|
||||||
import { sortByFirstChar } from '../../utils/sortByFirstChar';
|
import { sortByFirstChar } from '../../utils/sortByFirstChar'
|
||||||
import { onMounted, onUnmounted, ref } from '../../../../adapter-vue';
|
import { onMounted, onUnmounted, ref } from '../../../../adapter-vue'
|
||||||
import { Friend, StoreName, TUIStore } from '@tencentcloud/chat-uikit-engine-lite';
|
import {
|
||||||
import { FriendListData } from '../../../../interface';
|
Friend,
|
||||||
|
StoreName,
|
||||||
|
TUIStore
|
||||||
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
|
import { FriendListData } from '../../../../interface'
|
||||||
|
|
||||||
const emits = defineEmits(['enterConversation']);
|
const emits = defineEmits(['enterConversation'])
|
||||||
|
|
||||||
const friendListData = ref<FriendListData>({
|
const friendListData = ref<FriendListData>({
|
||||||
key: 'friendList',
|
key: 'friendList',
|
||||||
title: '我的好友',
|
title: '我的好友',
|
||||||
map: {},
|
map: {}
|
||||||
});
|
})
|
||||||
|
|
||||||
function onFriendListUpdated(friendList: Friend[]) {
|
function onFriendListUpdated(friendList: Friend[]) {
|
||||||
const { groupedList } = sortByFirstChar(
|
const { groupedList } = sortByFirstChar(
|
||||||
friendList,
|
friendList,
|
||||||
(friend: Friend) => friend.remark || friend.profile?.nick || friend.userID || '');
|
(friend: Friend) =>
|
||||||
friendListData.value.map = groupedList;
|
friend.remark || friend.profile?.nick || friend.userID || ''
|
||||||
}
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
console.log(groupedList, '====222==')
|
||||||
|
friendListData.value.map = {
|
||||||
|
A: [
|
||||||
|
{
|
||||||
|
profile: {
|
||||||
|
nick: '测试第三方',
|
||||||
|
avatar:
|
||||||
|
'https://jckj-1309258891.cos.ap-chengdu.myqcloud.com/common/19101767159307246_.png'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
...groupedList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.FRIEND, {
|
TUIStore.watch(StoreName.FRIEND, {
|
||||||
friendList: onFriendListUpdated,
|
friendList: onFriendListUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.FRIEND, {
|
TUIStore.unwatch(StoreName.FRIEND, {
|
||||||
friendList: onFriendListUpdated,
|
friendList: onFriendListUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const enterConversation = (item: any) => {
|
const enterConversation = (item: any) => {
|
||||||
emits('enterConversation', item);
|
emits('enterConversation', item)
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.friend-list {
|
.friend-list {
|
||||||
ul, li {
|
ul,
|
||||||
|
li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.friend-group-title {
|
.friend-group-title {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #666;
|
color: #666;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.friend-item {
|
.friend-item {
|
||||||
margin: 0 15px;
|
margin: 0 15px;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,7 +28,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tui-contact-list-item-header-right">
|
<div class="tui-contact-list-item-header-right">
|
||||||
<div>{{ TUITranslateService.t(`TUIContact.${contactListObj.title}`) }}</div>
|
<div>
|
||||||
|
{{
|
||||||
|
TUITranslateService.t(
|
||||||
|
`TUIContact.${contactListObj.title}`
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
<Icon
|
<Icon
|
||||||
:file="currentContactListKey === key ? downSVG : rightSVG"
|
:file="currentContactListKey === key ? downSVG : rightSVG"
|
||||||
size="20px"
|
size="20px"
|
||||||
@@ -41,7 +47,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<li
|
<li
|
||||||
v-for="contactListItem in contactListMap[currentContactListKey].list"
|
v-for="contactListItem in contactListMap[currentContactListKey]
|
||||||
|
.list"
|
||||||
:key="contactListItem.renderKey"
|
:key="contactListItem.renderKey"
|
||||||
class="tui-contact-list-item-main-item"
|
class="tui-contact-list-item-main-item"
|
||||||
:class="['selected']"
|
:class="['selected']"
|
||||||
@@ -55,19 +62,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul
|
<ul v-else-if="contactSearchingStatus" class="tui-contact-list">
|
||||||
v-else-if="contactSearchingStatus"
|
|
||||||
class="tui-contact-list"
|
|
||||||
>
|
|
||||||
<li
|
<li
|
||||||
v-for="(item, key) in contactSearchResult"
|
v-for="(item, key) in contactSearchResult"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="tui-contact-list-item"
|
class="tui-contact-list-item"
|
||||||
>
|
>
|
||||||
<div
|
<div v-if="item.list[0]" class="tui-contact-search-list">
|
||||||
v-if="item.list[0]"
|
|
||||||
class="tui-contact-search-list"
|
|
||||||
>
|
|
||||||
<div class="tui-contact-search-list-title">
|
<div class="tui-contact-search-list-title">
|
||||||
{{ TUITranslateService.t(`TUIContact.${item.label}`) }}
|
{{ TUITranslateService.t(`TUIContact.${item.label}`) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -89,209 +90,244 @@
|
|||||||
v-if="isContactSearchNoResult"
|
v-if="isContactSearchNoResult"
|
||||||
class="tui-contact-search-list-default"
|
class="tui-contact-search-list-default"
|
||||||
>
|
>
|
||||||
{{ TUITranslateService.t("TUIContact.无搜索结果") }}
|
{{ TUITranslateService.t('TUIContact.无搜索结果') }}
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
TUITranslateService,
|
TUITranslateService,
|
||||||
TUIStore,
|
TUIStore,
|
||||||
StoreName,
|
StoreName,
|
||||||
TUIFriendService,
|
TUIFriendService,
|
||||||
TUIUserService,
|
TUIUserService
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite';
|
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite'
|
||||||
import { ref, computed, onMounted, onUnmounted, provide } from '../../../adapter-vue';
|
import {
|
||||||
import { isPC } from '../../../utils/env';
|
ref,
|
||||||
import { deepCopy } from '../../TUIChat/utils/utils';
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
provide
|
||||||
|
} from '../../../adapter-vue'
|
||||||
|
import { isPC } from '../../../utils/env'
|
||||||
|
import { deepCopy } from '../../TUIChat/utils/utils'
|
||||||
|
|
||||||
import Icon from '../../common/Icon.vue';
|
import Icon from '../../common/Icon.vue'
|
||||||
import ContactListItem from './contact-list-item/index.vue';
|
import ContactListItem from './contact-list-item/index.vue'
|
||||||
import FriendList from './components/FriendList.vue';
|
import FriendList from './components/FriendList.vue'
|
||||||
|
|
||||||
import downSVG from '../../../assets/icon/down-icon.svg';
|
import downSVG from '../../../assets/icon/down-icon.svg'
|
||||||
import rightSVG from '../../../assets/icon/right-icon.svg';
|
import rightSVG from '../../../assets/icon/right-icon.svg'
|
||||||
import newContactsSVG from '../../../assets/icon/new-contacts.svg';
|
import newContactsSVG from '../../../assets/icon/new-contacts.svg'
|
||||||
import groupSVG from '../../../assets/icon/groups.svg';
|
import groupSVG from '../../../assets/icon/groups.svg'
|
||||||
import blackListSVG from '../../../assets/icon/black-list.svg';
|
import blackListSVG from '../../../assets/icon/black-list.svg'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
IContactList,
|
IContactList,
|
||||||
IContactSearchResult,
|
IContactSearchResult,
|
||||||
IBlackListUserItem,
|
IBlackListUserItem,
|
||||||
IUserStatus,
|
IUserStatus,
|
||||||
IUserStatusMap,
|
IUserStatusMap,
|
||||||
IContactInfoType,
|
IContactInfoType
|
||||||
} from '../../../interface';
|
} from '../../../interface'
|
||||||
import type {
|
import type {
|
||||||
IGroupModel,
|
IGroupModel,
|
||||||
Friend,
|
Friend,
|
||||||
FriendApplication } from '@tencentcloud/chat-uikit-engine-lite';
|
FriendApplication
|
||||||
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
|
|
||||||
const currentContactListKey = ref<keyof IContactList>('');
|
const currentContactListKey = ref<keyof IContactList>('')
|
||||||
const currentContactInfo = ref<IContactInfoType>({} as IContactInfoType);
|
const currentContactInfo = ref<IContactInfoType>({} as IContactInfoType)
|
||||||
const contactListMap = ref<IContactList>({
|
const contactListMap = ref<IContactList>({
|
||||||
friendApplicationList: {
|
friendApplicationList: {
|
||||||
icon: newContactsSVG,
|
icon: newContactsSVG,
|
||||||
key: 'friendApplicationList',
|
key: 'friendApplicationList',
|
||||||
title: '新的联系人',
|
title: '新的联系人',
|
||||||
list: [] as FriendApplication[],
|
list: [] as FriendApplication[],
|
||||||
unreadCount: 0,
|
unreadCount: 0
|
||||||
},
|
},
|
||||||
groupList: {
|
groupList: {
|
||||||
icon: groupSVG,
|
icon: groupSVG,
|
||||||
key: 'groupList',
|
key: 'groupList',
|
||||||
title: '我的群聊',
|
title: '我的群聊',
|
||||||
list: [] as IGroupModel[],
|
list: [] as IGroupModel[]
|
||||||
},
|
},
|
||||||
blackList: {
|
blackList: {
|
||||||
icon: blackListSVG,
|
icon: blackListSVG,
|
||||||
key: 'blackList',
|
key: 'blackList',
|
||||||
title: '黑名单',
|
title: '黑名单',
|
||||||
list: [] as IBlackListUserItem[],
|
list: [] as IBlackListUserItem[]
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const contactSearchingStatus = ref<boolean>(false);
|
const contactSearchingStatus = ref<boolean>(false)
|
||||||
const contactSearchResult = ref<IContactSearchResult>();
|
const contactSearchResult = ref<IContactSearchResult>()
|
||||||
const displayOnlineStatus = ref<boolean>(false);
|
const displayOnlineStatus = ref<boolean>(false)
|
||||||
const userOnlineStatusMap = ref<IUserStatusMap>();
|
const userOnlineStatusMap = ref<IUserStatusMap>()
|
||||||
|
|
||||||
const isContactSearchNoResult = computed((): boolean => (
|
const isContactSearchNoResult = computed(
|
||||||
!contactSearchResult?.value?.user?.list[0]
|
(): boolean =>
|
||||||
&& !contactSearchResult?.value?.group?.list[0]
|
!contactSearchResult?.value?.user?.list[0] &&
|
||||||
));
|
!contactSearchResult?.value?.group?.list[0]
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.APP, {
|
TUIStore.watch(StoreName.APP, {
|
||||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
|
enabledCustomerServicePlugin:
|
||||||
});
|
onCustomerServiceCommercialPluginUpdated
|
||||||
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.GRP, {
|
TUIStore.watch(StoreName.GRP, {
|
||||||
groupList: onGroupListUpdated,
|
groupList: onGroupListUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.USER, {
|
TUIStore.watch(StoreName.USER, {
|
||||||
userBlacklist: onUserBlacklistUpdated,
|
userBlacklist: onUserBlacklistUpdated,
|
||||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||||
userStatusList: onUserStatusListUpdated,
|
userStatusList: onUserStatusListUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.FRIEND, {
|
TUIStore.watch(StoreName.FRIEND, {
|
||||||
friendApplicationList: onFriendApplicationListUpdated,
|
friendApplicationList: onFriendApplicationListUpdated,
|
||||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
|
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.CUSTOM, {
|
TUIStore.watch(StoreName.CUSTOM, {
|
||||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
currentContactSearchingStatus:
|
||||||
|
onCurrentContactSearchingStatusUpdated,
|
||||||
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
||||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||||
currentContactInfo: onCurrentContactInfoUpdated,
|
currentContactInfo: onCurrentContactInfoUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.APP, {
|
TUIStore.unwatch(StoreName.APP, {
|
||||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
|
enabledCustomerServicePlugin:
|
||||||
});
|
onCustomerServiceCommercialPluginUpdated
|
||||||
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.GRP, {
|
TUIStore.unwatch(StoreName.GRP, {
|
||||||
groupList: onGroupListUpdated,
|
groupList: onGroupListUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.USER, {
|
TUIStore.unwatch(StoreName.USER, {
|
||||||
userBlacklist: onUserBlacklistUpdated,
|
userBlacklist: onUserBlacklistUpdated,
|
||||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||||
userStatusList: onUserStatusListUpdated,
|
userStatusList: onUserStatusListUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.FRIEND, {
|
TUIStore.unwatch(StoreName.FRIEND, {
|
||||||
friendApplicationList: onFriendApplicationListUpdated,
|
friendApplicationList: onFriendApplicationListUpdated,
|
||||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
|
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
currentContactSearchingStatus:
|
||||||
|
onCurrentContactSearchingStatusUpdated,
|
||||||
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
||||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||||
currentContactInfo: onCurrentContactInfoUpdated,
|
currentContactInfo: onCurrentContactInfoUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
function toggleCurrentContactList(key: keyof IContactList) {
|
function toggleCurrentContactList(key: keyof IContactList) {
|
||||||
if (currentContactListKey.value === key) {
|
if (currentContactListKey.value === key) {
|
||||||
currentContactListKey.value = '';
|
currentContactListKey.value = ''
|
||||||
currentContactInfo.value = {} as IContactInfoType;
|
currentContactInfo.value = {} as IContactInfoType
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '');
|
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '')
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {} as IContactInfoType);
|
TUIStore.update(
|
||||||
|
StoreName.CUSTOM,
|
||||||
|
'currentContactInfo',
|
||||||
|
{} as IContactInfoType
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
currentContactListKey.value = key;
|
currentContactListKey.value = key
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', key);
|
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', key)
|
||||||
if (key === 'friendApplicationList') {
|
|
||||||
TUIFriendService.setFriendApplicationRead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectItem(item: any) {
|
console.log(111)
|
||||||
currentContactInfo.value = item;
|
if (key === 'friendApplicationList') {
|
||||||
|
TUIFriendService.setFriendApplicationRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectItem(item: any) {
|
||||||
|
currentContactInfo.value = item
|
||||||
// For a result in the search list, before viewing the contactInfo details,
|
// For a result in the search list, before viewing the contactInfo details,
|
||||||
// it is necessary to update the data for the "already in the group list/already in the friend list" situation to obtain more detailed information
|
// it is necessary to update the data for the "already in the group list/already in the friend list" situation to obtain more detailed information
|
||||||
if (contactSearchingStatus.value) {
|
if (contactSearchingStatus.value) {
|
||||||
let targetListItem;
|
let targetListItem
|
||||||
if ((currentContactInfo.value as Friend)?.userID) {
|
if ((currentContactInfo.value as Friend)?.userID) {
|
||||||
targetListItem = contactListMap.value?.friendList?.list?.find(
|
targetListItem = contactListMap.value?.friendList?.list?.find(
|
||||||
(item: IContactInfoType) => (item as Friend)?.userID === (currentContactInfo.value as Friend)?.userID,
|
(item: IContactInfoType) =>
|
||||||
);
|
(item as Friend)?.userID ===
|
||||||
|
(currentContactInfo.value as Friend)?.userID
|
||||||
|
)
|
||||||
} else if ((currentContactInfo.value as IGroupModel)?.groupID) {
|
} else if ((currentContactInfo.value as IGroupModel)?.groupID) {
|
||||||
targetListItem = contactListMap.value?.groupList?.list?.find(
|
targetListItem = contactListMap.value?.groupList?.list?.find(
|
||||||
(item: IContactInfoType) => (item as IGroupModel)?.groupID === (currentContactInfo.value as IGroupModel)?.groupID,
|
(item: IContactInfoType) =>
|
||||||
);
|
(item as IGroupModel)?.groupID ===
|
||||||
|
(currentContactInfo.value as IGroupModel)?.groupID
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (targetListItem) {
|
if (targetListItem) {
|
||||||
currentContactInfo.value = targetListItem;
|
currentContactInfo.value = targetListItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', currentContactInfo.value);
|
TUIStore.update(
|
||||||
}
|
StoreName.CUSTOM,
|
||||||
|
'currentContactInfo',
|
||||||
|
currentContactInfo.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const selectFriend = (item: any) => {
|
const selectFriend = (item: any) => {
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', 'friendList');
|
TUIStore.update(
|
||||||
selectItem(item);
|
StoreName.CUSTOM,
|
||||||
};
|
'currentContactListKey',
|
||||||
|
'friendList'
|
||||||
|
)
|
||||||
|
selectItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
function onDisplayOnlineStatusUpdated(status: boolean) {
|
function onDisplayOnlineStatusUpdated(status: boolean) {
|
||||||
displayOnlineStatus.value = status;
|
displayOnlineStatus.value = status
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
|
function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
|
||||||
if (list?.size > 0) {
|
if (list?.size > 0) {
|
||||||
userOnlineStatusMap.value = Object.fromEntries(list?.entries());
|
userOnlineStatusMap.value = Object.fromEntries(list?.entries())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onCustomerServiceCommercialPluginUpdated(isEnabled: boolean) {
|
function onCustomerServiceCommercialPluginUpdated(isEnabled: boolean) {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the customer purchases the customer service plug-in,
|
// After the customer purchases the customer service plug-in,
|
||||||
// the engine updates the enabledCustomerServicePlugin to true through the commercial capability bit.
|
// the engine updates the enabledCustomerServicePlugin to true through the commercial capability bit.
|
||||||
const contactListExtensionID = TUIConstants.TUIContact.EXTENSION.CONTACT_LIST.EXT_ID;
|
const contactListExtensionID =
|
||||||
const tuiContactExtensionList = TUICore.getExtensionList(contactListExtensionID);
|
TUIConstants.TUIContact.EXTENSION.CONTACT_LIST.EXT_ID
|
||||||
|
const tuiContactExtensionList = TUICore.getExtensionList(
|
||||||
|
contactListExtensionID
|
||||||
|
)
|
||||||
|
|
||||||
const customerData = tuiContactExtensionList.find((extension: any) => {
|
const customerData = tuiContactExtensionList.find(
|
||||||
const { name, accountList = [] } = extension.data || {};
|
(extension: any) => {
|
||||||
return name === 'customer' && accountList.length > 0;
|
const { name, accountList = [] } = extension.data || {}
|
||||||
});
|
return name === 'customer' && accountList.length > 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (customerData) {
|
if (customerData) {
|
||||||
const { data, text } = customerData;
|
const { data, text } = customerData
|
||||||
const { accountList } = (data || {}) as { accountList: string[] };
|
const { accountList } = (data || {}) as { accountList: string[] }
|
||||||
|
|
||||||
TUIUserService.getUserProfile({ userIDList: accountList })
|
TUIUserService.getUserProfile({ userIDList: accountList })
|
||||||
.then((res) => {
|
.then(res => {
|
||||||
if (res.data.length > 0) {
|
if (res.data.length > 0) {
|
||||||
const customerList = {
|
const customerList = {
|
||||||
title: text,
|
title: text,
|
||||||
@@ -299,78 +335,124 @@ function onCustomerServiceCommercialPluginUpdated(isEnabled: boolean) {
|
|||||||
...item,
|
...item,
|
||||||
renderKey: generateRenderKey('customerList', item, index),
|
renderKey: generateRenderKey('customerList', item, index),
|
||||||
infoKeyList: [],
|
infoKeyList: [],
|
||||||
btnKeyList: ['enterC2CConversation'],
|
btnKeyList: ['enterC2CConversation']
|
||||||
})),
|
})),
|
||||||
key: 'customerList',
|
key: 'customerList'
|
||||||
};
|
}
|
||||||
contactListMap.value = { ...contactListMap.value, customerList };
|
contactListMap.value = {
|
||||||
|
...contactListMap.value,
|
||||||
|
customerList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => { });
|
.catch(() => {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onGroupListUpdated(groupList: IGroupModel[]) {
|
function onGroupListUpdated(groupList: IGroupModel[]) {
|
||||||
updateContactListMap('groupList', groupList);
|
updateContactListMap('groupList', groupList)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
|
function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
|
||||||
updateContactListMap('blackList', userBlacklist);
|
updateContactListMap('blackList', userBlacklist)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFriendApplicationUnreadCountUpdated(friendApplicationUnreadCount: number) {
|
function onFriendApplicationUnreadCountUpdated(
|
||||||
contactListMap.value.friendApplicationList.unreadCount = friendApplicationUnreadCount;
|
friendApplicationUnreadCount: number
|
||||||
}
|
|
||||||
|
|
||||||
function onFriendApplicationListUpdated(friendApplicationList: FriendApplication[]) {
|
|
||||||
updateContactListMap('friendApplicationList', friendApplicationList);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateContactListMap(key: keyof IContactList, list: IContactInfoType[]) {
|
|
||||||
contactListMap.value[key].list = list;
|
|
||||||
contactListMap.value[key].list.map((item: IContactInfoType, index: number) => item.renderKey = generateRenderKey(key, item, index));
|
|
||||||
updateCurrentContactInfoFromList(contactListMap.value[key].list, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCurrentContactInfoFromList(list: IContactInfoType[], type: keyof IContactList) {
|
|
||||||
if (
|
|
||||||
!(currentContactInfo.value as Friend)?.userID
|
|
||||||
&& !(currentContactInfo.value as IGroupModel)?.groupID
|
|
||||||
) {
|
) {
|
||||||
return;
|
contactListMap.value.friendApplicationList.unreadCount =
|
||||||
|
friendApplicationUnreadCount
|
||||||
}
|
}
|
||||||
if (type === currentContactListKey.value || contactSearchingStatus.value) {
|
|
||||||
currentContactInfo.value = list?.find(
|
function onFriendApplicationListUpdated(
|
||||||
|
friendApplicationList: FriendApplication[]
|
||||||
|
) {
|
||||||
|
updateContactListMap('friendApplicationList', friendApplicationList)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateContactListMap(
|
||||||
|
key: keyof IContactList,
|
||||||
|
list: IContactInfoType[]
|
||||||
|
) {
|
||||||
|
contactListMap.value[key].list = list
|
||||||
|
contactListMap.value[key].list.map(
|
||||||
|
(item: IContactInfoType, index: number) =>
|
||||||
|
(item.renderKey = generateRenderKey(key, item, index))
|
||||||
|
)
|
||||||
|
updateCurrentContactInfoFromList(contactListMap.value[key].list, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCurrentContactInfoFromList(
|
||||||
|
list: IContactInfoType[],
|
||||||
|
type: keyof IContactList
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!(currentContactInfo.value as Friend)?.userID &&
|
||||||
|
!(currentContactInfo.value as IGroupModel)?.groupID
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
type === currentContactListKey.value ||
|
||||||
|
contactSearchingStatus.value
|
||||||
|
) {
|
||||||
|
currentContactInfo.value =
|
||||||
|
list?.find(
|
||||||
(item: any) =>
|
(item: any) =>
|
||||||
(item?.groupID && item?.groupID === (currentContactInfo.value as IGroupModel)?.groupID) || (item?.userID && item?.userID === (currentContactInfo.value as Friend)?.userID),
|
(item?.groupID &&
|
||||||
) || {} as IContactInfoType;
|
item?.groupID ===
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', currentContactInfo.value);
|
(currentContactInfo.value as IGroupModel)?.groupID) ||
|
||||||
|
(item?.userID &&
|
||||||
|
item?.userID ===
|
||||||
|
(currentContactInfo.value as Friend)?.userID)
|
||||||
|
) || ({} as IContactInfoType)
|
||||||
|
TUIStore.update(
|
||||||
|
StoreName.CUSTOM,
|
||||||
|
'currentContactInfo',
|
||||||
|
currentContactInfo.value
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function generateRenderKey(contactListMapKey: keyof IContactList, contactInfo: IContactInfoType, index: number) {
|
function generateRenderKey(
|
||||||
return `${contactListMapKey}-${(contactInfo as Friend).userID || (contactInfo as IGroupModel).groupID || (`index${index}`)}`;
|
contactListMapKey: keyof IContactList,
|
||||||
}
|
contactInfo: IContactInfoType,
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
return `${contactListMapKey}-${
|
||||||
|
(contactInfo as Friend).userID ||
|
||||||
|
(contactInfo as IGroupModel).groupID ||
|
||||||
|
`index${index}`
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
function onCurrentContactSearchResultUpdated(searchResult: IContactSearchResult) {
|
function onCurrentContactSearchResultUpdated(
|
||||||
contactSearchResult.value = searchResult;
|
searchResult: IContactSearchResult
|
||||||
}
|
) {
|
||||||
|
contactSearchResult.value = searchResult
|
||||||
|
}
|
||||||
|
|
||||||
function onCurrentContactSearchingStatusUpdated(searchingStatus: boolean) {
|
function onCurrentContactSearchingStatusUpdated(
|
||||||
contactSearchingStatus.value = searchingStatus;
|
searchingStatus: boolean
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {} as IContactInfoType);
|
) {
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '');
|
contactSearchingStatus.value = searchingStatus
|
||||||
}
|
TUIStore.update(
|
||||||
|
StoreName.CUSTOM,
|
||||||
|
'currentContactInfo',
|
||||||
|
{} as IContactInfoType
|
||||||
|
)
|
||||||
|
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '')
|
||||||
|
}
|
||||||
|
|
||||||
function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
||||||
currentContactInfo.value = contactInfo;
|
currentContactInfo.value = contactInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCurrentContactListKeyUpdated(contactListKey: string) {
|
function onCurrentContactListKeyUpdated(contactListKey: string) {
|
||||||
currentContactListKey.value = contactListKey;
|
currentContactListKey.value = contactListKey
|
||||||
}
|
}
|
||||||
|
|
||||||
provide('userOnlineStatusMap', userOnlineStatusMap);
|
provide('userOnlineStatusMap', userOnlineStatusMap)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||||
|
|||||||
@@ -4,19 +4,24 @@
|
|||||||
v-else-if="isShowContactList"
|
v-else-if="isShowContactList"
|
||||||
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
||||||
>
|
>
|
||||||
<Navigation :title="currentContactKey ? contactInfoTitle : TUITranslateService.t('TUIChat.腾讯云 IM')">
|
<Navigation
|
||||||
|
:title="
|
||||||
|
currentContactKey
|
||||||
|
? contactInfoTitle
|
||||||
|
: TUITranslateService.t('TUIChat.腾讯云 IM')
|
||||||
|
"
|
||||||
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<div v-show="currentContactKey" @click="resetContactType">
|
<div v-show="currentContactKey" @click="resetContactType">
|
||||||
<Icon
|
<Icon :file="backSVG" />
|
||||||
:file="backSVG"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<div v-show="!isShowContactSearch && !currentContactKey" @click="openContactSearch">
|
<div
|
||||||
<Icon
|
v-show="!isShowContactSearch && !currentContactKey"
|
||||||
:file="addCircle"
|
@click="openContactSearch"
|
||||||
/>
|
>
|
||||||
|
<Icon :file="addCircle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
@@ -28,131 +33,162 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
:class="['tui-contact-left', !isPC && 'tui-contact-h5-left']">
|
:class="['tui-contact-left', !isPC && 'tui-contact-h5-left']"
|
||||||
<ContactSearch
|
>
|
||||||
v-if="isShowContactSearch"
|
<ContactSearch v-if="isShowContactSearch" />
|
||||||
|
<ContactList
|
||||||
|
:class="[
|
||||||
|
'tui-contact-left-list',
|
||||||
|
!isPC && 'tui-contact-h5-left-list'
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
<ContactList :class="['tui-contact-left-list', !isPC && 'tui-contact-h5-left-list']" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TUIStore, StoreName, TUITranslateService } from '@tencentcloud/chat-uikit-engine-lite';
|
import {
|
||||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
TUIStore,
|
||||||
import { onMounted, onUnmounted, ref, watchEffect } from '../../adapter-vue';
|
StoreName,
|
||||||
import { isPC, isUniFrameWork } from '../../utils/env';
|
TUITranslateService
|
||||||
import Navigation from '../common/Navigation/index.vue';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import Icon from '../common/Icon.vue';
|
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
ref,
|
||||||
|
watchEffect
|
||||||
|
} from '../../adapter-vue'
|
||||||
|
import { isPC, isUniFrameWork } from '../../utils/env'
|
||||||
|
import Navigation from '../common/Navigation/index.vue'
|
||||||
|
import Icon from '../common/Icon.vue'
|
||||||
|
|
||||||
import SelectFriend from './select-friend/index.vue';
|
import SelectFriend from './select-friend/index.vue'
|
||||||
import ContactSearch from './contact-search/index.vue';
|
import ContactSearch from './contact-search/index.vue'
|
||||||
import ContactList from './contact-list/index.vue';
|
import ContactList from './contact-list/index.vue'
|
||||||
import ContactInfo from './contact-info/index.vue';
|
import ContactInfo from './contact-info/index.vue'
|
||||||
|
|
||||||
import addCircle from '../../assets/icon/add-circle.svg';
|
import addCircle from '../../assets/icon/add-circle.svg'
|
||||||
import backSVG from '../../assets/icon/back.svg';
|
import backSVG from '../../assets/icon/back.svg'
|
||||||
import { CONTACT_INFO_TITLE } from '../../constant';
|
import { CONTACT_INFO_TITLE } from '../../constant'
|
||||||
|
|
||||||
const emits = defineEmits(['switchConversation']);
|
const emits = defineEmits(['switchConversation'])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// web/h5 single page application display format, uniapp please ignore
|
// web/h5 single page application display format, uniapp please ignore
|
||||||
displayType: {
|
displayType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'contactList', // "contactList" / "selectFriend"
|
default: 'contactList', // "contactList" / "selectFriend"
|
||||||
require: false,
|
require: false
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const displayTypeRef = ref<string>(props.displayType || 'contactList');
|
const displayTypeRef = ref<string>(props.displayType || 'contactList')
|
||||||
const isShowSelectFriend = ref(false);
|
const isShowSelectFriend = ref(false)
|
||||||
const isShowContactList = ref(true);
|
const isShowContactList = ref(true)
|
||||||
const isShowContactInfo = ref(true);
|
const isShowContactInfo = ref(true)
|
||||||
const isShowContactSearch = ref(false);
|
const isShowContactSearch = ref(false)
|
||||||
const currentContactKey = ref('');
|
const currentContactKey = ref('')
|
||||||
const contactInfoTitle = ref<string>('');
|
const contactInfoTitle = ref<string>('')
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
isShowContactList.value = props?.displayType !== 'selectFriend';
|
isShowContactList.value = props?.displayType !== 'selectFriend'
|
||||||
});
|
})
|
||||||
|
|
||||||
const switchConversation = (data: any) => {
|
const switchConversation = (data: any) => {
|
||||||
isUniFrameWork
|
isUniFrameWork &&
|
||||||
&& TUIGlobal?.navigateTo({
|
TUIGlobal?.navigateTo({
|
||||||
url: '/TUIKit/components/TUIChat/index',
|
url: '/TUIKit/components/TUIChat/index'
|
||||||
});
|
})
|
||||||
emits('switchConversation', data);
|
emits('switchConversation', data)
|
||||||
};
|
}
|
||||||
|
|
||||||
const openContactSearch = () => {
|
const openContactSearch = () => {
|
||||||
TUIStore.update(
|
TUIStore.update(
|
||||||
StoreName.CUSTOM,
|
StoreName.CUSTOM,
|
||||||
'currentContactSearchingStatus',
|
'currentContactSearchingStatus',
|
||||||
true,
|
true
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const resetContactType = () => {
|
const resetContactType = () => {
|
||||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '');
|
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', '')
|
||||||
};
|
}
|
||||||
|
|
||||||
const onCurrentContactSearchingStatusUpdated = (data: boolean) => {
|
const onCurrentContactSearchingStatusUpdated = (data: boolean) => {
|
||||||
isShowContactSearch.value = data;
|
isShowContactSearch.value = data
|
||||||
};
|
}
|
||||||
|
|
||||||
const onIsShowSelectFriendComponentUpdated = (data: any) => {
|
const onIsShowSelectFriendComponentUpdated = (data: any) => {
|
||||||
if (!isUniFrameWork && props?.displayType === 'selectFriend') {
|
if (!isUniFrameWork && props?.displayType === 'selectFriend') {
|
||||||
isShowSelectFriend.value = data;
|
isShowSelectFriend.value = data
|
||||||
isShowContactList.value = false;
|
isShowContactList.value = false
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
isShowSelectFriend.value = true;
|
isShowSelectFriend.value = true
|
||||||
if (isUniFrameWork) {
|
if (isUniFrameWork) {
|
||||||
displayTypeRef.value = 'selectFriend';
|
displayTypeRef.value = 'selectFriend'
|
||||||
TUIGlobal?.hideTabBar();
|
TUIGlobal?.hideTabBar()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isShowSelectFriend.value = false;
|
isShowSelectFriend.value = false
|
||||||
if (isUniFrameWork) {
|
if (isUniFrameWork) {
|
||||||
displayTypeRef.value = props.displayType;
|
displayTypeRef.value = props.displayType
|
||||||
TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
|
TUIGlobal?.showTabBar()?.catch(() => {
|
||||||
|
/* ignore */
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const onCurrentContactInfoUpdated = (contactInfo: any) => {
|
const onCurrentContactInfoUpdated = (contactInfo: any) => {
|
||||||
isShowContactInfo.value = isPC || (contactInfo && typeof contactInfo === 'object' && Object.keys(contactInfo)?.length > 0);
|
isShowContactInfo.value =
|
||||||
};
|
isPC ||
|
||||||
|
(contactInfo &&
|
||||||
|
typeof contactInfo === 'object' &&
|
||||||
|
Object.keys(contactInfo)?.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
const onCurrentContactListKeyUpdated = (key: string) => {
|
const onCurrentContactListKeyUpdated = (key: string) => {
|
||||||
currentContactKey.value = key;
|
currentContactKey.value = key
|
||||||
contactInfoTitle.value = TUITranslateService.t(`TUIContact.${CONTACT_INFO_TITLE[key]}`);
|
contactInfoTitle.value = TUITranslateService.t(
|
||||||
};
|
`TUIContact.${CONTACT_INFO_TITLE[key]}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.CUSTOM, {
|
TUIStore.watch(StoreName.CUSTOM, {
|
||||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
currentContactSearchingStatus:
|
||||||
|
onCurrentContactSearchingStatusUpdated,
|
||||||
isShowSelectFriendComponent: onIsShowSelectFriendComponentUpdated,
|
isShowSelectFriendComponent: onIsShowSelectFriendComponentUpdated,
|
||||||
currentContactInfo: onCurrentContactInfoUpdated,
|
currentContactInfo: onCurrentContactInfoUpdated,
|
||||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
currentContactListKey: onCurrentContactListKeyUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
currentContactSearchingStatus:
|
||||||
|
onCurrentContactSearchingStatusUpdated,
|
||||||
isShowSelectFriendComponent: onIsShowSelectFriendComponentUpdated,
|
isShowSelectFriendComponent: onIsShowSelectFriendComponentUpdated,
|
||||||
currentContactInfo: onCurrentContactInfoUpdated,
|
currentContactInfo: onCurrentContactInfoUpdated,
|
||||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
currentContactListKey: onCurrentContactListKeyUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../assets/styles/common";
|
@import '../../assets/styles/common';
|
||||||
|
|
||||||
.tui-contact {
|
uni-page-body,
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
page {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tui-contact {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -174,9 +210,9 @@ onUnmounted(() => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tui-contact-h5 {
|
.tui-contact-h5 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-left,
|
&-left,
|
||||||
@@ -196,5 +232,5 @@ onUnmounted(() => {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -18,174 +18,195 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine-lite';
|
import {
|
||||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
TUIStore,
|
||||||
import { ref } from '../../adapter-vue';
|
StoreName
|
||||||
import TUISearch from '../TUISearch/index.vue';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import ConversationList from './conversation-list/index.vue';
|
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||||
import ConversationHeader from './conversation-header/index.vue';
|
import { ref } from '../../adapter-vue'
|
||||||
import ConversationNetwork from './conversation-network/index.vue';
|
import TUISearch from '../TUISearch/index.vue'
|
||||||
import { onHide } from '@dcloudio/uni-app';
|
import ConversationList from './conversation-list/index.vue'
|
||||||
// #ifdef MP-WEIXIN
|
import ConversationHeader from './conversation-header/index.vue'
|
||||||
// uniapp packaged mini-programs are integrated by default, and the default initialization entry file is imported here
|
import ConversationNetwork from './conversation-network/index.vue'
|
||||||
// TUIChatKit init needs to be encapsulated because uni vue2 will report an error when compiling H5 directly through conditional compilation
|
import { onHide } from '@dcloudio/uni-app'
|
||||||
import './entry.ts';
|
// #ifdef MP-WEIXIN
|
||||||
// #endif
|
// uniapp packaged mini-programs are integrated by default, and the default initialization entry file is imported here
|
||||||
|
// TUIChatKit init needs to be encapsulated because uni vue2 will report an error when compiling H5 directly through conditional compilation
|
||||||
|
import './entry.ts'
|
||||||
|
// #endif
|
||||||
|
|
||||||
const emits = defineEmits(['handleSwitchConversation']);
|
const emits = defineEmits(['handleSwitchConversation'])
|
||||||
|
|
||||||
const totalUnreadCount = ref(0);
|
const totalUnreadCount = ref(0)
|
||||||
const headerRef = ref<typeof ConversationHeader>();
|
const headerRef = ref<typeof ConversationHeader>()
|
||||||
const conversationListDomRef = ref<typeof ConversationList>();
|
const conversationListDomRef = ref<typeof ConversationList>()
|
||||||
const touchX = ref<number>(0);
|
const touchX = ref<number>(0)
|
||||||
const touchY = ref<number>(0);
|
const touchY = ref<number>(0)
|
||||||
const isShowConversationHeader = ref<boolean>(true);
|
const isShowConversationHeader = ref<boolean>(true)
|
||||||
|
|
||||||
const getTabBarConfig = () => {
|
const getTabBarConfig = () => {
|
||||||
try {
|
try {
|
||||||
// 方法1: 从 getApp() 全局数据中获取
|
// 方法1: 从 getApp() 全局数据中获取
|
||||||
const app = getApp();
|
const app = getApp()
|
||||||
if (app?.globalData?.tabBar) {
|
if (app?.globalData?.tabBar) {
|
||||||
return app.globalData.tabBar;
|
return app.globalData.tabBar
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方法2: 从 pages.json 配置中读取(如果可访问)
|
// 方法2: 从 pages.json 配置中读取(如果可访问)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (typeof __uniConfig !== 'undefined' && __uniConfig.tabBar) {
|
if (typeof __uniConfig !== 'undefined' && __uniConfig.tabBar) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return __uniConfig.tabBar;
|
return __uniConfig.tabBar
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方法3: 尝试调用 uni.getTabBar() 检测是否存在 tabbar
|
// 方法3: 尝试调用 uni.getTabBar() 检测是否存在 tabbar
|
||||||
try {
|
try {
|
||||||
const tabBar = uni.getTabBar && uni.getTabBar();
|
const tabBar = uni.getTabBar && uni.getTabBar()
|
||||||
if (tabBar) {
|
if (tabBar) {
|
||||||
return { list: tabBar};
|
return { list: tabBar }
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const getTabBarIndex = () => {
|
const getTabBarIndex = () => {
|
||||||
try {
|
try {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (!pages || pages.length === 0) return
|
||||||
|
|
||||||
const pages = getCurrentPages();
|
const currentPage = pages[pages.length - 1]
|
||||||
if (!pages || pages.length === 0) return;
|
const currentRoute = currentPage.route
|
||||||
|
|
||||||
const currentPage = pages[pages.length - 1];
|
const isTUIConversationPage =
|
||||||
const currentRoute = currentPage.route;
|
currentRoute &&
|
||||||
|
(currentRoute.includes(
|
||||||
const isTUIConversationPage = currentRoute && (
|
'TUIKit/components/TUIConversation/index'
|
||||||
currentRoute.includes('TUIKit/components/TUIConversation/index') ||
|
) ||
|
||||||
currentRoute.includes('TUIConversation')
|
currentRoute.includes('TUIConversation'))
|
||||||
);
|
|
||||||
|
|
||||||
if (!isTUIConversationPage) {
|
if (!isTUIConversationPage) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabBarConfig = getTabBarConfig();
|
const tabBarConfig = getTabBarConfig()
|
||||||
|
|
||||||
if (!tabBarConfig) {
|
if (!tabBarConfig) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabBarIndex = -1;
|
let tabBarIndex = -1
|
||||||
|
|
||||||
if (tabBarConfig.list && Array.isArray(tabBarConfig.list)) {
|
if (tabBarConfig.list && Array.isArray(tabBarConfig.list)) {
|
||||||
tabBarIndex = tabBarConfig.list.findIndex((item: any) => {
|
tabBarIndex = tabBarConfig.list.findIndex((item: any) => {
|
||||||
const pagePath = item.pagePath || '';
|
const pagePath = item.pagePath || ''
|
||||||
return pagePath.includes('TUIConversation') ||
|
return (
|
||||||
pagePath.includes('TUIKit/components/TUIConversation/index');
|
pagePath.includes('TUIConversation') ||
|
||||||
});
|
pagePath.includes('TUIKit/components/TUIConversation/index')
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return tabBarIndex;
|
return tabBarIndex
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return -1;
|
return -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
TUIStore.watch(StoreName.CONV, {
|
TUIStore.watch(StoreName.CONV, {
|
||||||
totalUnreadCount: (count: number) => {
|
totalUnreadCount: (count: number) => {
|
||||||
totalUnreadCount.value = count;
|
totalUnreadCount.value = count
|
||||||
const tabBarIndex = getTabBarIndex() ?? -1;
|
const tabBarIndex = getTabBarIndex() ?? -1
|
||||||
if (tabBarIndex >= 0) {
|
if (tabBarIndex >= 0) {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
uni.setTabBarBadge({
|
uni.setTabBarBadge({
|
||||||
index: tabBarIndex,
|
index: tabBarIndex,
|
||||||
text: count > 99 ? '99+' : count.toString(),
|
text: count > 99 ? '99+' : count.toString()
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
uni.removeTabBarBadge({
|
uni.removeTabBarBadge({
|
||||||
index: tabBarIndex,
|
index: tabBarIndex
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.CUSTOM, {
|
TUIStore.watch(StoreName.CUSTOM, {
|
||||||
isShowConversationHeader: (showStatus: boolean) => {
|
isShowConversationHeader: (showStatus: boolean) => {
|
||||||
isShowConversationHeader.value = showStatus !== false;
|
isShowConversationHeader.value = showStatus !== false
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const handleSwitchConversation = (conversationID: string) => {
|
const handleSwitchConversation = (conversationID: string) => {
|
||||||
TUIGlobal?.navigateTo({
|
TUIGlobal?.navigateTo({
|
||||||
url: '/TUIKit/components/TUIChat/index',
|
url: '/TUIKit/components/TUIChat/index'
|
||||||
});
|
})
|
||||||
emits('handleSwitchConversation', conversationID);
|
emits('handleSwitchConversation', conversationID)
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeChildren = () => {
|
const closeChildren = () => {
|
||||||
headerRef?.value?.closeChildren();
|
headerRef?.value?.closeChildren()
|
||||||
conversationListDomRef?.value?.closeChildren();
|
conversationListDomRef?.value?.closeChildren()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClickConv = () => {
|
const handleClickConv = () => {
|
||||||
closeChildren();
|
closeChildren()
|
||||||
};
|
}
|
||||||
|
|
||||||
onHide(closeChildren);
|
onHide(closeChildren)
|
||||||
|
|
||||||
const handleTouchStart = (e: any) => {
|
const handleTouchStart = (e: any) => {
|
||||||
touchX.value = e.changedTouches[0].clientX;
|
touchX.value = e.changedTouches[0].clientX
|
||||||
touchY.value = e.changedTouches[0].clientY;
|
touchY.value = e.changedTouches[0].clientY
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleTouchEnd = (e: any) => {
|
const handleTouchEnd = (e: any) => {
|
||||||
const x = e.changedTouches[0].clientX;
|
const x = e.changedTouches[0].clientX
|
||||||
const y = e.changedTouches[0].clientY;
|
const y = e.changedTouches[0].clientY
|
||||||
let turn = '';
|
let turn = ''
|
||||||
if (x - touchX.value > 50 && Math.abs(y - touchY.value) < 50) {
|
if (x - touchX.value > 50 && Math.abs(y - touchY.value) < 50) {
|
||||||
// Swipe right
|
// Swipe right
|
||||||
turn = 'right';
|
turn = 'right'
|
||||||
} else if (x - touchX.value < -50 && Math.abs(y - touchY.value) < 50) {
|
} else if (
|
||||||
|
x - touchX.value < -50 &&
|
||||||
|
Math.abs(y - touchY.value) < 50
|
||||||
|
) {
|
||||||
// Swipe left
|
// Swipe left
|
||||||
turn = 'left';
|
turn = 'left'
|
||||||
}
|
}
|
||||||
if (y - touchY.value > 50 && Math.abs(x - touchX.value) < 50) {
|
if (y - touchY.value > 50 && Math.abs(x - touchX.value) < 50) {
|
||||||
// Swipe down
|
// Swipe down
|
||||||
turn = 'down';
|
turn = 'down'
|
||||||
} else if (y - touchY.value < -50 && Math.abs(x - touchX.value) < 50) {
|
} else if (
|
||||||
|
y - touchY.value < -50 &&
|
||||||
|
Math.abs(x - touchX.value) < 50
|
||||||
|
) {
|
||||||
// Swipe up
|
// Swipe up
|
||||||
turn = 'up';
|
turn = 'up'
|
||||||
}
|
}
|
||||||
// Operate according to the direction
|
// Operate according to the direction
|
||||||
if (turn === 'down' || turn === 'up') {
|
if (turn === 'down' || turn === 'up') {
|
||||||
closeChildren();
|
closeChildren()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const getPassingRef = (ref) => {
|
const getPassingRef = ref => {
|
||||||
ref.value = conversationListDomRef.value;
|
ref.value = conversationListDomRef.value
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
<style lang="scss" scoped src="./style/index.scss">
|
||||||
|
uni-page-body,
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
page {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
10
api/index.js
10
api/index.js
@@ -34,6 +34,15 @@ export const getUserData = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 修改用户信息 */
|
||||||
|
export const updateUserData = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/user/edit',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取用户地址列表 */
|
/** 获取用户地址列表 */
|
||||||
export const getUserAddress = (data, loading = true) => {
|
export const getUserAddress = (data, loading = true) => {
|
||||||
return http({
|
return http({
|
||||||
@@ -128,3 +137,4 @@ export const getTencentUserSig = () => {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,3 +50,94 @@ export const getUserPayPwd = () => {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加/修改支付密码
|
||||||
|
* @param {*} data
|
||||||
|
* @param {*} method post 添加 put 修改
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const updateUserPayPwd = (data, method = 'post') => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userPassword',
|
||||||
|
method,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加/修改银行卡
|
||||||
|
* @param {*} data
|
||||||
|
* @param {*} method post 添加 put 修改
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const addUserPayPwd = (data, method = 'post') => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userCard',
|
||||||
|
method,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除银行卡 */
|
||||||
|
export const deleteUserPayPwd = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/userCard/${id}`,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取银行卡列表 */
|
||||||
|
export const getUserBankList = () => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userCard/list',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取银行卡详情 */
|
||||||
|
export const getUserBankDetail = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/userCard/details/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加/修改用户第三方支付账户
|
||||||
|
* @param {*} data 提交数据
|
||||||
|
* @param {*} method post 添加 put 修改
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const addUserThirdPay = (data, method = 'post') => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userPayment',
|
||||||
|
method,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取用户第三方支付详情 */
|
||||||
|
export const getUserThirdPay = id => {
|
||||||
|
return http({
|
||||||
|
url: `/api/service/userPayment/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取身份证信息 */
|
||||||
|
export const getUserIdCard = () => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userVerification/details',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加身份证 */
|
||||||
|
export const addUserIdCard = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/service/userVerification',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
default: () => []
|
default: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 图片回显列表 */
|
||||||
|
const fileLists = defineModel('list', {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
})
|
||||||
|
|
||||||
/** 是否为身份证状态 */
|
/** 是否为身份证状态 */
|
||||||
const isIdCard = computed(() => props.isFront || props.isBack)
|
const isIdCard = computed(() => props.isFront || props.isBack)
|
||||||
|
|
||||||
@@ -61,25 +67,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onSelect = async e => {
|
const onSelect = async e => {
|
||||||
e.tempFiles.forEach(v => {
|
if (props.limit === '1') {
|
||||||
uploadFile(v.path)
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
const upData = e.tempFiles.map(v => v.path)[0]
|
const upData = e.tempFiles.map(v => v.path)[0]
|
||||||
imageValue.value = await uploadSingleFile(upData, {
|
imageValue.value = await uploadSingleFile(upData, {
|
||||||
url: '/api/common/admin/upload/up/single'
|
url: '/api/common/admin/upload/up/single'
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
e.tempFiles.forEach(v => {
|
||||||
|
uploadFile(v.path)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDelete = e => {
|
const onDelete = e => {
|
||||||
|
if (props.limit === '1') {
|
||||||
|
imageValue.value = ''
|
||||||
|
} else {
|
||||||
imageValue.value.splice(e.index, 1)
|
imageValue.value.splice(e.index, 1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view :class="{ 'file_card-box': isIdCard }" class="cb-file-picker">
|
<view :class="{ 'file_card-box': isIdCard }" class="cb-file-picker">
|
||||||
<uni-file-picker
|
<uni-file-picker
|
||||||
|
v-model="fileLists"
|
||||||
:file-mediatype="props.type"
|
:file-mediatype="props.type"
|
||||||
:image-styles="imageStyles"
|
:image-styles="imageStyles"
|
||||||
:limit="props.limit"
|
:limit="props.limit"
|
||||||
|
|||||||
63
package-lock.json
generated
63
package-lock.json
generated
@@ -9,7 +9,11 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/chat-uikit-uniapp": "^3.2.0",
|
"@tencentcloud/chat-uikit-engine-lite": "1.0.3",
|
||||||
|
"@tencentcloud/chat-uikit-uniapp": "3.1.0",
|
||||||
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
||||||
|
"@tencentcloud/universal-api": "^2.4.0",
|
||||||
|
"dayjs": "^1.11.10",
|
||||||
"unplugin-vue2-script-setup": "^0.11.4"
|
"unplugin-vue2-script-setup": "^0.11.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -654,18 +658,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tencentcloud/chat-uikit-engine-lite": {
|
"node_modules/@tencentcloud/chat-uikit-engine-lite": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.3",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@tencentcloud/chat-uikit-engine-lite/-/chat-uikit-engine-lite-1.0.4.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@tencentcloud/chat-uikit-engine-lite/-/chat-uikit-engine-lite-1.0.3.tgz",
|
||||||
"integrity": "sha512-YfDuXDkUlz7V1RA6iAEO2PLBI/efz60TRwAVy/Vhlu7oqZOMlphra72J30N3n5WhxbsFfcfTShoqW3OwcatUQg==",
|
"integrity": "sha512-Zh539tplI+BY1uljuXVoOCzR1k/UuBhX2bx9fZw0yRDKf3KGWWAjnkDzl3erpgYpNGXX6qPd/PhPhLWbEB5ouw==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/lite-chat": "^1.6.3"
|
"@tencentcloud/lite-chat": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tencentcloud/chat-uikit-uniapp": {
|
"node_modules/@tencentcloud/chat-uikit-uniapp": {
|
||||||
"version": "3.2.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@tencentcloud/chat-uikit-uniapp/-/chat-uikit-uniapp-3.2.0.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@tencentcloud/chat-uikit-uniapp/-/chat-uikit-uniapp-3.1.0.tgz",
|
||||||
"integrity": "sha512-5qUVRcsndwhWLAidgFMTk/JAhnkl2HrHux9jFJrk5iXZzXPdv6Wn9G2MA2bZcMQrWirYuTd7sAH/umcDYZsk3Q==",
|
"integrity": "sha512-cKQBaFGscSlwBK0fjK7feMY675c3K35uoUbntkiFZo2o8UwW2w5Cmp/76HbaQ0DY/wxG/fMTbfU9RQCfVe3SjQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/chat-uikit-engine-lite": "~1.0.3",
|
"@tencentcloud/chat-uikit-engine-lite": "~1.0.3",
|
||||||
@@ -680,9 +684,9 @@
|
|||||||
"unplugin-vue2-script-setup": "^0.11.3"
|
"unplugin-vue2-script-setup": "^0.11.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@trtc/calls-uikit-vue": "~4.4.0",
|
"@trtc/calls-uikit-vue": "4.2.2",
|
||||||
"@trtc/calls-uikit-vue2.6": "~4.4.0",
|
"@trtc/calls-uikit-vue2.6": "4.2.2",
|
||||||
"@trtc/calls-uikit-wx": "~4.2.0"
|
"@trtc/calls-uikit-wx": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tencentcloud/lite-chat": {
|
"node_modules/@tencentcloud/lite-chat": {
|
||||||
@@ -735,13 +739,13 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/@trtc/call-engine-lite-js": {
|
"node_modules/@trtc/call-engine-lite-js": {
|
||||||
"version": "3.5.0",
|
"version": "3.4.8",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/call-engine-lite-js/-/call-engine-lite-js-3.5.0.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/call-engine-lite-js/-/call-engine-lite-js-3.4.8.tgz",
|
||||||
"integrity": "sha512-XSKDFBOcs6kITZnbLdECg889bbhTbdmHdg/EQRmGJx/2KakEe/uubfSDydOPBO47UgV2QZ9dGiRVAX5SCY2g6A==",
|
"integrity": "sha512-VU+2VMhuNUfJg3F8TZ992d6iB33Rjc/xhbez0OkzYyejpaOtU1mExdU25On4xjeAx7XIZ6n80vfU5uEBsPusyw==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/lite-chat": "^1.6.3",
|
"@tencentcloud/lite-chat": "^1.5.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"eventemitter3": "^4.0.7",
|
"eventemitter3": "^4.0.7",
|
||||||
"rtc-detect": "^0.0.5",
|
"rtc-detect": "^0.0.5",
|
||||||
@@ -762,41 +766,40 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@trtc/calls-uikit-vue": {
|
"node_modules/@trtc/calls-uikit-vue": {
|
||||||
"version": "4.4.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-vue/-/calls-uikit-vue-4.4.2.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-vue/-/calls-uikit-vue-4.2.2.tgz",
|
||||||
"integrity": "sha512-a5oaNSO4qN0FSsDb65Kl9BEghDdWV0l5RdjMgaWEjrqNVVCuz7p5s7/6AYaB78dpdsIC9U9FXTVbM2+u79r4zw==",
|
"integrity": "sha512-xAHBo2RXb/PDCcd07sdftgE2HtiW3QC3hXY8g5yWfTkJ3WIPfDi5OsG9lti0KgzF9ZnfMf4i08ptAoC3k72iHQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/lite-chat": "^1.6.3",
|
"@tencentcloud/lite-chat": "^1.5.0",
|
||||||
"@tencentcloud/tui-core-lite": "1.0.0",
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
||||||
"@trtc/call-engine-lite-js": "~3.5.0"
|
"@trtc/call-engine-lite-js": "~3.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@trtc/calls-uikit-vue2.6": {
|
"node_modules/@trtc/calls-uikit-vue2.6": {
|
||||||
"version": "4.4.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-vue2.6/-/calls-uikit-vue2.6-4.4.2.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-vue2.6/-/calls-uikit-vue2.6-4.2.2.tgz",
|
||||||
"integrity": "sha512-KDHFXQVFMAPCpb0aRrOHAh3LPA4tLlr/LhTMDxyUJbJN6CGhYRIlbDyQ0BHl/z7yHYNkTwXaHkahpCJCmnaPNQ==",
|
"integrity": "sha512-OjB3R4/vrQiK5uF3FG6UlmUOR+3CvbZ8YMwm6MI0KfXyg27EJOq315JdQKJgX5ZU7HzUstfrMtxYTbGfUJKAcA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/lite-chat": "^1.6.3",
|
"@tencentcloud/lite-chat": "^1.5.0",
|
||||||
"@tencentcloud/tui-core-lite": "1.0.0",
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
||||||
"@trtc/call-engine-lite-js": "~3.5.0",
|
"@trtc/call-engine-lite-js": "~3.4.0",
|
||||||
"@trtc/call-engine-lite-wx": "~3.4.7",
|
|
||||||
"@vue/composition-api": "^1.7.2"
|
"@vue/composition-api": "^1.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@trtc/calls-uikit-wx": {
|
"node_modules/@trtc/calls-uikit-wx": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.2",
|
||||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-wx/-/calls-uikit-wx-4.2.4.tgz",
|
"resolved": "https://repo.huaweicloud.com/repository/npm/@trtc/calls-uikit-wx/-/calls-uikit-wx-4.2.2.tgz",
|
||||||
"integrity": "sha512-PuqIfEqUrLfwCxip1EjOj2HAObzZBvzN3Gpx8ULvWdPu0hqVZIPobo3aTtfGKUCdJPmNq+/iLVQ3MwneXhtR5A==",
|
"integrity": "sha512-pW0l3UhiekcTZlLDVPBnsvYyIhrjU0Mod9joL+KlO8gXnK/qeBauAyAz8AaipsveJV4FYUh6AZJkDNN+DzAgig==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/trtc-component-wx": "^1.0.5",
|
"@tencentcloud/trtc-component-wx": "^1.0.5",
|
||||||
"@tencentcloud/tui-core-lite": "1.0.0",
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
||||||
"@trtc/call-engine-lite-wx": "~3.4.7",
|
"@trtc/call-engine-lite-wx": "~3.4.3",
|
||||||
"aegis-mp-sdk": "latest"
|
"aegis-mp-sdk": "latest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,11 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tencentcloud/chat-uikit-uniapp": "^3.2.0",
|
"@tencentcloud/chat-uikit-engine-lite": "1.0.3",
|
||||||
"unplugin-vue2-script-setup": "^0.11.4"
|
"@tencentcloud/chat-uikit-uniapp": "3.1.0",
|
||||||
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
||||||
|
"@tencentcloud/universal-api": "^2.4.0",
|
||||||
|
"unplugin-vue2-script-setup": "^0.11.4",
|
||||||
|
"dayjs": "^1.11.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
56
pages.json
56
pages.json
@@ -14,6 +14,54 @@
|
|||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIConversation/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIChat/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"app-plus": {
|
||||||
|
"softinputMode": "adjustResize"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIContact/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIChat/video-play",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "腾讯云 IM",
|
||||||
|
"navigationBarBackgroundColor": "#EBF0F6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIChat/web-view",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "腾讯云 IM",
|
||||||
|
"navigationBarBackgroundColor": "#EBF0F6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUIGroup/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "TUIKit/components/TUISearch/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "聊天记录",
|
||||||
|
"navigationBarBackgroundColor": "#EBF0F6"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/login/protocol",
|
"path": "pages/login/protocol",
|
||||||
"style": {
|
"style": {
|
||||||
@@ -76,8 +124,8 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/my-index/personal-center/index",
|
"path": "pages/my-index/personal-center/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "个人中心",
|
"navigationStyle": "custom",
|
||||||
"navigationBarBackgroundColor": "#ffffff"
|
"navigationBarTitleText": "个人中心"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -262,13 +310,13 @@
|
|||||||
"backgroundColor": "#ffffff",
|
"backgroundColor": "#ffffff",
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"pagePath": "pages/news-list/news-list",
|
"pagePath": "TUIKit/components/TUIConversation/index",
|
||||||
"iconPath": "static/images/tabBar/news.png",
|
"iconPath": "static/images/tabBar/news.png",
|
||||||
"selectedIconPath": "static/images/tabBar/newsHL.png",
|
"selectedIconPath": "static/images/tabBar/newsHL.png",
|
||||||
"text": "消息"
|
"text": "消息"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/contacts/contacts",
|
"pagePath": "TUIKit/components/TUIContact/index",
|
||||||
"iconPath": "static/images/tabBar/contacts.png",
|
"iconPath": "static/images/tabBar/contacts.png",
|
||||||
"selectedIconPath": "static/images/tabBar/contactsHL.png",
|
"selectedIconPath": "static/images/tabBar/contactsHL.png",
|
||||||
"text": "通讯录"
|
"text": "通讯录"
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
<view class="swipe-box">
|
<view class="swipe-box">
|
||||||
<view
|
<view
|
||||||
v-if="item.defaultAddress == 0"
|
v-if="item.defaultAddress == 0"
|
||||||
class="btn-box"
|
class="public-uni-swipe-action-right"
|
||||||
@click="onDefault(item)"
|
@click="onDefault(item)"
|
||||||
>
|
>
|
||||||
<uni-icons
|
<uni-icons
|
||||||
@@ -104,7 +104,10 @@
|
|||||||
></uni-icons>
|
></uni-icons>
|
||||||
<text class="iocn-name">设为默认</text>
|
<text class="iocn-name">设为默认</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="btn-box" @click="onDelete(item)">
|
<view
|
||||||
|
class="public-uni-swipe-action-right"
|
||||||
|
@click="onDelete(item)"
|
||||||
|
>
|
||||||
<uni-icons
|
<uni-icons
|
||||||
type="trash"
|
type="trash"
|
||||||
size="18"
|
size="18"
|
||||||
@@ -121,6 +124,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '@/styles/global.scss';
|
||||||
|
|
||||||
.top-right-name {
|
.top-right-name {
|
||||||
font-family: PingFang SC, PingFang SC;
|
font-family: PingFang SC, PingFang SC;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -181,28 +186,6 @@
|
|||||||
}
|
}
|
||||||
.swipe-box {
|
.swipe-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
.btn-box {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: rgb(47, 194, 17);
|
|
||||||
width: 130rpx;
|
|
||||||
.iocn-name {
|
|
||||||
font-family: PingFang SC, PingFang SC;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #fff;
|
|
||||||
text-align: left;
|
|
||||||
font-style: normal;
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后一个
|
|
||||||
&:last-child {
|
|
||||||
background: rgb(206, 59, 22);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
})
|
})
|
||||||
setToken(res.token)
|
setToken(res.token)
|
||||||
await fetchUserInfo()
|
await fetchUserInfo()
|
||||||
reLaunch('/pages/news-list/news-list')
|
reLaunch('/TUIKit/components/TUIConversation/index')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRegister = () => {
|
const onRegister = () => {
|
||||||
|
|||||||
@@ -53,6 +53,9 @@
|
|||||||
:placeholder-style="placeholderStyle"
|
:placeholder-style="placeholderStyle"
|
||||||
:placeholder="props.placeholder"
|
:placeholder="props.placeholder"
|
||||||
/>
|
/>
|
||||||
|
<view v-else class="right-box">
|
||||||
|
<slot name="right"></slot>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="hasDefaultSlot" class="bottom-slot">
|
<view v-if="hasDefaultSlot" class="bottom-slot">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@@ -74,6 +77,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
.right-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.bottom-slot {
|
.bottom-slot {
|
||||||
margin-top: 26rpx;
|
margin-top: 26rpx;
|
||||||
|
|||||||
81
pages/my-index/components/popup-box.vue
Normal file
81
pages/my-index/components/popup-box.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
|
const show = defineModel({
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const name = defineModel('name', {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
/** 是否为性别选项 */
|
||||||
|
isSex: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['confirm'])
|
||||||
|
|
||||||
|
const inputDialog = ref(null)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => show.value,
|
||||||
|
v => {
|
||||||
|
if (v) {
|
||||||
|
// 'bottom'
|
||||||
|
inputDialog.value.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
const dialogInputConfirm = () => {
|
||||||
|
close()
|
||||||
|
emits('confirm')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<uni-popup ref="inputDialog" type="dialog">
|
||||||
|
<uni-popup-dialog
|
||||||
|
v-model="name"
|
||||||
|
mode="input"
|
||||||
|
:title="props.title"
|
||||||
|
:placeholder="`请输入${props.title}`"
|
||||||
|
@close="close"
|
||||||
|
@confirm="dialogInputConfirm"
|
||||||
|
>
|
||||||
|
<uni-data-checkbox
|
||||||
|
v-if="props.isSex"
|
||||||
|
v-model="name"
|
||||||
|
:localdata="[
|
||||||
|
{
|
||||||
|
text: '男',
|
||||||
|
value: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '女',
|
||||||
|
value: '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '未知',
|
||||||
|
value: '2'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
></uni-data-checkbox>
|
||||||
|
</uni-popup-dialog>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -52,10 +52,12 @@
|
|||||||
>
|
>
|
||||||
<view class="left-box">
|
<view class="left-box">
|
||||||
<image
|
<image
|
||||||
src="https://wx1.sinaimg.cn/mw690/92eeb099gy1i29hl0ne80j21jk2bcash.jpg"
|
v-if="userInfo?.avatar"
|
||||||
mode="scaleToFill"
|
:src="userInfo?.avatar"
|
||||||
|
mode="aspectFill"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
></image>
|
></image>
|
||||||
|
<uni-icons v-else type="contact-filled" size="70"></uni-icons>
|
||||||
<view class="nickname">
|
<view class="nickname">
|
||||||
<text class="name">{{ userInfo?.userName || '' }}</text>
|
<text class="name">{{ userInfo?.userName || '' }}</text>
|
||||||
<text class="name">ID:{{ userInfo?.userId || '' }}</text>
|
<text class="name">ID:{{ userInfo?.userId || '' }}</text>
|
||||||
|
|||||||
@@ -1,27 +1,84 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
import { useAuthUser } from '@/composables/useAuthUser'
|
import { useAuthUser } from '@/composables/useAuthUser'
|
||||||
|
import PopupBox from '../components/popup-box.vue'
|
||||||
|
import { chooseImage } from '@/utils/media.js'
|
||||||
|
import { uploadSingleFile } from '@/utils/uploadFile'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
|
const { updateUserInfo } = useUserStore()
|
||||||
|
|
||||||
const itemList = [
|
const itemList = [
|
||||||
{ title: '我的二维码', key: '1', value: '' },
|
{ title: '我的二维码', key: '1', value: '' },
|
||||||
{ title: 'ID', key: '2', value: 'userId' },
|
{ title: 'ID', key: '2', value: 'userId' },
|
||||||
{ title: '昵称', key: '3', value: 'userName' },
|
{ title: '昵称', key: '3', value: 'userName' },
|
||||||
{ title: '性别', key: '4', value: '' },
|
{ title: '性别', key: '4', value: 'sex' },
|
||||||
{ title: '手机号码', key: '5', value: 'mobile' },
|
{ title: '手机号码', key: '5', value: 'mobile' },
|
||||||
{ title: '个性签名', key: '6', value: '' }
|
{ title: '个性签名', key: '6', value: 'perSignature' }
|
||||||
]
|
]
|
||||||
|
/** 可修改的 key */
|
||||||
|
const MODIFY_KEY = ['3', '4', '6']
|
||||||
|
|
||||||
const { userInfo } = useAuthUser()
|
const { userInfo } = useAuthUser()
|
||||||
|
|
||||||
|
const popupData = reactive({
|
||||||
|
show: false,
|
||||||
|
title: '修改信息',
|
||||||
|
name: '',
|
||||||
|
key: '',
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
const formData = reactive({
|
||||||
|
avatar: '',
|
||||||
|
userName: '',
|
||||||
|
sex: '',
|
||||||
|
perSignature: ''
|
||||||
|
})
|
||||||
|
const upInfo = (key, value) => {
|
||||||
|
if (MODIFY_KEY.includes(key)) {
|
||||||
|
const titleData = {
|
||||||
|
3: '昵称',
|
||||||
|
4: '性别',
|
||||||
|
6: '个性签名'
|
||||||
|
}[key]
|
||||||
|
popupData.value = value
|
||||||
|
popupData.key = key
|
||||||
|
popupData.title = titleData
|
||||||
|
popupData.name = userInfo.value[value]
|
||||||
|
popupData.show = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editAvatar = async () => {
|
||||||
|
const paths = await chooseImage({ count: 1 })
|
||||||
|
const url = await uploadSingleFile(paths[0], {
|
||||||
|
url: '/api/common/admin/upload/up/single'
|
||||||
|
})
|
||||||
|
formData.avatar = url
|
||||||
|
updateUserInfo({ avatar: url })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
if (MODIFY_KEY.includes(popupData.key)) {
|
||||||
|
if (popupData.name === userInfo.value[popupData.value]) return
|
||||||
|
if (popupData.name === '') return
|
||||||
|
updateUserInfo({ [popupData.value]: popupData.name })
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<nav-bar isTopBg isPlaceholder title="个人中心"></nav-bar>
|
||||||
<view class="personal-center">
|
<view class="personal-center">
|
||||||
<view class="public-card">
|
<view class="public-card" @click="editAvatar">
|
||||||
<view class="left-img">
|
<view class="left-img">
|
||||||
<image
|
<image
|
||||||
src="https://p4.itc.cn/images01/20220619/46660ed163164c14be90e605a73ee5e8.jpeg"
|
v-if="userInfo.avatar"
|
||||||
|
:src="userInfo.avatar"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
></image>
|
></image>
|
||||||
|
<uni-icons v-else type="contact-filled" size="60"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
<text class="value">换头像</text>
|
<text class="value">换头像</text>
|
||||||
@@ -33,20 +90,37 @@
|
|||||||
v-for="(item, index) in itemList"
|
v-for="(item, index) in itemList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="public-card"
|
class="public-card"
|
||||||
|
@click="upInfo(item.key, item.value)"
|
||||||
>
|
>
|
||||||
<view class="left-box">
|
<view class="left-box">
|
||||||
<text>{{ item.title }}</text>
|
<text>{{ item.title }}</text>
|
||||||
<text v-if="item.key === '6'" class="text">
|
<text v-if="item.key === '6'" class="text">
|
||||||
这个人很懒,什么也没有
|
{{ userInfo[item.value] || '这个人很懒,什么也没有' }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
<text v-if="!['1', '6'].includes(item.key)" class="value">
|
<view v-if="!['1', '6'].includes(item.key)" class="value">
|
||||||
{{ item.value ? userInfo[item.value] : '' }}
|
<text v-if="item.key === '4'">
|
||||||
|
{{
|
||||||
|
userInfo[item.value] === '2'
|
||||||
|
? '未设置'
|
||||||
|
: userInfo[item.value] === '0'
|
||||||
|
? '男'
|
||||||
|
: '女'
|
||||||
|
}}
|
||||||
</text>
|
</text>
|
||||||
|
<text v-else>{{ item.value ? userInfo[item.value] : '' }}</text>
|
||||||
|
</view>
|
||||||
<uni-icons type="right" size="16" color="#999999"></uni-icons>
|
<uni-icons type="right" size="16" color="#999999"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<popup-box
|
||||||
|
v-model="popupData.show"
|
||||||
|
v-model:name="popupData.name"
|
||||||
|
:isSex="popupData.key === '4'"
|
||||||
|
:title="popupData.title"
|
||||||
|
@confirm="onConfirm"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.right-box {
|
.right-box {
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
.value {
|
.value {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { navigateBack } from '@/utils/router'
|
||||||
|
import {
|
||||||
|
addUserThirdPay,
|
||||||
|
addUserPayPwd,
|
||||||
|
getUserThirdPay,
|
||||||
|
getUserBankDetail
|
||||||
|
} from '@/api/my-index'
|
||||||
import CardInput from '../../components/card-input.vue'
|
import CardInput from '../../components/card-input.vue'
|
||||||
|
|
||||||
|
/** 是否第三方进入 */
|
||||||
|
const IS_THIRD_PAY = ['101', '102']
|
||||||
|
|
||||||
|
const { showToast } = useUI()
|
||||||
|
|
||||||
const stateData = reactive({
|
const stateData = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
state: '0'
|
state: '0',
|
||||||
|
loading: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
|
// 修改id
|
||||||
|
id: '',
|
||||||
// 银行卡名称
|
// 银行卡名称
|
||||||
name: '',
|
name: '',
|
||||||
// 开户行名称
|
// 开户行名称
|
||||||
@@ -18,34 +34,131 @@
|
|||||||
// 微信/支付宝号
|
// 微信/支付宝号
|
||||||
codeName: '',
|
codeName: '',
|
||||||
// 图片链接
|
// 图片链接
|
||||||
img: ''
|
img: '',
|
||||||
|
// 银行卡类型
|
||||||
|
cardType: '',
|
||||||
|
// 图片回显列表
|
||||||
|
imgList: []
|
||||||
})
|
})
|
||||||
|
|
||||||
const onAdd = () => {
|
const onAdd = async () => {
|
||||||
console.log(formData)
|
let data = {}
|
||||||
const data = {
|
const name = formData.id ? '修改' : '添加'
|
||||||
bankName: formData.name,
|
const method = formData.id ? 'put' : 'post'
|
||||||
cardNumber: formData.cardNum,
|
if (IS_THIRD_PAY.includes(stateData.state)) {
|
||||||
bankName: formData.khName
|
if (!formData.codeName) {
|
||||||
|
showToast('请输入账号')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if (!formData.img) {
|
||||||
|
showToast('请上传收款码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
paymentType: stateData.state === '101' ? 2 : 1,
|
||||||
|
qrCodeUrl: formData.img,
|
||||||
|
accountNumber: formData.codeName,
|
||||||
|
paymentId: formData.id
|
||||||
|
}
|
||||||
|
await addUserThirdPay(data, method)
|
||||||
|
} else {
|
||||||
|
if (!formData.name) {
|
||||||
|
showToast('请输入银行名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.khName) {
|
||||||
|
showToast('请输入持卡人姓名')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.cardNum) {
|
||||||
|
showToast('请输入银行卡号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.cardType) {
|
||||||
|
showToast('请选择银行卡类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
bankName: formData.name,
|
||||||
|
cardHolder: formData.khName,
|
||||||
|
cardNumber: formData.cardNum,
|
||||||
|
cardType: formData.cardType,
|
||||||
|
cardId: formData.id
|
||||||
|
}
|
||||||
|
await addUserPayPwd(data, method)
|
||||||
|
}
|
||||||
|
await showToast(`${name}成功`, 'success')
|
||||||
|
navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取详情 */
|
||||||
|
const getDetail = async () => {
|
||||||
|
stateData.loading = true
|
||||||
|
const res = await getUserThirdPay(
|
||||||
|
stateData.state === '101' ? '2' : '1'
|
||||||
|
)
|
||||||
|
let titltData = ''
|
||||||
|
if (res?.data) {
|
||||||
|
const { paymentId, accountNumber, qrCodeUrl } = res.data
|
||||||
|
formData.codeName = accountNumber
|
||||||
|
formData.img = qrCodeUrl
|
||||||
|
formData.id = paymentId
|
||||||
|
formData.imgList = [
|
||||||
|
{
|
||||||
|
url: qrCodeUrl
|
||||||
|
}
|
||||||
|
]
|
||||||
|
titltData = {
|
||||||
|
101: '修改支付宝账户',
|
||||||
|
102: '修改微信账户'
|
||||||
|
}[stateData.state]
|
||||||
|
} else {
|
||||||
|
titltData = {
|
||||||
|
101: '添加支付宝账户',
|
||||||
|
102: '添加微信账户'
|
||||||
|
}[stateData.state]
|
||||||
|
}
|
||||||
|
stateData.title = titltData
|
||||||
|
stateData.loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取对应银行卡详情 */
|
||||||
|
const getBankDetail = async () => {
|
||||||
|
stateData.loading = true
|
||||||
|
const res = await getUserBankDetail(stateData.state)
|
||||||
|
const { cardId, bankName, cardHolder, cardNumber, cardType } =
|
||||||
|
res.data
|
||||||
|
formData.id = cardId
|
||||||
|
formData.name = bankName
|
||||||
|
formData.khName = cardHolder
|
||||||
|
formData.cardNum = cardNumber
|
||||||
|
formData.cardType = cardType
|
||||||
|
stateData.title = '修改银行卡'
|
||||||
|
stateData.loading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(e => {
|
onLoad(e => {
|
||||||
const titltData = {
|
|
||||||
0: '添加银行卡',
|
|
||||||
101: '添加支付宝账户',
|
|
||||||
102: '添加微信账户'
|
|
||||||
}[e.key]
|
|
||||||
stateData.title = titltData
|
|
||||||
stateData.state = e.key
|
stateData.state = e.key
|
||||||
|
if (IS_THIRD_PAY.includes(e.key)) {
|
||||||
|
getDetail()
|
||||||
|
} else {
|
||||||
|
if (e.key === '0') {
|
||||||
|
stateData.title = '添加银行卡'
|
||||||
|
stateData.loading = false
|
||||||
|
} else {
|
||||||
|
getBankDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view>
|
<view v-if="!stateData.loading">
|
||||||
<nav-bar isTopBg isPlaceholder :title="stateData.title">
|
<nav-bar isTopBg isPlaceholder :title="stateData.title">
|
||||||
<template #right>
|
<template #right>
|
||||||
<text class="public-navbar__right-btn" @click="onAdd">添加</text>
|
<text class="public-navbar__right-btn" @click="onAdd">
|
||||||
|
{{ formData.id ? '修改' : '添加' }}
|
||||||
|
</text>
|
||||||
</template>
|
</template>
|
||||||
</nav-bar>
|
</nav-bar>
|
||||||
|
|
||||||
@@ -54,8 +167,26 @@
|
|||||||
class="card-details"
|
class="card-details"
|
||||||
>
|
>
|
||||||
<CardInput v-model="formData.name" title="银行名称"></CardInput>
|
<CardInput v-model="formData.name" title="银行名称"></CardInput>
|
||||||
<CardInput v-model="formData.khName" title="开户行"></CardInput>
|
<CardInput v-model="formData.khName" title="持卡人姓名"></CardInput>
|
||||||
<CardInput v-model="formData.cardNum" title="银行卡号"></CardInput>
|
<CardInput v-model="formData.cardNum" title="银行卡号"></CardInput>
|
||||||
|
<CardInput :isInput="false" title="银行卡类型">
|
||||||
|
<template #right>
|
||||||
|
<uni-data-checkbox
|
||||||
|
v-model="formData.cardType"
|
||||||
|
:localdata="[
|
||||||
|
{
|
||||||
|
text: '借记卡',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '信用卡',
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
class="card-checkbox"
|
||||||
|
></uni-data-checkbox>
|
||||||
|
</template>
|
||||||
|
</CardInput>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else class="card-details">
|
<view v-else class="card-details">
|
||||||
@@ -65,8 +196,16 @@
|
|||||||
stateData.state === '101' ? '支付宝账号' : '微信账号'
|
stateData.state === '101' ? '支付宝账号' : '微信账号'
|
||||||
}`"
|
}`"
|
||||||
></CardInput>
|
></CardInput>
|
||||||
<CardInput :is-input="false" title="收款码">
|
<CardInput
|
||||||
<cb-file-picker v-model="formData.img"></cb-file-picker>
|
:is-input="false"
|
||||||
|
:title="`${
|
||||||
|
stateData.state === '101' ? '支付宝' : '微信'
|
||||||
|
}收款码(必传)`"
|
||||||
|
>
|
||||||
|
<cb-file-picker
|
||||||
|
v-model="formData.img"
|
||||||
|
v-model:list="formData.imgList"
|
||||||
|
></cb-file-picker>
|
||||||
</CardInput>
|
</CardInput>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -77,4 +216,10 @@
|
|||||||
.card-details {
|
.card-details {
|
||||||
padding: 32rpx 24rpx;
|
padding: 32rpx 24rpx;
|
||||||
}
|
}
|
||||||
|
.card-checkbox {
|
||||||
|
:deep(.uni-label-pointer) {
|
||||||
|
margin: 0 !important;
|
||||||
|
margin-left: 60rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,56 +1,107 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { navigateTo } from '@/utils/router'
|
import { navigateTo } from '@/utils/router'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
|
import { getUserBankList, deleteUserPayPwd } from '@/api/my-index'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
|
||||||
const itemList = ref([
|
/** 是否第三方进入 */
|
||||||
{
|
const IS_THIRD_PAY = ['0', '101', '102']
|
||||||
title: '支付宝',
|
const itemList = ref([])
|
||||||
key: '101',
|
const { showToast, showDialog } = useUI()
|
||||||
icon: '/static/images/my-index/zfb.png'
|
|
||||||
},
|
|
||||||
{ title: '微信', key: '102', icon: '/static/images/my-index/wx.png' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const onAddCard = key => {
|
const onAddCard = key => {
|
||||||
navigateTo('/pages/my-index/wallet/bank-card/card-details', { key })
|
navigateTo('/pages/my-index/wallet/bank-card/card-details', { key })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getList = async type => {
|
||||||
|
itemList.value = []
|
||||||
|
const res = await getUserBankList()
|
||||||
|
|
||||||
|
itemList.value = [
|
||||||
|
{
|
||||||
|
bankName: '支付宝',
|
||||||
|
key: '101',
|
||||||
|
icon: '/static/images/my-index/zfb.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bankName: '微信',
|
||||||
|
key: '102',
|
||||||
|
icon: '/static/images/my-index/wx.png'
|
||||||
|
},
|
||||||
|
...res.data
|
||||||
|
]
|
||||||
|
if (type === 1) {
|
||||||
|
showToast('删除成功', 'success')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = async id => {
|
||||||
|
const res = await showDialog('提示', '确定要删除吗?')
|
||||||
|
if (!res) return
|
||||||
|
await deleteUserPayPwd(id)
|
||||||
|
await getList(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="bank-card">
|
<view class="bank-card">
|
||||||
<view
|
<uni-swipe-action>
|
||||||
|
<uni-swipe-action-item
|
||||||
v-for="(item, index) in itemList"
|
v-for="(item, index) in itemList"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
:disabled="!!item.key"
|
||||||
|
class="card-box"
|
||||||
|
>
|
||||||
|
<view
|
||||||
class="public-card"
|
class="public-card"
|
||||||
@click="onAddCard(item.key)"
|
@click="onAddCard(item.key || item.cardId)"
|
||||||
>
|
>
|
||||||
<view class="left-img">
|
<view class="left-img">
|
||||||
<image
|
<image
|
||||||
:src="
|
v-if="item.icon"
|
||||||
item.icon
|
:src="item.icon"
|
||||||
? item.icon
|
|
||||||
: 'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2024%2F1227%2F842e0e65j00sp543n001fd000i700iim.jpg&thumbnail=660x2147483647&quality=80&type=jpg'
|
|
||||||
"
|
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="card"
|
class="card"
|
||||||
></image>
|
></image>
|
||||||
<text>{{ item.title }}</text>
|
<text>{{ item.bankName }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
|
<text v-if="item.cardNumber">{{ item.cardNumber }}</text>
|
||||||
<uni-icons type="right" size="16" color="#999999"></uni-icons>
|
<uni-icons type="right" size="16" color="#999999"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<template v-slot:right>
|
||||||
|
<view
|
||||||
|
class="public-uni-swipe-action-right"
|
||||||
|
@click="onDelete(item.cardId)"
|
||||||
|
>
|
||||||
|
<uni-icons type="trash" size="18" color="#ffffff"></uni-icons>
|
||||||
|
<text class="iocn-name">删除</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</uni-swipe-action-item>
|
||||||
|
</uni-swipe-action>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
<bottom-view>
|
<bottom-view v-if="itemList.length > 0">
|
||||||
<cb-button @click="onAddCard('0')">+添加银行卡</cb-button>
|
<cb-button @click="onAddCard('0')">+添加银行卡</cb-button>
|
||||||
</bottom-view>
|
</bottom-view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '@/styles/global.scss';
|
||||||
@import '../../styles/index.scss';
|
@import '../../styles/index.scss';
|
||||||
.bank-card {
|
.bank-card {
|
||||||
padding: 38rpx 24rpx;
|
padding: 38rpx 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-box + .card-box {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import CardInput from '../components/card-input.vue'
|
import CardInput from '../components/card-input.vue'
|
||||||
import { useUI } from '@/utils/use-ui'
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { navigateBack } from '@/utils/router'
|
||||||
import { validateTransactionPassword } from '@/utils/validate'
|
import { validateTransactionPassword } from '@/utils/validate'
|
||||||
|
import { updateUserPayPwd, getUserPayPwd } from '@/api/my-index'
|
||||||
|
|
||||||
const { showToast } = useUI()
|
const { showToast } = useUI()
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
|
id: '',
|
||||||
|
state: '0',
|
||||||
// 旧密码
|
// 旧密码
|
||||||
password: '',
|
password: '',
|
||||||
// 新密码
|
// 新密码
|
||||||
@@ -14,7 +19,9 @@
|
|||||||
// 确认密码
|
// 确认密码
|
||||||
confirmPassword: ''
|
confirmPassword: ''
|
||||||
})
|
})
|
||||||
const onEdit = () => {
|
const onEdit = async () => {
|
||||||
|
let data = {}
|
||||||
|
if (formData.state === '1') {
|
||||||
const passwordValue = validateTransactionPassword(
|
const passwordValue = validateTransactionPassword(
|
||||||
formData.password,
|
formData.password,
|
||||||
'旧密码'
|
'旧密码'
|
||||||
@@ -47,8 +54,37 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('修改密码:', formData)
|
data = {
|
||||||
|
id: formData.id,
|
||||||
|
oldPassword: formData.password,
|
||||||
|
password: formData.confirmPassword
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!formData.password) {
|
||||||
|
showToast('请输入设置交易密码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
password: formData.password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateUserPayPwd(data, formData.id ? 'put' : 'post')
|
||||||
|
await showToast(`${formData.id ? '修改' : '添加'}成功`, 'success')
|
||||||
|
navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const res = await getUserPayPwd()
|
||||||
|
formData.id = res.data.id
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(e => {
|
||||||
|
formData.state = e.type
|
||||||
|
if (e.type === '1') {
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -59,7 +95,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</nav-bar>
|
</nav-bar>
|
||||||
|
|
||||||
<view class="input-box">
|
<view v-if="formData.state === '1'" class="input-box">
|
||||||
<CardInput
|
<CardInput
|
||||||
v-model="formData.password"
|
v-model="formData.password"
|
||||||
title="旧密码"
|
title="旧密码"
|
||||||
@@ -76,6 +112,13 @@
|
|||||||
type="password"
|
type="password"
|
||||||
></CardInput>
|
></CardInput>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else class="input-box">
|
||||||
|
<CardInput
|
||||||
|
v-model="formData.password"
|
||||||
|
title="设置交易密码"
|
||||||
|
type="password"
|
||||||
|
></CardInput>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import { navigateTo } from '@/utils/router'
|
import { navigateTo } from '@/utils/router'
|
||||||
import { onShow } from '@dcloudio/uni-app'
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
import { getUserPayPwd } from '@/api/my-index'
|
import { getUserPayPwd } from '@/api/my-index'
|
||||||
|
import { useAuthUser } from '@/composables/useAuthUser'
|
||||||
|
|
||||||
const itemList = [
|
const { userInfo } = useAuthUser()
|
||||||
|
|
||||||
|
const itemList = ref([])
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
itemList.value = []
|
||||||
|
const res = await getUserPayPwd()
|
||||||
|
const isSetPayPwd = res?.data ? '修改支付密码' : '设置支付密码'
|
||||||
|
|
||||||
|
itemList.value = [
|
||||||
{
|
{
|
||||||
title: '提现卡',
|
title: '提现卡',
|
||||||
key: '1',
|
key: '1',
|
||||||
@@ -11,16 +22,17 @@
|
|||||||
},
|
},
|
||||||
{ title: '交易记录', key: '2', url: '' },
|
{ title: '交易记录', key: '2', url: '' },
|
||||||
{
|
{
|
||||||
title: '修改支付密码',
|
title: isSetPayPwd,
|
||||||
key: '3',
|
key: '3',
|
||||||
|
isType: !!res?.data,
|
||||||
url: '/pages/my-index/wallet/edit-password'
|
url: '/pages/my-index/wallet/edit-password'
|
||||||
},
|
},
|
||||||
{ title: '实名认证', key: '4', url: '/pages/my-index/wallet/real-id' }
|
{
|
||||||
|
title: '实名认证',
|
||||||
|
key: '4',
|
||||||
|
url: '/pages/my-index/wallet/real-id'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const getData = async () => {
|
|
||||||
const res = await getUserPayPwd()
|
|
||||||
console.log(res, '===')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
@@ -34,7 +46,7 @@
|
|||||||
<view class="top-card">
|
<view class="top-card">
|
||||||
<view class="left-box">
|
<view class="left-box">
|
||||||
<text>我的资产</text>
|
<text>我的资产</text>
|
||||||
<text>1222</text>
|
<text>{{ userInfo?.totalPoints }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-box">
|
<view class="right-box">
|
||||||
<button>充值</button>
|
<button>充值</button>
|
||||||
@@ -46,7 +58,13 @@
|
|||||||
v-for="(item, index) in itemList"
|
v-for="(item, index) in itemList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="public-card"
|
class="public-card"
|
||||||
@click="item.url && navigateTo(item.url)"
|
@click="
|
||||||
|
item.url &&
|
||||||
|
navigateTo(
|
||||||
|
item.url,
|
||||||
|
item.key === '3' ? { type: item.isType ? 1 : 0 } : null
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<view class="left-box">
|
<view class="left-box">
|
||||||
<text>{{ item.title }}</text>
|
<text>{{ item.title }}</text>
|
||||||
|
|||||||
@@ -1,44 +1,121 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive } from 'vue'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
import CardInput from '../components/card-input.vue'
|
import CardInput from '../components/card-input.vue'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { navigateBack } from '@/utils/router'
|
||||||
|
import { getUserIdCard, addUserIdCard } from '@/api/my-index'
|
||||||
|
import { validateIdCard } from '@/utils/validate'
|
||||||
|
|
||||||
|
const { showToast } = useUI()
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
|
// 修改id
|
||||||
|
id: '',
|
||||||
// 正面
|
// 正面
|
||||||
front: '',
|
front: '',
|
||||||
// 背面
|
// 背面
|
||||||
back: '',
|
back: '',
|
||||||
// 手机号
|
// 姓名
|
||||||
phone: '',
|
realName: '',
|
||||||
// 身份证号码
|
// 身份证号码
|
||||||
idCard: ''
|
idCard: '',
|
||||||
|
frontList: [],
|
||||||
|
backList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
const res = await getUserIdCard()
|
||||||
|
if (res?.data) {
|
||||||
|
formData.id = res.data.id
|
||||||
|
formData.front = res.data.idCardFrontUrl
|
||||||
|
formData.back = res.data.idCardBackUrl
|
||||||
|
formData.realName = res.data.realName
|
||||||
|
formData.idCard = res.data.idCardNumber
|
||||||
|
|
||||||
|
formData.frontList = [{ url: res.data.idCardFrontUrl }]
|
||||||
|
formData.backList = [{ url: res.data.idCardBackUrl }]
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAddCode = async () => {
|
||||||
|
if (!formData.front) {
|
||||||
|
showToast('请上传身份证人像面')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.back) {
|
||||||
|
showToast('请上传身份证国徽面')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.realName) {
|
||||||
|
showToast('请输入姓名')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const codeData = validateIdCard(formData.idCard)
|
||||||
|
if (!codeData.valid) {
|
||||||
|
showToast(codeData.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
idCardFrontUrl: formData.front,
|
||||||
|
idCardBackUrl: formData.back,
|
||||||
|
realName: formData.realName,
|
||||||
|
idCardNumber: formData.idCard
|
||||||
|
}
|
||||||
|
|
||||||
|
await addUserIdCard(data)
|
||||||
|
await showToast(`添加成功`, 'success')
|
||||||
|
navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
getData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="real-id">
|
<view v-if="!loading" class="real-id">
|
||||||
<!-- 说明 -->
|
<!-- 说明 -->
|
||||||
<text class="top-text">*为保证您的账户安全,请先完成实名认证</text>
|
<text class="top-text">*为保证您的账户安全,请先完成实名认证</text>
|
||||||
|
|
||||||
<CardInput :is-input="false" title="收款码">
|
<CardInput :is-input="false" title="收款码">
|
||||||
<view class="qrcode-box">
|
<view class="qrcode-box">
|
||||||
<cb-file-picker v-model="formData.front" isFront></cb-file-picker>
|
<cb-file-picker
|
||||||
<cb-file-picker v-model="formData.back" isBack></cb-file-picker>
|
v-model="formData.front"
|
||||||
|
v-model:list="formData.frontList"
|
||||||
|
isFront
|
||||||
|
></cb-file-picker>
|
||||||
|
<cb-file-picker
|
||||||
|
v-model="formData.back"
|
||||||
|
v-model:list="formData.backList"
|
||||||
|
isBack
|
||||||
|
></cb-file-picker>
|
||||||
</view>
|
</view>
|
||||||
</CardInput>
|
</CardInput>
|
||||||
|
|
||||||
<CardInput
|
<CardInput
|
||||||
v-model="formData.phone"
|
v-model="formData.realName"
|
||||||
title="手机号"
|
title="姓名"
|
||||||
type="tel"
|
placeholder="请输入姓名"
|
||||||
placeholder="请输入手机号"
|
|
||||||
></CardInput>
|
></CardInput>
|
||||||
|
|
||||||
<CardInput
|
<CardInput
|
||||||
v-model="formData.idCard"
|
v-model="formData.idCard"
|
||||||
type="text"
|
|
||||||
title="身份证号"
|
title="身份证号"
|
||||||
placeholder="请输入身份证号"
|
placeholder="请输入身份证号"
|
||||||
></CardInput>
|
></CardInput>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<bottom-view>
|
||||||
|
<cb-button @click="onAddCode">
|
||||||
|
确认{{ formData.id ? '修改' : '添加' }}
|
||||||
|
</cb-button>
|
||||||
|
</bottom-view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { useAuthUser } from '@/composables/useAuthUser'
|
import { useAuthUser } from '@/composables/useAuthUser'
|
||||||
|
|
||||||
import { TUIConversation } from '@/TUIKit'
|
|
||||||
|
|
||||||
|
|
||||||
const { tencentUserSig } = useAuthUser()
|
const { tencentUserSig } = useAuthUser()
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
console.log(tencentUserSig.value, '===222===')
|
console.log(tencentUserSig.value, '===222===')
|
||||||
@@ -17,8 +14,7 @@
|
|||||||
<template #right>右侧按钮</template>
|
<template #right>右侧按钮</template>
|
||||||
</nav-bar>
|
</nav-bar>
|
||||||
<cb-search placeholder="搜索"></cb-search> -->
|
<cb-search placeholder="搜索"></cb-search> -->
|
||||||
<TUIConversation></TUIConversation>
|
222
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
removeSig
|
removeSig
|
||||||
} from '@/utils/storage'
|
} from '@/utils/storage'
|
||||||
import { useTokenStore } from './token'
|
import { useTokenStore } from './token'
|
||||||
import { getUserData, userLogout } from '@/api'
|
import { getUserData, userLogout, updateUserData } from '@/api'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useUI } from '@/utils/use-ui'
|
import { useUI } from '@/utils/use-ui'
|
||||||
import { reLaunch } from '@/utils/router'
|
import { reLaunch } from '@/utils/router'
|
||||||
@@ -62,6 +62,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
* 登录腾讯 IM
|
* 登录腾讯 IM
|
||||||
*/
|
*/
|
||||||
const loginTencentIM = async () => {
|
const loginTencentIM = async () => {
|
||||||
|
console.log(11111111111111111111111)
|
||||||
await TUILogin.login({
|
await TUILogin.login({
|
||||||
SDKAppID: tencentUserSig.value.sdkappID,
|
SDKAppID: tencentUserSig.value.sdkappID,
|
||||||
userID: tencentUserSig.value.userId,
|
userID: tencentUserSig.value.userId,
|
||||||
@@ -90,16 +91,17 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
/** 刷新用户信息(如用户信息被修改) */
|
/** 刷新用户信息(如用户信息被修改) */
|
||||||
const refreshUserInfo = async () => {
|
const refreshUserInfo = async () => {
|
||||||
const res = await getUserData()
|
const res = await getUserData()
|
||||||
await setUserInfo(res.data)
|
await setUserInfoData(res.data)
|
||||||
|
userInfo.value = res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新部分用户信息(例如昵称、头像)
|
* 更新部分用户信息(例如昵称、头像)
|
||||||
*/
|
*/
|
||||||
const updateUserInfo = partialData => {
|
const updateUserInfo = async partialData => {
|
||||||
if (!userInfo.value) return
|
if (!userInfo.value) return
|
||||||
userInfo.value = { ...userInfo.value, ...partialData }
|
await updateUserData(partialData)
|
||||||
setUserInfoData(userInfo.value)
|
await refreshUserInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -107,6 +109,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
tencentUserSig,
|
tencentUserSig,
|
||||||
refreshUserInfo,
|
refreshUserInfo,
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
|
loginTencentIM,
|
||||||
setUserInfo,
|
setUserInfo,
|
||||||
clearUserInfo,
|
clearUserInfo,
|
||||||
updateUserInfo
|
updateUserInfo
|
||||||
|
|||||||
@@ -15,3 +15,25 @@ page {
|
|||||||
padding: 12rpx 36rpx;
|
padding: 12rpx 36rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** uni-swipe-action 滑动右边样式 */
|
||||||
|
.public-uni-swipe-action-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgb(47, 194, 17);
|
||||||
|
width: 130rpx;
|
||||||
|
.iocn-name {
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
text-align: left;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
background: rgb(206, 59, 22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
29
uni_modules/TencentCloud-Push/changelog.md
Normal file
29
uni_modules/TencentCloud-Push/changelog.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
## 1.2.0(2025-03-31)
|
||||||
|
- 适配出海手机支持 FCM 推送。
|
||||||
|
## 1.1.0(2024-12-11)
|
||||||
|
- 大幅减小插件包体积,优化产品体验。
|
||||||
|
- 兼容 HBuilderX 4.36 的 Breaking changes。如果您需要 vivo/荣耀 的厂商推送,请参考 [文档](https://cloud.tencent.com/document/product/269/103522),正确配置 `manifestPlaceholders.json` 和 `mcn-services.json`。
|
||||||
|
|
||||||
|
## 1.0.0(2024-11-29)
|
||||||
|
- 优化和 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合时的产品体验。
|
||||||
|
- 新增点击通知栏事件 NOTIFICATION_CLICKED,支持获取推送扩展信息。
|
||||||
|
- 在线通道支持自定义铃音功能。
|
||||||
|
|
||||||
|
## 0.5.1(2024-11-07)
|
||||||
|
- 优化和 [@tencentcloud/chat-uikit-uniapp](https://cloud.tencent.com/document/product/269/64507) 融合时的产品体验。
|
||||||
|
- 优化和 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合时的产品体验。
|
||||||
|
- 新增接口 disablePostNotificationInForeground,此接口可实现应用在前台时,开/关通知栏通知(默认开)。
|
||||||
|
- 新增接口 createNotificationChannel,支持 FCM/OPPO 自定义铃音。
|
||||||
|
|
||||||
|
## 0.4.0(2024-10-17)
|
||||||
|
- 支持与 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合打包。
|
||||||
|
|
||||||
|
## 0.3.0(2024-10-12)
|
||||||
|
- 新增接口 addPushListener/removePushListener,支持获取在线推送消息,支持推送消息撤回通知。
|
||||||
|
|
||||||
|
## 0.2.0(2024-09-18)
|
||||||
|
- 支持 FCM
|
||||||
|
- 支持 hihonor
|
||||||
|
|
||||||
|
## 0.1.0(2024-09-10)
|
||||||
|
- 使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。
|
||||||
0
uni_modules/TencentCloud-Push/index.js
Normal file
0
uni_modules/TencentCloud-Push/index.js
Normal file
90
uni_modules/TencentCloud-Push/package.json
Normal file
90
uni_modules/TencentCloud-Push/package.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "@tencentcloud/uni-app-push",
|
||||||
|
"id": "TencentCloud-Push",
|
||||||
|
"main": "index.js",
|
||||||
|
"displayName": "【官方】uni-app 腾讯云推送服务(Push)",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"description": "使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。",
|
||||||
|
"license": "ISC",
|
||||||
|
"keywords": [
|
||||||
|
"腾讯云",
|
||||||
|
"Push",
|
||||||
|
"推送",
|
||||||
|
"Android/iOS",
|
||||||
|
"谷歌FCM"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.6.8"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "uts",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "腾讯云即时通信IM隐私保护指引: https://web.sdk.qcloud.com/document/Tencent-IM-Privacy-Protection-Guidelines.html\n移动推送隐私保护指引: https://privacy.qq.com/document/preview/8565a4a2d26e480187ed86b0cc81d727",
|
||||||
|
"permissions": "本地存储空间"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y",
|
||||||
|
"alipay": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-android": "y",
|
||||||
|
"app-ios": "y",
|
||||||
|
"app-harmony": "u"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "u",
|
||||||
|
"Android Browser": "u",
|
||||||
|
"微信浏览器(Android)": "u",
|
||||||
|
"QQ浏览器(Android)": "u"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "u",
|
||||||
|
"IE": "u",
|
||||||
|
"Edge": "u",
|
||||||
|
"Firefox": "u",
|
||||||
|
"Safari": "u"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "u",
|
||||||
|
"阿里": "u",
|
||||||
|
"百度": "u",
|
||||||
|
"字节跳动": "u",
|
||||||
|
"QQ": "u",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
299
uni_modules/TencentCloud-Push/readme-npm.md
Normal file
299
uni_modules/TencentCloud-Push/readme-npm.md
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
# TencentCloud-Push
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。
|
||||||
|
|
||||||
|
腾讯云推送服务(Push)提供一站式 App 推送解决方案,助您轻松提升用户留存和互动活跃度,支持与腾讯云即时通信 IM SDK、实时音视频 TRTC SDK、音视频通话 SDK、直播 SDK等音视频终端产品协同集成,在不同场景联合使用,提升业务整体功能体验。
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/60d714484e54b284cfa440adcc885349.png" width="618" height="456">
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/864c391ecf6f2724d26e368e4f09e466.png" width="618" height="444">
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/6af60f4b20dd46323e8f901a161a80a9.png" width="618" height="454">
|
||||||
|
|
||||||
|
#### 数据可视化,辅助运营策略
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/6c422f64900053c38a6bf66fe1103b3f.png" width="618" height="334">
|
||||||
|
|
||||||
|
#### 支持推送消息全链路问题排查
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/156d43ed48971f9bf865ad0c4e2342e3.png" width="618" height="443">
|
||||||
|
|
||||||
|
#### 六地服务部署,严守数据安全
|
||||||
|
|
||||||
|
提供了中国、东南亚(新加坡、印尼雅加达)、东北亚(韩国首尔)、欧洲(德国法兰克福)以及北美(美国硅谷)数据存储中心供选择,每个数据中心均支持全球接入。如果您的应用在境外上线且用户主要在境外,您可以根据消息传输需求及合规要求,选择适合您业务的境外数据中心,保障您的数据安全。
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/2ffc1a103a42d9c01cfb819cd92bbd1d.png" widht="618" height="308">
|
||||||
|
|
||||||
|
## 快速跑通
|
||||||
|
|
||||||
|
### 步骤1:创建应用
|
||||||
|
|
||||||
|
进入 [控制台](https://console.cloud.tencent.com/im) ,单击创建应用,填写应用名称,选择数据中心,单击确定,完成应用创建。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 步骤2:开通推送服务 Push
|
||||||
|
|
||||||
|
进入 [推送服务 Push](https://console.cloud.tencent.com/im/push-plugin-push-identifier),单击立即购买或免费试用 。(每个应用可免费试用一次,有效期7天)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 步骤3:下载腾讯云推送服务(Push)并复制 Push SDK 到您的项目中
|
||||||
|
|
||||||
|
1. 下载腾讯云推送服务(Push)。
|
||||||
|
```
|
||||||
|
npm install @tencentcloud/uni-app-push
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 复制 Push SDK 到您的项目中。
|
||||||
|
|
||||||
|
【macOS 端】
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
mkdir -p ./uni_modules/TencentCloud-Push && rsync -av ./node_modules/@tencentcloud/uni-app-push/ ./uni_modules/TencentCloud-Push
|
||||||
|
```
|
||||||
|
【Window 端】
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
xcopy .\node_modules\@tencentcloud\uni-app-push .\uni_modules\TencentCloud-Push /i /e
|
||||||
|
```
|
||||||
|
|
||||||
|
### 步骤4:在 App.vue 中引入并注册腾讯云推送服务(Push)
|
||||||
|
|
||||||
|
将 SDKAppID 和 appKey 替换为您在IM 控制台 - 推送服务 Push - 接入设置页面 获取的应用的信息。如图所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 集成 TencentCloud-Push
|
||||||
|
import * as Push from '@/uni_modules/TencentCloud-Push';
|
||||||
|
const SDKAppID = 0; // 您的 SDKAppID
|
||||||
|
const appKey = ''; // 客户端密钥
|
||||||
|
Push.registerPush(SDKAppID, appKey, (data) => {
|
||||||
|
console.log('registerPush ok', data);
|
||||||
|
Push.getRegistrationID((registrationID) => {
|
||||||
|
console.log('getRegistrationID ok', registrationID);
|
||||||
|
});
|
||||||
|
}, (errCode, errMsg) => {
|
||||||
|
console.error('registerPush failed', errCode, errMsg);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听通知栏点击事件,获取推送扩展信息
|
||||||
|
Push.addPushListener(Push.EVENT.NOTIFICATION_CLICKED, (res) => {
|
||||||
|
// res 为推送扩展信息
|
||||||
|
console.log('notification clicked', res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听在线推送
|
||||||
|
Push.addPushListener(Push.EVENT.MESSAGE_RECEIVED, (res) => {
|
||||||
|
// res 为消息内容
|
||||||
|
console.log('message received', res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听在线推送被撤回
|
||||||
|
Push.addPushListener(Push.EVENT.MESSAGE_REVOKED, (res) => {
|
||||||
|
// res 为被撤回的消息 ID
|
||||||
|
console.log('message revoked', res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="step5">步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)</span>
|
||||||
|
|
||||||
|
单击 HBuilderX 的 【运行 > 运行到手机或模拟器 > 制作自定义调试基座】,使用云端证书制作 Android 或 iOS 自定义调试基座。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
自定义调试基座打好后,安装到手机运行。
|
||||||
|
|
||||||
|
[登录](https://console.cloud.tencent.com/im/push-plugin-push-check) 控制台,使用测试工具进行在线推送测试。
|
||||||
|

|
||||||
|
|
||||||
|
## 厂商推送配置
|
||||||
|
> - 请注意!HBuilderX 4.36 发布了不向下兼容的更新,如果您使用的是 HBuilderX 4.36 或者更高版本,且需要 vivo/荣耀 的厂商推送,
|
||||||
|
请升级推送版本到 1.1.0 或更高版本,并参考文档正确配置 `manifestPlaceholders.json` 和 `mcs-services.json`。
|
||||||
|
> - 请在 `nativeResources` 目录下进行推送配置。若项目根目录尚未创建该文件夹,请新建一个名为 `nativeResources` 的文件夹。
|
||||||
|
> - 离线推送厂商配置完成后,需要打包自定义基座。参考:[[快速跑通]>[步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)]](#user-content-step5)
|
||||||
|
|
||||||
|
### 【Android】
|
||||||
|
|
||||||
|
1. 新建 nativeResources/android/assets 目录。
|
||||||
|
|
||||||
|
2. 在 [推送服务 Push > 接入设置 > 一键式快速配置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 下载 `timpush-configs.json` 文件,配置到 nativeResources/android/assets 目录下。
|
||||||
|
|
||||||
|
3. For 华为:
|
||||||
|
|
||||||
|
配置 `agconnect-services.json` (此文件获取详见 [厂商配置 > uniapp > 华为 > 步骤4:获取应用信息](https://cloud.tencent.com/document/product/269/103769))到 nativeResources/android 目录下。
|
||||||
|
|
||||||
|
4. For Google FCM:
|
||||||
|
|
||||||
|
4.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `project.plugins`,添加 `"com.google.gms.google-services"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"project": {
|
||||||
|
"plugins": [
|
||||||
|
...
|
||||||
|
"com.google.gms.google-services"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4.2. 配置 `google-services.json` 文件到 nativeResources/android/ 目录。
|
||||||
|
|
||||||
|
5. For 荣耀:
|
||||||
|
|
||||||
|
5.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:honor:8.3.6498"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"dependencies": [
|
||||||
|
...
|
||||||
|
"com.tencent.timpush:honor:8.3.6498"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5.2. 配置 `mcs-services.json` 文件到 nativeResources/android 目录下。
|
||||||
|
|
||||||
|
5.3. 配置 `appID` 到 nativeResources/android/manifestPlaceholders.json 中的 `"HONOR_APPID"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"HONOR_APPID": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. For vivo:
|
||||||
|
|
||||||
|
6.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:vivo:8.3.6498"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"dependencies": [
|
||||||
|
...
|
||||||
|
"com.tencent.timpush:vivo:8.3.6498"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6.2. 配置 `appID` 和 `appKey` 到 nativeResources/android/manifestPlaceholders.json 中的 `VIVO_APPKEY` 和 `VIVO_APPID`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"VIVO_APPKEY": "",
|
||||||
|
"VIVO_APPID": "",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 【iOS】
|
||||||
|
|
||||||
|
1. 新建 nativeResources/ios/Resources 目录。
|
||||||
|
|
||||||
|
2. 在 nativeResources/ios/Resources 中**新建 timpush-configs.json 文件**。
|
||||||
|
|
||||||
|
3. 并将在 [IM控制台 > 推送服务 Push > 接入设置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 获取的证书ID,补充到 timpush-configs.json 文件中。
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"businessID":"xxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
| API | 描述|
|
||||||
|
|----|---|
|
||||||
|
| registerPush | 注册推送服务 (必须在 App 用户同意了隐私政策,并且允许为 App 用户提供推送服务后,再调用该接口使用推送服务)。<br>首次注册成功后,TencentCloud-Push SDK 生成该设备的标识 - RegistrationID。<br> 业务侧可以把这个 RegistrationID 保存到业务服务器。业务侧根据 RegistrationID 向设备推送消息或者通知。|
|
||||||
|
| unRegisterPush | 反注册关闭推送服务。|
|
||||||
|
| setRegistrationID | 设置注册推送服务使用的推送 ID 标识,即 RegistrationID。<br/>如果业务侧期望业务账号 ID 和推送 ID 一致,方便使用,可使用此接口,此时需注意,此接口需在 registerPush(注册推送服务)之前调用。|
|
||||||
|
| getRegistrationID | 注册推送服务成功后,获取推送 ID 标识,即 RegistrationID。|
|
||||||
|
| getNotificationExtInfo | 获取推送扩展信息。|
|
||||||
|
| getNotificationExtInfo | 收到离线推送时,点击通知栏拉起 app,调用此接口可获取推送扩展信息。|
|
||||||
|
| addPushListener | 添加 Push 监听器。|
|
||||||
|
| removePushListener | 移除 Push 监听器。|
|
||||||
|
| disablePostNotificationInForeground | 应用在前台时,开/关通知栏通知。|
|
||||||
|
| createNotificationChannel | 创建客户端通知 channel。|
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|SDKAppID|number|是|推送(Push)应用 ID|
|
||||||
|
|appKey|string|是|推送(Push)应用客户端密钥|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|onError|function|否|接口调用失败的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|onError|function|否|接口调用失败的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
setRegistrationID(registrationID: string, onSuccess: () => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|registrationID|string|是|设备的推送标识 ID,卸载重装会改变。|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
getRegistrationID(onSuccess: (registrationID: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
getNotificationExtInfo(onSuccess: (extInfo: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
addPushListener(eventName: string, listener: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|eventName|string|是|推送事件类型|
|
||||||
|
|listener|function|是|推送事件处理方法|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
removePushListener(eventName: string, listener?: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|eventName|string|是|推送事件类型|
|
||||||
|
|listener|function|否|推送事件处理方法|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
disablePostNotificationInForeground(disable: boolean);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|disable|boolean|是|应用在前台时,开/关通知栏通知,默认开<br/> - true: 应用在前台时,关闭通知栏通知。<br/> - false: 应用在前台时,开启通知栏通知。|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
createNotificationChannel(options: any, listener: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|options.channelID|string|是|自定义 channel 的 ID|
|
||||||
|
|options.channelName|string|是|自定义 channel 的名称|
|
||||||
|
|options.channelDesc|string|否|自定义 channel 的描述|
|
||||||
|
|options.channelSound|string|否|自定义 channel 的铃音,音频文件名,不带后缀,音频文件需要放到 xxx/nativeResources/android/res/raw 中。<br/> 例如:<br/> `options.channelSound = private_ring`,即设置 `xxx/nativeResources/android/res/raw/private_ring.mp3` 为自定义铃音|
|
||||||
|
|listener|function|是|接口调用成功的回调函数|
|
||||||
285
uni_modules/TencentCloud-Push/readme.md
Normal file
285
uni_modules/TencentCloud-Push/readme.md
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
# TencentCloud-Push
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。
|
||||||
|
|
||||||
|
腾讯云推送服务(Push)提供一站式 App 推送解决方案,助您轻松提升用户留存和互动活跃度,支持与腾讯云即时通信 IM SDK、实时音视频 TRTC SDK、音视频通话 SDK、直播 SDK等音视频终端产品协同集成,在不同场景联合使用,提升业务整体功能体验。
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/60d714484e54b284cfa440adcc885349.png" width="618" height="456">
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/864c391ecf6f2724d26e368e4f09e466.png" width="618" height="444">
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/6af60f4b20dd46323e8f901a161a80a9.png" width="618" height="454">
|
||||||
|
|
||||||
|
#### 数据可视化,辅助运营策略
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/6c422f64900053c38a6bf66fe1103b3f.png" width="618" height="334">
|
||||||
|
|
||||||
|
#### 支持推送消息全链路问题排查
|
||||||
|
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/156d43ed48971f9bf865ad0c4e2342e3.png" width="618" height="443">
|
||||||
|
|
||||||
|
#### 六地服务部署,严守数据安全
|
||||||
|
|
||||||
|
提供了中国、东南亚(新加坡、印尼雅加达)、东北亚(韩国首尔)、欧洲(德国法兰克福)以及北美(美国硅谷)数据存储中心供选择,每个数据中心均支持全球接入。如果您的应用在境外上线且用户主要在境外,您可以根据消息传输需求及合规要求,选择适合您业务的境外数据中心,保障您的数据安全。
|
||||||
|
<img src="https://qcloudimg.tencent-cloud.cn/image/document/2ffc1a103a42d9c01cfb819cd92bbd1d.png" widht="618" height="308">
|
||||||
|
|
||||||
|
## 快速跑通
|
||||||
|
|
||||||
|
### 步骤1:创建应用
|
||||||
|
|
||||||
|
进入 [控制台](https://console.cloud.tencent.com/im) ,单击创建应用,填写应用名称,选择数据中心,单击确定,完成应用创建。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 步骤2:开通推送服务 Push
|
||||||
|
|
||||||
|
进入 [推送服务 Push](https://console.cloud.tencent.com/im/push-plugin-push-identifier),单击立即购买或免费试用 。(每个应用可免费试用一次,有效期7天)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 步骤3:将 [uni-app 腾讯云推送服务(Push)](https://ext.dcloud.net.cn/plugin?id=20169)插件导入 HbuilderX 中的工程。如图所示:
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
### 步骤4:在 App.vue 中引入并注册腾讯云推送服务(Push)
|
||||||
|
|
||||||
|
将 SDKAppID 和 appKey 替换为您在IM 控制台 - 推送服务 Push - 接入设置页面 获取的应用的信息。如图所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 集成 TencentCloud-Push
|
||||||
|
import * as Push from '@/uni_modules/TencentCloud-Push';
|
||||||
|
const SDKAppID = 0; // 您的 SDKAppID
|
||||||
|
const appKey = ''; // 客户端密钥
|
||||||
|
Push.registerPush(SDKAppID, appKey, (data) => {
|
||||||
|
console.log('registerPush ok', data);
|
||||||
|
Push.getRegistrationID((registrationID) => {
|
||||||
|
console.log('getRegistrationID ok', registrationID);
|
||||||
|
});
|
||||||
|
}, (errCode, errMsg) => {
|
||||||
|
console.error('registerPush failed', errCode, errMsg);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听通知栏点击事件,获取推送扩展信息
|
||||||
|
Push.addPushListener(Push.EVENT.NOTIFICATION_CLICKED, (res) => {
|
||||||
|
// res 为推送扩展信息
|
||||||
|
console.log('notification clicked', res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听在线推送
|
||||||
|
Push.addPushListener(Push.EVENT.MESSAGE_RECEIVED, (res) => {
|
||||||
|
// res 为消息内容
|
||||||
|
console.log('message received', res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听在线推送被撤回
|
||||||
|
Push.addPushListener(Push.EVENT.MESSAGE_REVOKED, (res) => {
|
||||||
|
// res 为被撤回的消息 ID
|
||||||
|
console.log('message revoked', res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="step5">步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)</span>
|
||||||
|
|
||||||
|
单击 HBuilderX 的 【运行 > 运行到手机或模拟器 > 制作自定义调试基座】,使用云端证书制作 Android 或 iOS 自定义调试基座。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
自定义调试基座打好后,安装到手机运行。
|
||||||
|
|
||||||
|
[登录](https://console.cloud.tencent.com/im/push-plugin-push-check) 控制台,使用测试工具进行在线推送测试。
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 厂商推送配置
|
||||||
|
|
||||||
|
> - 请注意!HBuilderX 4.36 发布了不向下兼容的更新,如果您使用的是 HBuilderX 4.36 或者更高版本,且需要 vivo/荣耀 的厂商推送,
|
||||||
|
请升级推送版本到 1.1.0 或更高版本,并参考文档正确配置 `manifestPlaceholders.json` 和 `mcs-services.json`。
|
||||||
|
> - 请在 `nativeResources` 目录下进行推送配置。若项目根目录尚未创建该文件夹,请新建一个名为 `nativeResources` 的文件夹。
|
||||||
|
> - 厂商推送配置完成后,需要打包自定义基座。参考:[[快速跑通]>[步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)]](#step5)
|
||||||
|
|
||||||
|
#### 【Android】
|
||||||
|
|
||||||
|
1. 新建 nativeResources/android/assets 目录。
|
||||||
|
|
||||||
|
2. 在 [推送服务 Push > 接入设置 > 一键式快速配置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 下载 `timpush-configs.json` 文件,配置到 nativeResources/android/assets 目录下。
|
||||||
|
|
||||||
|
3. For 华为:
|
||||||
|
|
||||||
|
配置 `agconnect-services.json` (此文件获取详见 [厂商配置 > uniapp > 华为 > 步骤4:获取应用信息](https://cloud.tencent.com/document/product/269/103769))到 nativeResources/android 目录下。
|
||||||
|
|
||||||
|
4. For Google FCM:
|
||||||
|
|
||||||
|
4.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `project.plugins`,添加 `"com.google.gms.google-services"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"project": {
|
||||||
|
"plugins": [
|
||||||
|
...
|
||||||
|
"com.google.gms.google-services"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4.2. 配置 `google-services.json` 文件到 nativeResources/android/ 目录。
|
||||||
|
|
||||||
|
5. For 荣耀:
|
||||||
|
|
||||||
|
5.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:honor:8.3.6498"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"dependencies": [
|
||||||
|
...
|
||||||
|
"com.tencent.timpush:honor:8.3.6498"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5.2. 配置 `mcs-services.json` 文件到 nativeResources/android 目录下。
|
||||||
|
|
||||||
|
5.3. 配置 `appID` 到 nativeResources/android/manifestPlaceholders.json 中的 `"HONOR_APPID"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"HONOR_APPID": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. For vivo:
|
||||||
|
|
||||||
|
6.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:vivo:8.3.6498"`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"dependencies": [
|
||||||
|
...
|
||||||
|
"com.tencent.timpush:vivo:8.3.6498"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6.2. 配置 `appID` 和 `appKey` 到 nativeResources/android/manifestPlaceholders.json 中的 `VIVO_APPKEY` 和 `VIVO_APPID`,如下:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"VIVO_APPKEY": "",
|
||||||
|
"VIVO_APPID": "",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 【iOS】
|
||||||
|
|
||||||
|
1. 新建 nativeResources/ios/Resources 目录。
|
||||||
|
|
||||||
|
2. 在 nativeResources/ios/Resources 目录下新建 `timpush-configs.json` 文件。
|
||||||
|
|
||||||
|
3. 将在 [IM控制台 > 推送服务 Push > 接入设置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 获取的证书ID,补充到 `timpush-configs.json` 文件中。
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"businessID":"xxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
| API | 描述|
|
||||||
|
|----|---|
|
||||||
|
| registerPush | 注册推送服务 (必须在 App 用户同意了隐私政策,并且允许为 App 用户提供推送服务后,再调用该接口使用推送服务)。<br>首次注册成功后,TencentCloud-Push SDK 生成该设备的标识 - RegistrationID。<br> 业务侧可以把这个 RegistrationID 保存到业务服务器。业务侧根据 RegistrationID 向设备推送消息或者通知。|
|
||||||
|
| unRegisterPush | 反注册关闭推送服务。|
|
||||||
|
| setRegistrationID | 设置注册推送服务使用的推送 ID 标识,即 RegistrationID。<br/>如果业务侧期望业务账号 ID 和推送 ID 一致,方便使用,可使用此接口,此时需注意,此接口需在 registerPush(注册推送服务)之前调用。|
|
||||||
|
| getRegistrationID | 注册推送服务成功后,获取推送 ID 标识,即 RegistrationID。|
|
||||||
|
| getNotificationExtInfo | 收到离线推送时,点击通知栏拉起 app,调用此接口可获取推送扩展信息。|
|
||||||
|
| addPushListener | 添加 Push 监听器。|
|
||||||
|
| removePushListener | 移除 Push 监听器。|
|
||||||
|
| disablePostNotificationInForeground | 应用在前台时,开/关通知栏通知(默认开)。|
|
||||||
|
| createNotificationChannel | 创建客户端通知 channel。|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|SDKAppID|number|是|推送(Push)应用 ID|
|
||||||
|
|appKey|string|是|推送(Push)应用客户端密钥|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|onError|function|否|接口调用失败的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|onError|function|否|接口调用失败的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
setRegistrationID(registrationID: string, onSuccess: () => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|registrationID|string|是|设备的推送标识 ID,卸载重装会改变。|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
getRegistrationID(onSuccess: (registrationID: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
getNotificationExtInfo(onSuccess: (extInfo: string) => void): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|onSuccess|function|是|接口调用成功的回调函数|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
addPushListener(eventName: string, listener: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|eventName|string|是|推送事件类型|
|
||||||
|
|listener|function|是|推送事件处理方法|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
removePushListener(eventName: string, listener?: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|eventName|string|是|推送事件类型|
|
||||||
|
|listener|function|否|推送事件处理方法|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
disablePostNotificationInForeground(disable: boolean);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|disable|boolean|是|应用在前台时,开/关通知栏通知,默认开<br/> - true: 应用在前台时,关闭通知栏通知。<br/> - false: 应用在前台时,开启通知栏通知。|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
createNotificationChannel(options: any, listener: (data: any) => void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|属性|类型|必填|说明|
|
||||||
|
|----|---|----|----|
|
||||||
|
|options.channelID|string|是|自定义 channel 的 ID|
|
||||||
|
|options.channelName|string|是|自定义 channel 的名称|
|
||||||
|
|options.channelDesc|string|否|自定义 channel 的描述|
|
||||||
|
|options.channelSound|string|否|自定义 channel 的铃音,音频文件名,不带后缀,音频文件需要放到 xxx/nativeResources/android/res/raw 中。<br/> 例如:<br/> `options.channelSound = private_ring`,即设置 `xxx/nativeResources/android/res/raw/private_ring.mp3` 为自定义铃音|
|
||||||
|
|listener|function|是|接口调用成功的回调函数|
|
||||||
27
uni_modules/TencentCloud-Push/utssdk/app-android/config.json
Normal file
27
uni_modules/TencentCloud-Push/utssdk/app-android/config.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"minSdkVersion": "21",
|
||||||
|
"dependencies": [
|
||||||
|
"com.google.android.material:material:1.3.0",
|
||||||
|
"com.google.code.gson:gson:2.9.1",
|
||||||
|
"commons-codec:commons-codec:1.15",
|
||||||
|
"com.github.bumptech.glide:glide:4.12.0",
|
||||||
|
"com.tencent.timpush:timpush:8.5.6864",
|
||||||
|
"com.tencent.liteav.tuikit:tuicore:8.5.6864",
|
||||||
|
"com.tencent.timpush:huawei:8.5.6864",
|
||||||
|
"com.tencent.timpush:xiaomi:8.5.6864",
|
||||||
|
"com.tencent.timpush:oppo:8.5.6864",
|
||||||
|
"com.tencent.timpush:meizu:8.5.6864",
|
||||||
|
"com.tencent.timpush:fcm:8.5.6864"
|
||||||
|
],
|
||||||
|
"project": {
|
||||||
|
"plugins": [
|
||||||
|
"com.huawei.agconnect",
|
||||||
|
"com.hihonor.mcs.asplugin"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"com.huawei.agconnect:agcp:1.9.1.301",
|
||||||
|
"com.google.gms:google-services:4.3.15",
|
||||||
|
"com.hihonor.mcs:asplugin:2.0.1.300"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
152
uni_modules/TencentCloud-Push/utssdk/app-android/index.uts
Normal file
152
uni_modules/TencentCloud-Push/utssdk/app-android/index.uts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { UTSAndroid } from 'io.dcloud.uts';
|
||||||
|
import Context from 'android.content.Context';
|
||||||
|
import TIMPushManager from 'com.tencent.qcloud.tim.push.TIMPushManager';
|
||||||
|
import TIMPushConfig from 'com.tencent.qcloud.tim.push.config.TIMPushConfig';
|
||||||
|
import { PushCallbackOptions } from './push-callback-options.uts';
|
||||||
|
import { PushListenerOptions } from './push-listener-options.uts';
|
||||||
|
import PushCallback from './push-callback.uts';
|
||||||
|
import PushListener from './push-listener.uts';
|
||||||
|
|
||||||
|
const context: Context | null = UTSAndroid.getAppContext();
|
||||||
|
console.warn('Push | package.name:', context?.getPackageName());
|
||||||
|
TIMPushConfig.getInstance().setRunningPlatform(2);
|
||||||
|
const Push = TIMPushManager.getInstance();
|
||||||
|
|
||||||
|
export class EVENT {
|
||||||
|
static MESSAGE_RECEIVED: string = 'message_received'
|
||||||
|
static MESSAGE_REVOKED: string = 'message_revoked'
|
||||||
|
static NOTIFICATION_CLICKED: string = 'notification_clicked'
|
||||||
|
}
|
||||||
|
|
||||||
|
let disableNotification = false;
|
||||||
|
export function disablePostNotificationInForeground(disable: boolean): void {
|
||||||
|
console.log('Push | disablePostNotificationInForeground', disable);
|
||||||
|
disableNotification = disable;
|
||||||
|
Push.disablePostNotificationInForeground(disableNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void {
|
||||||
|
if (SDKAppID == 0) {
|
||||||
|
onError?.(9010001, 'Invalid SDKAppID');
|
||||||
|
} else if (appKey == '') {
|
||||||
|
onError?.(9010002, 'Invalid appKey');
|
||||||
|
}
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'registerPush',
|
||||||
|
success: (res?: any) => {
|
||||||
|
Push.disablePostNotificationInForeground(disableNotification);
|
||||||
|
// 强转下类型,避免类型推断错误
|
||||||
|
let token: string = res as string;
|
||||||
|
onSuccess(token);
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
onError?.(errCode, errMsg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误
|
||||||
|
Push.registerPush(context, SDKAppID.toInt(), appKey, new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setRegistrationID(registrationID: string, onSuccess: () => void): void {
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'setRegistrationID',
|
||||||
|
success: (res?: any) => {
|
||||||
|
onSuccess();
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
// 空实现
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误
|
||||||
|
Push.setRegistrationID(registrationID, new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRegistrationID(onSuccess: (registrationID: string) => void): void {
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'getRegistrationID',
|
||||||
|
success: (res?: any) => {
|
||||||
|
// 强转下类型,避免类型推断错误
|
||||||
|
let registrationID: string = res as string;
|
||||||
|
onSuccess(registrationID);
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
// 空实现
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误
|
||||||
|
Push.getRegistrationID(new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void {
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'unRegisterPush',
|
||||||
|
success: (res?: any) => {
|
||||||
|
onSuccess();
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
// 空实现
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误
|
||||||
|
Push.unRegisterPush(new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNotificationChannel(options: any, onSuccess: (extInfo: string) => void): void {
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'createNotificationChannel',
|
||||||
|
success: (res?: any) => {
|
||||||
|
let ret: string = res as string;
|
||||||
|
onSuccess(ret);
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
// 空实现
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Push.callExperimentalAPI('createNotificationChannel', JSON.stringify(options), new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNotificationExtInfo(onSuccess: (extInfo: string) => void): void {
|
||||||
|
const pushCbOptions: PushCallbackOptions = {
|
||||||
|
apiName: 'getNotificationExtInfo',
|
||||||
|
success: (res?: any) => {
|
||||||
|
let ret: string = res as string;
|
||||||
|
onSuccess(ret);
|
||||||
|
},
|
||||||
|
fail: (errCode: number, errMsg: string) => {
|
||||||
|
// 空实现
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Push.callExperimentalAPI('getNotificationExtInfo', null, new PushCallback(pushCbOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
const listenerMap = new Map<string, Array<(res: any) => void>>();
|
||||||
|
|
||||||
|
const pushListenerOptions: PushListenerOptions = {
|
||||||
|
listener: (eventName: string, data: any) => {
|
||||||
|
listenerMap.get(eventName)?.forEach(item => {
|
||||||
|
item(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const pushListener = new PushListener(pushListenerOptions);
|
||||||
|
|
||||||
|
@UTSJS.keepAlive
|
||||||
|
export function addPushListener(eventName: string, listener: (res: any) => void): void {
|
||||||
|
if(listenerMap.size === 0) {
|
||||||
|
Push.addPushListener(pushListener);
|
||||||
|
}
|
||||||
|
const listeners:Array<(res: any) => void> = [listener];
|
||||||
|
listenerMap.get(eventName)?.forEach(item => {
|
||||||
|
listeners.push(item);
|
||||||
|
})
|
||||||
|
listenerMap.set(eventName, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function removePushListener(eventName: string, listener?: (res: any) => void): void {
|
||||||
|
listenerMap.delete(eventName);
|
||||||
|
if(listenerMap.size === 0) {
|
||||||
|
Push.removePushListener(pushListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export type PushCallbackOptions = {
|
||||||
|
apiName: string
|
||||||
|
success: (res?: any) => void
|
||||||
|
fail: (errCode: number, errMsg: string) => void
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import TIMPushCallback from 'com.tencent.qcloud.tim.push.TIMPushCallback';
|
||||||
|
import { PushCallbackOptions } from './push-callback-options.uts';
|
||||||
|
|
||||||
|
const LOG_PREFIX: string = 'Push |';
|
||||||
|
export default class PushCallback implements TIMPushCallback<any> {
|
||||||
|
private apiName: string;
|
||||||
|
private success: (data?: any) => void;
|
||||||
|
private fail: (errCode: number, errMsg: string) => void;
|
||||||
|
|
||||||
|
constructor(options: PushCallbackOptions) {
|
||||||
|
this.apiName = options.apiName;
|
||||||
|
this.success = options.success;
|
||||||
|
this.fail = options.fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onSuccess(data?: any) {
|
||||||
|
console.log(`${LOG_PREFIX} ${this.apiName} ok, data:`, data);
|
||||||
|
if (data == null) {
|
||||||
|
this.success?.('');
|
||||||
|
} else {
|
||||||
|
this.success?.(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override onError(errCode: Int, errMsg: string, data?: any) {
|
||||||
|
this.fail?.(errCode as number, errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type PushListenerOptions = {
|
||||||
|
listener: (eventType: string, data: any) => void
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import TIMPushListener from 'com.tencent.qcloud.tim.push.TIMPushListener';
|
||||||
|
import TIMPushMessage from 'com.tencent.qcloud.tim.push.TIMPushMessage';
|
||||||
|
import { PushListenerOptions } from './push-listener-options.uts';
|
||||||
|
|
||||||
|
const LOG_PREFIX: string = 'Push | PushListener';
|
||||||
|
export default class PushListener implements TIMPushListener {
|
||||||
|
private listener: (eventType: string, data: any) => void;
|
||||||
|
|
||||||
|
constructor(options: PushListenerOptions) {
|
||||||
|
this.listener = options.listener;
|
||||||
|
console.log(`${LOG_PREFIX} ok`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override onRecvPushMessage(message: TIMPushMessage) {
|
||||||
|
this.listener('message_received', { data: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
override onRevokePushMessage(messageID: string) {
|
||||||
|
this.listener('message_revoked', { data: messageID });
|
||||||
|
}
|
||||||
|
|
||||||
|
override onNotificationClicked(ext: string) {
|
||||||
|
this.listener('notification_clicked', { data: ext });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>aps-environment</key>
|
||||||
|
<string>development</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
11
uni_modules/TencentCloud-Push/utssdk/app-ios/config.json
Normal file
11
uni_modules/TencentCloud-Push/utssdk/app-ios/config.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"deploymentTarget": "9.0",
|
||||||
|
"dependencies-pods": [
|
||||||
|
{
|
||||||
|
"name": "TXIMSDK_Plus_iOS_XCFramework",
|
||||||
|
"version": "8.5.6864"
|
||||||
|
}, {
|
||||||
|
"name": "TIMPush",
|
||||||
|
"version": "8.5.6864"
|
||||||
|
}]
|
||||||
|
}
|
||||||
125
uni_modules/TencentCloud-Push/utssdk/app-ios/index.uts
Normal file
125
uni_modules/TencentCloud-Push/utssdk/app-ios/index.uts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import { TIMPushManager } from "TIMPush"
|
||||||
|
import { NSObject } from "DCloudUTSFoundation"
|
||||||
|
import PushListener from './push-listener.uts'
|
||||||
|
import { PushListenerOptions } from './push-listener-options.uts'
|
||||||
|
|
||||||
|
const LOG_PREFIX = 'Push |';
|
||||||
|
|
||||||
|
export class EVENT {
|
||||||
|
static MESSAGE_RECEIVED: string = 'message_received'
|
||||||
|
static MESSAGE_REVOKED: string = 'message_revoked'
|
||||||
|
static NOTIFICATION_CLICKED: string = 'notification_clicked'
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRunningPlatform(): void {
|
||||||
|
console.log(LOG_PREFIX, 'setRunningPlatform');
|
||||||
|
const param = new NSString("{\"runningPlatform\":2}");
|
||||||
|
TIMPushManager.callExperimentalAPI('setPushConfig', param = param, succ = (ext?: NSObject): void => {
|
||||||
|
let platform: string = ext as string;
|
||||||
|
console.log(LOG_PREFIX, 'setRunningPlatform ok. platform:', platform);
|
||||||
|
}, fail = (code?: Int32 ,desc?:String): void => {
|
||||||
|
console.log(LOG_PREFIX, `setRunningPlatform fail. code: ${code}, desc: ${desc}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let disableNotification = false;
|
||||||
|
|
||||||
|
export function disablePostNotificationInForeground(_disable: boolean): void {
|
||||||
|
console.log(LOG_PREFIX, 'disablePostNotificationInForeground', _disable);
|
||||||
|
disableNotification = _disable;
|
||||||
|
TIMPushManager.disablePostNotificationInForeground(disable = disableNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void {
|
||||||
|
if (SDKAppID == 0) {
|
||||||
|
onError?.(9010001, 'Invalid SDKAppID');
|
||||||
|
} else if (appKey == '') {
|
||||||
|
onError?.(9010002, 'Invalid appKey');
|
||||||
|
}
|
||||||
|
setRunningPlatform();
|
||||||
|
TIMPushManager.registerPush(SDKAppID.toInt32(), appKey = appKey, succ = (deviceToken?: Data): void => {
|
||||||
|
TIMPushManager.disablePostNotificationInForeground(disable = disableNotification);
|
||||||
|
console.log('devicetoken ->', deviceToken, deviceToken?.count);
|
||||||
|
onSuccess('');
|
||||||
|
}, fail = (code?: Int32 ,desc?:String): void => {
|
||||||
|
onError?.(code as number, desc as string);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unRegisterPush(onSuccess: () => void, onError: (errCode: number, errMsg: string) => void): void {
|
||||||
|
TIMPushManager.unRegisterPush((): void => {
|
||||||
|
onSuccess();
|
||||||
|
}, fail = (code?: Int32 ,desc?:String): void => {
|
||||||
|
onError(code as number, desc as string);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setRegistrationID(registrationID: string, onSuccess: () => void): void {
|
||||||
|
console.log(LOG_PREFIX, 'setRegistrationID', `registrationID:${registrationID}`);
|
||||||
|
TIMPushManager.setRegistrationID(registrationID, callback = (): void => {
|
||||||
|
console.log(LOG_PREFIX, 'setRegistrationID ok');
|
||||||
|
onSuccess();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRegistrationID(onSuccess: (registrationID: string) => void): void {
|
||||||
|
TIMPushManager.getRegistrationID((value ?: string): void => {
|
||||||
|
// 这里需要转一下,否则会有问题
|
||||||
|
let ret: string = value as string;
|
||||||
|
onSuccess(ret);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNotificationChannel(options: any, onSuccess: (data: string) => void): void {
|
||||||
|
// 空实现
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意!!!这里的 extInfo 不能写成 ext,否则会跟内部的 ext?:NSObject 有冲突;也不能写成 extension,否则会导致编译错误
|
||||||
|
export function getNotificationExtInfo(onSuccess: (extInfo: string) => void): void {
|
||||||
|
console.log(LOG_PREFIX, 'getNotificationExtInfo');
|
||||||
|
TIMPushManager.callExperimentalAPI('getNotificationExtInfo', param = {}, succ = (ext?: NSObject): void => {
|
||||||
|
let str: string = ext as string;
|
||||||
|
console.log(LOG_PREFIX, 'getNotificationExtInfo ok. ext:', str);
|
||||||
|
onSuccess(str);
|
||||||
|
}, fail = (code?: Int32 ,desc?:String): void => {
|
||||||
|
// 空实现
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const listenerMap = new Map<string, Array<(res: any) => void>>();
|
||||||
|
|
||||||
|
const pushListenerOptions: PushListenerOptions = {
|
||||||
|
listener: (eventName: string, data: any) => {
|
||||||
|
listenerMap.get(eventName)?.forEach(item => {
|
||||||
|
item(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const pushListener = new PushListener(pushListenerOptions);
|
||||||
|
|
||||||
|
@UTSJS.keepAlive
|
||||||
|
export function addPushListener(eventName: string, _listener: (res: any) => void): void {
|
||||||
|
console.log(LOG_PREFIX, 'addPushListener', eventName);
|
||||||
|
if(listenerMap.size === 0) {
|
||||||
|
TIMPushManager.addPushListener(listener = pushListener);
|
||||||
|
}
|
||||||
|
const listeners:Array<(res: any) => void> = [_listener];
|
||||||
|
listenerMap.get(eventName)?.forEach(item => {
|
||||||
|
listeners.push(item);
|
||||||
|
})
|
||||||
|
listenerMap.set(eventName, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removePushListener(eventName: string, _listener?: (res: any) => void): void {
|
||||||
|
console.log(LOG_PREFIX, 'removePushListener', eventName);
|
||||||
|
listenerMap.delete(eventName);
|
||||||
|
if(listenerMap.size === 0) {
|
||||||
|
TIMPushManager.removePushListener(listener = pushListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type PushListenerOptions = {
|
||||||
|
listener: (eventType: string, data: any) => void
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { TIMPushListener, TIMPushMessage} from "TIMPush"
|
||||||
|
import { PushListenerOptions } from './push-listener-options.uts';
|
||||||
|
|
||||||
|
const LOG_PREFIX: string = 'Push | PushListener';
|
||||||
|
export default class PushListener implements TIMPushListener {
|
||||||
|
private listener: (eventType: string, data: any) => void;
|
||||||
|
|
||||||
|
constructor(options: PushListenerOptions) {
|
||||||
|
this.listener = options.listener;
|
||||||
|
console.log(`${LOG_PREFIX} ok`);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvPushMessage(message: TIMPushMessage) {
|
||||||
|
this.listener('message_received', { data: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
onRevokePushMessage(messageID: string) {
|
||||||
|
this.listener('message_revoked', { data: messageID });
|
||||||
|
}
|
||||||
|
|
||||||
|
onNotificationClicked(ext: string) {
|
||||||
|
this.listener('notification_clicked', { data: ext });
|
||||||
|
}
|
||||||
|
}
|
||||||
11
uni_modules/TencentCloud-Push/utssdk/interface.uts
Normal file
11
uni_modules/TencentCloud-Push/utssdk/interface.uts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
interface Push {
|
||||||
|
setRegistrationID(registrationID: string, onSuccess: () => void): void,
|
||||||
|
registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void,
|
||||||
|
getRegistrationID(onSuccess: (registrationID: string) => void): void,
|
||||||
|
unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void,
|
||||||
|
getNotificationExtInfo(onSuccess: (extInfo: string) => void): void
|
||||||
|
addPushListener(eventName: string, listener: (res: any) => void): void
|
||||||
|
removePushListener(eventName: string, listener?: (res: any) => void): void
|
||||||
|
disablePostNotificationInForeground(disable: boolean): void
|
||||||
|
createNotificationChannel(options: any, onSuccess: (data: string) => void): void
|
||||||
|
}
|
||||||
17
uni_modules/uni-combox/changelog.md
Normal file
17
uni_modules/uni-combox/changelog.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## 1.0.2(2024-09-21)
|
||||||
|
- 新增 clearAble属性
|
||||||
|
## 1.0.1(2021-11-23)
|
||||||
|
- 优化 label、label-width 属性
|
||||||
|
## 1.0.0(2021-11-19)
|
||||||
|
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||||
|
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox)
|
||||||
|
## 0.1.0(2021-07-30)
|
||||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||||
|
## 0.0.6(2021-05-12)
|
||||||
|
- 新增 组件示例地址
|
||||||
|
## 0.0.5(2021-04-21)
|
||||||
|
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
|
||||||
|
## 0.0.4(2021-02-05)
|
||||||
|
- 优化 组件引用关系,通过uni_modules引用组件
|
||||||
|
## 0.0.3(2021-02-04)
|
||||||
|
- 调整为uni_modules目录规范
|
||||||
284
uni_modules/uni-combox/components/uni-combox/uni-combox.vue
Normal file
284
uni_modules/uni-combox/components/uni-combox/uni-combox.vue
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
|
||||||
|
<view v-if="label" class="uni-combox__label" :style="labelStyle">
|
||||||
|
<text>{{label}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="uni-combox__input-box">
|
||||||
|
<input class="uni-combox__input" type="text" :placeholder="placeholder" placeholder-class="uni-combox__input-plac"
|
||||||
|
v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" />
|
||||||
|
<uni-icons v-if="!inputVal || !clearAble" :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector">
|
||||||
|
</uni-icons>
|
||||||
|
<uni-icons v-if="inputVal && clearAble" type="clear" size="24" color="#999" @click="clean">
|
||||||
|
</uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="uni-combox__selector" v-if="showSelector">
|
||||||
|
<view class="uni-popper__arrow"></view>
|
||||||
|
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
|
||||||
|
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
|
||||||
|
<text>{{emptyTips}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index"
|
||||||
|
@click="onSelectorClick(index)">
|
||||||
|
<text>{{item}}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* Combox 组合输入框
|
||||||
|
* @description 组合输入框一般用于既可以输入也可以选择的场景
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
|
||||||
|
* @property {String} label 左侧文字
|
||||||
|
* @property {String} labelWidth 左侧内容宽度
|
||||||
|
* @property {String} placeholder 输入框占位符
|
||||||
|
* @property {Array} candidates 候选项列表
|
||||||
|
* @property {String} emptyTips 筛选结果为空时显示的文字
|
||||||
|
* @property {String} value 组合框的值
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'uniCombox',
|
||||||
|
emits: ['input', 'update:modelValue'],
|
||||||
|
props: {
|
||||||
|
clearAble: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
labelWidth: {
|
||||||
|
type: String,
|
||||||
|
default: 'auto'
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
candidates: {
|
||||||
|
type: Array,
|
||||||
|
default () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emptyTips: {
|
||||||
|
type: String,
|
||||||
|
default: '无匹配项'
|
||||||
|
},
|
||||||
|
// #ifndef VUE3
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
// #ifdef VUE3
|
||||||
|
modelValue: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showSelector: false,
|
||||||
|
inputVal: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
labelStyle() {
|
||||||
|
if (this.labelWidth === 'auto') {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return `width: ${this.labelWidth}`
|
||||||
|
},
|
||||||
|
filterCandidates() {
|
||||||
|
return this.candidates.filter((item) => {
|
||||||
|
return item.toString().indexOf(this.inputVal) > -1
|
||||||
|
})
|
||||||
|
},
|
||||||
|
filterCandidatesLength() {
|
||||||
|
return this.filterCandidates.length
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// #ifndef VUE3
|
||||||
|
value: {
|
||||||
|
handler(newVal) {
|
||||||
|
this.inputVal = newVal
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
// #ifdef VUE3
|
||||||
|
modelValue: {
|
||||||
|
handler(newVal) {
|
||||||
|
this.inputVal = newVal
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleSelector() {
|
||||||
|
this.showSelector = !this.showSelector
|
||||||
|
},
|
||||||
|
onFocus() {
|
||||||
|
this.showSelector = true
|
||||||
|
},
|
||||||
|
onBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showSelector = false
|
||||||
|
}, 153)
|
||||||
|
},
|
||||||
|
onSelectorClick(index) {
|
||||||
|
this.inputVal = this.filterCandidates[index]
|
||||||
|
this.showSelector = false
|
||||||
|
this.$emit('input', this.inputVal)
|
||||||
|
this.$emit('update:modelValue', this.inputVal)
|
||||||
|
},
|
||||||
|
onInput() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit('input', this.inputVal)
|
||||||
|
this.$emit('update:modelValue', this.inputVal)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clean() {
|
||||||
|
this.inputVal = ''
|
||||||
|
this.onInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.uni-combox {
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #DCDFE6;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
position: relative;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
// height: 40px;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
// border-bottom: solid 1px #DDDDDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__label {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
padding-right: 10px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__input-box {
|
||||||
|
position: relative;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__input-plac {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__selector {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* #endif */
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 12px);
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 2;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__selector-scroll {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
max-height: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__selector-empty,
|
||||||
|
.uni-combox__selector-item {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
/* #endif */
|
||||||
|
line-height: 36px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
// border-bottom: solid 1px #DDDDDD;
|
||||||
|
padding: 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__selector-item:hover {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__selector-empty:last-child,
|
||||||
|
.uni-combox__selector-item:last-child {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
border-bottom: none;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
// picker 弹出层通用的指示小三角
|
||||||
|
.uni-popper__arrow,
|
||||||
|
.uni-popper__arrow::after {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-color: transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popper__arrow {
|
||||||
|
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
|
||||||
|
top: -6px;
|
||||||
|
left: 10%;
|
||||||
|
margin-right: 3px;
|
||||||
|
border-top-width: 0;
|
||||||
|
border-bottom-color: #EBEEF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popper__arrow::after {
|
||||||
|
content: " ";
|
||||||
|
top: 1px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border-top-width: 0;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-combox__no-border {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
88
uni_modules/uni-combox/package.json
Normal file
88
uni_modules/uni-combox/package.json
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"id": "uni-combox",
|
||||||
|
"displayName": "uni-combox 组合框",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "可以选择也可以输入的表单项 ",
|
||||||
|
"keywords": [
|
||||||
|
"uni-ui",
|
||||||
|
"uniui",
|
||||||
|
"combox",
|
||||||
|
"组合框",
|
||||||
|
"select"
|
||||||
|
],
|
||||||
|
"repository": "https://github.com/dcloudio/uni-ui",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": ""
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"example": "../../temps/example_temps"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "无",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||||
|
"type": "component-vue"
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uni-scss",
|
||||||
|
"uni-icons"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y",
|
||||||
|
"alipay": "n"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "n"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
},
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
uni_modules/uni-combox/readme.md
Normal file
11
uni_modules/uni-combox/readme.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Combox 组合框
|
||||||
|
> **组件名:uni-combox**
|
||||||
|
> 代码块: `uCombox`
|
||||||
|
|
||||||
|
|
||||||
|
组合框组件。
|
||||||
|
|
||||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox)
|
||||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||||
51
uni_modules/uni-data-checkbox/changelog.md
Normal file
51
uni_modules/uni-data-checkbox/changelog.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
## 1.0.6(2024-10-22)
|
||||||
|
- 新增 当 multiple 为 false 且传递的 value 为 数组时,使用数组第一项用作反显
|
||||||
|
## 1.0.5(2024-03-20)
|
||||||
|
- 修复 单选模式下选中样式不生效的bug
|
||||||
|
## 1.0.4(2024-01-27)
|
||||||
|
- 修复 修复错别字chagne为change
|
||||||
|
## 1.0.3(2022-09-16)
|
||||||
|
- 可以使用 uni-scss 控制主题色
|
||||||
|
## 1.0.2(2022-06-30)
|
||||||
|
- 优化 在 uni-forms 中的依赖注入方式
|
||||||
|
## 1.0.1(2022-02-07)
|
||||||
|
- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
|
||||||
|
## 1.0.0(2021-11-19)
|
||||||
|
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
|
||||||
|
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
|
||||||
|
## 0.2.5(2021-08-23)
|
||||||
|
- 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题
|
||||||
|
## 0.2.4(2021-08-17)
|
||||||
|
- 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题
|
||||||
|
## 0.2.3(2021-08-11)
|
||||||
|
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
|
||||||
|
## 0.2.2(2021-07-30)
|
||||||
|
- 优化 在uni-forms组件,与label不对齐的问题
|
||||||
|
## 0.2.1(2021-07-27)
|
||||||
|
- 修复 单选默认值为0不能选中的Bug
|
||||||
|
## 0.2.0(2021-07-13)
|
||||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||||
|
## 0.1.11(2021-07-06)
|
||||||
|
- 优化 删除无用日志
|
||||||
|
## 0.1.10(2021-07-05)
|
||||||
|
- 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题
|
||||||
|
## 0.1.9(2021-07-05)
|
||||||
|
- 修复 nvue 黑框样式问题
|
||||||
|
## 0.1.8(2021-06-28)
|
||||||
|
- 修复 selectedTextColor 属性不生效的Bug
|
||||||
|
## 0.1.7(2021-06-02)
|
||||||
|
- 新增 map 属性,可以方便映射text/value属性
|
||||||
|
## 0.1.6(2021-05-26)
|
||||||
|
- 修复 不关联服务空间的情况下组件报错的Bug
|
||||||
|
## 0.1.5(2021-05-12)
|
||||||
|
- 新增 组件示例地址
|
||||||
|
## 0.1.4(2021-04-09)
|
||||||
|
- 修复 nvue 下无法选中的问题
|
||||||
|
## 0.1.3(2021-03-22)
|
||||||
|
- 新增 disabled属性
|
||||||
|
## 0.1.2(2021-02-24)
|
||||||
|
- 优化 默认颜色显示
|
||||||
|
## 0.1.1(2021-02-24)
|
||||||
|
- 新增 支持nvue
|
||||||
|
## 0.1.0(2021-02-18)
|
||||||
|
- “暂无数据”显示居中
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
|
||||||
|
const events = {
|
||||||
|
load: 'load',
|
||||||
|
error: 'error'
|
||||||
|
}
|
||||||
|
const pageMode = {
|
||||||
|
add: 'add',
|
||||||
|
replace: 'replace'
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = [
|
||||||
|
'pageCurrent',
|
||||||
|
'pageSize',
|
||||||
|
'collection',
|
||||||
|
'action',
|
||||||
|
'field',
|
||||||
|
'getcount',
|
||||||
|
'orderby',
|
||||||
|
'where'
|
||||||
|
]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
listData: this.getone ? {} : [],
|
||||||
|
paginationInternal: {
|
||||||
|
current: this.pageCurrent,
|
||||||
|
size: this.pageSize,
|
||||||
|
count: 0
|
||||||
|
},
|
||||||
|
errorMessage: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
let db = null;
|
||||||
|
let dbCmd = null;
|
||||||
|
|
||||||
|
if(this.collection){
|
||||||
|
this.db = uniCloud.database();
|
||||||
|
this.dbCmd = this.db.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isEnded = false
|
||||||
|
|
||||||
|
this.$watch(() => {
|
||||||
|
let al = []
|
||||||
|
attrs.forEach(key => {
|
||||||
|
al.push(this[key])
|
||||||
|
})
|
||||||
|
return al
|
||||||
|
}, (newValue, oldValue) => {
|
||||||
|
this.paginationInternal.pageSize = this.pageSize
|
||||||
|
|
||||||
|
let needReset = false
|
||||||
|
for (let i = 2; i < newValue.length; i++) {
|
||||||
|
if (newValue[i] != oldValue[i]) {
|
||||||
|
needReset = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needReset) {
|
||||||
|
this.clear()
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
if (newValue[0] != oldValue[0]) {
|
||||||
|
this.paginationInternal.current = this.pageCurrent
|
||||||
|
}
|
||||||
|
|
||||||
|
this._execLoadData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
this._debugDataList = []
|
||||||
|
if (!window.unidev) {
|
||||||
|
window.unidev = {
|
||||||
|
clientDB: {
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unidev.clientDB.data.push(this._debugDataList)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-TOUTIAO
|
||||||
|
let changeName
|
||||||
|
let events = this.$scope.dataset.eventOpts
|
||||||
|
for (let i = 0; i < events.length; i++) {
|
||||||
|
let event = events[i]
|
||||||
|
if (event[0].includes('^load')) {
|
||||||
|
changeName = event[1][0][0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changeName) {
|
||||||
|
let parent = this.$parent
|
||||||
|
let maxDepth = 16
|
||||||
|
this._changeDataFunction = null
|
||||||
|
while (parent && maxDepth > 0) {
|
||||||
|
let fun = parent[changeName]
|
||||||
|
if (fun && typeof fun === 'function') {
|
||||||
|
this._changeDataFunction = fun
|
||||||
|
maxDepth = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parent = parent.$parent
|
||||||
|
maxDepth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// if (!this.manual) {
|
||||||
|
// this.loadData()
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
// #ifdef H5
|
||||||
|
beforeDestroy() {
|
||||||
|
if (process.env.NODE_ENV === 'development' && window.unidev) {
|
||||||
|
let cd = this._debugDataList
|
||||||
|
let dl = unidev.clientDB.data
|
||||||
|
for (let i = dl.length - 1; i >= 0; i--) {
|
||||||
|
if (dl[i] === cd) {
|
||||||
|
dl.splice(i, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
methods: {
|
||||||
|
loadData(args1, args2) {
|
||||||
|
let callback = null
|
||||||
|
if (typeof args1 === 'object') {
|
||||||
|
if (args1.clear) {
|
||||||
|
this.clear()
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
if (args1.current !== undefined) {
|
||||||
|
this.paginationInternal.current = args1.current
|
||||||
|
}
|
||||||
|
if (typeof args2 === 'function') {
|
||||||
|
callback = args2
|
||||||
|
}
|
||||||
|
} else if (typeof args1 === 'function') {
|
||||||
|
callback = args1
|
||||||
|
}
|
||||||
|
|
||||||
|
this._execLoadData(callback)
|
||||||
|
},
|
||||||
|
loadMore() {
|
||||||
|
if (this._isEnded) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._execLoadData()
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.clear()
|
||||||
|
this._execLoadData()
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this._isEnded = false
|
||||||
|
this.listData = []
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.paginationInternal.current = 1
|
||||||
|
},
|
||||||
|
remove(id, {
|
||||||
|
action,
|
||||||
|
callback,
|
||||||
|
confirmTitle,
|
||||||
|
confirmContent
|
||||||
|
} = {}) {
|
||||||
|
if (!id || !id.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uni.showModal({
|
||||||
|
title: confirmTitle || '提示',
|
||||||
|
content: confirmContent || '是否删除该数据',
|
||||||
|
showCancel: true,
|
||||||
|
success: (res) => {
|
||||||
|
if (!res.confirm) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._execRemove(id, action, callback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_execLoadData(callback) {
|
||||||
|
if (this.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
this.errorMessage = ''
|
||||||
|
|
||||||
|
this._getExec().then((res) => {
|
||||||
|
this.loading = false
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
count
|
||||||
|
} = res.result
|
||||||
|
this._isEnded = data.length < this.pageSize
|
||||||
|
|
||||||
|
callback && callback(data, this._isEnded)
|
||||||
|
this._dispatchEvent(events.load, data)
|
||||||
|
|
||||||
|
if (this.getone) {
|
||||||
|
this.listData = data.length ? data[0] : undefined
|
||||||
|
} else if (this.pageData === pageMode.add) {
|
||||||
|
this.listData.push(...data)
|
||||||
|
if (this.listData.length) {
|
||||||
|
this.paginationInternal.current++
|
||||||
|
}
|
||||||
|
} else if (this.pageData === pageMode.replace) {
|
||||||
|
this.listData = data
|
||||||
|
this.paginationInternal.count = count
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
this._debugDataList.length = 0
|
||||||
|
this._debugDataList.push(...JSON.parse(JSON.stringify(this.listData)))
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
}).catch((err) => {
|
||||||
|
this.loading = false
|
||||||
|
this.errorMessage = err
|
||||||
|
callback && callback()
|
||||||
|
this.$emit(events.error, err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_getExec() {
|
||||||
|
let exec = this.db
|
||||||
|
if (this.action) {
|
||||||
|
exec = exec.action(this.action)
|
||||||
|
}
|
||||||
|
|
||||||
|
exec = exec.collection(this.collection)
|
||||||
|
|
||||||
|
if (!(!this.where || !Object.keys(this.where).length)) {
|
||||||
|
exec = exec.where(this.where)
|
||||||
|
}
|
||||||
|
if (this.field) {
|
||||||
|
exec = exec.field(this.field)
|
||||||
|
}
|
||||||
|
if (this.orderby) {
|
||||||
|
exec = exec.orderBy(this.orderby)
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
current,
|
||||||
|
size
|
||||||
|
} = this.paginationInternal
|
||||||
|
exec = exec.skip(size * (current - 1)).limit(size).get({
|
||||||
|
getCount: this.getcount
|
||||||
|
})
|
||||||
|
|
||||||
|
return exec
|
||||||
|
},
|
||||||
|
_execRemove(id, action, callback) {
|
||||||
|
if (!this.collection || !id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ids = Array.isArray(id) ? id : [id]
|
||||||
|
if (!ids.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showLoading({
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
|
||||||
|
let exec = this.db
|
||||||
|
if (action) {
|
||||||
|
exec = exec.action(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
exec.collection(this.collection).where({
|
||||||
|
_id: dbCmd.in(ids)
|
||||||
|
}).remove().then((res) => {
|
||||||
|
callback && callback(res.result)
|
||||||
|
if (this.pageData === pageMode.replace) {
|
||||||
|
this.refresh()
|
||||||
|
} else {
|
||||||
|
this.removeData(ids)
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
uni.showModal({
|
||||||
|
content: err.message,
|
||||||
|
showCancel: false
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeData(ids) {
|
||||||
|
let il = ids.slice(0)
|
||||||
|
let dl = this.listData
|
||||||
|
for (let i = dl.length - 1; i >= 0; i--) {
|
||||||
|
let index = il.indexOf(dl[i]._id)
|
||||||
|
if (index >= 0) {
|
||||||
|
dl.splice(i, 1)
|
||||||
|
il.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_dispatchEvent(type, data) {
|
||||||
|
if (this._changeDataFunction) {
|
||||||
|
this._changeDataFunction(data, this._isEnded)
|
||||||
|
} else {
|
||||||
|
this.$emit(type, data, this._isEnded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,853 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}">
|
||||||
|
<template v-if="!isLocal">
|
||||||
|
<view class="uni-data-loading">
|
||||||
|
<uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18"
|
||||||
|
:content-text="contentText"></uni-load-more>
|
||||||
|
<text v-else>{{mixinDatacomErrorMessage}}</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}"
|
||||||
|
@change="change">
|
||||||
|
<label class="checklist-box"
|
||||||
|
:class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
|
||||||
|
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
|
||||||
|
<checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''"
|
||||||
|
:checked="item.selected" />
|
||||||
|
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')"
|
||||||
|
class="checkbox__inner" :style="item.styleIcon">
|
||||||
|
<view class="checkbox__inner-icon"></view>
|
||||||
|
</view>
|
||||||
|
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
|
||||||
|
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
|
||||||
|
<view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view>
|
||||||
|
</view>
|
||||||
|
</label>
|
||||||
|
</checkbox-group>
|
||||||
|
<radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="change">
|
||||||
|
<label class="checklist-box"
|
||||||
|
:class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']"
|
||||||
|
:style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index">
|
||||||
|
<radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''"
|
||||||
|
:checked="item.selected" />
|
||||||
|
<view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner"
|
||||||
|
:style="item.styleBackgroud">
|
||||||
|
<view class="radio__inner-icon" :style="item.styleIcon"></view>
|
||||||
|
</view>
|
||||||
|
<view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}">
|
||||||
|
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
|
||||||
|
<view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view>
|
||||||
|
</view>
|
||||||
|
</label>
|
||||||
|
</radio-group>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* DataChecklist 数据选择器
|
||||||
|
* @description 通过数据渲染 checkbox 和 radio
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
|
||||||
|
* @property {String} mode = [default| list | button | tag] 显示模式
|
||||||
|
* @value default 默认横排模式
|
||||||
|
* @value list 列表模式
|
||||||
|
* @value button 按钮模式
|
||||||
|
* @value tag 标签模式
|
||||||
|
* @property {Boolean} multiple = [true|false] 是否多选
|
||||||
|
* @property {Array|String|Number} value 默认值
|
||||||
|
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
|
||||||
|
* @property {Number|String} min 最小选择个数 ,multiple为true时生效
|
||||||
|
* @property {Number|String} max 最大选择个数 ,multiple为true时生效
|
||||||
|
* @property {Boolean} wrap 是否换行显示
|
||||||
|
* @property {String} icon = [left|right] list 列表模式下icon显示位置
|
||||||
|
* @property {Boolean} selectedColor 选中颜色
|
||||||
|
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
|
||||||
|
* @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示
|
||||||
|
* @property {Object} map 字段映射, 默认 map={text:'text',value:'value'}
|
||||||
|
* @value left 左侧显示
|
||||||
|
* @value right 右侧显示
|
||||||
|
* @event {Function} change 选中发生变化触发
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'uniDataChecklist',
|
||||||
|
mixins: [uniCloud.mixinDatacom || {}],
|
||||||
|
emits: ['input', 'update:modelValue', 'change'],
|
||||||
|
props: {
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'default'
|
||||||
|
},
|
||||||
|
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: [Array, String, Number],
|
||||||
|
default () {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO vue3
|
||||||
|
modelValue: {
|
||||||
|
type: [Array, String, Number],
|
||||||
|
default () {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
localdata: {
|
||||||
|
type: Array,
|
||||||
|
default () {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: 'left'
|
||||||
|
},
|
||||||
|
selectedColor: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
selectedTextColor: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
emptyText: {
|
||||||
|
type: String,
|
||||||
|
default: '暂无数据'
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
text: 'text',
|
||||||
|
value: 'value'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
localdata: {
|
||||||
|
handler(newVal) {
|
||||||
|
this.range = newVal
|
||||||
|
this.dataList = this.getDataList(this.getSelectedValue(newVal))
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
mixinDatacomResData(newVal) {
|
||||||
|
this.range = newVal
|
||||||
|
this.dataList = this.getDataList(this.getSelectedValue(newVal))
|
||||||
|
},
|
||||||
|
value(newVal) {
|
||||||
|
this.dataList = this.getDataList(newVal)
|
||||||
|
// fix by mehaotian is_reset 在 uni-forms 中定义
|
||||||
|
// if(!this.is_reset){
|
||||||
|
// this.is_reset = false
|
||||||
|
// this.formItem && this.formItem.setValue(newVal)
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
modelValue(newVal) {
|
||||||
|
this.dataList = this.getDataList(newVal);
|
||||||
|
// if(!this.is_reset){
|
||||||
|
// this.is_reset = false
|
||||||
|
// this.formItem && this.formItem.setValue(newVal)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dataList: [],
|
||||||
|
range: [],
|
||||||
|
contentText: {
|
||||||
|
contentdown: '查看更多',
|
||||||
|
contentrefresh: '加载中',
|
||||||
|
contentnomore: '没有更多'
|
||||||
|
},
|
||||||
|
isLocal: true,
|
||||||
|
styles: {
|
||||||
|
selectedColor: '#2979ff',
|
||||||
|
selectedTextColor: '#666',
|
||||||
|
},
|
||||||
|
isTop: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dataValue() {
|
||||||
|
if (this.value === '') return this.modelValue
|
||||||
|
if (this.modelValue === '') return this.value
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// this.form = this.getForm('uniForms')
|
||||||
|
// this.formItem = this.getForm('uniFormsItem')
|
||||||
|
// this.formItem && this.formItem.setValue(this.value)
|
||||||
|
|
||||||
|
// if (this.formItem) {
|
||||||
|
// this.isTop = 6
|
||||||
|
// if (this.formItem.name) {
|
||||||
|
// // 如果存在name添加默认值,否则formData 中不存在这个字段不校验
|
||||||
|
// if(!this.is_reset){
|
||||||
|
// this.is_reset = false
|
||||||
|
// this.formItem.setValue(this.dataValue)
|
||||||
|
// }
|
||||||
|
// this.rename = this.formItem.name
|
||||||
|
// this.form.inputChildrens.push(this)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (this.localdata && this.localdata.length !== 0) {
|
||||||
|
this.isLocal = true
|
||||||
|
this.range = this.localdata
|
||||||
|
this.dataList = this.getDataList(this.getSelectedValue(this.range))
|
||||||
|
} else {
|
||||||
|
if (this.collection) {
|
||||||
|
this.isLocal = false
|
||||||
|
this.loadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadData() {
|
||||||
|
this.mixinDatacomGet().then(res => {
|
||||||
|
this.mixinDatacomResData = res.result.data
|
||||||
|
if (this.mixinDatacomResData.length === 0) {
|
||||||
|
this.isLocal = false
|
||||||
|
this.mixinDatacomErrorMessage = this.emptyText
|
||||||
|
} else {
|
||||||
|
this.isLocal = true
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
this.mixinDatacomErrorMessage = err.message
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取父元素实例
|
||||||
|
*/
|
||||||
|
getForm(name = 'uniForms') {
|
||||||
|
let parent = this.$parent;
|
||||||
|
let parentName = parent.$options.name;
|
||||||
|
while (parentName !== name) {
|
||||||
|
parent = parent.$parent;
|
||||||
|
if (!parent) return false
|
||||||
|
parentName = parent.$options.name;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
},
|
||||||
|
change(e) {
|
||||||
|
const values = e.detail.value
|
||||||
|
|
||||||
|
let detail = {
|
||||||
|
value: [],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.multiple) {
|
||||||
|
this.range.forEach(item => {
|
||||||
|
|
||||||
|
if (values.includes(item[this.map.value] + '')) {
|
||||||
|
detail.value.push(item[this.map.value])
|
||||||
|
detail.data.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const range = this.range.find(item => (item[this.map.value] + '') === values)
|
||||||
|
if (range) {
|
||||||
|
detail = {
|
||||||
|
value: range[this.map.value],
|
||||||
|
data: range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// this.formItem && this.formItem.setValue(detail.value)
|
||||||
|
// TODO 兼容 vue2
|
||||||
|
this.$emit('input', detail.value);
|
||||||
|
// // TOTO 兼容 vue3
|
||||||
|
this.$emit('update:modelValue', detail.value);
|
||||||
|
this.$emit('change', {
|
||||||
|
detail
|
||||||
|
})
|
||||||
|
if (this.multiple) {
|
||||||
|
// 如果 v-model 没有绑定 ,则走内部逻辑
|
||||||
|
// if (this.value.length === 0) {
|
||||||
|
this.dataList = this.getDataList(detail.value, true)
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
this.dataList = this.getDataList(detail.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取渲染的新数组
|
||||||
|
* @param {Object} value 选中内容
|
||||||
|
*/
|
||||||
|
getDataList(value) {
|
||||||
|
// 解除引用关系,破坏原引用关系,避免污染源数据
|
||||||
|
let dataList = JSON.parse(JSON.stringify(this.range))
|
||||||
|
let list = []
|
||||||
|
if (this.multiple) {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
value = []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Array.isArray(value) && value.length) {
|
||||||
|
value = value[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataList.forEach((item, index) => {
|
||||||
|
item.disabled = item.disable || item.disabled || false
|
||||||
|
if (this.multiple) {
|
||||||
|
if (value.length > 0) {
|
||||||
|
let have = value.find(val => val === item[this.map.value])
|
||||||
|
item.selected = have !== undefined
|
||||||
|
} else {
|
||||||
|
item.selected = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.selected = value === item[this.map.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item)
|
||||||
|
})
|
||||||
|
return this.setRange(list)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 处理最大最小值
|
||||||
|
* @param {Object} list
|
||||||
|
*/
|
||||||
|
setRange(list) {
|
||||||
|
let selectList = list.filter(item => item.selected)
|
||||||
|
let min = Number(this.min) || 0
|
||||||
|
let max = Number(this.max) || ''
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
if (this.multiple) {
|
||||||
|
if (selectList.length <= min) {
|
||||||
|
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
|
||||||
|
if (have !== undefined) {
|
||||||
|
item.disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectList.length >= max && max !== '') {
|
||||||
|
let have = selectList.find(val => val[this.map.value] === item[this.map.value])
|
||||||
|
if (have === undefined) {
|
||||||
|
item.disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setStyles(item, index)
|
||||||
|
list[index] = item
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 设置 class
|
||||||
|
* @param {Object} item
|
||||||
|
* @param {Object} index
|
||||||
|
*/
|
||||||
|
setStyles(item, index) {
|
||||||
|
// 设置自定义样式
|
||||||
|
item.styleBackgroud = this.setStyleBackgroud(item)
|
||||||
|
item.styleIcon = this.setStyleIcon(item)
|
||||||
|
item.styleIconText = this.setStyleIconText(item)
|
||||||
|
item.styleRightIcon = this.setStyleRightIcon(item)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选中值
|
||||||
|
* @param {Object} range
|
||||||
|
*/
|
||||||
|
getSelectedValue(range) {
|
||||||
|
if (!this.multiple) return this.dataValue
|
||||||
|
let selectedArr = []
|
||||||
|
range.forEach((item) => {
|
||||||
|
if (item.selected) {
|
||||||
|
selectedArr.push(item[this.map.value])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this.dataValue.length > 0 ? this.dataValue : selectedArr
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置背景样式
|
||||||
|
*/
|
||||||
|
setStyleBackgroud(item) {
|
||||||
|
let styles = {}
|
||||||
|
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
|
||||||
|
if (this.selectedColor) {
|
||||||
|
if (this.mode !== 'list') {
|
||||||
|
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
|
||||||
|
}
|
||||||
|
if (this.mode === 'tag') {
|
||||||
|
styles['background-color'] = item.selected ? selectedColor : '#f5f5f5'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let classles = ''
|
||||||
|
for (let i in styles) {
|
||||||
|
classles += `${i}:${styles[i]};`
|
||||||
|
}
|
||||||
|
return classles
|
||||||
|
},
|
||||||
|
setStyleIcon(item) {
|
||||||
|
let styles = {}
|
||||||
|
let classles = ''
|
||||||
|
if (this.selectedColor) {
|
||||||
|
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
|
||||||
|
styles['background-color'] = item.selected ? selectedColor : '#fff'
|
||||||
|
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
|
||||||
|
|
||||||
|
if (!item.selected && item.disabled) {
|
||||||
|
styles['background-color'] = '#F2F6FC'
|
||||||
|
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i in styles) {
|
||||||
|
classles += `${i}:${styles[i]};`
|
||||||
|
}
|
||||||
|
return classles
|
||||||
|
},
|
||||||
|
setStyleIconText(item) {
|
||||||
|
let styles = {}
|
||||||
|
let classles = ''
|
||||||
|
if (this.selectedColor) {
|
||||||
|
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff'
|
||||||
|
if (this.mode === 'tag') {
|
||||||
|
styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : '#fff') : '#666'
|
||||||
|
} else {
|
||||||
|
styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : selectedColor) : '#666'
|
||||||
|
}
|
||||||
|
if (!item.selected && item.disabled) {
|
||||||
|
styles.color = '#999'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i in styles) {
|
||||||
|
classles += `${i}:${styles[i]};`
|
||||||
|
}
|
||||||
|
return classles
|
||||||
|
},
|
||||||
|
setStyleRightIcon(item) {
|
||||||
|
let styles = {}
|
||||||
|
let classles = ''
|
||||||
|
if (this.mode === 'list') {
|
||||||
|
styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6'
|
||||||
|
}
|
||||||
|
for (let i in styles) {
|
||||||
|
classles += `${i}:${styles[i]};`
|
||||||
|
}
|
||||||
|
|
||||||
|
return classles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
$uni-primary: #2979ff !default;
|
||||||
|
$border-color: #DCDFE6;
|
||||||
|
$disable: 0.4;
|
||||||
|
|
||||||
|
@mixin flex {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-data-loading {
|
||||||
|
@include flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 36px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-data-checklist {
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
// 多选样式
|
||||||
|
.checklist-group {
|
||||||
|
@include flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&.is-list {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-box {
|
||||||
|
@include flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
margin: 5px 0;
|
||||||
|
margin-right: 25px;
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文字样式
|
||||||
|
.checklist-content {
|
||||||
|
@include flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-left: 5px;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkobx__list {
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-color: #007aff;
|
||||||
|
border-right-style: solid;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: #007aff;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
height: 12px;
|
||||||
|
width: 6px;
|
||||||
|
left: -5px;
|
||||||
|
transform-origin: center;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多选样式
|
||||||
|
.checkbox__inner {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* #endif */
|
||||||
|
position: relative;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.checkbox__inner-icon {
|
||||||
|
position: absolute;
|
||||||
|
/* #ifdef APP-NVUE */
|
||||||
|
top: 2px;
|
||||||
|
/* #endif */
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
top: 1px;
|
||||||
|
/* #endif */
|
||||||
|
left: 5px;
|
||||||
|
height: 8px;
|
||||||
|
width: 4px;
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-color: #fff;
|
||||||
|
border-right-style: solid;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: center;
|
||||||
|
transform: rotate(40deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单选样式
|
||||||
|
.radio__inner {
|
||||||
|
@include flex;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* #endif */
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.radio__inner-icon {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认样式
|
||||||
|
&.is--default {
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
&.is-disable {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
.checkbox__inner {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
border-color: $border-color;
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
border-color: $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中
|
||||||
|
&.is-checked {
|
||||||
|
.checkbox__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
|
||||||
|
.checkbox__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
|
||||||
|
.radio__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: $uni-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中禁用
|
||||||
|
&.is-disable {
|
||||||
|
.checkbox__inner {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮样式
|
||||||
|
&.is--button {
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px $border-color solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
&.is-disable {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
border: 1px #eee solid;
|
||||||
|
opacity: $disable;
|
||||||
|
|
||||||
|
.checkbox__inner {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
border-color: $border-color;
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
border-color: $border-color;
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-checked {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
|
||||||
|
.checkbox__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
|
||||||
|
.checkbox__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
|
||||||
|
.radio__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: $uni-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中禁用
|
||||||
|
&.is-disable {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标签样式
|
||||||
|
&.is--tag {
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px $border-color solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
margin: 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
&.is-disable {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-checked {
|
||||||
|
background-color: $uni-primary;
|
||||||
|
border-color: $uni-primary;
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表样式
|
||||||
|
&.is--list {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
padding: 10px 15px;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&.is-list-border {
|
||||||
|
border-top: 1px #eee solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
&.is-disable {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
.checkbox__inner {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
border-color: $border-color;
|
||||||
|
/* #ifdef H5 */
|
||||||
|
cursor: not-allowed;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-checked {
|
||||||
|
.checkbox__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
|
||||||
|
.checkbox__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio__inner {
|
||||||
|
border-color: $uni-primary;
|
||||||
|
.radio__inner-icon {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: $uni-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
color: $uni-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-content {
|
||||||
|
.checkobx__list {
|
||||||
|
opacity: 1;
|
||||||
|
border-color: $uni-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中禁用
|
||||||
|
&.is-disable {
|
||||||
|
.checkbox__inner {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checklist-text {
|
||||||
|
opacity: $disable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
87
uni_modules/uni-data-checkbox/package.json
Normal file
87
uni_modules/uni-data-checkbox/package.json
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"id": "uni-data-checkbox",
|
||||||
|
"displayName": "uni-data-checkbox 数据选择器",
|
||||||
|
"version": "1.0.6",
|
||||||
|
"description": "通过数据驱动的单选框和复选框",
|
||||||
|
"keywords": [
|
||||||
|
"uni-ui",
|
||||||
|
"checkbox",
|
||||||
|
"单选",
|
||||||
|
"多选",
|
||||||
|
"单选多选"
|
||||||
|
],
|
||||||
|
"repository": "https://github.com/dcloudio/uni-ui",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.1"
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"example": "../../temps/example_temps"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "无",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
|
||||||
|
"type": "component-vue"
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": ["uni-load-more","uni-scss"],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y",
|
||||||
|
"alipay": "n"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y",
|
||||||
|
"app-harmony": "u",
|
||||||
|
"app-uvue": "u"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
},
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
uni_modules/uni-data-checkbox/readme.md
Normal file
18
uni_modules/uni-data-checkbox/readme.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## DataCheckbox 数据驱动的单选复选框
|
||||||
|
> **组件名:uni-data-checkbox**
|
||||||
|
> 代码块: `uDataCheckbox`
|
||||||
|
|
||||||
|
|
||||||
|
本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括:
|
||||||
|
|
||||||
|
1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能
|
||||||
|
2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验
|
||||||
|
3. 本组件合并了单选多选
|
||||||
|
4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性
|
||||||
|
|
||||||
|
在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
|
||||||
|
|
||||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox)
|
||||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||||
45
utils/media.js
Normal file
45
utils/media.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 封装 uni.chooseImage,返回 Promise,便于 async/await 使用
|
||||||
|
* @param {Object} options - 透传给 uni.chooseImage 的配置
|
||||||
|
* @returns {Promise<Array<string>>} 返回选中的临时图片路径数组
|
||||||
|
*/
|
||||||
|
export function chooseImage(options = {}) {
|
||||||
|
// 默认配置
|
||||||
|
const defaultOptions = {
|
||||||
|
count: 1,
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.chooseImage({
|
||||||
|
...defaultOptions,
|
||||||
|
success(res) {
|
||||||
|
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
|
||||||
|
resolve(res.tempFilePaths)
|
||||||
|
} else {
|
||||||
|
reject(new Error('未选择任何图片'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
// 统一错误处理(如权限被拒、用户取消等)
|
||||||
|
let msg = '选择图片失败'
|
||||||
|
if (err.errMsg.includes('cancel')) {
|
||||||
|
msg = '用户取消选择'
|
||||||
|
} else if (
|
||||||
|
err.errMsg.includes('auth deny') ||
|
||||||
|
err.errMsg.includes('permission')
|
||||||
|
) {
|
||||||
|
msg = '请在设置中开启相册或相机权限'
|
||||||
|
}
|
||||||
|
// 可选:自动弹出 toast 提示
|
||||||
|
uni.showToast({
|
||||||
|
title: msg,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
reject(new Error(msg))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -132,3 +132,4 @@ export const uploadMultipleFiles = (filePaths, config = {}) => {
|
|||||||
)
|
)
|
||||||
return Promise.all(uploadPromises)
|
return Promise.all(uploadPromises)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const hideLoading = () => {
|
|||||||
* @param {string} type - 'success' | 'error' | 'warning' | 'none'
|
* @param {string} type - 'success' | 'error' | 'warning' | 'none'
|
||||||
* @param {number} duration - 持续时间(毫秒)
|
* @param {number} duration - 持续时间(毫秒)
|
||||||
*/
|
*/
|
||||||
const showToast = (message, type = 'none', duration = 2000) => {
|
const showToast = (message, type = 'none', duration = 1800) => {
|
||||||
let icon = 'none'
|
let icon = 'none'
|
||||||
if (type === 'success') icon = 'success'
|
if (type === 'success') icon = 'success'
|
||||||
if (type === 'error') icon = 'error'
|
if (type === 'error') icon = 'error'
|
||||||
|
|||||||
15
vue.config.js
Normal file
15
vue.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default;
|
||||||
|
module.exports = {
|
||||||
|
parallel: false,
|
||||||
|
configureWebpack: {
|
||||||
|
plugins: [
|
||||||
|
ScriptSetup({
|
||||||
|
/* options */
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
chainWebpack(config) {
|
||||||
|
// disable type check and let `vue-tsc` handles it
|
||||||
|
config.plugins.delete('fork-ts-checker');
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user