Эх сурвалжийг харах

Merge branch 'develop' of https://gitee.com/yudaocode/yudao-mall-uniapp

# Conflicts:
#	pages/activity/point/list.vue
YunaiV 9 сар өмнө
parent
commit
29a5024468

+ 3 - 3
pages/activity/point/list.vue

@@ -8,7 +8,7 @@
       :scroll-with-animation="false"
       :enable-back-to-top="true"
     >
-      <s-point-card ref="sPointCardRef" class="ss-p-x-20 ss-m-t-20"/>
+      <s-point-card ref="sPointCardRef" class="ss-p-x-20 ss-m-t-20" />
       <s-empty
         v-if="activityTotal === 0"
         icon="/static/goods-empty.png"
@@ -18,8 +18,8 @@
         v-if="activityTotal > 0"
         :status="loadStatus"
         :content-text="{
-            contentdown: '上拉加载更多',
-          }"
+          contentdown: '上拉加载更多',
+        }"
         @tap="loadMore"
       />
     </scroll-view>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 7 - 10
pages/app/sign.vue


+ 72 - 27
pages/chat/components/messageList.vue

@@ -1,20 +1,35 @@
 <template>
   <!--  聊天虚拟列表  -->
-  <z-paging ref="pagingRef" v-model="messageList" use-chat-record-mode use-virtual-list
-            cell-height-mode="dynamic" default-page-size="20" :auto-clean-list-when-reload="false"
-            safe-area-inset-bottom bottom-bg-color="#f8f8f8" :back-to-top-style="backToTopStyle"
-            :auto-show-back-to-top="showNewMessageTip" @backToTopClick="onBackToTopClick"
-            @scrolltoupper="onScrollToUpper" @query="queryList">
+  <z-paging
+    ref="pagingRef"
+    v-model="messageList"
+    use-chat-record-mode
+    use-virtual-list
+    cell-height-mode="dynamic"
+    default-page-size="20"
+    :auto-clean-list-when-reload="false"
+    safe-area-inset-bottom
+    bottom-bg-color="#f8f8f8"
+    :back-to-top-style="backToTopStyle"
+    :auto-show-back-to-top="showNewMessageTip"
+    @backToTopClick="onBackToTopClick"
+    @scrolltoupper="onScrollToUpper"
+    @query="queryList"
+  >
     <template #top>
       <!-- 撑一下顶部导航 -->
       <view :style="{ height: sys_navBar + 'px' }"></view>
     </template>
     <!-- style="transform: scaleY(-1)"必须写,否则会导致列表倒置!!! -->
     <!-- 注意不要直接在chat-item组件标签上设置style,因为在微信小程序中是无效的,请包一层view -->
-    <template #cell="{item,index}">
+    <template #cell="{ item, index }">
       <view style="transform: scaleY(-1)">
         <!--  消息渲染  -->
-        <MessageListItem :message="item" :message-index="index" :message-list="messageList"></MessageListItem>
+        <MessageListItem
+          :message="item"
+          :message-index="index"
+          :message-list="messageList"
+        ></MessageListItem>
       </view>
     </template>
     <!-- 底部聊天输入框 -->
@@ -34,49 +49,79 @@
   import KeFuApi from '@/sheep/api/promotion/kefu';
   import { isEmpty } from '@/sheep/helper/utils';
   import sheep from '@/sheep';
-  
+  import { formatDate } from '@/sheep/util';
+
   const sys_navBar = sheep.$platform.navbar;
   const messageList = ref([]); // 消息列表
   const showNewMessageTip = ref(false); // 显示有新消息提示
+  const refreshMessage = ref(false); // 更新消息列表
   const backToTopStyle = reactive({
-    'width': '100px',
+    width: '100px',
     'background-color': '#fff',
     'border-radius': '30px',
     'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
-    'display': 'flex',
-    'justifyContent': 'center',
-    'alignItems': 'center',
+    display: 'flex',
+    justifyContent: 'center',
+    alignItems: 'center',
   }); // 返回顶部样式
   const queryParams = reactive({
-    pageNo: 1,
-    pageSize: 10,
+    no: 1, // 查询次数,只用于触底计算
+    limit: 20,
+    createTime: undefined,
   });
   const pagingRef = ref(null); // 虚拟列表
