Ver código fonte

Signed-off-by: 李妹妹 <1639016684@qq.com>

李妹妹 22 horas atrás
pai
commit
b8f24a092e
65 arquivos alterados com 1395 adições e 4339 exclusões
  1. 0 68
      components/im/base/common.js
  2. 0 146
      components/im/base/emojiMap.js
  3. 0 113
      components/im/base/message-facade.js
  4. 0 29
      components/im/tui-chat/message-elements/audio-message/index.css
  5. 0 67
      components/im/tui-chat/message-elements/audio-message/index.vue
  6. 0 103
      components/im/tui-chat/message-elements/custom-message/index.css
  7. 0 236
      components/im/tui-chat/message-elements/custom-message/index.vue
  8. 0 20
      components/im/tui-chat/message-elements/emoji/index.css
  9. 0 48
      components/im/tui-chat/message-elements/emoji/index.vue
  10. 0 13
      components/im/tui-chat/message-elements/face-message/index.css
  11. 0 57
      components/im/tui-chat/message-elements/face-message/index.vue
  12. 0 65
      components/im/tui-chat/message-elements/file-message/index.css
  13. 0 83
      components/im/tui-chat/message-elements/file-message/index.vue
  14. 0 13
      components/im/tui-chat/message-elements/image-message/index.css
  15. 0 54
      components/im/tui-chat/message-elements/image-message/index.vue
  16. 0 29
      components/im/tui-chat/message-elements/system-message/index.css
  17. 0 86
      components/im/tui-chat/message-elements/system-message/index.vue
  18. 0 47
      components/im/tui-chat/message-elements/text-message/index.css
  19. 0 55
      components/im/tui-chat/message-elements/text-message/index.vue
  20. 0 9
      components/im/tui-chat/message-elements/tip-message/index.css
  21. 0 46
      components/im/tui-chat/message-elements/tip-message/index.vue
  22. 0 27
      components/im/tui-chat/message-elements/video-message/index.css
  23. 0 53
      components/im/tui-chat/message-elements/video-message/index.vue
  24. 0 156
      components/im/tui-chat/message-input/index.css
  25. 0 566
      components/im/tui-chat/message-input/index.vue
  26. 0 70
      components/im/tui-chat/message-list/index.css
  27. 0 195
      components/im/tui-chat/message-list/index.vue
  28. 0 86
      components/im/tui-chat/message-private/common-words/index.css
  29. 0 85
      components/im/tui-chat/message-private/common-words/index.vue
  30. 0 178
      components/im/tui-chat/message-private/order-list/index.css
  31. 0 137
      components/im/tui-chat/message-private/order-list/index.vue
  32. 0 93
      components/im/tui-chat/message-private/service-evaluation/index.css
  33. 0 116
      components/im/tui-chat/message-private/service-evaluation/index.vue
  34. 25 1
      components/likeProduct.vue
  35. 0 214
      components/tuiProduct.vue
  36. 2 2
      pages.json
  37. 53 5
      pages/home/components/CategoryTags.vue
  38. 212 13
      pages/home/components/RecommendSection.vue
  39. 0 377
      pages/home/doctorCase.vue
  40. 261 38
      pages/home/index.vue
  41. 70 6
      pages/home/newindex.vue
  42. 70 20
      pages/home/productList.vue
  43. 69 18
      pages/home/recommendList.vue
  44. 0 52
      pages/index/index.vue
  45. 82 79
      pages_course/videovip.vue
  46. 186 22
      pages_index/course.vue
  47. 172 27
      pages_index/courseSearch.vue
  48. 175 19
      pages_index/video.vue
  49. 14 11
      pages_user/user/addAddress.vue
  50. 4 5
      pages_user/user/address.vue
  51. 0 18
      static/assets/audio-calling.svg
  52. 0 16
      static/assets/audio.svg
  53. 0 26
      static/assets/calling.svg
  54. 0 16
      static/assets/down.svg
  55. 0 18
      static/assets/face-emoji.svg
  56. 0 26
      static/assets/keyboard.svg
  57. 0 17
      static/assets/more.svg
  58. 0 16
      static/assets/send-img.svg
  59. 0 19
      static/assets/send-order.svg
  60. 0 23
      static/assets/send-video.svg
  61. 0 17
      static/assets/serach-icon.svg
  62. 0 17
      static/assets/service-assess.svg
  63. 0 18
      static/assets/show.svg
  64. 0 17
      static/assets/take-photo.svg
  65. 0 17
      static/assets/take-video.svg

+ 0 - 68
components/im/base/common.js

@@ -1,68 +0,0 @@
-export function caculateTimeago(dateTimeStamp) {
-  const minute = 1000 * 60; // 把分,时,天,周,半个月,一个月用毫秒表示
-
-  const hour = minute * 60;
-  const day = hour * 24;
-  const week = day * 7;
-  const now = new Date().getTime(); // 获取当前时间毫秒
-
-  const diffValue = now - dateTimeStamp; // 时间差
-
-  let result = '';
-
-  if (diffValue < 0) {
-    return;
-  }
-
-  const minC = diffValue / minute; // 计算时间差的分,时,天,周,月
-
-  const hourC = diffValue / hour;
-  const dayC = diffValue / day;
-  const weekC = diffValue / week;
-
-  if (weekC >= 1 && weekC <= 4) {
-    result = ` ${parseInt(weekC, 10)}周前`;
-  } else if (dayC >= 1 && dayC <= 6) {
-    result = ` ${parseInt(dayC, 10)}天前`;
-  } else if (hourC >= 1 && hourC <= 23) {
-    result = ` ${parseInt(hourC, 10)}小时前`;
-  } else if (minC >= 1 && minC <= 59) {
-    result = ` ${parseInt(minC, 10)}分钟前`;
-  } else if (diffValue >= 0 && diffValue <= minute) {
-    result = '刚刚';
-  } else {
-    const datetime = new Date();
-    datetime.setTime(dateTimeStamp);
-    const Nyear = datetime.getFullYear();
-    const Nmonth = datetime.getMonth() + 1 < 10 ? `0${datetime.getMonth() + 1}` : datetime.getMonth() + 1;
-    const Ndate = datetime.getDate() < 10 ? `0${datetime.getDate()}` : datetime.getDate();
-    result = `${Nyear}-${Nmonth}-${Ndate}`;
-  }
-
-  return result;
-}
-export function formateTime(secondTime) {
-  const time = secondTime;
-  let newTime;
-  let hour;
-  let minite;
-  let seconds;
-  if (time >= 3600) {
-    hour = parseInt(time / 3600) < 10 ? `0${parseInt(time / 3600)}` : parseInt(time / 3600);
-    minite = parseInt(time % 60 / 60) < 10 ? `0${parseInt(time % 60 / 60)}` : parseInt(time % 60 / 60);
-    seconds = time % 3600 < 10 ? `0${time % 3600}` : time % 3600;
-    if (seconds > 60) {
-      minite = parseInt(seconds / 60) < 10 ? `0${parseInt(seconds / 60)}` : parseInt(seconds / 60);
-      seconds = seconds % 60 < 10 ? `0${seconds % 60}` : seconds % 60;
-    }
-    newTime = `${hour}:${minite}:${seconds}`;
-  } else if (time >= 60 && time < 3600) {
-    minite = parseInt(time / 60) < 10 ? `0${parseInt(time / 60)}` : parseInt(time / 60);
-    seconds = time % 60 < 10 ? `0${time % 60}` : time % 60;
-    newTime = `00:${minite}:${seconds}`;
-  } else if (time < 60) {
-    seconds = time < 10 ? `0${time}` : time;
-    newTime = `00:00:${seconds}`;
-  }
-  return newTime;
-}

+ 0 - 146
components/im/base/emojiMap.js

@@ -1,146 +0,0 @@
-export const emojiUrl = 'https://web.sdk.qcloud.com/im/assets/emoji/';
-export const emojiMap = {
-  '[NO]': 'emoji_0@2x.png',
-  '[OK]': 'emoji_1@2x.png',
-  '[下雨]': 'emoji_2@2x.png',
-  '[么么哒]': 'emoji_3@2x.png',
-  '[乒乓]': 'emoji_4@2x.png',
-  '[便便]': 'emoji_5@2x.png',
-  '[信封]': 'emoji_6@2x.png',
-  '[偷笑]': 'emoji_7@2x.png',
-  '[傲慢]': 'emoji_8@2x.png',
-  '[再见]': 'emoji_9@2x.png',
-  '[冷汗]': 'emoji_10@2x.png',
-  '[凋谢]': 'emoji_11@2x.png',
-  '[刀]': 'emoji_12@2x.png',
-  '[删除]': 'emoji_13@2x.png',
-  '[勾引]': 'emoji_14@2x.png',
-  '[发呆]': 'emoji_15@2x.png',
-  '[发抖]': 'emoji_16@2x.png',
-  '[可怜]': 'emoji_17@2x.png',
-  '[可爱]': 'emoji_18@2x.png',
-  '[右哼哼]': 'emoji_19@2x.png',
-  '[右太极]': 'emoji_20@2x.png',
-  '[右车头]': 'emoji_21@2x.png',
-  '[吐]': 'emoji_22@2x.png',
-  '[吓]': 'emoji_23@2x.png',
-  '[咒骂]': 'emoji_24@2x.png',
-  '[咖啡]': 'emoji_25@2x.png',
-  '[啤酒]': 'emoji_26@2x.png',
-  '[嘘]': 'emoji_27@2x.png',
-  '[回头]': 'emoji_28@2x.png',
-  '[困]': 'emoji_29@2x.png',
-  '[坏笑]': 'emoji_30@2x.png',
-  '[多云]': 'emoji_31@2x.png',
-  '[大兵]': 'emoji_32@2x.png',
-  '[大哭]': 'emoji_33@2x.png',
-  '[太阳]': 'emoji_34@2x.png',
-  '[奋斗]': 'emoji_35@2x.png',
-  '[奶瓶]': 'emoji_36@2x.png',
-  '[委屈]': 'emoji_37@2x.png',
-  '[害羞]': 'emoji_38@2x.png',
-  '[尴尬]': 'emoji_39@2x.png',
-  '[左哼哼]': 'emoji_40@2x.png',
-  '[左太极]': 'emoji_41@2x.png',
-  '[左车头]': 'emoji_42@2x.png',
-  '[差劲]': 'emoji_43@2x.png',
-  '[弱]': 'emoji_44@2x.png',
-  '[强]': 'emoji_45@2x.png',
-  '[彩带]': 'emoji_46@2x.png',
-  '[彩球]': 'emoji_47@2x.png',
-  '[得意]': 'emoji_48@2x.png',
-  '[微笑]': 'emoji_49@2x.png',
-  '[心碎了]': 'emoji_50@2x.png',
-  '[快哭了]': 'emoji_51@2x.png',
-  '[怄火]': 'emoji_52@2x.png',
-  '[怒]': 'emoji_53@2x.png',
-  '[惊恐]': 'emoji_54@2x.png',
-  '[惊讶]': 'emoji_55@2x.png',
-  '[憨笑]': 'emoji_56@2x.png',
-  '[手枪]': 'emoji_57@2x.png',
-  '[打哈欠]': 'emoji_58@2x.png',
-  '[抓狂]': 'emoji_59@2x.png',
-  '[折磨]': 'emoji_60@2x.png',
-  '[抠鼻]': 'emoji_61@2x.png',
-  '[抱抱]': 'emoji_62@2x.png',
-  '[抱拳]': 'emoji_63@2x.png',
-  '[拳头]': 'emoji_64@2x.png',
-  '[挥手]': 'emoji_65@2x.png',
-  '[握手]': 'emoji_66@2x.png',
-  '[撇嘴]': 'emoji_67@2x.png',
-  '[擦汗]': 'emoji_68@2x.png',
-  '[敲打]': 'emoji_69@2x.png',
-  '[晕]': 'emoji_70@2x.png',
-  '[月亮]': 'emoji_71@2x.png',
-  '[棒棒糖]': 'emoji_72@2x.png',
-  '[汽车]': 'emoji_73@2x.png',
-  '[沙发]': 'emoji_74@2x.png',
-  '[流汗]': 'emoji_75@2x.png',
-  '[流泪]': 'emoji_76@2x.png',
-  '[激动]': 'emoji_77@2x.png',
-  '[灯泡]': 'emoji_78@2x.png',
-  '[炸弹]': 'emoji_79@2x.png',
-  '[熊猫]': 'emoji_80@2x.png',
-  '[爆筋]': 'emoji_81@2x.png',
-  '[爱你]': 'emoji_82@2x.png',
-  '[爱心]': 'emoji_83@2x.png',
-  '[爱情]': 'emoji_84@2x.png',
-  '[猪头]': 'emoji_85@2x.png',
-  '[猫咪]': 'emoji_86@2x.png',
-  '[献吻]': 'emoji_87@2x.png',
-  '[玫瑰]': 'emoji_88@2x.png',
-  '[瓢虫]': 'emoji_89@2x.png',
-  '[疑问]': 'emoji_90@2x.png',
-  '[白眼]': 'emoji_91@2x.png',
-  '[皮球]': 'emoji_92@2x.png',
-  '[睡觉]': 'emoji_93@2x.png',
-  '[磕头]': 'emoji_94@2x.png',
-  '[示爱]': 'emoji_95@2x.png',
-  '[礼品袋]': 'emoji_96@2x.png',
-  '[礼物]': 'emoji_97@2x.png',
-  '[篮球]': 'emoji_98@2x.png',
-  '[米饭]': 'emoji_99@2x.png',
-  '[糗大了]': 'emoji_100@2x.png',
-  '[红双喜]': 'emoji_101@2x.png',
-  '[红灯笼]': 'emoji_102@2x.png',
-  '[纸巾]': 'emoji_103@2x.png',
-  '[胜利]': 'emoji_104@2x.png',
-  '[色]': 'emoji_105@2x.png',
-  '[药]': 'emoji_106@2x.png',
-  '[菜刀]': 'emoji_107@2x.png',
-  '[蛋糕]': 'emoji_108@2x.png',
-  '[蜡烛]': 'emoji_109@2x.png',
-  '[街舞]': 'emoji_110@2x.png',
-  '[衰]': 'emoji_111@2x.png',
-  '[西瓜]': 'emoji_112@2x.png',
-  '[调皮]': 'emoji_113@2x.png',
-  '[象棋]': 'emoji_114@2x.png',
-  '[跳绳]': 'emoji_115@2x.png',
-  '[跳跳]': 'emoji_116@2x.png',
-  '[车厢]': 'emoji_117@2x.png',
-  '[转圈]': 'emoji_118@2x.png',
-  '[鄙视]': 'emoji_119@2x.png',
-  '[酷]': 'emoji_120@2x.png',
-  '[钞票]': 'emoji_121@2x.png',
-  '[钻戒]': 'emoji_122@2x.png',
-  '[闪电]': 'emoji_123@2x.png',
-  '[闭嘴]': 'emoji_124@2x.png',
-  '[闹钟]': 'emoji_125@2x.png',
-  '[阴险]': 'emoji_126@2x.png',
-  '[难过]': 'emoji_127@2x.png',
-  '[雨伞]': 'emoji_128@2x.png',
-  '[青蛙]': 'emoji_129@2x.png',
-  '[面条]': 'emoji_130@2x.png',
-  '[鞭炮]': 'emoji_131@2x.png',
-  '[风车]': 'emoji_132@2x.png',
-  '[飞吻]': 'emoji_133@2x.png',
-  '[飞机]': 'emoji_134@2x.png',
-  '[饥饿]': 'emoji_135@2x.png',
-  '[香蕉]': 'emoji_136@2x.png',
-  '[骷髅]': 'emoji_137@2x.png',
-  '[麦克风]': 'emoji_138@2x.png',
-  '[麻将]': 'emoji_139@2x.png',
-  '[鼓掌]': 'emoji_140@2x.png',
-  '[龇牙]': 'emoji_141@2x.png'
-};
-export const emojiName = ['[龇牙]', '[调皮]', '[流汗]', '[偷笑]', '[再见]', '[敲打]', '[擦汗]', '[猪头]', '[玫瑰]', '[流泪]', '[大哭]', '[嘘]', '[酷]', '[抓狂]', '[委屈]', '[便便]', '[炸弹]', '[菜刀]', '[可爱]', '[色]', '[害羞]', '[得意]', '[吐]', '[微笑]', '[怒]', '[尴尬]', '[惊恐]', '[冷汗]', '[爱心]', '[示爱]', '[白眼]', '[傲慢]', '[难过]', '[惊讶]', '[疑问]', '[困]', '[么么哒]', '[憨笑]', '[爱情]', '[衰]', '[撇嘴]', '[阴险]', '[奋斗]', '[发呆]', '[右哼哼]', '[抱抱]', '[坏笑]', '[飞吻]', '[鄙视]', '[晕]', '[大兵]', '[可怜]', '[强]', '[弱]', '[握手]', '[胜利]', '[抱拳]', '[凋谢]', '[米饭]', '[蛋糕]', '[西瓜]', '[啤酒]', '[瓢虫]', '[勾引]', '[OK]', '[爱你]', '[咖啡]', '[月亮]', '[刀]', '[发抖]', '[差劲]', '[拳头]', '[心碎了]', '[太阳]', '[礼物]', '[皮球]', '[骷髅]', '[挥手]', '[闪电]', '[饥饿]', '[困]', '[咒骂]', '[折磨]', '[抠鼻]', '[鼓掌]', '[糗大了]', '[左哼哼]', '[打哈欠]', '[快哭了]', '[吓]', '[篮球]', '[乒乓]', '[NO]', '[跳跳]', '[怄火]', '[转圈]', '[磕头]', '[回头]', '[跳绳]', '[激动]', '[街舞]', '[献吻]', '[左太极]', '[右太极]', '[闭嘴]', '[猫咪]', '[红双喜]', '[鞭炮]', '[红灯笼]', '[麻将]', '[麦克风]', '[礼品袋]', '[信封]', '[象棋]', '[彩带]', '[蜡烛]', '[爆筋]', '[棒棒糖]', '[奶瓶]', '[面条]', '[香蕉]', '[飞机]', '[左车头]', '[车厢]', '[右车头]', '[多云]', '[下雨]', '[钞票]', '[熊猫]', '[灯泡]', '[风车]', '[闹钟]', '[雨伞]', '[彩球]', '[钻戒]', '[沙发]', '[纸巾]', '[手枪]', '[青蛙]'];

+ 0 - 113
components/im/base/message-facade.js

@@ -1,113 +0,0 @@
-import {
-  emojiMap,
-  emojiUrl
-} from './emojiMap';
-/** 传入message.element(群系统消息SystemMessage,群提示消息GroupTip除外)
- * content = {
- *  type: 'TIMTextElem',
- *  content: {
- *    text: 'AAA[龇牙]AAA[龇牙]AAA[龇牙AAA]'
- *  }
- *}
- **/
-// 群提示消息的含义 (opType)
-
-const GROUP_TIP_TYPE = {
-  MEMBER_JOIN: 1,
-  MEMBER_QUIT: 2,
-  MEMBER_KICKED_OUT: 3,
-  MEMBER_SET_ADMIN: 4,
-  // 被设置为管理员
-  MEMBER_CANCELED_ADMIN: 5,
-  // 被取消管理员
-  GROUP_INFO_MODIFIED: 6,
-  // 修改群资料,转让群组为该类型,msgBody.msgGroupNewInfo.ownerAccount表示新群主的ID
-  MEMBER_INFO_MODIFIED: 7 // 修改群成员信息
-
-}; // 解析小程序text, 表情信息也是[嘻嘻]文本
-
-export function parseText(message) {
-  const renderDom = [];
-  let temp = message.payload.text;
-  let left = -1;
-  let right = -1;
-
-  while (temp !== '') {
-    left = temp.indexOf('[');
-    right = temp.indexOf(']');
-
-    switch (left) {
-      case 0:
-        if (right === -1) {
-          renderDom.push({
-            name: 'span',
-            text: temp
-          });
-          temp = '';
-        } else {
-          const _emoji = temp.slice(0, right + 1);
-
-          if (emojiMap[_emoji]) {
-            renderDom.push({
-              name: 'img',
-              src: emojiUrl + emojiMap[_emoji]
-            });
-            temp = temp.substring(right + 1);
-          } else {
-            renderDom.push({
-              name: 'span',
-              text: '['
-            });
-            temp = temp.slice(1);
-          }
-        }
-
-        break;
-
-      case -1:
-        renderDom.push({
-          name: 'span',
-          text: temp
-        });
-        temp = '';
-        break;
-
-      default:
-        renderDom.push({
-          name: 'span',
-          text: temp.slice(0, left)
-        });
-        temp = temp.substring(left);
-        break;
-    }
-  }
-
-  return renderDom;
-} 
-// 解析图片消息
-
-export function parseImage(message) {
-  const renderDom = [{
-    name: 'image',
-    // 这里默认渲染的是 1080P 的图片
-    src: message.payload.imageInfoArray[0].url
-  }];
-  return renderDom;
-} // 解析视频消息
-
-export function parseVideo(message) {
-  const renderDom = {
-    name: 'video',
-    src: message.payload.videoUrl
-  };
-  return renderDom;
-} // 解析语音消息
-
-export function parseAudio(message) {
-  const renderDom = {
-    name: 'audio',
-    src: message.payload.url,
-    second: message.payload.second === 0 ? 1 : message.payload.second
-  };
-  return renderDom;
-}

+ 0 - 29
components/im/tui-chat/message-elements/audio-message/index.css

@@ -1,29 +0,0 @@
-.audio-message {
-	padding: 10rpx 18rpx;
-	border-radius: 2px 10px 10px 10px;
-	border: 1px solid #D9D9D9;
-	display: flex;
-}
-
-.audio-icon {
-	width: 28px;
-	height: 28px;
-}
-
-.my-audio {
-	border-radius: 10px 2px 10px 10px;
-	background: rgba(0, 110, 255, 0.10);
-	border: 1px solid rgba(0, 110, 255, 0.30);
-}
-
-.audio {
-	/*border-radius: 2px 10px 10px 10px;*/
-	height: 60rpx;
-	font-family: PingFangSC-Medium;
-	font-size: 28rpx;
-	color: #000000;
-	line-height: 28rpx;
-	display: flex;
-	align-items: center;
-	justify-content: flex-end;
-}

+ 0 - 67
components/im/tui-chat/message-elements/audio-message/index.vue

