Просмотр исходного кода

Merge branch 'zhongqi-hdAppModify'

shish 10 месяцев назад
Родитель
Сommit
e2f71cc3c2

+ 1 - 1
ghsApp/src/admin/home/member.vue

@@ -18,7 +18,7 @@
       <block v-for="(item, index) in list.data" :key="index">
         <tui-list-cell :arrow="true" @click="pageTo({url: '/pagesClient/member/detail?id='+item.id})">
           <div class="tui-msg-box">
-            <img :src="item.avatar" class="tui-msg-pic" mode="widthFix" />
+            <img :src="item.smallAvatar" class="tui-msg-pic" mode="widthFix" />
             <div class="tui-msg-item">
               <div class="tui-msg-name">
                 <div class="tui-user-name">

+ 8 - 5
ghsApp/src/components/RichMediaViewer/index.vue

@@ -83,6 +83,7 @@
 
         <!-- 图片展示区域 -->
         <view class="preview-content">
+          <!-- 多张图片预览 -->
           <swiper
             v-if="currentImageList.length > 1"
             :current="currentImageIndex"
@@ -121,6 +122,7 @@
                       @error="onPreviewImageError(index)"
                       @load="onPreviewImageLoad(index)"
                       @longpress="saveImage"
+                      @click="closeImagePreview"
                     />
                   </view>
                 </movable-view>
@@ -148,6 +150,7 @@
                   @error="onPreviewImageError(0)"
                   @load="onPreviewImageLoad(0)"
                   @longpress="saveImage"
+                  @click="closeImagePreview"
                 />
               </view>
             </movable-view>
