当前位置:首页 » 《休闲阅读》 » 正文

uniapp仿微信聊天界面(vue3组合式版本)

16 人参与  2024年10月11日 09:22  分类 : 《休闲阅读》  评论

点击全文阅读


先看效果图:

消息格式参照下方:

<template><view class="chat-index"><scroll-viewid="scrollview"class="scroll-style":style="{height: `${windowHeight - inputHeight}rpx`}"scroll-y="true" :scroll-top="conf.scrollTop"@scrolltoupper="topRefresh"@click="touchClose"><view id="msglistview" class="chat-body"><view v-for="item,index in data.msgInfoList" :key="index"><!-- 消息发送时间 --><view class="time-box" v-if="item.showTime"><view class="time-style"><view>{{ timeFormat(item.sendTime) }}</view></view></view><!-- 自己 --><view class="item self" v-if="item.scid == userInfo.scid"><!-- 文本消息 --><view class="content-text right" v-if="item.type=='text'">{{item.content}}</view><!-- 语音消息 --><view class="content-text right" v-else-if="item.type=='voice'"><view style="display: flex;" @click="playSound(item.content)"><text>{{ item.voiceLength }}''</text><image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/><image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/></view></view><!-- 图片消息 --><view class="content-img" v-else-if="item.type=='img'"><image class="img-style" :src="item.content"  mode="widthFix" :lazy-load="true"/></view><!-- 视频消息 --><view class="content-video" v-else><video class="video-style" :src="item.content" /></view><!-- 头像 --><image class="avatar" :src="userInfo.s_avatar" /></view><!-- 好友 --><view class="item Ai" v-else><!-- 头像 --><image class="avatar" :src="userInfo.r_avatar" /><!-- 文本消息 --><view class="content-text left" v-if="item.type=='text'">{{item.content}}</view><!-- 语音消息 --><view class="content-text left" v-else-if="item.type=='voice'"><view style="display: flex;" @click="playSound(item.content)"><text>{{ item.voiceLength }}''</text><image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/><image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/></view></view><!-- 图片消息 --><view class="content-img" v-else-if="item.type=='img'"><image class="img-style" :src="item.content"  mode="widthFix" :lazy-load="true"/></view><!-- 视频消息 --><view class="content-video" v-else><video class="video-style" :src="item.content" /></view></view></view></view></scroll-view><!-- 消息发送框 --><view class="chat-bottom" :style="{height:`${inputHeight}rpx`}"><view class="input-msg-box" :style="{bottom:`${conf.keyboardHeight}rpx`}"><!-- 输入框区域 --><view class="textarea-style"><!-- 语音/文字输入 --><view class="voice-btn" @click="isVoice"><image class="icon-style"  v-if="conf.isVoice" src="../../static/icon/keyboard.png" /><image class="icon-style" v-else src="../../static/icon/voice.png" /></view><!-- textarea输入框 --><view class="out_textarea_box" @click="() => conf.showMoreMenu=false"><textareaplaceholder-class="textarea_placeholder":style="{textAlign:(conf.textAreaDisabled?'center':'')}"v-model="sendMsg.text"maxlength="250"confirm-type="send"auto-height:placeholder="conf.textAreaText":show-confirm-bar="false":adjust-position="false":disabled="conf.textAreaDisabled"@confirm="handleSend"@linechange="listenTextAreaHeight"@focus="scrollToBottom" @blur="scrollToBottom"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"   /></view><!-- 输入菜单 --><view class="more-btn"><image class="icon-style" src="../../static/icon/emoji.png" @click="handleSend"/><image class="icon-style" style="margin-left: 20rpx;" src="../../static/icon/more.png" @click="showMoreMenuFunc"/></view></view><!-- 更多菜单 --><view :class="{'more-menu-box-max': conf.showMoreMenu,'more-menu-box-min': !conf.showMoreMenu}"><view class="inner-menu-box"><view class="menu-box" @click="sendFile('choose','')"><view class="out-icon-area"><image class="i-style" src="../../static/icon/photo.png" /></view><view class="t-style">照片</view></view><view class="menu-box" @click="sendFile('shoot','')"><view class="out-icon-area"><image class="i-style" src="../../static/icon/takePhoto.png" /></view><view class="t-style">拍摄</view></view></view></view></view></view><!-- 语音输入 --><view class="voice-mask" v-show="voice.mask"><view class="inner-mask"><view class="voice-progress-box" :style="{width:`${progressNum}`+'rpx'}"><view class="third-icon"/><view class="progress-num">{{ voice.length }}s</view></view><view class="cancel-btn" :class="{cancelBtn : voice.cancel}"><image style="width: 60rpx;height: 60rpx;" src="../../static/icon/cancel-voice.png"></image></view><view class="show-tips">上滑取消发送</view><view class="bottom-area"><image class="img-style" src="../../static/icon/icon-voice.png" /></view></view></view></view></template><script setup>import { computed, getCurrentInstance, reactive, ref, onUpdated } from 'vue';import { onLoad } from '@dcloudio/uni-app';import properties from '@/properties/index.js';import timeMethod from '@/utils/timeMethod.js';const { proxy } = getCurrentInstance();const _this = proxy;const sendMsg = reactive({text: ''})/* 接口数据 */const data = reactive({msgInfoList: [],pageNum: 1,pageSize: 20,pageNumCount: 0})/* 用户信息 */const userInfo = reactive({scid: null,rcid: null,s_avatar: '',r_avatar: ''})/* 配置项 */const conf = reactive({keyboardHeight: 0,bottomHeight: 150,scrollTop: 0,moreMenuHeight: 0,judgeScrollToBottom: true,showMoreMenu: false,loading: false,showMsgMenuBoxId: null,showMoreMenu: false,textAreaDisabled: false,textAreaText: '',isVoice: false,showMoreMenu: false,playVoice: false})/* 语音输入配置项 */const voice = reactive({mask: false,length: 0,cancel: false,startX: "",startY: "",timer: "",recordInstance: "",finished: false,}) /* msg配置项 */const msgConf = reactive({timeSpace: 120,initMsgTime: '',msgId: 0,latestTime: ''})/** * 页面加载时调用 */onLoad((e) => {userInfo.scid =parseInt(uni.getStorageSync('cid'));userInfo.rcid = parseInt(e.rcid);voice.recordInstance = uni.getRecorderManager();keyboardHeightChange();listenMsg();getAiUserInfo(parseInt(e.rcid));getSelfUserInfo(uni.getStorageSync('cid'));getAllMsg(parseInt(e.rcid));readMsg(parseInt(e.rcid))})/** * 数据更新时调用 */onUpdated(() => {/* 页面更新时调用聊天消息定位到最底部 */if (conf.judgeScrollToBottom) scrollToBottom();})/** * 计算属性 */const windowHeight = computed(() => rpxTopx(uni.getSystemInfoSync().windowHeight))const inputHeight = computed(() => conf.bottomHeight + conf.keyboardHeight + conf.moreMenuHeight)const progressNum = computed(() => voice.length * 2 + 250)/** * px 转换 rpx */  const rpxTopx = (px) => {const deviceWidth = uni.getSystemInfoSync().windowWidth;let rpx = ( 750 / deviceWidth ) * Number(px);return Math.floor(rpx);}/** * 监听聊天发送栏高度 */const listenTextAreaHeight = () => {setTimeout(()=>{let query = uni.createSelectorQuery();query.select('.input-msg-box').boundingClientRect();query.exec(res =>{conf.bottomHeight = rpxTopx(res[0].height);})},200)}/** * 监听键盘高度 */const keyboardHeightChange = () => {uni.onKeyboardHeightChange(res => {conf.keyboardHeight = rpxTopx(res.height);if(conf.keyboardHeight <= 0) {conf.keyboardHeight = 0;conf.showMoreMenu = false;}})}/** * 滑动到底部 */const scrollToBottom = (e) => {setTimeout(()=>{let query = uni.createSelectorQuery().in(_this);query.select('#scrollview').boundingClientRect();query.select('#msglistview').boundingClientRect();query.exec((res) =>{if(res[1].height > res[0].height){conf.scrollTop = rpxTopx(res[1].height - res[0].height);}})},200);}/** * 弹出更多菜单弹窗 */const showMoreMenuFunc = () => {conf.showMoreMenu = true;conf.isVoice = false;conf.textAreaText = '';conf.moreMenuHeight = 350;}/** * websocket监听 */const listenMsg = () => {uni.onSocketMessage((res)=>{let resData = JSON.parse(res.data);data.msgInfoList.push(resData);})}/** * 语音与输入切换 */const isVoice = () => {if (conf.isVoice) {conf.isVoice = false;conf.textAreaDisabled = false;conf.textAreaText = '';} else {conf.isVoice = true;conf.textAreaDisabled = true;conf.textAreaText = '按住 说话';conf.showMoreMenu = false;conf.moreMenuHeight = 0;}}/** * 获取用户信息(自己) */const getSelfUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => {userInfo.scid = cid;userInfo.s_avatar = res.data.avatarUrl;})/** * 获取用户信息(好友) */const getAiUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => {userInfo.rcid = cid;userInfo.r_avatar = res.data.avatarUrl;uni.setNavigationBarTitle({title:res.data.name});})/** * 上拉加载消息 */const topRefresh = () => {if (data.pageNum < data.pageNumCount) {data.pageNum++;conf.judgeScrollToBottom = false;conf.loading = true;getAllMsg(userInfo.rcid);}}/** * 获取消息 */const getAllMsg = (rcid) => {_this.$http('/msg/getChatMsg','POST',{'scid':uni.getStorageSync('cid'),'rcid':rcid,'pageNum':data.pageNum,'pageSize':data.pageSize}).then(res => {data.pageNumCount = res.data.pagesNum;showMsgTime(res.data.list);msgConf.latestTime = data.msgInfoList.slice(-1)[0].sendTime; })}/** * 已读消息 */const readMsg = (rcid) => {_this.$http('/msg/readMsg','POST',{'scid':rcid,'rcid':uni.getStorageSync('cid')})}/** * 控制消息时间是否展示 */const showMsgTime = (msgData) => {msgData.forEach(e => {e.showTime = false;data.msgInfoList.unshift(e);if (msgConf.msgId !== 0) {if (timeMethod.calculateTime(msgConf.initMsgTime,e.sendTime)/1000 > msgConf.timeSpace) {data.msgInfoList.slice(0 - msgConf.msgId)[0].showTime = true;}}msgConf.initMsgTime = e.sendTime;msgConf.msgId++;});data.msgInfoList.slice(0 - (msgConf.msgId + 1))[0].showTime = true;}/** * 日期转换 */const timeFormat = (time) => {//时间格式化const Time = timeMethod.getTime(time).split("T");//当前消息日期属于周const week = timeMethod.getDateToWeek(time); //当前日期0时const nti = timeMethod.setTimeZero(timeMethod.getNowTime());//消息日期当天0时const mnti = timeMethod.setTimeZero(timeMethod.getTime(time));//计算日期差值const diffDate = timeMethod.calculateTime(nti,mnti);//本周一日期0时 (后面+1是去除当天时间)const fwnti = timeMethod.setTimeZero(timeMethod.countDateStr(-timeMethod.getDateToWeek(timeMethod.getNowTime()).weekID + 1));//计算周日期差值const diffWeek = timeMethod.calculateTime(mnti,fwnti);if (diffDate === 0) { //消息发送日期减去当天日期如果等于0则是当天时间return Time[1].slice(0,5);} else if (diffDate < 172800000) { //当前日期减去消息发送日期小于2天(172800000ms)则是昨天-  一天最大差值前天凌晨00:00:00到今天晚上23:59:59return "昨天 " + Time[1].slice(0,5);} else if (diffWeek >= 0) { //消息日期减去本周一日期大于0则是本周return week.weekName;} else { //其他时间则是日期return Time[0].slice(5,10);}}/** * 关闭消息操作菜单 */const touchClose = () => {conf.showBoxId = null;conf.showMoreMenu = false;conf.keyboardHeight = 0;conf.moreMenuHeight = 0;}/** * 发送消息 */const handleSend = () => {conf.judgeScrollToBottom = true;data.pageNum = 1;/* 如果消息不为空 */if(sendMsg.text.length !== 0){_this.$http("/msg/sendMsg","POST",{"scid":userInfo.scid,"rcid":userInfo.rcid,"type": "text","content":sendMsg.text}).then(res => {if (res.status) {if (timeMethod.calculateTime(res.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) {res.data.showTime = true;} else {res.data.showTime = false;}data.msgInfoList.push(res.data);sendMsg.text = '';} })}}/** * 长按开始录制语音 */const handleTouchStart = (e) => {if (conf.textAreaDisabled) {voice.finished = false;uni.getSetting({success(res) {if (res.authSetting['scope.record'] === undefined) {console.log("第一次授权")} else if (!res.authSetting['scope.record']) {uni.showToast({icon: "none",title: "点击右上角···进入设置开启麦克风授权!",duration: 2000})} else {voice.recordInstance.start();voice.mask = true;voice.isRecord = true;voice.length = 1;voice.startX = e.touches[0].pageX;voice.startY = e.touches[0].pageY;voice.timer = setInterval(() => {voice.length += 1;if(voice.length >= 60) {clearInterval(voice.timer);handleTouchEnd();}},1000)//判断先结束按钮但是录制才开始时不会结束录制的条件;因为获取授权这儿存在延时;所以结束录制时可能还没开始录制if (voice.finished && voice.mask) {handleTouchEnd();}}}})}}/** * 长按滑动 */const handleTouchMove = (e) => {if (conf.textAreaDisabled) {if (voice.startY - e.touches[0].pageY > 80) {voice.cancel = true;}else {voice.cancel = false;}}}/** * 语音录制结束 */const handleTouchEnd = () => {if (conf.textAreaDisabled) {voice.finished = true;voice.mask = false;clearInterval(voice.timer);voice.recordInstance.stop();voice.recordInstance.onStop((res) => {const message = {voice:res.tempFilePath,length:voice.length}if (!voice.cancel) {if (voice.length>1) {sendFile("voice",message);} else {uni.showToast({icon: 'none',title: "语音时间太短",duration: 1000})}}else {voice.cancel = false;}})}}/** * 语音播放 */const playSound = (url) => {conf.playVoice = true;let music = null;music = uni.createInnerAudioContext(); music.src = url;music.play(); music.onEnded(()=>{music = null;conf.playVoice = false;})}/** * 发送文件 */const sendFile = (type,data) => {if (type === "choose") {uni.chooseMedia({count: 1,mediaType: ['image', 'video'],sourceType: ['album'],maxDuration: 30,success(res) {let type = 'img';if (res.tempFiles[0].fileType === 'image') {type = 'img'} else {type = 'video'}uploadFile(res.tempFiles[0].tempFilePath,type)}})} else if (type === "shoot") {uni.chooseMedia({count: 1,mediaType: ['image', 'video'],sourceType: ['camera'],maxDuration: 30,success(res) {let type = 'img';if (res.tempFiles[0].fileType === 'image') {type = 'img'} else {type = 'video'}uploadFile(res.tempFiles[0].tempFilePath,type)}})} else {uploadFile(data.voice,'voice')}}/** * 上传文件 */const uploadFile = (path,type) => {let param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type};if (type=='voice') {param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type,"voiceLength":voice.length};}uni.uploadFile({url: properties.appConf.url + "/msg/sendFileMsg",filePath: path,name: 'file',formData: param,header: {"Authorization": uni.getStorageSync('Authorization')},success(res) {let newMsg = JSON.parse(res.data)if (newMsg.status) {if (timeMethod.calculateTime(newMsg.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) {newMsg.data.showTime = true;} else {newMsg.data.showTime = false;}data.msgInfoList.push(newMsg.data)}}})}</script><style lang="scss">$chatContentbgc: #00ff7f;$chatBackground: #f0f0f0;center {display: flex;align-items: center;justify-content: center;}.chat-index {height: 100vh;background-color: $chatBackground;.scroll-style {.chat-body {display: flex;flex-direction: column;padding-top: 23rpx;.time-box {width: 100%;height: 100rpx;display: flex;justify-content: center;align-items: center;.time-style {font-size: 22rpx;background-color: rgba(213, 213, 213, 0.3);;padding: 5rpx 10rpx;border-radius: 8rpx;color: black;}}.self {justify-content: flex-end;position: relative;}.Ai {position: relative;}.item {display: flex;padding: 23rpx 30rpx;.right {background-color: $chatContentbgc;}.left {background-color: #FFFFFF;}.right::after {position: absolute;display: inline-block;content: '';width: 0;height: 0;left: 100%;top: 10px;border: 12rpx solid transparent;border-left: 12rpx solid $chatContentbgc;}.left::after {position: absolute;display: inline-block;content: '';width: 0;height: 0;top: 10px;right: 100%;border: 12rpx solid transparent;border-right: 12rpx solid #FFFFFF;}.content-text {position: relative;max-width: 486rpx;border-radius: 8rpx;word-wrap: break-word;padding: 24rpx 24rpx;margin: 0 24rpx;border-radius: 5px;font-size: 32rpx;font-family: PingFang SC;font-weight: 500;color: #333333;line-height: 42rpx;}.content-img {margin: 0 24rpx;}.content-video {margin: 0 24rpx;}.img-style {width: 400rpx;height: auto;border-radius: 10rpx;}.video-style {width: 400rpx;height: 400rpx;}.avatar {display: flex;justify-content: center;width: 78rpx;height: 78rpx;background: #fff;border-radius: 50rpx;overflow: hidden;image {align-self: center;}}}}}.chat-bottom {width: 100%;.input-msg-box {width: 100% ;min-height: 150rpx;position: fixed;bottom: 0;background: #e6e6e6;.textarea-style {width: 100%;padding-top: 20rpx;display: flex;.out_textarea_box {width:65%;min-height: 70rpx;border-radius: 10rpx;margin-left: 10rpx;background: #f0f0f0;display: flex;align-items: center;textarea {width: 94%;padding: 0 3%;min-height: 42rpx;max-height: 200rpx;font-size: 32rpx;font-family: PingFang SC;color: #333333;}}.voice-btn {width: 10%;@extend center;}.more-btn {width: calc(25% - 25rpx);margin-left: 10rpx;@extend center;}.icon-style {width: 50rpx;height: 50rpx;}}.more-menu-box-min {width: 100%;height: 0rpx;display: none;}.more-menu-box-max {height: 400rpx;margin-top: 10rpx;border-top: 1rpx solid #d6d6d6;transition: height 1ms linear;display: block;.inner-menu-box {width: calc(100% - 20rpx);height: calc(360rpx - 10rpx);padding: 10rpx;.menu-box {width: 150rpx;height: 150rpx;margin: 12rpx calc((100% - 600rpx) / 8);float: left;.out-icon-area {width: 110rpx;height: 110rpx;background-color: #fff;border-radius: 20rpx;margin: 0 20rpx;@extend center;.i-style {width: 60rpx;height: 60rpx;}}.t-style {font-size: 24rpx;font-weight: 400;text-align: center;margin-top: 10rpx;color: #717171;}}}}}}.voice-mask{position:fixed;top:0;right:0;bottom:0;left:0;background-color: rgba(0,0,0,0.8);.inner-mask {display: flex;flex-direction: column;align-items: center;.voice-progress-box {min-width: 250rpx;height: 150rpx;margin-top: 60%;border-radius: 50rpx;background: #4df861;position: relative;@extend center; .third-icon {width: 0;height: 0;border: 15rpx solid transparent;border-top: 15rpx solid #4df861;position: absolute;top: 100%;left: 45%;.progress-num {font-size: 50rpx;font-weight: 600;}}}.cancel-btn {width: 120rpx;height: 120rpx;clip-path: circle();margin-top: 50%;background: #080808;@extend center;}.cancelBtn {width: 150rpx;height: 150rpx;background-color: #ff0004;}.show-tips {width: 100%;margin-top: 50rpx;text-align: center;color: white;animation: 4s opacity2 1s infinite; font-size: 30rpx;font-weight: 400;font-family: sans-serif;}@keyframes opacity2{0%{opacity:0}50%{opacity:.8;}100%{opacity:0;}}.bottom-area {position: fixed;bottom: 0rpx;width: 100%;height:190rpx;border-top: #BABABB 8rpx solid;border-radius: 300rpx 300rpx 0 0;background-image: linear-gradient(#949794,#e1e3e1);@extend center;.img-style {width: 50rpx;height: 50rpx;}}}}}</style>

