armg пре 1 недеља
родитељ
комит
9a61619b58

+ 8 - 0
src/api/indexAI.js

@@ -47,6 +47,8 @@ export function CreateDesign(query) {
     const auth = getAuthCredentials();
     query.append('loginMark', auth.loginMark);
     query.append('token', auth.token);
+    const userWecomType = sessionStorage.getItem("userWecomType") ? Number(sessionStorage.getItem("userWecomType")) : 5;
+    query.append('wecomType', userWecomType);
     return request({
         url: '/aidesign/outside/CreateDesign',
         method: 'post',
@@ -127,6 +129,8 @@ export function GetDictList(query) {
     const auth = getAuthCredentials();
     query.append('loginMark', auth.loginMark);
     query.append('token', auth.token);
+    const userWecomType = sessionStorage.getItem("userWecomType") ? Number(sessionStorage.getItem("userWecomType")) : 5;
+    query.append('wecomType', userWecomType);
     return request({
         url: '/aidesign/GetBaseInfo',
         method: 'post',
@@ -142,6 +146,8 @@ export function insideCreateDesign(query) {
     const auth = getAuthCredentials();
     query.append('loginMark', auth.loginMark);
     query.append('token', auth.token);
+    const userWecomType = sessionStorage.getItem("userWecomType") ? Number(sessionStorage.getItem("userWecomType")) : 5;
+    query.append('wecomType', userWecomType);
     return request({
         url: '/aidesign/inside/CreateDesign',
         method: 'post',
@@ -214,6 +220,8 @@ export function diagCreateDesign(query) {
     const auth = getAuthCredentials();
     query.append('loginMark', auth.loginMark);
     query.append('token', auth.token);
+    const userWecomType = sessionStorage.getItem("userWecomType") ? Number(sessionStorage.getItem("userWecomType")) : 5;
+    query.append('wecomType', userWecomType);
     return request({
         url: '/aidesign/diag/CreateDesign',
         method: 'post',

+ 2 - 5
src/router/index.ts

@@ -1,6 +1,7 @@
 import Vue from "vue";
 import VueRouter from "vue-router";
 import layout from "@/layout/index.vue";
+import { checkWxWorkEnvAndUserCache } from '@/utils/index'
 import { checkLoginStatus, initGuidInfo, doWecomLogin, getQyCode } from '@/utils/wecomLogin.ts';
 Vue.use(VueRouter);
 
@@ -92,6 +93,7 @@ router.beforeEach((to, from, next) => {
     return;
   }
   try {
+    checkWxWorkEnvAndUserCache();//先判断企微环境
     initGuidInfo();
     const isLoggedIn = checkLoginStatus();
     if (isLoggedIn) {
@@ -117,11 +119,6 @@ router.beforeEach((to, from, next) => {
     } else {
       console.log(`访问 ${to.path} 需登录,正在跳转到授权页面`);
       getQyCode();
-      // 本地登录-需注释
-      // doWecomLogin('')
-      //   .then(() => {
-      //     console.log(`模拟登录成功,即将进入 ${to.path}`);
-      //   })
     }
   } catch (error) {
     console.error("登录校验过程异常:", error);

+ 1 - 0
src/utils/errorCode.js

@@ -2,5 +2,6 @@ export default {
   '401': '认证失败,无法访问系统资源',
   '403': '当前操作没有权限',
   '404': '访问资源不存在',
+  '430': '无权限访问',
   'default': '系统未知错误,请反馈给管理员'
 }

+ 41 - 25
src/utils/index.ts

@@ -52,40 +52,25 @@ export const Dateformat = (t: string | Date, format: string) => {
 };
 
 export const getWecomType = (agentFrom: string) => {
-  // 用户类型 1经销商 2 服务商 3立邦用户 4导购 5新零售客服
+  // 用户来源  0随身邦 1好邦手 2服务商随身邦 3金牌店随身邦 4导购 5新零售客服
   switch (agentFrom) {
-    case 'ssb':
+    case 'ssb'://经销商
+      return 0;
+    case 'hbs'://好邦手
       return 1;
-    case 'stoneLikePaint':
+    case 'stoneLikePaint'://服务商
       return 2;
-    case 'hbs':
+    case 'goldShop'://金牌店
       return 3;
-    case 'dg':
+    case 'dg'://导购
       return 4;
-    case 'xlskf':
+    case 'xlskf'://新零售客服
       return 5;
     default:
       return 5; // 默认为5
   }
 }
 
-export const getAgentFrom = (WecomType: number) => {
-  // 用户类型 1经销商 2 服务商 3立邦用户 4导购 5新零售客服
-  switch (WecomType) {
-    case 1://经销商
-      return 'ssb';
-    case 2://服务商
-      return 'stoneLikePaint';
-    case 3://好邦手
-      return 'hbs';
-    case 4://导购
-      return 'dg';
-    case 5://新零售客服
-      return 'xlskf';
-    default:
-      return 'xlskf'; // 默认为新零售客服
-  }
-}
 
 export const toLBHome = () => {
   router.replace('/');
@@ -164,9 +149,10 @@ export const getWxconfig = () => {
           wx.hideMenuItems({
             menuList: [
               "menuItem:share:wechat",
-              "menuItem:share:wechat_friend",
               "menuItem:openWithBrowser",
-              "menuItem:copyUrl"
+              "menuItem:copyUrl",
+              "menuItem:share:wechat_friend",
+              "menuItem:share:timeline"
             ],
             success: () => {
               console.log('菜单隐藏成功');
@@ -186,3 +172,33 @@ export const getWxconfig = () => {
     }
   });
 }
+
+/**
+ * 企业微信H5 环境+缓存双重校验工具
+ * 校验规则:1.必须是企微APP/客户端环境  
+ * 校验失败自动跳转 /error 页面,无返回值
+ */
+export const checkWxWorkEnvAndUserCache = () => {
+  try {
+    const currentHost = window.location.hostname;
+    const isLocalDev = currentHost === 'localhost' || currentHost.startsWith('192.168.101');
+    console.log("isLocalDev=", isLocalDev);
+    if (isLocalDev) {
+      return;
+    }
+    const userAgent = window.navigator.userAgent.toLowerCase();
+    const isWxWorkEnv = userAgent.includes('wxwork');
+    console.log("userAgent", userAgent)
+    console.log("isWxWorkEnv", isWxWorkEnv)
+    if (!isWxWorkEnv) {
+      console.error('校验失败:当前环境非企业微信APP/客户端');
+      sessionStorage.setItem("errorMsgTit", "请在企微客户端打开链接")
+      router.replace(`/error`);
+      return;
+    }
+  } catch (error) {
+    console.error('环境校验异常:', error);
+    sessionStorage.setItem("errorMsgTit", "环境异常")
+    router.replace(`/error`)
+  }
+}

+ 5 - 0
src/utils/requestAI.js

@@ -3,6 +3,7 @@
  * 负责请求的公共配置,以及请求拦截,响应拦截,错误处理,网络不佳处理
  */
 import axios from 'axios'
+import router from "../router";
 import { Toast } from 'vant';
 import errorCode from '@/utils/errorCode'
 import { getQyCode, authLock } from '@/utils/wecomLogin.ts';
@@ -74,6 +75,10 @@ service.interceptors.response.use(async(res) => {
             authLock.unlock();
             console.error('授权跳转失败:', err);
         }
+    } else if(code === 430){
+        // 没有读取到userid
+        sessionStorage.setItem("errorMsgTit", msg);
+        router.replace(`/error`);
     }
     // else if (code !== 200 && code !== 204) {
     //     Toast(msg);

+ 25 - 28
src/utils/wecomLogin.ts

@@ -11,14 +11,7 @@ export interface LastCode {
     code: string;
 }
 
-export interface WecomAuthResponse {
-    StatusCode: number;
-    Data: {
-        token: string; // AIToken
-        roleIds: string[];
-    };
-    Message?: string;
-}
+
 
 
 // 登录中状态锁(避免并发登录请求)
@@ -83,24 +76,24 @@ export const doWecomLogin = async (code: string): Promise<void> => {
         window.localStorage.setItem('lastCode', JSON.stringify({ code }));
         const formData = new FormData();
         formData.append('code', code);
-        // 本地登录-需注释
-        // formData.append("code", 'QWert!@345');
         // 调用接口兑换 AIToken
-        const res: WecomAuthResponse = await wecomAuth(formData);
+        const res = await wecomAuth(formData);
         if (res && res.StatusCode === 200 && res.Data && res.Data.token) {
             window.localStorage.setItem('AIToken', res.Data.token);
             // 处理身份,身份可能为多个,只能按身份权限大小来固定为某个身份;
-            if (res.Data.roleIds && res.Data.roleIds.length > 0) {
+            if (res.Data.userType) {
                 // 使用位运算
                 const ROLE_CONFIG = [
-                    { bit: 1 << 1, code: 'stoneLikePaint', desc: '服务商' },  // 2 
+                    { bit: 0, code: '', desc: '无' }, 
                     { bit: 1 << 0, code: 'ssb', desc: '经销商' },             // 1 
-                    { bit: 1 << 3, code: 'dg', desc: '导购' },                // 8
-                    { bit: 1 << 2, code: 'hbs', desc: '好邦手' },             // 4
-                    { bit: 1 << 4, code: 'xlskf', desc: '新零售客服' }          // 16
+                    { bit: 1 << 1, code: 'hbs', desc: '立邦用户' },           // 2
+                    { bit: 1 << 2, code: 'stoneLikePaint', desc: '服务商' },  // 4 
+                    { bit: 1 << 3, code: 'goldShop', desc: '金牌店' },        // 8 
+                    { bit: 1 << 4, code: 'dg', desc: '导购' },                // 16
+                    { bit: 1 << 5, code: 'xlskf', desc: '新零售客服' }         // 32
                 ];
-                const roleIds = res.Data.roleIds;
-                const matchRole = ROLE_CONFIG.find(item => (roleIds & item.bit) === item.bit);
+                const userType = Number(res.Data.userType);
+                const matchRole = ROLE_CONFIG.find(item => (userType & item.bit) === item.bit);
                 const agent = matchRole && matchRole.code || 'xlskf';
                 window.localStorage.setItem('agentFrom', agent);
                 window.localStorage.setItem('agentFromAI', agent);
@@ -109,23 +102,27 @@ export const doWecomLogin = async (code: string): Promise<void> => {
             // console.log("router.currentRoute.fullPath=",router.currentRoute.fullPath)
             // 重新跳转目标页面(此时登录状态已满足)
             // router.push(router.currentRoute.fullPath);
-        } else if (res && res.StatusCode === 403) {
-            // 无权限 → 跳错误页
-            isLogging = false;
-            router.push('/error');
-        } else if (res && res.StatusCode === 420) {
-            // 需重新获取 code → 清除缓存并重定向
-            isLogging = false;
-            getQyCode(); // 你的获取企业微信二维码/授权链接的函数
         } else {
             isLogging = false;
-            throw new Error(res && res.Message ? res.Message : '登录失败');
         }
+        // else if (res && res.StatusCode === 403) {
+        //     // 无权限 → 跳错误页
+        //     isLogging = false;
+        //     sessionStorage.setItem("errorMsgTit", '无操作权限');
+        //     router.push('/error');
+        // } else if (res && res.StatusCode === 420) {
+        //     // 需重新获取 code → 清除缓存并重定向
+        //     isLogging = false;
+        //     getQyCode(); // 你的获取企业微信二维码/授权链接的函数
+        // } else {
+        //     isLogging = false;
+        //     throw new Error(res && res.Message ? res.Message : '登录失败');
+        // }
     } catch (error) {
         isLogging = false;
         console.error('企业微信登录失败:', error);
         // 登录失败可提示用户或重试
-        alert('登录失败,请刷新页面重试');
+        // alert('登录失败,请刷新页面重试');
     }
 };
 

+ 24 - 8
src/views/AIDesign/design.vue

@@ -677,6 +677,12 @@ export default class extends Vue {
           }
           this.ColorCard = response.Data.F_ColorCard || this.stoneColors[0].value;
           this.selectedProcess = response.Data.F_DeepGrooveTech || this.deepSlotProcesses[0].value;
+          // 有墙面工艺
+          if(this.selectedProcess ){
+            const selectedProcessSelctIndex = this.deepSlotProcesses.findIndex(item => item.value === this.selectedProcess);
+            // 墙面分割线选项
+            this.splitWays = this.deepSlotProcesses[selectedProcessSelctIndex].subitems;
+          }
           this.selectedSplit = response.Data.F_DivisionMethod || this.splitWays[0].value;
           this.selectedWindowFrame = response.Data.F_WindowsColor || this.windowFrames[0].value;
           this.selectedRailing = response.Data.F_CastRailing || this.railings[0].value;
@@ -806,10 +812,9 @@ export default class extends Vue {
   }
 
   private selectProcess(value: string, refsName: string, selcIndex: number) {
-    // if (this.selectedSplit == '平涂' && value != '无') {
-    //   this.selectedSplit = '双横线';
-    // }
     this.selectedProcess = value;
+    this.splitWays = this.deepSlotProcesses[selcIndex].subitems;
+    this.selectedSplit = this.splitWays[0].value;
     this.autoScrollToActive(refsName, selcIndex); // 选中后触发自动滚动
   }
 
@@ -903,6 +908,11 @@ export default class extends Vue {
     // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
     const formData = new FormData();
     if (this.activeName === '外墙质感') {
+      // 如果字典渲染有误,不让提交;墙面工艺、电子色卡都为必填项且没有不需要;
+      if (!this.selectedProcess || !this.ColorCard) {
+        this.$toast.fail('数据有误,请重试');
+        return;
+      }
       // 用户选择的选项
       const uDesignStyle = this.DesignStyle === '无' ? '' : this.DesignStyle;
       const uColorCard = this.ColorCard === '无' ? '' : this.ColorCard;
@@ -924,6 +934,11 @@ export default class extends Vue {
       formData.append('skirtType', uSkirtType);//墙裙
 
     } else if (this.activeName === '外墙平涂') {
+      //  如果字典渲染有误,不让提交;
+      if (!this.color_selValue) {
+        this.$toast.fail('数据有误,请重试');
+        return;
+      }
       formData.append('DesignStyle', "CHANGE_COLOR");
       formData.append('Color', this.color_selValue)
     }
@@ -945,7 +960,7 @@ export default class extends Vue {
     //   formData.append('projectid', this.checkedProject.projectid);
     // }
     // 企微类型
-    formData.append('wecomType', 5);
+    // formData.append('wecomType', 5);
     // // 服务商代码
     // if (serviceCodeArray.length > 0) {
     //   formData.append('serivceCode', serviceCodeArray.join(','));
@@ -1105,7 +1120,7 @@ export default class extends Vue {
     // formData.append('roleIds', roleIdArray.join(','));
     // formData.append('WXuserid', userInfo.loginName);
     formData.append('baseType', 0);
-    formData.append('wecomType', 5);
+    // formData.append('wecomType', this.wecomType);
     // const isRefresh = userInfo.isRefreshProvider === '是' ? 1 : 0;//0=否 1=是
     // formData.append('isRefresh', isRefresh);
     GetDictList(formData).then(response => {
@@ -1119,8 +1134,10 @@ export default class extends Vue {
         this.stoneColors = response.Data.dict.ColorCard;
         // 墙面工艺选项
         this.deepSlotProcesses = response.Data.dict.DeepGrooveTech;
-        // 墙面分割线选项
-        this.splitWays = response.Data.dict.DivisionMethod;
+        if(this.deepSlotProcesses && this.deepSlotProcesses.length > 0){
+          // 墙面分割线选项
+          this.splitWays = this.deepSlotProcesses[0].subitems;
+        }
         // 窗套样式选项
         this.windowFrames = response.Data.dict.WindowsColor;
         // 浇筑栏杆选项
@@ -1191,7 +1208,6 @@ export default class extends Vue {
     that.projectActionListShow = false;
     // }
   }
-
 }
 </script>
 <style>

+ 4 - 3
src/views/AIDesign/diagnose.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="design-container AI-Design-container">
         <div class="header">
-            <van-nav-bar :title="wallType == 'outside' ? '外墙' : '内墙' + '一键诊断'" left-arrow @click-left="returnPage"
+            <van-nav-bar :title="wallType == 'outside' ? '外墙一键诊断' : '内墙一键诊断'" left-arrow @click-left="returnPage"
                 @click-right="toHome">
                 <template #right>
                     <van-icon name="wap-home-o" color="#333" size="26" />
@@ -139,6 +139,7 @@ export default class extends Vue {
             return;
         }
         let FileSize = (file.size / 1024 / 1024).toFixed(2);
+        // console.log("用户选择的图片大小=", file.size + 'KB', '  =', FileSize + 'M')
         // 2. 校验文件大小(对应 beforeRead 的大小检查)
         const maxSize = 20 * 1024 * 1024; // 10MB
         if (file.size > maxSize) {
@@ -191,8 +192,8 @@ export default class extends Vue {
         // formData.append('WXuserid', userInfo.loginName);
         // 企微类型
         const agentFrom = window.localStorage.getItem('agentFromAI');
-        const wecomType = getWecomType(agentFrom);
-        formData.append('wecomType', 5);
+        // const wecomType = getWecomType(agentFrom);
+        // formData.append('wecomType', 5);
         // // 服务商代码
         // if (serviceCodeArray.length > 0) {
         //     formData.append('serivceCode', serviceCodeArray.join(','));

+ 30 - 21
src/views/AIDesign/index.vue

@@ -1,17 +1,12 @@
 <template>
   <div class="home-container AI-Design-container">
     <div class="header">
-      <!-- <van-nav-bar title="首页" left-arrow @click-left="returnPage" @click-right="toHome">
-        <template #right>
-          <van-icon name="wap-home-o" color="#333" size="26" />
-        </template>
-      </van-nav-bar> -->
       <van-nav-bar title="首页"></van-nav-bar>
     </div>
     <!-- 顶部装饰背景图 -->
     <div class="top-bg">
       <img v-show="currentTab === 'inside'" src="@/assets/AIDesign/top-inside.jpg" class="bg-image" />
-      <img v-show="currentTab === 'outside'"  src="@/assets/AIDesign/top-outside.jpg" class="bg-image" />
+      <img v-show="currentTab === 'outside'" src="@/assets/AIDesign/top-outside.jpg" class="bg-image" />
     </div>
 
     <!-- 新版tab -->
@@ -36,7 +31,7 @@
       <!-- 一键诊断 -->
       <div class="card card-diagnosis" @click="handleDiagnosisClick('outside')">
         <div class="card-content">
-          <div class="card-title" style="color: #2F655B;">一键诊断(规划中)</div>
+          <div class="card-title" style="color: #2F655B;">一键诊断</div>
           <div class="card-desc" style="color: #6D9587;">AI诊断墙面问题,提供解决方案</div>
         </div>
         <div class="card-icon">
@@ -85,27 +80,41 @@
 
 <script lang="ts">
 import { Component, Vue } from "vue-property-decorator";
-import { toLBHome,getWxconfig } from '@/utils/index';
+import { toLBHome, getWxconfig, getWecomType } from '@/utils/index';
 import axios from "axios";
 declare let wx: any;
 @Component
 export default class extends Vue {
   private currentTab = 'outside';
+  // created() {
+  //   getWxconfig();
+  //   // 经销商随身邦,好邦手进去,默认在内墙空间;服务商进去,默认外墙空间
+  //   const agentFrom = window.localStorage.getItem('agentFrom');
+  //   window.localStorage.setItem('agentFromAI', agentFrom);
+  //   // 导购直接从AI设计首页进来的修改agentFrom=dg
+  //   const agent = this.$route.query.agent || null;
+  //   if (agent && agent === 'dg') {
+  //     window.localStorage.setItem('agentFromAI', agent);
+  //   }
+  //   if (agentFrom === 'ssb' || agentFrom === 'hbs') {
+  //     this.currentTab = 'inside';
+  //   } else {
+  //     this.currentTab = 'outside';
+  //   }
+  // }
+  // 入口新判断
   created() {
     getWxconfig();
-    // 经销商随身邦,好邦手进去,默认在内墙空间;服务商进去,默认外墙空间
     const agentFrom = window.localStorage.getItem('agentFrom');
-    window.localStorage.setItem('agentFromAI', agentFrom);
-    // 导购直接从AI设计首页进来的修改agentFrom=dg
-    const agent = this.$route.query.agent || null;
-    if (agent && agent === 'dg') {
-      window.localStorage.setItem('agentFromAI', agent);
-    }
+    // 经销商随身邦,好邦手进去,默认在内墙空间;服务商进去,默认外墙空间
     if (agentFrom === 'ssb' || agentFrom === 'hbs') {
       this.currentTab = 'inside';
     } else {
       this.currentTab = 'outside';
     }
+    // 存下用户来源,无agent即为新零售客服
+    const agent = this.$route.query.agent || 'xlskf';
+    sessionStorage.setItem("userWecomType", getWecomType(agent))
   }
   handleTabChange(tabType: 'outside' | 'inside'): void {
     this.currentTab = tabType;
@@ -128,10 +137,10 @@ export default class extends Vue {
   }
 
   handleDiagnosisClick(wallType) {
-    if (wallType === "outside") {
-      this.$toast('规划中,敬请期待');
-      return;
-    }
+    // if (wallType === "outside") {
+    //   this.$toast('规划中,敬请期待');
+    //   return;
+    // }
     this.$router.push({ path: '/AIDesign/diagnose', query: { wallType } });
   }
 
@@ -204,7 +213,7 @@ export default class extends Vue {
 
 .new-tab-wrapper {
   width: 100%;
-  height:60px;
+  height: 60px;
   margin-bottom: 20px;
   display: flex;
   justify-content: space-between;
@@ -212,7 +221,7 @@ export default class extends Vue {
 
   .tab-title {
     width: 50%;
-    height:100%;
+    height: 100%;
   }
 }
 

+ 3 - 3
src/views/AIDesign/insideDesign.vue

@@ -650,7 +650,7 @@ export default class extends Vue {
     // 企微类型
     // const agentFrom = window.localStorage.getItem('agentFromAI');
     // const wecomType = getWecomType(agentFrom);
-    formData.append('wecomType', 5);
+    // formData.append('wecomType', 5);
     // // 服务商代码
     // if (serviceCodeArray.length > 0) {
     //   formData.append('serivceCode', serviceCodeArray.join(','));
@@ -724,8 +724,8 @@ export default class extends Vue {
     // formData.append('WXuserid', userInfo.loginName);
     formData.append('baseType', 1);//必填 0外墙 1内墙
     const agentFrom = window.localStorage.getItem('agentFromAI');
-    const wecomType = getWecomType(agentFrom);
-    formData.append('wecomType', 5);
+    // const wecomType = getWecomType(agentFrom);
+    // formData.append('wecomType', 5);
     // const isRefresh = userInfo.isRefreshProvider === '是' ? 1 : 0;//0=否 1=是
     // formData.append('isRefresh', isRefresh);
     GetDictList(formData).then(response => {

+ 2 - 2
src/views/AIDesign/result.vue

@@ -198,8 +198,8 @@ export default class extends Vue {
     // formData.append('WXuserid', userInfo.loginName);
     formData.append('baseType', 0);//0外墙--这里只用查询外墙
     const agentFrom = window.localStorage.getItem('agentFromAI');
-    const wecomType = getWecomType(agentFrom);
-    formData.append('wecomType', 5);
+    // const wecomType = getWecomType(agentFrom);
+    // formData.append('wecomType', 5);
     GetDictList(formData).then(response => {
       if (response.StatusCode == 200) {
         if (that.serviceCodeArray.length == 0) {

+ 9 - 1
src/views/errorPage/weixinerr.vue

@@ -3,7 +3,7 @@
     <div class="error">
       <div class="imgDiv">
         <img src="@assets/images/err.png" alt="" />
-        <div class="infoText" @click="toLogin">请在企微客户端打开链接</div>
+        <div class="infoText">{{ errorMsg }}</div>
       </div>
     </div>
   </div>
@@ -16,6 +16,14 @@ export default class extends Vue {
   timer: any = null;
   currentTime: any = "0";
   lastTime: any = "0";
+  private errorMsg = '无权限';
+  activated() {
+    if(sessionStorage.getItem("errorMsgTit")){
+      this.errorMsg = sessionStorage.getItem("errorMsgTit");
+    }else{
+      this.errorMsg = '无权限';
+    }
+  }
   toLogin(event) {
     event.preventDefault();
     this.currentTime = new Date().getTime();