@@ -390,9 +393,9 @@ export default {
 
     // 点击遮罩层关闭预览
     handleMaskClick() {
-      if (this.currentScale === 1) {
-        this.closeImagePreview();
-      }
+      if (this.currentScale === 1) {}
+
+      this.closeImagePreview();
     },
 
     // 关闭图片预览
@@ -534,7 +537,7 @@ export default {
 }
 
 .content-block {
-  margin-bottom: 40rpx;
+  margin-bottom: 25rpx;
 
   &:last-child {
     margin-bottom: 0;
@@ -684,7 +687,7 @@ export default {
   right: 0;
   bottom: 0;
   background-color: rgba(0, 0, 0, 0.9);
-  z-index: 9999;
+  z-index: 999999;
   display: flex;
   flex-direction: column;
 }

+ 61 - 30
hdApp/src/admin/chat/chatPage.vue

@@ -22,7 +22,7 @@
         <!-- 左侧消息(客户) -->
         <view class="message-left" v-if="!message.isMine">
           <view class="avatar-wrapper">
-            <image class="avatar" :src="message.avatar" mode="aspectFill" />
+            <image class="avatar" :src="formatAvatarUrl(message.avatar)" mode="aspectFill" />
           </view>
 
           <view v-if="message.messageType == 'text' || message.messageType != 'goods'" class="message-content-wrapper">
@@ -43,13 +43,12 @@
                   <!-- <view class="sales-info">
                     <text class="delivery-time">48小时发</text>
                   </view> -->
-                  <text v-if="message.goodsInfo.goodsPriceType == 1 || message.goodsInfo.goodsPriceType == 2" class="goods-price"
-                    >¥{{ parseFloat(message.goodsInfo.goodsPrice).toFixed(2) }}</text
-                  >
+                  <text v-if="message.goodsInfo.goodsPriceType == 1 || message.goodsInfo.goodsPriceType == 2" class="goods-price">¥{{ parseFloat(message.goodsInfo.goodsPrice).toFixed(2) }}</text>
+                  <text v-if="message.goodsInfo.resourcePrice && message.goodsInfo.resourcePrice != parseFloat(message.goodsInfo.goodsPrice).toFixed(2)" class="goods-price--secondary">原先 ¥{{ message.goodsInfo.resourcePrice }}</text>
                 </view>
               </view>
               <view class="goods-footer" v-if="message.goodsInfo.goodsPriceType != 1"> </view>
-              <view v-if="message.goodsInfo.goodsPriceType == 0 && isExpired(message.timestamp)" class="action-buttons">
+              <view v-if="message.goodsInfo.goodsPriceType !== 2 && isExpired(message.timestamp)" class="action-buttons">
                 <form @submit.prevent="createQrotedPrice(message)">
                   <input 
                     class="price-input"
@@ -127,7 +126,7 @@
             <view class="message-time align-right">{{ message.time }}</view>
           </view>
           <view class="avatar-wrapper">
-            <image class="avatar" :src="message.avatar" mode="aspectFill" />
+            <image class="avatar" :src="formatAvatarUrl(message.avatar)" mode="aspectFill" />
           </view>
         </view>
       </view>
@@ -166,7 +165,7 @@
 </template>
 
 <script>
-import { getChatHistory, createQuotedPrice, getQuotedPrice } from '@/api/chat';
+import { getChatHistory, createQuotedPrice as createQuotedPriceApi, getQuotedPrice } from '@/api/chat';
 import { WSSHOST } from '@/config.js';
 export default {
   data() {
@@ -798,7 +797,7 @@ export default {
           //--------------------------------
           message: messageData.message || messageData || '',
           username: messageData.name || messageData.username || '未知用户',
-          avatar: messageData.avatar || this.$constant.imgUrl + '/retail/default-img.png',
+          avatar: messageData.avatar || '',
           time: this.getCurrentTime(), // 收到消息时间点
           timestamp: messageData.timestamp, // 消息发送时间点
           messageType: messageType
@@ -839,7 +838,7 @@ export default {
       }
 
       const messageContent = this.inputText.trim();
-      const avatar = this.currentUser.avatar || this.$constant.imgUrl + '/retail/default-img.png';
+      const avatar = this.currentUser.avatar || '';
       const timestamp = Math.floor(Date.now() / 1000);
 
       try {
@@ -991,7 +990,7 @@ export default {
         // 如果是字符串,则尝试解析
         if (typeof messageStr === 'string') {
           let parsed = JSON.parse(messageStr);
-          if(parsed.goodsPriceType == 0) { // 没价格类型,则要请求后端获取修改价格,再根据结果展示
+          if(parsed.goodsPriceType == 0 || parsed.goodsPriceType == 1) { // 请求后端获取修改价格,再根据结果展示
             // 如果没有过期,请求后端获取修改价格
             const now = Math.floor(Date.now()/1000);
             const timeDiff = now - parsed.timestamp;
@@ -1009,8 +1008,10 @@ export default {
                   // console.log("res", res)
                   if (res.code == 1) {
                     if (res.data.price != -1) {
+                      parsed.resourcePrice = parseFloat(parsed.goodsPrice).toFixed(2);
                       parsed.goodsPriceType = 2; // 价格类型
                       parsed.goodsPrice = parseFloat(res.data.price).toFixed(2);
+                      // 通过 parsed.goodsPriceType 判断哪种类型
                     }
                   }
                 }).catch(err => {
@@ -1052,7 +1053,7 @@ export default {
         });
         return;
       }
-      const avatar = this.currentUser.avatar || this.$constant.imgUrl + '/retail/default-img.png';
+      const avatar = this.currentUser.avatar || '';
       const timestamp = Math.floor(Date.now() / 1000);
 
       // 构建商品信息消息体 -------------------------------- 所看到的消息体 -------------------------------
@@ -1128,11 +1129,11 @@ export default {
         return;
       }
       if (isNaN(message.quotedPrice)) {
-        this.$msg('价格格式不正确');
+        this.$msg('价格不对,请输入数字');
         return;
       }
-      if (message.quotedPrice <= 0.01) {
-        this.$msg('价格不能小于等于0.01');
+      if (message.quotedPrice < 0.01) {
+        this.$msg('价格不能小于0.01');
         return;
       }
       const keyArr = message.goodsInfo.key.split('-');// shopId-userId-goodsId
@@ -1149,24 +1150,24 @@ export default {
         this.$msg('key数据出错');
         return;
       }
-      createQuotedPrice({
+      createQuotedPriceApi({
         key: message.goodsInfo.key,
         price: parseFloat(message.quotedPrice).toFixed(2)
-      })
-        .then((res) => {
-          // console.log("createQuotedPrice", res)
-          if (res.code == 1) {
-            this.$msg('改价成功');
-            message.goodsInfo.goodsPriceType = 2;
-            message.goodsInfo.goodsPrice = parseFloat(message.quotedPrice).toFixed(2);
-            message.goodsInfo.lastPrice = parseFloat(message.quotedPrice).toFixed(2);
-            message.quotedPrice = '';
-            this.sendMessageOfGoodsPrice(message.goodsInfo);
-          }
-        })
-        .catch((err) => {
-          console.log('createQuotedPrice error', err);
-        });
+      }).then((res) => {
+        // console.log("createQuotedPriceApi", res)
+        if (res.code == 1) {
+          this.$msg('改价成功');
+          message.goodsInfo.goodsPriceType = 2;
+          message.goodsInfo.goodsPrice = parseFloat(message.quotedPrice).toFixed(2);
+          message.goodsInfo.lastPrice = parseFloat(message.quotedPrice).toFixed(2);
+          message.quotedPrice = '';
+          this.sendMessageOfGoodsPrice(message.goodsInfo);
+        } else {
+          this.$msg(res.msg);
+        }
+      }).catch((err) => {
+        console.log('createQuotedPrice error', err);
+      });
     },
     isExpired(timestamp) {
       if (isNaN(timestamp)) {
@@ -1209,6 +1210,31 @@ export default {
     },
     showKeyboard() {
       this.msgInputShow = true;
+    },
+    // 补全头像地址:若不含 http(s):// 则加上 this.$constant.imgUrl + '/'
+    formatAvatarUrl(avatar) {
+      try {
+        const value = avatar || '';
+        if (!value) {
+          return this.$constant.imgUrl + '/retail/default-img.png';
+        }
+        // 已是 http/https 绝对地址
+        if (/^https?:\/\//i.test(value)) {
+          return value;
+        }
+        
+        const base =this.$constant.imgUrl || '';
+        if (!base) {
+          return value;
+        }
+        const normalizedBase = base.replace(/\/+$/, '');
+        if (value.startsWith('/')) {
+          return normalizedBase + value;
+        }
+        return normalizedBase + '/' + value;
+      } catch (e) {
+        return avatar;
+      }
     }
   }
 };
@@ -1410,6 +1436,11 @@ export default {
       font-weight: bold;
       margin-top: 40rpx;
     }
+    .goods-price--secondary {
+      font-size: 24rpx;
+      color: #999999;
+      font-weight: normal;
+    }
   }
 
   .goods-footer {

+ 204 - 10
hdApp/src/admin/home/workbench.vue

@@ -12,6 +12,7 @@
     </view>
   </block>
   <block v-else>
+    <!-- ========== 完整功能界面 ========== -->
     <view class="app-main app-content" v-if="loginStyle  == 2">
       <template v-if="lookMoney == 1">
         <view class="today-overview-card">
@@ -258,17 +259,78 @@
       </template>
 
     </view>
+
+    <!--  ========== 简化功能界面  ========== -->
     <view class="order-page" v-else>
       <view class="ghs_list_page flex-col">
-        <view style="background-color:white;margin-bottom:20upx;">
-          <button class="admin-button-com mini-btn blue" style="margin-left:11upx;padding-top:17upx;padding-bottom:17upx;" @click="pageTo({url:'/admin/shop/myShop'})">我的门店</button>
-          <button class="admin-button-com mini-btn blue" style="margin-left:11upx;padding-top:17upx;padding-bottom:17upx;" @click="pageTo({url:'/admin/staff/list'})">我的员工</button>
-          <button class="admin-button-com mini-btn blue" style="margin-left:11upx;padding-top:17upx;padding-bottom:17upx;" @click="pageTo({url:'/admin/shop/add?id='+loginInfo.shopId})">我的地址</button>
-          <button class="admin-button-com mini-btn blue" style="margin-left:11upx;padding-top:17upx;padding-bottom:17upx;" @click="pageTo({url:'/admin/stat/cgItem'})">采购统计</button>
-          <button class="admin-button-com mini-btn blue" style="margin-left:11upx;padding-top:17upx;padding-bottom:17upx;" @click="goMore()">更多</button>
-        </view>
-        <view style="text-align:center;font-size:29upx;">您好,{{ loginInfo.admin && loginInfo.admin.name ? loginInfo.admin.name : '' }} {{ loginInfo.admin && loginInfo.admin.mobile ? loginInfo.admin.mobile : '' }}</view>
-        <view v-if="!$util.isEmpty(data.warning)" style="color:red;font-size:36upx;font-weight:bold;text-align: center;margin-bottom:15upx;margin-top:20upx;">{{ data.warning }}</view>
+        <view class="profile-section">
+          <view class="profile-header">
+            <view class="avatar-container">
+              <button
+                class="avatar-button"
+                open-type="chooseAvatar"
+                @chooseavatar="onChooseAvatar"
+              >
+                <image
+                  class="avatar"
+                  :src="shopAvatar()"
+                  mode="aspectFill"
+                />
+              </button>
+            </view>
+            <view class="user-info">
+              <text class="username">{{ (shopInfo && shopInfo.shopName == '首店') ? (shopInfo.merchantName || '') : (((shopInfo && shopInfo.merchantName) ? shopInfo.merchantName : '') + (shopInfo && shopInfo.shopName ? (' ' + shopInfo.shopName) : '')) }}</text>
+              <text class="user-id">ID {{ shopInfo && shopInfo.id ? shopInfo.id : '' }}</text>
+            </view>
+            <view class="more-icon">
+              <zui-svg-icon icon="home-more" :width="26" color="#FFFFFF" />
+            </view>
+          </view>
+
+          <view class="function-grid function-grid--overlay" style="padding: 25upx 0upx 30upx 0upx; display: flex; justify-content: space-around; gap: 12upx;">
+            <view class="function-item function-item--overlay" style="flex: 1; text-align: center; border: 1upx solid rgba(255,255,255,0.5); border-radius: 16upx; padding: 15upx 0;" @click="pageTo({url:'/admin/shop/myShop'})">
+              <view class="function-icon" style="font-size: 38upx; margin-bottom: 8upx;">
+                🏪
+              </view>
+              <text class="function-text function-text--overlay" style="font-size: 22upx;">店铺管理</text>
+            </view>
+            <view class="function-item function-item--overlay" style="flex: 1; text-align: center; border: 1upx solid rgba(255,255,255,0.5); border-radius: 16upx; padding: 15upx 0;" @click="pageTo({url:'/admin/staff/list'})">
+              <view class="function-icon" style="font-size: 38upx; margin-bottom: 8upx;">
+                👥
+              </view>
+              <text class="function-text function-text--overlay" style="font-size: 22upx;">我的员工</text>
+            </view>
+            <view class="function-item function-item--overlay" style="flex: 1; text-align: center; border: 1upx solid rgba(255,255,255,0.5); border-radius: 16upx; padding: 15upx 0;" @click="pageTo({url:'/admin/shop/add?id='+loginInfo.shopId})">
+              <view class="function-icon" style="font-size: 38upx; margin-bottom: 8upx;">
+                📍
+              </view>
+              <text class="function-text function-text--overlay" style="font-size: 22upx;">我的地址</text>
+            </view>
+            <view class="function-item function-item--overlay" style="flex: 1; text-align: center; border: 1upx solid rgba(255,255,255,0.5); border-radius: 16upx; padding: 15upx 0;" @click="pageTo({url:'/admin/stat/cgItem'})">
+              <view class="function-icon" style="font-size: 38upx; margin-bottom: 8upx;">
+                📊
+              </view>
+              <text class="function-text function-text--overlay" style="font-size: 22upx;">采购统计</text>
+            </view>
+            <view class="function-item function-item--overlay" style="flex: 1; text-align: center; border: 1upx solid rgba(255,255,255,0.5); border-radius: 16upx; padding: 15upx 0;" @click="goMore()">
+              <view class="function-icon" style="font-size: 38upx; margin-bottom: 8upx;">
+                ⋯
+              </view>
+              <text class="function-text function-text--overlay" style="font-size: 22upx;">更多</text>
+            </view>
+          </view>
+
+          </view>
+          <view style="text-align:center;font-size:28upx;color:#666;padding:16upx 30upx;margin:0 20upx;background: linear-gradient(to right, transparent, #f8f9fa, transparent);border-radius:30upx;">
+            <view style="display:inline-flex;align-items:center;background:#fff;padding:10upx 25upx;border-radius:20upx;box-shadow:0 2upx 8upx rgba(0,0,0,0.05);">
+              <text style="color:#09C567;margin-right:10upx;font-size:32upx;">👋</text>
+              <text style="color:#333;font-weight:500;font-size:28upx;">{{ loginInfo.admin && loginInfo.admin.name ? loginInfo.admin.name : '' }}</text>
+              <text style="margin:0 15upx;color:#eee;">|</text>
+              <text style="color:#666;font-size:28upx;">{{ loginInfo.admin && loginInfo.admin.mobile ? loginInfo.admin.mobile : '' }}</text>
+            </view>
+          </view>
+
+          <view v-if="!$util.isEmpty(data.warning)" style="color:red;font-size:36upx;font-weight:bold;text-align:center;margin-bottom:15upx;margin-top:20upx;">{{ data.warning }}</view>
 
         <template v-if="!$util.isEmpty(ad)">
           <block v-for="(adInfo, adIndex) in ad" :key="adIndex">
@@ -457,9 +519,10 @@ import { consoleIndex } from "@/api/workbench";
 import autoUpdateMixins from "@/mixins/autoUpdate";
 import { ghsList } from "@/api/purchase";
 import { changeDelStatus,hasMoreGhs } from "@/api/ghs";
-import {currentShop} from "@/api/shop";
+import {currentShop, uploadShopAvatar} from "@/api/shop";
 import { clearLogin,hasNotice } from "@/api/admin";
 import { unReadMsgCount } from "@/api/chat";
+import { APIHOST } from "@/config";
 export default {
   name: "workbench",
   mixins: [autoUpdateMixins],
@@ -543,6 +606,66 @@ export default {
 		}
 	},
   methods: {
+    onChooseAvatar(e){
+      if(this.loginInfo && this.loginInfo.staff && this.loginInfo.staff.founder != 2){
+        this.$msg('门店LOGO请老板修改')
+        return
+      }
+      if(e && e.detail && e.detail.avatarUrl){
+        let avatarUrl = e.detail.avatarUrl
+        this.updateShopAvatar(avatarUrl)
+      }else{
+        this.$msg('获取头像失败')
+      }
+    },
+    updateShopAvatar(avatarUrl){
+      let token = uni.getStorageSync('token') || ''
+      let url = APIHOST + '/upload/save-file'
+      uni.uploadFile({
+        url: url,
+        filePath: avatarUrl,
+        name: 'file',
+        header: { token: token },
+        formData: {},
+        success: (res) => {
+          try{
+            const ret = JSON.parse(res.data)
+            if(ret.code == 1){
+              this.shopInfo.avatar = ret.data.shortUrl
+              this.$msg('头像已更新')
+              uni.setStorageSync('shopAvatar', this.shopInfo.avatar)
+
+              //请后端,上传新的头像地址,同步更新xhGhsCustom里面的头像地址
+              uploadShopAvatar({ id: this.shopInfo.id, avatar: ret.data.shortUrl }).then(res => {
+                if(res.code == 1){
+                  // this.$msg('头像上传成功')
+                }else{
+                  // this.$msg(res.msg || '上传失败')
+                  console.log(res.msg || '上传失败')
+                }
+              })
+            }else{
+              this.$msg(ret.msg || '上传失败')
+            }
+          }catch(err){
+            this.$msg('服务异常')
+          }
+        },
+        fail: () => {
+          this.$msg('上传失败')
+        }
+      })
+    },
+    shopAvatar(){
+      const avatar = (this.shopInfo && this.shopInfo.avatar) ? this.shopInfo.avatar : '/hhb_small.png?x-oss-process=image/resize,m_fill,h_130,w_130'
+      if(!avatar){
+        return ''
+      }
+      if(/^https?:\/\//i.test(avatar)){
+        return avatar
+      }
+      return this.$constant.imgUrl + (avatar.indexOf('/') === 0 ? avatar : ('/' + avatar))
+    },
 		buyItem() {
 			let that = this
 			// #ifdef MP-WEIXIN
@@ -1365,6 +1488,77 @@ export default {
   position: relative;
   width: 750upx; 
   overflow: hidden;
+  .profile-section {
+    background-color: #09C567;
+    padding: 30upx 25upx 0upx 25upx;
+    border-radius: 0;
+    .profile-header {
+      display: flex;
+      align-items: center;
+      .avatar-container {
+        width: 120upx;
+        height: 120upx;
+        border-radius: 60upx;
+        overflow: hidden;
+        background-color: rgba(255,255,255,0.2);
+        border: 1upx solid #ffffff;
+        .avatar-button {
+          background: none !important;
+          border: none !important;
+          padding: 0 !important;
+          margin: 0 !important;
+          line-height: 1;
+          display: block;
+          width: 120upx;
+          height: 120upx;
+          color: transparent !important;
+          &::after { border: none !important; }
+        }
+        .avatar {
+          width: 120upx;
+          height: 120upx;
+          border-radius: 60upx;
+        }
+      }
+      .user-info {
+        flex: 1;
+        margin-left: 24upx;
+        .username {
+          display: block;
+          font-size: 36upx;
+          font-weight: bold;
+          color: #ffffff;
+        }
+        .user-id {
+          display: block;
+          margin-top: 6upx;
+          font-size: 26upx;
+          color: #ffffff;
+        }
+      }
+      .more-icon {
+        margin-left: 12upx;
+        width: 60upx;
+        height: 60upx;
+        border-radius: 30upx;
+        align-items: center;
+        justify-content: center;
+        display: flex;
+        // background: rgba(255,255,255,0.15);
+        // border: 1upx solid rgba(255,255,255,0.35);
+      }
+    }
+    .function-grid--overlay {
+      background: transparent;
+    }
+    .function-item--overlay {
+      border-color: rgba(255,255,255,0.5);
+      background: rgba(255,255,255,0.12);
+    }
+    .function-text--overlay {
+      color: #ffffff;
+    }
+  }
   .flex-col {
     display: flex;
     flex-direction: column;

+ 27 - 9
hdApp/src/admin/recharge/rechargeCode.vue

@@ -5,7 +5,13 @@
       </view>
       <view style="margin:50upx 0 0 30upx;font-size:30upx;">保存图片到手机,可分享或打印张贴</view>
       <view class="confirm-btn">
-        <button class="admin-button-com big blue" @click="downloadImg">保存图片</button>
+        <button class="admin-button-com big blue" @click="downloadImg(imgUrl)">保存图片</button>
+      </view>
+      <view class="poster_box">
+        <image :src="posterUrl" mode="widthFix" class="img_url"></image>
+      </view>
+      <view class="confirm-btn">
+        <button class="admin-button-com big blue" @click="downloadImg(posterUrl)">保存海报</button>
       </view>
     </view>
   </template>
@@ -17,6 +23,7 @@
     data() {
       return {
         imgUrl: '',
+        posterUrl: '',
         getappIdList: {}
       }
     },
@@ -30,7 +37,7 @@
           this.getappIdList = res.data
         });
       },
-      downloadImg(){
+      downloadImg(url){
         let that = this
         // #ifdef APP-PLUS
         let platformType = uni.getSystemInfoSync().platform
@@ -38,22 +45,22 @@
           let store = plus.navigator.checkPermission('android.permission.READ_EXTERNAL_STORAGE')
           if(store != 'authorized'){
             this.$util.confirmModal({content:'将申请您的存储权限,用于保存小程序码'},() => {
-              this.downloadImg2()
+              this.downloadImg2(url)
             })
           }else{
-            this.downloadImg2()
+            this.downloadImg2(url)
           }
         }else{
-          this.downloadImg2()
+          this.downloadImg2(url)
         }
         // #endif
         // #ifdef MP-WEIXIN
-        this.downloadImg2()
+        this.downloadImg2(url)
         // #endif
       },
-      downloadImg2(){
+      downloadImg2(url){
         uni.downloadFile({
-          url: this.imgUrl,
+          url: url,
           success: (res) => {
             if (res.statusCode === 200) {
               console.log(res)
@@ -70,7 +77,8 @@
       },
       getImg() {
         getRechargeCode().then(res => {
-          this.imgUrl = res.data.imgUrl
+          this.imgUrl = res.data.imgUrl,
+          this.posterUrl = res.data.posterUrl
         })
       }
     }
@@ -89,6 +97,16 @@
       margin: 100upx auto;
     }
   }
+  .poster_box{
+    width: 95%;
+    margin: 0 auto;
+    box-sizing: border-box;
+    &>.img_url{
+      width: 100%;
+      height: auto;
+      margin: 0 auto;
+    }
+  }
   // 按钮
   .confirm-btn {
     width: calc(100% - 60upx);

+ 7 - 0
hdApp/src/api/shop/index.js

@@ -69,6 +69,13 @@ export const delShop = data => {
 	return https.get('/shop/delete', data)
 }
 
+/** *
+ * 上传(更新)门店头像
+ */
+export const uploadShopAvatar = data => {
+	return https.post('/shop/update-shop-avatar', data)
+}
+
 /** *
  * 下载收款码
  */

+ 1 - 0
hdApp/src/assets/svg-icons/home/address.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758012649989" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10502" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 64c-175.8 0-318.3 134.6-318.3 300.5 0 222.9 195.5 430.8 280.1 510.3 21.3 20.1 55.7 20.3 77.4 0.6 85.1-77.2 279.2-278.7 279.2-510.9C830.3 198.6 687.8 64 512 64z m0 401.1c-56.9 0-103.1-46.2-103.1-103.1S455.1 258.9 512 258.9c56.9 0 103.1 46.2 103.1 103.1S568.9 465.1 512 465.1z" p-id="10503"></path><path d="M364.5 926.5a147.5 21.5 0 1 0 295 0 147.5 21.5 0 1 0-295 0Z" p-id="10504"></path></svg>

+ 1 - 0
hdApp/src/assets/svg-icons/home/more-2.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758013331870" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16231" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M682.666667 577.6384h236.305066C988.9792 577.6384 1024 612.6592 1024 682.666667v236.305066C1024 988.9792 988.9792 1024 918.971733 1024H682.666667c-70.007467 0-105.028267-35.0208-105.028267-105.028267V682.666667c0-70.007467 35.0208-105.028267 105.028267-105.028267zM105.028267 0H341.333333c70.007467 0 105.028267 35.0208 105.028267 105.028267V341.333333c0 70.007467-35.0208 105.028267-105.028267 105.028267H105.028267C35.0208 446.3616 0 411.3408 0 341.333333V105.028267C0 35.0208 35.0208 0 105.028267 0z m0 577.6384H341.333333c70.007467 0 105.028267 35.0208 105.028267 105.028267v236.305066C446.3616 988.9792 411.3408 1024 341.333333 1024H105.028267C35.0208 1024 0 988.9792 0 918.971733V682.666667c0-70.007467 35.0208-105.028267 105.028267-105.028267z m620.9536-156.228267c34.133333 34.133333 87.9616 34.133333 122.094933 0l136.533333-137.8304c34.133333-34.133333 34.133333-87.9616 0-122.094933l-136.533333-136.533333c-32.802133-32.836267-87.9616-32.836267-120.832 0l-137.796267 136.533333c-34.133333 34.133333-34.133333 87.9616 0 122.094933l136.533334 137.8304z" fill="#38BFD4" p-id="16232"></path></svg>

+ 1 - 0
hdApp/src/assets/svg-icons/home/more.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758013104896" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15229" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M323.8912 131.072a51.2 51.2 0 0 1 71.4752-72.8064l6.5536 6.4512L753.664 478.8224a51.2 51.2 0 0 1 5.12 59.1872l-5.4272 7.5776-351.744 405.0944A51.2 51.2 0 0 1 318.8736 890.88l5.4272-7.4752 322.7648-371.9168L323.8912 131.072z" fill="#000000" fill-opacity=".45" p-id="15230"></path></svg>

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
hdApp/src/assets/svg-icons/home/shop.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
hdApp/src/assets/svg-icons/home/staffs.svg


+ 1 - 0
hdApp/src/assets/svg-icons/home/statistics.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758012699298" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14222" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="14223"></path><path d="M921.6 546.133333A443.733333 443.733333 0 1 1 477.866667 102.4v443.733333h443.733333z" fill="#11AA66" p-id="14224"></path><path d="M989.866667 477.866667A443.733333 443.733333 0 0 0 546.133333 34.133333v443.733334h443.733334z" fill="#FFAA44" p-id="14225"></path></svg>

+ 6 - 3
hdApp/src/components/RichMediaViewer/index.vue

@@ -121,6 +121,7 @@
                       @error="onPreviewImageError(index)"
                       @load="onPreviewImageLoad(index)"
                       @longpress="saveImage"
+                      @click="closeImagePreview"
                     />
                   </view>
                 </movable-view>
@@ -148,6 +149,7 @@
                   @error="onPreviewImageError(0)"
                   @load="onPreviewImageLoad(0)"
                   @longpress="saveImage"
+                  @click="closeImagePreview"
                 />
               </view>
             </movable-view>
@@ -392,8 +394,9 @@ export default {
     // 点击遮罩层关闭预览
     handleMaskClick() {
       if (this.currentScale === 1) {
-        this.closeImagePreview();
       }
+
+      this.closeImagePreview();
     },
 
     // 关闭图片预览
@@ -535,7 +538,7 @@ export default {
 }
 
 .content-block {
-  margin-bottom: 40rpx;
+  margin-bottom: 25rpx;
 
   &:last-child {
     margin-bottom: 0;
@@ -685,7 +688,7 @@ export default {
   right: 0;
   bottom: 0;
   background-color: rgba(0, 0, 0, 0.9);
-  z-index: 9999;
+  z-index: 999999;
   display: flex;
   flex-direction: column;
 }

+ 1 - 1
hdApp/src/plugins/luch-request_0.0.7/request.js

@@ -145,7 +145,7 @@ class Request {
 							}
 							reject(response.data)
 						} else if (response.data.code && response.data.code == -1) {
-
+							resolve(response.data)
 						} else if (response.data.code == 2) {
 							uni.removeStorageSync('token')
 							uni.removeStorageSync('shopUser')

Разница между файлами не показана из-за своего большого размера
+ 15 - 1
hdApp/src/static/svg-icons-lib.js


+ 4 - 2
mallApp/src/components/RichMediaViewer/index.vue

@@ -120,6 +120,7 @@
                       class="preview-image"
                       @error="onPreviewImageError(index)"
                       @load="onPreviewImageLoad(index)"
+                      @click="closeImagePreview"
                     />
                   </view>
                 </movable-view>
@@ -146,6 +147,7 @@
                   class="preview-image single"
                   @error="onPreviewImageError(0)"
                   @load="onPreviewImageLoad(0)"
+                  @click="closeImagePreview"
                 />
               </view>
             </movable-view>
@@ -490,7 +492,7 @@ export default {
 }
 
 .content-block {
-  margin-bottom: 40rpx;
+  margin-bottom: 25rpx;
 
   &:last-child {
     margin-bottom: 0;
@@ -640,7 +642,7 @@ export default {
   right: 0;
   bottom: 0;
   background-color: rgba(0, 0, 0, 0.9);
-  z-index: 9999;
+  z-index: 999999;
   display: flex;
   flex-direction: column;
 }

+ 30 - 5
mallApp/src/pages/chat/chatPage.vue

@@ -27,7 +27,7 @@
         <!-- 左侧消息(商家/其他用户) -->
         <view class="message-left" v-if="!message.isMine">
           <view class="avatar-wrapper">
-            <image class="avatar" :src="message.avatar" mode="aspectFill" />
+            <image class="avatar" :src="formatAvatarUrl(message.avatar)" mode="aspectFill" />
           </view>
 
           <view v-if="message.messageType == 'text'|| message.messageType != 'goods'" class="message-content-wrapper">
@@ -111,7 +111,7 @@
             <view class="message-time align-right">{{ message.time }}</view>
           </view>
           <view class="avatar-wrapper">
-            <image class="avatar" :src="message.avatar" mode="aspectFill" />
+            <image class="avatar" :src="formatAvatarUrl(message.avatar)" mode="aspectFill" />
           </view>
         </view>
       </view>
@@ -874,7 +874,7 @@ export default {
           time: this.getCurrentTime(),
           isMine: false, // 既然不是自己的消息,就标记为false
           username: messageData.name || messageData.username || "未知用户",
-          avatar: messageData.avatar || this.$constant.imgUrl + "/retail/default-img.png",
+          avatar: messageData.avatar || "",
           timestamp: messageData.timestamp,
           messageType: messageType,
         };
@@ -914,7 +914,7 @@ export default {
       }
 
       const messageContent = this.inputText.trim();
-      const avatar = this.currentUser.avatar || this.$constant.imgUrl + '/retail/default-img.png';
+      const avatar = this.currentUser.avatar || '';
       const timestamp = Math.floor(Date.now() / 1000);
 
       try {
@@ -1057,7 +1057,7 @@ export default {
         });
         return;
       }
-      const avatar = this.currentUser.avatar || this.$constant.imgUrl + "/retail/default-img.png";
+      const avatar = this.currentUser.avatar || "";
       const timestamp = Math.floor(Date.now() / 1000);
       // 生成一个唯一的字符串,结合时间戳和随机数
       // const randomString = timestamp.toString() + "_" + Math.random().toString(36).substring(2, 10);
@@ -1302,6 +1302,31 @@ export default {
         }
       });
     },
+    // 补全头像地址:若不含 http(s):// 则加上 this.$constant.imgUrl + '/'
+    formatAvatarUrl(avatar) {
+      try {
+        const value = avatar || '';
+        if (!value) {
+          return this.$constant.imgUrl + '/retail/default-img.png';
+        }
+        // 已是 http/https 绝对地址
+        if (/^https?:\/\//i.test(value)) {
+          return value;
+        }
+        
+        const base =this.$constant.imgUrl || '';
+        if (!base) {
+          return value;
+        }
+        const normalizedBase = base.replace(/\/+$/, '');
+        if (value.startsWith('/')) {
+          return normalizedBase + value;
+        }
+        return normalizedBase + '/' + value;
+      } catch (e) {
+        return avatar;
+      }
+    },
   },
 };
 </script>

+ 27 - 0
mallApp/src/pages/goods/detail.vue

@@ -327,6 +327,33 @@ export default {
       });
     },
     showPopup() {
+      const currentShop = uni.getStorageSync('currentShop');
+
+      if (this.$util.isEmpty(currentShop)) {
+        console.error("当前所在门店数据异常");
+      } else if (currentShop.openStartTime !='' && currentShop.openEndTime !='') {
+        // 当前小时与分钟是否在营业时间范围内
+        const currentTime = new Date();
+        const currentHour = currentTime.getHours();
+        const currentMinute = currentTime.getMinutes();
+
+        const openTime = currentShop.openStartTime; //营业开始时间,例如:3:30
+        const openHour = parseInt(openTime.split(':')[0]);
+        const openMinute = parseInt(openTime.split(':')[1]);
+        const closeTime = currentShop.openEndTime; //营业结束时间,例如:18:30
+        const closeHour = parseInt(closeTime.split(':')[0]);
+        const closeMinute = parseInt(closeTime.split(':')[1]);
+
+        if (currentHour < openHour || (currentHour === openHour && currentMinute < openMinute)) {
+          this.$msg("当前时间不在营业时间范围内");
+          return;
+        }
+        if (currentHour > closeHour || (currentHour === closeHour && currentMinute > closeMinute)) {
+          this.$msg("当前时间不在营业时间范围内");
+          return;
+        }
+      }
+
       this.popupShow = true;
     },
     hidePopup() {

+ 2 - 0
mallApp/src/pages/home/category.vue

@@ -209,6 +209,8 @@ export default {
 						}
 					}
 
+					//保存当前所在门店信息
+					uni.setStorageSync('currentShop', res.data.info)
 				}
 			})
     },

+ 3 - 0
mallApp/src/pages/home/mall.vue

@@ -238,6 +238,9 @@ export default {
             this.shopName = name;
             this.shopImg = res.data.info.avatar;
             this.shopInfo = res.data.info;
+
+            //保存当前所在门店信息
+            uni.setStorageSync('currentShop', this.shopInfo)
           }
 
           if (res.data.hd && Number(res.data.hd.id) > 0) {

+ 3 - 0
mallApp/src/pages/item/item.vue

@@ -343,6 +343,9 @@ export default {
 							this.hdId = res.data.hd.id
 						}
 					}
+
+					//保存当前所在门店信息
+					uni.setStorageSync('currentShop', res.data.info)
 				}
 			})
 			this.initData()

Некоторые файлы не были показаны из-за большого количества измененных файлов