-  const queryList = async (pageNo, pageSize) => {
+  const queryList = async (no, limit) => {
     // 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
-    // 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
-    queryParams.pageNo = pageNo;
-    queryParams.pageSize = pageSize;
+    queryParams.no = no;
+    queryParams.limit = limit;
     await getMessageList();
   };
   // 获得消息分页列表
   const getMessageList = async () => {
-    const { data } = await KeFuApi.getKefuMessagePage(queryParams);
-    if (isEmpty(data.list)) {
+    const { data } = await KeFuApi.getKefuMessageList(queryParams);
+    if (isEmpty(data)) {
+      pagingRef.value.completeByNoMore([], true);
+      return;
+    }
+    if (queryParams.no > 1 && refreshMessage.value) {
+      const newMessageList = [];
+      for (const message of data) {
+        if (messageList.value.some((val) => val.id === message.id)) {
+          continue;
+        }
+        newMessageList.push(message);
+      }
+      // 新消息追加到开头
+      messageList.value = [...newMessageList, ...messageList.value];
+      pagingRef.value.updateCache(); // 更新缓存
+      refreshMessage.value = false; // 更新好后重置状态
       return;
     }
-    pagingRef.value.completeByTotal(data.list, data.total);
+    if (data.slice(-1).length > 0) {
+      // 设置最后一次历史查询的最后一条消息的 createTime
+      queryParams.createTime = formatDate(data.slice(-1)[0].createTime);
+    }
+    pagingRef.value.completeByNoMore(data, false);
   };
   /** 刷新消息列表 */
-  const refreshMessageList = (message = undefined) => {
-    if (message !== undefined) {
-      showNewMessageTip.value = true;
+  const refreshMessageList = async (message = undefined) => {
+    if (typeof message !== 'undefined') {
       // 追加数据
       pagingRef.value.addChatRecordData([message], false);
-      return;
+    } else {
+      queryParams.createTime = undefined;
+      refreshMessage.value = true;
+      await getMessageList();
+    }
+
+    // 若已是第一页则不做处理
+    if (queryParams.no > 1) {
+      showNewMessageTip.value = true;
+    } else {
+      onScrollToUpper();
     }
-    pagingRef.value.reload();
   };
+
   /** 滚动到最新消息 */
   const onBackToTopClick = (event) => {
     event(false); // 禁用默认操作
@@ -85,7 +130,7 @@
   /** 监听滚动到底部事件(因为 scroll 翻转了顶就是底) */
   const onScrollToUpper = () => {
     // 若已是第一页则不做处理
-    if (queryParams.pageNo === 1) {
+    if (queryParams.no === 1) {
       return;
     }
     showNewMessageTip.value = false;

+ 20 - 35
pages/chat/components/messageListItem.vue

@@ -46,7 +46,7 @@
         <!-- 内容 -->
         <template v-if="message.contentType === KeFuMessageContentTypeEnum.TEXT">
           <view class="message-box" :class="{ admin: message.senderType === UserTypeEnum.ADMIN }">
-            <mp-html :content="replaceEmoji(message.content)" />
+            <mp-html :content="replaceEmoji(getMessageContent(message).text || message.content)" />
           </view>
         </template>
         <template v-if="message.contentType === KeFuMessageContentTypeEnum.IMAGE">
@@ -58,9 +58,9 @@
             <su-image
               class="message-img"
               isPreview
-              :previewList="[sheep.$url.cdn(message.content)]"
+              :previewList="[sheep.$url.cdn(getMessageContent(message).picUrl || message.content)]"
               :current="0"
-              :src="sheep.$url.cdn(message.content)"
+              :src="sheep.$url.cdn(getMessageContent(message).picUrl || message.content)"
               :height="200"
               :width="200"
               mode="aspectFill"
@@ -68,10 +68,12 @@
           </view>
         </template>
         <template v-if="message.contentType === KeFuMessageContentTypeEnum.PRODUCT">
+          <div class="ss-m-b-10">
           <GoodsItem
             :goodsData="getMessageContent(message)"
             @tap="sheep.$router.go('/pages/goods/index', { id: getMessageContent(message).spuId })"
           />
+          </div>
         </template>
         <template v-if="message.contentType === KeFuMessageContentTypeEnum.ORDER">
           <OrderItem
@@ -84,7 +86,7 @@
           v-if="message.senderType === UserTypeEnum.MEMBER"
           class="chat-avatar ss-m-l-24"
           :src="
-            sheep.$url.cdn(message.senderAvatar) ||
+            sheep.$url.cdn(userInfo.avatar) ||
             sheep.$url.static('/static/img/shop/chat/default.png')
           "
           mode="aspectFill"
@@ -101,7 +103,7 @@
   import { KeFuMessageContentTypeEnum, UserTypeEnum } from '@/pages/chat/util/constants';
   import { emojiList } from '@/pages/chat/util/emoji';
   import sheep from '@/sheep';
-  import { formatDate } from '@/sheep/util';
+  import { formatDate, jsonParse } from '@/sheep/util';
   import GoodsItem from '@/pages/chat/components/goods.vue';
   import OrderItem from '@/pages/chat/components/order.vue';
 
@@ -122,7 +124,9 @@
       default: () => [],
     },
   });
-  const getMessageContent = computed(() => (item) => JSON.parse(item.content)); // 解析消息内容
+
+  const getMessageContent = computed(() => (item) => jsonParse(item.content)); // 解析消息内容
+  const userInfo = computed(() => sheep.$store('user').userInfo);
 
   //======================= 工具 =======================
 
@@ -145,7 +149,7 @@
           let emojiFile = selEmojiFile(item);
           newData = newData.replace(
             item,
-            `<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;" src="${sheep.$url.cdn(
+            `<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;vertical-align: middle;" src="${sheep.$url.cdn(
               '/static/img/chat/emoji/' + emojiFile,
             )}"/>`,
           );
@@ -167,7 +171,7 @@
 
 <style scoped lang="scss">
   .message-item {
-    margin-bottom: 33rpx;
+    margin-bottom: 10rpx;
   }
 
   .date-message,
@@ -231,18 +235,23 @@
   .message-box {
     max-width: 50%;
     font-size: 16px;
-    line-height: 20px;
     white-space: normal;
     word-break: break-all;
     word-wrap: break-word;
     padding: 20rpx;
-    border-radius: 10rpx;
     color: #fff;
     background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-
+    margin-top: 18px;
+    margin-bottom: 9px;
+    border-top-left-radius: 10px;
+    border-bottom-right-radius: 10px;
+    border-bottom-left-radius: 10px;
     &.admin {
       background: #fff;
       color: #333;
+      margin-top: 18px;
+      margin-bottom: 9px;
+      border-radius: 0 10px 10px 10px;
     }
 
     :deep() {
@@ -270,30 +279,6 @@
     border-radius: 6rpx;
   }
 
-  .template-wrap {
-    // width: 100%;
-    padding: 20rpx 24rpx;
-    background: #fff;
-    border-radius: 10rpx;
-
-    .title {
-      font-size: 26rpx;
-      font-weight: 500;
-      color: #333;
-      margin-bottom: 29rpx;
-    }
-
-    .item {
-      font-size: 24rpx;
-      color: var(--ui-BG-Main);
-      margin-bottom: 16rpx;
-
-      &:last-of-type {
-        margin-bottom: 0;
-      }
-    }
-  }
-
   .error-img {
     width: 400rpx;
     height: 400rpx;

+ 33 - 13
pages/chat/index.vue

@@ -1,17 +1,35 @@
 <template>
-  <s-layout class="chat-wrap" :title="!isReconnecting ? '连接客服成功' : '会话重连中'" navbar="inner">
+  <s-layout
+    class="chat-wrap"
+    :title="!isReconnecting ? '连接客服成功' : '会话重连中'"
+    navbar="inner"
+  >
     <!--  覆盖头部导航栏背景颜色  -->
     <div class="page-bg" :style="{ height: sys_navBar + 'px' }"></div>
     <!--  聊天区域  -->
     <MessageList ref="messageListRef">
       <template #bottom>
-        <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
+        <message-input
+          v-model="chat.msg"
+          @on-tools="onTools"
+          @send-message="onSendMessage"
+        ></message-input>
       </template>
     </MessageList>
     <!--  聊天工具  -->
-    <tools-popup :show-tools="chat.showTools" :tools-mode="chat.toolsMode" @close="handleToolsClose"
-                 @on-emoji="onEmoji" @image-select="onSelect" @on-show-select="onShowSelect">
-      <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
+    <tools-popup
+      :show-tools="chat.showTools"
+      :tools-mode="chat.toolsMode"
+      @close="handleToolsClose"
+      @on-emoji="onEmoji"
+      @image-select="onSelect"
+      @on-show-select="onShowSelect"
+    >
+      <message-input
+        v-model="chat.msg"
+        @on-tools="onTools"
+        @send-message="onSendMessage"
+      ></message-input>
     </tools-popup>
     <!--  商品订单选择  -->
     <SelectPopup
@@ -30,10 +48,14 @@
   import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
   import MessageInput from '@/pages/chat/components/messageInput.vue';
   import SelectPopup from '@/pages/chat/components/select-popup.vue';
-  import { KeFuMessageContentTypeEnum, WebSocketMessageTypeConstants } from '@/pages/chat/util/constants';
+  import {
+    KeFuMessageContentTypeEnum,
+    WebSocketMessageTypeConstants,
+  } from '@/pages/chat/util/constants';
   import FileApi from '@/sheep/api/infra/file';
   import KeFuApi from '@/sheep/api/promotion/kefu';
   import { useWebSocket } from '@/sheep/hooks/useWebSocket';
+  import { jsonParse } from '@/sheep/util';
 
   const sys_navBar = sheep.$platform.navbar;
 
@@ -52,7 +74,7 @@
     try {
       const data = {
         contentType: KeFuMessageContentTypeEnum.TEXT,
-        content: chat.msg,
+        content: JSON.stringify({ text: chat.msg }),
       };
       await KeFuApi.sendKefuMessage(data);
       await messageListRef.value.refreshMessageList();
@@ -104,7 +126,7 @@
         const res = await FileApi.uploadFile(data.tempFiles[0].path);
         msg = {
           contentType: KeFuMessageContentTypeEnum.IMAGE,
-          content: res.data,
+          content: JSON.stringify({ picUrl: res.data }),
         };
         break;
       case 'goods':
@@ -134,19 +156,18 @@
   //======================= 聊天工具相关 end =======================
   const { options } = useWebSocket({
     // 连接成功
-    onConnected: async () => {
-    },
+    onConnected: async () => {},
     // 收到消息
     onMessage: async (data) => {
       const type = data.type;
       if (!type) {
-        console.error('未知的消息类型:' + data.value);
+        console.error('未知的消息类型:' + data);
         return;
       }
       // 2.2 消息类型:KEFU_MESSAGE_TYPE
       if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
         // 刷新消息列表
-        await messageListRef.value.refreshMessageList(JSON.parse(data.content));
+        await messageListRef.value.refreshMessageList(jsonParse(data.content));
         return;
       }
       // 2.3 消息类型:KEFU_MESSAGE_ADMIN_READ
@@ -160,7 +181,6 @@
 
 <style scoped lang="scss">
   .chat-wrap {
-
     .page-bg {
       width: 100%;
       position: absolute;

+ 11 - 5
pages/commission/components/account-type-select.vue

@@ -45,7 +45,8 @@
       type: Boolean,
       default: false,
     },
-    methods: { // 开启的提现方式
+    methods: {
+      // 开启的提现方式
       type: Array,
       default: [],
     },
@@ -57,7 +58,7 @@
 
   const typeList = [
     {
-      // icon: '/static/img/shop/pay/wechat.png', // TODO 芋艿:后续给个 icon
+      icon: '/static/img/shop/pay/wechat.png',
       title: '钱包余额',
       value: '1',
     },
@@ -68,14 +69,19 @@
     },
     {
       icon: '/static/img/shop/pay/wechat.png',
-      title: '微信零钱',
+      title: '微信账户', // 微信手动转账
       value: '3',
     },
     {
       icon: '/static/img/shop/pay/alipay.png',
       title: '支付宝账户',
       value: '4',
-    }
+    },
+    {
+      icon: '/static/img/shop/pay/wechat.png',
+      title: '微信零钱', // 微信 API 转账
+      value: '5',
+    },
   ];
 
   function onChange(e) {
@@ -89,7 +95,7 @@
     }
     // 赋值
     emits('update:modelValue', {
-      type: state.currentValue
+      type: state.currentValue,
     });
     // 关闭弹窗
     emits('close');

+ 3 - 2
pages/commission/withdraw.vue

@@ -30,8 +30,9 @@
           <view v-if="!state.accountInfo.type" class="empty-text">请选择提现方式</view>
           <view v-if="state.accountInfo.type === '1'" class="empty-text">钱包余额</view>
           <view v-if="state.accountInfo.type === '2'" class="empty-text">银行卡转账</view>
-          <view v-if="state.accountInfo.type === '3'" class="empty-text">微信零钱</view>
+          <view v-if="state.accountInfo.type === '3'" class="empty-text">微信账户</view>
           <view v-if="state.accountInfo.type === '4'" class="empty-text">支付宝账户</view>
+          <view v-if="state.accountInfo.type === '5'" class="empty-text">微信零钱</view>
           <text class="cicon-forward" />
         </view>
       </view>
@@ -48,7 +49,7 @@
         />
       </view>
       <!-- 提现账号 -->
-      <view class="card-title" v-show="['2', '3', '4'].includes(state.accountInfo.type)">
+      <view class="card-title" v-show="['2', '3', '4', '5'].includes(state.accountInfo.type)">
         提现账号
       </view>
       <view

+ 8 - 4
pages/order/addressSelection.vue

@@ -29,9 +29,9 @@
         </view>
         <view class="flex flex-wrap">
           <text class="default font-color" v-if="state.addressInfo.defaultStatus">[默认]</text>
-          <text class="line2"
-            >{{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}</text
-          >
+          <text class="line2">
+            {{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}
+          </text>
         </view>
       </view>
       <view class="addressCon" v-else>
@@ -44,7 +44,11 @@
       </view>
     </view>
     <!-- 情况二:门店的选择 -->
-    <view class="address flex flex-wrap flex-center ss-row-between" v-else @tap="onSelectAddress">
+    <view
+      class="address flex flex-wrap flex-center ss-row-between"
+      v-if="state.deliveryType === 2"
+      @tap="onSelectAddress"
+    >
       <view class="addressCon" v-if="state.pickUpInfo.name">
         <view class="name"
           >{{ state.pickUpInfo.name }}

+ 2 - 2
pages/order/aftersale/list.vue

@@ -37,14 +37,14 @@
           <text class="_icon-forward"></text>
         </view>
         <view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
-          <!-- TODO 功能缺失:填写退货信息 -->
           <view>
             <button
               class="ss-reset-button tool-btn"
               @tap.stop="onApply(order.id)"
               v-if="order?.buttons.includes('cancel')"
-              >取消申请</button
             >
+              取消申请
+            </button>
           </view>
         </view>
       </view>

+ 24 - 3
pages/order/confirm.vue

@@ -244,7 +244,7 @@
 
   const addressState = ref({
     addressInfo: {}, // 选择的收货地址
-    deliveryType: 1, // 收货方式:1-快递配送,2-门店自提
+    deliveryType: undefined, // 收货方式:1-快递配送,2-门店自提
     isPickUp: true, // 门店自提是否开启
     pickUpInfo: {}, // 选择的自提门店信息
     receiverName: '', // 收件人名称
@@ -349,7 +349,7 @@
       pointActivityId: state.orderPayload.pointActivityId,
     });
     if (code !== 0) {
-      return;
+      return code;
     }
     state.orderInfo = data;
     state.couponInfo = data.coupons || [];
@@ -357,20 +357,41 @@
     if (state.orderInfo.address) {
       addressState.value.addressInfo = state.orderInfo.address;
     }
+    return code;
   }
 
   onLoad(async (options) => {
+    // 解析参数
     if (!options.data) {
       sheep.$helper.toast('参数不正确,请检查!');
       return;
     }
     state.orderPayload = JSON.parse(options.data);
-    await getOrderInfo();
+
     // 获取交易配置
     const { data, code } = await TradeConfigApi.getTradeConfig();
     if (code === 0) {
       addressState.value.isPickUp = data.deliveryPickUpEnabled;
     }
+
+    // 价格计算
+    // 情况一:先自动选择“快递物流”
+    addressState.value.deliveryType = 1;
+    let orderCode = await getOrderInfo();
+    if (orderCode === 0) {
+      return;
+    }
+    // 情况二:失败,再自动选择“门店自提”
+    if (addressState.value.isPickUp) {
+      addressState.value.deliveryType = 2;
+      let orderCode = await getOrderInfo();
+      if (orderCode === 0) {
+        return;
+      }
+    }
+    // 情况三:都失败,则不选择
+    addressState.value.deliveryType = undefined;
+    await getOrderInfo();
   });
 
   // 使用 watch 监听地址和配送方式的变化

+ 2 - 2
sheep/api/promotion/kefu.js

@@ -15,9 +15,9 @@ const KeFuApi = {
       },
     });
   },
-  getKefuMessagePage: (params) => {
+  getKefuMessageList: (params) => {
     return request({
-      url: '/promotion/kefu-message/page',
+      url: '/promotion/kefu-message/list',
       method: 'GET',
       params,
       custom: {

+ 3 - 0
sheep/api/trade/config.js

@@ -6,6 +6,9 @@ const TradeConfigApi = {
     return request({
       url: `/trade/config/get`,
       method: 'GET',
+      custom: {
+        showLoading: false,
+      },
     });
   },
 };

+ 3 - 0
sheep/api/trade/order.js

@@ -35,6 +35,9 @@ const OrderApi = {
     if (!(data.pointActivityId > 0)) {
       delete data2.pointActivityId;
     }
+    if (!(data.deliveryType > 0)) {
+      delete data2.deliveryType;
+    }
     // 解决 SpringMVC 接受 List<Item> 参数的问题
     delete data2.items;
     for (let i = 0; i < data.items.length; i++) {

+ 3 - 2
sheep/components/s-activity-pop/s-activity-pop.vue

@@ -57,8 +57,9 @@
               <view class="ss-m-b-24 cotBu-txt">
                 {{
                   item.validityType == 1
-                    ? sheep.$helper.timeFormat(item.validStartTime, 'yyyy.mm.dd') -
-                      sheep.$helper.timeFormat(item.validEndTime, 'yyyy.mm.dd')
+                    ? sheep.$helper.timeFormat(item.validStartTime, 'yyyy-mm-dd') +
+                      '-' +
+                      sheep.$helper.timeFormat(item.validEndTime, 'yyyy-mm-dd')
                     : '领取后' + item.fixedStartTerm + '-' + item.fixedEndTerm + '天可用'
                 }}
               </view>

+ 95 - 92
sheep/components/s-title-block/s-title-block.vue

@@ -1,108 +1,111 @@
 <!-- 装修商品组件:标题栏 -->
 <template>
-	<view class="ss-title-wrap ss-flex ss-col-center" :class="[state.typeMap[data.textAlign]]" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
-		<view class="title-content">
-			<!-- 主标题 -->
-			<view v-if="data.title" class="title-text" :style="[titleStyles]">{{ data.title }}</view>
-			<!-- 副标题 -->
-			<view v-if="data.description" :style="[descStyles]" class="sub-title-text">{{ data.description }}</view>
-		</view>
-		<!-- 查看更多 -->
-		<view v-if="data.more?.show" class="more-box ss-flex ss-col-center" @tap="sheep.$router.go(data.more.url)"
-			:style="{color: data.descriptionColor}">
-			<view class="more-text" v-if="data.more.type !== 'icon'">{{ data.more.text }} </view>
-			<text class="_icon-forward" v-if="data.more.type !== 'text'"></text>
-		</view>
-	</view>
+  <view
+    class="ss-title-wrap ss-flex ss-col-center"
+    :class="[state.typeMap[data.textAlign]]"
+    :style="[bgStyle, { marginLeft: `${data.space}px` }]"
+  >
+    <view class="title-content">
+      <!-- 主标题 -->
+      <view v-if="data.title" class="title-text" :style="[titleStyles]">{{ data.title }}</view>
+      <!-- 副标题 -->
+      <view v-if="data.description" :style="[descStyles]" class="sub-title-text">{{
+        data.description
+      }}</view>
+    </view>
+    <!-- 查看更多 -->
+    <view
+      v-if="data.more?.show"
+      class="more-box ss-flex ss-col-center"
+      @tap="sheep.$router.go(data.more.url)"
+      :style="{ color: data.descriptionColor }"
+    >
+      <view class="more-text" v-if="data.more.type !== 'icon'">{{ data.more.text }} </view>
+      <text class="_icon-forward" v-if="data.more.type !== 'text'"></text>
+    </view>
+  </view>
 </template>
 
 <script setup>
-	/**
-	 * 标题栏
-	 */
-	import {
-		reactive,
-		computed
-	} from 'vue';
-	import sheep from '@/sheep';
+  /**
+   * 标题栏
+   */
+  import { reactive, computed } from 'vue';
+  import sheep from '@/sheep';
 
-	// 数据
-	const state = reactive({
-		typeMap: {
-			left: 'ss-row-left',
-			center: 'ss-row-center',
-		},
-	});
+  // 数据
+  const state = reactive({
+    typeMap: {
+      left: 'ss-row-left',
+      center: 'ss-row-center',
+    },
+  });
 
-	// 接收参数
-	const props = defineProps({
-		// 装修数据
-		data: {
-			type: Object,
-			default: () => ({}),
-		},
-		// 装修样式
-		styles: {
-			type: Object,
-			default: () => ({}),
-		},
-	});
-	// 设置背景样式
-	const bgStyle = computed(() => {
-		// 直接从 props.styles 解构
-		const {
-			bgType,
-			bgImg,
-			bgColor
-		} = props.styles;
+  // 接收参数
+  const props = defineProps({
+    // 装修数据
+    data: {
+      type: Object,
+      default: () => ({}),
+    },
+    // 装修样式
+    styles: {
+      type: Object,
+      default: () => ({}),
+    },
+  });
+  // 设置背景样式
+  const bgStyle = computed(() => {
+    // 直接从 props.styles 解构
+    const { bgType, bgImg, bgColor } = props.styles;
 
-		// 根据 bgType 返回相应的样式
-		return {
-			background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
-		};
-	});
+    // 根据 bgType 返回相应的样式
+    return {
+      background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor,
+    };
+  });
 
-	// 标题样式
-	const titleStyles = {
-		color: props.data.titleColor,
-		fontSize: `${props.data.titleSize}px`,
-		textAlign: props.data.textAlign
-	};
+  // 标题样式
+  const titleStyles = {
+    color: props.data.titleColor,
+    fontSize: `${props.data.titleSize}px`,
+    textAlign: props.data.textAlign,
+  };
 
-	// 副标题
-	const descStyles = {
-		color: props.data.descriptionColor,
-		textAlign: props.data.textAlign,
-		fontSize: `${props.data.descriptionSize}px`,
-		fontWeight: `${props.data.descriptionWeight}px`,
-	};
+  // 副标题
+  const descStyles = {
+    color: props.data.descriptionColor,
+    textAlign: props.data.textAlign,
+    fontSize: `${props.data.descriptionSize}px`,
+    fontWeight: `${props.data.descriptionWeight}`,
+  };
 </script>
 
 <style lang="scss" scoped>
-	.ss-title-wrap {
-		height: 80rpx;
-		position: relative;
+  .ss-title-wrap {
+    height: 80rpx;
+    position: relative;
 
-		.title-content {
-			.title-text {
-				font-size: 30rpx;
-				color: #333;
-			}
+    .title-content {
+      .title-text {
+        font-size: 30rpx;
+        color: #333;
+      }
 
-			.sub-title-text {
-				font-size: 22rpx;
-				color: #999;
-			}
-		}
+      .sub-title-text {
+        font-size: 22rpx;
+        color: #999;
+      }
+    }
 
-		.more-box {
-			white-space: nowrap;
-			font-size: 22rpx;
-			color: #999;
-			position: absolute;
-			top: 50%;
-			transform: translateY(-50%);
-			right: 20rpx;
-		}
-	}
-</style>
+    .more-box {
+      white-space: nowrap;
+      font-size: 22rpx;
+      color: #999;
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 20rpx;
+    }
+  }
+</style>

+ 56 - 36
sheep/util/index.js

@@ -1,4 +1,4 @@
-import dayjs from "dayjs";
+import dayjs from 'dayjs';
 
 /**
  * 将一个整数转换为分数保留两位小数
@@ -6,10 +6,10 @@ import dayjs from "dayjs";
  * @return {number} 分数
  */
 export const formatToFraction = (num) => {
-  if (typeof num === 'undefined') return 0
-  const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
-  return parseFloat((parsedNumber / 100).toFixed(2))
-}
+  if (typeof num === 'undefined') return 0;
+  const parsedNumber = typeof num === 'string' ? parseFloat(num) : num;
+  return parseFloat((parsedNumber / 100).toFixed(2));
+};
 
 /**
  * 将一个数转换为 1.00 这样
@@ -19,26 +19,26 @@ export const formatToFraction = (num) => {
  * @return {string} 分数
  */
 export const floatToFixed2 = (num) => {
-  let str = '0.00'
+  let str = '0.00';
   if (typeof num === 'undefined') {
-    return str
+    return str;
   }
-  const f = formatToFraction(num)
-  const decimalPart = f.toString().split('.')[1]
-  const len = decimalPart ? decimalPart.length : 0
+  const f = formatToFraction(num);
+  const decimalPart = f.toString().split('.')[1];
+  const len = decimalPart ? decimalPart.length : 0;
   switch (len) {
     case 0:
-      str = f.toString() + '.00'
-      break
+      str = f.toString() + '.00';
+      break;
     case 1:
-      str = f.toString() + '.0'
-      break
+      str = f.toString() + '.0';
+      break;
     case 2:
-      str = f.toString()
-      break
+      str = f.toString();
+      break;
   }
-  return str
-}
+  return str;
+};
 
 /**
  * 将一个分数转换为整数
@@ -47,11 +47,11 @@ export const floatToFixed2 = (num) => {
  * @return {number} 整数
  */
 export const convertToInteger = (num) => {
-  if (typeof num === 'undefined') return 0
-  const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
+  if (typeof num === 'undefined') return 0;
+  const parsedNumber = typeof num === 'string' ? parseFloat(num) : num;
   // TODO 分转元后还有小数则四舍五入
-  return Math.round(parsedNumber * 100)
-}
+  return Math.round(parsedNumber * 100);
+};
 
 /**
  * 时间日期转换
@@ -64,16 +64,16 @@ export const convertToInteger = (num) => {
  * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
  * @returns {string} 返回拼接后的时间字符串
  */
-export function formatDate(date, format= 'YYYY-MM-DD HH:mm:ss') {
+export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
   // 日期不存在,则返回空
   if (!date) {
-    return ''
+    return '';
   }
   // 日期存在,则进行格式化
   if (format === undefined) {
-    format = 'YYYY-MM-DD HH:mm:ss'
+    format = 'YYYY-MM-DD HH:mm:ss';
   }
-  return dayjs(date).format(format)
+  return dayjs(date).format(format);
 }
 
 /**
@@ -85,16 +85,22 @@ export function formatDate(date, format= 'YYYY-MM-DD HH:mm:ss') {
  * @param {*} children 孩子节点字段 默认 'children'
  * @param {*} rootId 根Id 默认 0
  */
-export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {
+export function handleTree(
+  data,
+  id = 'id',
+  parentId = 'parentId',
+  children = 'children',
+  rootId = 0,
+) {
   // 对源数据深度克隆
-  const cloneData = JSON.parse(JSON.stringify(data))
+  const cloneData = JSON.parse(JSON.stringify(data));
   // 循环所有项
-  const treeData = cloneData.filter(father => {
-    let branchArr = cloneData.filter(child => {
+  const treeData = cloneData.filter((father) => {
+    let branchArr = cloneData.filter((child) => {
       //返回每一项的子级数组
-      return father[id] === child[parentId]
+      return father[id] === child[parentId];
     });
-    branchArr.length > 0 ? father.children = branchArr : '';
+    branchArr.length > 0 ? (father.children = branchArr) : '';
     //返回第一层
     return father[parentId] === rootId;
   });
@@ -120,14 +126,28 @@ export function resetPagination(pagination) {
  * @param source 源对象
  */
 export const copyValueToTarget = (target, source) => {
-  const newObj = Object.assign({}, target, source)
+  const newObj = Object.assign({}, target, source);
   // 删除多余属性
   Object.keys(newObj).forEach((key) => {
     // 如果不是target中的属性则删除
     if (Object.keys(target).indexOf(key) === -1) {
-      delete newObj[key]
+      delete newObj[key];
     }
-  })
+  });
   // 更新目标对象值
-  Object.assign(target, newObj)
+  Object.assign(target, newObj);
+};
+
+/**
+ * 解析 JSON 字符串
+ *
+ * @param str
+ */
+export function jsonParse(str) {
+  try {
+    return JSON.parse(str);
+  } catch (e) {
+    console.error(`str[${str}] 不是一个 JSON 字符串`);
+    return '';
+  }
 }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно