Browse Source

Merge branch 'feature_20260413_陈列照片提交后修改' into release

# Conflicts:
#	src/views/historicalVisit/perfectStore.vue
zhujindu 21 hours ago
parent
commit
2b32be8eb0
1 changed files with 427 additions and 160 deletions
  1. 427 160
      src/views/historicalVisit/perfectStore.vue

+ 427 - 160
src/views/historicalVisit/perfectStore.vue

@@ -2,189 +2,288 @@
   <div class="perfectStore">
     <!--        顶部条-->
     <van-nav-bar class="navBar" title="AI完美门店报告" left-arrow @click-left="onClickLeft">
-      <template #right>
-        <span
-          style="
-            color: white;
-            background: #74a4d9;
-            display: block;
-            padding: 6px 10px;
-            border-radius: 6px;
-          "
-          @click="editorFn"
-          v-if="!remarkShow && sameDay"
-          >编辑</span
-        >
+      <template #right v-if="list">
+        <template v-if="list.wanmeiStore">
+          <span
+            style="
+              color: white;
+              background: #74a4d9;
+              display: block;
+              padding: 6px 10px;
+              border-radius: 6px;
+            "
+            @click="editorFn"
+            v-if="!remarkShow && sameDay"
+            >编辑
+          </span>
+        </template>
+        <template v-else>
+          <!-- 新的生成中动画组件 -->
+          <div class="ai-generating">
+            <!-- AI 星星图标 -->
+            <div class="ai-icon">
+              <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <defs>
+                  <linearGradient id="starGrad" x1="0%" y1="0%" x2="100%" y2="100%">
+                    <stop offset="0%" stop-color="#6366f1" />
+                    <stop offset="100%" stop-color="#a855f7" />
+                  </linearGradient>
+                </defs>
+                <!-- 大星星 -->
+                <path
+                  d="M10 2 L11.2 7.8 L17 9 L11.2 10.2 L10 16 L8.8 10.2 L3 9 L8.8 7.8 Z"
+                  fill="url(#starGrad)"
+                  style="
+                    animation: starPulse 1.8s ease-in-out infinite;
+                    transform-origin: 10px 9px;
+                  ">
+                  <animateTransform
+                    attributeName="transform"
+                    type="scale"
+                    values="1;1.12;1"
+                    dur="1.8s"
+                    repeatCount="indefinite"
+                    additive="sum"
+                    calcMode="ease" />
+                </path>
+                <!-- 小星星 -->
+                <circle cx="15.5" cy="4.5" r="1.5" fill="#a855f7" opacity="0.7">
+                  <animate
+                    attributeName="opacity"
+                    values="0.4;1;0.4"
+                    dur="1.4s"
+                    repeatCount="indefinite" />
+                  <animate
+                    attributeName="r"
+                    values="1.2;1.8;1.2"
+                    dur="1.4s"
+                    repeatCount="indefinite" />
+                </circle>
+                <!-- 微星 -->
+                <circle cx="4.5" cy="14.5" r="1" fill="#6366f1" opacity="0.5">
+                  <animate
+                    attributeName="opacity"
+                    values="0.3;0.9;0.3"
+                    dur="1.8s"
+                    begin="0.5s"
+                    repeatCount="indefinite" />
+                  <animate
+                    attributeName="r"
+                    values="0.8;1.4;0.8"
+                    dur="1.8s"
+                    begin="0.5s"
+                    repeatCount="indefinite" />
+                </circle>
+              </svg>
+            </div>
+
+            <!-- 三个跳动点 -->
+            <div class="dots-container">
+              <div class="dot"></div>
+              <div class="dot"></div>
+              <div class="dot"></div>
+            </div>
+
+            <!-- 渐变文字 -->
+            <span class="gen-text">生成中</span>
+          </div>
+          <!-- <span style="font-size: 18px; color: #1989fa; display: flex; align-items: center"
+            ><img width="35px" :src="require('@/assets/wanmeiStoreImg.gif')" />生成中</span
+          > -->
+        </template>
       </template>
     </van-nav-bar>
     <!--        主体内容-->
     <div class="container" v-if="list">
-      <div class="lineGrey"></div>
-      <div class="card mt10">
-        <div class="title">
-          {{ list.storeName }}(<span style="color: #1989fa">{{ list.storeCode }}</span
-          >)
-        </div>
-        <div class="subtitle">地址:{{ list.addressLine }}</div>
-        <div class="subtitle">拜访人:{{ list.nickName }}</div>
-        <div class="subtitle">拜访日期:{{ list.startTime }}~{{ list.stopTime }}</div>
-        <div class="subtitle" v-if="list.dwellTime">拜访时长:{{ list.dwellTime }}</div>
-        <!-- 分销店 -->
-        <template
-          v-if="
-            verifyStoreType(list.storeCategory) && verifyStoreType(list.storeCategory).type == 'fxd'
-          ">
-          <div class="subtitle" style="display: flex">
-            <div class="label" style="width: 75px">经销商:</div>
-            <div class="valuue TCFXList">
-              <div
-                class="sfaStoreChainsContactList"
-                v-for="(item, index) in list.sfaStoreChainsContactList"
-                :key="index">
-                {{ item.categoryDescribe }}
-                {{ item.chainCode }}
-                {{ item.chainName }}
+      <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+        <div class="lineGrey"></div>
+        <div class="card mt10">
+          <div class="title">
+            {{ list.storeName }}(<span style="color: #1989fa">{{ list.storeCode }}</span
+            >)
+          </div>
+          <div class="subtitle">地址:{{ list.addressLine }}</div>
+          <div class="subtitle">拜访人:{{ list.nickName }}</div>
+          <div class="subtitle">拜访日期:{{ list.startTime }}~{{ list.stopTime }}</div>
+          <div class="subtitle" v-if="list.dwellTime">拜访时长:{{ list.dwellTime }}</div>
+          <!-- 分销店 -->
+          <template
+            v-if="
+              verifyStoreType(list.storeCategory) &&
+              verifyStoreType(list.storeCategory).type == 'fxd'
+            ">
+            <div class="subtitle" style="display: flex">
+              <div class="label" style="width: 75px">经销商:</div>
+              <div class="valuue TCFXList">
+                <div
+                  class="sfaStoreChainsContactList"
+                  v-for="(item, index) in list.sfaStoreChainsContactList"
+                  :key="index">
+                  {{ item.categoryDescribe }}
+                  {{ item.chainCode }}
+                  {{ item.chainName }}
+                </div>
               </div>
             </div>
-          </div>
-        </template>
-        <template v-else>
-          <div class="subtitle">经销商:{{ list.chainName }}</div>
-        </template>
-        <div class="subtitle" v-if="updateTimeShow">更新时间:{{ list.updateTime }}</div>
-      </div>
-      <!-- 特殊任务展示 -->
-      <!-- 店招 -->
-      <div class="shopSign specialTask" v-if="shopSignDetail && shopSignDetail.qualifiedState">
-        <div class="specialTaskLeft" @click="openPerfectStoreSign(shopSignDetail)">
-          <div class="SignText">AI 店招识别</div>
-          <div class="signContent">
-            <div class="" style="display: flex">
-              <van-button round type="primary" v-if="shopSignDetail.qualifiedState == '1'"
-                >通过</van-button
-              >
-              <van-button round type="danger" v-if="shopSignDetail.qualifiedState == '0'"
-                >不通过</van-button
-              >
-              <div class="jiantou">
-                <van-icon name="arrow" />
+          </template>
+          <template v-else>
+            <div class="subtitle">经销商:{{ list.chainName }}</div>
+          </template>
+          <div class="subtitle" v-if="updateTimeShow">更新时间:{{ list.updateTime }}</div>
+        </div>
+        <!-- 特殊任务展示 -->
+        <!-- 店招 -->
+        <div class="shopSign specialTask" v-if="shopSignDetail && shopSignDetail.qualifiedState">
+          <div class="specialTaskLeft" @click="openPerfectStoreSign(shopSignDetail)">
+            <div class="SignText">AI 店招识别</div>
+            <div class="signContent">
+              <div class="" style="display: flex">
+                <template v-if="shopSignDetail.qualifiedState != null">
+                  <van-button round type="primary" v-if="shopSignDetail.qualifiedState == '1'"
+                    >通过</van-button
+                  >
+                  <van-button round type="danger" v-else-if="shopSignDetail.qualifiedState == '0'"
+                    >不通过</van-button
+                  >
+                  <div class="jiantou">
+                    <van-icon name="arrow" />
+                  </div>
+                </template>
+                <template v-else>
+                  <span class="AISpan">AI识别中</span>
+                </template>
               </div>
             </div>
           </div>
         </div>
