Quellcode durchsuchen

feat: 增加扫码溯源功能

liujt vor 9 Monaten
Ursprung
Commit
e1b40917e9

+ 13 - 0
src/api/traceability/index.js

@@ -0,0 +1,13 @@
+import request from '@/utils/request';
+
+/**
+ * 获取溯源详情
+ * @data {*} data
+ */
+export async function queryPrintV2detail (data) {
+    const res = await request.post(`/mes/workorder/queryPrintV2detail`, data);
+    if (res.data.code == 0) {
+      return res.data;
+    }
+    return Promise.reject(new Error(res.data.message));
+}

+ 1 - 0
src/assets/traceability/1.svg

@@ -0,0 +1 @@
+<svg t="1689757729915" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2603" width="200" height="200"><path d="M512 590.65c-5.59 0-11.18-1.38-16.23-4.16L81.5 359.17C70.71 353.25 64 341.91 64 329.6s6.71-23.65 17.5-29.56L495.77 72.73a33.65 33.65 0 0 1 32.45 0L942.5 300.04A33.73 33.73 0 0 1 960 329.6c0 12.31-6.71 23.65-17.5 29.56L528.23 586.49a33.74 33.74 0 0 1-16.23 4.16zM167.84 329.6L512 518.45 856.16 329.6 512 140.76 167.84 329.6z" fill="#1296db" p-id="2604"></path><path d="M512 773.04c-5.59 0-11.18-1.38-16.23-4.16L81.5 541.56c-16.32-8.96-22.3-29.46-13.34-45.79 8.97-16.3 29.47-22.28 45.79-13.34L512 700.85l398.04-218.42c16.36-8.95 36.83-2.98 45.79 13.34 8.96 16.34 2.99 36.83-13.34 45.79L528.23 768.88a33.628 33.628 0 0 1-16.23 4.16z" fill="#1296db" p-id="2605"></path><path d="M512 955.44c-5.59 0-11.18-1.38-16.23-4.16L81.5 723.96C65.18 715 59.2 694.5 68.16 678.17c8.97-16.31 29.47-22.26 45.79-13.34L512 883.24l398.04-218.42c16.36-8.97 36.83-2.96 45.79 13.34 8.96 16.34 2.99 36.83-13.34 45.79L528.23 951.28a33.74 33.74 0 0 1-16.23 4.16z" fill="#1296db" p-id="2606"></path></svg>

BIN
src/assets/traceability/bg.jpg


+ 6 - 1
src/config/setting.js

@@ -24,7 +24,12 @@ export const WHITE_LIST = [
   '/fromQRCode/designDrawing.html',
   '/3DViewer/view.html',
   '/2DViewer/view.html',
-  '/singleLogin'
+  '/singleLogin',
+  '/traceability',
+  '/traceability/company', 
+  '/traceability/production', 
+  '/traceability/purchase', 
+  '/traceability/quality'
 ];
 
 // 开启 KeepAlive 后仍然不需要缓存的路由地址

+ 26 - 1
src/router/routes.js

@@ -36,7 +36,32 @@ export const routes = [
     meta: {
       title: ''
     }
-  }
+  },
+  {
+    path: '/traceability',
+    component: () => import('@/views/traceability/traceabilityCode/index.vue'),
+    meta: { title: '溯源' }
+  },
+  {
+    path: '/traceability/company',
+    component: () => import('@/views/traceability/traceabilityCode/company.vue'),
+    meta: { title: '企业信息' }
+  },
+  {
+    path: '/traceability/production',
+    component: () => import('@/views/traceability/traceabilityCode/production.vue'),
+    meta: { title: '生产信息' }
+  },
+  {
+    path: '/traceability/purchase',
+    component: () => import('@/views/traceability/traceabilityCode/purchase.vue'),
+    meta: { title: '采购信息' }
+  },
+  {
+    path: '/traceability/quality',
+    component: () => import('@/views/traceability/traceabilityCode/quality.vue'),
+    meta: { title: '质量信息' }
+  },
 
   // {
   //   path: '/main-data/*',

