新增功能

This commit is contained in:
Your Name
2026-03-04 15:32:30 +08:00
parent a07e844c47
commit ab77f5488d
2266 changed files with 177942 additions and 3444 deletions
+329
View File
@@ -0,0 +1,329 @@
<template>
<div v-if="callInfoContextValue.callStatus !== CallStatus.IDLE" :style="[{ visibility: floatWindowContextValue.isFloatWindow ? 'hidden' : '' }]" id="tuicallkit-id" :class="[bodyStyle, miniMizedDeskStyle, mobileVideoStyle, mobileAudioStyle]">
<SingleCall v-if="!callInfoContextValue.isGroupCall" class="singCall"></SingleCall>
<GroupCall v-else class="singCall"></GroupCall>
<AISubtitle />
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, reactive, onUnmounted, toRefs, watchEffect, provide } from '../adapter-vue';
import SingleCall from './components/SingleCall/SingleCall.vue';
import GroupCall from './components/GroupCall/GroupCall.vue';
import { TUIGlobal, TUIStore, StoreName, TUICallKitAPI, NAME, CallStatus, CallMediaType, VideoDisplayMode, VideoResolution } from '../TUICallService/index';
import {
CallInfoContextKey,
CallerUserInfoContextKey,
UserInfoExcludeVolumeContextKey,
FloatWindowContextKey,
TCallerUserInfoValue,
TFloatWindowContextValue,
CustomUIConfigContextKey,
translateContextKey,
} from './context';
import { isEmpty } from './util/isEmpty';
const isMobile = !TUIGlobal.isPC;
const bodyStyle = isMobile ? 'TUICallKit-mobile transition-animation' : 'TUICallKit-desktop';
const miniMizedDeskStyle = ref('');
const mobileAudioStyle = ref('');
const mobileVideoStyle = ref('');
const isShowFloatWindow = ref(false);
const props = withDefaults(
defineProps<{
beforeCalling?: (...args: any[]) => void;
afterCalling?: (...args: any[]) => void;
onMinimized?: (...args: any[]) => void;
onMessageSentByMe?: (...args: any[]) => void;
kickedOut?: (...args: any[]) => void;
statusChanged?: (...args: any[]) => void;
allowedMinimized?: boolean;
allowedFullScreen?: boolean;
videoDisplayMode?: string;
videoResolution?: string;
}>(),
{
allowedMinimized: false,
allowedFullScreen: true,
videoDisplayMode: VideoDisplayMode.COVER,
videoResolution: VideoResolution.RESOLUTION_720P,
},
);
const callStatus = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS));
const callRole = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE));
const callType = ref(TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE));
const isGroupCall = ref(TUIStore.getData(StoreName.CALL, NAME.IS_GROUP));
const isEarPhone = ref(TUIStore.getData(StoreName.CALL, NAME.IS_EAR_PHONE));
const focusElement = ref(null);
const localUserInfoExcludeVolume = ref(TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN));
const remoteUserListExcludeVolume = ref(TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST));
const callerUserInfo = ref(TUIStore.getData(StoreName.CALL, NAME.CALLER_USER_INFO));
const isFloatWindow = ref(TUIStore.getData(StoreName.CALL, NAME.IS_MINIMIZED));
const enableVirtualBackground = ref(TUIStore.getData(StoreName.CALL, NAME.ENABLE_VIRTUAL_BACKGROUND));
const isShowEnableVirtualBackground = ref(TUIStore.getData(StoreName.CALL, NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND));
const customUIConfigContextValue = ref(TUIStore.getData(StoreName.CALL, NAME.CUSTOM_UI_CONFIG));
const isMuteSpeaker = ref(TUIStore.getData(StoreName.CALL, NAME.IS_MUTE_SPEAKER));
const translate = ref(TUIStore.getData(StoreName.CALL, NAME.TRANSLATE));
const callInfoContextValue = reactive({
callStatus,
callRole,
callType,
isGroupCall,
isEarPhone,
focusElement,
allowedFullScreen: props.allowedFullScreen,
enableVirtualBackground,
isShowEnableVirtualBackground,
isMuteSpeaker,
});
const callerUserInfoValue: TCallerUserInfoValue = reactive({ callerUserInfo });
const userInfoExcludeVolumeContextValue = reactive({
localUserInfoExcludeVolume,
remoteUserListExcludeVolume,
});
const floatWindowContextValue: TFloatWindowContextValue = reactive({ isFloatWindow });
const translateContextValue = ref(translate);
const {
beforeCalling,
afterCalling,
onMinimized,
onMessageSentByMe,
videoDisplayMode,
videoResolution,
kickedOut,
statusChanged,
allowedMinimized,
} = toRefs(props);
const handleCallStatusChange = (value) => {
callInfoContextValue.callStatus = value;
};
const handleIsGroupChange = (value) => {
callInfoContextValue.isGroupCall = value;
};
const handleToastInfoChange = (value) => {
if (typeof value === 'object') {
const { content, type = 'info' } = value;
!isEmpty(content) && showToast(translate.value(content), type);
}
};
const handleCallMediaTypeChange = (value) => {
callInfoContextValue.callType = value;
if (isMobile && isShowFloatWindow.value) {
mobileVideoStyle.value = 'miniMized-mobile-audio';
mobileAudioStyle.value = '';
}
if (isMobile && !isShowFloatWindow.value) {
mobileAudioStyle.value = 'mobile-audio';
}
};
const handleIsMinimizedChange = (value) => {
floatWindowContextValue.isFloatWindow = value;
// 开启悬浮窗
if (value) {
if (!isMobile) {
miniMizedDeskStyle.value = 'miniMized';
} else {
if (callInfoContextValue.callType === CallMediaType.AUDIO) {
mobileAudioStyle.value = 'miniMized-mobile-audio';
} else {
mobileVideoStyle.value = 'miniMized-mobile-video';
}
}
} else {
// 关闭悬浮窗
mobileAudioStyle.value = 'mobile-audio';
miniMizedDeskStyle.value = '';
mobileVideoStyle.value = '';
}
};
const handlePermissionErrorTipChange = (data) => {
};
const handleCallRoleChange = (value) => {
callInfoContextValue.callRole = value;
};
const handleLocalUserInfoChange = (value) => {
userInfoExcludeVolumeContextValue.localUserInfoExcludeVolume = value;
};
const handleRemoteUserInfoListChange = (value) => {
userInfoExcludeVolumeContextValue.remoteUserListExcludeVolume = [...value];
};
const handleCallerUserInfoChange = (value) => {
callerUserInfoValue.callerUserInfo = value;
};
const handEarPhoneChange = (value) => {
callInfoContextValue.isEarPhone = value;
};
const handleEnableVirtualBackgroundChange = (value) => {
callInfoContextValue.enableVirtualBackground = value;
};
const handleIsShowEnableVirtualBackgroundChange = (value) => {
callInfoContextValue.isShowEnableVirtualBackground = value;
};
const handleCustomUIConfigChange = (value) => {
customUIConfigContextValue.value = value;
};
const handleMuteSpeakerChange = (value) => {
callInfoContextValue.isMuteSpeaker = value;
};
const handleTranslateChange = (value) => {
translate.value = value;
};
watchEffect(() => {
TUICallKitAPI.setCallback({
beforeCalling: beforeCalling && beforeCalling.value,
afterCalling: afterCalling && afterCalling.value,
onMinimized: onMinimized && onMinimized.value,
onMessageSentByMe: onMessageSentByMe && onMessageSentByMe.value,
kickedOut: kickedOut && kickedOut.value,
statusChanged: statusChanged && statusChanged.value,
});
});
const watchOptions = {
[NAME.CALL_STATUS]: handleCallStatusChange,
[NAME.IS_GROUP]: handleIsGroupChange,
[NAME.TOAST_INFO]: handleToastInfoChange,
[NAME.CALL_MEDIA_TYPE]: handleCallMediaTypeChange,
[NAME.SHOW_PERMISSION_TIP]: handlePermissionErrorTipChange,
[NAME.CALL_ROLE]: handleCallRoleChange,
[NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN]: handleLocalUserInfoChange,
[NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST]: handleRemoteUserInfoListChange,
[NAME.CALLER_USER_INFO]: handleCallerUserInfoChange,
[NAME.IS_EAR_PHONE]: handEarPhoneChange,
[NAME.ENABLE_VIRTUAL_BACKGROUND]: handleEnableVirtualBackgroundChange,
[NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND]: handleIsShowEnableVirtualBackgroundChange,
[NAME.CUSTOM_UI_CONFIG]: handleCustomUIConfigChange,
[NAME.IS_MUTE_SPEAKER]: handleMuteSpeakerChange,
[NAME.TRANSLATE]: handleTranslateChange,
};
onMounted(() => {
if(allowedMinimized.value){
TUICallKitAPI.enableFloatWindow(allowedMinimized.value)
}
TUICallKitAPI.setVideoDisplayMode(videoDisplayMode.value as any);
TUICallKitAPI.setVideoResolution(videoResolution.value as any);
TUIStore.watch(
StoreName.CALL,
watchOptions,
{
notifyRangeWhenWatch: NAME.MYSELF,
},
);
TUIStore.watch(StoreName.CALL, {
[NAME.IS_MINIMIZED]: handleIsMinimizedChange,
});
});
function showToast(value: string, type: string) {
switch (type) {
case 'info':
uni.showToast({
title: value,
icon: 'none',
});
break;
case 'error':
break;
default:
break;
}
}
onUnmounted(async () => {
TUIStore.unwatch(StoreName.CALL, {
...watchOptions,
[NAME.IS_MINIMIZED]: handleIsMinimizedChange,
});
await TUICallKitAPI.handleExceptionExit();
});
provide(CallInfoContextKey, callInfoContextValue);
provide(CallerUserInfoContextKey, callerUserInfoValue);
provide(UserInfoExcludeVolumeContextKey, userInfoExcludeVolumeContextValue);
provide(FloatWindowContextKey, floatWindowContextValue);
provide(CustomUIConfigContextKey, customUIConfigContextValue);
provide(translateContextKey, translateContextValue);
</script>
<style lang="scss">
.TUICallKit-mobile {
width: 100vw;
height: 100vh;
.singCall {
width: 100vw;
height: 100vh;
}
}
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.TUICallKit-desktop {
margin: 0 auto;
position: relative;
position: relative;
border-radius: inherit;
width: 100%;
height: 100%;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
z-index: 12;
border-radius: 16px;
.singCall {
width: 100%;
height: 100%;
}
}
.mobile-audio {
background-color: white;
}
.miniMized {
width: 168px !important;
height: 56px !important;
overflow: visible !important;
}
.miniMized-mobile-audio {
width: 72px;
height: 72px;
position: fixed;
top: 40px;
right: 40px;
}
.miniMized-mobile-video {
width: 40%;
height: 30%;
position: fixed;
top: 40px;
right: 40px;
}
</style>
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.73708 12.9963C8.71266 12.8344 8.7 12.6687 8.7 12.5V6C8.7 4.17746 10.1775 2.7 12 2.7C13.5488 2.7 14.8484 3.76695 15.2038 5.20592L16.4458 3.70974C15.615 2.10033 13.936 1 12 1C9.23858 1 7 3.23858 7 6V12.5C7 13.2333 7.15786 13.9297 7.44145 14.5572L8.73708 12.9963ZM11.3385 15.7337L10.1618 17.1513C10.7308 17.3763 11.3509 17.5 12 17.5C14.7614 17.5 17 15.2614 17 12.5V8.9133L15.3 10.9613V12.5C15.3 14.3225 13.8225 15.8 12 15.8C11.7734 15.8 11.5522 15.7772 11.3385 15.7337ZM5.96519 16.3356C5.25933 15.2273 4.85039 13.9114 4.85039 12.5V11H3.15039V12.5C3.15039 14.4416 3.77562 16.2371 4.83572 17.6963L5.96519 16.3356ZM7.63218 20.1986L8.7394 18.8647C9.71718 19.3667 10.8257 19.65 12.0004 19.65C15.9492 19.65 19.1504 16.4488 19.1504 12.5V11H20.8504V12.5C20.8504 17.0498 17.4171 20.7976 13 21.2942V24H11V21.2941C9.78613 21.1575 8.64658 20.7754 7.63218 20.1986Z" fill="#fff" />
<path d="M5.73682 19.5513L18.2667 4.46371" stroke="#ED414D" style="stroke:#ED414D;stroke:color(display-p3 0.9292 0.2555 0.3004);stroke-opacity:1;" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<rect x="11" y="20" width="2" height="4" fill="#fff" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.1499 11V12.5C3.1499 17.3877 7.11218 21.35 11.9999 21.35C16.8876 21.35 20.8499 17.3877 20.8499 12.5V11H19.1499V12.5C19.1499 16.4488 15.9487 19.65 11.9999 19.65C8.05107 19.65 4.8499 16.4488 4.8499 12.5V11H3.1499Z" fill="#fff"/>
<rect x="7.85" y="1.85" width="8.3" height="14.8" rx="4.15" stroke="#fff" stroke-width="1.7"/>
</svg>

After