-      </div>
-      <!-- 调色机 -->
-      <div class="TSJBox specialTask" v-if="tiaoSJDetail">
-        <div class="specialTaskLeft" @click="openPerfectStoreTSJ(tiaoSJDetail)">
-          <div class="SignText">AI 调色机识别</div>
-          <div class="signContent">
-            <div class="" style="display: flex">
-              <van-button round type="primary" v-if="tiaoSJDetail.qualifiedState == '1'"
-                >通过</van-button
-              >
-              <van-button round type="danger" v-if="tiaoSJDetail.qualifiedState == '0'"
-                >不通过</van-button
-              >
-              <div class="jiantou">
-                <van-icon name="arrow" />
+        <!-- 调色机 -->
+        <div class="TSJBox specialTask" v-if="tiaoSJDetail">
+          <div class="specialTaskLeft" @click="openPerfectStoreTSJ(tiaoSJDetail)">
+            <div class="SignText">AI 调色机识别</div>
+            <div class="signContent">
+              <div class="" style="display: flex">
+                <template v-if="tiaoSJDetail.qualifiedState != null">
+                  <van-button round type="primary" v-if="tiaoSJDetail.qualifiedState == '1'"
+                    >通过</van-button
+                  >
+                  <van-button round type="danger" v-if="tiaoSJDetail.qualifiedState == '0'"
+                    >不通过</van-button
+                  >
+                  <div class="jiantou">
+                    <van-icon name="arrow" />
+                  </div>
+                </template>
+                <template v-else>
+                  <span class="AISpan">AI识别中</span>
+                </template>
               </div>
             </div>
           </div>
         </div>
-      </div>
-      <!-- 陈列SKU个数: -->
-      <div class="TSJBox specialTask" v-if="list.isSku == '是'">
-        <div class="specialTaskLeft">
-          <div class="SignText">
-            AI SKU个数识别: <span v-if="list.skuNum">{{ list.skuNum }}个</span>
-          </div>
-          <div class="signContent" @click="toSkuRecognize">
-            <div class="" style="display: flex">
-              <span style="color: rgb(25, 137, 250)">查看详情</span>
-              <div class="jiantou">
-                <van-icon name="arrow" />
+        <!-- 陈列SKU个数: -->
+        <div class="TSJBox specialTask" v-if="list.isSku == '是'">
+          <div class="specialTaskLeft">
+            <div class="SignText">
+              AI SKU个数识别: <span v-if="list.skuNum">{{ list.skuNum }}个</span>
+            </div>
+            <div class="signContent" @click="toSkuRecognize">
+              <div class="" style="display: flex">
+                <template v-if="list.wanmeiStore">
+                  <span style="color: rgb(25, 137, 250)">查看详情</span>
+                  <div class="jiantou">
+                    <van-icon name="arrow" />
+                  </div>
+                </template>
+                <template v-else>
+                  <span class="AISpan">AI识别中</span>
+                </template>
               </div>
             </div>
           </div>
         </div>