+ 280 - 0
src/views/traceability/traceabilityCode/company.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="production-traceability-page">
+    <!-- 顶部导航栏 -->
+    <div class="nav-bar">
+      <div class="nav-left" @click="handleBack">
+        <i class="el-icon-arrow-left back-icon"></i>
+      </div>
+      <div class="nav-title">采购溯源</div>
+      <div class="nav-right"></div>
+    </div>
+    
+    <!-- 主内容区 -->
+    <div class="content-wrapper">
+      <!-- 企业图片区域 -->
+      <div class="product-image-section">
+        <div class="product-title">企业信息</div>
+        <div style="height: 200px;">
+          <img v-if="companyInfo.companyImgUrl" class="product-image" :src="companyInfo.companyImgUrl" alt="企业图片"></img>
+        </div>
+      </div>
+      
+      <!-- 企业信息列表 -->
+      <div class="info-list">
+        <!-- 企业详情标题 -->
+        <div class="info-title">
+          <span>企业简介</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">企业名称:</span>
+          <span class="info-value">{{ companyInfo.name }}</span>
+        </div>
+        <!-- <div class="info-item">
+          <span class="info-label">企业类别:</span>
+          <span class="info-value"></span>
+        </div> -->
+        <div class="info-item">
+          <span class="info-label">工商注册号:</span>
+          <span class="info-value">{{ companyInfo.code }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">组织机构代码:</span>
+          <span class="info-value">{{ companyInfo.groupId }}</span>
+        </div>
+        <!-- <div class="info-item">
+          <span class="info-label">法人:</span>
+          <span class="info-value">{{ companyInfo.legalPerson }}</span>
+        </div> -->
+        <div v-if="showMoreInfo">
+          <div class="info-item">
+            <span class="info-label">联系人:</span>
+            <span class="info-value">{{ companyInfo.legalPerson }}</span>
+          </div>
+          <div class="info-item">
+            <span class="info-label">联系电话:</span>
+            <span class="info-value">{{ companyInfo.tel }}</span>
+          </div>
+          <div class="info-item">
+            <span class="info-label">成立时间:</span>
+            <span class="info-value">{{ companyInfo.registerDate }}</span>
+          </div>
+          <div class="info-item">
+            <span class="info-label">成立时间:</span>
+            <span class="info-value">{{ companyInfo.registerDate }}</span>
+          </div>
+          <div class="info-item">
+            <span class="info-label">详细地址地址:</span>
+            <span class="info-value">{{ companyInfo.address }}</span>
+          </div>
+          <div class="info-item">
+            <span class="info-label">营业执照:</span>
+            <span class="info-value">{{ companyInfo.businessLicenseFile[0] }}</span>
+          </div>
+        </div>
+        <!-- 查看更多信息按钮 -->
+        <div class="more-info-btn" @click="toggleMoreInfo">
+          <span>查看更多信息</span>
+          <i class="el-icon-arrow-down"></i>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'CompanyInfoPage',
+  data() {
+    return {
+      // 使用项目中已有的图标路径
+      productImageSrc: require('@/assets/traceability/1.svg'),
+      companyIconSrc: require('@/assets/traceability/1.svg'),
+      showMoreInfo: false,
+      detail: {},
+      purchaseInfo: {},
+      companyInfo: {}
+    };
+  },
+  mounted() {
+    console.log('路由参数:');
+    console.log('路由参数:', this.$route.query);
+    // 从路由参数中获取detail对象
+    if (this.$route.query && this.$route.query.detail) {
+      try {
+        // 解析JSON字符串为对象
+        this.detail = JSON.parse(this.$route.query.detail);
+        console.log('获取到的detail参数:', this.detail);
+        this.purchaseInfo = this.detail.purchaseInfo || {};
+        this.companyInfo = this.detail.groupInfo || {};
+      } catch (error) {
+        console.error('解析detail参数失败:', error);
+        this.detail = {};
+        this.purchaseInfo = {};
+        this.companyInfo = {};
+      }
+    }
+  },
+  methods: {
+    handleBack() {
+      // 返回上一页
+      this.$router.back();
+    },
+    goToHome() {
+      // 返回首页
+      this.$router.push('/views/home/home');
+    },
+    toggleMoreInfo() {
+      // 切换显示更多信息
+      this.showMoreInfo = !this.showMoreInfo;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.production-traceability-page {
+  background-color: #fff;
+  min-height: 100vh;
+  background-image: url('~@/assets/traceability/bg.jpg');
+  background-size: cover;
+  background-position: center;
+}
+
+.nav-bar {
+  height: 56px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  color: #000;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  padding: 0 16px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
+}
+
+.nav-left {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.back-icon {
+  fill: #333;
+  font-size: 24px;
+}
+
+.nav-title {
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.nav-right {
+  width: 40px;
+}
+
+.content-wrapper {
+  padding: 80px 20px 30px;
+  position: relative;
+}
+
+.product-image-section {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 20px;
+  background-color: #fff;
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+}
+
+.product-title {
+  position: absolute;
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  text-align: center;
+  padding: 5px 20px;
+  background: #bd0402;
+  border-radius: 5px;
+  box-shadow: 0 1px 4px rgba(255, 107, 107, 0.3);
+  top: -10px;
+}
+
+.info-title {
+  position: absolute;
+  top: -10px;
+  left: 50%;
+  transform: translateX(-50%);
+  background-color: #bd0402;
+  color: #fff;
+  text-align: center;
+  padding: 5px 20px;
+  border-radius: 5px;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.info-list {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 15px;
+  margin-bottom: 15px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+  padding-top: 28px;
+}
+
+.info-item {
+  display: flex;
+  align-items: flex-start;
+  padding: 10px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.info-item:last-child {
+  border-bottom: none;
+}
+
+.info-label {
+  font-size: 14px;
+  color: #bd0402;
+  margin-right: 5px;
+  flex: 1;
+  font-weight: 500;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  flex: 2;
+}
+
+.more-info-btn {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 13px;
+  color: #bd0402;
+  padding: 5px 0;
+  margin-top: 10px;
+  cursor: pointer;
+}
+
+.dropdown-icon {
+  width: 12px;
+  height: 12px;
+  margin-left: 5px;
+  transition: transform 0.3s;
+}
+</style>

+ 488 - 0
src/views/traceability/traceabilityCode/index.vue

@@ -0,0 +1,488 @@
+<template>
+  <div class="traceability-page">
+    <!-- 顶部导航栏 -->
+    <!-- <div class="navbar">
+      <button class="back-button" @click="handleBack">
+        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+          <path d="M19 12H5M12 19l-7-7 7-7"/>
+        </svg>
+      </button>
+      <h1 class="navbar-title">溯源</h1>
+      <div class="navbar-right"></div>
+    </div> -->
+    
+    <!-- 主内容区 -->
+    <div class="content-wrapper">
+      <!-- 编辑中标签 -->
+      <!-- <div class="edit-tag">编辑中</div> -->
+      
+      <!-- 产品图片区域 -->
+      <div class="product-image-section">
+        <div class="image-container">
+          <img v-if="detail.purchaseInfo && detail.purchaseInfo.purchaseImgUrl" class="product-image" :src="detail.purchaseInfo.purchaseImgUrl" alt="产品图片">
+          <div v-else>暂无图片</div>
+        </div>
+      </div>
+      
+      <!-- 产品基本信息区域 -->
+      <div class="product-info-section">
+        <div class="info-title">产品基本信息</div>
+        <div class="info-content">
+          <div class="info-row">
+            <div class="info-col">
+              <div class="info-item">
+                <span class="info-label">品名:</span>
+                <span class="info-value">{{ detail.productName }}</span>
+              </div>
+            </div>
+            <div class="info-col">
+              <div class="info-item">
+                <span class="info-label">产地:</span>
+                <span class="info-value">{{ detail.purchaseOrigins }}</span>
+              </div>
+            </div>
+          </div>
+          <div class="info-row">
+            <div class="info-col">
+              <div class="info-item">
+                <span class="info-label">商品编码:</span>
+                <span class="info-value">{{ detail.productCode }}</span>
+              </div>
+            </div>
+            <div class="info-col">
+              <div class="info-item">
+                <span class="info-label">批号:</span>
+                <span class="info-value">{{ detail.batchNo }}</span>
+              </div>
+            </div>
+          </div>
+          <div v-if="showMoreInfo">
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">规格:</span>
+                  <span class="info-value">{{ detail.specification }}</span>
+                </div>
+              </div>
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">内控等级:</span>
+                  <span class="info-value">{{ detail.level }}</span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">内控规格:</span>
+                  <span class="info-value"></span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">执行标准:</span>
+                  <span class="info-value">{{ detail.enforceStandards }}</span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">性味与归经:</span>
+                  <span class="info-value"></span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">功能与主治:</span>
+                  <span class="info-value"></span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">用法与用量:</span>
+                  <span class="info-value"></span>
+                </div>
+              </div>
+            </div>
+            <div class="info-row">
+              <div class="info-col">
+                <div class="info-item">
+                  <span class="info-label">贮藏:</span>
+                  <span class="info-value">{{ detail.layBy }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- 查看更多信息按钮 -->
+        <div class="more-info-btn" @click="toggleMoreInfo">
+          <span>{{ showMoreInfo ? '收起' : '查看更多信息' }}</span>
+           <i :class="[showMoreInfo ? 'el-icon-arrow-up' : 'el-icon-arrow-down']"></i>
+        </div>
+      </div>
+      
+      <!-- 功能按钮区域 -->
+      <div class="function-buttons">
+        <div class="button-row">
+          <div class="function-btn" @click="navigateToPurchase">
+            <img class="btn-icon" :src="cartIconSrc" alt="采购溯源图标">
+            <span class="btn-text">采购溯源</span>
+          </div>
+          <div class="function-btn" @click="navigateToProduction">
+            <img class="btn-icon" :src="productionIconSrc" alt="生产溯源图标">
+            <span class="btn-text">生产溯源</span>
+          </div>
+          <div class="function-btn" @click="navigateToQuality">
+            <img class="btn-icon" :src="qualityIconSrc" alt="质检溯源图标">
+            <span class="btn-text">质检溯源</span>
+          </div>
+          <div class="function-btn" @click="navigateToCompany">
+            <img class="btn-icon" :src="companyIconSrc" alt="企业信息图标">
+            <span class="btn-text">企业信息</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { queryPrintV2detail } from '@/api/traceability';
+export default {
+  name: 'TraceabilityPage',
+  data() {
+    return {
+      traceId: '',
+      detail: {},
+      showMoreInfo: false,
+      // 使用项目中已有的图标路径
+      productImageSrc: require('@/assets/traceability/1.svg'),
+      selectIconSrc: require('@/assets/traceability/1.svg'),
+      cartIconSrc: require('@/assets/traceability/1.svg'),
+      productionIconSrc: require('@/assets/traceability/1.svg'),
+      qualityIconSrc: require('@/assets/traceability/1.svg'),
+      companyIconSrc: require('@/assets/traceability/1.svg'),
+    };
+  },
+  mounted() {
+    // 组件挂载时获取路由传递的ID
+    this.getTraceabilityId();
+  },
+  methods: {
+    handleBack() {
+      // 返回上一页
+      this.$router.back();
+    },
+    toggleMoreInfo() {
+      // 切换显示更多信息
+      this.showMoreInfo = !this.showMoreInfo;
+    },
+    navigateToPurchase() {
+      // 跳转到采购溯源页面并传递detail对象
+      this.$router.push({
+        path: '/traceability/purchase',
+        query: {
+          detail: JSON.stringify(this.detail)
+        }
+      });
+    },
+    navigateToProduction() {
+      // 跳转到生产溯源页面
+      this.$router.push({
+        path: '/traceability/production',
+        query: {
+          detail: JSON.stringify(this.detail)
+        }
+      });
+    },
+    navigateToQuality() {
+      // 跳转到质检溯源页面
+      this.$router.push({
+        path: '/traceability/quality',
+        query: {
+          detail: JSON.stringify(this.detail)
+        }
+      });
+    },
+    navigateToCompany() {
+      // 跳转到企业信息页面
+      this.$router.push({
+        path: '/traceability/company',
+        query: {
+          detail: JSON.stringify(this.detail)
+        }
+      });
+    },
+    getTraceabilityId() {
+      // 从路由参数中获取ID
+      // 首先尝试从params中获取
+      const paramsId = this.$route.params.id;
+      // 然后尝试从query中获取
+      const queryId = this.$route.query.id;
+      
+      // 设置ID,优先使用params中的ID
+      this.traceId = paramsId || queryId || '';
+      
+      // 如果获取到了ID,可以根据需要进行后续操作
+      if (this.traceId) {
+        console.log('获取到溯源ID:', this.traceId);
+        // 这里可以根据ID加载对应的数据
+        this.loadTraceabilityData(this.traceId);
+      }
+    },
+    loadTraceabilityData(id) {
+      queryPrintV2detail({
+        workOrderId: id
+      }).then(res => {
+        console.log('获取到溯源详情:', res.data);
+        this.detail = res.data;
+      }).catch(err => {
+        console.error('获取溯源详情失败:', err);
+      });
+    }
+  },
+  watch: {
+    // 监听路由变化,当路由参数改变时重新获取ID
+    '$route': {
+      handler: 'getTraceabilityId',
+      immediate: true
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.traceability-page {
+  background-color: #fff;
+  min-height: 100vh;
+  background-image: url('~@/assets/traceability/bg.jpg');
+  background-size: cover;
+  background-position: center;
+  position: relative;
+}
+
+.navbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 60px;
+  background-color: #fff;
+  color: #000;
+  padding: 0 20px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 1000;
+}
+
+.back-button {
+  background: none;
+  border: none;
+  color: #000;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 40px;
+  height: 40px;
+}
+
+.navbar-title {
+  font-size: 18px;
+  font-weight: 600;
+  margin: 0;
+}
+
+.navbar-right {
+  width: 40px;
+}
+
+.content-wrapper {
+  padding: 80px 20px 20px;
+  position: relative;
+}
+
+.edit-tag {
+  position: absolute;
+  top: 80px;
+  left: 0;
+  width: 80px;
+  height: 30px;
+  background: linear-gradient(135deg, #ff6b6b, #ff8e53);
+  color: #fff;
+  text-align: center;
+  line-height: 30px;
+  font-size: 13px;
+  border-radius: 0 15px 15px 0;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  z-index: 10;
+}
+
+.product-image-section {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 30px;
+}
+
+.image-container {
+  width: 150px;
+  height: 150px;
+  border-radius: 50%;
+  background-color: #fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  border: 8px solid #fff;
+  position: relative;
+}
+
+/* 添加红色装饰边框 */
+.image-container::before {
+  content: '';
+  position: absolute;
+  top: -12px;
+  left: -12px;
+  right: -12px;
+  bottom: -12px;
+  border-radius: 50%;
+  border: 1px dashed #ff6b6b;
+  opacity: 0.6;
+}
+
+.product-image {
+  width: 120px;
+  height: 120px;
+  border-radius: 4px;
+}
+
+.product-info-section {
+  background-color: rgba(255, 255, 255, 0.95);
+  border-radius: 10px;
+  padding: 15px;
+  margin-bottom: 30px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  border: 1px solid #ffe6e6;
+  position: relative;
+}
+
+.info-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  text-align: center;
+  margin-bottom: 10px;
+  padding: 5px 20px;
+  background: #bd0402;
+  border-radius: 5px;
+  box-shadow: 0 1px 4px rgba(255, 107, 107, 0.3);
+  position: absolute;
+  top: -10px;
+  left: 30%;
+  // transform: translateX(-50%);
+}
+
+.info-content {
+  margin-bottom: 10px;
+  padding-top: 16px;
+}
+
+.info-row {
+  display: flex;
+  margin-bottom: 10px;
+  gap: 10px;
+}
+
+.info-col {
+  flex: 1;
+}
+
+.info-item {
+  display: flex;
+  align-items: flex-start;
+}
+
+.info-label {
+  font-size: 14px;
+  color: #bd0402;
+  margin-right: 5px;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+  flex: 1;
+}
+
+.more-info-btn {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 13px;
+  color: #bd0402;
+  padding: 5px 0;
+  cursor: pointer;
+}
+
+.dropdown-icon {
+  width: 12px;
+  height: 12px;
+  margin-left: 5px;
+  transition: transform 0.3s;
+}
+
+.function-buttons {
+  margin-bottom: 30px;
+}
+
+.button-row {
+  display: flex;
+  justify-content: space-between;
+  gap: 10px;
+}
+
+.function-btn {
+  flex: 1;
+  height: 120px;
+  background: #bd0402;
+  border-radius: 10px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  color: #fff;
+  box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
+  transition: transform 0.2s;
+  border: 1px solid #fff;
+  cursor: pointer;
+}
+
+.function-btn:hover {
+  transform: translateY(-2px);
+}
+
+.function-btn:active {
+  transform: scale(0.98);
+}
+
+.btn-icon {
+  width: 30px;
+  height: 30px;
+  margin-bottom: 5px;
+}
+
+.btn-text {
+  font-size: 14px;
+  font-weight: 500;
+}
+</style>

+ 271 - 0
src/views/traceability/traceabilityCode/production.vue

@@ -0,0 +1,271 @@
+<template>
+  <div class="production-traceability-page">
+    <!-- 顶部导航栏 -->
+    <div class="nav-bar">
+      <div class="nav-left" @click="handleBack">
+        <i class="el-icon-arrow-left back-icon"></i>
+      </div>
+      <div class="nav-title">生产溯源</div>
+      <div class="nav-right"></div>
+    </div>
+    
+    <!-- 主内容区 -->
+    <div class="content-wrapper">
+      <!-- 编辑中标签 -->
+      <!-- <div class="edit-tag">编辑中</div> -->
+      
+      <!-- 产品图片区域 -->
+      <div class="product-image-section">
+        <div class="product-title">
+          生产溯源
+        </div>
+        <div style="height: 200px;">
+          <img v-if="purchaseInfo && purchaseInfo.purchaseImgUrl" class="product-image" :src="purchaseInfo.purchaseImgUrl" alt="产品图片">
+        </div>
+      </div>
+      
+      <!-- 生产信息列表 -->
+      <div class="info-list">
+        <!-- 生产详情标题 -->
+        <div class="info-title">
+          生产详情
+        </div>
+        <div class="info-item">
+          <span class="info-label">生产日期:</span>
+          <span class="info-value">{{ detail.createDate }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">批生产量:</span>
+          <span class="info-value">{{ detail.quantity }}{{ detail.unit }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">执行标准:</span>
+          <span class="info-value">{{ detail.enforceStandards }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">加工工序:</span>
+          <span class="info-value">{{ detail.printTaskCarDetailStr }}</span>
+        </div>
+      </div>
+      
+      <!-- 返回首页按钮 -->
+      <!-- <div class="home-button" @click="goToHome">
+        <svg class="home-icon" viewBox="0 0 16 16" width="32" height="32">
+          <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6a.5.5 0 1 0 .708.708L8 2.207l5.646 5.647a.5.5 0 0 0 .708-.708l-6-6zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm-.5 7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2a.5.5 0 0 1 .5-.5zm0 3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5z"/>
+        </svg>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ProductionTraceabilityPage',
+  data() {
+    return {
+      // 使用项目中已有的图标路径
+      productImageSrc: require('@/assets/traceability/1.svg'),
+      detail: {},
+      purchaseInfo: {}
+    };
+  },
+  mounted() {
+    console.log('路由参数:');
+    console.log('路由参数:', this.$route.query);
+    // 从路由参数中获取detail对象
+    if (this.$route.query && this.$route.query.detail) {
+      try {
+        // 解析JSON字符串为对象
+        this.detail = JSON.parse(this.$route.query.detail);
+        console.log('获取到的detail参数:', this.detail);
+        this.purchaseInfo = this.detail.purchaseInfo || {};
+      } catch (error) {
+        console.error('解析detail参数失败:', error);
+        this.detail = {};
+        this.purchaseInfo = {};
+      }
+    }
+  },
+  methods: {
+    handleBack() {
+      // 返回上一页
+      this.$router.back();
+    },
+    goToHome() {
+      // 返回首页
+      this.$router.push('/pages/home/home');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.production-traceability-page {
+  background-color: #fff;
+  min-height: 100vh;
+  background-image: url('~@/assets/traceability/bg.jpg');
+  background-size: cover;
+  background-position: center;
+}
+
+.nav-bar {
+  height: 56px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  color: #000;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  padding: 0 16px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
+}
+
+.nav-left {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.back-icon {
+  fill: #333;
+  font-size: 24px;
+}
+
+.nav-title {
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.nav-right {
+  width: 40px;
+}
+
+.content-wrapper {
+  padding: 76px 20px 60px;
+  position: relative;
+}
+
+.edit-tag {
+  position: absolute;
+  top: 96px;
+  left: 0;
+  width: 80px;
+  height: 30px;
+  background: linear-gradient(135deg, #ff6b6b, #ff8e53);
+  color: #fff;
+  text-align: center;
+  line-height: 30px;
+  font-size: 13px;
+  border-radius: 0 15px 15px 0;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  z-index: 10;
+}
+
+.product-image-section {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+  background-color: #fff;
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+}
+
+.product-title {
+  position: absolute;
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  text-align: center;
+  margin-bottom: 10px;
+  padding: 5px 20px;
+  background: #bd0402;
+  border-radius: 5px;
+  box-shadow: 0 1px 4px rgba(255, 107, 107, 0.3);
+  top: -10px;
+}
+
+.product-image {
+  max-width: 200px;
+  max-height: 200px;
+}
+
+.info-title {
+  position: absolute;
+  top: -10px;
+  left: 35%;
+  background-color: #bd0402;
+  color: #fff;
+  text-align: center;
+  padding: 5px 20px;
+  margin-bottom: 15px;
+  border-radius: 5px;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.info-list {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 30px;
+  margin-bottom: 30px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+  padding-top: 30px;
+}
+
+.info-item {
+  display: flex;
+  align-items: flex-start;
+  padding: 10px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.info-item:last-child {
+  border-bottom: none;
+}
+
+.info-label {
+  font-size: 14px;
+  color: #bd0402;
+  margin-right: 10px;
+  flex: 1;
+  font-weight: 500;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  flex: 2;
+}
+
+.home-button {
+  position: fixed;
+  bottom: 30px;
+  right: 30px;
+  width: 50px;
+  height: 50px;
+  background-color: #bd0402;
+  border-radius: 50%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 2px 10px rgba(189, 4, 2, 0.4);
+  z-index: 20;
+  cursor: pointer;
+}
+
+.home-icon {
+  fill: #fff;
+}
+</style>

+ 294 - 0
src/views/traceability/traceabilityCode/purchase.vue

@@ -0,0 +1,294 @@
+<template>
+  <div class="purchase-traceability-page">
+    <!-- 顶部导航栏 -->
+    <div class="nav-bar">
+      <div class="nav-left" @click="handleBack">
+        <i class="el-icon-arrow-left back-icon"></i>
+      </div>
+      <div class="nav-title">采购溯源</div>
+      <div class="nav-right"></div>
+    </div>
+    
+    <!-- 主内容区 -->
+    <div class="content-wrapper">
+      <!-- 产品图片区域 -->
+      <div class="product-image-section">
+        <div class="product-title">
+          采购溯源
+        </div>
+        <div style="height: 200px;">
+          <img v-if="purchaseInfo && purchaseInfo.purchaseImgUrl" class="product-image" :src="purchaseInfo.purchaseImgUrl" alt="产品图片">
+        </div>
+      </div>
+      
+      <!-- 采购信息列表 -->
+      <div class="info-list">
+        <!-- 采购详情标题 -->
+        <div class="info-title">
+          采购详情
+        </div>
+        <div class="info-item">
+          <span class="info-label">原药批号:</span>
+          <span class="info-value">{{ purchaseInfo.sourceBatchNo }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">原药产地:</span>
+          <span class="info-value">{{ purchaseInfo.purchaseOrigins }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">采收时间:</span>
+          <span class="info-value"></span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">采购人员:</span>
+          <span class="info-value">{{ purchaseInfo.purchaseUserName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">采购重量:</span>
+          <span class="info-value">{{ purchaseInfo.purchaseWeight }}{{ detail.unit }}</span>
+        </div>
+        <div class="info-item">
+          <span class="info-label">采购日期:</span>
+          <span class="info-value">{{ purchaseInfo.purchaseDate }}</span>
+        </div>
+        <div class="images-section">
+          <span class="section-subtitle">图片:</span>
+          <div class="images-grid">
+            <img v-if="purchaseInfo && purchaseInfo.purchaseImgUrl" class="detail-image" :src="purchaseInfo.purchaseImgUrl" alt="详情图片">
+          </div>
+        </div>
+      </div>
+      
+      <!-- 返回首页按钮 -->
+      <!-- <div class="home-button" @click="goToHome">
+        <svg class="home-icon" viewBox="0 0 16 16" width="32" height="32">
+          <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6a.5.5 0 1 0 .708.708L8 2.207l5.646 5.647a.5.5 0 0 0 .708-.708l-6-6zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm-.5 7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2a.5.5 0 0 1 .5-.5zm0 3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5z"/>
+        </svg>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'PurchaseTraceabilityPage',
+  data() {
+    return {
+      // 使用项目中已有的图标路径
+      productImageSrc: require('@/assets/traceability/1.svg'),
+      detailImageSrc: require('@/assets/traceability/1.svg'),
+      // 定义detail对象来存储从路由获取的参数
+      detail: {},
+      purchaseInfo: {}
+    };
+  },
+  
+  mounted() {
+    console.log('路由参数:');
+    console.log('路由参数:', this.$route.query);
+    // 从路由参数中获取detail对象
+    if (this.$route.query && this.$route.query.detail) {
+      try {
+        // 解析JSON字符串为对象
+        this.detail = JSON.parse(this.$route.query.detail);
+        console.log('获取到的detail参数:', this.detail);
+        this.purchaseInfo = this.detail.purchaseInfo || {};
+      } catch (error) {
+        console.error('解析detail参数失败:', error);
+        this.detail = {};
+        this.purchaseInfo = {};
+      }
+    }
+  },
+  
+  methods: {
+    handleBack() {
+      // 返回上一页
+      this.$router.back();
+    },
+    goToHome() {
+      // 返回首页
+      this.$router.push('/pages/home/home');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.purchase-traceability-page {
+  background-color: #f5f5f5;
+  min-height: 100vh;
+  background-image: url('~@/assets/traceability/bg.jpg');
+  background-size: cover;
+  background-position: center;
+}
+
+.nav-bar {
+  height: 56px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  color: #000;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  padding: 0 16px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
+}
+
+.nav-left {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.back-icon {
+  fill: #333;
+  font-size: 24px;
+}
+
+.nav-title {
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.nav-right {
+  width: 40px;
+}
+
+.content-wrapper {
+  padding: 76px 20px 60px;
+}
+
+.product-image-section {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+  background-color: #fff;
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+}
+
+.product-title {
+  position: absolute;
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  text-align: center;
+  margin-bottom: 10px;
+  padding: 5px 20px;
+  background: #bd0402;
+  border-radius: 5px;
+  box-shadow: 0 1px 4px rgba(255, 107, 107, 0.3);
+  top: -10px;
+}
+
+.product-image {
+  width: 100%;
+  height: 200px;
+  border-radius: 5px;
+  margin-top: 20px;
+  object-fit: contain;
+}
+
+.info-list {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 30px;
+  margin-bottom: 30px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  position: relative;
+  padding-top: 30px;
+  border: 1px solid #ffe6e6;
+}
+
+.info-title {
+  position: absolute;
+  top: -10px;
+  left: 35%;
+  background-color: #bd0402;
+  color: #fff;
+  text-align: center;
+  padding: 5px 20px;
+  margin-bottom: 15px;
+  border-radius: 5px;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.info-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.info-item:last-child {
+  border-bottom: none;
+}
+
+.info-label {
+  font-size: 14px;
+  color: #bd0402;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  text-align: right;
+}
+
+.images-section {
+  padding: 10px 0;
+}
+
+.section-subtitle {
+  font-size: 14px;
+  color: #bd0402;
+  margin-bottom: 10px;
+  display: block;
+}
+
+.images-grid {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.detail-image {
+  width: 100%;
+  height: 200px;
+  border-radius: 5px;
+  object-fit: cover;
+}
+
+.home-button {
+  position: fixed;
+  bottom: 30px;
+  right: 30px;
+  width: 50px;
+  height: 50px;
+  background-color: #bd0402;
+  border-radius: 50%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 2px 10px rgba(189, 4, 2, 0.4);
+  cursor: pointer;
+  z-index: 10;
+}
+
+.home-icon {
+  fill: #fff;
+}
+</style>

+ 294 - 0
src/views/traceability/traceabilityCode/quality.vue

@@ -0,0 +1,294 @@
+<template>
+  <div class="quality-traceability-page">
+    <!-- 顶部导航栏 -->
+    <div class="nav-bar">
+      <div class="nav-left" @click="handleBack">
+        <i class="el-icon-arrow-left back-icon"></i>
+      </div>
+      <div class="nav-title">质检溯源</div>
+      <div class="nav-right"></div>
+    </div>
+    
+    <!-- 主内容区 -->
+    <div class="content-wrapper">
+      <!-- 编辑中标签 -->
+      <div class="edit-tag">编辑中</div>
+      
+      <!-- 产品图片区域 -->
+      <div class="product-image-section">
+        <div class="product-title">
+          质检溯源
+        </div>
+        <div style="height: 200px;">
+          <img v-if="purchaseInfo && purchaseInfo.productImage" class="product-image" :src="purchaseInfo.productImage" alt="产品图片">
+        </div>
+      </div>
+      
+      <!-- 质检信息列表 -->
+      <div class="info-list">
+        <!-- 质检详情标题 -->
+        <div class="info-title">
+          质检详情
+        </div>
+        <div class="info-item">
+          <span class="info-label">检测依据:</span>
+          <span class="info-value"></span>
+        </div>
+        <div class="images-section">
+          <span class="section-subtitle">报告单:</span>
+          <div class="images-grid">
+            <!-- <img class="detail-image" :src="detailImageSrc" alt="质检报告单"> -->
+          </div>
+        </div>
+      </div>
+      
+      <!-- 返回首页按钮 -->
+      <!-- <div class="home-button" @click="goToHome">
+        <svg class="home-icon" viewBox="0 0 16 16" width="32" height="32">
+          <path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6a.5.5 0 1 0 .708.708L8 2.207l5.646 5.647a.5.5 0 0 0 .708-.708l-6-6zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm-.5 7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2a.5.5 0 0 1 .5-.5zm0 3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5z"/>
+        </svg>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'QualityTraceabilityPage',
+  data() {
+    return {
+      // 使用项目中已有的图标路径
+      productImageSrc: require('@/assets/traceability/1.svg'),
+      detailImageSrc: require('@/assets/traceability/1.svg'),
+      detail: {},
+      purchaseInfo: {},
+    };
+  },
+  mounted() {
+    console.log('路由参数:');
+    console.log('路由参数:', this.$route.query);
+    // 从路由参数中获取detail对象
+    if (this.$route.query && this.$route.query.detail) {
+      try {
+        // 解析JSON字符串为对象
+        this.detail = JSON.parse(this.$route.query.detail);
+        console.log('获取到的detail参数:', this.detail);
+        this.purchaseInfo = this.detail.purchaseInfo || {};
+      } catch (error) {
+        console.error('解析detail参数失败:', error);
+        this.detail = {};
+        this.purchaseInfo = {};
+      }
+    }
+  },
+  methods: {
+    handleBack() {
+      // 返回上一页
+      this.$router.back();
+    },
+    goToHome() {
+      // 返回首页
+      this.$router.push('/pages/home/home');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.quality-traceability-page {
+  background-color: #fff;
+  min-height: 100vh;
+  background-image: url('~@/assets/traceability/bg.jpg');
+  background-size: cover;
+  background-position: center;
+}
+
+.nav-bar {
+  height: 56px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  color: #000;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  padding: 0 16px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
+}
+
+.nav-left {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.back-icon {
+  fill: #333;
+  font-size: 24px;
+}
+
+.nav-title {
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.nav-right {
+  width: 40px;
+}
+
+.content-wrapper {
+  padding: 76px 20px 60px;
+  position: relative;
+}
+
+.edit-tag {
+  position: absolute;
+  top: 96px;
+  left: 0;
+  width: 80px;
+  height: 30px;
+  background: linear-gradient(135deg, #ff6b6b, #ff8e53);
+  color: #fff;
+  text-align: center;
+  line-height: 30px;
+  font-size: 13px;
+  border-radius: 0 15px 15px 0;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  z-index: 10;
+}
+
+.product-image-section {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+  background-color: #fff;
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+}
+
+.product-title {
+  position: absolute;
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  text-align: center;
+  margin-bottom: 10px;
+  padding: 5px 20px;
+  background: #bd0402;
+  border-radius: 5px;
+  box-shadow: 0 1px 4px rgba(255, 107, 107, 0.3);
+  top: -10px;
+}
+
+.product-image-container {
+  width: 200px;
+}
+
+.product-image {
+  max-width: 200px;
+  max-height: 200px;
+}
+
+.info-title {
+  position: absolute;
+  top: -10px;
+  left: 35%;
+  background-color: #bd0402;
+  color: #fff;
+  text-align: center;
+  padding: 5px 20px;
+  margin-bottom: 15px;
+  border-radius: 5px;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.info-list {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 30px;
+  margin-bottom: 30px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
+  border: 1px solid #ffe6e6;
+  position: relative;
+  padding-top: 30px;
+}
+
+.info-item {
+  display: flex;
+  align-items: flex-start;
+  padding: 10px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.info-item:last-child {
+  border-bottom: none;
+}
+
+.info-label {
+  font-size: 14px;
+  color: #bd0402;
+  margin-right: 10px;
+  flex: 1;
+  font-weight: 500;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  flex: 2;
+}
+
+.images-section {
+  padding: 10px 0;
+}
+
+.section-subtitle {
+  font-size: 14px;
+  color: #bd0402;
+  margin-bottom: 10px;
+  display: block;
+}
+
+.images-grid {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.detail-image {
+  width: 100%;
+  height: 200px;
+  border-radius: 5px;
+  object-fit: cover;
+}
+
+.home-button {
+  position: fixed;
+  bottom: 30px;
+  right: 30px;
+  width: 50px;
+  height: 50px;
+  background-color: #bd0402;
+  border-radius: 50%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-shadow: 0 2px 10px rgba(189, 4, 2, 0.4);
+  cursor: pointer;
+  z-index: 20;
+}
+
+.home-icon {
+  fill: #fff;
+}
+</style>