Width:  |  Height:  |  Size: 537 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="m557.2 512 233.4-233.4c12.5-12.5 12.5-32.8 0-45.2s-32.8-12.5-45.2 0L512 466.8 278.6 233.4c-12.5-12.5-32.8-12.5-45.2 0s-12.5 32.8 0 45.2L466.8 512 233.4 745.4c-12.5 12.5-12.5 32.8 0 45.2 6.2 6.2 14.4 9.4 22.6 9.4s16.4-3.1 22.6-9.4L512 557.2l233.4 233.4c6.2 6.2 14.4 9.4 22.6 9.4s16.4-3.1 22.6-9.4c12.5-12.5 12.5-32.8 0-45.2L557.2 512z"/></svg>

After

Width:  |  Height:  |  Size: 451 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M512 97.524c228.913 0 414.476 185.563 414.476 414.476S740.913 926.476 512 926.476 97.524 740.913 97.524 512 283.087 97.524 512 97.524zM641.292 330.97 512.024 460.264 382.708 330.97l-51.737 51.737 129.317 129.268-129.317 129.316 51.737 51.737L512 563.688l129.292 129.316 51.737-51.736L563.688 512l129.316-129.292-51.736-51.737z" fill="#d54941"/></svg>

After

Width:  |  Height:  |  Size: 459 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M512 97.524c228.913 0 414.476 185.563 414.476 414.476S740.913 926.476 512 926.476 97.524 740.913 97.524 512 283.087 97.524 512 97.524zm36.571 341.333H475.43V731.43h73.142V438.857zm0-121.905H475.43v73.143h73.142v-73.143z" fill="#0052d9"/></svg>

After

Width:  |  Height:  |  Size: 352 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M520.665 64.564a29.244 29.244 0 0 0-7.071-.894c-16.211 0-29.35 13.14-29.35 29.35 0 15.83 12.538 28.696 28.223 29.293v.073c.375 0 .751-.014 1.127-.014 216.493 0 389.629 173.942 389.629 390.433 0 216.496-173.136 388.827-389.63 388.827s-389.63-172.333-389.63-388.829c0-.27.01-.536.01-.804h-.01c0-16.209-13.141-29.35-29.35-29.35s-29.35 13.139-29.35 29.35c0 .055.007.11.007.164-.001.214-.007.425-.007.64 0 247.609 200.722 448.331 448.33 448.331 247.607 0 448.33-200.722 448.33-448.33-.002-245.242-196.917-444.456-441.258-448.24z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 653 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M512 97.524c228.913 0 414.476 185.563 414.476 414.476S740.913 926.476 512 926.476 97.524 740.913 97.524 512 283.087 97.524 512 97.524zm193.195 218.331L447.22 581.315 343.284 473.502l-52.663 50.762 156.38 162.23 310.661-319.683-52.467-50.956z" fill="#2ba471"/></svg>

After

Width:  |  Height:  |  Size: 376 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M512 97.524c228.913 0 414.476 185.563 414.476 414.476S740.913 926.476 512 926.476 97.524 740.913 97.524 512 283.087 97.524 512 97.524zm36.571 341.333H475.43V731.43h73.142V438.857zm0-121.905H475.43v73.143h73.142v-73.143z" fill="#e37318"/></svg>

After

Width:  |  Height:  |  Size: 352 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="M4.162 5.677C2.58 7.258 1.967 9.608 2.874 11.652a32.422 32.422 0 0 0 6.733 9.793 32.423 32.423 0 0 0 9.793 6.733c2.044.907 4.394.293 5.975-1.288l2.543-2.543a1.5 1.5 0 0 0-.073-2.19l-4.808-4.207a1.5 1.5 0 0 0-2.048.068l-2.131 2.131a.736.736 0 0 1-.932.095 26.385 26.385 0 0 1-3.9-3.218 26.385 26.385 0 0 1-3.218-3.9.736.736 0 0 1 .095-.932l2.13-2.13a1.5 1.5 0 0 0 .069-2.05L8.895 3.208a1.5 1.5 0 0 0-2.19-.073L4.162 5.677z" fill="#fff"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h30v30H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 631 B

@@ -0,0 +1 @@
<svg width="26" height="26" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 1a1 1 0 0 1 1-1h24a1 1 0 0 1 1 1v24a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1zm13 13a4.179 4.179 0 1 0 0-8.357A4.179 4.179 0 0 0 13 14zm-1.857 1.346a5.571 5.571 0 0 0-5.572 5.571v2.25a1 1 0 0 0 1 1H19.43a1 1 0 0 0 1-1v-2.25a5.571 5.571 0 0 0-5.572-5.571L13 18.019l-1.857-2.673zm-9.75-7.31 6.643-6.643h2.232l-8.875 8.875V8.036zm0 12.232v-2.233l6.642-6.641L9.15 12.51l-7.758 7.758zm23.214-5.447v2.233l-2.473 2.473-1.116-1.116 3.59-3.59zm0-10v2.233l-7.092 7.092-1.116-1.116 8.208-8.209zm-4.339-3.428-3.895 3.895-1.117-1.116 2.78-2.78h2.232z" fill="#000" style="fill:#000;fill-opacity:1"/></svg>

After

Width:  |  Height:  |  Size: 708 B

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M1 2a1 1 0 0 1 1-1h24a1 1 0 0 1 1 1v24a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2zm14.653 14.801a4.18 4.18 0 1 0-3.306 0h-.204a5.571 5.571 0 0 0-5.572 5.572v2.25a1 1 0 0 0 1 1H20.43a1 1 0 0 0 1-1v-2.25a5.571 5.571 0 0 0-5.572-5.572h-.204zm-13.26-9.18L7.62 2.393h2.232l-7.46 7.46V7.621zm0 8.13v-2.232L13.518 2.393h2.233L2.393 15.75zm0 5.898v-2.233l5.459-5.459c.062.626.217 1.224.452 1.78L2.393 21.65zM16.398 7.644a6.14 6.14 0 0 0-1.78-.452l4.798-4.8h2.233l-5.251 5.252zM2.84 24.867l1.731-1.732V25c0 .118.007.234.02.348l-.258.258a1.996 1.996 0 0 1-1.493-.739zM18.549 9.158c.36.393.672.833.923 1.31l6.134-6.135a1.996 1.996 0 0 0-.739-1.493l-6.319 6.318zm.97 6.963.125.071 5.963-5.963V7.997l-5.43 5.43a6.149 6.149 0 0 1-.659 2.694zm2.12 1.741c.346.409.65.854.903 1.33l3.065-3.065v-2.232l-3.967 3.967zm1.79 4.888v1.453l2.178-2.178v-2.233l-2.215 2.215c.024.244.037.492.037.743z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1005 B

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="a" fill="#fff"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.952 23.334h8.881a1 1 0 0 0 1-1V11.43L9.952 23.334zM21.364 9.585l1.626-1.958 2.42-1.13a1 1 0 0 1 1.423.907v13.193a1 1 0 0 1-1.423.906l-2.952-1.378-.516-.24a1 1 0 0 1-.578-.907V9.585zm-3.5-4.918L2.367 23.334h-.201a1 1 0 0 1-1-1V5.667a1 1 0 0 1 1-1h15.696zM5.833 11.35h4.082v-1.7H5.833v1.7z"/></mask><path fill-rule="evenodd" clip-rule="evenodd" d="M9.952 23.334h8.881a1 1 0 0 0 1-1V11.43L9.952 23.334zM21.364 9.585l1.626-1.958 2.42-1.13a1 1 0 0 1 1.423.907v13.193a1 1 0 0 1-1.423.906l-2.952-1.378-.516-.24a1 1 0 0 1-.578-.907V9.585zm-3.5-4.918L2.367 23.334h-.201a1 1 0 0 1-1-1V5.667a1 1 0 0 1 1-1h15.696zM5.833 11.35h4.082v-1.7H5.833v1.7z" fill="#fff" style="fill:#fff;fill-opacity:1"/><path d="m9.952 23.334-1.308-1.086-2.313 2.786h3.62v-1.7zm9.881-11.904h1.7V6.72l-3.008 3.624 1.308 1.086zm3.157-3.803-.72-1.54-.345.161-.244.294 1.309 1.085zm-1.626 1.958L20.056 8.5l-.392.472v.614h1.7zm4.046-3.087.72 1.54-.72-1.54zm1.423.906h1.7-1.7zm0 13.193h-1.7 1.7zm-1.423.906-.719 1.54.72-1.54zm-2.952-1.378.72-1.54-.72 1.54zm-.516-.24-.72 1.54.72-1.54zm-.578-.907h-1.7 1.7zM2.368 23.334v1.7h.798l.51-.614-1.308-1.086zM17.863 4.667l1.308 1.086 2.313-2.786h-3.62v1.7zM9.916 11.35v1.7h1.7v-1.7h-1.7zm-4.083 0h-1.7v1.7h1.7v-1.7zm4.083-1.7h1.7v-1.7h-1.7v1.7zm-4.083 0v-1.7h-1.7v1.7h1.7zm13 11.984H9.952v3.4h8.881v-3.4zm-.7.7a.7.7 0 0 1 .7-.7v3.4a2.7 2.7 0 0 0 2.7-2.7h-3.4zm0-10.904v10.904h3.4V11.43h-3.4zm.392-1.086L8.644 22.248l2.616 2.171 9.881-11.903-2.616-2.172zm3.157-3.802-1.626 1.957 2.616 2.172 1.626-1.958-2.616-2.171zm3.01-1.585-2.421 1.13 1.438 3.08 2.42-1.129-1.438-3.08zm3.841 2.447c0-1.976-2.052-3.282-3.842-2.447l1.438 3.081a.7.7 0 0 1-.996-.634h3.4zm0 13.193V7.404h-3.4v13.193h3.4zm-3.842 2.447c1.79.835 3.842-.472 3.842-2.447h-3.4a.7.7 0 0 1 .996-.635l-1.438 3.082zm-2.952-1.378 2.952 1.378 1.438-3.082-2.952-1.377-1.438 3.08zm-.516-.241.516.24 1.438-3.08-.517-.241-1.437 3.08zm-1.559-2.447a2.7 2.7 0 0 0 1.559 2.447l1.438-3.081a.7.7 0 0 1 .403.634h-3.4zm0-9.393v9.393h3.4V9.585h-3.4zM3.676 24.42 19.171 5.753 16.555 3.58 1.06 22.248l2.616 2.171zm-1.308-2.785h-.201v3.4h.201v-3.4zm-.201 0a.7.7 0 0 1 .7.7h-3.4a2.7 2.7 0 0 0 2.7 2.7v-3.4zm.7.7V5.667h-3.4v16.667h3.4zm0-16.667a.7.7 0 0 1-.7.7v-3.4a2.7 2.7 0 0 0-2.7 2.7h3.4zm-.7.7h15.696v-3.4H2.166v3.4zm7.75 3.283H5.832v3.4h4.083v-3.4zm-1.7 0v1.7h3.4v-1.7h-3.4zm-2.384 1.7h4.083v-3.4H5.833v3.4zm1.7 0v-1.7h-3.4v1.7h3.4z" fill="#fff" style="fill:#fff;fill-opacity:1" mask="url(#a)"/><path d="M4.563 25.269 22.14 4.084" stroke="#fff" style="stroke:#fff;stroke-opacity:1" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg width="26" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.916 8.2h.85V4.8H3.984v3.4h4.933zm-7.9-6.533a.15.15 0 0 1 .15-.15h16.667a.15.15 0 0 1 .15.15v16.666a.15.15 0 0 1-.15.15H1.167a.15.15 0 0 1-.15-.15V1.667zm20.198 3.355a.15.15 0 0 1 .087-.136l3.469-1.618a.15.15 0 0 1 .213.135v13.193a.15.15 0 0 1-.213.136l-3.47-1.618a.15.15 0 0 1-.086-.136V5.022z" fill="#22262E" stroke="#22262E" stroke-width="1.7" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.66683 9.33337H3.3335V4.00004C3.3335 3.63185 3.63197 3.33337 4.00016 3.33337L9.3335 3.33337V4.66671H5.60963L9.8049 8.86199L8.86209 9.8048L4.66683 5.60952V9.33337ZM15.3333 10.6667H16.6667V16C16.6667 16.3682 16.3682 16.6667 16 16.6667L10.6667 16.6667L10.6667 15.3333H14.3905L10.1953 11.138L11.1381 10.1952L15.3333 14.3905V10.6667Z" fill="white"/>
<path d="M0.666667 1.33333C0.666667 0.965144 0.965144 0.666667 1.33333 0.666667H18.6667C19.0349 0.666667 19.3333 0.965144 19.3333 1.33333V18.6667C19.3333 19.0349 19.0349 19.3333 18.6667 19.3333H1.33333C0.965143 19.3333 0.666667 19.0349 0.666667 18.6667V1.33333Z" stroke="white" stroke-width="1.33333"/>
</svg>

After

Width:  |  Height:  |  Size: 801 B

@@ -0,0 +1 @@
<svg t="1708400849531" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8267" width="200" height="200"><path d="M121.51 460.15h779.64v100.6H121.51z" p-id="8268" fill="#ffffff"></path><path d="M461.035 900.271v-779.64h100.6v779.64z" p-id="8269" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 315 B