-      </div>
-      <!-- 目前陈列任务 -->
-      <div class="TSJBox specialTask" v-if="taskTypeArr && taskTypeArr.length">
-        <div class="specialTaskLeft">
-          <div class="SignText">AI 陈列任务识别</div>
+        <!-- 目前陈列任务 -->
+        <div class="TSJBox specialTask" v-if="taskTypeArr && taskTypeArr.length">
+          <div class="specialTaskLeft">
+            <div class="SignText">AI 陈列任务识别</div>
+          </div>
         </div>
-      </div>
-      <div class="SKUBox" v-if="taskTypeArr && taskTypeArr.length">
-        <div class="SKUList">
-          <div
-            class="itemList"
-            v-for="(value, index) in taskTypeArr"
-            @click="historiStoreVisit(value)">
-            <div class="itemTitle">{{ convertToChinese(index + 1) }}、{{ value.taskName }}</div>
-            <div class="" style="display: flex">
-              <van-button round type="primary" v-if="value.taskPhotoConditionPassed == 1"
-                >通过</van-button
-              >
-              <van-button round type="danger" v-if="value.taskPhotoConditionPassed == 0"
-                >不通过</van-button
-              >
-              <div class="jiantou">
-                <van-icon name="arrow" />
+        <div class="SKUBox" v-if="taskTypeArr && taskTypeArr.length">
+          <div class="SKUList">
+            <div
+              class="itemList"
+              v-for="(value, index) in taskTypeArr"
+              @click="historiStoreVisit(value)">
+              <div class="itemTitle">{{ convertToChinese(index + 1) }}、{{ value.taskName }}</div>
+              <div class="" style="display: flex">
+                <template v-if="value.taskPhotoConditionPassed != null">
+                  <van-button round type="primary" v-if="value.taskPhotoConditionPassed == 1"
+                    >通过</van-button
+                  >
+                  <van-button round type="danger" v-if="value.taskPhotoConditionPassed == 0"
+                    >不通过</van-button
+                  >
+                  <div class="jiantou">
+                    <van-icon name="arrow" />
+                  </div>
+                </template>
+                <template v-else>
+                  <span class="AISpan">AI识别中</span>
+                </template>
               </div>
             </div>
           </div>
         </div>
-      </div>
 
-      <div style="padding: 15px 16px; font-size: 16px; font-weight: bold; background: #f5f5f5">
-        其他拜访任务
-      </div>
-      <div class="card" v-if="list.visitSource != 2">
-        <div
-          class="info"
-          style="
-            font-size: 14px;
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            padding: 3px;
-          "
-          v-for="(item, index) in list.sfaTaskList"
-          :key="index"
-          v-if="
-            item.photoIdentifyType != '1' && item.photoIdentifyType != '3' && item.taskType != '5'
-          "
-          @click="historiStoreVisit(item, index)">
-          <p style="flex: 1; margin: 0">
-            {{ item.taskName }}
-          </p>
+        <div style="padding: 15px 16px; font-size: 16px; font-weight: bold; background: #f5f5f5">
+          其他拜访任务
+        </div>
+        <div class="card" v-if="list.visitSource != 2">
+          <div
+            class="info"
+            style="
+              font-size: 14px;
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+              padding: 3px;
+            "
+            v-for="(item, index) in list.sfaTaskList"
+            :key="index"
+            v-if="
+              item.photoIdentifyType != '1' && item.photoIdentifyType != '3' && item.taskType != '5'
+            "
+            @click="historiStoreVisit(item, index)">
+            <p style="flex: 1; margin: 0">
+              {{ item.taskName }}
+            </p>
 