@@ -1,67 +0,0 @@
-<template>
-	<view :class="'audio-message ' + (isMine ? 'my-audio' : '')">
-		<image class="audio-icon" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/audio.png"></image>
-		<view class="audio " @click="handlePlayAudioMessage" :style="'width: ' + 120 + 'rpx'">{{ '" ' + message.payload.second }}</view>
-	</view>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			audio: {}
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object,
-			default: () => {}
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					message: newVal
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-
-	beforeMount() {
-		this.audio = uni.createInnerAudioContext();
-		this.audio.onPlay(() => {
-			console.log('开始播放');
-		});
-		this.audio.onEnded(() => {
-			console.log('停止播放');
-		});
-		this.audio.onError(e => {
-			console.error(e, 'onError');
-			// ios 音频播放无声,可能是因为系统开启了静音模式
-			uni.showToast({
-				icon: 'none',
-				title: '该音频暂不支持播放'
-			});
-		});
-	},
-
-	methods: {
-		handlePlayAudioMessage() {
-			this.audio.src = this.message.payload.url;
-			this.audio.play();
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 103
components/im/tui-chat/message-elements/custom-message/index.css

@@ -1,103 +0,0 @@
-.custom-message {
-	display: flex;
-	padding: 10rpx 24rpx;
-	background-color: #fff;
-	border-radius: 2px 10px 10px 10px;
-	border: 1px solid #D9D9D9;
-}
-
-.my-custom {
-	border-radius: 10px 2px 10px 10px;
-	border: 1px solid rgba(0, 110, 255, 0.30);
-}
-
-.custom-content-title {
-	font-family: PingFangSC-Medium;
-	width: 278rpx;
-	line-height: 34rpx;
-	font-size: 24rpx;
-	color: #000000;
-	letter-spacing: 0;
-	margin-bottom: 12rpx;
-}
-
-.custom-content-description {
-	font-family: PingFangSC-Regular;
-	width: 278rpx;
-	line-height: 34rpx;
-	font-size: 28rpx;
-	color: #006EFF;
-	letter-spacing: 0;
-	margin-bottom: 12rpx;
-}
-
-.custom-content-price {
-	font-family: PingFangSC-Medium;
-	line-height: 50rpx;
-	color: #FF7201;
-	letter-spacing: 0;
-}
-
-.custom-image {
-	width: 135rpx;
-	height: 135rpx;
-	border-radius: 6rpx;
-	margin-right: 10rpx;
-	margin-top: 4rpx;
-}
-
-.custom-content-score {
-	display: flex;
-	align-items: center;
-	padding-bottom: 12rpx;
-}
-
-.custom-content-score .score-star {
-	width: 36rpx;
-	height: 36rpx;
-	margin-right: 10rpx;
-}
-
-.text-message {
-	display: inline-flex;
-	max-width: 60vw;
-	line-height: 52rpx;
-	padding: 12rpx 24rpx;
-	background: #F8F8F8;
-	border: 1px solid #D9D9D9;
-	border-radius: 2px 10px 10px 10px;
-}
-
-.my-text {
-	border-radius: 10px 2px 10px 10px;
-	border: 1px solid rgba(0, 110, 255, 0.30);
-	background: rgba(0, 110, 255, 0.10);
-}
-
-.message-body-span {
-	display: flex;
-	justify-content: center;
-	align-items: center;
-	/*justify-content: flex-start;*/
-	flex-wrap: wrap;
-	outline: none;
-	font-size: 28rpx;
-	color: #333333;
-	position: relative;
-	max-width: 60vw;
-}
-
-.message-body-span-text {
-	width: 100%;
-	display: inline;
-	word-wrap: break-word;
-	word-break: break-all;
-}
-
-.custom-content-text {
-	font-family: PingFangSC-Regular;
-	height: 25px;
-	line-height: 25px;
-	font-size: 28rpx;
-	letter-spacing: 0;
-}

+ 0 - 236
components/im/tui-chat/message-elements/custom-message/index.vue

@@ -1,236 +0,0 @@
-<template>
-	<view>
-		<view v-if="renderDom[0].type === 'order'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<image class="custom-image" :src="renderDom[0].imageUrl"></image>
-			<view class="custom-content">
-				<view class="custom-content-title">{{ renderDom[0].title }}</view>
-				<view class="custom-content-description">{{ renderDom[0].description }}</view>
-				<view class="custom-content-price">{{ renderDom[0].price }}</view>
-			</view>
-		</view>
-		<view v-if="renderDom[0].type === 'consultion'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<view class="custom-content">
-				<view class="custom-content-title">{{ renderDom[0].title }}</view>
-				<view v-for="(item, index) in renderDom[0].item" :key="index" class="custom-content-description" :id="item.key">{{ item.key }}</view>
-				<view class="custom-content-description">{{ renderDom[0].description }}</view>
-			</view>
-		</view>
-		<view v-if="renderDom[0].type === 'evaluation'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<view class="custom-content">
-				<view class="custom-content-title">{{ renderDom[0].title }}</view>
-				<view class="custom-content-score">
-					<image v-for="(item, index) in renderDom[0].score" :key="index" class="score-star" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/star.png"></image>
-				</view>
-				<view class="custom-content-description">{{ renderDom[0].description }}</view>
-			</view>
-		</view>
-		<view v-if="renderDom[0].type === 'group_create'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<view class="custom-content-text">{{ renderDom[0].text }}</view>
-		</view>
-		<view v-if="renderDom[0].type === 'c2cCalling' || renderDom[0].type === 'groupCalling'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<view class="custom-content-text">{{ renderDom[0].text }}</view>
-		</view>
-		<view v-if="renderDom[0].type === 'notSupport'" class="message-body-span text-message">
-			<view class="message-body-span-text">{{ renderDom[0].text }}</view>
-		</view>
-		<view v-if="renderDom[0].type === 'finish'" :class="'custom-message ' + (isMine ? 'my-custom' : '')">
-			<view class="custom-content-text">{{ renderDom[0].text }}</view>
-		</view>
-	</view>
-</template>
-
-<script>
-import { formateTime } from '../../../base/common.js';
-export default {
-	data() {
-		return {};
-	},
-	components: {},
-	props: {
-		message: {
-			type: Object,
-			default: () => {}
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					message: newVal,
-					renderDom: this.parseCustom(newVal)
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		// 解析音视频通话消息
-		extractCallingInfoFromMessage(message) {
-			const callingmessage = JSON.parse(message.payload.data);
-			if (callingmessage.businessID !== 1) {
-				return '';
-			}
-			const objectData = JSON.parse(callingmessage.data);
-			switch (callingmessage.actionType) {
-				case 1: {
-					if (objectData.call_end >= 0 && !callingmessage.groupID) {
-						return `通话时长:${formateTime(objectData.call_end)}`;
-					}
-					if (callingmessage.groupID) {
-						return '结束群聊';
-					}
-					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
-						return '切换语音通话';
-					}
-					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
-						return '切换视频通话';
-					}
-					return '发起通话';
-				}
-				case 2:
-					return '取消通话';
-				case 3:
-					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
-						return '切换语音通话';
-					}
-					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
-						return '切换视频通话';
-					}
-					return '已接听';
-				case 4:
-					return '拒绝通话';
-				case 5:
-					if (objectData.data && objectData.data.cmd === 'switchToAudio') {
-						return '切换语音通话';
-					}
-					if (objectData.data && objectData.data.cmd === 'switchToVideo') {
-						return '切换视频通话';
-					}
-					return '无应答';
-				default:
-					return '';
-			}
-		},
-		parseCustom(message) {
-			
-			// 约定自定义消息的 data 字段作为区分,不解析的不进行展示
-			if (message.payload.data === 'order') {
-				const extension = JSON.parse(message.payload.extension);
-				const renderDom = [
-					{
-						type: 'order',
-						name: 'custom',
-						title: extension.title || '',
-						imageUrl: extension.imageUrl || '',
-						price: extension.price || 0,
-						description: message.payload.description
-					}
-				];
-				return renderDom;
-			} // 客服咨询
-
-			if (message.payload.data === 'consultion') {
-				const extension = JSON.parse(message.payload.extension);
-				const renderDom = [
-					{
-						type: 'consultion',
-						title: extension.title || '',
-						item: extension.item || 0,
-						description: extension.description
-					}
-				];
-				return renderDom;
-			} // 服务评价
-
-			if (message.payload.data === 'evaluation') {
-				const extension = JSON.parse(message.payload.extension);
-				const renderDom = [
-					{
-						type: 'evaluation',
-						title: message.payload.description,
-						score: extension.score,
-						description: extension.comment
-					}
-				];
-				return renderDom;
-			} // 群消息解析
-			// 群消息解析
-			if (message.payload.data === 'group_create') {
-				const renderDom = [
-					{
-						type: 'group_create',
-						text: message.payload.extension
-					}
-				];
-				return renderDom;
-			}
-			if (message.payload.data === 'finish') {
-				console.log(111)
-				const renderDom = [
-					{
-						type: 'finish',
-						text: message.payload.extension
-					}
-				];
-				return renderDom;
-			}
-			// 音视频通话消息解析
-			const callingmessage = JSON.parse(message.payload.data);
-
-			if (callingmessage.businessID === 1) {
-				if (message.conversationType === 'GROUP') {
-					if (message.payload.data.actionType === 5) {
-						message.nick = message.payload.data.inviteeList ? message.payload.data.inviteeList.join(',') : message.from;
-					}
-					const _text = this.extractCallingInfoFromMessage(message);
-					const groupText = `${_text}`;
-					const renderDom = [
-						{
-							type: 'groupCalling',
-							text: groupText,
-							userIDList: []
-						}
-					];
-					return renderDom;
-				}
-				if (message.conversationType === 'C2C') {
-					const c2cText = this.extractCallingInfoFromMessage(message);
-					const renderDom = [
-						{
-							type: 'c2cCalling',
-							text: c2cText
-						}
-					];
-					return renderDom;
-				}
-			}
-
-			if (message.payload.data === 'group_create') {
-				const renderDom = [
-					{
-						type: 'group_create',
-						text: message.payload.extension
-					}
-				];
-				return renderDom;
-			}
-			
-
-			return [
-				{
-					type: 'notSupport',
-					text: '[自定义消息]'
-				}
-			];
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 20
components/im/tui-chat/message-elements/emoji/index.css

@@ -1,20 +0,0 @@
-.TUI-Emoji {
-	display: flex;
-	justify-content: flex-start;
-	flex-wrap: wrap;
-	width: 100%;
-	height: 480rpx;
-	margin-left: 4vw;
-	overflow-y: scroll;
-}
-
-.TUI-emoji-image {
-	width: 9vw;
-	height: 9vw;
-	margin: 2vw;
-}
-
-.emoji-image {
-	width: 100%;
-	height: 100%;
-}

+ 0 - 48
components/im/tui-chat/message-elements/emoji/index.vue

@@ -1,48 +0,0 @@
-<template>
-	<view class="TUI-Emoji">
-		<view v-for="(item, index) in emojiList" :key="index" class="TUI-emoji-image">
-			<image :data-name="item.emojiName" class="emoji-image" :src="item.url" @tap="handleEnterEmoji"></image>
-		</view>
-	</view>
-</template>
-
-<script>
-import { emojiName, emojiUrl, emojiMap } from '../../../base/emojiMap';
-
-export default {
-	data() {
-		return {
-			emojiList: []
-		};
-	},
-
-	components: {},
-	props: {},
-
-	beforeMount() {
-		for (let i = 0; i < emojiName.length; i++) {
-			this.emojiList.push({
-				emojiName: emojiName[i],
-				url: emojiUrl + emojiMap[emojiName[i]]
-			});
-		}
-
-		this.setData({
-			emojiList: this.emojiList
-		});
-	},
-
-	methods: {
-		handleEnterEmoji(event) {
-			this.$emit('enterEmoji', {
-				detail: {
-					message: event.currentTarget.dataset.name
-				}
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 13
components/im/tui-chat/message-elements/face-message/index.css

@@ -1,13 +0,0 @@
-.TUI-faceMessage {
-  width: 150px;
-  height: 110px;
-  max-width: 60vw;
-}
-.face-message {
-  width: 100%;
-  height: 100%;
-  border-radius: 10px 10px 10px 10px;
-}
-.my-image {
-  border-radius: 10px 2px 10px 10px;
-}

+ 0 - 57
components/im/tui-chat/message-elements/face-message/index.vue

@@ -1,57 +0,0 @@
-<template>
-	<view class="TUI-faceMessage" @tap="previewImage"><image class="face-message" :src="renderDom.src"></image></view>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			renderDom: [],
-			percent: 0,
-			faceUrl: 'https://web.sdk.qcloud.com/im/assets/face-elem/'
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					renderDom: this.parseFace(newVal)
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		// 解析face 消息
-		parseFace(message) {
-			const renderDom = {
-				src: `${this.faceUrl + message.payload.data}@2x.png`
-			};
-			return renderDom;
-		},
-
-		previewImage() {
-			uni.previewImage({
-				current: this.renderDom[0].src,
-				// 当前显示图片的http链接
-				urls: [this.renderDom[0].src]
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 65
components/im/tui-chat/message-elements/file-message/index.css

@@ -1,65 +0,0 @@
-.TUI-fileMessage {
-	display: flex;
-	padding: 10rpx 24rpx;
-	background-color: #fff;
-	border-radius: 2px 10px 10px 10px;
-	border: 1px solid #D9D9D9;
-}
-
-.fileMessage {
-	display: flex;
-}
-
-.fileMessage-box {
-	display: flex;
-	background: white;
-	align-items: center;
-	height: 150rpx;
-}
-
-.file-icon {
-	width: 80rpx;
-	height: 80rpx;
-}
-
-.pop {
-	position: fixed;
-	width: 50%;
-	bottom: 400rpx;
-	margin-left: 90rpx;
-	background: rgba(0, 0, 0, 0.3);
-	z-index: 99999;
-}
-
-.text-box {
-	display: flex;
-	justify-content: center;
-	align-items: center;
-	height: 112rpx;
-}
-
-.download-confirm {
-	font-family: PingFangSC-Regular;
-	font-size: 16px;
-	color: #E85454;
-	letter-spacing: 0;
-	text-align: center;
-	line-height: 22px;
-}
-
-.abandon {
-	opacity: 0.8;
-	font-family: PingFangSC-Regular;
-	font-size: 16px;
-	color: #FFFFFF;
-	letter-spacing: 0;
-	text-align: center;
-	line-height: 22px;
-}
-
-.file-title {
-	max-width: 53vw;
-	display: inline;
-	word-wrap: break-word;
-	word-break: break-all;
-}

+ 0 - 83
components/im/tui-chat/message-elements/file-message/index.vue

@@ -1,83 +0,0 @@
-<template>
-	<view>
-		<view class="TUI-fileMessage">
-			<view class="fileMessage">
-				<view class="fileMessage-box">
-					<image class="file-icon" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/file.png"></image>
-					<label @tap="download" class="file-title">{{ filePayload.fileName }}</label>
-				</view>
-			</view>
-		</view>
-		<view class="pop" v-if="Show">
-			<view class="text-box"><text class="download-confirm" @tap.stop="downloadConfirm">下载</text></view>
-			<view class="text-box"><text class="abandon" @tap="cancel">取消</text></view>
-		</view>
-	</view>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			Show: false,
-			filePayload: {}
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object,
-			default: () => {}
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					filePayload: newVal.payload
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		download() {
-			this.setData({
-				Show: true
-			});
-		},
-
-		downloadConfirm() {
-			uni.downloadFile({
-				url: this.filePayload.fileUrl,
-
-				success(res) {
-					const filePath = res.tempFilePath;
-					uni.openDocument({
-						filePath,
-
-						success() {
-							console.log('打开文档成功');
-						}
-					});
-				}
-			});
-		},
-
-		cancel() {
-			this.setData({
-				Show: false
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 13
components/im/tui-chat/message-elements/image-message/index.css

@@ -1,13 +0,0 @@
-.TUI-ImageMessage {
-	width: 150px;
-}
-
-.image-message {
-	width: 100%;
-	height: 100%;
-	border-radius: 10px 10px 10px 10px;
-}
-
-.my-image {
-	border-radius: 10px 2px 10px 10px;
-}

+ 0 - 54
components/im/tui-chat/message-elements/image-message/index.vue

@@ -1,54 +0,0 @@
-<template>
-	<view class="TUI-ImageMessage" @tap="previewImage">
-		<image :class="'image-message ' + (isMine ? 'my-image' : '')" mode="widthFix" :src="renderDom[0].src"></image>
-	</view>
-</template>
-
-<script>
-import { parseImage } from '../../../base/message-facade';
-
-export default {
-	data() {
-		return {
-			renderDom: [],
-			percent: 0
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object,
-			default: ''
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					renderDom: parseImage(newVal),
-					percent: newVal.percent
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		previewImage() {
-			uni.previewImage({
-				current: this.renderDom[0].src,
-				// 当前显示图片的http链接
-				urls: [this.renderDom[0].src]
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 29
components/im/tui-chat/message-elements/system-message/index.css

@@ -1,29 +0,0 @@
-.container {
-	width: 100%;
-	height: 200rpx;
-}
-
-.handle {
-	display: flex;
-	justify-content: space-between;
-}
-
-.card {
-	font-size: 14px;
-	margin: 20px;
-	padding: 20px;
-	box-sizing: border-box;
-	border: 1px solid #abdcff;
-	background-color: #f0faff;
-	border-radius: 12px;
-}
-
-.time {}
-
-.button {
-	color: blue;
-	border-radius: 8px;
-	line-height: 30px;
-	font-size: 16px;
-	width: 70px;
-}

+ 0 - 86
components/im/tui-chat/message-elements/system-message/index.vue

@@ -1,86 +0,0 @@
-<template>
-	<view>
-		<view v-if="message.payload.operationType === 1" class="card handle">
-			<view>
-				<view class="time">{{ messageTime }}</view>
-				{{ renderDom }}
-			</view>
-			<view class="choose"><view class="button" @tap="handleClick">处理</view></view>
-		</view>
-		<view class="card" v-else>
-			<view class="time">{{ messageTime }}</view>
-			{{ renderDom }}
-		</view>
-	</view>
-</template>
-
-<script>
-import { parseGroupSystemNotice } from '../../../base/message-facade';
-import { caculateTimeago } from '../../../base/common';
-
-export default {
-	data() {
-		return {
-			// message: {},
-			messageTime: '',
-			renderDom: ''
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					messageTime: caculateTimeago(newVal.time * 1000),
-					renderDom: parseGroupSystemNotice(newVal)
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-
-	methods: {
-		handleClick() {
-			uni.showActionSheet({
-				itemList: ['同意', '拒绝'],
-				success: res => {
-					const option = {
-						handleAction: 'Agree',
-						handleMessage: '欢迎进群',
-						message: this.message
-					};
-
-					if (res.tapIndex === 1) {
-						option.handleAction = 'Reject';
-						option.handleMessage = '拒绝申请';
-					}
-
-					uni.$TUIKit
-						.handleGroupApplication(option)
-						.then(() => {
-							uni.showToast({
-								title: option.handleAction === 'Agree' ? '已同意申请' : '已拒绝申请'
-							});
-						})
-						.catch(error => {
-							uni.showToast({
-								title: error.message || '处理失败',
-								icon: 'none'
-							});
-						});
-				}
-			});
-		}
-	}
-};
-</script>
-<style scoped>
-@import './index.css';
-</style>

+ 0 - 47
components/im/tui-chat/message-elements/text-message/index.css

@@ -1,47 +0,0 @@
-.text-message {
-	display: inline-flex;
-	max-width: 60vw;
-	line-height: 52rpx;
-	padding: 12rpx 24rpx;
-	background: #F8F8F8;
-	border: 1px solid #D9D9D9;
-	border-radius: 2px 10px 10px 10px;
-}
-
-.my-text {
-	border-radius: 10px 2px 10px 10px;
-	border: 1px solid rgba(0, 110, 255, 0.30);
-	background: rgba(0, 110, 255, 0.10);
-}
-
-.message-body-span {
-	display: flex;
-	justify-content: center;
-	align-items: center;
-	/*justify-content: flex-start;*/
-	flex-wrap: wrap;
-	outline: none;
-	font-size: 28rpx;
-	color: #333333;
-	position: relative;
-	max-width: 60vw;
-}
-
-.message-body-span-text {
-	width: 100%;
-	display: inline;
-	word-wrap: break-word;
-	word-break: break-all;
-}
-
-.message-body-span-image {
-	display: inline-block;
-	width: 32rpx;
-	height: 32rpx;
-	margin: 0 4rpx;
-}
-
-.emoji-icon {
-	width: 20px;
-	height: 20px;
-}

+ 0 - 55
components/im/tui-chat/message-elements/text-message/index.vue

@@ -1,55 +0,0 @@
-<template>
-	<view :class="'text-message ' + (isMine ? 'my-text' : '')">
-		<view v-for="(item, index) in renderDom" :key="index" class="message-body-span">
-			<span class="message-body-span-text" v-if="item.name === 'span'">{{ item.text }}</span>
-			<image v-if="item.name === 'img'" class="emoji-icon" :src="item.src"></image>
-		</view>
-	</view>
-</template>
-
-<script>
-import { parseText } from '../../../base/message-facade';
-
-export default {
-	data() {
-		return {
-			renderDom: []
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					renderDom: parseText(newVal)
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-
-	beforeMount() {
-		// 在组件实例进入页面节点树时执行
-	},
-
-	destroyed() {
-		// 在组件实例被从页面节点树移除时执行
-	},
-
-	methods: {}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 9
components/im/tui-chat/message-elements/tip-message/index.css

@@ -1,9 +0,0 @@
-.tip-message {
-	width: 100%;
-}
-
-.text-message {
-	text-align: center;
-	font-size: 12px;
-	color: #999999;
-}

+ 0 - 46
components/im/tui-chat/message-elements/tip-message/index.vue

@@ -1,46 +0,0 @@
-<template>
-	<view class="tip-message">
-		<view class="text-message">{{ renderDom[0].text }}</view>
-	</view>
-</template>
-
-<script>
-import { parseGroupTip } from '../../../base/message-facade';
-
-export default {
-	data() {
-		return {};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.setData({
-					renderDom: parseGroupTip(newVal)
-				});
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-
-	beforeMount() {
-		// 在组件实例进入页面节点树时执行
-	},
-
-	destroyed() {
-		// 在组件实例被从页面节点树移除时执行
-	},
-
-	methods: {}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 27
components/im/tui-chat/message-elements/video-message/index.css

@@ -1,27 +0,0 @@
-.video-message {}
-
-.video-box {
-	width: 160px;
-	height: 120px;
-	background-color: rgba(0, 0, 0, 0.2);
-	border-radius: 10px 2px 10px 10px;
-	display: flex;
-	justify-content: center;
-	align-items: center;
-}
-
-.my-video {
-	border-radius: 10px 2px 10px 10px;
-}
-
-.icon {
-	position: absolute;
-	bottom: 30px;
-	right: 30px;
-}
-
-.video-icon {
-	display: block;
-	width: 32px;
-	height: 32px;
-}

+ 0 - 53
components/im/tui-chat/message-elements/video-message/index.vue

@@ -1,53 +0,0 @@
-<template>
-	<view :class="'video-box ' + (isMine ? 'my-video' : '')" @click="playerHander">
-		<image class="video-icon" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/video-play.png"></image>
-	</view>
-</template>
-
-<script>
-import videoIcon from 'https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/video-play.png';
-export default {
-	data() {
-		return {
-			isPlay: false,
-			videoIcon: videoIcon
-		};
-	},
-
-	components: {},
-	props: {
-		message: {
-			type: Object,
-			default: () => {}
-		},
-		isMine: {
-			type: Boolean,
-			default: true
-		}
-	},
-	watch: {
-		message: {
-			handler: function(newVal) {
-				this.message=newVal
-				 
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		playerHander() {
-			uni.$emit('videoPlayerHandler', {
-				isPlay: true,
-				message: this.message
-			});
-		},
-		stopHander() {
-			this.isPlay = false;
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 156
components/im/tui-chat/message-input/index.css

@@ -1,156 +0,0 @@
-.TUI-message-input-container {
-	background-color: #F1F1F1;
-	padding-bottom: 20rpx;
-}
-
-.TUI-message-input {
-	display: flex;
-	padding-bottom: 16rpx;
-	background-color: #F1F1F1;
-	width: 100vw;
-}
-
-.TUI-commom-function {
-	display: flex;
-	flex-wrap: nowrap;
-	width: 750rpx;
-	height: 106rpx;
-	background-color: #F1F1F1;
-	align-items: center;
-}
-
-.TUI-commom-function-item {
-	display: flex;
-	width: 136rpx;
-	justify-content: center;
-	align-items: center;
-	font-size: 24rpx;
-	color: #FFFFFF;
-	height: 48rpx;
-	margin-left: 16rpx;
-	border-radius: 24rpx;
-	background-color: #FF233C;
-}
-
-.TUI-commom-function-item:first-child {
-	margin-left: 48rpx;
-}
-
-.TUI-message-input-functions {
-	display: flex;
-	align-items: center;
-}
-
-.TUI-message-input-main {
-	background-color: #fff;
-	flex: 1;
-	height: 66rpx;
-	margin: 0 10rpx;
-	padding: 0 5rpx;
-	border-radius: 5rpx;
-	display: flex;
-	align-items: center;
-}
-
-.TUI-message-input-area {
-	width: 100%;
-	height: 100%;
-}
-
-.TUI-icon {
-	width: 56rpx;
-	height: 56rpx;
-	margin: 0 16rpx;
-}
-
-.TUI-Extensions {
-	display: flex;
-	flex-wrap: wrap;
-	width: 100vw;
-	height: 450rpx;
-	margin-left: 14rpx;
-	margin-right: 14rpx;
-}
-
-.TUI-Extension-slot {
-	width: 128rpx;
-	height: 170rpx;
-	margin-left: 26rpx;
-	margin-right: 26rpx;
-	margin-top: 24rpx;
-}
-
-.TUI-Extension-icon {
-	width: 128rpx;
-	height: 128rpx;
-}
-
-.TUI-sendMessage-btn {
-	display: flex;
-	align-items: center;
-	margin: 0 10rpx;
-}
-
-.TUI-Emoji-area {
-	width: 100vw;
-	height: 450rpx;
-}
-
-.TUI-Extension-slot-name {
-	line-height: 34rpx;
-	font-size: 24rpx;
-	color: #333333;
-	letter-spacing: 0;
-	text-align: center;
-}
-
-.record-modal {
-	height: 300rpx;
-	width: 60vw;
-	background-color: #000;
-	opacity: 0.8;
-	position: fixed;
-	top: 670rpx;
-	z-index: 9999;
-	left: 20vw;
-	border-radius: 24rpx;
-	display: flex;
-	flex-direction: column;
-}
-
-.record-modal .wrapper {
-	display: flex;
-	height: 200rpx;
-	box-sizing: border-box;
-	padding: 10vw;
-}
-
-.record-modal .wrapper .modal-loading {
-	opacity: 1;
-	width: 40rpx;
-	height: 16rpx;
-	border-radius: 4rpx;
-	background-color: #006fff;
-	animation: loading 2s cubic-bezier(0.17, 0.37, 0.43, 0.67) infinite;
-}
-
-.modal-title {
-	text-align: center;
-	color: #fff;
-}
-
-@keyframes loading {
-	0% {
-		transform: translate(0, 0)
-	}
-
-	50% {
-		transform: translate(30vw, 0);
-		background-color: #f5634a;
-		width: 40px;
-	}
-
-	100% {
-		transform: translate(0, 0);
-	}
-}

+ 0 - 566
components/im/tui-chat/message-input/index.vue

@@ -1,566 +0,0 @@
-<template>
-	<view>
-		<view class="TUI-message-input-container">
-			<view class="TUI-commom-function">
-				<view v-for="(item, index) in commonFunction" :key="index" class="TUI-commom-function-item" :data-function="item" @tap="handleCommonFunctions">
-					{{ item.name }}
-				</view>
-			</view>
-			<view class="TUI-message-input">
-				<image class="TUI-icon" @tap="switchAudio" :src="isAudio ? '/static/assets/keyboard.svg' : '/static/assets/audio.svg'"></image>
-				<view v-if="!isAudio" class="TUI-message-input-main">
-					<input
-						class="TUI-message-input-area"
-						:adjust-position="true"
-						cursor-spacing="20"
-						v-model="inputText"
-						@input="onInputValueChange"
-						maxlength="140"
-						type="text"
-						placeholder-class="input-placeholder"
-						placeholder="请输入内容"
-						@focus="inputBindFocus"
-						@blur="inputBindBlur"
-					/>
-				</view>
-				<view
-					v-else
-					class="TUI-message-input-main"
-					@longpress="handleLongPress"
-					@touchmove="handleTouchMove"
-					@touchend="handleTouchEnd"
-					style="display: flex; justify-content: center; font-size: 32rpx; font-family: PingFangSC-Regular;"
-				>
-					<text>{{ text }}</text>
-				</view>
-				<view class="TUI-message-input-functions" hover-class="none">
-					<image class="TUI-icon" @tap="handleEmoji" src="/static/assets/face-emoji.svg"></image>
-					<view v-if="!sendMessageBtn" @tap="handleExtensions"><image class="TUI-icon" src="/static/assets/more.svg"></image></view>
-					<view v-else class="TUI-sendMessage-btn" @tap="sendTextMessage">发送</view>
-				</view>
-			</view>
-			<view v-if="displayFlag === 'emoji'" class="TUI-Emoji-area"><TUI-Emoji @enterEmoji="appendMessage"></TUI-Emoji></view>
-			<view v-if="displayFlag === 'extension'" class="TUI-Extensions">
-				<!-- TODO: 这里功能还没实现 -->
-				<!--        <camera device-position="back" flash="off" binderror="error" style="width: 100%; height: 300px;"></camera>-->
-				<view class="TUI-Extension-slot" @tap="handleSendPicture">
-					<image class="TUI-Extension-icon" src="/static/assets/take-photo.svg"></image>
-					<view class="TUI-Extension-slot-name">拍摄照片</view>
-				</view>
-				<view class="TUI-Extension-slot" @tap="handleSendImage">
-					<image class="TUI-Extension-icon" src="/static/assets/send-img.svg"></image>
-					<view class="TUI-Extension-slot-name">发送图片</view>
-				</view>
-				<view class="TUI-Extension-slot" @tap="handleShootVideo">
-					<image class="TUI-Extension-icon" src="/static/assets/take-video.svg"></image>
-					<view class="TUI-Extension-slot-name">拍摄视频</view>
-				</view>
-				<view class="TUI-Extension-slot" @tap="handleSendVideo">
-					<image class="TUI-Extension-icon" src="/static/assets/send-video.svg"></image>
-					<view class="TUI-Extension-slot-name">发送视频</view>
-				</view>
-				<view class="TUI-Extension-slot" @tap="handleServiceEvaluation">
-					<image class="TUI-Extension-icon" src="/static/assets/service-assess.svg"></image>
-					<view class="TUI-Extension-slot-name">服务评价</view>
-				</view>
-				<!-- <view class="TUI-Extension-slot" @tap="handleSendOrder">
-					<image class="TUI-Extension-icon" src="/static/assets/send-order.svg"></image>
-					<view class="TUI-Extension-slot-name">发送订单</view>
-				</view> -->
-			</view>
-			<TUI-Common-Words class="tui-cards" :display="displayCommonWords" @sendMessage="$handleSendTextMessage" @close="$handleCloseCards"></TUI-Common-Words>
-			<TUI-Order-List class="tui-cards" :display="displayOrderList" @sendCustomMessage="$handleSendCustomMessage" @close="$handleCloseCards"></TUI-Order-List>
-			<TUI-Service-Evaluation
-				class="tui-cards"
-				:display="displayServiceEvaluation"
-				@sendCustomMessage="$handleSendCustomMessage"
-				@close="$handleCloseCards"
-			></TUI-Service-Evaluation>
-		</view>
-		<view class="record-modal" v-if="popupToggle" @longpress="handleLongPress" @touchmove="handleTouchMove" @touchend="handleTouchEnd">
-			<view class="wrapper"><view class="modal-loading"></view></view>
-			<view class="modal-title">{{ title }}</view>
-		</view>
-	</view>
-</template>
-
-<script>
-import TUIEmoji from '../message-elements/emoji/index';
-import TUICommonWords from '../message-private/common-words/index';
-import TUIOrderList from '../message-private/order-list/index';
-import TUIServiceEvaluation from '../message-private/service-evaluation/index';
-
-export default {
-	data() {
-		return {
-			 
-			firstSendMessage: true,
-			inputText: '',
-			extensionArea: false,
-			sendMessageBtn: false,
-			displayFlag: '',
-			isAudio: false,
-			bottomVal: 0,
-			startPoint: 0,
-			popupToggle: false,
-			isRecording: false,
-			canSend: true,
-			text: '按住说话',
-			title: ' ',
-			notShow: false,
-			isShow: true,
-			recordTime: 0,
-			recordTimer: null,
-			commonFunction: [
-				{
-					name: '常用语',
-					key: '0'
-				},
-				// {
-				// 	name: '发送订单',
-				// 	key: '1'
-				// },
-				{
-					name: '服务评价',
-					key: '2'
-				}
-			],
-			displayServiceEvaluation: false,
-			displayCommonWords: false,
-			displayOrderList: false
-		};
-	},
-	components: {
-		TUIEmoji,
-		TUICommonWords,
-		TUIOrderList,
-		TUIServiceEvaluation
-	},
-	props: {
-		conversation: {
-			type: Object,
-			default: () => {}
-		},
-		toUser: {
-			type: String,
-			default: () => {}
-		}
-	},
-	watch: {
-		conversation: {
-			handler: function(newVal) {
-				// todo 值会被改变
-				// this.setData({
-				//   conversation: newVal
-				// });
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-
-	beforeMount() {
-		var that=this;
-		// 加载声音录制管理器
-		this.recorderManager = uni.getRecorderManager();
-		this.recorderManager.onStop(res => {
-			clearInterval(this.recordTimer);
-			// 兼容 uniapp 打包app,duration 和 fileSize 需要用户自己补充
-			// 文件大小 = (音频码率) x 时间长度(单位:秒) / 8
-			let msg = {
-				duration: res.duration ? res.duration : this.recordTime * 1000,
-				tempFilePath: res.tempFilePath,
-				fileSize: res.fileSize ? res.fileSize : ((48 * this.recordTime) / 8) * 1024
-			};
-			uni.hideLoading();
-			// 兼容 uniapp 语音消息没有duration
-			if (this.canSend) {
-				if (msg.duration < 1000) {
-					uni.showToast({
-						title: '录音时间太短',
-						icon: 'none'
-					});
-				} else {
-					var orderId=uni.getStorageSync('orderId');
-					// res.tempFilePath 存储录音文件的临时路径
-					const message = uni.$TUIKit.createAudioMessage({
-						to: this.getToAccount(),
-						conversationType: this.conversation.type,
-						payload: {
-							file: msg
-						},
-						cloudCustomData: 'orderId='+orderId
-					});
-					this.$sendTIMMessage(message);
-				}
-			}
-
-			that.setData({
-				startPoint: 0,
-				popupToggle: false,
-				isRecording: false,
-				canSend: true,
-				title: ' ',
-				text: '按住说话'
-			});
-		});
-	},
-
-	methods: {
-		switchAudio() {
-			this.isAudio= !this.isAudio;
-			this.text='按住说话';
-			
-		},
-		handleLongPress(e) {
-			this.recorderManager.start({
-				duration: 60000,
-				// 录音的时长,单位 ms,最大值 600000(10 分钟)
-				sampleRate: 44100,
-				// 采样率
-				numberOfChannels: 1,
-				// 录音通道数
-				encodeBitRate: 192000,
-				// 编码码率
-				format: 'aac' // 音频格式,选择此格式创建的音频消息,可以在即时通信 IM 全平台(Android、iOS、微信小程序和Web)互通
-			});
-			this.setData({
-				startPoint: e.touches[0],
-				title: '正在录音',
-				// isRecording : true,
-				// canSend: true,
-				notShow: true,
-				isShow: false,
-				isRecording: true,
-				popupToggle: true,
-				recordTime: 0
-			});
-			this.recordTimer = setInterval(() => {
-				this.recordTime++;
-			}, 1000);
-		},
-
-		// 录音时的手势上划移动距离对应文案变化
-		handleTouchMove(e) {
-			if (this.isRecording) {
-				if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 100) {
-					this.setData({
-						text: '抬起停止',
-						title: '松开手指,取消发送',
-						canSend: false
-					});
-				} else if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 20) {
-					this.setData({
-						text: '抬起停止',
-						title: '上划可取消',
-						canSend: true
-					});
-				} else {
-					this.setData({
-						text: '抬起停止',
-						title: '正在录音',
-						canSend: true
-					});
-				}
-			}
-		},
-
-		// 手指离开页面滑动
-		handleTouchEnd() {
-			this.setData({
-				isRecording: false,
-				popupToggle: false
-			});
-			uni.hideLoading();
-			this.recorderManager.stop();
-		},
-		handleEmoji() {
-			let targetFlag = 'emoji';
-
-			if (this.displayFlag === 'emoji') {
-				targetFlag = '';
-			}
-			this.displayFlag=targetFlag;
-			 
-		},
-
-		handleExtensions() {
-			let targetFlag = 'extension';
-
-			if (this.displayFlag === 'extension') {
-				targetFlag = '';
-			}
-			this.displayFlag=targetFlag;
-		},
-
-		error(e) {
-			console.log(e.detail);
-		},
-
-		handleSendPicture() {
-			this.sendImageMessage('camera');
-		},
-
-		handleSendImage() {
-			this.sendImageMessage('album');
-		},
-
-		sendImageMessage(type) {
-		
-			uni.chooseImage({
-				sourceType: [type],
-				count: 1,
-				success: res => {
-					if (res) {
-						var orderId=uni.getStorageSync('orderId');
-						const message = uni.$TUIKit.createImageMessage({
-							to: this.getToAccount(),
-							conversationType: this.conversation.type,
-							payload: {
-								file: res
-							},
-							cloudCustomData: 'orderId='+orderId,
-							onProgress: percent => {
-								message.percent = percent;
-							}
-						});
-						this.$sendTIMMessage(message);
-					}
-				}
-			});
-		},
-
-		handleShootVideo() {
-			this.sendVideoMessage('camera');
-		},
-
-		handleSendVideo() {
-			this.sendVideoMessage('album');
-		},
-
-		sendVideoMessage(type) {
-			uni.chooseVideo({
-				sourceType: [type],
-				// 来源相册或者拍摄
-				maxDuration: 60,
-				// 设置最长时间60s
-				camera: 'back',
-				// 后置摄像头
-				success: res => {
-					if (res) {
-						var orderId=uni.getStorageSync('orderId');
-						const message = uni.$TUIKit.createVideoMessage({
-							to: this.getToAccount(),
-							conversationType: this.conversation.type,
-							payload: {
-								file: res
-							},
-							cloudCustomData: 'orderId='+orderId,
-							onProgress: percent => {
-								message.percent = percent;
-							}
-						});
-						this.$sendTIMMessage(message);
-					}
-				}
-			});
-		},
-
-		handleCommonFunctions(e) {
-			switch (e.target.dataset.function.key) {
-				case '0':
-					this.setData({
-						displayCommonWords: true
-					});
-					break;
-
-				// case '1':
-				// 	this.setData({
-				// 		displayOrderList: true
-				// 	});
-				// 	break;
-
-				case '2':
-					this.setData({
-						displayServiceEvaluation: true
-					});
-					break;
-
-				default:
-					break;
-			}
-		},
-
-		handleSendOrder() {
-			this.setData({
-				displayOrderList: true
-			});
-		},
-
-		appendMessage(e) {
-			this.setData({
-				inputText: this.inputText + e.detail.message,
-				sendMessageBtn: true
-			});
-		},
-
-		getToAccount() {
-			return this.toUser;
-		},
-		handleCalling(value) {
-			// todo 目前支持单聊
-			if (this.conversation.type === 'GROUP') {
-				uni.showToast({
-					title: '群聊暂不支持',
-					icon: 'none'
-				});
-				return;
-			}
-			const { userID } = this.conversation.userProfile;
-
-			// #ifdef APP-PLUS
-			if(typeof(uni.$TUICalling) === 'undefined') {
-					logger.error('请使用真机运行并且自定义基座调试,可能影响音视频功能~ 插件地址:https://ext.dcloud.net.cn/plugin?id=7097 , 调试地址:https://nativesupport.dcloud.net.cn/NativePlugin/use/use');
-					uni.showToast({
-						title: '请使用真机运行并且自定义基座调试,可能影响音视频功能~ ',
-						icon: 'none',
-						duration: 3000
-					});
-			} else {
-				uni.$TUICalling.call(
-					{
-						userID: userID,
-						type: value
-					},
-					res => {
-						console.log(JSON.stringify(res));
-					}
-				);
-			}
-			// #endif
-			// #ifdef MP-WEIXIN
-			uni.showToast({
-				title: '微信小程序暂不支持',
-				icon: 'none'
-			});
-			// uni.$wxTUICalling.call({userID, type: value})
-			// #endif
-		},
-		sendTextMessage(msg, flag) {
-			var orderId=uni.getStorageSync('orderId');
-			 console.log(this.conversation.type)
-			const to = this.getToAccount();
-			const text = flag ? msg : this.inputText;
-			const message = uni.$TUIKit.createTextMessage({
-				to,
-				conversationType: this.conversation.type,
-				payload: {
-					text:text
-				},
-				cloudCustomData: 'orderId='+orderId
-			});
-			this.setData({
-				inputText: '',
-				sendMessageBtn: false
-			});
-			this.$sendTIMMessage(message);
-		},
-
-		onInputValueChange(event) {
-			if (event.detail.value) {
-				this.setData({
-					sendMessageBtn: true
-				});
-			} else {
-				this.setData({
-					sendMessageBtn: false
-				});
-			}
-		},
-
-		$handleSendTextMessage(event) {
-			this.sendTextMessage(event.detail.message, true);
-			this.setData({
-				displayCommonWords: false
-			});
-		},
-
-		$handleSendCustomMessage(e) {
-			var orderId=uni.getStorageSync('orderId');
-			const message = uni.$TUIKit.createCustomMessage({
-				to: this.getToAccount(),
-				conversationType: this.conversation.type,
-				payload: e.detail.payload,
-				cloudCustomData: 'orderId='+orderId
-			});
-			this.$sendTIMMessage(message);
-			this.setData({
-				displayOrderList: false
-			});
-		},
-
-		$handleCloseCards(e) {
-			switch (e.detail.key) {
-				case '0':
-					this.setData({
-						displayCommonWords: false
-					});
-					break;
-
-				case '1':
-					this.setData({
-						displayOrderList: false
-					});
-					break;
-
-				case '2':
-					this.setData({
-						displayServiceEvaluation: false
-					});
-					break;
-
-				default:
-					break;
-			}
-		},
-		$sendTIMMessage(message) {
-			this.$emit('sendMessage', {
-				detail: {
-					message
-				}
-			});
-			uni.$TUIKit.sendMessage(message).then((res) => {
-				this.firstSendMessage = false
-			}).catch((error) => {
-				 
-			})
-			this.setData({
-				displayFlag: ''
-			});
-		},
-
-		handleClose() {
-			this.setData({
-				displayFlag: ''
-			});
-		},
-
-		handleServiceEvaluation() {
-			this.setData({
-				displayServiceEvaluation: true
-			});
-		},
-
-		inputBindFocus() {
-			console.log('占位:函数 inputBindFocus 未声明');
-		},
-
-		inputBindBlur() {
-			console.log('占位:函数 inputBindBlur 未声明');
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 70
components/im/tui-chat/message-list/index.css

@@ -1,70 +0,0 @@
-.message-list-container {
-	width: 100%;
-	height: 100%;
-}
-
-.t-message-item {
-	/*max-width: 60vw;*/
-	padding: 16rpx 0;
-}
-
-.t-recieve-message {
-	display: flex;
-	flex-direction: row;
-	justify-items: flex-start;
-	align-items: center;
-	width: 100vw;
-}
-
-.t-message-avatar {
-	margin-left: 20rpx;
-	margin-right: 12rpx;
-	border-radius: 10rpx;
-	width: 80rpx;
-	height: 80rpx;
-}
-
-.t-self-message {
-	display: flex;
-	flex-direction: row;
-	justify-content: flex-end;
-	/*align-items: center;*/
-	width: 100vw;
-}
-
-.t-self-message-body {
-	display: flex;
-	justify-content: flex-start;
-	flex-wrap: wrap;
-	outline: none;
-}
-
-.t-recieve-message-body {
-	display: flex;
-	justify-content: flex-start;
-	flex-wrap: wrap;
-	outline: none;
-	/*background: #F8F8F8;*/
-	border-radius: 2px 10px 10px 10px;
-	margin-left: 8rpx;
-
-}
-
-.read-receipts {
-	line-height: 42px;
-	height: 42px;
-	font-size: 12px;
-	color: #6e7981;
-	margin-right: 10px
-}
-
-.no-message {
-	text-align: center;
-	position: fixed;
-	width: 100%;
-	font-size: 12px;
-	color: #a5b5c1;
-	height: 40px;
-	top: -40px;
-	right: 0;
-}

+ 0 - 195
components/im/tui-chat/message-list/index.vue

@@ -1,195 +0,0 @@
-<template>
-	<scroll-view
-		class="message-list-container"
-		scroll-y="true"
-		:scroll-into-view="scrollView"
-		:refresher-enabled="true"
-		@refresherrefresh="refresh"
-		:scroll-top="scrollTop"
-		:refresher-triggered="triggered"
-	>
-		<view id="message-scroll" style="width:100%">
-			<view class="no-message" v-if="isCompleted">没有更多啦</view>
-			<view v-for="item in messageList" :key="item.ID" class="t-message">
-				<view v-if="conversation.type !== '@TIM#SYSTEM'" :id="item.ID">
-					<view class="t-message-item">
-						<TUI-TipMessage v-if="item.type === 'TIMGroupTipElem'" :message="item"></TUI-TipMessage>
-						<view v-if="item.type !== 'TIMGroupTipElem'" :class="item.flow === 'out' ? 't-self-message' : 't-recieve-message'">
-							<image
-								class="t-message-avatar"
-								v-if="item.flow === 'in'"
-								:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
-							></image>
-							<view class="read-receipts" v-if="conversation.type === 'C2C' && item.flow === 'out'">
-								<view v-if="item.isPeerRead">已读</view>
-								<view v-else>未读</view>
-							</view>
-							<view>
-								<TUI-TextMessage v-if="item.type === 'TIMTextElem'" :message="item" :isMine="item.flow === 'out'"></TUI-TextMessage>
-								<TUI-ImageMessage v-if="item.type === 'TIMImageElem'" :message="item" :isMine="item.flow === 'out'"></TUI-ImageMessage>
-								<TUI-VideoMessage v-if="item.type === 'TIMVideoFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-VideoMessage>
-								<TUI-AudioMessage v-if="item.type === 'TIMSoundElem'" :message="item" :isMine="item.flow === 'out'"></TUI-AudioMessage>
-								<TUI-CustomMessage v-if="item.type === 'TIMCustomElem'" :message="item" :isMine="item.flow === 'out'"></TUI-CustomMessage>
-								<TUI-FaceMessage v-if="item.type === 'TIMFaceElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FaceMessage>
-								<TUI-FileMessage v-if="item.type === 'TIMFileElem'" :message="item" :isMine="item.flow === 'out'"></TUI-FileMessage>
-							</view>
-							<image
-								class="t-message-avatar"
-								v-if="item.flow === 'out'"
-								:src="item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'"
-							></image>
-						</view>
-					</view>
-				</view>
-				<view v-else :id="item.ID" :data-value="item.ID"><TUI-SystemMessage :message="item"></TUI-SystemMessage></view>
-			</view>
-		</view>
-	</scroll-view>
-</template>
-
-<script>
-import TUITextMessage from '../message-elements/text-message/index';
-import TUIImageMessage from '../message-elements/image-message/index';
-import TUIVideoMessage from '../message-elements/video-message/index';
-import TUIAudioMessage from '../message-elements/audio-message/index';
-import TUICustomMessage from '../message-elements/custom-message/index';
-import TUITipMessage from '../message-elements/tip-message/index';
-import TUISystemMessage from '../message-elements/system-message/index';
-import TUIFaceMessage from '../message-elements/face-message/index';
-import TUIFileMessage from '../message-elements/file-message/index';
-
-export default {
-	data() {
-		return {
-			conversation:null,
-			// 当前会话
-			messageList: [],
-			// 自己的 ID 用于区分历史消息中,哪部分是自己发出的
-			scrollView: '',
-			scrollTop: 0,
-			triggered: true,
-			nextReqMessageID: '',
-			// 下一条消息标志
-			isCompleted: false // 当前会话消息是否已经请求完毕
-		};
-	},
-
-	components: {
-		TUITextMessage,
-		TUIImageMessage,
-		TUIVideoMessage,
-		TUIAudioMessage,
-		TUICustomMessage,
-		TUITipMessage,
-		TUISystemMessage,
-		TUIFaceMessage,
-		TUIFileMessage
-	},
-	mounted() {
-		uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived, this);
-		uni.$TUIKit.on(uni.$TUIKitEvent.MESSAGE_READ_BY_PEER, this.$onMessageReadByPeer, this);
-		
-	},
-	destroyed() {
-		// 一定要解除相关的事件绑定
-		uni.$TUIKit.off(uni.$TUIKitEvent.MESSAGE_RECEIVED, this.$onMessageReceived);
-	},
-	methods: {
-		refresh() {
-			if (this.isCompleted) {
-				this.setData({
-					isCompleted: true,
-					triggered: false
-				});
-				return;
-			}
-			this.getMessageList(this.conversation);
-			setTimeout(() => {
-				this.setData({
-					triggered: false
-				});
-			}, 2000);
-		},
-		getMessageList(conversation) {
-			this.conversation=conversation
-			var that=this;
-			if (!this.isCompleted) {
-				uni.$TUIKit
-					.getMessageList({
-						conversationID: that.conversation.conversationID,
-						nextReqMessageID: that.nextReqMessageID,
-						count: 15
-					})
-					.then(res => {
-						const { messageList } = res.data; // 消息列表。
-						this.nextReqMessageID = res.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
-						this.isCompleted = res.data.isCompleted; // 表示是否已经拉完所有消息。
-						this.messageList = [...messageList, ...this.messageList];
-						this.$handleMessageRender(this.messageList, messageList);
-					});
-			}
-		},
-
-		// 自己的消息上屏
-		updateMessageList(msg) {
-			this.messageList = [...this.messageList, msg];
-			this.scrollToButtom();
-		},
-
-		// 消息已读更新
-		$onMessageReadByPeer() {
-			this.setData({
-				messageList: this.messageList
-			});
-		},
-		scrollToButtom() {
-			const query = uni.createSelectorQuery().in(this);
-			let nodesRef = query.select('#message-scroll');
-			nodesRef
-				.boundingClientRect(res => {
-					this.$nextTick(() => {
-						//进入页面滚动到底部
-						this.scrollTop = res.height;
-					});
-				})
-				.exec();
-		},
-		// 收到的消息
-		$onMessageReceived(value) {
-			console.log(value)
-			// 若需修改消息,需将内存的消息复制一份,不能直接更改消息,防止修复内存消息,导致其他消息监听处发生消息错误
-			const event = value;
-			const list = [];
-			event.data.forEach(item => {
-				if (item.conversationID === this.conversation.conversationID) {
-					list.push(Object.assign(item));
-					if(item.type=="TIMCustomElem"&&item.payload.data=='finish'){
-						this.$emit("finish");
-					}
-				}
-			});
-			this.messageList = this.messageList.concat(list);
-			this.scrollToButtom();
-		},
-
-		// 历史消息渲染
-		$handleMessageRender(messageList) {
-			if (messageList.length > 0) {
-				this.setData(
-					{
-						messageList
-					},
-					() => {}
-				);
-				this.$nextTick(() => {
-					//进入页面滚动到底部
-					this.scrollTop = 9999;
-				});
-			}
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 86
components/im/tui-chat/message-private/common-words/index.css

@@ -1,86 +0,0 @@
-.tui-common-words-container {
-	position: fixed;
-	width: 100vw;
-	height: 100vh;
-	z-index: 100;
-	top: 0;
-	/*  #ifdef  H5  */
-	top: calc(88rpx + constant(safe-area-inset-top));
-	top: calc(88rpx + env(safe-area-inset-top));
-	/*  #endif  */
-	background: rgba(0, 0, 0, 0.5);
-}
-
-.tui-common-words-box {
-	position: absolute;
-	width: 100%;
-	height: 60%;
-	bottom: 0;
-	background: rgba(255, 255, 255, 1);
-	padding-bottom: 68rpx;
-	z-index: 200;
-}
-
-.tui-common-words-title {
-	display: flex;
-	flex-wrap: nowrap;
-	justify-content: space-between;
-	padding-left: 40rpx;
-	padding-right: 40rpx;
-	padding-top: 48rpx;
-	font-family: PingFangSC-Medium;
-	font-size: 36rpx;
-	color: #000000;
-	letter-spacing: 0;
-	line-height: 50rpx;
-}
-
-.tui-search-bar {
-	display: flex;
-	flex-wrap: nowrap;
-	align-items: center;
-	margin: 32rpx 40rpx;
-	width: 670rpx;
-	height: 80rpx;
-	background: #FFFFFF;
-	border-radius: 40rpx;
-	border-radius: 40rpx;
-	background-color: #F8F8F8;
-}
-
-.tui-searchcion {
-	display: inline-block;
-	margin-left: 24rpx;
-	width: 48rpx;
-	height: 48rpx;
-}
-
-.tui-search-bar-input {
-	margin-left: 16rpx;
-	line-height: 40rpx;
-	font-size: 28rpx;
-	width: 100%;
-	display: inline-block;
-}
-
-.tui-common-words-list {
-	position: absolute;
-	top: 242rpx;
-	bottom: 68rpx;
-	width: 750rpx;
-}
-
-.tui-common-words-item {
-	width: 750rpx;
-	height: 112rpx;
-	border-bottom: 2rpx solid #EEF0F3;
-	background-color: #FFFFFF;
-	font-family: PingFangSC-Regular;
-	font-size: 32rpx;
-	color: #333333;
-	letter-spacing: 0;
-	line-height: 44rpx;
-	padding: 0 40rpx;
-	display: flex;
-	align-items: center;
-}

+ 0 - 85
components/im/tui-chat/message-private/common-words/index.vue

@@ -1,85 +0,0 @@
-<template>
-	<view v-show="display" class="tui-common-words-container">
-		<view class="tui-common-words-box">
-			<view class="tui-common-words-title">
-				<view>请选择常用语</view>
-				<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-common-words-close" @tap="handleClose">关闭</view>
-			</view>
-			<view class="tui-search-bar">
-				<image class="tui-searchcion" src="/static/assets/serach-icon.svg"></image>
-				<input class="tui-search-bar-input" :value="words" placeholder="请输入关键字搜索" @input="wordsInput" />
-			</view>
-			<scroll-view class="tui-common-words-list" scroll-y="true" enable-flex="true">
-				<view v-for="(item, index) in commonWordsMatch" :key="index" class="tui-common-words-item" @tap="sendMessage" :data-words="item">{{ item }}</view>
-			</scroll-view>
-		</view>
-	</view>
-</template>
-
-<script>
-const commonWordsList = [
-	'你好在吗',
-	'问题A',
-	'问题B'
-];
-
-export default {
-	data() {
-		return {
-			words: '',
-			commonWordsMatch: commonWordsList
-		};
-	},
-
-	components: {},
-	props: {
-		display: {
-			type: Boolean,
-			default: false
-		}
-	},
-	watch: {
-		display: {
-			handler: function(newVal) {
-				// this.setData({
-				//   display: newVal
-				// });
-			},
-			immediate: true
-		}
-	},
-	methods: {
-		handleClose() {
-			this.$emit('close', {
-				detail: {
-					key: '0'
-				}
-			});
-		},
-
-		wordsInput(e) {
-			(this.commonWordsMatch = []),
-				commonWordsList.forEach(item => {
-					if (item.indexOf(e.detail.value) > -1) {
-						this.commonWordsMatch.push(item);
-					}
-				});
-			this.setData({
-				words: e.detail.value,
-				commonWordsMatch: this.commonWordsMatch
-			});
-		},
-
-		sendMessage(e) {
-			this.$emit('sendMessage', {
-				detail: {
-					message: e.currentTarget.dataset.words
-				}
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 178
components/im/tui-chat/message-private/order-list/index.css

@@ -1,178 +0,0 @@
-.tui-cards-container {
-	position: fixed;
-	width: 100vw;
-	height: 100vh;
-	z-index: 100;
-	top: 0;
-	/*  #ifdef  H5  */
-	top: calc(88rpx + constant(safe-area-inset-top));
-	top: calc(88rpx + env(safe-area-inset-top));
-	/*  #endif  */
-	background: rgba(0, 0, 0, 0.5);
-}
-
-.tui-cards-box {
-	position: absolute;
-	width: 100%;
-	height: 60%;
-	bottom: 0;
-	background: #F4F5F9;
-	padding-bottom: 68rpx;
-	z-index: 200;
-}
-
-.tui-cards-title {
-	display: flex;
-	flex-wrap: nowrap;
-	justify-content: space-between;
-	padding-left: 40rpx;
-	padding-right: 40rpx;
-	padding-top: 48rpx;
-	font-family: PingFangSC-Medium;
-	font-size: 36rpx;
-	color: #000000;
-	letter-spacing: 0;
-	line-height: 50rpx;
-}
-
-.tui-search-bar {
-	display: flex;
-	flex-wrap: nowrap;
-	align-items: center;
-	margin: 32rpx 40rpx;
-	width: 670rpx;
-	height: 80rpx;
-	background: #FFFFFF;
-	border-radius: 40rpx;
-	border-radius: 40rpx;
-	background-color: #F8F8F8;
-}
-
-.tui-searchcion {
-	display: inline-block;
-	margin-left: 24rpx;
-	width: 48rpx;
-	height: 48rpx;
-}
-
-.tui-search-bar-input {
-	margin-left: 16rpx;
-	line-height: 40rpx;
-	font-size: 28rpx;
-	width: 100%;
-	display: inline-block;
-}
-
-.tui-order-list {
-	position: absolute;
-	top: 242rpx;
-	bottom: 68rpx;
-	width: 750rpx;
-}
-
-.tui-order-item {
-	width: 670rpx;
-	margin: 32rpx 40rpx;
-	height: 388rpx;
-	background-color: #FFFFFF;
-	border-radius: 4px;
-	display: flex;
-	flex-wrap: wrap;
-	align-items: center;
-}
-
-.order-title {
-	width: 670rpx;
-	height: 102rpx;
-	padding: 32rpx 40rpx;
-	padding-bottom: 0;
-	border-bottom: 2rpx solid #EEF0F3;
-}
-
-.order-title>.order-number {
-	font-family: PingFangSC-Medium;
-	font-size: 32rpx;
-	line-height: 44rpx;
-	color: #000000;
-	letter-spacing: 0;
-	margin-bottom: 8rpx;
-}
-
-.order-title>.order-time {
-	font-family: PingFangSC-Regular;
-	font-size: 24rpx;
-	line-height: 34rpx;
-	color: #999999;
-	letter-spacing: 0;
-}
-
-.order-info {
-	display: flex;
-	flex-wrap: nowrap;
-	width: 670rpx;
-	height: 236rpx;
-	padding: 32rpx 40rpx;
-}
-
-.order-content {
-	width: 450rpx;
-	margin-left: 32rpx;
-}
-
-.order-content-title {
-	font-family: PingFangSC-Medium;
-	width: 378rpx;
-	line-height: 34rpx;
-	font-size: 24rpx;
-	color: #000000;
-	letter-spacing: 0;
-	margin-bottom: 12rpx;
-}
-
-.order-content-description {
-	display: flex;
-	flex-wrap: nowrap;
-	font-family: PingFangSC-Regular;
-	max-width: 410rpx;
-	line-height: 34rpx;
-	font-size: 24rpx;
-	color: #999999;
-	letter-spacing: 0;
-	margin-bottom: 12rpx;
-}
-
-.order-content-price {
-	font-family: PingFangSC-Medium;
-	font-size: 36rpx;
-	line-height: 50rpx;
-	color: #FF7201;
-	letter-spacing: 0;
-}
-
-.order-image {
-	width: 156rpx;
-	height: 156rpx;
-}
-
-.btn-send-order {
-	width: 176rpx;
-	height: 58rpx;
-	background-color: #006EFF;
-	border-radius: 14.5px;
-	font-family: PingFangSC-Regular;
-	font-size: 24rpx;
-	color: #FFFFFF;
-	line-height: 28px;
-	text-align: center;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	margin-right: 40rpx;
-}
-
-.btn-send-text {
-	font-family: PingFangSC-Regular;
-	font-size: 12px;
-	color: #FFFFFF;
-	line-height: 14px;
-}

+ 0 - 137
components/im/tui-chat/message-private/order-list/index.vue

@@ -1,137 +0,0 @@
-<template>
-	<view v-if="display" class="tui-cards-container">
-		<view class="tui-cards-box">
-			<view class="tui-cards-title">
-				<view>请选择你要发送的订单</view>
-				<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-cards-close" @tap="handleClose">关闭</view>
-			</view>
-			<view class="tui-search-bar">
-				<image class="tui-searchcion" src="/static/assets/serach-icon.svg"></image>
-				<input class="tui-search-bar-input" :value="words" placeholder="搜索" @input="wordsInput" />
-			</view>
-			<scroll-view class="tui-order-list" scroll-y="true" enable-flex="true">
-				<view v-for="(item, index) in orderMatch" :key="index" class="tui-order-item">
-					<view class="order-title">
-						<view class="order-number">订单编号: {{ item.orderNum }}</view>
-						<view class="order-time">{{ item.time }}</view>
-					</view>
-					<view class="order-info">
-						<image class="order-image" :src="item.imageUrl"></image>
-						<view class="order-content">
-							<view class="order-content-title">{{ item.title }}</view>
-							<view class="order-content-description">{{ item.description }}</view>
-							<view style="display: flex; flex-wrap: no-wrap; justify-content: space-between;">
-								<view class="order-content-price">{{ item.price }}</view>
-								<view class="btn-send-order" :data-order="item" @tap.stop="sendMessage"><text class="btn-send-text">发送此订单</text></view>
-							</view>
-						</view>
-					</view>
-				</view>
-			</scroll-view>
-		</view>
-	</view>
-</template>
-
-<script>
-const orderList = [
-	{
-		orderNum: 1,
-		time: '2021-7-20 20:45',
-		title: '[天博检验]新冠核酸检测/预约',
-		description: '专业医学检测,电子报告',
-		imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/miles.jpeg',
-		price: '80元'
-	},
-	{
-		orderNum: 2,
-		time: '2021-7-20 22:45',
-		title: '[路边]新冠核酸检测/预约',
-		description: '专业医学检测,电子报告',
-		imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/miles.jpeg',
-		price: '7000元'
-	}
-];
-
-export default {
-	data() {
-		return {
-			words: '',
-			orderMatch: orderList
-		};
-	},
-
-	components: {},
-	props: {
-		display: {
-			type: Boolean,
-			default: false
-		},
-		conversation: {
-			type: Object,
-			default: () => {}
-		}
-	},
-	watch: {
-		display: {
-			handler: function(newVal) {
-				// this.setData({
-				//   display: newVal
-				// });
-			},
-			immediate: true
-		},
-		conversation: {
-			
-			handler: function(newVal) {
-				this.conversation=newVal
-			},
-			immediate: true,
-			deep: true
-		}
-	},
-	methods: {
-		handleClose() {
-			this.$emit('close', {
-				detail: {
-					key: '1'
-				}
-			});
-		},
-		wordsInput(e) {
-			var that=this;
-			(this.orderMatch = []),
-				orderList.forEach(item => {
-					if (item.title.indexOf(e.detail.value) > -1 || item.orderNum === ~~e.detail.value) {
-						this.orderMatch.push(item);
-					}
-				});
-			that.setData({
-				words: e.detail.value,
-				orderMatch: this.orderMatch
-			});
-		},
-
-		sendMessage(e) {
-			const { order } = e.currentTarget.dataset;
-			this.$emit('sendCustomMessage', {
-				detail: {
-					payload: {
-						// data 字段作为表示,可以自定义
-						data: 'order',
-						description: order.description,
-						// 获取骰子点数
-						extension: JSON.stringify({
-							title: order.title,
-							imageUrl: order.imageUrl,
-							price: order.price
-						})
-					}
-				}
-			});
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 0 - 93
components/im/tui-chat/message-private/service-evaluation/index.css

@@ -1,93 +0,0 @@
-.tui-cards-container {
-	position: fixed;
-	width: 100vw;
-	height: 100vh;
-	z-index: 100;
-	top: 0;
-	/*  #ifdef  H5  */
-	top: calc(88rpx + constant(safe-area-inset-top));
-	top: calc(88rpx + env(safe-area-inset-top));
-	/*  #endif  */
-	background: rgba(0, 0, 0, 0.5);
-}
-
-.service-evaluation {
-	position: absolute;
-	bottom: 0;
-	right: 0;
-	left: 0;
-	background: #FFFFFF;
-	padding: 48rpx 40rpx;
-}
-
-.header {
-	display: flex;
-	justify-content: space-between;
-	font-family: PingFangSC-Regular;
-}
-
-.btn {
-	width: 100%;
-	padding: 0;
-	margin: 0 auto;
-	text-align: center;
-	background: none;
-}
-
-.btn-close {
-	color: #006EFF;
-}
-
-.header-label {
-	font-size: 18px;
-	color: #000000;
-	letter-spacing: 0;
-	line-height: 25px;
-}
-
-.header .btn {
-	font-size: 16px;
-	color: #006EFF;
-	letter-spacing: 0;
-	line-height: 24px;
-}
-
-.main {
-	display: flex;
-	flex-direction: column;
-	padding: 48rpx 0;
-}
-
-.main-evaluation-score {
-	padding: 0 60rpx 48rpx;
-	display: flex;
-	justify-content: space-between;
-	align-items: flex-end;
-}
-
-.main-evaluation-score .score-star {
-	width: 72rpx;
-	height: 72rpx;
-}
-
-.main-textarea {
-	background: #F8F8F8;
-	border: 0 solid #D9D9D9;
-	border-radius: 4px;
-	font-size: 14px;
-	padding: 16rpx 32rpx;
-}
-
-.textarea-placeholder {
-	color: #B0B0B0;
-}
-
-.footer .btn {
-	width: 100%;
-	padding: 26rpx 0;
-	background: #006EFF;
-	border-radius: 24px;
-	border-radius: 24px;
-	font-size: 16px;
-	color: #FFFFFF;
-}

+ 0 - 116
components/im/tui-chat/message-private/service-evaluation/index.vue

@@ -1,116 +0,0 @@
-<template>
-	<view class="tui-cards-container" v-if="display">
-		<view class="service-evaluation">
-			<view class="header">
-				<label class="header-label">请对本次服务进行评价</label>
-				<view class="btn-close" @tap="handleClose">关闭</view>
-			</view>
-			<view class="main">
-				<view class="main-evaluation-score">
-					<image
-						v-for="(item, index) in scoreList"
-						:key="index"
-						class="score-star"
-						:data-score="item"
-						:src="'https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/star' + (item > score ? '-grey' : '') + '.png'"
-						@tap="handleScore"
-					></image>
-				</view>
-				<textarea
-					class="main-textarea"
-					cols="30"
-					rows="10"
-					@input="bindTextAreaInput"
-					placeholder="请输入评语"
-					placeholder-style="textarea-placeholder"
-				></textarea>
-			</view>
-			<view class="footer"><view class="btn" @tap="sendMessage" :disabled="score === 0 && !comment">提交评价</view></view>
-		</view>
-	</view>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			scoreList: [1, 2, 3, 4, 5],
-			score: 5,
-			comment: ''
-		};
-	},
-
-	components: {},
-	props: {
-		display: {
-			type: Boolean,
-			default: ''
-		}
-	},
-	watch: {
-		display: {
-			handler: function(newVal) {},
-			immediate: true
-		}
-	},
-
-	onPageShow() {
-		this.score= 0;
-		this.comment='';
-		 
-	},
-
-	methods: {
-		handleClose() {
-			this.$emit('close', {
-				detail: {
-					key: '2'
-				}
-			});
-		},
-
-		handleScore(e) {
-			let { score } = e.currentTarget.dataset;
-
-			if (score === this.score) {
-				score = 0;
-			}
-
-			this.setData({
-				score
-			});
-		},
-
-		bindTextAreaInput(e) {
-			this.setData({
-				comment: e.detail.value
-			});
-		},
-
-		sendMessage() {
-			this.$emit('sendCustomMessage', {
-				detail: {
-					payload: {
-						// data 字段作为表示,可以自定义
-						data: 'evaluation',
-						description: '对本次服务的评价',
-						// 获取骰子点数
-						extension: JSON.stringify({
-							score: this.score,
-							comment: this.comment
-						})
-					}
-				}
-			});
-			this.setData({
-				score: 0,
-				comment: ''
-			});
-			this.handleClose();
-		}
-	}
-};
-</script>
-<style>
-@import './index.css';
-</style>

+ 25 - 1
components/likeProduct.vue

@@ -7,7 +7,10 @@
 		<view class="like-list">
 			<view class="item" v-for="(item,index) in list" :key="index" @click="showProduct(item)">
 				<view class="img-box">
-					<image :src="item.image" mode=""></image>
+					<image class="img" :src="item.image" mode="aspectFill"></image>
+					<view class="tag-row" v-if="item.tagList && item.tagList.length > 0">
+						<text class="tag-chip" v-for="(t, i) in item.tagList" :key="i">{{ t }}</text>
+					</view>
 				</view>
 				<view class="info-box">
 					<view class="title ellipsis2">{{ item.productName }}</view>
@@ -158,11 +161,32 @@
 			.img-box{
 				width: 100%;
 				height: 394rpx;
+				position: relative;
 				image{
 					width: 100%;
 					height: 100%;
 				}
+				.tag-row {
+					position: absolute;
+					bottom:0;
+					display: flex;
+					flex-wrap: wrap;
+					gap: 12rpx;
+					padding: 0 12rpx;
+					margin-bottom: 12rpx;
+				}
+				.tag-chip {
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 30rpx;
+					color: #AA4726;
+					line-height: 42rpx;
+					background: #FFF4F1;
+					padding: 4rpx 12rpx;
+					border-radius: 6rpx;
+				}
 			}
+			
 			.info-box{
 				box-sizing: border-box;
 				//height: 182upx;

+ 0 - 214
components/tuiProduct.vue

@@ -1,214 +0,0 @@
-<template>
-   <view>
-		<view class="like-title">
-			<image src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/tui.png" mode=""></image>
-			<text class="text">精选商品</text>
-		</view>
-		<view class="like-list">
-			<view class="item" v-for="(item,index) in list" :key="index" @click="showProduct(item)">
-				<view class="img-box">
-					<image :src="item.image" mode=""></image>
-				</view>
-				<view class="info-box">
-					<view class="title ellipsis2">{{ item.productName }}</view>
-					<view class="price-box">
-						<view class="now">
-							<text class="unit">¥</text>
-							<text class="num"  v-if="userinfoa.isShow==1&&isuser==false">{{item.price.toFixed(2)}}</text>
-							<text class="num" v-else>{{item.otPrice.toFixed(2)}}</text>
-						</view>
-						<view class="old" v-if="userinfoa.isShow==1&&isuser==false">¥{{item.otPrice.toFixed(2)}}</view>
-					</view>
-				</view>
-			</view>
-		</view>
-		<Loading :loaded="loaded" :loading="loading"></Loading>
-   </view>
-</template>
-
-<script>
-  import {getTuiProducts} from '@/api/product'
-  import {getUserInfo} from '@/api/user'
-  import Loading from "@/components/Loading";
-  export default {
-	components: {Loading },
-    name: "likeProduct",
-	data() {
-		return {
-			page:{
-				page: 1,
-				pageSize: 10
-			},
-			total:0,
-			list:[],
-			loaded: false,
-			loading: false,
-			userinfoa:[],
-			isuser:false,
-		};
-	},
-	created() {
-	},
-	mounted() {
-		this.getTuiProducts();
-		if(uni.getStorageSync('AppToken')){
-			this.getUserInfos()
-		}else{
-			this.isuser=true
-		}
-	},
-	watch: {
-		UserInfo() {
-		    return uni.getStorageSync('AppToken')
-		}
-	},
-	methods: {
-		getUserInfos(){
-			getUserInfo().then(res => {
-					if(res.code==200){
-						if(res.user!=null){
-							// if(res.user.isPromoter==null||res.user.isPromoter==0){
-							// 	this.tuiModalControl=true
-							// }
-							this.userinfoa=res.user
-							console.log(this.userinfoa.isShow,78787)
-						}
-					}else{
-						uni.showToast({
-							icon:'none',
-							title: "请求失败",
-						});
-					}
-				},
-				rej => {}
-			);
-		},
-		getTuiProducts(){
-			console.log(1)
-			var that=this;
-			if (that.loaded == true || that.loading == true) return;
-			that.loading = true;
-			uni.showLoading({
-				title:"加载中..."
-			})
-			getTuiProducts(that.page).then(
-				res => {
-					if(res.code==200){
-						that.total=res.data.total;
-						that.list.push.apply(that.list, res.data.list);
-						that.loading = false;
-						that.loaded = that.list.length<that.total?false:true;
-						that.page.page = that.page.page + 1;
-						uni.hideLoading()
-					}
-				},
-				err => {
-					uni.hideLoading()
-					uni.showToast({
-						title: err.msg ,
-						icon: 'none',
-						duration: 2000
-					});
-				}
-			);
-		},
-		showProduct(item){
-			uni.navigateTo({
-				url: '/pages/shopping/productDetails?productId='+item.productId
-			})
-		},
-	}
- 
-  };
-</script>
-<style lang="scss">
-	.like-title{
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		padding: 15upx 0rpx 30upx 0rpx;
-		image{
-			width: 37upx;
-			height: 37upx;
-			margin-right: 20upx;
-		}
-		.text{
-			font-size: 36upx;
-			font-family: PingFang SC;
-			font-weight: bold;
-			color: #111111;
-			line-height: 1;
-		}
-	}
-	.like-list{
-		display: flex;
-		flex-wrap: wrap;
-		.item{
-			margin-right: 20rpx;
-			margin-bottom: 20rpx;
-			width: 345rpx;
-			background: #FFFFFF;
-			box-shadow: 0px 0px 10rpx 4rpx rgba(199, 199, 199, 0.22);
-			border-radius: 20rpx;
-			overflow: hidden;
-			&:nth-child(2n) {
-				margin-right: 0;
-			}
-			.img-box{
-				width: 100%;
-				height: 334upx;
-				image{
-					width: 100%;
-					height: 100%;
-				}
-			}
-			.info-box{
-				box-sizing: border-box;
-				height: 182upx;
-				padding: 20upx 20upx 30upx;
-				display: flex;
-				flex-direction: column;
-				justify-content: space-between;
-				.title{
-					font-size: 26upx;
-					font-family: PingFang SC;
-					font-weight: 500;
-					color: #111111;
-					line-height: 40upx;
-				}
-				.price-box{
-					display: flex;
-					align-items: flex-end;
-					.now{
-						display: flex;
-						align-items: flex-end;
-						margin-right: 20upx;
-						.unit{
-							font-size: 24upx;
-							font-family: PingFang SC;
-							font-weight: 500;
-							color: #FF6633;
-							line-height: 1.2;
-							margin-right: 4upx;
-						}
-						.num{
-							font-size: 36upx;
-							font-family: PingFang SC;
-							font-weight: bold;
-							color: #FF6633;
-							line-height: 1;
-						}
-					}
-					.old{
-						font-size: 26upx;
-						font-family: PingFang SC;
-						font-weight: 500;
-						text-decoration: line-through;
-						color: #BBBBBB;
-						line-height: 1.1;
-					}
-				}
-			}
-		}
-	}
-</style>

+ 2 - 2
pages.json

@@ -897,7 +897,7 @@
 				{
 					"path": "managerOrder/productList",
 					"style": {
-						"navigationBarTitleText": "品列表",
+						"navigationBarTitleText": "品列表",
 						"app-plus": {
 							"titleNView": false
 						}
@@ -906,7 +906,7 @@
 				{
 					"path": "managerOrder/productDetails",
 					"style": {
-						"navigationBarTitleText": "品详情",
+						"navigationBarTitleText": "品详情",
 						"enablePullDownRefresh": false
 					}
 				},

+ 53 - 5
pages/home/components/CategoryTags.vue

@@ -1,5 +1,16 @@
 <template>
-	<view class="category-tags" v-if="tags && tags.length > 0">
+	<view class="category-tags" v-if="loading || (tags && tags.length > 0)">
+		<!-- 骨架屏:默认 2 行 4 列,与最多布局一致 -->
+		<view class="category-skeleton" v-show="loading && !(tags && tags.length)">
+			<view class="sk-grid">
+				<view class="sk-item" v-for="i in skeletonCount" :key="'sk-' + i">
+					<view class="sk-icon"></view>
+					<view class="sk-name"></view>
+				</view>
+			</view>
+		</view>
+
+		<view v-show="tags && tags.length > 0">
 		<!-- 1个:整行单模块 -->
 		<view v-if="tags.length === 1" class="layout-single">
 			<view class="tag-item" @tap="onSelect(tags[0])">
@@ -31,6 +42,7 @@
 				<text class="tag-name">{{ item.categoryName|| '-' }}</text>
 			</view>
 		</view>
+		</view>
 	</view>
 </template>
 
@@ -38,11 +50,10 @@
 export default {
 	name: 'CategoryTags',
 	props: {
-		tags: { type: Array, default: () => [] }
+		tags: { type: Array, default: () => [] },
+		loading: { type: Boolean, default: false },
+		skeletonCount: { type: Number, default: 8 }
 	},
-  onload(val){
-    //console.log('-----',this.tags)
-  },
 	methods: {
 		onSelect(item) {
 			//console.log(item,'item')
@@ -152,4 +163,41 @@ export default {
 		text-align: center;
 	}
 }
+
+.sk-grid {
+	display: grid;
+	grid-template-columns: repeat(4, 1fr);
+}
+
+.sk-item {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	margin-bottom: 30rpx;
+}
+
+.sk-icon {
+	width: 100rpx;
+	height: 100rpx;
+	border-radius: 50%;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+	flex-shrink: 0;
+}
+
+.sk-name {
+	width: 80rpx;
+	height: 28rpx;
+	margin-top: 12rpx;
+	border-radius: 6rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+@keyframes shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
 </style>

+ 212 - 13
pages/home/components/RecommendSection.vue

@@ -1,5 +1,27 @@
 <template>
-	<view class="recommend-section" v-if="hasContent">
+	<view class="recommend-section" v-if="loading || hasContent">
+		<!-- 骨架屏:固定占位高度,避免数据到达后布局拉伸闪烁 -->
+		<view class="recommend-skeleton" v-show="loading && !hasContent">
+			<view class="recommend-layout">
+				<view class="sk-block-left"></view>
+				<view class="sk-block-right">
+					<view class="sk-block-small" v-for="i in 2" :key="'sk-' + i">
+						<view class="sk-head">
+							<view class="sk-title"></view>
+							<view class="sk-more"></view>
+						</view>
+						<view class="sk-body">
+							<view class="sk-item" v-for="j in 2" :key="'sk-item-' + j">
+								<view class="sk-thumb"></view>
+								<view class="sk-price"></view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view v-show="hasContent">
 		<!-- 平均分(占位,无数据时显示 —) -->
 		<view class="score-row" v-if="showScore">
 			<text class="score-label">平均分</text>
@@ -51,12 +73,19 @@
 						</view>
 					</view>
 					<view class="block-small-body">
-						<view class="small-item" v-for="(item, i) in hot.slice(0, 2)" :key="i"
-							@click.stop="onItemClick(item, 'hot')">
-							<image class="small-thumb" :src="getFirstImage(item)" mode="aspectFill"></image>
+						<view class="small-item" v-for="row in hotItems" :key="row.key"
+							@click.stop="onItemClick(row.item, 'hot')">
+							<view class="small-thumb-wrap">
+								<image
+									v-if="row.src"
+									class="small-thumb"
+									mode="aspectFill"
+									:src="row.src"
+								></image>
+							</view>
 							<view>
 								<text class="unit">¥</text>
-								<text class="small-price" v-if="item.price != null">{{ Number(item.price).toFixed(2)
+								<text class="small-price" v-if="row.item.price != null">{{ Number(row.item.price).toFixed(2)
 									}}</text>
 							</view>
 						</view>
@@ -71,12 +100,19 @@
 						</view>
 					</view>
 					<view class="block-small-body">
-						<view class="small-item" v-for="(item, e) in green.slice(0, 2)" :key="e"
-							@click.stop="onItemClick(item, 'green')">
-							<image class="small-thumb" :src="getFirstImage(item)" mode="aspectFill"></image>
+						<view class="small-item" v-for="row in greenItems" :key="row.key"
+							@click.stop="onItemClick(row.item, 'green')">
+							<view class="small-thumb-wrap">
+								<image
+									v-if="row.src"
+									class="small-thumb"
+									mode="aspectFill"
+									:src="row.src"
+								></image>
+							</view>
 							<view>
 								<text class="unit">¥</text>
-								<text class="small-price" v-if="item.price != null">{{ Number(item.price).toFixed(2)
+								<text class="small-price" v-if="row.item.price != null">{{ Number(row.item.price).toFixed(2)
 									}}</text>
 							</view>
 
@@ -85,6 +121,7 @@
 				</view>
 			</view>
 		</view>
+		</view>
 	</view>
 </template>
 
@@ -95,6 +132,7 @@ export default {
 		live: { type: Array, default: () => [] },
 		green: { type: Array, default: () => [] },
 		hot: { type: Array, default: () => [] },
+		loading: { type: Boolean, default: false },
 		averageScore: { type: [String, Number], default: null },
 		showScore: { type: Boolean, default: false }
 	},
@@ -107,14 +145,68 @@ export default {
 		},
 		liveCurrentDisplay() {
 			return this.liveList.length > 0 ? this.liveCurrent + 1 : 0
+		},
+		hotItems() {
+			return this.buildThumbRows(this.hot, 'hot')
+		},
+		greenItems() {
+			return this.buildThumbRows(this.green, 'green')
 		}
 	},
 	data() {
 		return {
-			liveCurrent: 0
+			liveCurrent: 0,
+			thumbSrcMap: {}
 		}
 	},
+	watch: {
+		hot: { handler: 'preloadThumbs', deep: true },
+		green: { handler: 'preloadThumbs', deep: true }
+	},
+	mounted() {
+		this.preloadThumbs()
+	},
 	methods: {
+		thumbKey(type, item, index) {
+			const id = item && item.productId
+			return id ? `${type}-${id}` : `${type}-${index}`
+		},
+		buildThumbRows(list, type) {
+			return (list || []).slice(0, 2).map((item, index) => {
+				const key = this.thumbKey(type, item, index)
+				return { key, item, src: this.thumbSrcMap[key] || '' }
+			})
+		},
+		preloadThumbs() {
+			const pending = []
+			;[
+				{ type: 'hot', list: this.hot },
+				{ type: 'green', list: this.green }
+			].forEach(({ type, list }) => {
+				;(list || []).slice(0, 2).forEach((item, index) => {
+					const key = this.thumbKey(type, item, index)
+					const src = this.getFirstImage(item)
+					if (!src) {
+						if (this.thumbSrcMap[key]) this.$delete(this.thumbSrcMap, key)
+						return
+					}
+					if (this.thumbSrcMap[key] === src) return
+					if (this.thumbSrcMap[key]) this.$delete(this.thumbSrcMap, key)
+					pending.push({ key, src })
+				})
+			})
+			pending.forEach(({ key, src }) => {
+				uni.getImageInfo({
+					src,
+					success: () => {
+						this.$set(this.thumbSrcMap, key, src)
+					},
+					fail: () => {
+						this.$set(this.thumbSrcMap, key, src)
+					}
+				})
+			})
+		},
 	onLive(){
 		uni.showToast({
 			title: '暂无课程',
@@ -177,20 +269,26 @@ export default {
 
 .recommend-layout {
 	display: flex;
+	align-items: stretch;
 	gap: 20rpx;
 }
 
 .block-left-wrap {
 	flex: 1;
-	// width: 340rpx;
 	flex-shrink: 0;
-	// height: 420rpx;
+	align-self: stretch;
+	min-height: 0;
 	border-radius: 24rpx;
 	overflow: hidden;
 	position: relative;
 }
 
 .live-swiper {
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
 	width: 100%;
 	height: 100%;
 }
@@ -372,10 +470,23 @@ export default {
 	align-items: center;
 }
 
-.small-thumb {
+.small-thumb-wrap {
 	width: 110rpx;
 	height: 110rpx;
 	border-radius: 10rpx;
+	background: #f0f0f0;
+	overflow: hidden;
+	flex-shrink: 0;
+	position: relative;
+}
+
+.small-thumb {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	display: block;
 }
 
 .unit {
@@ -392,4 +503,92 @@ export default {
 	color: #FF233C;
 	margin-top: 8rpx;
 }
+
+.sk-block-left {
+	flex: 1;
+	flex-shrink: 0;
+	align-self: stretch;
+	min-height: 0;
+	border-radius: 24rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-block-right {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 20rpx;
+	min-width: 0;
+}
+
+.sk-block-small {
+	flex: 1;
+	min-height: 200rpx;
+	border-radius: 24rpx;
+	padding: 16rpx;
+	background: #fff;
+	overflow: hidden;
+}
+
+.sk-head {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	margin-bottom: 18rpx;
+}
+
+.sk-title {
+	width: 140rpx;
+	height: 36rpx;
+	border-radius: 8rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-more {
+	width: 80rpx;
+	height: 28rpx;
+	border-radius: 6rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-body {
+	display: flex;
+}
+
+.sk-item {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+
+.sk-thumb {
+	width: 110rpx;
+	height: 110rpx;
+	border-radius: 10rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-price {
+	width: 72rpx;
+	height: 28rpx;
+	margin-top: 16rpx;
+	border-radius: 6rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: shimmer 1.2s ease-in-out infinite;
+}
+
+@keyframes shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
 </style>

+ 0 - 377
pages/home/doctorCase.vue

@@ -1,377 +0,0 @@
-<template>
-	<view>
-		<view class="top-fixed">
-			<!-- 搜索框 -->
-			<view class="search-cont">
-				<view class="inner">
-					<image class="icon-search" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/search.png" mode=""></image>
-					<input type="text"  v-model="searchVal" placeholder="输入关键字搜索"  confirm-type="搜索" @confirm="goSearch" placeholder-style="font-size:28rpx;color:#BBBBBB;font-family: PingFang SC;" />
-				</view>
-			</view>
-			<!-- tab切换 -->
-			<view class="pub-tab-box">
-				<view class="tab-inner">
-					<view 
-						v-for="(item,index) in depts" 
-						:key="index"
-						:class="deptId == item.departmentId?'item active':'item'"
-						@click="changeDept(item)"
-					>
-						<view class="text">
-							{{ item.departmentName }}
-							<image v-show="deptId == item.departmentId" class="tab-bg" src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/tab_bg.png" mode=""></image>
-						</view>
-					</view>
-				</view>
-			</view>
-		</view>
-		<!-- 数据列表 -->
-		<mescroll-body ref="mescroll"  top="190rpx" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
-			<view class="cases-list">
-				<view class="item" v-for="(item,index) in dataList" :key="index">
-					<!-- 文字 -->
-					<view class="dec-text ellipsis2">{{item.title}}</view>
-					<!-- 有图 -->
-					<view class="images-box"  v-if="item.imgs!=null"  >
-						<view class="img-item" v-for="(subitem,j) in utils.photosToArr(item.imgs)" @click="showImg(item.imgs)" :key="j">
-							<image :src="subitem" mode="aspectFill" ></image>
-						</view>
-					</view>
-					<!-- 医生信息 -->
-					<view class="doc-info">
-						<view class="head">
-							<image :src="item.doctorHeadImg" mode="aspectFill"></image>
-						</view>
-						<view class="name">{{item.doctorName}}</view>
-						<view class="line"></view>
-						<view class="posit">
-							{{utils.getDictLabelName("doctorPosition",item.doctorPosition)}}
-						</view>
-						<view class="line"></view>
-						<view class="address">{{item.hospitalName}}</view>
-					</view>
-					<view class="answer-box">
-						<!-- 文字回答 -->
-						<text class="text-inner" v-if="item.orderType == '1' ">{{item.replyContent}}</text>
-						<!-- 语音回答 -->
-						<view class="voice-inner" v-if="item.orderType == '2'">
-							<free-audio 
-								startPic='https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/play.png' 
-								endPic='https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/pause.png'
-								activeColor="#FF233C"
-								:audioId="'audio'+index"
-								:url='item.replyAudioUrl'
-							></free-audio>
-						</view>
-					</view>
-					<view class="read-box">
-						<image src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/eye.png" mode=""></image>
-						<text class="text">{{item.views}}人看过</text>
-					</view>
-				</view>
-			</view>
-		</mescroll-body>
-	</view>
-</template>
-
-<script>
-	import {getDepartmentList,getDoctorCase} from '@/api/doctorOrder.js'
-	import freeAudio from '@/components/chengpeng-audio/free-audio.vue'
-	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
-	
-	export default {
-		components: { freeAudio},
-		mixins: [MescrollMixin], 
-		data() {
-			return {
-				depts:[],
-				deptId:0,
-				searchVal:"",
-				// tab切换
-				casesCateChose: 1,
-				// 上拉加载的配置
-				mescroll:null,
-				// 上拉加载的配置
-				upOption: {
-					onScroll:true,
-					use: true, // 是否启用上拉加载; 默认true
-					page: {
-						num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
-						size: 10 // 每页数据的数量,默认10
-					},
-					noMoreSize: 10, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
-					empty: {
-						icon:'https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/no_data.png',
-						tip: '暂无数据'
-					}
-				},
-				// 列表数据
-				dataList: []
-			};
-		},
-		onLoad() {
-			this.getDepartmentList()
-		},
-		methods: {
-			changeDept(item) {
-				this.deptId = item.departmentId;
-				this.mescroll.resetUpScroll()
-				
-			},
-			getDepartmentList(){
-				getDepartmentList().then(res => {
-					if(res.code==200){
-						var allDept={departmentId:0,departmentName:"全部"}
-						this.depts.push(allDept);
-						this.depts=this.depts.concat(res.data);
-						console.log(this.depts)
-						
-					}else{
-						uni.showToast({
-							icon:'none',
-							title: "请求失败",
-						});
-					}
-				});
-			},
-			goSearch(){
-				this.mescroll.resetUpScroll()
-			},
-			showImg(urls) {
-				 var imgArr =urls.split(',');
-				 //预览图片
-				 uni.previewImage({
-				 	urls: imgArr,
-				 	current: imgArr[0]
-				 });
-			},
-			mescrollInit(mescroll) {
-				this.mescroll = mescroll;
-			},
-			/*下拉刷新的回调 */
-			downCallback(mescroll) {
-				mescroll.resetUpScroll()
-			},
-			upCallback(page) {
-				//联网加载数据
-				var that = this;
-				var data = {
-					departmentId:this.deptId,
-					title:this.searchVal,
-					page: page.num,
-					pageSize: page.size
-				};
-				getDoctorCase(data).then(res => {
-					if(res.code==200){
-						//设置列表数据
-						if (page.num == 1) {
-							that.dataList = res.data.list; 
-							
-						} else {
-							that.dataList = that.dataList.concat(res.data.list);
-							 
-						}
-						that.mescroll.endBySize(res.data.list.length, res.data.total);
-						
-					}else{
-						uni.showToast({
-							icon:'none',
-							title: "请求失败",
-						});
-						that.dataList = null;
-						that.mescroll.endErr();
-					}
-				});
-			},
-		}
-	}
-</script>
-
-<style lang="scss">
-	.search-cont{
-		padding: 16upx 30upx;
-		background-color: #FFFFFF;
-		.inner{
-			box-sizing: border-box;
-			width: 100%;
-			height: 72upx;
-			background: #F7F7F7;
-			border-radius: 36upx;
-			display: flex;
-			align-items: center;
-			padding: 0 30upx;
-			.icon-search{
-				width: 28upx;
-				height: 28upx;
-				margin-right: 20upx;
-			}
-			input{
-				height: 60upx;
-				line-height: 60upx;
-				flex: 1;
-			}
-		}
-	}
-	.pub-tab-box{
-		padding: 0 33upx;
-		background-color: #FFFFFF;
-		.tab-inner{
-			height: 88upx;
-			line-height: 88upx;
-			display: flex;
-			overflow-x: auto;
-		}
-		.item{
-			font-size: 28upx;
-			white-space: nowrap;
-			line-height: 1;
-			font-family: PingFang SC;
-			font-weight: 500;
-			color: #666666;
-			margin-right: 60upx;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			&:last-child{
-				margin-right: 0;
-			}
-			&.active{
-				font-weight: bold;
-				color: #333333;
-			}
-			.text{
-				position: relative;
-				z-index: 1;
-			}
-			.tab-bg{
-				width: 72upx;
-				height: 28upx;
-				position: absolute;
-				top: 17upx;
-				left: 50%;
-				transform: translateX(-36upx);
-				z-index: -1;
-			}
-		}
-	}
-	.top-fixed{
-		width: 100%;
-		position: fixed;
-		top: 0;
-		left: 0;
-		z-index: 10;
-	}
-	.top-seat{
-		width: 100%;
-		height: 212upx;
-	}
-	// 问诊案例
-	.cases-list{
-		padding: 20upx;
-		.item{
-			padding: 30upx;
-			background: #FFFFFF;
-			border-radius: 16upx;
-			margin-bottom: 20upx;
-			.dec-text{
-				font-size: 32upx;
-				font-family: PingFang SC;
-				font-weight: bold;
-				color: #111111;
-				line-height: 48upx;
-			}
-			.images-box{
-				margin-top: 10upx;
-				display: flex;
-				flex-wrap: wrap;
-				.img-item{
-					width: 155upx;
-					height: 155upx;
-					background: #F5F5F5;
-					border-radius: 8upx;
-					margin: 0 10upx 10upx 0;
-					overflow: hidden;
-					image{
-						width: 100%;
-						height: 100%;
-					}
-					&:nth-child(4n){
-						margin-right: 0;
-					}
-				}
-			}
-			.doc-info{
-				display: flex;
-				align-items: center;
-				margin: 30upx 0 20upx;
-				.head{
-					width: 60upx;
-					height: 60upx;
-					background: #F2F5F9;
-					border-radius: 50%;
-					margin-right: 20upx;
-					overflow: hidden;
-					image{
-						width: 100%;
-						height: 100%;
-					}
-				}
-				.name{
-					font-size: 28upx;
-					line-height: 1;
-					font-family: PingFang SC;
-					font-weight: 500;
-					color: #111111;
-				}
-				.line{
-					width: 1px;
-					height: 22upx;
-					background: #DDDDDD;
-					margin: 0 16upx;
-				}
-				.posit,
-				.address{
-					font-size: 26upx;
-					font-family: PingFang SC;
-					font-weight: 500;
-					color: #999999;
-				}
-			}
-			.answer-box{
-				width: 100%;
-				// height: 117upx;
-				background: #F5F7F7;
-				border-radius: 10upx;
-				display: flex;
-				flex-direction: column;
-				justify-content: center;
-				.text-inner{
-					// height: 84upx;
-					font-size: 28upx;
-					font-family: PingFang SC;
-					font-weight: 500;
-					color: #666666;
-					line-height: 42upx;
-					padding: 15upx;
-				}
-			}
-			.read-box{
-				margin-top: 30upx;
-				display: flex;
-				align-items: center;
-				justify-content: flex-end;
-				image{
-					width: 24upx;
-					height: 19upx;
-					margin-right: 10upx;
-				}
-				.text{
-					font-size: 24upx;
-					font-family: PingFang SC;
-					font-weight: 500;
-					color: #999999;
-				}
-			}
-		}
-	}
-</style>

+ 261 - 38
pages/home/index.vue

@@ -49,7 +49,24 @@
 					 <image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/back.png" ></image>
 					</view>
 				</view>
-				<view class="feature-video-card" >
+				<!-- 视频区骨架屏 -->
+				<view class="video-skeleton" v-if="videoLoading && !hasVideoContent">
+					<view class="sk-tag-row">
+						<view class="sk-tag" v-for="i in 4" :key="'vtag-' + i"></view>
+					</view>
+					<view class="sk-video-card">
+						<view class="sk-fv-body">
+							<view class="sk-fv-thumb"></view>
+							<view class="sk-fv-info">
+								<view class="sk-line sk-line-lg"></view>
+								<view class="sk-line sk-line-md"></view>
+								<view class="sk-line sk-line-md"></view>
+								<view class="sk-btn"></view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="feature-video-card" v-else>
 					<scroll-view scroll-x class="tag-scroll" :show-scrollbar="false">
 						<view
 							v-for="(tag, idx) in videoTags"
@@ -63,11 +80,7 @@
 					</scroll-view>
 					<view class="x-bc" @click="onWatchLive(videos)">
 						<view class="fv-thumb">
-							<image class="fv-img" :src="videos.imgUrl" mode="aspectFill"></image>
-							<!-- <view class="live-tag">
-								<text class="live-dot"></text>
-								<text>直播中</text>
-							</view> -->
+							<image v-if="videoThumbSrc" class="fv-img" :src="videoThumbSrc" mode="aspectFill"></image>
 						</view>
 						<view class="fv-info">
 							<text class="fv-title">{{videos.courseName}}</text>
@@ -75,7 +88,6 @@
 							<view class="btn-watch" @click.stop="onWatchLive(videos)">立即观看</view>
 						</view>
 					</view>
-					
 				</view>
 			</view>
 
@@ -90,7 +102,7 @@
 					 <image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/back.png" ></image>
 					</view>
 				</view>
-				<view class="nav-row">
+				<view class="nav-row" v-if="navList.length > 0">
 					<scroll-view scroll-x class="nav-scroll" :show-scrollbar="false">
 					<view class="nav-inner">
 						<view
@@ -104,29 +116,48 @@
 					</view>
 					</scroll-view>
 				</view>
-				<view class="course-list">
-					<view
-						v-for="(course, idx) in courses"
-						:key="idx"
-						class="course-card"
-						@click="onCourseClick(course)"
-					>
-						<view class="course-thumb">
-							<image class="course-img" :src="course.cover" mode="aspectFill"></image>
-							<!-- <text class="course-tag">{{ course.tag }}</text> -->
+				<!-- 首屏骨架:仅课程列表 -->
+				<view class="course-skeleton" v-if="courseSectionLoading && courses.length === 0">
+					<view class="sk-course-list">
+						<view class="sk-course-card" v-for="i in 3" :key="'course-' + i">
+							<view class="sk-course-thumb"></view>
+							<view class="sk-course-info">
+								<view class="sk-line sk-line-lg"></view>
+								<view class="sk-line sk-line-md"></view>
+								<view class="sk-course-foot">
+									<view class="sk-line sk-line-sm"></view>
+									<view class="sk-btn-round"></view>
+								</view>
+							</view>
 						</view>
-						<view class="course-info">
-							<text class="course-name">{{ course.name }}</text>
-							<view class="x-end" style="justify-content: space-between;">
-								<view class="course-meta">
-									<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
-									<text class="meta-count">{{ courseViewsDisplay(course.views) }}</text>
+					</view>
+				</view>
+				<view class="course-list-wrap" v-else>
+					<view class="course-list" :class="{ 'list-dimmed': courseRefreshing }">
+						<view
+							v-for="(course, idx) in courses"
+							:key="course.courseId"
+							class="course-card"
+							@click="onCourseClick(course)"
+						>
+							<view class="course-thumb">
+								<image v-if="course.cover" class="course-img" :src="course.cover" mode="aspectFill"></image>
+							</view>
+							<view class="course-info">
+								<text class="course-name">{{ course.name }}</text>
+								<view class="x-end" style="justify-content: space-between;">
+									<view class="course-meta">
+										<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
+										<text class="meta-count">{{ courseViewsDisplay(course.views) }}</text>
+									</view>
+									<view class="btn-watch2" @click.stop="onCourseClick(course)">立即观看</view>
 								</view>
-								<view class="btn-watch2" @click.stop="onCourseClick(course)">立即观看</view>
 							</view>
-							
 						</view>
 					</view>
+					<view class="course-refresh-mask" v-if="courseRefreshing">
+						<view class="refresh-spinner"></view>
+					</view>
 				</view>
 				<view class="course-loadmore" v-if="courses.length > 0">
 					<text v-if="courseLoading">加载中...</text>
@@ -155,10 +186,20 @@ export default {
 			coursePageNum: 1,
 			coursePageSize: 10,
 			courseLoading: false,
+			courseRefreshing: false,
 			courseHasMore: true,
+			courseRequestId: 0,
+			videoLoading: true,
+			courseSectionLoading: true,
+			videoThumbSrc: '',
 			menuButtonInfo: {}, // 胶囊按钮布局信息
 		}
 	},
+	computed: {
+		hasVideoContent() {
+			return !!(this.videos && this.videos.courseId)
+		}
+	},
 	onLoad() {
 		this.getMenuButtonInfo(); // 初始化获取胶囊信息
 		
@@ -183,6 +224,7 @@ export default {
 			return "100w+";
 		},
 		onSelectByIndex(cateId) {
+			if (cateId === this.activeId || this.courseRefreshing) return;
 			this.activeId = cateId;
 			this.getPublicCourseList(cateId, { reset: true });
 		},
@@ -191,6 +233,8 @@ export default {
 			this.getPublicVideoList(cateId);
 		},
 		async initPublicVideoData() {
+			const hasCache = this.videoTags.length > 0 && this.hasVideoContent
+			if (!hasCache) this.videoLoading = true
 			try {
 				const res = await listPublicCourseCategory({homePage:1,yxxTag:1, pageNum: 1, pageSize: 4 });
 				const list = (res && res.data && res.data.list) || [];
@@ -200,13 +244,19 @@ export default {
 					await this.getPublicVideoList(this.videoTagIndex);
 				} else {
 					this.videos = {};
+					this.videoThumbSrc = '';
 				}
 			} catch (e) {
 				this.videoTags = [];
 				this.videos = {};
+				this.videoThumbSrc = '';
+			} finally {
+				this.videoLoading = false
 			}
 		},
 		async initPublicCourseData() {
+			const hasCache = this.navList.length > 0 && this.courses.length > 0
+			if (!hasCache) this.courseSectionLoading = true
 			try {
 				const res = await listPublicCourseCategory({ homePage:1, pageNum: 1, pageSize: 10 });
 				const list = (res && res.data && res.data.list) || [];
@@ -221,9 +271,32 @@ export default {
 				this.navList = [];
 				this.courses = [];
 				this.courseHasMore = false;
+				this.courseSectionLoading = false
+			}
+		},
+		preloadVideoThumb(src) {
+			if (!src) {
+				this.videoThumbSrc = ''
+				return Promise.resolve()
 			}
+			if (this.videoThumbSrc === src) return Promise.resolve()
+			return new Promise(resolve => {
+				uni.getImageInfo({
+					src,
+					success: () => {
+						this.videoThumbSrc = src
+						resolve()
+					},
+					fail: () => {
+						this.videoThumbSrc = src
+						resolve()
+					}
+				})
+			})
 		},
-		async getPublicVideoList(subCateId, options = {}) {
+		async getPublicVideoList(subCateId) {
+			const hasCache = this.hasVideoContent && this.videos.imgUrl
+			if (!hasCache) this.videoLoading = true
 			try {
 				const res = await listPublicCourse({
 					recommendSlot:1,
@@ -233,32 +306,42 @@ export default {
 					subCateId: subCateId
 				});
 				const list = (res && res.data && res.data.list) || [];
-				this.videos = list[0]
+				this.videos = list[0] || {}
+				await this.preloadVideoThumb(this.videos.imgUrl)
 			} catch (e) {
 				this.videos = {};
-			} 
+				this.videoThumbSrc = '';
+			} finally {
+				this.videoLoading = false
+			}
 		},
 		async getPublicCourseList(subCateId, options = {}) {
 			const { reset = false } = options;
-			if (this.courseLoading) return;
+			if (this.courseLoading && !reset) return;
 			if (!reset && !this.courseHasMore) return;
+			const requestId = ++this.courseRequestId;
 			if (reset) {
 				this.coursePageNum = 1;
 				this.courseHasMore = true;
-				this.courses = [];
+				if (this.courses.length > 0) {
+					this.courseRefreshing = true;
+				} else {
+					this.courseSectionLoading = true;
+				}
 			}
 			this.courseLoading = true;
-			const params={
-				recommendSlot:3,
-				yxxTag:0,
+			const params = {
+				recommendSlot: 3,
+				yxxTag: 0,
 				pageNum: this.coursePageNum,
 				pageSize: this.coursePageSize,
-			}
-			if (subCateId!==0) {
-				params.subCateId = subCateId
+			};
+			if (subCateId !== 0) {
+				params.subCateId = subCateId;
 			}
 			try {
 				const res = await listPublicCourse(params);
+				if (requestId !== this.courseRequestId) return;
 				const list = (res && res.data && res.data.list) || [];
 				const mappedList = list.map(item => ({
 					courseId: item.courseId,
@@ -272,12 +355,16 @@ export default {
 					this.coursePageNum += 1;
 				}
 			} catch (e) {
+				if (requestId !== this.courseRequestId) return;
 				if (reset) {
 					this.courses = [];
 				}
 				this.courseHasMore = false;
 			} finally {
+				if (requestId !== this.courseRequestId) return;
 				this.courseLoading = false;
+				this.courseRefreshing = false;
+				if (reset) this.courseSectionLoading = false;
 			}
 		},
 		onScrollToLower() {
@@ -318,7 +405,7 @@ export default {
 		},
 		onCourseClick(course) {
 			if (course && course.courseId) {
-				uni.navigateTo({ url: '/pages_index/courseDetail?courseId=' + course.courseId + '&type=1' })
+				uni.navigateTo({ url: '/pages_index/courseDetail?courseId=' + course.courseId})
 				return;
 			}
 			//uni.navigateTo({ url: '/pages_index/courseDetail'})
@@ -693,10 +780,39 @@ export default {
 	}
 }
 /* 课程列表 */
+.course-list-wrap {
+	position: relative;
+}
 .course-list {
 	display: flex;
 	flex-direction: column;
 	gap: 24rpx;
+	transition: opacity 0.2s ease;
+}
+.course-list.list-dimmed {
+	opacity: 0.55;
+}
+.course-refresh-mask {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	pointer-events: none;
+}
+.refresh-spinner {
+	width: 48rpx;
+	height: 48rpx;
+	border: 4rpx solid #e8e8e8;
+	border-top-color: #FF233C;
+	border-radius: 50%;
+	animation: spin 0.8s linear infinite;
+}
+@keyframes spin {
+	to { transform: rotate(360deg); }
 }
 .course-loadmore {
 	padding: 20rpx 0 4rpx;
@@ -782,4 +898,111 @@ export default {
 .bottom-placeholder {
 	height: 120rpx;
 }
+
+/* 骨架屏通用 */
+@mixin sk-shimmer {
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-line {
+	height: 28rpx;
+	border-radius: 6rpx;
+	@include sk-shimmer;
+}
+.sk-line-lg { width: 90%; }
+.sk-line-md { width: 70%; margin-top: 16rpx; }
+.sk-line-sm { width: 40%; }
+
+@keyframes skeleton-shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
+
+/* 视频区骨架 */
+.video-skeleton {
+	.sk-tag-row {
+		white-space: nowrap;
+		margin-bottom: 5rpx;
+		height: 80rpx;
+		overflow: hidden;
+	}
+	.sk-tag {
+		display: inline-block;
+		width: 120rpx;
+		height: 56rpx;
+		margin-right: 24rpx;
+		border-radius: 10rpx;
+		@include sk-shimmer;
+	}
+	.sk-video-card {
+		background: linear-gradient(135deg, #5F111A 0%, #37050B 100%);
+		border-radius: 30rpx;
+		padding: 30rpx 20rpx;
+	}
+	.sk-fv-body {
+		display: flex;
+		align-items: flex-start;
+	}
+	.sk-fv-thumb {
+		width: 360rpx;
+		height: 240rpx;
+		border-radius: 20rpx;
+		flex-shrink: 0;
+		margin-right: 20rpx;
+		@include sk-shimmer;
+	}
+	.sk-fv-info {
+		flex: 1;
+		min-width: 0;
+		padding-top: 8rpx;
+	}
+	.sk-btn {
+		width: 188rpx;
+		height: 64rpx;
+		margin-top: 28rpx;
+		border-radius: 12rpx;
+		@include sk-shimmer;
+	}
+}
+
+/* 课程区骨架 */
+.course-skeleton {
+	.sk-course-list {
+		display: flex;
+		flex-direction: column;
+		gap: 24rpx;
+	}
+	.sk-course-card {
+		display: flex;
+		background: #fff;
+		border-radius: 20rpx;
+		padding: 20rpx;
+	}
+	.sk-course-thumb {
+		width: 296rpx;
+		height: 222rpx;
+		border-radius: 20rpx;
+		flex-shrink: 0;
+		@include sk-shimmer;
+	}
+	.sk-course-info {
+		flex: 1;
+		padding-left: 24rpx;
+		min-width: 0;
+	}
+	.sk-course-foot {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		margin-top: 40rpx;
+	}
+	.sk-btn-round {
+		width: 168rpx;
+		height: 64rpx;
+		border-radius: 32rpx;
+		@include sk-shimmer;
+	}
+}
 </style>

+ 70 - 6
pages/home/newindex.vue

@@ -30,11 +30,12 @@
     <ChannelEntry :list="channelList" :per-row="4" :rows="2" @click="onChannelClick" />
 
 		<!-- 金刚区:分类图标 2 行 4 列,仅展示 -->
-		<CategoryTags :tags="categoryTagsData" @selectClick="onCategoryTagsSelect" />
+		<CategoryTags :tags="categoryTagsData" :loading="categoryTagsLoading" @selectClick="onCategoryTagsSelect" />
 
 
 		<!-- 推荐区块:左侧直播卡(直播中/回放)+ 右上绿色有机 + 右下上新推荐 -->
 		<RecommendSection
+			:loading="recommendLoading"
 			:live="recommendData.live"
 			:green="recommendData.green"
 			:hot="recommendData.hot"
@@ -79,7 +80,7 @@ import { getMenu, getIndexData, getCartCount } from '@/api/index'
 import { getStoreConfig } from '@/api/common'
 import { getHomeInit, getHomeRecommend, getHomeGoods } from '@/api/home.js'
 import HotProduct from './components/HotProduct.vue'
-import TuiProduct from '@/components/tuiProduct.vue'
+//import TuiProduct from '@/components/tuiProduct.vue'
 import SearchBar from './components/SearchBar.vue'
 import CategoryTags from './components/CategoryTags.vue'
 import ChannelEntry from './components/ChannelEntry.vue'
@@ -88,11 +89,14 @@ import GoodsNav from './components/GoodsNav.vue'
 import GoodsList from './components/GoodsList.vue'
 import { getUserInfo, bindPromoter } from '@/api/user'
 
+const HOME_RECOMMEND_CACHE_KEY = 'home_recommend_cache'
+const HOME_INIT_CACHE_KEY = 'home_init_cache'
+
 export default {
 	components: {
 		zModal,
 		HotProduct,
-		TuiProduct,
+		//TuiProduct,
 		SearchBar,
 		CategoryTags,
 		ChannelEntry,
@@ -114,8 +118,10 @@ export default {
 			menus: [],
 			channelList: [],
 			categoryTagsData: [],
+			categoryTagsLoading: true,
 			goodsNav: [],
 			recommendData: { live: [], green: [], hot: [] },
+			recommendLoading: true,
 			activeGoodsNavId: 'all',
 			goodsList: [],
 			goodsLoading: false,
@@ -151,6 +157,8 @@ export default {
 		}
 	},
 	onLoad(option) {
+		this.loadRecommendCache()
+		this.loadHomeInitCache()
 		this.getMenuButtonInfo(); // 初始化获取胶囊信息
 		if (option.userCode != null) {
 			uni.setStorageSync('userCode', option.userCode)
@@ -193,7 +201,7 @@ export default {
 	},
 	onReachBottom() {
 		this.loadMoreGoods()
-		this.$refs.tuiProduct && this.$refs.tuiProduct.getTuiProducts()
+		//this.$refs.tuiProduct && this.$refs.tuiProduct.getTuiProducts()
 	},
 	methods: {
 		// 获取胶囊按钮布局参数
@@ -210,20 +218,73 @@ export default {
 				};
 			}
 		},
+		loadHomeInitCache() {
+			try {
+				const raw = uni.getStorageSync(HOME_INIT_CACHE_KEY)
+				if (!raw) return
+				const data = typeof raw === 'string' ? JSON.parse(raw) : raw
+				if (data.channelList && data.channelList.length) {
+					this.channelList = data.channelList
+				}
+				if (data.categoryTags && data.categoryTags.length) {
+					this.categoryTagsData = data.categoryTags
+					this.categoryTagsLoading = false
+				}
+				if (data.goodsNav && data.goodsNav.length) {
+					this.goodsNav = data.goodsNav
+				}
+			} catch (e) {}
+		},
+		saveHomeInitCache(data) {
+			try {
+				uni.setStorageSync(HOME_INIT_CACHE_KEY, JSON.stringify(data))
+			} catch (e) {}
+		},
 		getHomeInit() {
+			if (!this.categoryTagsData.length) this.categoryTagsLoading = true
 			getHomeInit().then(res => {
 				if (res.code == 200 && res.data) {
 					this.channelList = res.data.channelList || []
 					this.categoryTagsData = res.data.categoryTags || []
 					this.goodsNav = res.data.goodsNav || []
+					this.saveHomeInitCache({
+						channelList: this.channelList,
+						categoryTags: this.categoryTagsData,
+						goodsNav: this.goodsNav
+					})
 					if (this.activeGoodsNavId === null || this.activeGoodsNavId === undefined) {
 						this.activeGoodsNavId = 'all'
 					}
 					this.fetchGoodsList(true)
 				}
-			}).catch(() => {})
+			}).catch(() => {}).finally(() => {
+				this.categoryTagsLoading = false
+			})
+		},
+		loadRecommendCache() {
+			try {
+				const raw = uni.getStorageSync(HOME_RECOMMEND_CACHE_KEY)
+				if (!raw) return
+				const data = typeof raw === 'string' ? JSON.parse(raw) : raw
+				const hasCache = (data.live && data.live.length) || (data.green && data.green.length) || (data.hot && data.hot.length)
+				if (!hasCache) return
+				this.recommendData = {
+					live: data.live || [],
+					green: data.green || [],
+					hot: data.hot || []
+				}
+				this.recommendLoading = false
+			} catch (e) {}
+		},
+		saveRecommendCache(data) {
+			try {
+				uni.setStorageSync(HOME_RECOMMEND_CACHE_KEY, JSON.stringify(data))
+			} catch (e) {}
 		},
 		getHomeRecommend() {
+			const cached = this.recommendData
+			const hasCache = (cached.live && cached.live.length) || (cached.green && cached.green.length) || (cached.hot && cached.hot.length)
+			if (!hasCache) this.recommendLoading = true
 			getHomeRecommend().then(res => {
 				if (res.code != 200) return
 				this.recommendData = {
@@ -231,7 +292,10 @@ export default {
 					green: res.green && Array.isArray(res.green) ? res.green : [],
 					hot: res.hot && Array.isArray(res.hot) ? res.hot : []
 				}
-			}).catch(() => {})
+				this.saveRecommendCache(this.recommendData)
+			}).catch(() => {}).finally(() => {
+				this.recommendLoading = false
+			})
 		},
 		fetchGoodsList(reset) {
 			if (reset) {

+ 70 - 20
pages/home/productList.vue

@@ -49,8 +49,16 @@
 	 
 		<!-- 数据列表 -->
 		<mescroll-body top="100rpx" bottom="40rpx" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
-			<view class="goods-grid">
-				<view class="grid-item" v-for="(item, index) in dataList" :key="index">
+			<!-- 首屏 / 刷新:骨架屏 -->
+			<view class="skeleton-column" v-if="listLoading && (!dataList || dataList.length === 0)">
+				<view class="skeleton-item" v-for="i in 3" :key="'sk-' + i">
+					<view class="skeleton-img"></view>
+					<view class="skeleton-line skeleton-line-lg"></view>
+					<view class="skeleton-line skeleton-line-sm"></view>
+				</view>
+			</view>
+			<view class="goods-grid" v-else-if="dataList && dataList.length > 0">
+				<view class="grid-item" v-for="(item, index) in dataList" :key="item.productId || index">
 					<GoodsCard :item="item" />
 				</view>
 			</view>
@@ -93,6 +101,7 @@
 				},
 				// 列表数据
 				dataList: [],
+				listLoading: true,
 			};
 		},
 		onLoad(option) {
@@ -127,34 +136,37 @@
 			},
 			upCallback(page) {
 				// 与 newindex 瀑布流使用同一接口:getHomeGoods,id 为分类(0=全部)
-				//联网加载数据
 				var that = this;
-				this.form.pageNum=page.num;
-				this.form.pageSize=page.size;
+				if (page.num === 1) {
+					that.listLoading = true;
+					that.dataList = [];
+				}
+				this.form.pageNum = page.num;
+				this.form.pageSize = page.size;
 				getHomeGoods(this.form).then(res => {
-					if(res.code==200){
-						//设置列表数据
+					if (res.code == 200) {
+						const list = (res.data && res.data.list) ? res.data.list : [];
+						const total = res.data && res.data.total != null ? res.data.total : 0;
 						if (page.num == 1) {
-							that.dataList = res.data.list; 
-							
+							that.dataList = list;
 						} else {
-							that.dataList = that.dataList.concat(res.data.list);
-							 
+							that.dataList = that.dataList.concat(list);
 						}
-						that.mescroll.endBySize(res.data.list.length, res.data.total);
-						
-					}else{
+						that.mescroll.endBySize(list.length, total);
+					} else {
 						uni.showToast({
-							icon:'none',
-							title: "请求失败",
+							icon: 'none',
+							title: '请求失败',
 						});
-						that.dataList = null;
+						if (page.num == 1) that.dataList = [];
 						that.mescroll.endErr();
 					}
 				}).catch(() => {
-				that.dataList = page.num == 1 ? [] : that.dataList
-				that.mescroll.endErr()
-			})
+					if (page.num == 1) that.dataList = [];
+					that.mescroll.endErr();
+				}).finally(() => {
+					if (page.num === 1) that.listLoading = false;
+				});
 			},
 			// 价格升序是否选中
 			priceUp(value) {
@@ -278,4 +290,42 @@
 	.goods-grid .grid-item {
 		width: 100%;
 	}
+	.skeleton-column {
+		padding: 20rpx;
+		display: flex;
+		flex-direction: column;
+		gap: 20rpx;
+	}
+	.skeleton-item {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		overflow: hidden;
+	}
+	.skeleton-img {
+		width: 100%;
+		height: 394rpx;
+		background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+		background-size: 200% 100%;
+		animation: skeleton-shimmer 1.2s ease-in-out infinite;
+		border-radius: 8rpx;
+	}
+	.skeleton-line {
+		height: 28rpx;
+		margin-top: 20rpx;
+		border-radius: 6rpx;
+		background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+		background-size: 200% 100%;
+		animation: skeleton-shimmer 1.2s ease-in-out infinite;
+	}
+	.skeleton-line-lg {
+		width: 85%;
+	}
+	.skeleton-line-sm {
+		width: 50%;
+	}
+	@keyframes skeleton-shimmer {
+		0% { background-position: 100% 0; }
+		100% { background-position: -100% 0; }
+	}
 </style>

+ 69 - 18
pages/home/recommendList.vue

@@ -55,7 +55,15 @@
 			:down="downOption"
 			:up="upOption"
 		>
-			<view class="list-wrap">
+			<!-- 首屏 / 刷新:骨架屏 -->
+			<view class="skeleton-column" v-if="listLoading && (!dataList || dataList.length === 0)">
+				<view class="skeleton-item" v-for="i in 3" :key="'sk-' + i">
+					<view class="skeleton-img"></view>
+					<view class="skeleton-line skeleton-line-lg"></view>
+					<view class="skeleton-line skeleton-line-sm"></view>
+				</view>
+			</view>
+			<view class="list-wrap" v-else-if="dataList && dataList.length > 0">
 				<view class="goods-grid">
 					<view class="grid-item" v-for="(item, index) in dataList" :key="item.productId">
 						<GoodsCard :item="item" :show-price="true" @click="showProduct(item)" />
@@ -96,7 +104,8 @@ export default {
 				},
 				textNoMore: '已经到底了'
 			},
-			dataList: []
+			dataList: [],
+			listLoading: true
 		}
 	},
 	onLoad(options) {
@@ -117,34 +126,38 @@ export default {
 		},
 		upCallback(page) {
 			const that = this
+			if (page.num === 1) {
+				that.listLoading = true
+				that.dataList = []
+			}
 			getHomeGoodsRecommend({
-				keyword:this.keyword,
+				keyword: this.keyword,
 				type: that.type,
 				pageNum: page.num,
 				pageSize: page.size
 			}).then(res => {
-				if(res.code==200){
-					//设置列表数据
+				if (res.code == 200) {
+					const list = (res.data && res.data.list) ? res.data.list : []
+					const total = res.data && res.data.total != null ? res.data.total : 0
 					if (page.num == 1) {
-						that.dataList = res.data.list; 
-						
+						that.dataList = list
 					} else {
-						that.dataList = that.dataList.concat(res.data.list);
-						 
+						that.dataList = that.dataList.concat(list)
 					}
-					that.mescroll.endBySize(res.data.list.length, res.data.total);
-					
-				}else{
+					that.mescroll.endBySize(list.length, total)
+				} else {
 					uni.showToast({
-						icon:'none',
-						title: "请求失败",
-					});
-					that.dataList = null;
-					that.mescroll.endErr();
+						icon: 'none',
+						title: '请求失败'
+					})
+					if (page.num == 1) that.dataList = []
+					that.mescroll.endErr()
 				}
 			}).catch(() => {
-				that.dataList = page.num == 1 ? [] : that.dataList
+				if (page.num == 1) that.dataList = []
 				that.mescroll.endErr()
+			}).finally(() => {
+				if (page.num === 1) that.listLoading = false
 			})
 		},
 		showProduct(item) {
@@ -217,4 +230,42 @@ export default {
 .goods-grid .grid-item {
 	width: 100%;
 }
+.skeleton-column {
+	padding: 24rpx;
+	display: flex;
+	flex-direction: column;
+	gap: 20rpx;
+}
+.skeleton-item {
+	background: #fff;
+	border-radius: 16rpx;
+	padding: 24rpx;
+	overflow: hidden;
+}
+.skeleton-img {
+	width: 100%;
+	height: 394rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+	border-radius: 8rpx;
+}
+.skeleton-line {
+	height: 28rpx;
+	margin-top: 20rpx;
+	border-radius: 6rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+.skeleton-line-lg {
+	width: 85%;
+}
+.skeleton-line-sm {
+	width: 50%;
+}
+@keyframes skeleton-shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
 </style>

+ 0 - 52
pages/index/index.vue

@@ -1,52 +0,0 @@
-<template>
-	<view class="content">
-		<image class="logo" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/share.png"></image>
-		<view class="text-area">
-			<text class="title">{{title}}</text>
-		</view>
-	</view>
-</template>
-
-<script>
-	export default {
-		data() {
-			return {
-				title: 'Hello'
-			}
-		},
-		onLoad() {
-
-		},
-		methods: {
-
-		}
-	}
-</script>
-
-<style>
-	.content {
-		display: flex;
-		flex-direction: column;
-		align-items: center;
-		justify-content: center;
-	}
-
-	.logo {
-		height: 200rpx;
-		width: 200rpx;
-		margin-top: 200rpx;
-		margin-left: auto;
-		margin-right: auto;
-		margin-bottom: 50rpx;
-	}
-
-	.text-area {
-		display: flex;
-		justify-content: center;
-	}
-
-	.title {
-		font-size: 36rpx;
-		color: #8f8f94;
-	}
-</style>

+ 82 - 79
pages_course/videovip.vue

@@ -232,64 +232,28 @@
 			<image class="bg" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/bg-back.png" mode="widthFix"></image>
 			<view class="tab-Box" v-if="visibleRatingTabCount > 1">
 				<view
-					v-if="showTabReviewPoints"
+					v-for="tab in ratingTabList"
+					:key="tab.key"
 					class="tab-item"
-					:class="{ 'tab-item--active': tabIndex == 0 }"
-					@click="selecTab(0)"
+					:class="{ 'tab-item--active': tabIndex == tab.index }"
+					@click="selecTab(tab.index)"
 				>
 					<view class="tab-item__surface">
-						<image v-if="tabIndex == 0" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/zan-sel.png"></image>
-						<image v-else src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/sel.png"></image>
-						<text class="tab-item__text">评价得积分</text>
-					</view>
-				</view>
-				<view
-					v-if="showTabColumnIntro"
-					class="tab-item"
-					:class="{ 'tab-item--active': tabIndex == 1 }"
-					@click="selecTab(1)"
-				>
-					<view class="tab-item__surface">
-						<image v-if="tabIndex == 1" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/des-sel.png"></image>
-						<image v-else src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/des.png"></image>
-						<text class="tab-item__text">栏目介绍</text>
-					</view>
-				</view>
-				<view
-					v-if="showTabFeaturedComments"
-					class="tab-item"
-					:class="{ 'tab-item--active': tabIndex == 2 }"
-					@click="selecTab(2)"
-				>
-					<view class="tab-item__surface">
-						<image v-if="tabIndex == 2" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/pl-sel.png"></image>
-						<image v-else src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/pl.png"></image>
-						<text class="tab-item__text">精选留言</text>
+						<image :src="tabIndex == tab.index ? tab.iconActive : tab.icon" mode="aspectFit"></image>
+						<text class="tab-item__text">{{ tab.label }}</text>
 					</view>
 				</view>
 			</view>
-			<!-- 仅一个 Tab:单行左对齐标题,无选中态 -->
+			<!-- 仅一个 Tab:单行左对齐标题,无选中态(顺序:精选留言 / 栏目介绍 / 评价得积分) -->
 			<view class="tab-Box tab-Box--single" v-else-if="visibleRatingTabCount === 1">
-				<view v-if="showTabReviewPoints" class="tab-item tab-item--single">
-					<view class="tab-item__surface tab-item__surface--single">
-						<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/zan-sel.png" mode="aspectFit"></image>
-						<text class="tab-item__text tab-item__text--single">评价得积分</text>
-					</view>
-				</view>
-				<view v-if="showTabColumnIntro" class="tab-item tab-item--single">
+				<view v-for="tab in ratingTabList" :key="tab.key" class="tab-item tab-item--single">
 					<view class="tab-item__surface tab-item__surface--single">
-						<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/des-sel.png" mode="aspectFit"></image>
-						<text class="tab-item__text tab-item__text--single">栏目介绍</text>
-					</view>
-				</view>
-				<view v-if="showTabFeaturedComments" class="tab-item tab-item--single">
-					<view class="tab-item__surface tab-item__surface--single">
-						<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/pl-sel.png" mode="aspectFit"></image>
-						<text class="tab-item__text tab-item__text--single">精选留言</text>
+						<image :src="tab.iconActive" mode="aspectFit"></image>
+						<text class="tab-item__text tab-item__text--single">{{ tab.label }}</text>
 					</view>
 				</view>
 			</view>
-			<view class="rating-body" v-if="tabIndex == 0 && showTabReviewPoints">
+			<view class="rating-body" v-if="tabIndex == 2 && showTabReviewPoints">
 				<view class="rating-ques-block" v-for="(item,index) in quesList" :key="index">
 					<view class="title">{{item.title}}</view>
 					<view class="sat-box">
@@ -310,7 +274,7 @@
 					mode="widthFix"
 				></image>
 			</view>
-			<view class="rating-body rating-body--featured" v-if="tabIndex == 2 && showTabFeaturedComments">
+			<view class="rating-body rating-body--featured" v-if="tabIndex == 0 && showTabFeaturedComments">
 				<scroll-view
 					scroll-y
 					class="rating-msg-scroll"
@@ -585,9 +549,9 @@
 					@getuserinfo="userInfologin"  v-if="authType==0&&!isquestion">{{isLogin&&isAddKf==1 ? '提交答案领取奖励' : '立即学习'}}</button> -->
 				
 				<template v-if="!isquestion && showTabReviewPoints">
-					<button class="author-btn" @click="openList" v-if="isLogin&&isAddKf==1&&isShu">评价得积分<image src="/static/images/jifen.png"></image>
-					</button>
-					<button class="author-btn" @click="submit" v-if="isLogin&&isAddKf==1&&remainTime==0&&!isShu&&tabIndex==0">评价得积分
+					<button class="author-btn" @click="openList" v-if="isLogin&&isAddKf==1&&isShu">{{ showTabFeaturedComments ? '精选留言' : showTabColumnIntro ? '栏目介绍' : '评价得积分' }}
+					<image  v-if="showTabReviewPoints" src="/static/images/jifen.png"></image></button>
+					<button class="author-btn" @click="submit" v-if="isLogin&&isAddKf==1&&remainTime==0&&!isShu&&tabIndex==2">评价得积分
 					<image src="/static/images/jifen.png"></image>
 					</button>
 				</template>
@@ -597,7 +561,7 @@
 					open-type="getUserInfo" :disabled="userdisabled"
 					@getuserinfo="userInfologin" v-if="authType==0&&!isLogin&&isAddKf!==1">立即学习</button> -->
 					<!-- 竖屏 isShu 下 rating 区隐藏;若仍停留在精选留言 tab,勿在 footer 展示留言框,只保留「评价得积分」按钮(与 _expandLayoutIfPortraitFeaturedCommentsOnly 竖屏体验一致) -->
-					<view v-if="tabIndex==2 && showTabFeaturedComments && !(showTabReviewPoints && isShu)" class="rating-msg-input-shell">
+					<view v-if="tabIndex==0 && showTabFeaturedComments && !(showTabReviewPoints && isShu)" class="rating-msg-input-shell">
 						<view
 							v-if="featuredCommentMedia"
 							class="rating-msg-media-pill"
@@ -628,7 +592,7 @@
 							<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/upimg.png"></image>
 						</view>
 					</view>
-				<view class="author-cart" v-show="!(tabIndex === 2 && showTabFeaturedComments && isLogin && isAddKf == 1 && !isquestion && !isShu)">
+				<view class="author-cart" v-show="!(tabIndex === 0 && showTabFeaturedComments && isLogin && isAddKf == 1 && !isquestion && !isShu)">
 					<view class="more-box">
 						<image src="/static/images/more24.png" @click="showMore"></image>
 						<view class="tips">更多</view>
@@ -648,7 +612,7 @@
 		<u-loading-page :loading="viewload" iconSize="32" loadingColor="#3c9cff" fontSize="24"
 			:loading-text="loadingtext"></u-loading-page>
 		<ykscreenRecord></ykscreenRecord>
-		<template v-if="tabIndex==2 && showTabFeaturedComments && !(showTabReviewPoints && isShu)">
+		<template v-if="tabIndex==0 && showTabFeaturedComments && !(showTabReviewPoints && isShu)">
 			<view
 				v-if="featuredCommentComposerActive"
 				class="featured-comment-composer-mask"
@@ -691,7 +655,6 @@
 							placeholder-class="fc-composer-ph"
 							:focus="featuredCommentInputFocused"
 							:adjust-position="false"
-							@focus="onFeaturedCommentComposerInputFocus"
 							@blur="onFeaturedCommentComposerInputBlur"
 						/>
 						<view class="fc-composer-input-icon" @click.stop="onFeaturedPickMedia">
@@ -1160,11 +1123,39 @@
 				return !this.featuredCommentHasMore
 			},
 			visibleRatingTabCount() {
-				let n = 0
-				if (this.showTabReviewPoints) n++
-				if (this.showTabColumnIntro) n++
-				if (this.showTabFeaturedComments) n++
-				return n
+				return this.ratingTabList.length
+			},
+			/** 评价区 Tab 展示顺序:精选留言 → 栏目介绍 → 评价得积分(仅渲染有数据的项) */
+			ratingTabList() {
+				const tabs = []
+				if (this.showTabFeaturedComments) {
+					tabs.push({
+						key: 'featured',
+						index: 0,
+						label: '精选留言',
+						icon: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/pl.png',
+						iconActive: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/pl-sel.png'
+					})
+				}
+				if (this.showTabColumnIntro) {
+					tabs.push({
+						key: 'column',
+						index: 1,
+						label: '栏目介绍',
+						icon: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/des.png',
+						iconActive: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/des-sel.png'
+					})
+				}
+				if (this.showTabReviewPoints) {
+					tabs.push({
+						key: 'review',
+						index: 2,
+						label: '评价得积分',
+						icon: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/sel.png',
+						iconActive: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/zan-sel.png'
+					})
+				}
+				return tabs
 			},
 			hasVisibleRatingTab() {
 				return this.visibleRatingTabCount > 0
@@ -1219,23 +1210,26 @@
 				if (!this.fakeMarqueeEligible || !this._fakeMarqueeStarted) return
 				this.syncTripleMarqueeImmediate()
 			},
+			/** 精选留言 Tab 出现后默认高亮 */
+			showTabFeaturedComments(now, was) {
+				if (now && was !== true) {
+					this._selectDefaultRatingTab()
+				}
+			},
 			/**
-			 * 题库(评价得积分)晚于课程信息返回时,_syncRatingTabIndex 会先落到栏目介绍 tab=1;
-			 * 待评价 Tab 出现后 tabs[1] 仍为 true 会提前 return,无法回到 tab=0。此处在「评价 Tab 变为可见」时强制回到默认首 Tab。
+			 * 评价 Tab 晚于栏目介绍返回时,_syncRatingTabIndex 可能暂落在 tab=1;
+			 * 若有精选留言则仍默认高亮 tab=0,否则仅校正到当前第一个可见 Tab。
 			 */
 			showTabReviewPoints(now, was) {
 				if (now && was !== true) {
-					this.tabIndex = 0
+					this._selectDefaultRatingTab()
 				}
 			},
-			/** 三个 Tab 都就绪时默认「评价得积分」;两个 Tab 且含评价时同样默认 tab=0 */
+			/** Tab 数量增加且含精选留言时,默认高亮精选留言 */
 			visibleRatingTabCount(n, o) {
-				if (n === 2 && this.showTabReviewPoints) {
-					this.tabIndex = 0
-				}
-				if (n !== 3) return
-				if (o != null && o >= 3) return
-				this.tabIndex = 0
+				if (n <= 0 || !this.showTabFeaturedComments) return
+				if (o != null && n <= o) return
+				this._selectDefaultRatingTab()
 			},
 			// tabIndex(n) {
 			// 	if (n !== 2) {
@@ -1471,27 +1465,36 @@
 			onFeaturedCommentComposerInputBlur() {
 				this.featuredCommentInputFocused = false
 			},
+			/** 有精选留言时默认 tabIndex=0,否则落到第一个可见 Tab */
+			_selectDefaultRatingTab() {
+				if (this.showTabFeaturedComments) {
+					this.tabIndex = 0
+					this.loadFeaturedComments()
+					return
+				}
+				this._syncRatingTabIndex()
+			},
 			_syncRatingTabIndex() {
 				const tabs = [
-					this.showTabReviewPoints,
+					this.showTabFeaturedComments,
 					this.showTabColumnIntro,
-					this.showTabFeaturedComments
+					this.showTabReviewPoints
 				]
 				if (!tabs.some(Boolean)) return
 				if (tabs[this.tabIndex]) return
 				const idx = tabs.findIndex(Boolean)
 				if (idx < 0) return
 				this.tabIndex = idx
-				if (idx === 2) {
+				if (idx === 0) {
 					this.loadFeaturedComments()
 				}
 			},
 			selecTab(idx){
-				if (idx === 0 && !this.showTabReviewPoints) return
+				if (idx === 0 && !this.showTabFeaturedComments) return
 				if (idx === 1 && !this.showTabColumnIntro) return
-				if (idx === 2 && !this.showTabFeaturedComments) return
+				if (idx === 2 && !this.showTabReviewPoints) return
 				this.tabIndex = idx
-				if (idx === 2) {
+				if (idx === 0) {
 					this.loadFeaturedComments()
 				}
 			},
@@ -1618,7 +1621,7 @@
 				if (pageLen === 0) this.featuredCommentHasMore = false
 			},
 			onFeaturedCommentScrollToLower() {
-				if (this.tabIndex !== 2 || !this.showTabFeaturedComments) return
+				if (this.tabIndex !== 0 || !this.showTabFeaturedComments) return
 				this.loadFeaturedComments({ append: true })
 			},
 			loadFeaturedComments(opts) {
@@ -2035,7 +2038,7 @@
 				if (!this.isShu) return
 				if (!(this.visibleRatingTabCount === 1 && this.showTabFeaturedComments)) return
 				this.isShu = false
-				this.tabIndex = 2
+				this.tabIndex = 0
 			},
 			/** 避免选图/选视频从原生页返回时 onShow 重复拉课程详情;若未触发 onShow 则在 complete 后兜底清除标志 */
 			_armFeaturedMediaPickerOnShowSuppress() {
@@ -2643,7 +2646,7 @@
 			openList(){
 				const wasShu = this.isShu
 				this.isShu = !this.isShu
-				if (wasShu && this.showTabReviewPoints) this.tabIndex = 0
+				if (wasShu && this.showTabFeaturedComments) this.tabIndex = 0
 			},
 			// 倒计时格式化函数
 			formatCountdown(seconds) {

+ 186 - 22
pages_index/course.vue

@@ -52,28 +52,45 @@
 			<!-- 分类标签区 -->
 			
 
-			<!-- 课程网格 -->
-			<view class="course-grid">
-				<view
-					v-for="(course, idx) in courseList"
-					:key="idx"
-					class="course-card"
-					@click="onCourseClick(course)"
-				>
-					<view class="card-cover">
-						<image :src="course.cover" mode="aspectFill" class="cover-img"></image>
+			<!-- 课程骨架屏 -->
+			<view class="course-skeleton" v-if="courseSectionLoading && courseList.length === 0">
+				<view class="sk-course-card" v-for="i in 3" :key="'sk-course-' + i">
+					<view class="sk-thumb"></view>
+					<view class="sk-info">
+						<view class="sk-line sk-line-lg"></view>
+						<view class="sk-line sk-line-md"></view>
+						<view class="sk-foot">
+							<view class="sk-line sk-line-sm"></view>
+							<view class="sk-btn"></view>
+						</view>
 					</view>
-					<view class="course-info">
-						<text class="card-title">{{ course.title }}</text>
-						<view class="x-end" style="justify-content: space-between;">
-							<view class="course-meta">
-								<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
-								<text class="meta-count">{{ courseViewsDisplay(course.views) }}</text>
+				</view>
+			</view>
+			<view class="course-grid-wrap" v-else>
+				<view class="course-grid" :class="{ 'list-dimmed': courseRefreshing }">
+					<view
+						v-for="(course, idx) in courseList"
+						:key="course.courseId || idx"
+						class="course-card"
+						@click="onCourseClick(course)"
+					>
+						<view class="card-cover">
+							<image v-if="course.cover" :src="course.cover" mode="aspectFill" class="cover-img"></image>
+						</view>
+						<view class="course-info">
+							<text class="card-title">{{ course.title }}</text>
+							<view class="x-end" style="justify-content: space-between;">
+								<view class="course-meta">
+									<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
+									<text class="meta-count">{{ courseViewsDisplay(course.views) }}</text>
+								</view>
+								<view class="btn-watch" @click.stop="onCourseClick(course)">立即观看</view>
 							</view>
-							<view class="btn-watch" @click.stop="onCourseClick(course)">立即观看</view>
 						</view>
 					</view>
-					
+				</view>
+				<view class="refresh-mask" v-if="courseRefreshing">
+					<view class="refresh-spinner"></view>
 				</view>
 			</view>
 			<view v-if="courseList.length > 0" class="load-tip">
@@ -92,8 +109,22 @@
 			lower-threshold="120"
 			@scrolltolower="onRecentScrollToLower"
 		>
+			<!-- 最近学习骨架屏 -->
+			<view class="recent-skeleton" v-if="recentSectionLoading && recentList.length === 0">
+				<view class="sk-recent-group" v-for="g in 2" :key="'sk-group-' + g">
+					<view class="sk-date"></view>
+					<view class="sk-recent-card" v-for="i in 2" :key="'sk-recent-' + g + '-' + i">
+						<view class="sk-thumb"></view>
+						<view class="sk-info">
+							<view class="sk-line sk-line-lg"></view>
+							<view class="sk-line sk-line-md"></view>
+							<view class="sk-btn"></view>
+						</view>
+					</view>
+				</view>
+			</view>
 			<!-- 有学习记录:按日期分组列表 -->
-			<template v-if="recentList.length > 0">
+			<template v-else-if="recentList.length > 0">
 				<view v-for="(group, gIdx) in recentList" :key="gIdx" class="recent-group">
 					<view class="group-date">
 						<image class="date-icon" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/time.png"></image>
@@ -124,7 +155,7 @@
 			</template>
 
 			<!-- 无学习记录:空态 + 为您精选 -->
-			<template v-else>
+			<template v-else-if="!recentSectionLoading">
 				<view class="empty-state">
 					<!-- <view class="empty-icon"></view> -->
 					<image class="empty-icon" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/nodata.png"></image>
@@ -178,11 +209,15 @@ export default {
 			coursePageNum: 1,
 			coursePageSize: 10,
 			courseLoading: false,
+			courseRefreshing: false,
+			courseSectionLoading: true,
 			courseHasMore: true,
 			courseQuerySubCateId: null,
+			courseRequestId: 0,
 			recentPageNum: 1,
 			recentPageSize: 10,
 			recentLoading: false,
+			recentSectionLoading: true,
 			recentHasMore: true,
 			recentList: [
 				// {
@@ -289,14 +324,20 @@ export default {
 		},
 		async getCourseList(options = {}) {
 			const loadMore = !!options.loadMore
-			if (this.courseLoading) return
+			if (this.courseLoading && loadMore) return
 			if (loadMore && !this.courseHasMore) return
+			const requestId = ++this.courseRequestId
 			if (!loadMore) {
 				this.coursePageNum = 1
 				this.courseHasMore = true
 				if (options.subCateId !== undefined) {
 					this.courseQuerySubCateId = options.subCateId
 				}
+				if (this.courseList.length > 0) {
+					this.courseRefreshing = true
+				} else {
+					this.courseSectionLoading = true
+				}
 			}
 			const currentPageNum = this.coursePageNum
 			const params = {
@@ -310,6 +351,7 @@ export default {
 			this.courseLoading = true
 			try {
 				const res = await listPublicCourse(params)
+				if (requestId !== this.courseRequestId) return
 				const list = (res && res.data && res.data.list) || []
 				const mapped = list.map(item => ({
 					courseId: item.courseId,
@@ -330,12 +372,16 @@ export default {
 					this.courseHasMore = false
 				}
 			} catch (e) {
+				if (requestId !== this.courseRequestId) return
 				if (!loadMore) {
 					this.courseList = []
 				}
 				this.courseHasMore = false
 			} finally {
+				if (requestId !== this.courseRequestId) return
 				this.courseLoading = false
+				this.courseRefreshing = false
+				if (!loadMore) this.courseSectionLoading = false
 			}
 		},
 		onCourseScrollToLower() {
@@ -344,11 +390,14 @@ export default {
 		},
 		async getCourseStudyList(options = {}) {
 			const loadMore = !!options.loadMore
-			if (this.recentLoading) return
+			if (this.recentLoading && !loadMore) return
 			if (loadMore && !this.recentHasMore) return
 			if (!loadMore) {
 				this.recentPageNum = 1
 				this.recentHasMore = true
+				if (this.recentList.length === 0) {
+					this.recentSectionLoading = true
+				}
 			}
 			const currentPageNum = this.recentPageNum
 			const params = {
@@ -382,6 +431,7 @@ export default {
 				this.recentHasMore = false
 			} finally {
 				this.recentLoading = false
+				if (!loadMore) this.recentSectionLoading = false
 			}
 		},
 		onRecentScrollToLower() {
@@ -390,6 +440,7 @@ export default {
 			this.getCourseStudyList({ loadMore: true })
 		},
 		onSelectCategory(idx) {
+			if (idx === this.categoryIndex || this.courseRefreshing) return
 			this.categoryIndex = idx
 			const selected = this.categoryRows[idx]
 			const subCateId = selected && selected.cateId ? selected.cateId : null
@@ -775,4 +826,117 @@ export default {
 	font-size: 28rpx;
 	color: #999;
 }
+
+@mixin sk-shimmer {
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-line {
+	height: 28rpx;
+	border-radius: 6rpx;
+	@include sk-shimmer;
+}
+.sk-line-lg { width: 90%; }
+.sk-line-md { width: 70%; margin-top: 16rpx; }
+.sk-line-sm { width: 40%; }
+
+@keyframes skeleton-shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
+
+/* 精品课程骨架 */
+.course-skeleton {
+	padding: 24rpx;
+	display: flex;
+	flex-direction: column;
+	gap: 24rpx;
+}
+.sk-course-card,
+.sk-recent-card {
+	display: flex;
+	background: #fff;
+	border-radius: 20rpx;
+	padding: 20rpx;
+}
+.sk-thumb {
+	width: 296rpx;
+	height: 222rpx;
+	border-radius: 20rpx;
+	flex-shrink: 0;
+	@include sk-shimmer;
+}
+.sk-info {
+	flex: 1;
+	padding-left: 24rpx;
+	min-width: 0;
+}
+.sk-foot {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	margin-top: 40rpx;
+}
+.sk-btn {
+	width: 168rpx;
+	height: 64rpx;
+	border-radius: 32rpx;
+	@include sk-shimmer;
+}
+
+.course-grid-wrap {
+	position: relative;
+}
+.course-grid.list-dimmed {
+	opacity: 0.55;
+	transition: opacity 0.2s ease;
+}
+.refresh-mask {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	pointer-events: none;
+}
+.refresh-spinner {
+	width: 48rpx;
+	height: 48rpx;
+	border: 4rpx solid #e8e8e8;
+	border-top-color: #FF233C;
+	border-radius: 50%;
+	animation: spin 0.8s linear infinite;
+}
+@keyframes spin {
+	to { transform: rotate(360deg); }
+}
+
+/* 最近学习骨架 */
+.recent-skeleton {
+	padding: 24rpx 24rpx 0;
+}
+.sk-recent-group {
+	margin-bottom: 24rpx;
+}
+.sk-date {
+	width: 240rpx;
+	height: 40rpx;
+	margin-bottom: 24rpx;
+	border-radius: 8rpx;
+	@include sk-shimmer;
+}
+.sk-recent-card {
+	margin-bottom: 24rpx;
+}
+.sk-recent-card:last-child {
+	margin-bottom: 0;
+}
+.sk-recent-card .sk-btn {
+	margin-top: 40rpx;
+}
 </style>

+ 172 - 27
pages_index/courseSearch.vue

@@ -39,52 +39,84 @@
 			lower-threshold="120"
 			@scrolltolower="onCourseScrollToLower"
 		>
-			<!-- 有学习记录:按日期分组列表 -->
+			<!-- 搜索结果 -->
 			<template v-if="isSearch">
-				<view class="course-grid">
-					<view
-						v-for="(course, idx) in courseList"
-						:key="idx"
-						class="course-card"
-						@click="onCourseClick(course)"
-					>
-						<view class="card-cover">
-							<image :src="course.cover" mode="aspectFill" class="cover-img"></image>
+				<view class="course-skeleton" v-if="courseSectionLoading && courseList.length === 0">
+					<view class="sk-course-card" v-for="i in 3" :key="'sk-search-' + i">
+						<view class="sk-thumb"></view>
+						<view class="sk-info">
+							<view class="sk-line sk-line-lg"></view>
+							<view class="sk-line sk-line-md"></view>
+							<view class="sk-foot">
+								<view class="sk-line sk-line-sm"></view>
+								<view class="sk-btn"></view>
+							</view>
 						</view>
-						<view class="course-info">
-							<text class="card-title">{{ course.title }}</text>
-							<view class="x-end" style="justify-content: space-between;">
-								<view class="course-meta">
-									<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
-									<text class="meta-count">{{ courseViewsDisplay(course.views)}}</text>
+					</view>
+				</view>
+				<view class="course-grid-wrap" v-else-if="courseList.length > 0">
+					<view class="course-grid" :class="{ 'list-dimmed': courseRefreshing }">
+						<view
+							v-for="(course, idx) in courseList"
+							:key="course.courseId || idx"
+							class="course-card"
+							@click="onCourseClick(course)"
+						>
+							<view class="card-cover">
+								<image v-if="course.cover" :src="course.cover" mode="aspectFill" class="cover-img"></image>
+							</view>
+							<view class="course-info">
+								<text class="card-title">{{ course.title }}</text>
+								<view class="x-end" style="justify-content: space-between;">
+									<view class="course-meta">
+										<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/renshu.png"></image>
+										<text class="meta-count">{{ courseViewsDisplay(course.views)}}</text>
+									</view>
+									<view class="btn-watch" @click.stop="onCourseClick(course)">立即观看</view>
 								</view>
-								<view class="btn-watch" @click.stop="onCourseClick(course)">立即观看</view>
 							</view>
 						</view>
-						
+					</view>
+					<view class="refresh-mask" v-if="courseRefreshing">
+						<view class="refresh-spinner"></view>
 					</view>
 				</view>
 				<view v-if="courseList.length > 0" class="load-tip">
-					<text v-if="courseLoading">加载中...</text>
+					<text v-if="courseLoading && !courseRefreshing">加载中...</text>
 					<text v-else-if="!courseHasMore">没有更多了</text>
 				</view>
 			</template>
          
-			<!-- 无学习记录:空态 + 为您精选 -->
+			<!-- 大家都在学习 -->
 			<template v-else>
-				<view class="recommend-section">
+				<view class="recommend-skeleton-wrap" v-if="recommendLoading && recommendList.length === 0">
+					<text class="recommend-title">大家都在学习</text>
+					<view class="course-skeleton" style="padding: 0;">
+						<view class="sk-course-card" v-for="i in 3" :key="'sk-rec-' + i">
+							<view class="sk-thumb"></view>
+							<view class="sk-info">
+								<view class="sk-line sk-line-lg"></view>
+								<view class="sk-line sk-line-md"></view>
+								<view class="sk-foot">
+									<view class="sk-line sk-line-sm"></view>
+									<view class="sk-btn"></view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="recommend-section" v-else>
 					<text class="recommend-title">大家都在学习</text>
 					<view class="course-grid" style="padding: 0;">
 						<view
 							v-for="(course, idx) in recommendList"
-							:key="idx"
+							:key="course.courseId || idx"
 							class="course-card"
 							@click="onCourseClick(course)"
 						>
 							<view class="card-cover">
-								<image :src="course.cover || '/static/logo.jpg'" mode="aspectFill" class="cover-img"></image>
+								<image v-if="course.cover" :src="course.cover" mode="aspectFill" class="cover-img"></image>
 							</view>
-							
 							<view class="card-footer">
 								<text class="card-title">{{ course.title }}</text>
 								<view class="x-end" style="justify-content: space-between;">
@@ -99,7 +131,7 @@
 					</view>
 				</view>
 			</template>
-			<template v-if="keyword&&courseList.length==0">
+			<template v-if="isSearch && keyword && courseList.length === 0 && !courseSectionLoading && !courseLoading">
 				<view class="empty-state">
 					<!-- <view class="empty-icon"></view> -->
 					<image class="empty-icon" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/nodata.png"></image>
@@ -131,8 +163,12 @@ export default {
 			coursePageNum: 1,
 			coursePageSize: 10,
 			courseLoading: false,
+			courseRefreshing: false,
+			courseSectionLoading: false,
 			courseHasMore: true,
 			courseSearchKeyword: '',
+			courseRequestId: 0,
+			recommendLoading: true,
 			recentList: [
 				// {
 				// 	date: '2026-02-08',
@@ -198,18 +234,26 @@ export default {
 		        },
 		async getCourseList(options = {}) {
 			const loadMore = !!options.loadMore
-			if (this.courseLoading) return
+			if (this.courseLoading && loadMore) return
 			if (loadMore && !this.courseHasMore) return
+			const requestId = ++this.courseRequestId
 			if (!loadMore) {
 				this.coursePageNum = 1
 				this.courseHasMore = true
 				if (options.keyword !== undefined) {
 					this.courseSearchKeyword = (options.keyword || '').trim()
 				}
+				if (this.isSearch) {
+					if (this.courseList.length > 0) {
+						this.courseRefreshing = true
+					} else {
+						this.courseSectionLoading = true
+					}
+				}
 			}
 			const currentPageNum = this.coursePageNum
 			const params = {
-				yxxTag:3,
+				yxxTag: 3,
 				pageNum: currentPageNum,
 				pageSize: this.coursePageSize
 			}
@@ -219,6 +263,7 @@ export default {
 			this.courseLoading = true
 			try {
 				const res = await listPublicCourse(params)
+				if (requestId !== this.courseRequestId) return
 				const list = (res && res.data && res.data.list) || []
 				const mapped = list.map(item => ({
 					courseId: item.courseId,
@@ -239,12 +284,16 @@ export default {
 					this.courseHasMore = false
 				}
 			} catch (e) {
+				if (requestId !== this.courseRequestId) return
 				if (!loadMore) {
 					this.courseList = []
 				}
 				this.courseHasMore = false
 			} finally {
+				if (requestId !== this.courseRequestId) return
 				this.courseLoading = false
+				this.courseRefreshing = false
+				if (!loadMore && this.isSearch) this.courseSectionLoading = false
 			}
 		},
 		onCourseScrollToLower() {
@@ -252,6 +301,7 @@ export default {
 			this.getCourseList({ loadMore: true })
 		},
 		async getNewCourseList() {
+			if (!this.recommendList.length) this.recommendLoading = true
 			try {
 				const params = { pageNum: 1, pageSize: 6 }
 				const res = await listPublicCourse(params)
@@ -264,6 +314,8 @@ export default {
 				}))
 			} catch (e) {
 				this.recommendList = []
+			} finally {
+				this.recommendLoading = false
 			}
 		},
 		goSearch() {
@@ -274,6 +326,7 @@ export default {
 				})
 				return
 			}
+			if (this.courseRefreshing || this.courseSectionLoading) return
 			this.isSearch = true
 			this.getCourseList({ keyword: this.keyword })
 		},
@@ -693,4 +746,96 @@ export default {
 	font-size: 28rpx;
 	color: #999;
 }
+
+@mixin sk-shimmer {
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+
+.sk-line {
+	height: 28rpx;
+	border-radius: 6rpx;
+	@include sk-shimmer;
+}
+.sk-line-lg { width: 90%; }
+.sk-line-md { width: 70%; margin-top: 16rpx; }
+.sk-line-sm { width: 40%; }
+
+@keyframes skeleton-shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
+
+.course-skeleton {
+	padding: 24rpx;
+	display: flex;
+	flex-direction: column;
+	gap: 24rpx;
+}
+.sk-course-card {
+	display: flex;
+	background: #fff;
+	border-radius: 20rpx;
+	padding: 20rpx;
+}
+.sk-thumb {
+	width: 296rpx;
+	height: 222rpx;
+	border-radius: 20rpx;
+	flex-shrink: 0;
+	@include sk-shimmer;
+}
+.sk-info {
+	flex: 1;
+	padding-left: 24rpx;
+	min-width: 0;
+}
+.sk-foot {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	margin-top: 40rpx;
+}
+.sk-btn {
+	width: 168rpx;
+	height: 64rpx;
+	border-radius: 32rpx;
+	@include sk-shimmer;
+}
+
+.recommend-skeleton-wrap {
+	margin-top: 300rpx;
+	padding: 0 24rpx 32rpx;
+}
+
+.course-grid-wrap {
+	position: relative;
+}
+.course-grid.list-dimmed {
+	opacity: 0.55;
+	transition: opacity 0.2s ease;
+}
+.refresh-mask {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	pointer-events: none;
+}
+.refresh-spinner {
+	width: 48rpx;
+	height: 48rpx;
+	border: 4rpx solid #e8e8e8;
+	border-top-color: #FF233C;
+	border-radius: 50%;
+	animation: spin 0.8s linear infinite;
+}
+@keyframes spin {
+	to { transform: rotate(360deg); }
+}
 </style>

+ 175 - 19
pages_index/video.vue

@@ -52,28 +52,42 @@
 			<!-- 分类标签区 -->
 			
 
-			<!-- 课程网格 -->
-			<view class="course-grid">
-				<view
-					v-for="(course, idx) in courseList"
-					:key="idx"
-					class="course-card"
-					@click="onCourseClick(course)"
-				>
-					<view class="card-cover">
-						<image :src="course.imgUrl" mode="aspectFill" class="cover-img"></image>
+			<!-- 视频列表骨架屏 -->
+			<view class="course-skeleton" v-if="courseSectionLoading && courseList.length === 0">
+				<view class="sk-course-card" v-for="i in 3" :key="'sk-course-' + i">
+					<view class="sk-thumb"></view>
+					<view class="sk-info">
+						<view class="sk-line sk-line-lg"></view>
+						<view class="sk-line sk-line-md"></view>
+						<view class="sk-line sk-line-md"></view>
+						<view class="sk-btn"></view>
 					</view>
-					<view class="course-info">
-						<text class="card-title">{{ course.courseName }}</text>
-					
+				</view>
+			</view>
+			<view class="course-grid-wrap" v-else>
+				<view class="course-grid" :class="{ 'list-dimmed': courseRefreshing }">
+					<view
+						v-for="(course, idx) in courseList"
+						:key="course.courseId"
+						class="course-card"
+						@click="onCourseClick(course)"
+					>
+						<view class="card-cover">
+							<image v-if="course.imgUrl" :src="course.imgUrl" mode="aspectFill" class="cover-img"></image>
+						</view>
+						<view class="course-info">
+							<text class="card-title">{{ course.courseName }}</text>
 							<view class="course-meta">
-							{{ course.courseDesc||'-' }}
+								{{ course.courseDesc || '-' }}
 							</view>
 							<view class="y-end">
 								<view class="btn-watch" @click.stop="onCourseClick(course)">立即观看</view>
 							</view>
+						</view>
 					</view>
-					
+				</view>
+				<view class="refresh-mask" v-if="courseRefreshing">
+					<view class="refresh-spinner"></view>
 				</view>
 			</view>
 			<view v-if="courseList.length > 0" class="load-tip">
@@ -92,8 +106,22 @@
 			lower-threshold="120"
 			@scrolltolower="onRecentScrollToLower"
 		>
+			<!-- 最近学习骨架屏 -->
+			<view class="recent-skeleton" v-if="recentSectionLoading && recentList.length === 0">
+				<view class="sk-recent-group" v-for="g in 2" :key="'sk-group-' + g">
+					<view class="sk-date"></view>
+					<view class="sk-recent-card" v-for="i in 2" :key="'sk-recent-' + g + '-' + i">
+						<view class="sk-thumb"></view>
+						<view class="sk-info">
+							<view class="sk-line sk-line-lg"></view>
+							<view class="sk-line sk-line-md"></view>
+							<view class="sk-btn"></view>
+						</view>
+					</view>
+				</view>
+			</view>
 			<!-- 有学习记录:按日期分组列表 -->
-			<template v-if="recentList.length > 0">
+			<template v-else-if="recentList.length > 0">
 				<view v-for="(group, gIdx) in recentList" :key="gIdx" class="recent-group">
 					<view class="group-date">
 						<image class="date-icon" src="@/static/images/new/time.png"></image>
@@ -126,7 +154,7 @@
 			</template>
 
 			<!-- 无学习记录:空态 + 为您精选 -->
-			<template v-else>
+			<template v-else-if="!recentSectionLoading">
 				<view class="empty-state">
 					<!-- <view class="empty-icon"></view> -->
 					<image class="empty-icon" src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/class/nodata.png"></image>
@@ -181,11 +209,15 @@ export default {
 			coursePageNum: 1,
 			coursePageSize: 10,
 			courseLoading: false,
+			courseRefreshing: false,
+			courseSectionLoading: true,
 			courseHasMore: true,
 			courseQuerySubCateId: null,
+			courseRequestId: 0,
 			recentPageNum: 1,
 			recentPageSize: 10,
 			recentLoading: false,
+			recentSectionLoading: true,
 			recentHasMore: true,
 			recentList: [
 				// {
@@ -216,6 +248,7 @@ export default {
 	},
 	methods: {
 		onSelectCategory(cat, idx) {
+			if (idx === this.categoryIndex || this.courseRefreshing) return
 			this.categoryIndex = idx
 			this.activeCateId = cat.cateId
 			const subCateId = cat.cateId === 0 || cat.cateId == null ? null : cat.cateId
@@ -286,11 +319,14 @@ export default {
 		},
 		async getCourseStudyList(options = {}) {
 			const loadMore = !!options.loadMore
-			if (this.recentLoading) return
+			if (this.recentLoading && !loadMore) return
 			if (loadMore && !this.recentHasMore) return
 			if (!loadMore) {
 				this.recentPageNum = 1
 				this.recentHasMore = true
+				if (this.recentList.length === 0) {
+					this.recentSectionLoading = true
+				}
 			}
 			const currentPageNum = this.recentPageNum
 			const params = {
@@ -324,6 +360,7 @@ export default {
 				this.recentHasMore = false
 			} finally {
 				this.recentLoading = false
+				if (!loadMore) this.recentSectionLoading = false
 			}
 		},
 		onRecentScrollToLower() {
@@ -333,14 +370,20 @@ export default {
 		},
 		async getPublicVideoList(options = {}) {
 			const loadMore = !!options.loadMore
-			if (this.courseLoading) return
+			if (this.courseLoading && loadMore) return
 			if (loadMore && !this.courseHasMore) return
+			const requestId = ++this.courseRequestId
 			if (!loadMore) {
 				this.coursePageNum = 1
 				this.courseHasMore = true
 				if (options.subCateId !== undefined) {
 					this.courseQuerySubCateId = options.subCateId
 				}
+				if (this.courseList.length > 0) {
+					this.courseRefreshing = true
+				} else {
+					this.courseSectionLoading = true
+				}
 			}
 			const currentPageNum = this.coursePageNum
 			const params = {
@@ -354,6 +397,7 @@ export default {
 			this.courseLoading = true
 			try {
 				const res = await listPublicCourse(params)
+				if (requestId !== this.courseRequestId) return
 				const list = (res && res.data && res.data.list) || []
 				this.courseList = loadMore ? this.courseList.concat(list) : list
 				const total = Number(res.data && res.data.total)
@@ -368,12 +412,16 @@ export default {
 					this.courseHasMore = false
 				}
 			} catch (e) {
+				if (requestId !== this.courseRequestId) return
 				if (!loadMore) {
 					this.courseList = []
 				}
 				this.courseHasMore = false
 			} finally {
+				if (requestId !== this.courseRequestId) return
 				this.courseLoading = false
+				this.courseRefreshing = false
+				if (!loadMore) this.courseSectionLoading = false
 			}
 		},
 		onCourseScrollToLower() {
@@ -779,4 +827,112 @@ export default {
 	font-size: 28rpx;
 	color: #999;
 }
+
+.sk-line {
+	height: 28rpx;
+	border-radius: 6rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+.sk-line-lg { width: 90%; }
+.sk-line-md { width: 70%; margin-top: 16rpx; }
+.sk-line-sm { width: 40%; }
+
+@keyframes skeleton-shimmer {
+	0% { background-position: 100% 0; }
+	100% { background-position: -100% 0; }
+}
+
+.course-skeleton {
+	padding: 24rpx;
+	display: flex;
+	flex-direction: column;
+	gap: 24rpx;
+}
+.sk-course-card,
+.sk-recent-card {
+	display: flex;
+	background: #fff;
+	border-radius: 20rpx;
+	padding: 20rpx;
+}
+.sk-thumb {
+	width: 296rpx;
+	height: 222rpx;
+	border-radius: 20rpx;
+	flex-shrink: 0;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+.sk-info {
+	flex: 1;
+	padding-left: 24rpx;
+	min-width: 0;
+}
+.sk-btn {
+	width: 168rpx;
+	height: 64rpx;
+	margin-top: 24rpx;
+	border-radius: 32rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+
+.course-grid-wrap {
+	position: relative;
+}
+.course-grid.list-dimmed {
+	opacity: 0.55;
+	transition: opacity 0.2s ease;
+}
+.refresh-mask {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	pointer-events: none;
+}
+.refresh-spinner {
+	width: 48rpx;
+	height: 48rpx;
+	border: 4rpx solid #e8e8e8;
+	border-top-color: #FF233C;
+	border-radius: 50%;
+	animation: spin 0.8s linear infinite;
+}
+@keyframes spin {
+	to { transform: rotate(360deg); }
+}
+
+.recent-skeleton {
+	padding: 24rpx 24rpx 0;
+}
+.sk-recent-group {
+	margin-bottom: 24rpx;
+}
+.sk-date {
+	width: 240rpx;
+	height: 40rpx;
+	margin-bottom: 24rpx;
+	border-radius: 8rpx;
+	background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
+	background-size: 200% 100%;
+	animation: skeleton-shimmer 1.2s ease-in-out infinite;
+}
+.sk-recent-card {
+	margin-bottom: 24rpx;
+}
+.sk-recent-card:last-child {
+	margin-bottom: 0;
+}
+.sk-recent-card .sk-btn {
+	margin-top: 40rpx;
+}
 </style>

+ 14 - 11
pages_user/user/addAddress.vue

@@ -78,17 +78,20 @@
 				})
 				this.id=option.id;
 				this.getAddressById();
+				 uni.hideShareMenu({
+				      menus: ["shareAppMessage", "shareTimeline"]
+				    })
 			}
 			else{
 				uni.setNavigationBarTitle({
 				  title:"新增收货地址"
 				})
+				uni.showShareMenu({
+					withShareTicket: true,
+					//小程序的原生菜单中显示分享按钮,才能够让发送给朋友与分享到朋友圈两个按钮可以点击
+					menus: ["shareAppMessage", "shareTimeline"] //不设置默认发送给朋友
+				})
 			}
-			uni.showShareMenu({
-				withShareTicket: true,
-				//小程序的原生菜单中显示分享按钮,才能够让发送给朋友与分享到朋友圈两个按钮可以点击
-				menus: ["shareAppMessage", "shareTimeline"] //不设置默认发送给朋友
-			})
 			this.getCitys()
 		},
 		//发送给朋友
@@ -96,9 +99,9 @@
 			if (this.utils.isLogin()) {
 				var user = JSON.parse(uni.getStorageSync('userInfo'))
 				return {
-					title: '邀请填写地址,下单更方便',
-					path: '/pages_user/user/addAddress?id='+this.id +'&type='+this.type,
-					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/share.png' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
+					title: '邀请填写地址,下单更方便',
+					path: '/pages_user/user/addAddress?type='+this.type,
+					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/address-share.jpg' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
 				}
 			}
 		},
@@ -107,9 +110,9 @@
 			if (this.utils.isLogin()) {
 				var user = JSON.parse(uni.getStorageSync('userInfo'))
 				return {
-					title: '邀请填写地址,下单更方便',
-					query: 'id='+this.id+'&type='+this.type, //页面参数
-					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/share.png' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
+					title: '邀请填写地址,下单更方便',
+					query: 'type='+this.type, //页面参数
+					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/address-share.jpg' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
 				}
 			}
 		

+ 4 - 5
pages_user/user/address.vue

@@ -59,9 +59,9 @@
 			if (this.utils.isLogin()) {
 				var user = JSON.parse(uni.getStorageSync('userInfo'))
 				return {
-					title: '邀请填写地址,下单更方便',
+					title: '邀请填写地址,下单更方便',
 					path: '/pages_user/user/address',
-					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/share.png' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
+					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/address-share.jpg' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
 				}
 			}
 		},
@@ -70,8 +70,8 @@
 			if (this.utils.isLogin()) {
 				var user = JSON.parse(uni.getStorageSync('userInfo'))
 				return {
-					title: '邀请填写地址,下单更方便',
-					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/vip/share.png' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
+					title: '邀请填写地址,下单更方便',
+					imageUrl: 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/app/address-share.jpg' //分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径.支持PNG及JPG。显示图片长宽比是 5:4
 				}
 			}
 		
@@ -88,7 +88,6 @@
 			  if (pages.length >= 2) {
 			    // 获取上一页
 			    const prevPage = pages[pages.length - 2];
-			    
 			    // 判断上一页的路径 是否是 订单页
 			    if (prevPage.route === 'pages/shopping/confirmOrder') { // 👈 改成你真实的订单页路径
 			      uni.$emit('updateAddress',item);

+ 0 - 18
static/assets/audio-calling.svg

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>音频通话</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-20.000000, -677.000000)">
-            <g id="编组-20" transform="translate(20.000000, 677.000000)">
-                <g id="编组-15">
-                    <rect id="矩形备份-7" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <g id="编组-5" transform="translate(22.000000, 17.000000)" fill="#444444">
-                        <path d="M19,15 C19.5522847,15 20,15.4477153 20,16 C20,21.1849719 16.0538953,25.4482817 11.0010997,25.9505155 L11,29 L13,29 C13.5522847,29 14,29.4477153 14,30 C14,30.5522847 13.5522847,31 13,31 L7,31 C6.44771525,31 6,30.5522847 6,30 C6,29.4477153 6.44771525,29 7,29 L9,29 L8.99989907,25.9506147 C3.94662476,25.4488436 0,21.1853136 0,16 C0,15.4477153 0.44771525,15 1,15 C1.55228475,15 2,15.4477153 2,16 C2,20.418278 5.581722,24 10,24 C14.418278,24 18,20.418278 18,16 C18,15.4477153 18.4477153,15 19,15 Z M10,0 C13.3137085,-6.08718376e-16 16,2.6862915 16,6 L16,16 C16,19.3137085 13.3137085,22 10,22 C6.6862915,22 4,19.3137085 4,16 L4,6 C4,2.6862915 6.6862915,6.08718376e-16 10,0 Z" id="形状结合"></path>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 16
static/assets/audio.svg

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>语音</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服" transform="translate(-8.000000, -737.000000)">
-            <g id="编组-3" transform="translate(8.000000, 737.000000)">
-                <g id="编组-13备份">
-                    <circle id="椭圆形备份-25" stroke="#000000" stroke-width="1.5" cx="14" cy="14" r="13.25"></circle>
-                    <path d="M16.7254202,6.49944534 C18.7422695,8.41282782 20,11.1185808 20,14.1180925 C20,17.0259755 18.8179377,19.6577731 16.908112,21.5591864 L15.8511317,20.4948571 C17.487349,18.8651761 18.5,16.6098929 18.5,14.1180925 C18.5,11.5480628 17.4227672,9.22963431 15.6951946,7.58969985 L16.7254202,6.49944534 Z M13.6344329,9.76991965 C14.7836334,10.8629724 15.5,12.4068742 15.5,14.1180925 C15.5,15.7789367 14.8251887,17.2821704 13.7347892,18.3685703 L12.6769909,17.3050548 C13.4942624,16.4903245 14,15.3632691 14,14.1180925 C14,12.836496 13.4642454,11.6800317 12.6044838,10.8604471 L13.6344329,9.76991965 Z M9.5,12.6180925 C10.3284271,12.6180925 11,13.2896653 11,14.1180925 C11,14.9465196 10.3284271,15.6180925 9.5,15.6180925 C8.67157288,15.6180925 8,14.9465196 8,14.1180925 C8,13.2896653 8.67157288,12.6180925 9.5,12.6180925 Z" id="形状结合备份-2" fill="#000000"></path>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 26
static/assets/calling.svg

@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>实时通话</title>
-    <desc>Created with Sketch.</desc>
-    <defs>
-        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
-            <stop stop-color="#006EFF" offset="0%"></stop>
-            <stop stop-color="#006EFF" stop-opacity="0" offset="100%"></stop>
-        </linearGradient>
-    </defs>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="主页" transform="translate(-44.000000, -325.000000)">
-            <g id="编组-10" transform="translate(44.000000, 325.000000)">
-                <g id="编组-7">
-                    <rect id="矩形" x="0" y="0" width="48" height="48"></rect>
-                    <path d="M32.5,14 L42.2763932,33.5527864 C42.5233825,34.0467649 42.3231581,34.6474379 41.8291796,34.8944272 C41.6903242,34.9638549 41.5372111,35 41.381966,35 L23.618034,35 C23.0657492,35 22.618034,34.5522847 22.618034,34 C22.618034,33.8447549 22.6541791,33.6916418 22.7236068,33.5527864 L32.5,14 L32.5,14 Z" id="三角形" stroke="#006EFF" stroke-width="2" transform="translate(32.500000, 24.500000) rotate(-90.000000) translate(-32.500000, -24.500000) "></path>
-                    <rect id="矩形" fill="#FFFFFF" x="15" y="16" width="15" height="18"></rect>
-                    <rect id="矩形" stroke="#006EFF" stroke-width="2" x="6" y="11" width="25" height="27" rx="4"></rect>
-                    <rect id="矩形" fill="url(#linearGradient-1)" opacity="0.2" x="10" y="14" width="25" height="27" rx="2"></rect>
-                    <circle id="椭圆形" fill="#00C8DC" cx="13" cy="19" r="2"></circle>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 16
static/assets/down.svg

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>下拉</title>
-    <desc>Created with Sketch.</desc>
-    <g id="严肃版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="群聊" transform="translate(-339.000000, -108.000000)">
-            <g id="编组-12" transform="translate(347.000000, 116.000000) rotate(-270.000000) translate(-347.000000, -116.000000) translate(339.000000, 108.000000)">
-                <g id="编组-3">
-                    <rect id="矩形" x="0" y="0" width="16" height="16"></rect>
-                    <polygon id="Shape-Copy-4备份-2" fill="#FFFFFF" fill-rule="nonzero" opacity="0.8" points="5 3.05 9.91489362 8 5 12.95 6.04255319 14 12 8 6.04255319 2"></polygon>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 18
static/assets/face-emoji.svg

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>表情</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服" transform="translate(-303.000000, -737.000000)">
-            <g id="编组-6" transform="translate(303.000000, 737.000000)">
-                <g id="编组-11备份">
-                    <circle id="椭圆形备份-26" stroke="#000000" stroke-width="1.5" cx="14" cy="14" r="13.25"></circle>
-                    <path d="M22.9862985,14.5008636 C22.7264281,19.2384517 18.8024924,23 14,23 C9.19750761,23 5.27357191,19.2384517 5.01370152,14.5008636 Z" id="形状结合备份" fill="#000000"></path>
-                    <circle id="椭圆形备份-3" fill="#000000" cx="9" cy="9.5" r="1.5"></circle>
-                    <circle id="椭圆形备份-5" fill="#000000" cx="19" cy="9.5" r="1.5"></circle>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 26
static/assets/keyboard.svg

@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>键盘</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="语音消息" transform="translate(-8.000000, -737.000000)">
-            <g id="编组-2" transform="translate(8.000000, 737.000000)">
-                <g id="编组-13备份">
-                    <circle id="椭圆形备份-25" stroke="#000000" stroke-width="1.5" cx="14" cy="14" r="13.25"></circle>
-                    <circle id="椭圆形" fill="#000000" cx="11" cy="11" r="1"></circle>
-                    <circle id="椭圆形备份-10" fill="#000000" cx="8" cy="11" r="1"></circle>
-                    <circle id="椭圆形备份-4" fill="#000000" cx="11" cy="14" r="1"></circle>
-                    <circle id="椭圆形备份-11" fill="#000000" cx="8" cy="14" r="1"></circle>
-                    <circle id="椭圆形备份" fill="#000000" cx="14" cy="11" r="1"></circle>
-                    <circle id="椭圆形备份-8" fill="#000000" cx="17" cy="11" r="1"></circle>
-                    <circle id="椭圆形备份-6" fill="#000000" cx="14" cy="14" r="1"></circle>
-                    <circle id="椭圆形备份-2" fill="#000000" cx="20" cy="11" r="1"></circle>
-                    <circle id="椭圆形备份-9" fill="#000000" cx="20" cy="14" r="1"></circle>
-                    <circle id="椭圆形备份-7" fill="#000000" cx="17" cy="14" r="1"></circle>
-                    <rect id="矩形" fill="#000000" x="11" y="17" width="6" height="2"></rect>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 17
static/assets/more.svg

@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>更多功能</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服" transform="translate(-339.000000, -737.000000)">
-            <g id="编组-7" transform="translate(339.000000, 737.000000)">
-                <g id="编组-10备份">
-                    <circle id="椭圆形备份-27" stroke="#000000" stroke-width="1.5" cx="14" cy="14" r="13.25"></circle>
-                    <rect id="矩形备份-8" fill="#000000" x="7.5" y="13.5" width="13" height="1.5" rx="0.75"></rect>
-                    <rect id="矩形备份-9" fill="#000000" transform="translate(14.000000, 14.250000) rotate(90.000000) translate(-14.000000, -14.250000) " x="7.5" y="13.5" width="13" height="1.5" rx="0.75"></rect>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 16
static/assets/send-img.svg

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>发送照片</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-110.000000, -568.000000)">
-            <g id="编组-17" transform="translate(110.000000, 568.000000)">
-                <g id="编组-9">
-                    <rect id="矩形备份" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <path d="M43,21 C44.6568542,21 46,22.3431458 46,24 L46,24 L46,41 C46,42.6568542 44.6568542,44 43,44 L43,44 L21,44 C19.3431458,44 18,42.6568542 18,41 L18,41 L18,24 C18,22.3431458 19.3431458,21 21,21 L21,21 Z M43,23 L21,23 C20.4477153,23 20,23.4477153 20,24 L20,24 L20,35.005981 L26.0319192,31.9271194 L29.543,33.948 L36.9230783,28.9185728 L44,33.279 L44,24 C44,23.4871642 43.6139598,23.0644928 43.1166211,23.0067277 L43,23 Z M26,26 C27.1045695,26 28,26.8954305 28,28 C28,29.1045695 27.1045695,30 26,30 C24.8954305,30 24,29.1045695 24,28 C24,26.8954305 24.8954305,26 26,26 Z" id="形状结合" fill="#444444" fill-rule="nonzero"></path>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 19
static/assets/send-order.svg

@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>发送订单</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-291.000000, -677.000000)">
-            <g id="编组-23" transform="translate(291.000000, 677.000000)">
-                <g id="编组-12">
-                    <rect id="矩形备份-12" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <path d="M32,16 C33.0543618,16 33.9181651,16.8158778 33.9945143,17.8507377 L34,18 L35,18 C36.1045695,18 37,18.8954305 37,20 L42,20 C43.1045695,20 44,20.8954305 44,22 L44,43 C44,44.1045695 43.1045695,45 42,45 L22,45 C20.8954305,45 20,44.1045695 20,43 L20,22 C20,20.8954305 20.8954305,20 22,20 L27,20 L27,20 C27,18.8954305 27.8954305,18 29,18 L30,18 C30,16.8954305 30.8954305,16 32,16 Z" id="形状结合" fill="#444444"></path>
-                    <rect id="矩形" fill="#FFFFFF" x="26" y="26" width="12" height="2" rx="1"></rect>
-                    <rect id="矩形备份-22" fill="#FFFFFF" x="26" y="31" width="12" height="2" rx="1"></rect>
-                    <rect id="矩形备份-23" fill="#FFFFFF" x="26" y="36" width="7" height="2" rx="1"></rect>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 23
static/assets/send-video.svg

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>发送视频</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-291.000000, -568.000000)">
-            <g id="编组-19" transform="translate(291.000000, 568.000000)">
-                <g id="编组-11">
-                    <rect id="矩形备份-6" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <rect id="矩形备份-13" fill="#444444" x="18" y="21" width="28" height="23" rx="2"></rect>
-                    <g id="编组-4" transform="translate(21.000000, 40.000000)" fill="#FFFFFF">
-                        <path d="M3,0 L2,3 L0,3 L1,0 L3,0 Z M7,0 L6,3 L4,3 L5,0 L7,0 Z M11,0 L10,3 L8,3 L9,0 L11,0 Z M15,0 L14,3 L12,3 L13,0 L15,0 Z M19,0 L18,3 L16,3 L17,0 L19,0 Z M23,0 L22,3 L20,3 L21,0 L23,0 Z" id="形状结合"></path>
-                    </g>
-                    <g id="编组-4备份" transform="translate(20.000000, 22.000000)" fill="#FFFFFF">
-                        <path d="M3,0 L2,3 L0,3 L1,0 L3,0 Z M7,0 L6,3 L4,3 L5,0 L7,0 Z M11,0 L10,3 L8,3 L9,0 L11,0 Z M15,0 L14,3 L12,3 L13,0 L15,0 Z M19,0 L18,3 L16,3 L17,0 L19,0 Z M23,0 L22,3 L20,3 L21,0 L23,0 Z" id="形状结合"></path>
-                    </g>
-                    <path d="M32.5,30.8492574 L29.8316655,35 L35.1683345,35 L32.5,30.8492574 Z" id="三角形" stroke="#FFFFFF" stroke-width="2" fill="#FFFFFF" transform="translate(32.500000, 32.500000) rotate(-270.000000) translate(-32.500000, -32.500000) "></path>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 17
static/assets/serach-icon.svg

@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>搜索</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="消息列表-添加-发起会话" transform="translate(-32.000000, -112.000000)">
-            <g id="编组-2" transform="translate(32.000000, 112.000000)">
-                <g id="编组">
-                    <rect id="矩形" x="0" y="0" width="24" height="24"></rect>
-                    <circle id="椭圆形" stroke="#B0B0B0" stroke-width="2" cx="11" cy="11" r="6"></circle>
-                    <rect id="矩形" fill="#B0B0B0" transform="translate(17.292893, 17.292893) rotate(-45.000000) translate(-17.292893, -17.292893) " x="16.2928932" y="14.2928932" width="2" height="6"></rect>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 17
static/assets/service-assess.svg

@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>服务评价</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-201.000000, -677.000000)">
-            <g id="编组-22" transform="translate(201.000000, 677.000000)">
-                <g id="编组-13">
-                    <rect id="矩形备份-11" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <circle id="椭圆形" fill="#444444" cx="32" cy="32" r="14"></circle>
-                    <path d="M32,35.5 L29.6789754,36.7202349 C29.1901308,36.9772357 28.5855036,36.7892891 28.3285028,36.3004446 C28.2261638,36.1057841 28.1908491,35.882816 28.2280259,35.6660584 L28.6713022,33.0815595 L28.6713022,33.0815595 L26.7935538,31.2512072 C26.3980703,30.865706 26.389978,30.2325927 26.7754792,29.8371091 C26.9289878,29.6796255 27.1301302,29.5771383 27.3477672,29.5455138 L29.9427516,29.1684405 L29.9427516,29.1684405 L31.1032639,26.8169858 C31.3476862,26.3217321 31.9473121,26.1183931 32.4425657,26.3628154 C32.6397783,26.4601456 32.7994058,26.6197731 32.8967361,26.8169858 L34.0572484,29.1684405 L34.0572484,29.1684405 L36.6522328,29.5455138 C37.1987777,29.6249314 37.5774591,30.1323743 37.4980415,30.6789192 C37.466417,30.8965562 37.3639298,31.0976986 37.2064462,31.2512072 L35.3286978,33.0815595 L35.3286978,33.0815595 L35.7719741,35.6660584 C35.8653351,36.2103948 35.4997472,36.7273507 34.9554108,36.8207118 C34.7386532,36.8578886 34.5156851,36.8225739 34.3210246,36.7202349 L32,35.5 L32,35.5 Z" id="星形" fill="#FFFFFF"></path>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 18
static/assets/show.svg

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>更多</title>
-    <desc>Created with Sketch.</desc>
-    <g id="严肃版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="群聊-查看群成员-多成员" transform="translate(-170.000000, -143.000000)">
-            <g id="编组-10" transform="translate(170.000000, 143.000000)">
-                <g id="编组-9">
-                    <rect id="矩形备份-12" fill="#383C46" x="0" y="0" width="40" height="40" rx="4"></rect>
-                    <circle id="椭圆形" fill="#FFFFFF" opacity="0.8" cx="12" cy="20" r="2"></circle>
-                    <circle id="椭圆形备份-12" fill="#FFFFFF" opacity="0.8" cx="20" cy="20" r="2"></circle>
-                    <circle id="椭圆形备份-13" fill="#FFFFFF" opacity="0.8" cx="28" cy="20" r="2"></circle>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 17
static/assets/take-photo.svg

@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>拍摄照片</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-20.000000, -568.000000)">
-            <g id="编组-16" transform="translate(20.000000, 568.000000)">
-                <g id="编组-8">
-                    <rect id="矩形" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <path d="M35.763932,21 C36.5214761,21 37.2140024,21.4280048 37.5527864,22.1055728 L38.499,24 L44,24 C45.1045695,24 46,24.8954305 46,26 L46,42 C46,43.1045695 45.1045695,44 44,44 L20,44 C18.8954305,44 18,43.1045695 18,42 L18,26 C18,24.8954305 18.8954305,24 20,24 L25.5,24 L26.4472136,22.1055728 C26.7859976,21.4280048 27.4785239,21 28.236068,21 L35.763932,21 Z" id="形状结合" fill="#444444"></path>
-                    <circle id="椭圆形" stroke="#FFFFFF" stroke-width="2" cx="32" cy="34" r="5"></circle>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 0 - 17
static/assets/take-video.svg

@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
-    <title>拍摄视频</title>
-    <desc>Created with Sketch.</desc>
-    <g id="浅色版本" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="在线客服-更多功能" transform="translate(-201.000000, -568.000000)">
-            <g id="编组-18" transform="translate(201.000000, 568.000000)">
-                <g id="编组-10">
-                    <rect id="矩形备份-5" fill="#FFFFFF" x="0" y="0" width="64" height="64" rx="4"></rect>
-                    <path d="M37,22 C38.1045695,22 39,22.8954305 39,24 L39,29 L47.5527864,24.7236068 C48.0467649,24.4766175 48.6474379,24.6768419 48.8944272,25.1708204 C48.9638549,25.3096758 49,25.4627889 49,25.618034 L49,39.381966 C49,39.9342508 48.5522847,40.381966 48,40.381966 C47.8447549,40.381966 47.6916418,40.3458209 47.5527864,40.2763932 L39,36 L39,41 C39,42.1045695 38.1045695,43 37,43 L18,43 C16.8954305,43 16,42.1045695 16,41 L16,24 C16,22.8954305 16.8954305,22 18,22 L37,22 Z" id="形状结合" fill="#444444"></path>
-                    <circle id="椭圆形备份" fill="#FFFFFF" cx="22" cy="28" r="2"></circle>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>