@@ -0,0 +1,6 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.769231" y="0.769231" width="18.4615" height="18.4615" rx="0.769231" stroke="white" stroke-width="1.53846"/>
<path d="M11.5385 12.3077C11.5385 11.8829 11.8829 11.5385 12.3077 11.5385H19.2308V18.4615C19.2308 18.8864 18.8864 19.2308 18.4616 19.2308H11.5385V12.3077Z" stroke="white" stroke-width="1.53846"/>
<path d="M0.769287 0.769226L8.9723 8.92412" stroke="white" stroke-width="1.53846"/>
<path d="M9.4967 4.71716V9.4486H4.74146" stroke="white" stroke-width="1.53846" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 647 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M30 16.05c0-2.235-1.228-4.33-3.314-5.135A32.423 32.423 0 0 0 15 8.751c-4.12 0-8.06.766-11.685 2.164C1.228 11.719 0 13.815 0 16.051v3.597a1.5 1.5 0 0 0 1.6 1.496l6.375-.425a1.5 1.5 0 0 0 1.4-1.496v-3.014c0-.353.245-.659.591-.726A26.382 26.382 0 0 1 15 15.001c1.722 0 3.404.166 5.034.482a.736.736 0 0 1 .591.726v3.014a1.5 1.5 0 0 0 1.4 1.496l6.375.425a1.5 1.5 0 0 0 1.6-1.496V16.05z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 485 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.75 7.5a6.25 6.25 0 0 1 11.133-3.902L8.873 16.862a6.28 6.28 0 0 1-.123-1.237V7.5zM6.806 19.352A8.967 8.967 0 0 1 6 15.625V13.75H4v1.875c0 1.963.514 3.806 1.416 5.402l1.39-1.675zm2.406 5.629 1.292-1.558A9 9 0 0 0 24 15.625v-1.874h2v1.875c0 5.738-4.393 10.45-10 10.955V30h-2v-3.42a10.934 10.934 0 0 1-4.788-1.6zm3.086-3.719 8.952-10.784v5.147a6.25 6.25 0 0 1-8.952 5.637z" fill="#fff" style="fill:#fff;fill-opacity:1"/><path d="M6.419 24.084 22.094 5.209" stroke="#fff" style="stroke:#fff;stroke-opacity:1" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 703 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 15.625V13.75h2v1.875a9 9 0 1 0 18 0V13.75h2v1.875c0 5.738-4.393 10.45-10 10.955V30h-2v-3.42c-5.607-.505-10-5.217-10-10.955z" fill="#22262E"/><rect x="8.75" y="1.25" width="12.5" height="20.625" rx="6.25" fill="#22262E"/></svg>

After

Width:  |  Height:  |  Size: 353 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><circle opacity=".5" cx="15" cy="15" r="15" fill="#22262E"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.484 14.313a.4.4 0 0 1 .014-.565l.87-.827a.4.4 0 0 1 .566.015L15 17.216l4.066-4.28a.4.4 0 0 1 .565-.015l.87.827a.4.4 0 0 1 .015.565l-5.033 5.298a.667.667 0 0 1-.966 0l-5.033-5.298z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 387 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><mask id="b" fill="#fff"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 11.05a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zm-2 1.45a6 6 0 0 0-6 6V21a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-2.5a6 6 0 0 0-6-6l-2 2.88-2-2.88z"/></mask><path d="m8.5 12.5 1.396-.97-.507-.73H8.5v1.7zm4 0v-1.7h-.889l-.507.73 1.396.97zm-2 2.88-1.396.969 1.396 2.01 1.396-2.01-1.396-.97zm2.8-8.83a2.8 2.8 0 0 1-2.8 2.8v3.4a6.2 6.2 0 0 0 6.2-6.2h-3.4zm-2.8-2.8a2.8 2.8 0 0 1 2.8 2.8h3.4a6.2 6.2 0 0 0-6.2-6.2v3.4zm-2.8 2.8a2.8 2.8 0 0 1 2.8-2.8V.35a6.2 6.2 0 0 0-6.2 6.2h3.4zm2.8 2.8a2.8 2.8 0 0 1-2.8-2.8H4.3a6.2 6.2 0 0 0 6.2 6.2v-3.4zM4.2 18.5a4.3 4.3 0 0 1 4.3-4.3v-3.4a7.7 7.7 0 0 0-7.7 7.7h3.4zm0 2.5v-2.5H.8V21h3.4zm-.7-.7a.7.7 0 0 1 .7.7H.8a2.7 2.7 0 0 0 2.7 2.7v-3.4zm14 0h-14v3.4h14v-3.4zm-.7.7a.7.7 0 0 1 .7-.7v3.4a2.7 2.7 0 0 0 2.7-2.7h-3.4zm0-2.5V21h3.4v-2.5h-3.4zm-4.3-4.3a4.3 4.3 0 0 1 4.3 4.3h3.4a7.7 7.7 0 0 0-7.7-7.7v3.4zm-.604 2.149 2-2.879-2.792-1.94-2 2.88 2.792 1.939zM7.104 13.47l2 2.879 2.792-1.94-2-2.879-2.792 1.94z" fill="#D5E0F2" mask="url(#b)"/></g><path d="M16 11h7m-3.5-3.5v7" stroke="#D5E0F2" stroke-width="1.7"/><defs><filter id="a" x="-5.5" y="-5.949" width="32" height="35.95" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feGaussianBlur in="BackgroundImageFix" stdDeviation="4"/><feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_2584_236"/><feBlend in="SourceGraphic" in2="effect1_backgroundBlur_2584_236" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.5 4H3a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5.5" stroke="#D5E0F2" stroke-width="1.5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 3.5v5h5v-5h-5zM11 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-6z" fill="#D5E0F2"/></svg>

After

Width:  |  Height:  |  Size: 348 B

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M25.018 5.763a2 2 0 0 1 2 2v15.528a2 2 0 0 1-2 2H2.982a2 2 0 0 1-2-2V7.763a2 2 0 0 1 2-2h4.262a2 2 0 0 0 1.494-.67l1.41-1.587a2 2 0 0 1 1.495-.672h4.714a2 2 0 0 1 1.494.671l1.41 1.587a2 2 0 0 0 1.495.671h4.262zM11.723 9.585a5.907 5.907 0 0 1 2.186-.417c1.719 0 3.27.733 4.341 1.903V9.918h1.5v5.001h-1.5v-.005c-.002-2.332-1.932-4.245-4.34-4.245-.58 0-1.13.11-1.633.31l-.554-1.394zM8.25 19.919v-5h1.5c0 2.334 1.93 4.25 4.34 4.25.58 0 1.13-.111 1.633-.31l.554 1.393a5.908 5.908 0 0 1-2.186.417 5.873 5.873 0 0 1-4.341-1.903v1.153h-1.5z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 677 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><circle opacity=".5" cx="15" cy="15" r="15" fill="#22262E" style="fill:color(display-p3 .1333 .149 .1804);fill-opacity:1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M20.516 17.452a.4.4 0 0 1-.014.565l-.87.827a.4.4 0 0 1-.566-.015L15 14.55l-4.066 4.28a.4.4 0 0 1-.566.015l-.87-.827a.4.4 0 0 1-.014-.565l5.033-5.298a.667.667 0 0 1 .966 0l5.033 5.298z" fill="#fff" style="fill:#fff;fill-opacity:1"/></svg>

After

Width:  |  Height:  |  Size: 484 B

@@ -0,0 +1 @@
<svg width="30" height="30" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="m10.36 22.842 4.235 3.56c.697.587 1.76.091 1.76-.82V15.62l-5.995 7.223zm13.356-16.09 1.285-1.548A13.955 13.955 0 0 1 29 15c0 5.183-2.817 9.707-7 12.127l-.865.5-1.001-1.731.865-.5C24.59 23.318 27 19.44 27 15c0-3.194-1.248-6.097-3.284-8.248zm-3.278 3.95 1.288-1.553C23.14 10.613 24 12.71 24 15c0 2.869-1.35 5.433-3.445 6.832l-.831.555-1.11-1.663.83-.556C20.938 19.172 22 17.258 22 15c0-1.716-.613-3.232-1.562-4.299zm-4.082-4.22L3.928 21.456H2.252c-.592 0-1.072-.48-1.072-1.072V9.635c0-.591.478-1.07 1.069-1.072l6.073-.017c.251 0 .494-.09.687-.251l5.586-4.697c.697-.586 1.76-.091 1.76.82v2.065z" fill="#fff"/><path d="M4.564 25.268 22.14 4.084" stroke="#fff" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 852 B

@@ -0,0 +1 @@
<svg width="28" height="26" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="m20.135.373.866.5C25.183 3.294 28 7.818 28 13c0 5.183-2.817 9.707-7 12.127l-.865.5-1.001-1.731.865-.5C23.59 21.318 26 17.44 26 13s-2.411-8.319-6-10.395l-.866-.5L20.135.372zm-6.49 1.016a1.071 1.071 0 0 1 1.71.86V23.75c0 .881-1.003 1.386-1.71.86l-6.65-4.945a1.071 1.071 0 0 0-.639-.211H1.252c-.592 0-1.072-.48-1.072-1.072V7.634c0-.59.478-1.07 1.068-1.071l5.11-.017c.23 0 .452-.075.636-.211l6.65-4.946zm5.91 4.78-.831-.556-1.111 1.663.832.556C19.937 8.828 21 10.742 21 13c0 2.259-1.063 4.172-2.555 5.168l-.832.556 1.11 1.663.832-.555C21.65 18.432 23 15.869 23 13c0-2.869-1.35-5.433-3.445-6.832z" fill="#2E333D"/></svg>

After

Width:  |  Height:  |  Size: 739 B

@@ -0,0 +1 @@
<svg width="20" height="19" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.302 2.09a7.164 7.164 0 0 0-8.287 7.539l-1.978.358A9.274 9.274 0 0 1 0 9.162C0 4.102 4.104 0 9.167 0c.508 0 1.007.041 1.493.12l-.358 1.97zm-.239 1.322A5.832 5.832 0 0 0 3.338 9.4l1.998-.364a3.832 3.832 0 0 1 4.37-3.655l.357-1.97zM8.181 7.218l10.445-.005a.47.47 0 0 1 .457.57l-2.287 10.186a.467.467 0 0 1-.823.19l-2.32-2.896C9.61 18.498 3.71 17.846.473 13.806c-.138-.172.049-.408.259-.339a8.44 8.44 0 0 0 7.94-1.42l1.463-1.172-2.32-2.896a.468.468 0 0 1 .365-.761z" fill="#fff" style="fill:#fff;fill-opacity:1"/></svg>

After

Width:  |  Height:  |  Size: 643 B

@@ -0,0 +1 @@
<svg width="10" height="6" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 0 5 3.75.5 0v1.562l4.116 3.43.384.32.384-.32L9.5 1.562V0z" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 206 B

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.536 6.682c-.33-.955.088-1.98.9-2.581.617-.456 1.236-.916 1.589-1.178a.396.396 0 0 1 .539.062l1.925 2.264a.396.396 0 0 1 0 .513l-1.18 1.39a.388.388 0 0 0-.041.45 8.667 8.667 0 0 0 1.37 1.758c.57.57 1.12.982 1.765 1.364a.389.389 0 0 0 .45-.041l1.383-1.174a.397.397 0 0 1 .514 0l2.266 1.925c.159.135.186.37.062.538l-1.185 1.604c-.596.806-1.614 1.216-2.562.89a12.545 12.545 0 0 1-4.797-3.003 12.54 12.54 0 0 1-2.998-4.78z" fill="#1C66E5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8.889 2.133a.356.356 0 0 0-.356.356v3.685c0 .196.16.355.356.355h3.685c.196 0 .355-.159.355-.355v-.186a.32.32 0 0 1 .455-.29l.687.32a.356.356 0 0 0 .506-.322v-2.73c0-.26-.27-.432-.506-.322l-.687.32a.32.32 0 0 1-.455-.29V2.49a.356.356 0 0 0-.355-.356H8.889z" fill="#1C66E5"/></svg>

After

Width:  |  Height:  |  Size: 851 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="10" height="10"><path d="M512 466.752 86.656 41.344a32 32 0 0 0-45.312 45.312L466.752 512 41.344 937.344a32 32 0 0 0 45.312 45.312L512 557.248l425.344 425.408a32 32 0 0 0 45.312-45.312L557.248 512 982.656 86.656a32 32 0 0 0-45.312-45.312L512 466.752z" fill="#707070"/></svg>

After

Width:  |  Height:  |  Size: 358 B