-          <div class="taskPhotoConditionPassed" @click.stop>
-            <el-popover
-              :popper-class="item.taskPhotoConditionPassed == 1 ? 'zpoverSuccess' : 'zpover'"
-              placement="bottom"
-              width="120"
-              trigger="click"
-              :content="
-                item.taskPhotoConditionPassed == 1
-                  ? '陈列奖励案拍照AI识别通过'
-                  : '陈列奖励案拍照AI识别不通过'
-              ">
-              <div class="taskPhotoConditionPassed" slot="reference">
-                <img
-                  v-if="item.taskPhotoConditionPassed == 1"
-                  :src="require('@/assets/taskPhotoSu.png')" />
-                <img
-                  v-if="item.taskPhotoConditionPassed == 0"
-                  :src="require('@/assets/taskPhotoErr.png')" />
-              </div>
-            </el-popover>
+            <div class="taskPhotoConditionPassed" @click.stop>
+              <el-popover
+                :popper-class="item.taskPhotoConditionPassed == 1 ? 'zpoverSuccess' : 'zpover'"
+                placement="bottom"
+                width="120"
+                trigger="click"
+                :content="
+                  item.taskPhotoConditionPassed == 1
+                    ? '陈列奖励案拍照AI识别通过'
+                    : '陈列奖励案拍照AI识别不通过'
+                ">
+                <div class="taskPhotoConditionPassed" slot="reference">
+                  <img
+                    v-if="item.taskPhotoConditionPassed == 1"
+                    :src="require('@/assets/taskPhotoSu.png')" />
+                  <img
+                    v-if="item.taskPhotoConditionPassed == 0"
+                    :src="require('@/assets/taskPhotoErr.png')" />
+                </div>
+              </el-popover>
+            </div>
+            <p class="arrowdetils1">
+              <van-icon name="arrow" />
+            </p>
           </div>
-          <p class="arrowdetils1">
-            <van-icon name="arrow" />
-          </p>
         </div>
-      </div>
+      </van-pull-refresh>
     </div>
   </div>
 </template>