导入的时间工具包 timeMethod.js

class TimeMethod {constructor() {}//日期格式化addZero(data) {if (parseInt(data) < 10) {return "0" + String(data);}return data;}/** * 获取当前日期 */getNowTime() {const myDate = new Date();const year = myDate.getFullYear();const mouth = this.addZero(myDate.getMonth() + 1);const day = this.addZero(myDate.getDate());const hour = this.addZero(myDate.getHours());const minute = this.addZero(myDate.getMinutes());const second = this.addZero(myDate.getSeconds());return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second}/** * 根据时间返回标准字符串时间 * @param {Object} time */getTime(time) {const myDate = new Date(time);const year = myDate.getFullYear();const mouth = this.addZero(myDate.getMonth() + 1);const day = this.addZero(myDate.getDate());const hour = this.addZero(myDate.getHours());const minute = this.addZero(myDate.getMinutes());const second = this.addZero(myDate.getSeconds());return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second}/** * @param {Object} timestamp * @param {Object} type * 时间戳转时间 */timestampToTime(timestamp,type) {if(String(timestamp).length===10) {//时间戳为10位需*1000var date = new Date(timestamp * 1000);}else {var date = new Date(timestamp);}        const Y = date.getFullYear() + '-';        const M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';        const D = date.getDate() + ' ';        const h = date.getHours() + ':';        const m = date.getMinutes() + ':';        const s = date.getSeconds();if(type==="date") {return Y+M+D;}else {return Y+M+D+h+m+s;}    }/** * @param {Object} time * 时间转时间戳 */timeToTimestamp(time) {//精确到秒,毫秒用000代替 :Date.parse(date); return new Date(time).getTime(); }/** * @param {Object} startTime * @param {Object} endTime * 日期计算 */calculateTime(startTime,endTime) {return new Date(startTime) - new Date(endTime)}/** * @param {Object} time * 日期转星期 */getDateToWeek(time) {let weekArrayList = [{"weekID":7,"weekName":"星期日"},{"weekID":1,"weekName":"星期一"},{"weekID":2,"weekName":"星期二"},{"weekID":3,"weekName":"星期三"},{"weekID":4,"weekName":"星期四"},{"weekID":5,"weekName":"星期五"},{"weekID":6,"weekName":"星期六"}];return weekArrayList[new Date(time).getDay()]}/** * @param {Object} date *  yyyy-MM-dd HH:mm:ss转为   yyyy-MM-ddTHH:mm:ss */timeFormat(date,type) {if (type == "T")return date.replace(" ","T")elsereturn date.replace("T"," ")}/** * @param {Object} time * 定时器 */timeSleep(time) {return new Promise((resolve) => setTimeout(resolve,time))}/** * 根据日期加减计算日期 * @param dayCount */countDateStr(dayCount) {let dd = new Date();dd.setDate(dd.getDate()+ dayCount);let ms = dd.getTime();return new Date(ms).toJSON();}/** * @param {Object} time * 日期中时间置0 */setTimeZero(time) {return time.slice(0,10) + 'T00:00:00.000+00:00';}}export default new TimeMethod();


点击全文阅读


本文链接:http://zhangshiyu.com/post/170374.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1