@@ -0,0 +1 @@
<svg width="100" height="100" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="url(#a)" d="M-5-5h110v110H-5z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M25 71.5c0-10.264 7.93-18.676 17.998-19.443.496-.038.997-.057 1.502-.057h13c.505 0 1.006.02 1.501.057C69.07 52.824 77 61.236 77 71.5v6.783H25V71.5zM44.5 45.605a14.075 14.075 0 0 1-.205-.104c-4.704-2.431-7.92-7.34-7.92-13.001 0-8.077 6.548-14.625 14.625-14.625S65.625 24.423 65.625 32.5c0 5.66-3.216 10.57-7.92 13-.068.036-.136.07-.205.105a14.565 14.565 0 0 1-6.5 1.52c-2.335 0-4.542-.547-6.5-1.52z" fill="#D5E0F2"/><defs><linearGradient id="a" x1="73.5" y1="-5" x2="5" y2="98.5" gradientUnits="userSpaceOnUse"><stop stop-color="#7C859B"/><stop offset="1" stop-color="#747C90"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 777 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M512 170.667V256a256 256 0 1 1-223.573 131.2l-74.496-41.6A341.333 341.333 0 1 0 512 170.667z" opacity=".3"/></svg>

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" class="design-iconfont"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.828 12.476c.754.429 1.61.722 2.505.82V15c0 .184.15.334.334.334h.666c.184 0 .334-.15.334-.334v-1.703C11.54 12.98 14 10.666 14 8.273v-.592a.333.333 0 0 0-.333-.333H13a.333.333 0 0 0-.333.333v.592l-.004.12c-.095 1.76-2.265 3.605-4.659 3.607h-.008c-.77 0-1.519-.192-2.181-.51l-.987.986zm2.004-2.004c.364.12.758.186 1.168.186 1.827 0 3.333-1.303 3.333-2.932V5.97l-4.501 4.502zm4.075-8.317-6.17 6.17a2.602 2.602 0 0 1-.067-.468l-.003-.131V3.59C4.667 1.961 6.173.658 8 .658c1.24 0 2.333.601 2.907 1.497zM3.625 9.437l-.979.978c-.384-.625-.615-1.307-.643-1.999L2 8.273v-.608c0-.182.147-.33.33-.333l.664-.007a.333.333 0 0 1 .336.332l.003.616c0 .389.104.784.292 1.164z" fill="#fff"/><rect x=".61" y="13.451" width="19.013" height="1.5" rx=".75" transform="rotate(-45 .61 13.451)" fill="#FE3C44"/></svg>

After

Width:  |  Height:  |  Size: 939 B

@@ -0,0 +1,3 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.4437 12.449C18.4437 14.1142 18.2336 15.3515 21.5089 15.7126C24.7875 16.0737 24.3666 13.6794 24.3662 12.0669C24.3649 10.2061 20.0602 7.61895 13.2726 7.6173C6.48662 7.61735 2.1789 10.205 2.17888 12.067C2.17887 13.68 1.75621 16.0726 5.03315 15.7123C8.31009 15.3537 8.10005 14.1155 8.09837 12.4495C8.10134 11.2862 10.7587 11.0316 13.2725 11.0298C15.7863 11.0281 18.4454 11.2844 18.4437 12.449Z" fill="#FE3C44"/>
</svg>

After

Width:  |  Height:  |  Size: 536 B

@@ -0,0 +1,3 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.33337 9.33331H0.333374V0.99998C0.333374 0.63179 0.631851 0.333313 1.00004 0.333313L9.33338 0.333313V2.33331H3.74758L10.0405 8.62624L8.62627 10.0404L2.33337 3.74753V9.33331ZM18.3331 11.3333H20.3331V19.6666C20.3331 20.0348 20.0347 20.3333 19.6665 20.3333L11.3331 20.3333L11.3331 18.3333H16.9189L10.626 12.0403L12.0402 10.6261L18.3331 16.919V11.3333Z" fill="#101419"/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66666 4.16668C6.66666 2.32834 8.16166 0.833344 9.99999 0.833344C11.8383 0.833344 13.3333 2.32834 13.3333 4.16668V9.16668C13.3333 11.005 11.8383 12.5 9.99999 12.5C8.16166 12.5 6.66666 11.005 6.66666 9.16668V4.16668ZM6.66666 18.3333C6.66666 17.8725 7.03999 17.5 7.49999 17.5H12.5C12.9608 17.5 13.3333 17.8725 13.3333 18.3333C13.3333 18.7942 12.9608 19.1667 12.5 19.1667H7.49999C7.03999 19.1667 6.66666 18.7942 6.66666 18.3333Z" fill="#101419"/>
<path d="M15.8333 8.3512V9.50825C15.8333 12.081 13.2217 15 9.99999 15C6.77833 15 4.16666 12.081 4.16666 9.50825V8.33334" stroke="#101419" stroke-width="1.66667" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 783 B

@@ -0,0 +1,3 @@
<svg width="15" height="21" viewBox="0 0 15 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.6247 0.719168C14.1934 0.374158 13.5641 0.44408 13.2191 0.875342L10.8168 3.83203C10.1789 3.02455 9.15387 2.50004 8.00001 2.50004C6.06976 2.50004 4.50001 3.96785 4.50001 5.77276V10.6819C4.50001 10.9563 4.5363 11.2229 4.60463 11.4778L3.388 12.9752C2.98449 12.3433 2.75001 11.6522 2.75001 10.9717V9.81819L2.74319 9.71556C2.6892 9.31213 2.32108 9.00001 1.87501 9.00001C1.39176 9.00001 1.00001 9.36632 1.00001 9.81819V10.9717L1.00466 11.1761C1.05557 12.2968 1.52161 13.4016 2.26939 14.3519L0.219137 16.8753L0.146861 16.9782C-0.112977 17.4017 -0.0227767 17.9624 0.375311 18.2809C0.806573 18.6259 1.43587 18.556 1.78088 18.1247L3.71661 15.7423C3.71849 15.7437 3.72038 15.745 3.72227 15.7464L4.2997 15.0246L8.28088 10.1247L14.7809 2.12473L14.8532 2.02191C15.113 1.59834 15.0228 1.03764 14.6247 0.719168ZM7.72499 13.9446L11.5 9.22582V10.6819C11.5 12.4868 9.93026 13.9546 8.00001 13.9546C7.90746 13.9546 7.81575 13.9512 7.72499 13.9446ZM6.60445 15.3453L5.50798 16.7159C6.29225 17.0136 7.13532 17.1818 8.00001 17.1818C11.6976 17.1818 15 14.1064 15 10.9717V9.83572L14.9932 9.73309C14.9392 9.32966 14.5711 9.01754 14.125 9.01754C13.6418 9.01754 13.25 9.38385 13.25 9.83572V10.9717L13.2445 11.1527C13.1138 13.3262 10.6589 15.5455 8.00001 15.5455C7.52424 15.5455 7.05501 15.4744 6.60445 15.3453ZM5.37501 18.8637C4.89201 18.8637 4.50001 19.2294 4.50001 19.6819C4.50001 20.1343 4.89201 20.5 5.37501 20.5H10.625C11.1089 20.5 11.5 20.1343 11.5 19.6819C11.5 19.2294 11.1089 18.8637 10.625 18.8637H5.37501Z" fill="#101419"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.672 5.777a.6.6 0 0 0-.331.536v2.693a.6.6 0 0 0 .33.536l2.803 1.406a.2.2 0 0 0 .29-.179V4.55a.2.2 0 0 0-.29-.178l-2.802 1.406z" fill="#ADADAD"/><g filter="url(#a)"><rect x=".667" y="2.553" width="10.667" height="10.213" rx="1.5" fill="#ADADAD"/></g><defs><filter id="a" x="-7.333" y="-5.447" width="26.667" height="26.213" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feGaussianBlur in="BackgroundImageFix" stdDeviation="4"/><feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_131_1449"/><feBlend in="SourceGraphic" in2="effect1_backgroundBlur_131_1449" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 819 B

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.672 6.117a.6.6 0 0 0-.331.536v2.693a.6.6 0 0 0 .33.537l2.803 1.405a.2.2 0 0 0 .29-.178V4.89a.2.2 0 0 0-.29-.179l-2.802 1.406z" fill="#12B969"/><g filter="url(#a)"><rect x=".667" y="2.894" width="10.667" height="10.213" rx="1.5" fill="#12B969"/></g><defs><filter id="a" x="-7.333" y="-5.106" width="26.667" height="26.213" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feGaussianBlur in="BackgroundImageFix" stdDeviation="4"/><feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_1977_11630"/><feBlend in="SourceGraphic" in2="effect1_backgroundBlur_1977_11630" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 823 B

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M.761 9.332C.001 7.134.963 4.773 2.833 3.39 4.252 2.343 5.676 1.285 6.49.681a.912.912 0 0 1 1.24.141l4.429 5.21c.29.34.29.841 0 1.182l-2.716 3.198a.893.893 0 0 0-.093 1.034 19.946 19.946 0 0 0 3.151 4.048 19.62 19.62 0 0 0 4.064 3.137.895.895 0 0 0 1.033-.094l3.186-2.702a.913.913 0 0 1 1.181 0l5.214 4.431c.366.311.429.852.143 1.238l-2.727 3.69c-1.372 1.855-3.714 2.798-5.895 2.047a28.865 28.865 0 0 1-11.04-6.908A28.855 28.855 0 0 1 .761 9.332z" fill="#12B969"/></svg>

After

Width:  |  Height:  |  Size: 554 B

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.917 7.57v-.731h1.5v.73a4.583 4.583 0 1 0 9.166 0v-.73h1.5v.73a6.084 6.084 0 0 1-5.333 6.038v1.302h-1.5v-1.302a6.084 6.084 0 0 1-5.333-6.038z" fill="#ADADAD"/><rect x="4.667" y="1.091" width="6.667" height="9.897" rx="3.333" fill="#ADADAD"/></svg>

After

Width:  |  Height:  |  Size: 373 B

@@ -0,0 +1 @@
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.917 7.57v-.731h1.5v.73a4.583 4.583 0 1 0 9.166 0v-.73h1.5v.73a6.084 6.084 0 0 1-5.333 6.038v1.302h-1.5v-1.302a6.084 6.084 0 0 1-5.333-6.038z" fill="#12B969"/><rect x="4.667" y="1.091" width="6.667" height="9.897" rx="3.333" fill="#12B969"/></svg>

After

Width:  |  Height:  |  Size: 373 B