@@ -226,6 +325,7 @@ export default {
       shopSignDetail: null,
       tiaoSJDetail: null,
       taskTypeArr: null,
+      refreshing: false,
     };
   },
   activated() {
@@ -241,6 +341,11 @@ export default {
     this.getPhotoTypeList();
   },
   methods: {
+    onRefresh() {
+      this.refreshing = true;
+      this.toastLoading(0, '加载中...', true);
+      this.getVisitsDetailFn();
+    },
     resultCorrect(resultCorrect) {
       let data = this.AIResultOption.find((item) => item.dictValue == resultCorrect);
       return data ? data.dictLabel : '';
@@ -344,6 +449,9 @@ export default {
       this.taskTypeArr = null;
       getVisitsDetailPerfectStore({ visitsId: this.$route.query.visitId }).then((res) => {
         this.toastLoading().clear();
+        if (this.refreshing) {
+          this.refreshing = false;
+        }
         if (res.code == 200) {
           this.list = res.data;
           this.filterSfaTaskList(this.list.sfaTaskList);
@@ -407,6 +515,9 @@ export default {
     },
     historiStoreVisit(val, index) {
       if (val.taskType == '5') {
+        if (val.taskPhotoConditionPassed == null) {
+          return;
+        }
         this.$router.push({
           path: '/perfectStoreTask',
           query: {
@@ -427,10 +538,12 @@ export default {
       sessionStorage.setItem('collectionItemList', JSON.stringify(val.collectionItemList));
     },
     toSkuRecognize() {
-      this.$router.push({
-        path: '/perfectStoreSku',
-        query: { visitId: this.visitsId },
-      });
+      if (this.list.wanmeiStore) {
+        this.$router.push({
+          path: '/perfectStoreSku',
+          query: { visitId: this.visitsId },
+        });
+      }
     },
     onClickLeft() {
       if (this.$route.query.token) {
@@ -445,12 +558,18 @@ export default {
       ImagePreview([url]);
     },
     openPerfectStoreSign(val) {
+      if (val.qualifiedState == null) {
+        return;
+      }
       this.$router.push({
         path: '/perfectStoreSign',
         query: { visitId: this.visitsId, taskId: val.taskId },
       });
     },
     openPerfectStoreTSJ(val) {
+      if (val.qualifiedState == null) {
+        return;
+      }
       let tiaoSJArr = this.list.sfaTaskList.filter((val) => val.photoIdentifyType == '3');
       let taskIds = tiaoSJArr.map((item) => item.taskId).join(',');
       this.$router.push({
@@ -653,4 +772,152 @@ export default {
   border: 0 !important;
   box-shadow: none !important;
 }
+.perfectStore {
+  /* ===== AI 生成中动画 ===== */
+  .ai-generating {
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    background: linear-gradient(135deg, rgba(99, 102, 241, 0.08), rgba(168, 85, 247, 0.08));
+    border: 1px solid rgba(99, 102, 241, 0.2);
+    border-radius: 20px;
+    padding: 3px 10px 3px 7px;
+    margin-right: -3px;
+  }
+
+  /* AI 图标 - 彩色渐变扫描动效 */
+  .ai-icon {
+    width: 20px;
+    height: 20px;
+    position: relative;
+    flex-shrink: 0;
+  }
+  .ai-icon svg {
+    width: 20px;
+    height: 20px;
+  }
+
+  /* 三个跳动圆点 */
+  .dots-container {
+    display: flex;
+    align-items: center;
+    gap: 3px;
+  }
+  .dot {
+    width: 5px;
+    height: 5px;
+    border-radius: 50%;
+    background: linear-gradient(135deg, #6366f1, #a855f7);
+    animation: dotBounce 1.4s ease-in-out infinite;
+  }
+  .dot:nth-child(1) {
+    animation-delay: 0s;
+  }
+  .dot:nth-child(2) {
+    animation-delay: 0.2s;
+  }
+  .dot:nth-child(3) {
+    animation-delay: 0.4s;
+  }
+
+  @keyframes dotBounce {
+    0%,
+    60%,
+    100% {
+      transform: translateY(0);
+      opacity: 0.5;
+    }
+    30% {
+      transform: translateY(-5px);
+      opacity: 1;
+    }
+  }
+
+  /* 生成中文字 */
+  .gen-text {
+    font-size: 12px;
+    font-weight: 500;
+    background: linear-gradient(90deg, #6366f1, #a855f7, #6366f1);
+    background-size: 200% auto;
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+    animation: textShimmer 2s linear infinite;
+  }
+  @keyframes textShimmer {
+    0% {
+      background-position: 0% center;
+    }
+    100% {
+      background-position: 200% center;
+    }
+  }
+
+  /* AI 图标内部的星星动画 */
+  @keyframes starPulse {
+    0%,
+    100% {
+      transform: scale(1) rotate(0deg);
+      opacity: 0.9;
+    }
+    50% {
+      transform: scale(1.15) rotate(15deg);
+      opacity: 1;
+    }
+  }
+  @keyframes starOrbit {
+    0% {
+      transform: rotate(0deg) translateX(5px) rotate(0deg);
+    }
+    100% {
+      transform: rotate(360deg) translateX(5px) rotate(-360deg);
+    }
+  }
+
+  /* 光晕扫描 */
+  .scan-line {
+    position: absolute;
+    top: 0;
+    left: -100%;
+    width: 60%;
+    height: 100%;
+    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
+    animation: scanAnim 2s ease-in-out infinite;
+    border-radius: 50%;
+  }
+  @keyframes scanAnim {
+    0% {
+      left: -60%;
+    }
+    100% {
+      left: 160%;
+    }
+  }
+  .AISpan {
+    display: flex;
+    align-items: center;
+    color: #1989fa;
+    margin-right: 5px;
+    font-size: 12px;
+  }
+  .AISpan::before {
+    content: '';
+    display: inline-block;
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background: #2979ff;
+    animation: blink 1.2s ease-in-out infinite;
+    margin-right: 5px;
+  }
+  @keyframes blink {
+    0%,
+    100% {
+      opacity: 0.3;
+    }
+    50% {
+      opacity: 1;
+    }
+  }
+}
 </style>