@@ -0,0 +1,11 @@
import MacPermitZhPng from './mac/permit-zh.jpg';
import MacPermitEnPng from './mac/permit-en.jpg';
import WinPermitZhPng from './win/permit-zh.png';
import WinPermitEnPng from './win/permit-en.png';
export {
MacPermitZhPng,
MacPermitEnPng,
WinPermitZhPng,
WinPermitEnPng,
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><circle cx="14" cy="14" r="12" fill="#fff"/></g><path fill-rule="evenodd" clip-rule="evenodd" d="M10.667 10a3.333 3.333 0 0 1 5.937-2.08l-5.872 7.073a3.345 3.345 0 0 1-.065-.66V10zM9.63 16.321a4.782 4.782 0 0 1-.43-1.988v-1H8.133v1c0 1.047.275 2.03.755 2.881l.742-.893zm1.283 3.002.69-.83a4.8 4.8 0 0 0 7.198-4.16v-1h1.066v1a5.867 5.867 0 0 1-5.334 5.843V22h-1.066v-1.824a5.831 5.831 0 0 1-2.554-.853zm1.646-1.983 4.774-5.751v2.744a3.333 3.333 0 0 1-4.774 3.007z" fill="#E6395C"/><path d="m9.423 18.845 8.36-10.067" stroke="#E6395C" stroke-width="1.067" stroke-linecap="square" stroke-linejoin="round"/><defs><filter id="a" x="0" y="0" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset/><feGaussianBlur stdDeviation="1"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1741_10678"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1741_10678" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><circle cx="14" cy="14" r="12" fill="#fff"/></g><path fill-rule="evenodd" clip-rule="evenodd" d="M15.812 22.538c4.716-4.715 4.716-12.36 0-17.076l-1.767 1.767a9.575 9.575 0 0 1 0 13.541l1.767 1.768z" fill="#1C66E5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M12.188 19.136a7.264 7.264 0 0 0 0-10.273l-1.768 1.768a4.764 4.764 0 0 1 0 6.737l1.768 1.768zM9.121 16.07a2.927 2.927 0 0 0 0-4.14l-2.018 2.018a.073.073 0 0 1 0 .103l2.018 2.018z" fill="#1C66E5"/><defs><filter id="a" x="0" y="0" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset/><feGaussianBlur stdDeviation="1"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_124_647"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_124_647" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><circle cx="14" cy="14" r="12" fill="#fff"/></g><path fill-rule="evenodd" clip-rule="evenodd" d="M10.667 10a3.333 3.333 0 0 1 5.937-2.08l-5.872 7.073a3.345 3.345 0 0 1-.065-.66V10zM9.63 16.321a4.782 4.782 0 0 1-.43-1.988v-1H8.133v1c0 1.047.275 2.03.755 2.881l.742-.893zm1.283 3.002.69-.83a4.8 4.8 0 0 0 7.198-4.16v-1h1.066v1a5.867 5.867 0 0 1-5.334 5.843V22h-1.066v-1.824a5.831 5.831 0 0 1-2.554-.853zm1.646-1.983 4.774-5.751v2.744a3.333 3.333 0 0 1-4.774 3.007z" fill="#E6395C"/><path d="m9.423 18.845 8.36-10.067" stroke="#E6395C" stroke-width="1.067" stroke-linecap="square" stroke-linejoin="round"/><defs><filter id="a" x="0" y="0" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset/><feGaussianBlur stdDeviation="1"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1741_10678"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1741_10678" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><g opacity=".8"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.833 5A4.167 4.167 0 0 1 13.7 3.083l-7.503 9.039a4.152 4.152 0 0 1-.364-1.705V5zm-.772 8.49a5.79 5.79 0 0 1-.878-3.073v-1.25h-1.7v1.25c0 1.659.538 3.192 1.448 4.435l1.13-1.361zm2.011 3.852 1.15-1.385a5.817 5.817 0 0 0 7.595-5.54v-1.25h1.7v1.25a7.518 7.518 0 0 1-6.684 7.47V20H9.167v-2.112a7.468 7.468 0 0 1-2.095-.546zm2.326-2.802 4.769-5.745v1.622a4.167 4.167 0 0 1-4.77 4.123z" fill="#D5E0F2"/><path d="M5.041 16.654 15.325 4.27" stroke="#D5E0F2" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 668 B

@@ -0,0 +1 @@
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><circle cx="14" cy="14" r="12" fill="#fff"/></g><path fill-rule="evenodd" clip-rule="evenodd" d="M15.812 22.538c4.716-4.715 4.716-12.36 0-17.076l-1.767 1.767a9.575 9.575 0 0 1 0 13.541l1.767 1.768z" fill="#1C66E5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M12.188 19.136a7.264 7.264 0 0 0 0-10.273l-1.768 1.768a4.764 4.764 0 0 1 0 6.737l1.768 1.768zM9.121 16.07a2.927 2.927 0 0 0 0-4.14l-2.018 2.018a.073.073 0 0 1 0 .103l2.018 2.018z" fill="#1C66E5"/><defs><filter id="a" x="0" y="0" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset/><feGaussianBlur stdDeviation="1"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_124_647"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_124_647" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,20 @@
<template>
<Overlay
bgColor="#22262ed9"
:bgImage="defaultAvatarSrc"
:customStyle="{ position: 'absolute', zIndex: 0 }"
/>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script lang="ts" setup>
import Overlay from '../../base/Overlay/Overlay.vue';
import defaultAvatarSrc from '../../../assets/common/defaultAvatar.svg';
</script>
@@ -0,0 +1,129 @@
<template>
<div class="groupcall-container">
<TopBar />
<Waiting v-if="callStatus === CallStatus.CALLING && callRole === CallRole.CALLEE && !isFloatWindow" />
<MediaContainer />
<Tip />
<ButtonPanel />
<BackGround />
<SelectUser
v-if="showSelectUser"
:isNeedSearch="true"
:userList="groupMemberList"
:isPC="TUIGlobal.isPC"
:total="memberCount"
@confirm="handleSelectedResult"
@cancel="handleCancel"
@getMore="getMoreMemberList"
@search="handleSearch"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, provide, toRefs, onMounted, onUnmounted } from '../../../adapter-vue';
import TopBar from '../common/TopBar/TopBar.vue';
import Waiting from '../common/Waiting/Waiting.vue';
import MediaContainer from './MediaContainer/MediaContainer.vue';
import Tip from '../common/Tip/Tip.vue';
import ButtonPanel from '../common/ButtonPanel/ButtonPanel.vue';
import SelectUser from '../common/SelectUser/SelectUser.vue';
import BackGround from './BackGround/BackGround.vue';
import { ButtonPanelContextKey, FocusContextKey } from '../../context';
import { useCallInfoContext, useFloatWindowContext, useUserInfoExcludeVolumeContext } from '../../hooks';
import { CallRole, CallStatus, TUIGlobal, TUIStore, TUICallKitAPI, StoreName, NAME } from '../../../TUICallService';
const focusElement = ref(null);
const buttonPanelStatus = ref('open');
const showSelectUser = ref(false);
const memberCount = ref(0);
const groupMemberList = ref([]);
const backupGroupMemberList = ref([]);
const offset = ref(0);
const count: number = 30;
const FocusContextValue = focusElement;
const ButtonPanelContextValue = { status: buttonPanelStatus };
const { callRole, callStatus } = toRefs(useCallInfoContext());
const { isFloatWindow } = toRefs(useFloatWindowContext());
const { localUserInfoExcludeVolume, remoteUserListExcludeVolume } = toRefs(useUserInfoExcludeVolumeContext());
const handleShowSelectUser = async (value: boolean) => {
showSelectUser.value = value;
if (showSelectUser.value) {
await getGroupMemberList();
const groupProfile = await TUICallKitAPI.getGroupProfile();
memberCount.value = groupProfile.memberCount;
}
}
const handleCancel = () => {
showSelectUser.value = false;
TUIStore.update(StoreName.CALL, NAME.SHOW_SELECT_USER, false);
offset.value = 0;
groupMemberList.value = [];
}
const getMoreMemberList = async () => {
offset.value += count;
await getGroupMemberList();
}
const getGroupMemberList = async () => {
const memberList = await TUICallKitAPI.getGroupMemberList(count, offset.value);
const inCallUserIdList = [...remoteUserListExcludeVolume.value, localUserInfoExcludeVolume.value].map(obj => obj.userId);
groupMemberList.value.push(...memberList);
groupMemberList.value = groupMemberList.value.map(obj => {
if (inCallUserIdList.includes(obj.userID)) {
obj = { ...obj, isDisabled: true };
}
return obj;
});
backupGroupMemberList.value = groupMemberList.value;
};
const handleSelectedResult = async (selectedUserInfoList: Array<any>) => {
try {
if (selectedUserInfoList.length <= 0) {
return ;
}
showSelectUser.value = false;
TUIStore.update(StoreName.CALL, NAME.SHOW_SELECT_USER, false);
offset.value = 0;
const userIDList = selectedUserInfoList.map(obj => obj.userID);
await TUICallKitAPI.inviteUser({userIDList});
groupMemberList.value = [];
} catch (error) {
console.debug(error);
}
};
const handleSearch = (searchValue: string) => {
if (searchValue) {
groupMemberList.value = groupMemberList.value.filter(obj => {
return obj.userID.includes(searchValue) || obj.nick.includes(searchValue)
});
} else {
groupMemberList.value = backupGroupMemberList.value;
}
};
const watchOptions = {
[NAME.SHOW_SELECT_USER]: handleShowSelectUser,
};
onMounted(() => {
TUIStore.watch(StoreName.CALL, watchOptions, { notifyRangeWhenWatch: NAME.MYSELF });
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CALL, watchOptions);
});
ButtonPanelContextValue.status = ref('open');
FocusContextValue.value = null;
provide(ButtonPanelContextKey, ButtonPanelContextValue);
provide(FocusContextKey, FocusContextValue);
</script>
<style lang="scss" scoped>
.groupcall-container {
height: 100%;
}
</style>
@@ -0,0 +1,267 @@
<template>
<Portal id="source" :disabled="!isFloatWindow" source="#source" to="body">
<FloatWindow>
<div
:class="groupMediaContainerClassName"
:style="[mediaContainerStyle]"
>
<Grid
:unit="unit"
:enable-focus="enableFocus"
:focus="focus"
:length="streamLength"
:layout="layout"
@toggle="changeFocus"
>
<GridItem :index="0" :key="localUserInfoExcludeVolume.userId">
<Pusher
:domId="localUserInfoExcludeVolume.domId"
:show-audio-stream="!localUserInfoExcludeVolume.isVideoAvailable"
:show="visibleStreamIdList.includes(localUserInfoExcludeVolume.domId) && showStream"
>
<template v-slot:audio-stream>
<AudioStream
:user-id="localUserInfoExcludeVolume.userId"
:username="localUserInfoExcludeVolume.displayUserInfo"
:avatar="localUserInfoExcludeVolume.avatar"
:is-video-available="localUserInfoExcludeVolume.isVideoAvailable"
/>
</template>
<template v-slot:loading>
<StreamLoading v-if="isPusherLoading" />
</template>
<template v-slot:stream-info>
<TKStreamInfo
v-if="!isFloatWindow"
:is-self="true"
:show-nick-name="showNickName"
:showSwitchCameraButton="showSwitchCameraButton"
:showVirtualBackgroundButton="showVirtualBackgroundButton"
:showNetWorkStatus="isShowNetWork(localUserInfoExcludeVolume.userId)"
:nickName="localUserInfoExcludeVolume.displayUserInfo"
:isMuted="!localUserInfoExcludeVolume.isAudioAvailable"
:volume="volumeMap && volumeMap[localUserInfoExcludeVolume.domId]"
/>
</template>
</Pusher>
</GridItem>
<GridItem v-for="(remoteStreamItem, index) in remoteUserListExcludeVolume" :key="remoteStreamItem.userId" :index="index + 1">
<Player
:domId="remoteStreamItem.domId"
:show-audio-stream="isShowAudioStream(remoteStreamItem)"
:show="visibleStreamIdList.includes(remoteStreamItem.domId) && showStream"
>
<template v-slot:audio-stream>
<AudioStream
:user-id="remoteStreamItem.userId"
:username="remoteStreamItem.displayUserInfo"
:avatar="remoteStreamItem.avatar"
/>
</template>
<template v-slot:loading>
<StreamLoading v-if="!remoteStreamItem.isEnter" />
</template>
<template v-slot:stream-info>
<TKStreamInfo
v-if="!isFloatWindow"
:show-nick-name="TUIGlobal.isPC || String(focus) === String(index + 1)"
:show-control-button="false"
:showNetWorkStatus="isShowNetWork(remoteStreamItem.userId)"
:nickName="remoteStreamItem.displayUserInfo"
:is-muted="isMute(remoteStreamItem)"
:volume="volumeMap && volumeMap[remoteStreamItem.domId]"
/>
</template>
</Player>
</GridItem>
</Grid>
</div>
</FloatWindow>
</Portal>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
},
};
</script>
<script setup lang="ts">
import { ref, watch, toRefs, computed } from '../../../../adapter-vue';
import { CallMediaType, CallStatus, TUIGlobal, CallRole } from '../../../../TUICallService';
import Pusher from '../../common/Pusher/Pusher.vue';
import Player from '../../common/Player/Player.vue';
import Grid from '../../base/Grid/Grid.vue';
import GridItem from '../../base/Grid/GridItem/GridItem.vue';
import AudioStream from '../../common/AudioStream/AudioStream.vue';
import TKStreamInfo from '../../common/TKStreamInfo/TKStreamInfo.vue';
import FloatWindow from '../../common/FloatWindow/FloatWindow.vue';
import Portal from '../../base/Portal/Portal.vue';
import {
useUserInfoExcludeVolumeContext,
useCallInfoContext,
usePlayer,
useGetVolumeMap,
useFloatWindowContext,
useFocusContext,
useButtonPanelStatus,
useGroupCallLayout,
useCustomUI,
useNetWorkStatus
} from '../../../hooks';
import { classNames } from '../../base/util';
import StreamLoading from './StreamLoading/StreamLoading.vue';
const bigWindow = ref('local');
const showSmallWindow = ref(false);
const customUI = useCustomUI();
const { localUserInfoExcludeVolume, remoteUserListExcludeVolume } = toRefs(useUserInfoExcludeVolumeContext());
const { callStatus, callType, isShowEnableVirtualBackground, callRole } = toRefs(useCallInfoContext());
const focus = ref(TUIGlobal.isPC
? null
: callType.value === CallMediaType.VIDEO ? 0 : null); // video call localView default big
const { netWorkQualityList } = useNetWorkStatus();
const streamLength = computed(() => remoteUserListExcludeVolume.value.length + 1);
const wxPlayer = usePlayer();
const layout = useGroupCallLayout(focus, streamLength);
const volumeMap = useGetVolumeMap();
const { isFloatWindow } = toRefs(useFloatWindowContext());
const focusElement = useFocusContext();
const { status: panelStatus } = useButtonPanelStatus() || {};
const enableFocus = !TUIGlobal.isPC;
const isCurrentUserStreamZoomIn = computed(() => String(focus.value) === '0');
const showControlButton = computed(() => !TUIGlobal.isPC && isCurrentUserStreamZoomIn.value && localUserInfoExcludeVolume.value.isVideoAvailable)
const showSwitchCameraButton = computed(() => showControlButton.value);
const showVirtualBackgroundButton = computed(() => showControlButton.value && isShowEnableVirtualBackground.value && TUIGlobal.isWeChat);
const showNickName = computed(() => TUIGlobal.isPC || isCurrentUserStreamZoomIn.value);
const unit = computed(() => (TUIGlobal.isPC || isFloatWindow.value) ? '%' : 'vw');
const visibleStreamIdList = computed(() => {
return [localUserInfoExcludeVolume.value, ...remoteUserListExcludeVolume.value].map((stream) => {
if (isFloatWindow.value) {
if (volumeMap.value?.[stream.domId] >= 10) {
return stream.domId;
}
} else {
return stream.domId;
}
});
});
const showStream = computed(() => !(
callRole.value === CallRole.CALLEE
&& callStatus.value === CallStatus.CALLING
&& !isFloatWindow.value
));
const mediaContainerStyle = computed(() => {
let visibility = '';
if (callRole.value === CallRole.CALLEE && callStatus.value === CallStatus.CALLING && !isFloatWindow.value) {
visibility = 'hidden';
}
return {
visibility,
}
});
function changeFocus(value) {
focus.value = value;
focusElement.value = value;
panelStatus.value = value !== null ? 'close' : 'open';
}
function isShowAudioStream(stream) {
return !stream.isVideoAvailable;
}
function isMute(stream) {
return !stream.isAudioAvailable;
}
function isShowNetWork(userId) {
if(!netWorkQualityList.value) return;
const isRemoteUser = userId !== localUserInfoExcludeVolume.value.userId;
if(!TUIGlobal.isWeChat && isRemoteUser) return;
const targetNetwork = netWorkQualityList.value.find(item => item.userId === userId);
return targetNetwork && targetNetwork?.quality >= 4;
}
watch([remoteUserListExcludeVolume, callType], () => {
if (remoteUserListExcludeVolume.value?.[0]?.isEnter) {
bigWindow.value = 'remote';
}
if (callType.value === CallMediaType.AUDIO) {
showSmallWindow.value = false;
} else {
showSmallWindow.value = true;
}
});
const groupMediaContainerClassName = computed(() => classNames([
'groupcall-media-container',
{
mobile: !TUIGlobal.isPC,
pc: TUIGlobal.isPC,
'two-layout': streamLength.value === 2 && focus.value === null,
float: isFloatWindow.value,
}
]));
const isPusherLoading = computed(() => {
if (callStatus.value === CallStatus.CALLING) {
if ((callType.value === CallMediaType.AUDIO && !localUserInfoExcludeVolume.value.isAudioAvailable)
|| (callType.value === CallMediaType.VIDEO && !localUserInfoExcludeVolume.value.isVideoAvailable)) {
return true;
}
}
return false;
});
</script>
<style lang="scss" scoped>
.groupcall-media-container {
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
&.pc {
border-radius: 12px;
overflow: hidden;
&.two-layout {
margin-top: 20%;
}
.tk-toggle-window-item--small {
top: 2%;
left: 2%;
width: 22%;
height: 21%;
border-radius: 12px;
overflow: hidden;
}
}
&.mobile {
margin-top: 5.5vh;
&.float {
margin-top: 0;
}
&.two-layout {
margin-top: 15vh;
&.float {
margin-top: 0;
}
}
}
}
</style>
@@ -0,0 +1,22 @@
<template>
<div class="stream-loading-container">
<Loading mode="dot" color="#FFF" loadingWidth="50px" loadingHeight="50px" />
</div>
</template>
<script lang="ts" setup>
import Loading from '../../../base/Loading/Loading.vue';
</script>
<style scoped>
.stream-loading-container {
width: 100%;
height: 100%;
position: absolute;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
}
</style>
@@ -0,0 +1,165 @@
<template>
<Portal id="source" :disabled="!isFloatWindow" source="#source" to="body">
<FloatWindow>
<div :class="singleMediaContainerClassName">
<ToggleWindow
:big-window="largeViewName"
:show-small-window="showSmallWindow"
@toggle="handleToggle"
>
<ToggleWindowItem :value="ViewName.LOCAL" :key="ViewName.LOCAL">
<Pusher
:domId="localUserInfoExcludeVolume.domId"
:show-audio-stream="!localUserInfoExcludeVolume.isVideoAvailable"
>
<template v-slot:audio-stream>
<AudioStream
:userId="localUserInfoExcludeVolume.userId"
:username="localUserInfoExcludeVolume.displayUserInfo"
:avatar="localUserInfoExcludeVolume.avatar"
:is-video-available="localUserInfoExcludeVolume.isVideoAvailable"
:is-small-window="!(largeViewName === ViewName.LOCAL)"
:is-muted="!localUserInfoExcludeVolume.isAudioAvailable"
:volume="volumeMap && volumeMap[localUserInfoExcludeVolume.domId]"
/>
</template>
<template v-slot:stream-info>
<TKStreamInfo
v-if="callType === CallMediaType.VIDEO"
:nick-name="localUserInfoExcludeVolume.displayUserInfo"
:is-self="true"
:is-muted="!localUserInfoExcludeVolume.isAudioAvailable"
:volume="volumeMap && volumeMap[localUserInfoExcludeVolume.domId]"
/>
</template>
</Pusher>
</ToggleWindowItem>
<ToggleWindowItem :value="ViewName.REMOTE" :key="ViewName.REMOTE">
<Player
:dom-id="playerDomId"
:show-audio-stream="!isPusherVideoAvailable"
>
<template v-slot:audio-stream>
<AudioStream
:userId="remoteUserListExcludeVolume[0] && remoteUserListExcludeVolume[0].userId"
:username="remoteUserListExcludeVolume[0] && remoteUserListExcludeVolume[0].displayUserInfo"
:avatar="remoteUserListExcludeVolume[0] && remoteUserListExcludeVolume[0].avatar"
:is-video-available="isPusherVideoAvailable"
:is-small-window="!(largeViewName === ViewName.REMOTE)"
:is-muted="remoteUserListExcludeVolume[0] && !remoteUserListExcludeVolume[0].isAudioAvailable"
:volume="remoteStreamVolume"
/>
</template>
<template v-slot:stream-info>
<TKStreamInfo
v-if="callType === CallMediaType.VIDEO"
:nick-name="remoteUserListExcludeVolume[0] && remoteUserListExcludeVolume[0].displayUserInfo"
:is-muted="remoteUserListExcludeVolume[0] && !remoteUserListExcludeVolume[0].isAudioAvailable"
:volume="remoteStreamVolume"
/>
</template>
</Player>
</ToggleWindowItem>
</ToggleWindow>
</div>
</FloatWindow>
</Portal>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
},
};
</script>
<script setup lang="ts">
import { ref, watch, toRefs, computed } from '../../../../adapter-vue';
import { CallMediaType, CallStatus, TUIGlobal } from '../../../../TUICallService';
import ToggleWindow from '../../base/ToggleWindow/ToggleWindow.vue';
import Portal from '../../base/Portal/Portal.vue';
import ToggleWindowItem from '../../base/ToggleWindow/ToggleWindowItem/ToggleWindowItem.vue';
import TKStreamInfo from '../../common/TKStreamInfo/TKStreamInfo.vue';
import Pusher from '../../common/Pusher/Pusher.vue';
import Player from '../../common/Player/Player.vue';
import AudioStream from '../../common/AudioStream/AudioStream.vue';
import FloatWindow from '../../common/FloatWindow/FloatWindow.vue';
import {
useUserInfoExcludeVolumeContext,
useCallInfoContext,
useGetVolumeMap,
usePlayer,
useFloatWindowContext,
} from '../../../hooks';
import { useGetLargeViewName } from '../hooks/useGetLargeViewName';
import { classNames } from '../../base/util';
import { ViewName } from '../../../../TUICallService/const/index';
const largeViewName = useGetLargeViewName();
const showSmallWindow = ref(true);
const { isFloatWindow } = toRefs(useFloatWindowContext());
function toggle() {
isFloatWindow.value = !isFloatWindow.value;
}
const { localUserInfoExcludeVolume, remoteUserListExcludeVolume } = toRefs(useUserInfoExcludeVolumeContext());
const volumeMap = useGetVolumeMap();
const { callType, callStatus } = toRefs(useCallInfoContext());
const wxPlayer = usePlayer();
const isPusherVideoAvailable = computed(() => remoteUserListExcludeVolume.value?.[0]?.isVideoAvailable);
const playerDomId = computed(() => remoteUserListExcludeVolume.value?.[0]?.domId);
const remoteStreamVolume = computed(() => volumeMap.value?.[remoteUserListExcludeVolume.value?.[0]?.domId]);
watch([callType, callStatus], () => {
if (callType.value === CallMediaType.AUDIO || callStatus.value === CallStatus.CALLING) {
showSmallWindow.value = false;
} else {
showSmallWindow.value = true;
}
}, {
immediate: true,
});
const singleMediaContainerClassName = computed(() => classNames([
'singlecall-media-container',
{
mobile: !TUIGlobal.isPC,
pc: TUIGlobal.isPC,
float: isFloatWindow.value,
}
]));
function handleToggle(value) {
largeViewName.value = value;
}
</script>
<style lang="scss" scoped>
.singlecall-media-container {
width: 100%;
height: 100%;
position: absolute;
z-index: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
&.float {
position: relative;
}
&.pc {
border-radius: 12px;
overflow: hidden;
}
}
.roggle-btn {
position: absolute;
left: 100px;
z-index: 100;
top: 0;
}
</style>
@@ -0,0 +1,28 @@
<template>
<div class="singlecall-container">
<TopBar />
<Waiting v-if="callStatus === CallStatus.CALLING" />
<MediaContainer />
<Tip />
<ButtonPanel />
</div>
</template>
<script setup lang="ts">
import { toRefs } from '../../../adapter-vue';
import { CallStatus } from '../../../TUICallService';
import TopBar from '../common/TopBar/TopBar.vue';
import Waiting from '../common/Waiting/Waiting.vue';
import MediaContainer from './MediaContainer/MediaContainer.vue';
import Tip from '../common/Tip/Tip.vue';
import ButtonPanel from '../common/ButtonPanel/ButtonPanel.vue';
import { useCallInfoContext } from '../../hooks';
const { callStatus } = toRefs(useCallInfoContext());
</script>
<style lang="scss" scoped>
.singlecall-container {
height: 100%;
}
</style>
@@ -0,0 +1,32 @@
import { CallStatus, LayoutMode, ViewName } from '../../../../TUICallService/const';
import { ref, watch, toRefs } from '../../../../adapter-vue';
import { useCallInfoContext, useCustomUI, useUserInfoExcludeVolumeContext } from '../../../hooks';
export function useGetLargeViewName() {
const config = useCustomUI();
const { callStatus } = toRefs(useCallInfoContext());
const largeViewName = ref(ViewName.LOCAL);
const { remoteUserListExcludeVolume } = toRefs(useUserInfoExcludeVolumeContext());
watch([remoteUserListExcludeVolume, config, callStatus], () => {
if (callStatus.value === CallStatus.CALLING) {
return;
}
const c2cLayoutModes = [LayoutMode.RemoteInLargeView, LayoutMode.LocalInLargeView];
const customLayoutMode = config.value?.layoutMode;
if (c2cLayoutModes.includes(customLayoutMode)) {
// @ts-ignore
largeViewName.value = config.value?.layoutMode;
return;
}
if (remoteUserListExcludeVolume.value?.[0]?.isEnter) {
largeViewName.value = ViewName.REMOTE;
}
}, {
immediate: true,
});
return largeViewName;
}
@@ -0,0 +1,38 @@
const Fit = ['fill', 'contain', 'cover'] as const;
const Shape = ['circle', 'square'] as const;
export const AvatarProps = {
icon: {
type: String,
},
size: {
type: [Number, String],
default: 100,
},
shape: {
type: String,
values: Shape,
default: 'square',
},
src: {
type: String,
},
defaultSrc: {
type: String,
},
text: {
type: String,
},
fit: {
type: String,
values: Fit,
default: 'cover',
},
customClass: {
type: String,
},
} as const;
export const avatarEmits = {
error: (evt) => evt,
};
@@ -0,0 +1,53 @@
<template>
<div :class="classname" :style="[avatarStyle]">
<TKImage v-if="avatarSrc" :fit="fit" :width="width" :height="height" :src="avatarSrc" @error="handleError" />
<slot v-else />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<script setup lang="ts">
import { computed, ref, watch } from '../../../../adapter-vue';
import TKImage from '../TKImage/TKImage.vue';
import { classNames } from '../util';
import { AvatarProps } from './Avatar';
import { PREFIX } from '../constants/index';
const props = defineProps(AvatarProps);
const avatarSrc = ref('');
watch(() => props.src, () => {
avatarSrc.value = props.src;
}, {
immediate: true,
});
const classname = computed(() => classNames([
`${PREFIX}-avatar`,
`${PREFIX}-avatar--${props.shape}`,
`${PREFIX}-avatar--${props.size}`,
props.customClass,
]));
const width = computed(() => typeof props.size === 'number' ? `${props.size}px` : props.size);
const height = computed(() => typeof props.size === 'number' ? `${props.size}px` : props.size);
const avatarStyle = computed(() => ({ width: width.value, height: height.value }));
function handleError(e) {
console.error(e);
if (props.defaultSrc) {
avatarSrc.value = props.defaultSrc;
}
}
</script>
<style lang="scss">
@import './style/Avatar.scss';
</style>
@@ -0,0 +1,40 @@
@import '../../styles/var.scss';
@import '../../styles/common.scss';
$--tk-avatar-text-color: $--tk-color-white;
$--tk-avatar-bg-color: $--tk-text-color-disabled;
$--tk-avatar-text-size: 14px;
$--tk-avatar-icon-size: 18px;
$--tk-avatar-border-radius: $--tk-border-radius-base;
$--tk-avatar-size-large: 56px;
$--tk-avatar-size-small: 24px;
$--tk-avatar-size: 40px;
.#{$PREFIX}-avatar {
display: inline-flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
text-align: center;
overflow: hidden;
color: $--tk-avatar-text-color;
background: $--tk-avatar-bg-color;
width: $--tk-avatar-size;
height: $--tk-avatar-size;
font-size: $--tk-avatar-text-size;
&.#{$PREFIX}-avatar--square {
border-radius: $--tk-avatar-border-radius;
}
&.#{$PREFIX}-avatar--circle {
border-radius: $--tk-border-radius-circle;
}
.#{$PREFIX}-avatar_img {
width: 100%;
height: 100%;
}
.#{$PREFIX}-avatar_image {
width: 100%;
height: 100%;
}
}
@@ -0,0 +1,60 @@
const Size = ['small','middle','large'] as const;
const Direction = ['row','column'] as const;
const Shape = ['circle','round'] as const;
export const ButtonProps = {
iconSrc: {
type: String,
},
iconSize: {
type: Number,
},
text: {
type: String,
},
loading: {
type: Boolean,
default: false,
},
loadingColor: {
type: String,
default: '#fff',
},
loadingWidth: {
type: String,
default: '40px',
},
loadingHeight: {
type: String,
default: '40px',
},
size: {
type: String,
values: Size,
default: 'middle',
},
width: {
type: String,
},
height: {
type: String,
},
color: {
type: String,
},
direction: {
type: String,
values: Direction,
default: 'row',
},
shape: {
type: String,
values: Shape,
},
buttonStyle: {
type: Object,
},
buttonTextStyle: {
type: Object,
},
};
@@ -0,0 +1,61 @@
<template>
<div :style="[style]" :class="buttonClassName" @click="handleClick">
<Loading
v-if="loading"
:loadingWidth="loadingWidth"
:loadingHeight="loadingHeight"
:color="loadingColor"
/>
<Icon v-if="iconSrc && !loading" :size="iconSize" :src="iconSrc"></Icon>
<div
v-if="text"
:style="[buttonTextStyle]"
:class="buttonTextClassName"
>
{{ text }}
</div>
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
},
};
</script>
<script setup lang="ts">
import { computed } from '../../../../adapter-vue';
import Loading from '../Loading/Loading.vue';
import Icon from '../Icon/Icon.vue';
import { classNames, IS_PC } from '../util';
import { ButtonProps } from './Button';
import { PREFIX } from '../constants/index';
const props = defineProps(ButtonProps);
const buttonTextClassName = `${PREFIX}-button--content`;
const style = computed(() => ({
width: props.width,
height: props.height,
backgroundColor: props.color,
flexDirection: props.direction,
cursor: IS_PC ? 'pointer' : 'auto',
...props.buttonStyle,
}));
const buttonClassName = classNames([
`${PREFIX}-button`,
{[`${PREFIX}-${props.shape}`]: props.shape},
`${PREFIX}-button--${props.size}`
]);
const emit = defineEmits(['click']);
const handleClick = (event) => {
!props.loading && emit('click', event);
};
</script>
<style lang="scss">
@import './style/Button.scss';
</style>
@@ -0,0 +1,42 @@
@import '../../styles/var.scss';
@import '../../styles/common.scss';
$--tk-button-size-default: $--tk-button-size-middle;
.#{$PREFIX}-button {
display: inline-flex;
align-items: center;
justify-content: center;
&.#{$PREFIX}-button--small {
height: $--tk-button-size-small;
}
&.#{$PREFIX}-button--middle {
height: $--tk-button-size-middle;
}
&.#{$PREFIX}-button--large {
height: $--tk-button-size-large;
}
.#{$PREFIX}-button--content {
color: $--tk-text-color-regular;
font-size: $--tk-font-size-base;
font-weight: $--tk-font-weight-primary;
}
&.#{$PREFIX}-circle {
&.#{$PREFIX}-button--small {
width: $--tk-button-size-small;
}
&.#{$PREFIX}-button--default {
width: $--tk-button-size-default;
}
&.#{$PREFIX}-button--large {
width: $--tk-button-size-large;
}
}
}
@@ -0,0 +1,26 @@
const Unit = ['%', 'vw'];
export const GridProps = {
length: {
type: Number,
default: 0,
},
unit: {
type: String,
values: Unit,
default: '%',
},
enableFocus: {
type: Boolean,
default: false,
},
layout: {
type: Array,
},
focus: {
type: [String, Number],
},
} as const;
export const ChangeFocusEmits = ['change', 'toggle'];
export const GridContextKey = 'GridContextKey';
@@ -0,0 +1,40 @@
<template>
<div :style="{ height: '100%'}">
<slot />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script setup lang="ts">
import { provide, watch, ref } from '../../../../adapter-vue';
import { ChangeFocusEmits, GridContextKey, GridProps } from './Grid';
const props = defineProps(GridProps);
const focus = ref(props.focus);
const layout = ref(props.layout);
const unit = ref(props.unit);
const emit = defineEmits(ChangeFocusEmits);
function handleFocusChange(value) {
emit('toggle', value);
}
watch(() => props.focus, () => focus.value = props.focus);
watch(() => props.layout, () => layout.value = props.layout);
watch(() => props.unit, () => unit.value = props.unit);
provide(GridContextKey, {
layout,
enableFocus: props.enableFocus,
handleFocusChange,
focus,
unit,
});
</script>
@@ -0,0 +1,81 @@
<template>
<div
v-if="show"
:class="gridItemClassName"
:style="[style]"
@click="handleClick"
>
<slot />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script setup lang="ts">
import { inject, computed, ref, watchEffect, watch, onUnmounted } from '../../../../../adapter-vue';
import { GridContextKey } from '../Grid';
import { PREFIX } from '../../constants/index';
import { IS_PC, IS_H5, classNames, findTarget } from '../../util';
const props = defineProps(['index', 'height', 'customStyle']);
const {
layout,
enableFocus,
handleFocusChange,
focus,
unit,
} = inject(GridContextKey);
const colWidth = 100 / 12;
const rowHeight = colWidth;
const style = ref({});
const gridItemClassName = classNames([
`${PREFIX}-grid-item`,
{
pc: IS_PC,
mobile: !IS_PC,
h5: IS_H5,
},
]);
const handleClick = () => {
const value = String(props.index) === String(focus.value) ? null : props.index;
enableFocus && handleFocusChange(value);
};
const show = computed(() => !!findTarget(layout.value, { key: 'i', value: props.index}));
watchEffect(() => {
const target = findTarget(layout.value, { key: 'i', value: props.index});
if (!target) return;
const { x, y, w, h, customStyle, customProps } = target;
style.value = {
width: w * colWidth + unit.value,
height: props.height || h * rowHeight + unit.value,
left: x * colWidth + unit.value,
top: y * rowHeight + unit.value,
position: 'absolute',
visibility: customProps?.show === false ? 'hidden' : '',
...props.customStyle,
...customStyle,
};
});
onUnmounted(() => {
if (String(props.index) === String(focus.value)) {
enableFocus && handleFocusChange(null);
}
});
</script>
<style lang="scss">
@import './style/GridItem.scss';
</style>
@@ -0,0 +1,13 @@
@import '../../../styles/common.scss';
@import '../../../styles/var.scss';
.#{$PREFIX}-grid-item {
display: flex;
justify-content: center;
&.h5 {
transition-property: width,height,left,top;
transition-duration: 0.3s;
transition-timing-function: ease-in;
}
}
@@ -0,0 +1,9 @@
export const IconProps = {
src: {
type: String
},
size: {
type: Number,
default: 20,
},
};
@@ -0,0 +1,21 @@
<template>
<TKImage :src="src" :width="width" :height="height"></TKImage>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
},
};
</script>
<script setup lang="ts">
import { computed } from '../../../../adapter-vue';
import { IconProps } from './Icon';
import TKImage from '../TKImage/TKImage.vue';
const props = defineProps(IconProps);
const width = computed(() => `${props.size}px`);
const height = computed(() => `${props.size}px`);
</script>
@@ -0,0 +1,23 @@
const ColJustify = ['start', 'center', 'end', 'space-around', 'space-between', 'space-evenly'] as const;
const ColAlign = ['top', 'middle', 'bottom'] as const;
export const colProps = {
span: {
type: Number,
default: 24,
},
justify: {
type: String,
values: ColJustify,
default: 'start',
},
align: {
type: String,
values: ColAlign,
default: 'middle',
},
offset: {
type: Number,
default: 0,
},
};
@@ -0,0 +1,46 @@
<template>
<div
:class="classname"
:style="{
width,
marginLeft,
paddingLeft,
paddingRight,
}">
<slot />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script setup lang="ts">
import { computed, inject } from '../../../../../adapter-vue';
import { classNames } from '../../util';
import { colProps } from './Col';
import { RowContextKey } from '../constant';
import { PREFIX } from '../../constants/index';
const props = defineProps(colProps);
const { gutter } = inject(RowContextKey, { gutter: computed(() => 0)});
const classname = classNames([
`${PREFIX}-col`,
`${PREFIX}-justify-${props.justify}`,
`${PREFIX}-align-${props.align}`,
]);
const width = computed(() => `${props.span / 24 * 100}%`);
const marginLeft = computed(() => `${props.offset / 24 * 100}%`);
const paddingLeft = computed(() => `${gutter.value / 2}px`);
const paddingRight = paddingLeft;
</script>
<style lang="scss">
@import './style/Col.scss';
</style>
@@ -0,0 +1,9 @@
@import '../../../styles/common.scss';
@import '../../../styles/var.scss';
.#{$PREFIX}-col {
display: flex;
flex-wrap: wrap;
position: relative;
box-sizing: border-box;
}
@@ -0,0 +1,23 @@
const RowJustify = ['start', 'center', 'end', 'space-around', 'space-between', 'space-evenly'] as const;
const RowAlign = ['top', 'middle' , 'bottom'] as const;
export const rowProps = {
gutter: {
type: Number,
default: 0,
},
justify: {
type: String,
values: RowJustify,
default: 'start',
},
align: {
type: String,
values: RowAlign,
default: 'top',
},
customStyle: {
type: Object,
default: () => {},
},
} as const;
@@ -0,0 +1,38 @@
<template>
<div :class="classname" :style="[customStyle]" >
<slot />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script setup lang="ts">
import { provide, computed } from '../../../../../adapter-vue';
import { rowProps } from './Row';
import { classNames } from '../../util';
import { RowContextKey } from '../constant';
import { PREFIX } from '../../constants/index';
const props = defineProps(rowProps);
const gutter = computed(() => props.gutter);
provide(RowContextKey, {
gutter,
});
const classname = classNames([
`${PREFIX}-row`,
`${PREFIX}-justify-${props.justify}`,
`${PREFIX}-align-${props.align}`,
]);
</script>
<style lang="scss">
@import './style/Row.scss';
</style>
@@ -0,0 +1,10 @@
@import '../../../styles/var.scss';
@import '../../../styles/common.scss';
.#{$PREFIX}-row {
display: flex;
flex-wrap: wrap;
position: relative;
box-sizing: border-box;
width: 100%;
}
@@ -0,0 +1 @@
export const RowContextKey = 'RowContextKey';
@@ -0,0 +1,13 @@
export const LoadingCircleProps = {
width: {
type: String,
default: '40px',
},
height: {
type: String,
default: '40px',
},
color: {
type: String,
},
} as const;
@@ -0,0 +1,36 @@
<template>
<div :class="circleContainerClassName" :style="[circleContainerStyle]"></div>
</template>
<script setup lang="ts">
import { computed } from '../../../../../adapter-vue';
import { LoadingCircleProps } from './Circle';
import { PREFIX } from '../../constants';
const props = defineProps(LoadingCircleProps);
const circleContainerStyle = computed(() => {
const style = {
width: props.width,
height: props.height,
};
if (props.color) {
style['--tk-loading-primary-color'] = props.color;
}
return style;
});
const circleContainerClassName = `${PREFIX}-loading_circle-container`;
</script>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<style lang="scss">
@import './style/Circle.scss';
</style>
@@ -0,0 +1,26 @@
@import '../../../styles/var.scss';
@import '../../../styles/common.scss';
.#{$PREFIX}-loading_circle-container {
height: 100%;
border: $--tk-loading-border-width $--tk-loading-border-style;
border-radius: 50%;
border-top-color: $--tk-loading-other-color;
border-right-color: $--tk-loading-other-color;
border-bottom-color: $--tk-loading-primary-color;
border-left-color: $--tk-loading-primary-color;
background: 0 0;
vertical-align: middle;
box-sizing: border-box;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@@ -0,0 +1,13 @@
export const LoadingDotProps = {
width: {
type: String,
default: '40px',
},
height: {
type: String,
default: '40px',
},
color: {
type: String,
},
} as const;
@@ -0,0 +1,44 @@
<template>
<div :class="dotContainerClassName" :style="[dotContainerStyle]">
<div :class="dotClassName" :style="[dotStyle]" />
<div :class="dotClassName" :style="[dotStyle]" />
<div :class="dotClassName" :style="[dotStyle]" />
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<script setup lang="ts">
import { computed } from '../../../../../adapter-vue';
import { LoadingDotProps } from './Dot';
import { PREFIX } from '../../constants';
const props = defineProps(LoadingDotProps);
const dotContainerClassName = `${PREFIX}-loading_dot-container`;
const dotClassName = `${PREFIX}-loading_dot`
const dotContainerStyle = computed(() => ({
width: props.width,
height: props.height,
}));
const dotStyle = computed(() => {
const style = {};
if (props.color) {
style.backgroundColor = props.color;
}
return style;
});
</script>
<style lang="scss">
@import './style/Dot.scss';
</style>
@@ -0,0 +1,61 @@
@import '../../../styles/var.scss';
@import '../../../styles/common.scss';
.#{$PREFIX}-loading_dot-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
& .#{$PREFIX}-loading_dot:nth-child(1) {
opacity: 0;
animation-duration: 0.8s;
animation-delay: 0s;
animation-play-state: running;
}
& .#{$PREFIX}-loading_dot:nth-child(2) {
opacity: 0.083;
animation-duration: 0.8s;
animation-delay: 0.26666666666666666s;
animation-play-state: running;
}
& .#{$PREFIX}-loading_dot:nth-child(3) {
opacity: 0.1667;
animation-duration: 0.8s;
animation-delay: 0.5333333333333333s;
animation-play-state: running;
}
.#{$PREFIX}-loading_dot {
width: 20%;
height: 20%;
border-radius: 50%;
background-color: $--tk-loading-primary-color;
animation-duration: 1.8s;
animation-name: dotting;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-fill-mode: both;
}
}
@keyframes dotting {
0% {
opacity: 0.15;
}
1% {
opacity: 0.8;
}
33% {
opacity: 0.8;
}
34% {
opacity: 0.15;
}
100% {
opacity: 0.15;
}
}
@@ -0,0 +1,29 @@
const LoadingMode = ['circle', 'dot'] as const;
const LoadingLayout = ['row', 'column'] as const;
export const LoadingProps = {
mode: {
type: String,
values: LoadingMode,
default: 'circle',
},
loadingWidth: {
type: String,
default: '40px',
},
loadingHeight: {
type: String,
default: '40px',
},
color: {
type: String,
},
text: {
type: String,
},
layout: {
type: String,
values: LoadingLayout,
default: 'column',
},
} as const;
@@ -0,0 +1,36 @@
<template>
<div :class="loadingContainerClassName" :style="[loadingContainerStyle]">
<LoadingDot v-if="mode === 'dot'" :width="loadingWidth" :height="loadingHeight" :color="color" />
<LoadingCircle v-else :width="loadingWidth" :height="loadingHeight" :color="color" />
<div v-if="text" :class="loadingTextClassName">{{ text }}</div>
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<script setup lang="ts">
import { computed } from '../../../../adapter-vue';
import LoadingDot from './Dot/Dot.vue';
import LoadingCircle from './Circle/Circle.vue';
import { LoadingProps } from './Loading';
import { PREFIX } from '../constants/index';
const props = defineProps(LoadingProps);
const loadingContainerClassName = `${PREFIX}-loading ${PREFIX}-loading--${props.mode}`;
const loadingTextClassName = `${PREFIX}-loading_text`;
const loadingContainerStyle = computed(() => ({
flexDirection: props.layout,
}));
</script>
<style lang="scss">
@import './style/Loading.scss';
</style>
@@ -0,0 +1,18 @@
@import '../../styles/var.scss';
@import '../../styles/common.scss';
$--tk-loading-primary-color: $--tk-color-primary;
$--tk-loading-other-color: #fff0;
$--tk-loading-border-width: 2px;
$--tk-loading-border-style: $--tk-border-style;
$--tk-loading-layout: column;
.#{$PREFIX}-loading {
display: flex;
align-items: center;
flex-direction: $--tk-loading-layout;
.#{$PREFIX}-loading_text {
margin: 10px;
}
}
@@ -0,0 +1,117 @@
import { ref, watch } from '../../../../adapter-vue';
import { NAME } from '../constants';
const MessageType = [NAME.SUCCESS, NAME.INFO, NAME.WARNING, NAME.ERROR] as const;
export const MessageProps = {
isShow: {
type: Boolean,
default: false,
},
message: {
type: String,
default: '',
},
type: {
type: String,
values: MessageType,
default: NAME.INFO,
},
duration: {
type: Number,
default: 3000,
},
offset: {
type: Number,
default: 16,
},
showClose: {
type: Boolean,
default: false,
},
showIcon: {
type: Boolean,
default: true,
},
customClass: {
type: String,
},
customStyle: {
type: Object
},
} as const;
export const MessageEmits = {
onClose: null,
};
export function useMessage(props, emits) {
const messageContent = ref(props?.message || MessageProps.message.default);
const messageDuration = ref(props?.duration || MessageProps.duration.default);
const messageType = ref(props?.type || MessageProps.type.default);
const messageOffset = ref(props?.offset || MessageProps.offset.default);
const isShowCloseIcon = ref(props?.showClose || MessageProps.showClose.default);
const visible = ref<boolean>(false);
let timerId: any = -1;
const show = (messageObj?: any) => {
if (timerId > -1) {
clearTimeout(timerId);
timerId = -1;
}
visible.value = true;
updateData(messageObj || {});
if (messageDuration.value) {
timerId = setTimeout(() => {
close();
}, messageDuration.value);
}
};
const close = () => {
visible.value = false;
if (timerId > -1) {
clearTimeout(timerId);
timerId = -1;
}
};
const updateData = (messageObj?: any) => {
const {
message = messageContent.value,
type = messageType.value,
offset = messageOffset.value,
duration = messageDuration.value,
showClose = isShowCloseIcon.value,
} = messageObj;
// duration 值为 0 时,Message 会一直展示
messageDuration.value = props?.duration === 0 ? props?.duration : duration;
messageContent.value = message;
messageType.value = type;
messageOffset.value = offset;
isShowCloseIcon.value = showClose;
};
watch(
() => props?.isShow,
(newValue) => {
if (newValue) {
show();
}
},
{ immediate: true },
);
watch(visible, (newValue) => {
if (!newValue) {
emits('onClose');
}
});
return {
messageContent,
messageDuration,
messageType,
messageOffset,
isShowCloseIcon,
visible,
show,
close,
};
}
@@ -0,0 +1,60 @@
<template>
<div v-if="visible" :class="messageClassName" :style="[messageStyle]">
<div :class="typeIconClassName" v-show="showIcon">
<Icon :src="IconSrcMap[messageType]"></Icon>
</div>
<span>{{ messageContent }}</span>
<div v-show="isShowCloseIcon" :class="closeIconClassName" @click="close">
<Icon :src="CloseSrc"></Icon>
</div>
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<script setup lang="ts">
import { computed } from '../../../../adapter-vue';
import Icon from '../Icon/Icon.vue';
import { classNames } from '../util';
import { PREFIX } from '../constants/index';
import { MessageProps, MessageEmits, useMessage } from './Message';
import ErrorSrc from '../assets/message/error.svg';
import SuccessSrc from '../assets/message/success.svg';
import WaringSrc from '../assets/message/warning.svg';
import InfoSrc from '../assets/message/info.svg';
import CloseSrc from '../assets/message/close.svg';
const props = defineProps(MessageProps);
const emits = defineEmits(MessageEmits);
const { messageContent, messageType, messageOffset, isShowCloseIcon, visible, show, close } = useMessage(props, emits);
const messageClassName = computed(() => classNames([
`${PREFIX}-message`,
`${PREFIX}-message--${messageType.value}`,
props.customClass,
]));
const typeIconClassName = computed(() => classNames([`${PREFIX}-message_icon`]));
const closeIconClassName = computed(() => classNames([`${PREFIX}-message_close`]));
const top = computed(() => `${messageOffset.value}px`);
const messageStyle = computed(() => ({top: top.value, ...props.customStyle}));
const IconSrcMap = {
info: InfoSrc,
waring: WaringSrc,
success: SuccessSrc,
error: ErrorSrc,
};
defineExpose({
show,
close,
});
</script>
<style lang="scss">
@import './style/Message.scss';
</style>
@@ -0,0 +1,49 @@
@import '../../styles/var.scss';
@import '../../styles/common.scss';
$--tk-message-font-size: $--tk-font-size-base;
$--tk-message-border-radius: $--tk-border-radius-base;
.#{$PREFIX}-message {
position: fixed;
left: 50%;
transform: translateX(-50%);
padding: 10px;
border-radius: #{$--tk-message-border-radius};
display: flex;
align-items: center;
justify-content: center;
font-size: #{$--tk-message-font-size};
z-index: 9999;
color: $--tk-color-black;
border-color: $--tk-color-info-light-8;
background-color: $--tk-color-info-light-9;
.#{$PREFIX}-message_icon {
margin-right: 5px;
}
.#{$PREFIX}-message_close {
cursor: pointer;
margin-left: 5px;
}
}
.#{$PREFIX}-message--info {
color: $--tk-color-info;
border-color: $--tk-color-info-light-8;
background-color: $--tk-color-info-light-9;
}
.#{$PREFIX}-message--success {
color: $--tk-color-success;
border-color: $--tk-color-success-light-8;
background-color: $--tk-color-success-light-9;
}
.#{$PREFIX}-message--warning {
color: $--tk-color-warning;
border-color: $--tk-color-warning-light-8;
background-color: $--tk-color-warning-light-9;
}
.#{$PREFIX}-message--error {
color: $--tk-color-error;
border-color: $--tk-color-error-light-8;
background-color: $--tk-color-error-light-9;
}
@@ -0,0 +1,37 @@
<template>
<div>
<Message
v-for="(item, index) in messageList"
:isShow="item.isShow"
:message="item.message"
:type="item.type"
:offset="handleOffset(index)"
:duration="item.duration"
:showClose="item.showClose"
:key="index"
></Message>
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
}
}
</script>
<script setup lang="ts">
import { watch } from '../../../../adapter-vue';
import Message from '../Message/Message.vue';
const props = defineProps(['messageList']);
const handleOffset = (index: number) => {
return 16 + 50 * index; // 16 represents the default offset, and 50 is the distance between the two messages
}
watch(
() => props.messageList,
() => {},
{ immediate: true },
)
</script>
@@ -0,0 +1,46 @@
export const OverlayProps = {
show: {
type: Boolean,
default: true,
},
showMask: {
type: Boolean,
default: true,
},
showBackgroundImage: {
type: Boolean,
default: true,
},
blur: {
type: Boolean,
default: true,
},
bgColor: {
type: String,
},
bgImage: {
type: String,
},
zIndex: {
type: Number,
default: 11000,
},
customClass: {
type: String,
},
customStyle: {
type: Object,
},
customMaskStyle: {
type: Object,
},
fit: {
type: String,
default: 'cover',
},
defaultSrc: {
type: String,
},
} as const;
export const OverlayEmits = ['click', 'error'];
@@ -0,0 +1,75 @@
<template>
<div
v-if="show"
:class="overlayContainerClassName"
:style="[overlayStyle]"
@click="handleClick"
>
<div :class="maskContainerClassName">
<div
v-if="showMask"
:class="maskClassName"
:style="[maskStyle]"
/>
<TKImage
v-if="showBackgroundImage"
:fit="fit"
:src="bgImage"
width="100%"
height="100%"
:defaultSrc="defaultSrc"
@error="handleError"
/>
</div>
<div :class="slotClassName">
<slot />
</div>
</div>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true
}
}
</script>
<script setup lang="ts">
import { computed } from '../../../../adapter-vue';
import TKImage from '../TKImage/TKImage.vue';
import { OverlayProps, OverlayEmits } from './Overlay';
import { classNames } from '../util';
import { PREFIX } from '../constants/index';
const props = defineProps(OverlayProps);
const emit = defineEmits(OverlayEmits);
const overlayContainerClassName = classNames([
`${PREFIX}-overlay`,
props.customClass,
]);
const maskContainerClassName = `${PREFIX}-overlay_mask-container`;
const maskClassName = computed(() => classNames([
`${PREFIX}-overlay_mask`,
{ [`${PREFIX}-blur`]: props.blur },
]));
const slotClassName = `${PREFIX}-overlay_slot`;
const overlayStyle = computed(() => ({ zIndex: props.zIndex, ...props.customStyle }));
const maskStyle = computed(() => (
{
backgroundColor: props.bgColor,
...props.customMaskStyle,
}
));
function handleClick() {
emit('click');
}
const handleError = (event) => {
emit('error', event);
}
</script>
<style lang="scss">
@import './style/Overlay.scss';
</style>
@@ -0,0 +1,40 @@
@import '../../styles/var.scss';
@import '../../styles/common.scss';
$--tk-overlay-bg-color: $--tk-overlay-color-lighter;
.#{$PREFIX}-overlay {
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: 100%;
.#{$PREFIX}-overlay_mask-container {
width: 100%;
height: 100%;
z-index: 0;
position: absolute;
}
.#{$PREFIX}-overlay_mask {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: $--tk-overlay-bg-color;
z-index: 1;
}
.#{$PREFIX}-overlay_slot {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
display: flex;
justify-content: center;
}
}
@@ -0,0 +1,36 @@
const Trigger = ['click','hover'] as const;
const Placement = ['top', 'bottom', 'left', 'right'] as const;
export const PopoverProps = {
trigger: {
type: String,
values: Trigger,
default: 'click',
},
placement: {
type: String,
values: Placement,
default: 'top',
},
color: {
type: String,
},
isShowArrow: {
type: Boolean,
default: true,
},
arrowSize: {
type: Number,
default: 5,
},
arrowDistance: {
type: Number,
default: 5,
},
show: {
type: Boolean,
},
autoClose: {
type: Number,
default: 300,
},
};
@@ -0,0 +1,31 @@
<template>
<PopoverWx v-bind="$props" v-if="IN_WX_MINI_APP">
<template #trigger>
<slot name="trigger"></slot>
</template>
<template #content>
<slot name="content"></slot>
</template>
</PopoverWx>
</template>
<script lang="ts">
export default {
options: {
virtualHost: true,
},
};
</script>
<script setup lang="ts">
import PopoverWx from './PopoverWx/PopoverWx.vue';
import { IN_WX_MINI_APP } from '../util';
import { PopoverProps } from './Popover';
defineProps(PopoverProps);
const emit = defineEmits(['hover']);
const handleHover = () => {
emit('hover');
};
</script>

Some files were not shown because too many files have changed in this diff Show More