فهرست منبع

Merge remote-tracking branch 'origin/master'

yjwang 1 هفته پیش
والد
کامیت
18376eb98a
43فایلهای تغییر یافته به همراه6851 افزوده شده و 216 حذف شده
  1. 1 1
      .env.prod-mengniu
  2. 44 0
      .env.prod-sxtb
  3. 1 0
      package.json
  4. 26 0
      src/api/company/withdrawDetail.js
  5. 70 0
      src/api/crm/business.js
  6. 53 0
      src/api/crm/extDetail.js
  7. 28 0
      src/api/crm/extLog.js
  8. 20 0
      src/api/kdniao/eorder.js
  9. 53 0
      src/api/system/show.js
  10. 111 3
      src/api/watch/deviceInfo.js
  11. 2 1
      src/components/DeviceInfo/AtrialFibrillation.vue
  12. 5 0
      src/components/DeviceInfo/BloodSugar.vue
  13. 13 6
      src/components/DeviceInfo/Bloodoxygen.vue
  14. 5 0
      src/components/DeviceInfo/Heartrate.vue
  15. 8 3
      src/components/DeviceInfo/Pressure.vue
  16. 291 0
      src/components/DeviceInfo/PressureValue.vue
  17. 5 0
      src/components/DeviceInfo/Pulse.vue
  18. 1943 0
      src/components/DeviceInfo/Report.vue
  19. 5 0
      src/components/DeviceInfo/Sleep.vue
  20. 5 0
      src/components/DeviceInfo/Sports.vue
  21. 8 4
      src/components/DeviceInfo/Temperature.vue
  22. 5 0
      src/components/DeviceInfo/Uricacid.vue
  23. 5 0
      src/components/DeviceInfo/Urineketones.vue
  24. 8 1
      src/utils/request.js
  25. 298 0
      src/views/company/companyMoneyLogsDetail/index.vue
  26. 480 0
      src/views/crm/components/addBusiness.vue
  27. 96 0
      src/views/crm/components/setColumn.vue
  28. 826 0
      src/views/crm/customerBusiness/index.vue
  29. 24 18
      src/views/hisStore/components/productAfterSalesOrder.vue
  30. 5 0
      src/views/hisStore/integralGoods/index.vue
  31. 1085 0
      src/views/kdniao/eorder/index.vue
  32. 937 0
      src/views/kdniao/eorder/indexNew.vue
  33. 25 0
      src/views/live/liveConfig/liveLotteryConf.vue
  34. 2 2
      src/views/live/liveConfig/liveRedConf.vue
  35. 6 6
      src/views/live/liveCoupon/index.vue
  36. 140 2
      src/views/system/config/config.vue
  37. 10 0
      src/views/system/config/integralConfig.vue
  38. 34 14
      src/views/watch/deviceInfo/details.vue
  39. 94 47
      src/views/watch/deviceInfo/index.vue
  40. 11 3
      src/views/watch/iot/index.vue
  41. 58 45
      src/views/watch/isBind/index.vue
  42. 4 59
      src/views/watch/isSend/index.vue
  43. 1 1
      vue.config.js

+ 1 - 1
.env.prod-mengniu

@@ -23,7 +23,7 @@ VUE_APP_COS_BUCKET = mengniu-1323137866
 # 存储桶配置
 VUE_APP_COS_REGION = ap-chongqing
 # 线路一地址
-VUE_APP_VIDEO_LINE_1 = http://mengniutcpv.ylrzcloud.com
+VUE_APP_VIDEO_LINE_1 = https://mengniutcpv.ylrzcloud.com
 # 线路二地址
 VUE_APP_VIDEO_LINE_2 = https://mengniuobs.ylrztop.com
 

+ 44 - 0
.env.prod-sxtb

@@ -0,0 +1,44 @@
+# 页面标题
+VUE_APP_TITLE =挑宝易购总管理系统
+# 首页菜单标题
+VUE_APP_TITLE_INDEX =挑宝易购
+# 公司名称
+VUE_APP_COMPANY_NAME =西安挑宝益康医药连锁有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =宁ICP备2022001349号
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/sxjz.png
+# 存储桶配置
+VUE_APP_OBS_ACCESS_KEY_ID = K2UTJGIN7UTZJR2XMXYG
+# 存储桶配置
+VUE_APP_OBS_SECRET_ACCESS_KEY = sbyeNJLbcYmH6copxeFP9pAoksM4NIT9Zw4x0SRX
+# 存储桶配置
+VUE_APP_OBS_SERVER = https://obs.cn-north-4.myhuaweicloud.com
+# 存储桶配置
+VUE_APP_OBS_BUCKET = sxjz-hw079058881
+# 存储桶配置
+VUE_APP_COS_BUCKET = sxjz-1323137866
+# 存储桶配置
+VUE_APP_COS_REGION = ap-chongqing
+# 线路一地址
+VUE_APP_VIDEO_LINE_1 = https://sxjztcpv.ylrzcloud.com
+# 线路二地址
+VUE_APP_VIDEO_LINE_2 = https://sxjzobs.ylrztop.com
+#火山云视频地址域名
+VUE_APP_VIDEO_URL = https://sxjzvolcengine.ylrztop.com
+#火山云视频点播空间名
+VUE_APP_HSY_SPACE = sxjz-2114522511
+
+# 开发环境配置
+ENV = 'development'
+
+# 今正互联网医院管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "build:prod-bly": "vue-cli-service build --mode prod-bly",
     "build:prod-fzbt": "vue-cli-service build --mode prod-fzbt",
     "build:prod-sxjz": "vue-cli-service build --mode prod-sxjz",
+    "build:prod-sxtb": "vue-cli-service build --mode prod-sxtb",
     "build:prod-xfk": "vue-cli-service build --mode prod-xfk",
     "build:prod-jnmy": "vue-cli-service build --mode prod-jnmy",
     "build:prod-knt": "vue-cli-service build --mode prod-knt",

+ 26 - 0
src/api/company/withdrawDetail.js

@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+/** 分公司下拉(关键词模糊) */
+export function listWithdrawDetailCompanyOptions(query) {
+  return request({
+    url: '/company/withdrawDetailAdmin/companyOptions',
+    method: 'get',
+    params: query
+  })
+}
+
+export function listWithdrawDetail(query) {
+  return request({
+    url: '/company/withdrawDetailAdmin/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function exportWithdrawDetail(query) {
+  return request({
+    url: '/company/withdrawDetailAdmin/export',
+    method: 'get',
+    params: query
+  })
+}

+ 70 - 0
src/api/crm/business.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+
+// 查询商机列表
+export function listBusiness(query) {
+  return request({
+    url: '/crm/business/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询商机详细
+export function getBusiness(businessId) {
+  return request({
+    url: '/crm/business/' + businessId,
+    method: 'get'
+  })
+}
+
+// 新增商机
+export function addBusiness(data) {
+  return request({
+    url: '/crm/business',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改商机
+export function updateBusiness(data) {
+  return request({
+    url: '/crm/business',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除商机
+export function delBusiness(businessId) {
+  return request({
+    url: '/crm/business/' + businessId,
+    method: 'delete'
+  })
+}
+
+// 导出商机
+export function exportBusiness(query) {
+  return request({
+    url: '/crm/business/export',
+    method: 'get',
+    params: query
+  })
+}
+
+//捞取商机
+export function gainBusiness(data) {
+  return request({
+    url: '/crm/business/gain',
+    method: 'post',
+    data: data
+  })
+}
+
+// // 删除商机
+// export function setBusinessPool(businessId) {
+//   return request({
+//     url: '/crm/business/' + businessId,
+//     method: 'get'
+//   })
+// }

+ 53 - 0
src/api/crm/extDetail.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询字段扩展详情列表
+export function listDetail(query) {
+  return request({
+    url: '/crm/detail/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询字段扩展详情详细
+export function getDetail(id) {
+  return request({
+    url: '/crm/detail/' + id,
+    method: 'get'
+  })
+}
+
+// 新增字段扩展详情
+export function addDetail(data) {
+  return request({
+    url: '/crm/detail',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改字段扩展详情
+export function updateDetail(data) {
+  return request({
+    url: '/crm/detail',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除字段扩展详情
+export function delDetail(id) {
+  return request({
+    url: '/crm/detail/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出字段扩展详情
+export function exportDetail(query) {
+  return request({
+    url: '/crm/detail/export',
+    method: 'get',
+    params: query
+  })
+}

+ 28 - 0
src/api/crm/extLog.js

@@ -0,0 +1,28 @@
+import request from '@/utils/request'
+
+// 查询修改字段扩展日志列表
+export function listLog(query) {
+  return request({
+    url: '/crm/log/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询修改字段扩展日志详细
+export function getLog(logId) {
+  return request({
+    url: '/crm/log/' + logId,
+    method: 'get'
+  })
+}
+
+
+// 导出修改字段扩展日志
+export function exportLog(query) {
+  return request({
+    url: '/crm/log/export',
+    method: 'get',
+    params: query
+  })
+}

+ 20 - 0
src/api/kdniao/eorder.js

@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+
+// 快递鸟电子面单下单
+export function submitKdniaoEOrder(data) {
+  return request({
+    url: '/kdniao/eorder/submit',
+    method: 'post',
+    data: data
+  })
+}
+
+
+// 快递鸟统一规则版下单
+export function submitKdniaoUniversalEOrder(data) {
+  return request({
+    url: '/kdniao/universal/eorder/submit',
+    method: 'post',
+    data: data
+  })
+}

+ 53 - 0
src/api/system/show.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询用户展示字段列表
+export function listShow(query) {
+  return request({
+    url: '/system/show/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询用户展示字段详细
+export function getShow(type) {
+  return request({
+    url: '/system/show/' + type,
+    method: 'get'
+  })
+}
+
+// 新增用户展示字段
+export function addShow(data) {
+  return request({
+    url: '/system/show',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改用户展示字段
+export function updateShow(data) {
+  return request({
+    url: '/system/show',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除用户展示字段
+export function delShow(id) {
+  return request({
+    url: '/system/show/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出用户展示字段
+export function exportShow(query) {
+  return request({
+    url: '/system/show/export',
+    method: 'get',
+    params: query
+  })
+}

+ 111 - 3
src/api/watch/deviceInfo.js

@@ -69,13 +69,21 @@ export function exportDeviceInfo(query) {
     params: query
   })
 }
+export function exportDeviceIdAndIotInfo(query) {
+  return request({
+    url: '/watch-api/watch/deviceInfo/exportDeviceIdAndIotInfo',
+    method: 'get',
+    params: query
+  })
+}
+
 
   // 查询用户信息
-export function getUser(deviceId) {
+export function getUser(data) {
   return request({
     url: '/watch-api/device/set/up/getUserInfo',
     method: 'get',
-    params: { deviceId } 
+    params:data
   })
 }
 
@@ -104,6 +112,14 @@ export function queryByDate(date,deviceId) {
   })
 }
 
+export function queryHrList(data) {
+  return request({
+    url: `/watch-api/watch/heart/rate/list`,
+    method: 'get',
+    params: data
+  })
+}
+
 //exportHeartDate导出
 export function exportHeartDate(date,deviceId) {
   return request({
@@ -149,6 +165,15 @@ export function queryGlucoseDate(date,deviceId) {
   })
 }
 
+// 根据时间获取血糖数据
+export function queryGlucoseList(data) {
+  return request({
+    url: `/watch-api/watch/blood/glucose/list`,
+    method: 'get',
+    params: data
+  })
+}
+
 // 根据时间获取房颤数据
 export function queryAfDate(date,deviceId) {
   return request({
@@ -167,6 +192,15 @@ export function queryPressure(date,deviceId) {
   })
 }
 
+// 根据时间获取血压数据
+export function queryPressureList(data) {
+  return request({
+    url: `/watch-api/watch/blood/pressure/list`,
+    method: 'get',
+    params:data
+  })
+}
+
 // 根据时间获取温度数据
 export function queryTemperature(date,deviceId) {
   return request({
@@ -194,6 +228,15 @@ export function querySpo2(date,deviceId) {
   })
 }
 
+// 根据时间获取血氧数据
+export function querySpo2List(data) {
+  return request({
+    url: `/watch-api/watch/spo2/data/list`,
+    method: 'get',
+    params: data
+  })
+}
+
 // 根据时间获取睡眠数据
 export function querySleep(date,deviceId) {
   return request({
@@ -203,6 +246,15 @@ export function querySleep(date,deviceId) {
   })
 }
 
+// 根据时间获取睡眠数据
+export function querySleepList(data) {
+  return request({
+    url: `/watch-api/watch/sleep/data/list`,
+    method: 'get',
+    params: data
+  })
+}
+
 // 获取预警数据
 export function queryAlarm(status) {
   return request({
@@ -269,6 +321,15 @@ export function queryUaData(data) {
   })
 }
 
+//获取尿酸数据
+export function queryUaList(data) {
+  return request({
+      url: '/watch-api/watch/third/ua/list',
+      method: 'get',
+      params:data
+  })
+}
+
 //获最新健康数据
 export function queryLastHealthData(data) {
   return request({
@@ -349,4 +410,51 @@ export function queryIotList(data) {
       method: 'get',
       params:data
   })
-}
+}
+
+//查询某个时间段的压力数据 分页
+export function queryFatiguePageByDate(data) {
+  return request({
+      url: '/watch-api/watch/fatigue/page',
+      method: 'get',
+      params:data
+  })
+}
+
+// 根据时间获取压力数据
+export function queryFatigue(date,deviceId) {
+  return request({
+    url: `/watch-api/watch/fatigue/queryByDateAndDeviceId`,
+    method: 'get',
+    params: { date,deviceId } 
+  })
+}
+
+
+//健康周报-步数,卡路里,睡眠等信息
+export function getUserHealthInfoByDeviceId(data) {
+  return request({
+      url: '/watch-api/watch/sport/data/getUserHealthInfoByDeviceId',
+      method: 'get',
+      params:data
+  })
+}
+
+//健康周报-步数,卡路里,睡眠等信息
+export function queryHealthReport(data) {
+  return request({
+      url: '/watch-api/device/queryHealthReport',
+      method: 'get',
+      params:data
+  })
+}
+
+//舌诊
+export function queryTongueList(data) {
+  return request({
+      url: '/watch-api/device/getHealthTongueList',
+      method: 'get',
+      params:data
+  })
+}
+

+ 2 - 1
src/components/DeviceInfo/AtrialFibrillation.vue

@@ -21,7 +21,8 @@
     </div>
     <div class="afib-diagnosis">
       <div class="afib-diagnosis-title">诊断结论:</div>
-      <div class="afib-diagnosis-note">{{conclusion||'没有房颤数据'}}</div>
+      <div class="afib-diagnosis-note">{{conclusion||'今日暂无数据,请选择其他日期'}}</div>
+      <div class="afib-diagnosis-note">散点图: 基于⾮线性混沌理论,依靠计算机快速运算技术,对⻓程RR间期数据进⾏图形表达,⽤于⼼率变异</div>
       <div class="afib-diagnosis-tips">
         注:非医疗诊断,供参考,请以医院确诊结论为准。
       </div>

+ 5 - 0
src/components/DeviceInfo/BloodSugar.vue

@@ -17,6 +17,11 @@
             </el-table-column>
             <el-table-column prop="bloodGlucose" align="center" label="测量结果">
             </el-table-column>
+            <template #empty>
+                <div>
+                    今日暂无数据,请选择其他日期
+                </div>
+            </template>
         </el-table>
 
     </div>

+ 13 - 6
src/components/DeviceInfo/Bloodoxygen.vue

@@ -19,12 +19,19 @@
         <!-- 列表展示 -->
         <div v-show="detailsType">
             <el-table :data="dataList">
-            <el-table-column type="index" label="序号" align="center"/>
-            <el-table-column label="时间" align="center" prop="time" />
-            <el-table-column label="血氧" align="center" prop="avgBoxy" />
+                <el-table-column type="index" label="序号" align="center"/>
+                <el-table-column label="时间" align="center" prop="time" />
+                <el-table-column label="血氧" align="center" prop="avgBoxy" />
+                 <!-- 使用 empty 插槽自定义无数据时的显示内容 -->
+                <template #empty>
+                    <div>
+                        今日暂无数据,请选择其他日期
+                    </div>
+                </template>
             </el-table>
             <pagination v-show="(total>0) && detailsType" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getDataPage"/>
         </div>
+        
     </div>
 </template>
 
@@ -87,9 +94,9 @@ export default {
                 xAxis: {
                     type: "time",
                     splitNumber: 6,
-                    axisLine: {
-                        show: false,
-                    },
+                    // axisLine: {
+                    //     show: false,
+                    // },
                     axisTick: {
                         show: false,
                     },

+ 5 - 0
src/components/DeviceInfo/Heartrate.vue

@@ -65,6 +65,11 @@
         <el-table-column label="最小心率" align="center" prop="minBpm" />
         <el-table-column label="状态" align="center" prop="status" :formatter="statusFormatter"/>
         <el-table-column label="时间" align="center" prop="createTime" />
+        <template #empty>
+            <div>
+                今日暂无数据,请选择其他日期
+            </div>
+        </template>
       </el-table>
       
     </div>

+ 8 - 3
src/components/DeviceInfo/Pressure.vue

@@ -24,6 +24,11 @@
                 <el-table-column label="sbp(mmgh)" align="center" prop="sbp" />
                 <el-table-column label="dbp(mmgh)" align="center" prop="dbp" />
                 <el-table-column label="hr(次/分)" align="center" prop="hr" />
+                <template #empty>
+                <div>
+                    今日暂无数据,请选择其他日期
+                </div>
+            </template>
             </el-table>
             <pagination v-show="(dataListTotal > 0) && detailsType" :total="dataListTotal" :page.sync="tqueryParams.pageNum" :limit.sync="tqueryParams.pageSize" @pagination="getTableList"/>
         </div>
@@ -66,9 +71,9 @@ export default {
                     boundaryGap: false,
                     type: "time",
                     splitNumber: 6,
-                    axisLine: {
-                        show: false,
-                    },
+                    // axisLine: {
+                    //     show: false,
+                    // },
                     axisTick: {
                         show: false,
                     },

+ 291 - 0
src/components/DeviceInfo/PressureValue.vue

@@ -0,0 +1,291 @@
+<template>
+    <div>
+        <div class="box-header">
+            <div class="box-title boldtext">
+                压力 <span style="margin-left: 14px">{{ currentDate }}</span>
+            </div>
+            <div>
+                <!-- <el-button @click="exportData" size="small" style="margin-right: 20px"
+            >导出</el-button
+          > -->
+                <el-date-picker size="small" v-model="currentDatePicker" type="date" placeholder="选择日期"
+                    value-format="yyyy-MM-dd" @change="datePickerchange">
+                </el-date-picker>
+            </div>
+        </div>
+        <div v-show="!detailsType">
+            <div style="min-width: 841px; height: 320px; width: 100%;">
+                <div ref="heartrateChart" style="width: 100%; height: 100%;min-width: 783px;"></div>
+            </div>
+        </div>
+        <!-- 列表展示 -->
+        <div v-show="detailsType">
+            <el-table :data="dataList" style="width: 100%">
+                <el-table-column prop="createTime" label="时间" />
+                <el-table-column prop="pressure" label="压力值" />
+                <template #empty>
+                    <div>
+                        今日暂无数据,请选择其他日期
+                    </div>
+                </template>
+            </el-table>
+            <pagination v-show="(total > 0) && detailsType" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getDataPage"/>
+        </div>
+        
+    </div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+import { queryFatiguePageByDate, queryFatigue} from "@/api/watch/deviceInfo";
+export default {
+    props: {
+        detailsType: { 
+            type: Boolean,
+            default: false
+        },
+        deviceId: {
+            type: String || Number,
+            default: "",
+        },
+    },
+    data() {
+        return {
+            total:0,
+            dataList:[],
+            queryParams:{
+                pageNum: 1,
+                pageSize: 10,
+                deviceId:"",
+                beginTime:"",
+                endTime:""
+            },
+            currentDate: this.parseTime(new Date(), "{y}-{m}-{d}"),
+            currentDatePicker: "",
+            exportLoading: false,
+            chart: null,
+            chartOptions: {
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {},
+                xAxis: {
+                    boundaryGap: false,
+                    type: "time",
+                    splitNumber: 6,
+                    axisTick: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: false,
+                    },
+                    axisLabel: {
+                        color: "#999999",
+                        margin: 20,
+                        // fontSize: 14,
+                        fontWeight: "bold",
+                        formatter: (value) => {
+                            return this.parseTime(value, "{h}:{i}");
+                        },
+                    }
+                },
+                yAxis: {
+                    type: 'value',
+                    max: 100, // 设置y轴最大值为100
+                    // splitLine: {
+                    //     show: false // 去掉y轴的横线
+                    // }
+                },
+                series: [
+                    {
+                        name: '压力值',
+                        type: 'line',
+                        data: [],
+                        smooth: true
+                    }
+                    
+                ]
+            }
+
+        }
+    },
+    methods: {
+        getDataPage(){
+            this.loading = true;
+            this.queryParams.deviceId = this.deviceId;
+            this.queryParams.beginTime = this.currentDate + " 00:00:00";
+            this.queryParams.endTime = this.currentDate + " 23:59:59";
+            queryFatiguePageByDate(this.queryParams)
+                .then((response) => {
+                if (response.data) {
+                    this.dataList = response.data.rows;
+                    this.total = response.data.total;
+                    this.loading = false;
+                } else {
+                    console.warn("分页数据返回为空或格式不正确:", response);
+                }
+                })
+                .catch((error) => {
+                console.error("获取分页数据失败:", error);
+                });
+        },
+        datePickerchange() {
+            this.currentDate = this.currentDatePicker || this.parseTime(new Date(), "{y}-{m}-{d}");
+            if(this.detailsType){
+                this.getDataPage();
+            } else{
+                this.getDetailsData();
+            }
+        },
+        initChart() {
+            const chartElement = this.$refs.heartrateChart;
+            const sportChartElement = this.$refs.sportHeartrate;
+            if (chartElement) {
+                this.chart = echarts.init(chartElement);
+                this.seChartOptions()
+            }
+            if (sportChartElement) {
+                this.sportChart = echarts.init(sportChartElement);
+                this.sportChart.setOption(this.sportChartOption);
+            }
+        },
+        seChartOptions() {
+            this.chartOptions.xAxis.min = this.currentDate + ' 00:00:00'
+            this.chartOptions.xAxis.max = this.currentDate + ' 23:59:59'
+            this.chart.setOption(this.chartOptions);
+        },
+        // 获取体温折线图
+        getDetailsData() {
+            queryFatigue(this.currentDate, this.deviceId)
+                .then((response) => {
+                    if (response && response.data) {
+                        // console.log("------------------" + JSON.stringify(response.data, null, 2))
+                        const data = response.data.map((item) => (
+                            {
+                                value: [
+                                    new Date(item.createTime).getTime(),
+                                    item.pressure,
+                                ],
+                            }));
+                     
+
+                        // 将数据分别赋值给不同的 series
+                        this.chartOptions.series[0].data = data;
+                        // 更新图表
+                        if (this.detailsType == false) {
+                            this.initChart();
+                        }
+                    } else {
+                        console.warn("数据返回为空或格式不正确:", response);
+                    }
+                })
+                .catch((error) => {
+                    console.error("获取数据失败:", error);
+                });
+        },
+       
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+@mixin u-flex($flexD, $alignI, $justifyC) {
+    display: flex;
+    flex-direction: $flexD;
+    align-items: $alignI;
+    justify-content: $justifyC;
+}
+
+.fz15 {
+    font-size: 15px !important;
+}
+
+.boldtext {
+    font-size: 16px;
+    color: #292929;
+    line-height: 30px;
+    font-weight: 700;
+}
+
+.box-header {
+    padding: 0 15px;
+    @include u-flex(row, center, space-between);
+    height: 50px;
+}
+
+.box-title {
+    color: #292929;
+    font-size: 16px;
+    font-family: PingFang SC-Medium;
+    padding-left: 10px;
+    border-left: 4px solid #2284ff;
+    line-height: 13px;
+}
+
+.heartrate-data {
+    @include u-flex(row, center, space-around);
+    margin-top: 30px;
+
+    &-item {
+        height: 60px;
+        width: 196px;
+        border-radius: 4px;
+        box-shadow: 0 0 10px rgba(1, 24, 54, 0.1);
+        overflow: hidden;
+        @include u-flex(row, center, flex-start);
+
+        img {
+            height: 41px;
+            width: 41px;
+        }
+    }
+
+    &-imgbox {
+        flex-shrink: 0;
+        height: 60px;
+        width: 60px;
+        @include u-flex(row, center, center);
+    }
+
+    &-right {
+        flex: 1;
+        text-align: center;
+        font-size: 13px;
+        color: #606165;
+        font-family: PingFang SC-Medium;
+        background: #fff;
+        height: 60px;
+    }
+
+    &-num {
+        font-size: 22px;
+        color: #2c2c3b;
+        line-height: 38px;
+    }
+}
+
+.heartrate-sport {
+    @include u-flex(row, flex-start, flex-start);
+    margin-top: 20px;
+
+    &-l {
+        width: 490px;
+        flex-shrink: 0;
+    }
+
+    &-r {
+        flex: 1;
+        max-width: 720px;
+    }
+
+    &-lechart {
+        width: 490px;
+        height: 300px;
+    }
+
+    &-rechart {
+        width: 100%;
+        @include u-flex(row, center, center);
+    }
+}
+</style>

+ 5 - 0
src/components/DeviceInfo/Pulse.vue

@@ -70,6 +70,11 @@
           <el-table-column type="index" label="序号" align="center"/>
           <el-table-column label="时间" align="center" prop="time" />
           <el-table-column label="脉搏" align="center" prop="pulseRate" />
+          <template #empty>
+                <div>
+                    今日暂无数据,请选择其他日期
+                </div>
+            </template>
         </el-table>
         <pagination v-show="(dataListTotal > 0) && detailsType" :total="dataListTotal" :page.sync="tqueryParams.pageNum" :limit.sync="tqueryParams.pageSize" @pagination="getTableList"/>
       </div>

+ 1943 - 0
src/components/DeviceInfo/Report.vue

@@ -0,0 +1,1943 @@
+<template>
+    <div class="weekly-report no-scrollbar">
+      <div class="report-header">
+        <div class="report-title">
+          <img class="title-icon" src="@/assets/images/watchApi/title-icon.png" />
+          <span>周报</span>
+        </div>
+        <div class="date-selector">
+          <el-date-picker
+            :picker-options="{ firstDayOfWeek: 1 }"
+            v-model="selectedDate"
+            type="week"
+            format="yyyy 第 WW 周"
+            placeholder="选择周"
+            @change="handleDateChange"
+          ></el-date-picker>
+        </div>
+      </div>
+  
+      <el-card class="report-card">
+        <div class="progress-section">
+          <div class="progress-container">
+            <el-progress type="circle" :percentage="healthIndex" :width="120" color="#FF9F43"></el-progress>
+            <div class="progress-info">
+              <div class="progress-label">健康指数</div>
+            </div>
+            <div class="health-status">{{ healthStatus }}</div>
+            <div class="health-description">{{ healthDesc }}</div>
+          </div>
+          
+          <div class="health-metrics-container">
+            <div class="health-metrics-row">
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ height }}<span class="metric-unit">cm</span></div>
+                  <div class="metric-label">身高</div>
+                </div>
+                <div class="metric-icon orange">
+                  <i class="el-icon-sort"></i>
+                </div>
+              </div>
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ weight }}<span class="metric-unit">kg</span></div>
+                <div class="metric-label">体重</div>
+                </div>
+                <div class="metric-icon green">
+                  <i class="el-icon-top"></i>
+                </div>
+              </div>
+            </div>
+            
+            <div class="health-metrics-row">
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ bmiStatus }}</div>
+                <div class="metric-label">BMI</div>
+                </div>
+                <div class="metric-icon blue">
+                  <i class="el-icon-plus"></i>
+                </div>
+              </div>
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ caloriesData.value }}<span class="metric-unit">{{ caloriesData.unit }}</span></div>
+                  <div class="metric-label">{{ caloriesData.label }}</div>
+                </div>
+                <div class="metric-icon red">
+                  <i class="el-icon-hot-water"></i>
+                </div>
+              </div>
+            </div>
+            
+            <div class="health-metrics-row">
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ stepsData.value }}<span class="metric-unit">{{ stepsData.unit }}</span></div>
+                  <div class="metric-label">{{ stepsData.label }}</div>
+                </div>
+                <div class="metric-icon orange">
+                  <i class="el-icon-s-promotion"></i>
+                </div>
+              </div>
+              <div class="metric-item">
+                <div class="metric-data">
+                  <div class="metric-value">{{ sleepCardData.status }}</div>
+                <div class="metric-label">{{ sleepCardData.label }}</div>
+                </div>
+                <div class="metric-icon purple">
+                  <i class="el-icon-moon"></i>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+  
+        <!-- 健康数据 (Health Data) Section -->
+        <div class="health-data-section">
+          <div class="section-title">健康数据</div>
+          
+          <!-- 血糖趋势 (Blood Sugar Trend) -->
+          <div class="trend-chart">
+            <div class="trend-title">血糖趋势</div>
+            <div class="trend-subtitle">单位: mmol/L</div>
+            <div class="line-chart-container" ref="bloodSugarChart"></div>
+            <div class="trend-advice">
+            </div>
+          </div>
+          
+          <!-- 尿酸趋势 (Uric Acid Trend) -->
+          <div class="trend-chart">
+            <div class="trend-title">尿酸趋势</div>
+            <div class="trend-subtitle">单位: μmol/L</div>
+            <div class="bar-chart-container" ref="uricAcidChart"></div>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #4CD964;"></span>
+                <span>150~416μmol/L</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #FFCC00;"></span>
+                <span>&lt;150μmol/L</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #FF6B6B;"></span>
+                <span>&gt;416μmol/L</span>
+              </div>
+            </div>
+          </div>
+
+           <div class="trend-chart">
+            <div class="trend-title">血压趋势</div>
+            <div class="trend-subtitle">单位: mmgh</div>
+            <div class="line-chart-container" ref="bloodPressureChart"></div>
+          </div>
+
+           <div class="trend-chart">
+            <div class="trend-title">心率趋势</div>
+            <div class="trend-subtitle">单位: 次/分</div>
+            <div class="line-chart-container" ref="heartRateChart"></div>
+          </div>
+
+           <div class="trend-chart">
+            <div class="trend-title">血氧趋势</div>
+            <div class="trend-subtitle"></div>
+            <div class="line-chart-container" ref="bloodOxygenChart"></div>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #4CD964;"></span>
+                <span>&lt;90%</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #FFCC00;"></span>
+                <span>&lt;95%</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #FF6B6B;"></span>
+                <span>其他</span>
+              </div>
+            </div>
+          </div>
+
+           <div class="trend-chart">
+            <div class="trend-title">睡眠趋势</div>
+            <div class="trend-subtitle"></div>
+            <div class="line-chart-container" ref="sleepChart"></div>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #8C37E6;"></span>
+                <span>深睡</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #D138CF;"></span>
+                <span>浅睡</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #F88082;"></span>
+                <span>快速眼动</span>
+              </div>
+              <div class="legend-item">
+                <span class="legend-color" style="background-color: #FDBD27;"></span>
+                <span>清醒</span>
+              </div>
+            </div>
+          </div>
+          
+         
+        </div>
+  
+        
+        <div class="health-archive-section">
+          <div class="section-title">健康档案</div>
+          <div class="archive-items">
+            <!-- 健康预测 -->
+            <div class="archive-item">
+              <div class="archive-icon warning">
+                <i class="el-icon-warning-outline"></i>
+              </div>
+              <div class="archive-content">
+                <div class="archive-title">健康预测</div>
+                <div class="archive-text" v-for="(item, idx) in healthArchive" :key="'prediction' + idx">
+                  {{ item.prediction }}
+                </div>
+              </div>
+            </div>
+
+            <!-- 解析 -->
+            <div class="archive-item">
+              <div class="archive-icon analysis">
+                <i class="el-icon-data-analysis"></i>
+              </div>
+              <div class="archive-content">
+                <div class="archive-title">解析</div>
+                <div class="archive-text" v-for="(item, idx) in healthArchive" :key="'analysis' + idx">
+                  {{ item.analysis }}
+                </div>
+              </div>
+            </div>
+
+            <!-- 治疗建议 -->
+            <div class="archive-item">
+              <div class="archive-icon suggestion">
+                <i class="el-icon-first-aid-kit"></i>
+              </div>
+              <div class="archive-content">
+                <div class="archive-title">治疗建议</div>
+                <div class="archive-text" v-for="(item, idx) in healthArchive" :key="'suggestion' + idx">
+                  {{ item.suggestion }}
+                </div>
+              </div>
+            </div>
+
+            <!-- 注意事项 -->
+            <div class="archive-item">
+              <div class="archive-icon attention">
+                <i class="el-icon-info"></i>
+              </div>
+              <div class="archive-content">
+                <div class="archive-title">注意事项</div>
+                <div class="archive-text" v-for="(item, idx) in healthArchive" :key="'precautions' + idx">
+                  {{ item.precautions }}
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- AI舌诊 (AI Tongue Diagnosis) Section -->
+        <div class="ai-diagnosis-section">
+          <div class="section-title">AI舌诊</div>
+          <div class="date-selector-tabs">
+            <div 
+              v-for="date in weekDates" 
+              :key="date"
+              :class="['date-tab', selectedDiagnosisDate === date ? 'active' : '']"
+              @click="selectedDiagnosisDate = date"
+            >
+              {{ date }}
+            </div>
+          </div>
+          
+          <div class="diagnosis-content" v-if="tongueData">
+            <div class="diagnosis-time">检测时间 {{ tongueData.createTime || '未知时间' }}</div>
+            
+            <div class="diagnosis-result">
+              <div class="diagnosis-label">您属于</div>
+              <div class="diagnosis-type">{{ tongueData.typeName || '未知体质' }}</div>
+            </div>
+            
+            <div class="diagnosis-section">
+              <div class="diagnosis-section-title">舌苔特征</div>
+              <div class="diagnosis-feature">
+                <div class="feature-dot"></div>
+                <div class="feature-title">{{ tongueData.taiseName || '未知' }}</div>
+                <div class="feature-description">{{ tongueData.taiseDesc || '未知' }}</div>
+              </div>
+              <div class="diagnosis-feature">
+                <div class="feature-dot"></div>
+                <div class="feature-title">{{ tongueData.shemianName || '未知' }}</div>
+                <div class="feature-description">{{ tongueData.shemianDesc || '未知' }}</div>
+              </div>
+            </div>
+            
+            <div class="diagnosis-section">
+              <!-- <div class="diagnosis-section-title">体质解析</div> -->
+              <div 
+                class="diagnosis-analysis" 
+                v-for="(item, index) in tongueData.typeJsonObj" 
+                :key="index"
+              >
+                <div class="diagnosis-section-title">{{ item.name }}</div>
+                <div class="analysis-content">{{ item.value }}</div>
+              </div>
+            </div>
+          </div>
+          <div class="diagnosis-content" v-else>
+            <div class="no-data">暂无舌诊数据</div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+  </template>
+  
+  <script>
+  import * as echarts from 'echarts';
+  import {getUserHealthInfoByDeviceId,queryHealthReport,queryGlucoseList,queryUaList,queryPressureList,queryHrList,querySpo2List,querySleepList,queryTongueList} from "@/api/watch/deviceInfo";
+  export default {
+    props: {
+        // detailsType: { 
+        //     type: Boolean,
+        //     default: false
+        // },
+        userInfo: {
+          type: Object,
+          default: () => ({})
+        },
+        deviceId: {
+            type: String || Number,
+            default: "",
+        },
+    },
+    name: 'WeeklyReport',
+    data() {
+      return {
+        sleepDefaultInfo: {
+          4: { name: "浅睡", color: "#D138CF", yValue: 7 },
+          3: { name: "深睡", color: "#8C37E6", yValue: 3 },
+          6: { name: "清醒", color: "#FDBD27", yValue: 9 },
+          7: { name: "快速眼动", color: "#F88082", yValue: 12 },
+        },
+        startTime: null,
+        endTime:  null,
+        // 基础数据
+        height: '--',
+        weight: '--',
+        bmi: 0,
+        bmiStatus: '--',
+        healthIndex: 0,
+        healthStatus: '--',
+        healthDesc:"",
+        // 健康指标数据
+        caloriesData: {
+          value: '--',
+          unit: 'kcal',
+          label: '卡路里'
+        },
+        stepsData: {
+          value: '--',
+          unit: '步',
+          label: '步数'
+        },
+        sleepCardData: {
+          status: '--',
+          label: '睡眠'
+        },
+        selectedDate: new Date(),
+        // 健康档案
+        healthArchive: [],
+        selectedDiagnosisDate: '12/27',
+        bloodSugarChart: null,
+        uricAcidChart: null,
+        bloodPressureChart:null,
+        heartRateChart:null,
+        bloodOxygenChart:null,
+        sleepChart:null,
+        bloodSugarData: {
+          values: []
+        },
+       
+        uricAcidData: {
+          values: [
+          ]
+        },
+        bloodPressureData:{},
+        heartRateData:{},
+        bloodOxygenData:{values: []},
+        sleepData:[],
+        tongueData:null
+      };
+    },
+    mounted() {
+      this.$nextTick(() => {
+        this.initBloodSugarChart();
+        this.initBloodPressureChart();
+        this.initUricAcidChart();
+        this.initHeartRateChart();
+        this.initBloodOxygenChart();
+        this.initSleepChart();
+        this.setInitialDiagnosisDate();
+      });
+    },
+    computed: {
+      weekDates() {
+        const dates = [];
+        const startOfWeek = new Date(this.selectedDate);
+        startOfWeek.setDate(this.selectedDate.getDate() - (this.selectedDate.getDay() || 7) + 1); // Set to Monday
+        for (let i = 0; i < 7; i++) {
+          const date = new Date(startOfWeek);
+          date.setDate(startOfWeek.getDate() + i);
+          dates.push(this.formatDate(date));
+        }
+        return dates;
+      }
+    },
+    watch: {
+      userInfo: {
+        handler(newVal) {
+          if (newVal) {
+            this.updateUserInfo();
+          }
+        },
+        immediate: true,
+        deep: true
+      },
+      selectedDate() {
+        this.setInitialDiagnosisDate();
+      },
+      selectedDiagnosisDate(newVal) {
+        if (newVal) {
+          this.getTongueDate();
+        }
+      }
+    },
+    methods: {
+      formatDate(date) {
+        return `${date.getMonth() + 1}/${date.getDate()}`;
+      },
+      setInitialDiagnosisDate() {
+        this.selectedDiagnosisDate = this.weekDates[0] || null;
+      },
+      getWeekDates(date) {
+        const startOfWeek = new Date(date);
+        const endOfWeek = new Date(date);
+        startOfWeek.setDate(date.getDate() - (date.getDay() || 7) + 1); // 如果是周日,getDay() 返回 0,需要加 7
+         // 将日期设置为周一,时间为00:00:00
+        // startOfWeek.setDate(date.getDate() - date.getDay());
+        startOfWeek.setHours(0, 0, 0, 0);
+
+        // 将日期设置为周日,时间为23:59:59
+        endOfWeek.setDate(date.getDate() - (date.getDay() || 7) + 7); // 如果是周日,getDay() 返回 0,需要加 7
+        // endOfWeek.setDate(date.getDate() - date.getDay() + 6);
+        endOfWeek.setHours(23, 59, 59, 999);
+       
+        this.startTime= startOfWeek,
+        this.endTime=  endOfWeek
+      },
+      // 更新基本信息
+      updateUserInfo() {
+        if (this.userInfo) {
+          // 设置身高体重
+          this.height = this.userInfo.height || '--';
+          this.weight = this.userInfo.weight || '--';
+          
+          // 计算BMI
+          this.calculateBMI();}
+      },
+      // 计算BMI
+      calculateBMI() {
+        if (this.height && this.weight && this.height !== '--' && this.weight !== '--') {
+          // BMI = 体重(kg) / 身高(m)²
+          const heightInMeters = parseFloat(this.height) / 100;
+          const weightInKg = parseFloat(this.weight);
+          
+          if (!isNaN(heightInMeters) && !isNaN(weightInKg) && heightInMeters > 0) {
+            this.bmi = (weightInKg / (heightInMeters * heightInMeters)).toFixed(1);
+            
+            // 设置BMI状态
+            if (this.bmi < 18.5) {
+              this.bmiStatus = '偏瘦';
+            } else if (this.bmi >= 18.5 && this.bmi < 24) {
+              this.bmiStatus = '正常';
+            } else if (this.bmi >= 24 && this.bmi < 28) {
+              this.bmiStatus = '超重';
+            } else {
+              this.bmiStatus = '肥胖';
+            }
+          }
+        }
+      },
+      async getSportData() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime),
+            endTime: this.parseTime(this.endTime),
+            userId: this.userInfo.userId
+          };
+          const response = await getUserHealthInfoByDeviceId(params);
+          if(response.data){
+            this.caloriesData.value = response.data.calorie;
+            this.stepsData.value = response.data.step;
+            this.sleepData.status = response.data.sleepStatus;
+          }
+        }catch(error){
+          console.error("获取步数,卡路里,睡眠等信息数据失败:", error);
+        }
+      },
+
+      async getHealthArchive() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime),
+            endTime: this.parseTime(this.endTime),
+          };
+          const response = await queryHealthReport(params);
+          if(response.data){
+            this.healthArchive = response.data.list;
+            this.healthIndex = response.data.score;
+            this.healthStatus = response.data.title;
+            this.healthDesc = response.data.desc;
+
+          }
+        }catch(error){
+          console.error("获取步数,卡路里,睡眠等信息数据失败:", error);
+        }
+      },
+      async getGlucoseDate() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await queryGlucoseList(params);
+          if(response.data && response.data.length > 0){
+            const values = response.data.map(item => {
+              const timestamp = new Date(item.createTime).getTime();
+              return [timestamp, item.bloodGlucose, item.createTime];
+            });
+            this.bloodSugarData = {
+              values: values
+            };
+        
+          } else {
+            this.bloodSugarData = {
+              values: []
+            };
+          }
+        }catch(error){
+          console.error("获取血糖数据失败:", error);
+          this.bloodSugarData = {
+            values: []
+          };
+        }
+      },
+
+      async getUaDate() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await queryUaList(params);
+          if(response.data && response.data.length > 0){
+            const values = response.data.map(item => {
+              const timestamp = new Date(item.createTime).getTime();
+              const val = item.val/10;
+              let color;
+              if (val < 150) {
+                  color = '#FFCC00';
+              } else if (val < 416) {
+                color = '#4CD964';
+              } else {
+                color = '#FF6B6B';
+              }
+              return [timestamp, val, item.createTime,color];
+            });
+            this.uricAcidData.values = values;
+        
+          } else {
+            this.uricAcidChart.values = [];
+          }
+        }catch(error){
+          console.error("获取尿酸数据失败:", error);
+          this.uricAcidChart.values = [];
+        }
+      },
+      async getSleepDate() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await querySleepList(params);
+          if(response.data.sleepSection && response.data.sleepSection.length > 0){
+            this.sleepData = response.data.sleepSection;
+          }
+        }catch(error){
+          console.error("获取睡眠数据失败:", error);
+        }
+      },
+      async querySpo2Data() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await querySpo2List(params);
+          if(response.data.list && response.data.list.length > 0){
+            const values = response.data.list.map(item => {
+              const timestamp = new Date(item.createTime).getTime();
+              const val = item.avgBoxy;
+              let color;
+              if (val < 150) {
+                  color = '#FFCC00';
+              } else if (val < 416) {
+                color = '#4CD964';
+              } else {
+                color = '#FF6B6B';
+              }
+              return [timestamp, val, item.createTime,color];
+            });
+            this.bloodOxygenData.values = values;
+        
+          } else {
+            this.bloodOxygenData.values = [];
+          }
+        }catch(error){
+          console.error("获取尿酸数据失败:", error);
+          this.bloodOxygenData.values = [];
+        }
+      },
+
+      async getHrDate() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await queryHrList(params);
+          if(response.data.list && response.data.list.length > 0){
+            const values = response.data.list.map(item => {
+              const timestamp = new Date(item.createTime).getTime();
+              return [timestamp, (item.maxBpm + item.minBpm)/2, item.createTime];
+            });
+            this.heartRateData = {
+              values: values
+            };
+           
+          } else {
+            this.heartRateData = {
+              values: []
+            };
+          }
+        }catch(error){
+          console.error("获取心率数据失败:", error);
+          this.bloodSugarData = {
+            values: []
+          };
+        }
+      },
+
+      
+      async queryPressureData() {
+        try{
+          const params = {
+            deviceId: this.deviceId,
+            startTime: this.parseTime(this.startTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+            endTime: this.parseTime(this.endTime,'{y}/{m}/{d} {h}:{i}:{s}'),
+          };
+          const response = await queryPressureList(params);
+          if(response.data && response.data.length > 0){
+            
+
+            const sbp = response.data.map((item) => ({
+              value: [
+                new Date(item.createTime).getTime(),
+                item.sbp,
+              ]
+            }));
+            const dbp = response.data.map((item) => ({
+              value: [
+                  new Date(item.createTime).getTime(),
+                  item.dbp,
+              ]
+            }));
+            const hr = response.data.map((item) => ({
+              value: [
+                  new Date(item.createTime).getTime(),
+                  item.hr,
+              ]
+            }));
+            const createTime = response.data.map((item) => ({
+              value: [
+                  item.createTime
+              ]
+            }));
+            this.bloodPressureData = {
+              sbp : sbp,
+              dbp : dbp,
+              hr : hr,
+              createTime:createTime,
+            };
+          } else {
+            this.bloodPressureData = {
+              sbp : [],
+              dbp : [],
+              hr :  [],
+              createTime:[]
+            };
+          }
+        }catch(error){
+          console.error("获取血压数据失败:", error);
+          this.bloodPressureData = {
+            sbp : [],
+            dbp : [],
+            hr :  [],
+            createTime:[]
+          };
+        }
+      },
+
+      formatCreateDay(dateStr) {
+        const [month, day] = dateStr.split('/').map(Number);
+        const year = this.selectedDate.getFullYear();
+        const date = new Date(year, month - 1, day);
+        return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
+      },
+
+      async getTongueDate(){
+        if(this.userInfo && this.userInfo.userId){
+          const params = {
+            userId: this.userInfo.userId,
+            createDay: this.formatCreateDay(this.selectedDiagnosisDate),
+          };
+          
+          const response = await queryTongueList(params);
+          if(response && response.rows.length > 0){
+            this.tongueData = response.rows[0]
+            this.tongueData.typeJsonObj = JSON.parse(this.tongueData.typeJson || '[]')
+            console.log(this.tongueData)
+          } else {
+            this.tongueData = null;
+          }
+        }
+      },
+      // 获取周报详情数据
+      async getDetailsData() {
+        try {
+          this.getWeekDates(this.selectedDate);
+          // 获取健康周报-步数,卡路里,睡眠等信息
+          await this.getSportData();
+          await this.getHealthArchive();
+          await this.getGlucoseDate();
+          await this.getUaDate();
+          await this.queryPressureData();
+          await this.getHrDate();
+          await this.querySpo2Data();
+          await this.getSleepDate();
+          await this.getTongueDate();
+          this.updateCharts();
+        } catch (error) {
+          console.error("获取周报数据失败:", error);
+        }
+      },
+
+        
+      handleDateChange(date) {
+        this.selectedDate = date || this.parseTime(new Date(), "{y}-{m}-{d}");
+        this.getDetailsData();
+      },
+     
+      initBloodSugarChart() {
+        const chartDom = this.$refs.bloodSugarChart;
+        this.bloodSugarChart = echarts.init(chartDom);
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: function(params) {
+              if (!params.data || params.data.length < 3) {
+                return '无数据';
+              }
+              const value = params.data[1];
+              const createTime = params.data[2];
+              return `时间: ${createTime}<br/>血糖: ${value} mmol/L`;
+            }
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'time',
+            boundaryGap: false,
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime() + 1,
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              // fontSize: 14,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          yAxis: {
+            type: 'value',
+            min: 0,
+            max: 12,
+            interval: 2,
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          series: [
+            {
+              name: '血糖',
+              data: this.bloodSugarData.values,
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 7,
+              itemStyle: {
+                color: '#FF9F43'
+              },
+              lineStyle: {
+                color: '#FF9F43'
+              },
+              areaStyle: {
+                color: {
+                  type: 'linear',
+                  x: 0,
+                  y: 0,
+                  x2: 0,
+                  y2: 1,
+                  colorStops: [
+                    {
+                      offset: 0,
+                      color: 'rgba(255, 159, 67, 0.3)'
+                    },
+                    {
+                      offset: 1,
+                      color: 'rgba(255, 159, 67, 0.1)'
+                    }
+                  ]
+                }
+              }
+            }
+          ]
+        };
+        
+        this.bloodSugarChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          this.bloodSugarChart.resize();
+        });
+      },
+      initBloodPressureChart() {
+        const chartDom = this.$refs.bloodPressureChart;
+        this.bloodPressureChart = echarts.init(chartDom);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            formatter: function(params) {
+              if (!params) {
+                return '无数据';
+              }
+              const sbp = params[0].data.value;
+              const dbp = params[1].data.value;
+              const hr = params[2].data.value;
+              const createTime = new Date(sbp[0]).toLocaleString();
+              return `时间: ${createTime}<br/>sbp: ${sbp[1]} mmgh<br/>bdp: ${dbp[1]} mmgh<br/>hr: ${hr[1]} 次/分`;
+            }
+          },
+          legend: {},
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: "time",
+            boundaryGap: false,
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime(),
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+                show: false,
+            },
+            splitLine: {
+                show: false,
+            },
+          },
+          yAxis: {
+              type: 'value',
+              min: 0,
+              max: 180,
+              interval: 30,
+              axisLabel: {
+                  formatter: '{value}'
+              },
+              axisTick: {
+                show: false,
+              },
+              splitLine: {
+                show: false,
+              },
+          },
+          
+          series: [
+              {
+                  name: 'sbp(mmgh)',
+                  type: "line",
+                  symbol: "none",
+                  data: this.bloodPressureData.sbp
+              },
+              {
+                  name: 'dbp(mmgh)',
+                  type: "line",
+                  symbol: "none",
+                  data: this.bloodPressureData.dbp
+              },
+              {
+                  name: 'hr(次/分)',
+                  type: "line",
+                  symbol: "none",
+                  data: this.bloodPressureData.hr
+              }
+          ]
+          
+        };
+        
+        this.bloodPressureChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          this.bloodPressureChart.resize();
+        });
+      },
+      initUricAcidChart() {
+        const chartDom = this.$refs.uricAcidChart;
+        this.uricAcidChart = echarts.init(chartDom);
+
+        const data = this.uricAcidData.values.map((item, index) => {
+          return {
+            value: [item[0],item[1]],
+            itemStyle: {
+              color: item[3]
+            },
+            time:item[2]
+          };
+        });
+        
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: function(params) {
+              if (!params.data || params.data.length < 4) {
+                return '无数据';
+              }
+              const value = params.data.value[1];
+              const createTime = params.data.time;
+              return `时间: ${createTime}<br/>尿酸: ${value} μmol/L`;
+            }
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'time',
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime() + 1,
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          yAxis: {
+            type: 'value',
+            min: 0,
+            max: 600,
+            interval: 100,
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          series: [
+            {
+              name: '尿酸',
+              type: 'bar',
+              data: data,
+              // barWidth: '40%'
+              barWidth: 10, // 设置柱子的宽度为 20 像素
+            }
+          ]
+        };
+        
+        this.uricAcidChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          if (this.uricAcidChart) {
+            this.uricAcidChart.resize();
+          }
+        });
+      },
+      initHeartRateChart(){
+        const chartDom = this.$refs.heartRateChart;
+        this.heartRateChart = echarts.init(chartDom);
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: function(params) {
+              if (!params.data || params.data.length < 3) {
+                return '无数据';
+              }
+              const value = params.data[1];
+              const createTime = params.data[2];
+              return `时间: ${createTime}<br/>心率: ${value} 次/分`;
+            }
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'time',
+            boundaryGap: false,
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime() + 1,
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              // fontSize: 14,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          yAxis: {
+            type: 'value',
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          series: [
+            {
+              name: '心率',
+              data: this.heartRateData.values,
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 7,
+              itemStyle: {
+                color: '#FF9F43'
+              },
+              lineStyle: {
+                color: '#FF9F43'
+              },
+              areaStyle: {
+                color: {
+                  type: 'linear',
+                  x: 0,
+                  y: 0,
+                  x2: 0,
+                  y2: 1,
+                  colorStops: [
+                    {
+                      offset: 0,
+                      color: 'rgba(255, 159, 67, 0.3)'
+                    },
+                    {
+                      offset: 1,
+                      color: 'rgba(255, 159, 67, 0.1)'
+                    }
+                  ]
+                }
+              }
+            }
+          ]
+        };
+        
+        this.heartRateChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          this.heartRateChart.resize();
+        });
+      },
+      initBloodOxygenChart(){
+        const chartDom = this.$refs.bloodOxygenChart;
+        this.bloodOxygenChart = echarts.init(chartDom);
+
+        const data = this.bloodOxygenData.values.map((item, index) => {
+          return {
+            value: [item[0],item[1]],
+            itemStyle: {
+              color: item[3]
+            },
+            time:item[2]
+          };
+        });
+        
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: function(params) {
+              if (!params.data || params.data.length < 4) {
+                return '无数据';
+              }
+              const value = params.data.value[1];
+              const createTime = params.data.time;
+              return `时间: ${createTime}<br/>血氧: ${value} %`;
+            }
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'time',
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime() + 1,
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          yAxis: {
+            type: 'value',
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          series: [
+            {
+              name: '血氧',
+              type: 'bar',
+              data: data,
+              // barWidth: '40%'
+              barWidth: 10, // 设置柱子的宽度为 20 像素
+            }
+          ]
+        };
+        
+        this.bloodOxygenChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          if (this.bloodOxygenChart) {
+            this.bloodOxygenChart.resize();
+          }
+        });
+      },
+      initSleepChart(){
+        const chartDom = this.$refs.sleepChart;
+        this.sleepChart = echarts.init(chartDom);
+        const option = {
+          tooltip: {
+            show: true,
+            formatter: (params) => {
+              const start = params.value[3]
+              const end = params.value[4]
+              return `${start}到${end}<br/>${params.name}`;
+            },
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            top: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'time',
+            boundaryGap: false,
+            splitNumber: 7,
+            min:new Date(this.startTime).getTime(),
+            max:new Date(this.endTime).getTime() + 1,
+            axisLabel: {
+              color: "#999999",
+              margin: 20,
+              // fontSize: 14,
+              fontWeight: "bold",
+              formatter: (value) => {
+                return this.parseTime(value, "{m}/{d}");
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          yAxis: {
+            max: 14,
+            show: false,
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+          series: [
+          {
+            type: "custom",
+            renderItem: (params, api) => {
+              let yValue = api.value(2);
+              let start = api.coord([api.value(0), yValue]);
+              let size = api.size([api.value(1) - api.value(0), yValue]);
+              let style = api.style();
+              return {
+                type: "rect",
+                shape: {
+                  x: start[0],
+                  y: start[1],
+                  width: size[0],
+                  height: size[1],
+                },
+                style: style,
+              };
+            },
+            data: [],
+          },
+          ]
+        };
+        
+        this.sleepChart.setOption(option);
+        
+        // 响应式调整
+        window.addEventListener('resize', () => {
+          this.sleepChart.resize();
+        });
+      },
+      updateCharts() {
+        if (this.bloodSugarChart) {
+          // 更新血糖图表数据
+          this.bloodSugarChart.setOption({
+            series: [
+              {
+                data: this.bloodSugarData.values
+              }
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime() + 1,
+            },
+          });
+        }
+
+        if (this.bloodPressureChart) {
+          this.bloodPressureChart.setOption({
+            series: [
+              {
+                name: 'sbp(mmgh)',
+                type: "line",
+                symbol: "none",
+                data: this.bloodPressureData.sbp
+              },
+              {
+                name: 'dbp(mmgh)',
+                type: "line",
+                symbol: "none",
+                data: this.bloodPressureData.dbp
+              },
+              {
+                name: 'hr(次/分)',
+                type: "line",
+                symbol: "none",
+                data: this.bloodPressureData.hr
+              },
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime()  + 1,
+            },
+          });
+        }
+        
+        if (this.uricAcidChart) {
+          // 更新尿酸图表数据
+          const data = this.uricAcidData.values.map((item, index) => {
+            return {
+              value: [item[0],item[1]],
+              itemStyle: {
+                color: item[3]
+              },
+              time:item[2]
+            };
+          });
+          
+          this.uricAcidChart.setOption({
+            series: [
+              {
+                // type: 'bar',
+                data: data
+              }
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime() + 1,
+            },
+          });
+        }
+        if (this.heartRateChart) {
+          this.heartRateChart.setOption({
+            series: [
+              {
+                data: this.heartRateData.values
+              }
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime() + 1,
+            },
+          });
+        }
+        if (this.bloodOxygenChart) {
+          const data = this.bloodOxygenData.values.map((item, index) => {
+            return {
+              value: [item[0],item[1]],
+              itemStyle: {
+                color: item[3]
+              },
+              time:item[2]
+            };
+          });
+          
+          this.bloodOxygenChart.setOption({
+            series: [
+              {
+                // type: 'bar',
+                data: data
+              }
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime() + 1,
+            },
+          });
+        }
+       
+        if (this.sleepChart) {
+          const chartData = this.sleepData.map((item, index) => {
+            return {
+              name: this.sleepDefaultInfo[item.type] && this.sleepDefaultInfo[item.type].name,
+              value: [
+                new Date(item.start).getTime(),
+                new Date(item.end).getTime(),
+                this.sleepDefaultInfo[item.type] && this.sleepDefaultInfo[item.type].yValue,
+                item.start,
+                item.end
+              ],
+              itemStyle: {
+                color: this.sleepDefaultInfo[item.type] && this.sleepDefaultInfo[item.type].color,
+              },
+            };
+          });
+          this.sleepChart.setOption({
+            series: [
+              {
+                data: chartData
+              }
+            ],
+            xAxis: {
+              min:new Date(this.startTime).getTime(),
+              max:new Date(this.endTime).getTime() + 1,
+            },
+          });
+        }
+      }
+    },
+    beforeDestroy() {
+      // 清除事件监听
+      window.removeEventListener('resize', this.resizeHandler);
+      
+      // 销毁图表实例
+      if (this.bloodSugarChart) {
+        this.bloodSugarChart.dispose();
+        this.bloodSugarChart = null;
+      }
+      if (this.bloodPressureChart) {
+        this.bloodPressureChart.dispose();
+        this.bloodPressureChart = null;
+      }
+      if (this.uricAcidChart) {
+        this.uricAcidChart.dispose();
+        this.uricAcidChart = null;
+      }
+      if (this.heartRateChart) {
+        this.heartRateChart.dispose();
+        this.heartRateChart = null;
+      }
+      if (this.bloodOxygenChart) {
+        this.bloodOxygenChart.dispose();
+        this.bloodOxygenChart = null;
+      }
+      if (this.sleepChart) {
+        this.sleepChart.dispose();
+        this.sleepChart = null;
+      }
+    }
+  };
+  </script>
+  
+  <style scoped lang="scss">
+  
+  /* 确保内容不会因为隐藏滚动条而被截断 */
+  .weekly-report {
+    padding: 20px;
+    background-color: #f5f7fa;
+    min-height: 100%;
+    overflow-y: auto;
+    overflow-x: hidden;
+  }
+  
+  .report-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+  
+  .report-title {
+    display: flex;
+    align-items: center;
+    font-size: 18px;
+    font-weight: bold;
+    color: #2284ff;
+    
+    .title-icon {
+      width: 13px;
+      height: 20px;
+      margin-right: 10px;
+    }
+  }
+  
+  .report-card {
+    margin-bottom: 20px;
+    border-radius: 8px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    overflow: hidden; /* 确保内容不会溢出卡片 */
+  }
+  
+  .progress-section {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 20px 0;
+    
+    @media (min-width: 768px) {
+      flex-direction: row;
+      justify-content: space-around;
+      align-items: flex-start;
+    }
+  }
+  
+  .progress-container {
+    position: relative;
+    margin-bottom: 20px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    
+    @media (min-width: 768px) {
+      margin-bottom: 0;
+    }
+    
+    .progress-info {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      text-align: center;
+      
+      .progress-value {
+        font-size: 24px;
+        font-weight: bold;
+        color: #FF9F43;
+        
+        .progress-unit {
+          font-size: 14px;
+        }
+      }
+      
+      .progress-label {
+        font-size: 12px;
+        color: #606266;
+      }
+    }
+    
+    .health-status {
+      margin-top: 10px;
+      font-size: 16px;
+      font-weight: bold;
+      color: #FF9F43;
+    }
+    .health-description {
+    margin-top: 8px;
+    font-size: 12px;
+    color: #606266;
+    text-align: center;
+    max-width: 240px;
+    line-height: 1.5;
+    padding: 0 10px;
+  }
+}
+  
+  .health-metrics-container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    
+    @media (min-width: 768px) {
+      width: 60%;
+    }
+  }
+  
+  .health-metrics-row {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 10px;
+  }
+  
+  .metric-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    background-color: #fff;
+    border-radius: 8px;
+    padding: 10px 15px;
+    width: 48%;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    
+    .metric-data {
+      .metric-value {
+        font-size: 16px;
+        font-weight: bold;
+        
+        .metric-unit {
+          font-size: 12px;
+          font-weight: normal;
+        }
+      }
+      
+      .metric-label {
+        font-size: 12px;
+        color: #606266;
+      }
+    }
+    
+    .metric-icon {
+      width: 32px;
+      height: 32px;
+      border-radius: 50%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      
+      i {
+        font-size: 16px;
+        color: white;
+      }
+      
+      &.green {
+        background-color: #4CD964;
+      }
+      
+      &.blue {
+        background-color: #2284FF;
+      }
+      
+      &.orange {
+        background-color: #FF9F43;
+      }
+      
+      &.red {
+        background-color: #FF6B6B;
+      }
+      
+      &.purple {
+        background-color: #8A70D6;
+      }
+    }
+  }
+  
+  .section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin: 20px 0 15px;
+    color: #303133;
+    padding-left: 10px;
+  }
+  
+  /* 健康数据部分 */
+  .health-data-section {
+    padding: 0 10px;
+  }
+  
+  .trend-chart {
+    background-color: #fff;
+    border-radius: 8px;
+    padding: 15px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  }
+  
+  .trend-title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #303133;
+  }
+  
+  .trend-subtitle {
+    font-size: 12px;
+    color: #909399;
+    margin-bottom: 10px;
+  }
+  
+  .line-chart-container,
+  .bar-chart-container {
+    height: 250px;
+    width: 100%;
+  }
+  
+  .trend-advice {
+    font-size: 12px;
+    color: #606266;
+    background-color: #f5f7fa;
+    padding: 10px;
+    border-radius: 4px;
+    margin-top: 10px;
+  }
+  
+  .legend {
+    display: flex;
+    flex-wrap: wrap;
+    margin-top: 10px;
+  }
+  
+  .legend-item {
+    display: flex;
+    align-items: center;
+    margin-right: 15px;
+    font-size: 12px;
+    color: #606266;
+  }
+  
+  .legend-color {
+    display: inline-block;
+    width: 12px;
+    height: 12px;
+    margin-right: 5px;
+    border-radius: 2px;
+  }
+  
+  .health-tabs {
+    margin-top: 20px;
+  }
+  
+  /* 健康档案部分 */
+  .health-archive-section {
+    padding: 0 10px;
+  }
+  
+  .archive-items {
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+  }
+  
+  .archive-item {
+    display: flex;
+    background-color: #fff;
+    border-radius: 8px;
+    padding: 15px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  }
+  
+  .archive-icon {
+    width: 36px;
+    height: 36px;
+    border-radius: 50%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-right: 15px;
+    flex-shrink: 0;
+    
+    i {
+      font-size: 18px;
+      color: white;
+    }
+    
+    &.warning {
+      background-color: #FF6B6B;
+    }
+    
+    &.analysis {
+      background-color: #4CD964;
+    }
+    
+    &.suggestion {
+      background-color: #2284FF;
+    }
+    
+    &.attention {
+      background-color: #FF9F43;
+    }
+  }
+  
+  .archive-content {
+    flex: 1;
+  }
+  
+  .archive-title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #303133;
+    margin-bottom: 5px;
+  }
+  
+  .archive-text {
+    font-size: 12px;
+    color: #606266;
+    line-height: 1.5;
+  }
+  
+  /* AI舌诊部分 */
+  .ai-diagnosis-section {
+    padding: 0 10px 20px;
+  }
+  
+  .date-selector-tabs {
+    display: flex;
+    overflow-x: auto;
+    margin-bottom: 15px;
+  }
+  
+  .date-tab {
+    padding: 8px 15px;
+    background-color: #f5f7fa;
+    border-radius: 4px;
+    margin-right: 8px;
+    font-size: 14px;
+    cursor: pointer;
+    white-space: nowrap;
+    
+    &.active {
+      background-color: #FF9F43;
+      color: white;
+    }
+  }
+  
+  .diagnosis-content {
+    background-color: #fff;
+    border-radius: 8px;
+    padding: 15px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  }
+  
+  .diagnosis-time {
+    text-align: center;
+    font-size: 14px;
+    color: #909399;
+    margin-bottom: 15px;
+  }
+  
+  .diagnosis-result {
+    text-align: center;
+    margin-bottom: 20px;
+  }
+  
+  .diagnosis-label {
+    font-size: 14px;
+    color: #606266;
+  }
+  
+  .diagnosis-type {
+    font-size: 20px;
+    font-weight: bold;
+    color: #FF6B6B;
+    margin-top: 5px;
+  }
+  
+  .diagnosis-section {
+    margin-bottom: 20px;
+  }
+  
+  .diagnosis-section-title {
+    font-size: 16px;
+    font-weight: bold;
+    color: #FF9F43;
+    margin-bottom: 10px;
+    position: relative;
+    padding-left: 10px;
+    
+    &:before {
+      content: '';
+      position: absolute;
+      left: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 4px;
+      height: 16px;
+      background-color: #FF9F43;
+      border-radius: 2px;
+    }
+  }
+  
+  .diagnosis-feature {
+    margin-bottom: 10px;
+    display: flex;
+    flex-wrap: wrap;
+  }
+  
+  .feature-dot {
+    width: 8px;
+    height: 8px;
+    background-color: #FF9F43;
+    border-radius: 50%;
+    margin-right: 8px;
+    margin-top: 6px;
+  }
+  
+  .feature-title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #303133;
+    margin-right: 10px;
+  }
+  
+  .feature-description {
+    font-size: 12px;
+    color: #606266;
+    flex: 1;
+  }
+  
+  .diagnosis-analysis {
+    margin-bottom: 15px;
+  }
+  
+  .analysis-title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #303133;
+    margin-bottom: 5px;
+  }
+  
+  .analysis-content {
+    font-size: 12px;
+    color: #606266;
+    line-height: 1.5;
+  }
+  
+  .diagnosis-characteristics {
+    margin-bottom: 10px;
+  }
+  
+  .characteristics-title {
+    font-size: 13px;
+    font-weight: bold;
+    color: #303133;
+    margin-bottom: 3px;
+  }
+  
+  .characteristics-content {
+    font-size: 12px;
+    color: #606266;
+    line-height: 1.5;
+  }
+  </style>

+ 5 - 0
src/components/DeviceInfo/Sleep.vue

@@ -117,6 +117,11 @@
             <span v-if="scope.row.type === 7">快速眼动</span>
           </template>
         </el-table-column>
+        <template #empty>
+            <div>
+                今日暂无数据,请选择其他日期
+            </div>
+        </template>
       </el-table>
       <pagination v-show="(total > 0) && detailsType" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getDataPage"/>
     </div> 

+ 5 - 0
src/components/DeviceInfo/Sports.vue

@@ -29,6 +29,11 @@
       <el-table-column prop="sportTime" label="运动时长(min)" />
       <el-table-column prop="startTime" label="开始时间" />
       <el-table-column prop="type" label="运动类型" />
+      <template #empty>
+          <div>
+              今日暂无数据,请选择其他日期
+          </div>
+      </template>
     </el-table>
     <pagination v-show="(total > 0) && detailsType" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getDataPage"/>
     </div>

+ 8 - 4
src/components/DeviceInfo/Temperature.vue

@@ -24,7 +24,11 @@
                 <el-table-column prop="createTime" label="时间" />
                 <el-table-column prop="estTemp" label="体温(℃)" />
                 <el-table-column prop="shellTemp" label="体表温度(℃)" />
-                
+                <template #empty>
+                    <div>
+                        今日暂无数据,请选择其他日期
+                    </div>
+                </template>
             </el-table>
             <pagination v-show="(total > 0) && detailsType" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getDataPage"/>
         </div>
@@ -70,9 +74,9 @@ export default {
                     boundaryGap: false,
                     type: "time",
                     splitNumber: 6,
-                    axisLine: {
-                        show: false,
-                    },
+                    // axisLine: {
+                    //     show: false,
+                    // },
                     axisTick: {
                         show: false,
                     },

+ 5 - 0
src/components/DeviceInfo/Uricacid.vue

@@ -17,6 +17,11 @@
             </el-table-column>
             <el-table-column prop="val" label="测量结果">
             </el-table-column>
+            <template #empty>
+                <div>
+                    今日暂无数据,请选择其他日期
+                </div>
+            </template>
         </el-table>
 
     </div>

+ 5 - 0
src/components/DeviceInfo/Urineketones.vue

@@ -16,6 +16,11 @@
             </el-table-column>
             <el-table-column prop="val" align="center" label="测量结果">
             </el-table-column>
+            <template #empty>
+                <div>
+                    今日暂无数据,请选择其他日期
+                </div>
+            </template>
         </el-table>
 
     </div>

+ 8 - 1
src/utils/request.js

@@ -33,7 +33,14 @@ service.interceptors.request.use(config => {
       const value = config.params[propName];
       var part = encodeURIComponent(propName) + "=";
       if (value !== null && typeof(value) !== "undefined") {
-        if (typeof value === 'object') {
+        // 数组:重复键名 companyIds=1&companyIds=2,便于 Spring @RequestParam List 绑定
+        if (Array.isArray(value)) {
+          value.forEach(item => {
+            if (item !== null && typeof item !== 'undefined') {
+              url += encodeURIComponent(propName) + '=' + encodeURIComponent(item) + '&';
+            }
+          });
+        } else if (typeof value === 'object') {
           for (const key of Object.keys(value)) {
             if (value[key] !== null && typeof (value[key]) !== 'undefined') {
               let params = propName + '[' + key + ']';

+ 298 - 0
src/views/company/companyMoneyLogsDetail/index.vue

@@ -0,0 +1,298 @@
+<template>
+  <div class="app-container withdraw-detail">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" class="search-form" label-width="88px">
+      <el-form-item label="分公司" prop="companyIds">
+        <el-select
+          v-model="queryParams.companyIds"
+          multiple
+          collapse-tags
+          filterable
+          remote
+          reserve-keyword
+          clearable
+          placeholder="分公司名称关键词模糊搜索"
+          :remote-method="remoteSearchCompany"
+          :loading="companyLoading"
+          style="width: 320px"
+        >
+          <el-option
+            v-for="item in companyOptions"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+        <el-button type="text" size="small" @click="selectAllCompanies">全选</el-button>
+        <el-button type="text" size="small" @click="invertCompanySelection">反选</el-button>
+      </el-form-item>
+      <el-form-item label="明细类型" prop="detailTypes">
+        <el-select
+          v-model="queryParams.detailTypes"
+          multiple
+          collapse-tags
+          clearable
+          placeholder="全部"
+          style="width: 280px"
+        >
+          <el-option
+            v-for="item in detailTypeOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <p class="hint-text">不选分公司、不选类型时,查询全部公司与全部明细类型;数据自 2026-03-27 起。</p>
+
+    <el-row class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-download"
+          size="small"
+          @click="handleExport"
+          v-hasPermi="['company:companyMoneyLogsDetail:export']"
+        >导出</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table
+      v-loading="loading"
+      :data="tableList"
+      border
+      stripe
+      :header-cell-style="{ textAlign: 'center' }"
+      :cell-style="{ textAlign: 'center' }"
+    >
+      <el-table-column label="序号" width="70" align="center">
+        <template slot-scope="scope">
+          {{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="公司名称" prop="companyName" min-width="140" show-overflow-tooltip />
+      <el-table-column label="所属销售" prop="salesName" min-width="100">
+        <template slot-scope="scope">
+          {{ scope.row.salesName || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="订单号" prop="orderCode" min-width="160" show-overflow-tooltip>
+        <template slot-scope="scope">
+          {{ scope.row.orderCode || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="交易单号" prop="tradeNo" min-width="180" show-overflow-tooltip>
+        <template slot-scope="scope">
+          {{ scope.row.tradeNo || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="订单状态" align="center" min-width="100">
+        <template slot-scope="scope">
+          <template v-if="scope.row.orderStatusText != null && scope.row.orderStatusText !== ''">
+            <el-tag
+              v-for="(item, index) in statusOptions"
+              :key="index"
+              v-if="scope.row.orderStatusText == item.dictValue"
+            >{{ item.dictLabel }}</el-tag>
+            <el-tag v-if="!statusOptions.some(i => scope.row.orderStatusText == i.dictValue)">{{ scope.row.orderStatusText || scope.row.orderStatus }}</el-tag>
+          </template>
+          <el-tag v-else>{{ scope.row.orderStatusText || '-' }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="售后状态" prop="afterSalesStatusText" min-width="100">
+        <template slot-scope="scope">
+          {{ scope.row.afterSalesStatusText || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="订单记录时间" prop="recordTime" min-width="170" align="center">
+        <template slot-scope="scope">
+          {{ formatRecordTime(scope.row.recordTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="明细类型" prop="detailTypeText" min-width="130" />
+      <el-table-column label="金额" prop="amount" min-width="120" align="right" header-align="center">
+        <template slot-scope="scope">
+          <span :class="amountClass(scope.row.amount)">{{ formatAmount(scope.row.amount) }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { listWithdrawDetailCompanyOptions, listWithdrawDetail, exportWithdrawDetail } from '@/api/company/withdrawDetail'
+
+export default {
+  name: 'CompanyWithdrawDetailAdmin',
+  data() {
+    return {
+      loading: true,
+      companyLoading: false,
+      companyOptions: [],
+      tableList: [],
+      total: 0,
+      detailTypeOptions: [
+        { value: 1, label: '订单金额入账' },
+        { value: 2, label: '订单金额扣减' },
+        { value: 3, label: '总公司充值' },
+        { value: 4, label: '总公司扣款' },
+        { value: 5, label: '分公司提现' },
+        { value: 6, label: '总公司驳回' }
+      ],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyIds: [],
+        detailTypes: []
+      },
+      statusOptions: []
+    }
+  },
+  created() {
+    this.getDicts('store_order_status').then(res => {
+      this.statusOptions = res.data || []
+    })
+    this.remoteSearchCompany('')
+    this.getList()
+  },
+  methods: {
+    remoteSearchCompany(query) {
+      this.companyLoading = true
+      const keyword = query != null ? String(query).trim() : ''
+      listWithdrawDetailCompanyOptions({ keyword }).then(res => {
+        this.companyOptions = res.data || []
+        this.companyLoading = false
+      }).catch(() => {
+        this.companyLoading = false
+      })
+    },
+    selectAllCompanies() {
+      const ids = this.companyOptions.map(o => o.companyId).filter(id => id != null)
+      this.queryParams.companyIds = [...new Set(ids)]
+    },
+    invertCompanySelection() {
+      const ids = this.companyOptions.map(o => o.companyId).filter(id => id != null)
+      const next = new Set(this.queryParams.companyIds || [])
+      ids.forEach(id => {
+        if (next.has(id)) {
+          next.delete(id)
+        } else {
+          next.add(id)
+        }
+      })
+      this.queryParams.companyIds = [...next]
+    },
+    buildListParams() {
+      const p = {
+        pageNum: this.queryParams.pageNum,
+        pageSize: this.queryParams.pageSize
+      }
+      if (this.queryParams.companyIds && this.queryParams.companyIds.length > 0) {
+        p.companyIds = this.queryParams.companyIds
+      }
+      if (this.queryParams.detailTypes && this.queryParams.detailTypes.length > 0) {
+        p.detailTypes = this.queryParams.detailTypes
+      }
+      return p
+    },
+    getList() {
+      this.loading = true
+      listWithdrawDetail(this.buildListParams()).then(response => {
+        this.tableList = response.rows || []
+        this.total = response.total || 0
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        companyIds: [],
+        detailTypes: []
+      }
+      this.remoteSearchCompany('')
+      this.getList()
+    },
+    formatRecordTime(val) {
+      if (!val) return '-'
+      if (typeof val === 'string') {
+        const norm = val.replace('T', ' ').replace(/-/g, '.')
+        if (norm.length >= 19) {
+          return norm.slice(0, 10) + '-' + norm.slice(11, 19)
+        }
+      }
+      return this.parseTime(val, '{y}.{m}.{d}-{h}:{i}:{s}')
+    },
+    formatAmount(val) {
+      if (val == null || val === '') return '-'
+      const n = Number(val)
+      if (Number.isNaN(n)) return String(val)
+      const abs = Math.abs(n).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
+      if (n > 0) return '+' + abs
+      if (n < 0) return '-' + abs
+      return '0.00'
+    },
+    amountClass(val) {
+      if (val == null || val === '') return ''
+      const n = Number(val)
+      if (n < 0) return 'amt-neg'
+      if (n > 0) return 'amt-pos'
+      return ''
+    },
+    handleExport() {
+      this.$confirm('是否按当前条件导出提现明细?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        const p = {}
+        if (this.queryParams.companyIds && this.queryParams.companyIds.length > 0) {
+          p.companyIds = this.queryParams.companyIds
+        }
+        if (this.queryParams.detailTypes && this.queryParams.detailTypes.length > 0) {
+          p.detailTypes = this.queryParams.detailTypes
+        }
+        return exportWithdrawDetail(p)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(() => {})
+    }
+  }
+}
+</script>
+
+<style scoped>
+.search-form {
+  margin-bottom: 8px;
+}
+.hint-text {
+  color: #909399;
+  font-size: 13px;
+  margin: 0 0 12px 0;
+}
+.withdraw-detail .amt-pos {
+  color: #67c23a;
+}
+.withdraw-detail .amt-neg {
+  color: #f56c6c;
+}
+</style>

+ 480 - 0
src/views/crm/components/addBusiness.vue

@@ -0,0 +1,480 @@
+<template>
+    <div>
+        <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+            <el-form-item label="线索来源" prop="source">
+                <el-select v-model="form.source" :disabled="form.source !== null" placeholder="请选择线索来源" clearable size="small">
+                <el-option
+                    v-for="item in clueSourceOptions"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="客户经理" prop="manager">
+                <el-input v-model="form.manager" placeholder="请输入客户经理" />
+            </el-form-item>
+            <el-form-item label="客户名称" prop="companyName">
+                <el-input v-model="form.companyName" :disabled="form.customerId !== null" placeholder="请输入公司名称(只填公司全称,个人用户就填名字)" />
+            </el-form-item>
+            <el-form-item label="联系电话" prop="mobile">
+                <el-input  v-model="form.mobile" :disabled="form.customerId !== null" placeholder="请输入电话号码"/>
+                <!-- <el-button type="text" @click="showAddContactDialog">添加其他联系电话</el-button> -->
+            </el-form-item>
+            <el-form-item label="其他联系方式">
+                <el-button type="text" @click="showAddContactDialog">添加其他联系电话</el-button>
+            </el-form-item>
+            <el-table border v-loading="loading" :data="mobileList" v-if="mobileList && mobileList.length > 0" >
+                <el-table-column label="姓名" align="center" prop="name" />
+                <el-table-column label="手机" align="center" prop="mobile" />
+                <el-table-column label="备注" align="center" prop="remark" />
+                <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                <template slot-scope="scope">
+                    <!-- <el-button
+                    size="mini"
+                    type="text"
+                    icon="el-icon-edit"
+                    @click="handleUpdate(scope.row)"
+                    v-hasPermi="['crm:customerContacts:edit']"
+                    >修改</el-button> -->
+                    <el-button
+                    size="mini"
+                    type="text"
+                    icon="el-icon-delete"
+                    @click="handleDelete(scope.row)"
+                    v-hasPermi="['crm:customerContacts:remove']"
+                    v-if="scope.row.contactsId == null"
+                    >删除</el-button>
+                </template>
+                </el-table-column>
+            </el-table>
+
+            <el-form-item label="接口人角色" prop="contactRole">
+                <el-select v-model="form.contactRole" placeholder="请选择" clearable size="small">
+                    <el-option
+                        v-for="item in contactRoleOptions"
+                        :key="item.dictValue"
+                        :label="item.dictLabel"
+                        :value="item.dictValue"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="业务场景" prop="businessScenario">
+                <!-- <el-input v-model="form.businessScenario" placeholder="例:网站,电商,游戏,小程序,APP,OA,ERP,CRM,物联网,AI人工智能,国产化,数字人,内部办公系统,等保,其他" /> -->
+                <el-select v-model="form.businessScenario"  placeholder="请选择线索业务场景" clearable size="small">
+                <el-option
+                    v-for="item in businessScenarioOptions"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="意向产品" prop="product">
+                <!-- <el-input v-model="form.product" placeholder="请输入方案涉及产品" /> -->
+                <el-button icon="el-icon-plus" size="mini" @click="addProduct">添加产品</el-button>
+            </el-form-item>
+            <el-form-item label="采购周期" prop="purchaseCycle">
+                <el-input v-model="form.purchaseCycle" placeholder="请输入采购周期(单位:天)" />
+            </el-form-item>
+            <el-form-item label="跟进状态" prop="businessStatus">
+                <el-radio-group v-model="form.businessStatus">
+                    <el-radio label="0">跟进中</el-radio>
+                    <el-radio label="1">已流失</el-radio>
+                    <el-radio label="2">已赢单</el-radio>
+                    <el-radio label="3">待验证</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <!-- <el-form-item label="沟通进展" prop="remark">
+                <el-input v-model="form.remark" placeholder="请输入沟通进展" />
+            </el-form-item> -->
+            <el-form-item label="项目阶段" prop="projectPhase">
+                <el-select v-model="form.projectPhase" placeholder="请选择" clearable size="small">
+                    <el-option
+                        v-for="item in projectPhaseOptions"
+                        :key="item.dictValue"
+                        :label="item.dictLabel"
+                        :value="item.dictValue"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="意向等级" prop="level">
+                <el-select v-model="form.level" placeholder="请选择" clearable size="small">
+                    <el-option
+                        v-for="item in levelOptions"
+                        :key="item.dictValue"
+                        :label="item.dictLabel"
+                        :value="item.dictValue"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="是否绑定BP账号">
+                <el-radio-group v-model="form.isBp">
+                    <el-radio label="0">未注册</el-radio>
+                    <el-radio label="1">已注册未绑定</el-radio>
+                    <el-radio label="2">不明确</el-radio>
+                    <el-radio label="3">已绑定</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <!-- <el-form-item label="BP账户" prop="bpAccount">
+                <el-input v-model="form.bpAccount" placeholder="请输入BP账户" />
+            </el-form-item> -->
+            <el-form-item label="预计成单时间" prop="preTime">
+                <el-date-picker clearable size="small" style="width: 200px"
+                    v-model="form.preTime"
+                    type="date"
+                    value-format="yyyy-MM-dd"
+                    placeholder="选择预计成单时间">
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item label="预计成交金额" prop="preMoney">
+                <el-input v-model="form.preMoney" placeholder="请输入预计成交金额(元)" />
+            </el-form-item>
+            <el-form-item label="下次跟进时间" prop="nextTime">
+                <el-date-picker clearable size="small" style="width: 200px"
+                    v-model="form.nextTime"
+                    type="date"
+                    value-format="yyyy-MM-dd"
+                    placeholder="选择下次跟进时间">
+                </el-date-picker>
+            </el-form-item>
+            <!-- <el-form-item label="回收时间" prop="recoveryTime">
+                <el-date-picker clearable size="small" style="width: 200px"
+                    v-model="form.recoveryTime"
+                    type="date"
+                    value-format="yyyy-MM-dd"
+                    placeholder="选择回收时间">
+                </el-date-picker>
+            </el-form-item> -->
+            <!-- <el-form-item label="状态">
+                <el-radio-group v-model="form.status">
+                    <el-radio label="1">已分配</el-radio>
+                    <el-radio label="2">进行中</el-radio>
+                    <el-radio label="3">回收</el-radio>
+                </el-radio-group>
+            </el-form-item> -->
+        </el-form>
+        <div class="footer">
+            <el-button type="primary" @click="submitForm">确 定</el-button>
+        </div>
+
+
+        <el-dialog  :title=title :visible.sync="open" width="500px" append-to-body>
+            <el-form ref="contactForm" :model="contactForm" :rules="contactRules" label-width="120px">
+            <el-form-item label="联系人名称" prop="name">
+                <el-input v-model="contactForm.name" placeholder="请输入联系人名称" />
+            </el-form-item>
+            <el-form-item label="手机" prop="mobile">
+                <el-input v-model="contactForm.mobile" placeholder="请输入手机" />
+            </el-form-item>
+            <el-form-item label="备注" prop="remark">
+                <el-input v-model="contactForm.remark" type="textarea" placeholder="请输入内容" />
+            </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="submitContactForm">确 定</el-button>
+                <el-button @click="cancelContact">取 消</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+    import { addBusiness } from "@/api/crm/business";
+    import { listCustomerContacts, getCustomerContacts, addCustomerContacts, exportCustomerContacts } from "@/api/crm/customerContacts";
+    export default {
+        name: "business",
+        data() {
+            return {
+                // 弹出层标题
+                title: "",
+                // 是否显示弹出层
+                open: false,
+                // 遮罩层
+                loading: true,
+                mobileList:[],
+                // dialogVisible:false,
+                //联系方式表格
+                contactForm:{
+                    contactsId:null,
+                    customerId:null,
+                    businessId:null,
+                    name:null,
+                    mobile:null,
+                    remark:null
+                },
+                businessScenarioOptions:[],
+                clueSourceOptions:[],
+                contactRoleOptions:[],
+                projectPhaseOptions:[],
+                levelOptions:[],
+                form: {
+                    businessId: null,
+                    customerId: null,
+                    source: null,
+                    manager: null,
+                    companyName: null,
+                    mobile: null,
+                    mobileList:[],
+                    contactRole: null,
+                    businessScenario: null,
+                    product: null,
+                    purchaseCycle: null,
+                    businessStatus: "0",
+                    remark: null,
+                    projectPhase: null,
+                    level: null,
+                    isBp: '0',
+                    bpAccount: null,
+                    preTime: null,
+                    preMoney: null,
+                    nextTime: null,
+                    createTime: null,
+                    updateTime: null,
+                    createBy: null,
+                    recoveryTime: null,
+                    status: '1'
+                },
+                // 表单校验
+                rules: {
+                    source:[
+                        { required: true, message: "客户来源不能为空", trigger: "change" }
+                    ],
+                    companyName: [
+                        { required: true, message: "客户名称不能为空", trigger: "blur" }
+                    ],
+                    mobile: [
+                        { required: true, message: "联系电话不能为空", trigger: "blur" },
+                    ],
+                    businessScenario: [
+                        { required: true, message: "业务场景不能为空", trigger: "change" }
+                    ],
+                    businessStatus:[
+                        { required: true, message: "请选择跟进状态", trigger: "change" }
+                    ],
+                    projectPhase:[
+                        { required: true, message: "请选择项目阶段", trigger: "change" }
+                    ],
+
+                },
+                //联系方式表单校验
+                contactRules:{
+                    name:[
+                        { required: true, message: "联系人不能为空", trigger: "blur" }
+                    ],
+                    mobile: [
+                        { required: true, message: "手机不能为空", trigger: "blur" },
+                        { min: 8, max: 11, message: '长度在 8 到 11 个字符', trigger: 'blur' }
+                    ]
+                }
+
+            };
+        },
+        created() {
+
+            this.getDicts("crm_customer_source").then((response) => {
+                this.clueSourceOptions = response.data;
+            });
+            this.getDicts("crm_contact_role").then((response) => {
+                this.contactRoleOptions = response.data;
+            });
+            this.getDicts("crm_project_phase").then((response) => {
+                this.projectPhaseOptions = response.data;
+            });
+            this.getDicts("crm_level").then((response) => {
+                this.levelOptions = response.data;
+            });
+            this.getDicts("business_scenario_type").then((response) => {
+                this.businessScenarioOptions = response.data;
+            });
+        },
+        methods: {
+            addProduct(){
+
+            },
+            delCustomerContacts(mobile){
+                const index = this.mobileList.findIndex(phone => phone.id === mobile);
+                if (index !== -1) {
+                    this.mobileList.splice(index, 1); // 删除找到的对象
+                }
+                this.open = false;
+                this.loading = false;
+                console.log("3333333333333",JSON.String(mobileList))
+            },
+            /** 删除按钮操作 */
+            handleDelete(row) {
+                this.loading = true;
+                const mobile = row.mobile
+                const index = this.mobileList.findIndex(phone => phone.mobile === mobile);
+                if (index !== -1) {
+                    this.mobileList.splice(index, 1); // 删除找到的对象
+                }
+                this.open = false;
+                this.loading = false;
+
+            },
+            /** 修改按钮操作 */
+            // handleUpdate(row) {
+            //     // this.resetContact();
+            //     if (row.contactsId == undefined){
+            //         console.log('============================')
+            //         this.contactForm = {name:this.form.companyName,mobile:this.form.mobile}
+            //     } else{
+            //         this.resetContact();
+            //         getCustomerContacts(row.contactsId).then(response => {
+            //             this.contactForm = response.data;
+            //         });
+            //     }
+            //     // const contactsId = row.contactsId
+            //     console.log("row",JSON.stringify(this.contactForm))
+            //     this.open = true;
+            //     this.title = "修改客户联系人";
+            // },
+            /** 提交按钮 */
+            submitContactForm() {
+                this.$refs["contactForm"].validate(valid => {
+                if (valid) {
+                    console.log("13579246810",JSON.stringify(this.contactForm))
+                        //新增
+                        const newMobile = {
+                            mobile: this.contactForm.mobile,
+                            name:this.contactForm.name,
+                            remark:this.contactForm.remark,
+                        };
+                        this.mobileList.push(newMobile);
+                        this.open = false;
+                        this.loading = false;
+                }});
+            },
+            updateMobileList(mobile, newMobileData) {
+                // 查找要修改的对象
+                const index = this.mobileList.findIndex(phone => phone.mobile === mobile);
+                if (index !== -1) {
+                // 更新找到的对象
+                this.$set(this.mobileList, index, { ...this.mobileList[index], ...newMobileData });
+                }
+            },
+            // 取消按钮
+            cancelContact() {
+                this.open = false;
+                this.resetContact();
+            },
+            resetContact(){
+                this.contactForm = {
+                    contactsId: null,
+                    customerId: null,
+                    name: null,
+                    mobile: null,
+                    email: null,
+                    weixin: null,
+                    address: null,
+                    remark: null,
+                    createUserId: null,
+                    createTime: null,
+                    updateTime: null,
+                    isDel: null,
+                    companyId: null
+                };
+            },
+            //添加联系方式
+            showAddContactDialog() {
+                this.open = true; // 显示弹窗
+                this.contactForm.mobile = ''; // 清空表单数据
+            },
+            addContact() {
+                // 添加联系方式逻辑
+                console.log('添加联系方式:', this.contactForm);
+                this.form.mobile = this.contactForm.mobile; // 将联系方式添加到form.mobile
+                this.open = false; // 关闭弹窗
+            },
+            updateReset(row){
+                this.form = row;
+                if(row.source != null){
+                    this.form.source = String(row.source);
+                }
+                if(row.contactRole != null){
+                    this.form.contactRole = String(row.contactRole);
+                }
+                this.mobileList = this.form.mobileList;
+                this.loading = false;
+                if(row.businessStatus != null){
+                    this.form.businessStatus = String(row.businessStatus);
+                }
+                if(row.level != null){
+                    this.form.level = String(row.level);
+                }
+                if(row.isBp != null){
+                    this.form.isBp = String(row.isBp);
+                }
+            },
+            reset(row) {
+                this.form = row;
+                this.form.source=String(row.source);
+                //电话
+                this.getContact(row.customerId,this.form.businessId);
+                //跟进状态
+                this.form.businessStatus = '0'
+                //客户名字
+                this.form.companyName = row.customerName
+            },
+            getContact(customerId,businessId){
+                this.loading = true;
+                listCustomerContacts({customerId:customerId,businessId:businessId}).then(response=>{
+                    if (response.code === 200) {
+                        if(response.rows.length>0)
+                            this.mobileList = response.rows;
+                            this.loading = false;
+                        } else{
+                            this.mobileList = []
+                        }
+                })
+                console.log("========",JSON.stringify(this.mobileList))
+            },
+            /** 提交按钮 */
+            submitForm() {
+                this.$refs["form"].validate(valid => {
+                if (valid) {
+                    this.form.mobileList = this.mobileList;
+                    addBusiness(this.form).then(response => {
+                        if (response.code === 200) {
+                            this.msgSuccess("提交成功");
+                            this.$emit('closeBusiness');
+                        }
+                    });
+                }
+                });
+            },
+        }
+    };
+</script>
+<style lang="scss" scoped>
+.contents{
+    height: 100%;
+    background-color: #fff;
+    padding: 20px;
+
+}
+.footer{
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+}
+</style>
+
+
+<style scoped>
+  .avatar-uploader .el-upload {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+  }
+  .avatar-uploader .el-upload:hover {
+    border-color: #409EFF;
+  }
+</style>
+
+
+
+

+ 96 - 0
src/views/crm/components/setColumn.vue

@@ -0,0 +1,96 @@
+<template>
+    <div>
+        <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+            <el-form-item label="全部字段" prop="columns">
+                <p></p>
+                <el-checkbox-group v-model="form.columns">
+                    <!-- 循环遍历选项 -->
+                    <el-checkbox
+                        v-for="(item, index) in allColumns"
+                        :key="index"
+                        :label="item.prop"
+                        :disabled="item.prop == 'customerId' || item.prop == 'businessId'"
+                    >
+                        {{ item.label }}
+                    </el-checkbox>
+                </el-checkbox-group>
+            </el-form-item>
+            <div   class="footer">
+                <el-button type="primary" @click="submitForm">确定</el-button>
+                <el-button  @click="cancel">取消</el-button>
+            </div>
+        </el-form>
+    </div>
+</template>
+
+<script>
+    import {updateShow} from "@/api/system/show";
+
+    export default {
+        name: "business",
+        data() {
+            return {
+                form:{
+                    type:'',
+                    columns:[],
+                },
+                allColumns:[],
+                // 表单校验
+                rules: {
+                    type: [
+                        { required: true, message: "type不能为空", trigger: "blur" }
+                    ],
+                    columns: [
+                        { required: true, message: '请至少选择一个选项', trigger: 'change' },
+                        ],
+
+                },
+
+
+            }
+        },
+
+        created() {
+
+        },
+        methods: {
+            cancel(){
+                this.$emit('close');
+            },
+            reset(allColumns,selectedKeys,type,){
+                this.allColumns = allColumns;
+                this.form.columns = selectedKeys;
+                this.form.type = type;
+                console.log("==========",JSON.stringify(this.allColumns))
+                console.log("==========2",JSON.stringify(this.form.columns))
+            },
+            /** 提交按钮 */
+            submitForm() {
+                this.$refs["form"].validate(valid => {
+                if (valid) {
+                    console.log("==========2",JSON.stringify(this.form))
+                    updateShow(this.form).then(response => {
+                        if (response.code === 200) {
+                            this.msgSuccess("提交成功");
+                            this.$emit('close');
+                        }
+                    });
+                }
+                });
+            },
+        }
+    };
+</script>
+<style lang="scss" scoped>
+.contents{
+    height: 100%;
+    background-color: #fff;
+    padding: 20px;
+
+}
+.footer{
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+}
+</style>

+ 826 - 0
src/views/crm/customerBusiness/index.vue

@@ -0,0 +1,826 @@
+<template>
+  <div class="app-container"  customer-page-box>
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <!-- <el-form-item label="客户名称" prop="companyName">
+        <el-input
+          v-model="queryParams.companyName"
+          placeholder="请输入客户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <el-form-item label="线索来源" prop="source">
+        <el-select v-model="queryParams.source" placeholder="请选择线索来源" clearable size="small">
+          <el-option
+            v-for="item in clueSourceOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <!-- <el-form-item label="客户经理" prop="manager">
+        <el-input
+          v-model="queryParams.manager"
+          placeholder="请输入客户经理"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <el-form-item label="公司名称" prop="companyName">
+        <el-input
+          v-model="queryParams.companyName"
+          placeholder="请输入公司名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="电话号码" prop="mobile">
+        <el-input
+          v-model="queryParams.mobile"
+          placeholder="请输入电话号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <!-- <el-form-item label="接口人角色" prop="contactRole">
+        <el-select v-model="queryParams.contactRole" placeholder="请选择" clearable size="small">
+          <el-option
+            v-for="item in contactRoleOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+            />
+         </el-select>
+      </el-form-item> -->
+      <el-form-item label="业务场景" prop="businessScenario">
+        <el-input
+          v-model="queryParams.businessScenario"
+          placeholder="请输入业务场景"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <!-- <el-form-item label="方案涉及的产品" prop="product">
+        <el-input
+          v-model="queryParams.product"
+          placeholder="请输入方案涉及的产品"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <!-- <el-form-item label="采购周期(单位:天)" prop="purchaseCycle">
+        <el-input
+          v-model="queryParams.purchaseCycle"
+          placeholder="请输入采购周期(单位:天)"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <el-form-item label="跟进状态" prop="businessStatus">
+        <el-select v-model="queryParams.businessStatus" placeholder="请选择跟进状态" clearable size="small">
+          <el-option
+            v-for="item in businessStatusOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="项目阶段" prop="projectPhase">
+        <el-select v-model="queryParams.projectPhase" placeholder="请选择" clearable size="small">
+          <el-option
+              v-for="item in projectPhaseOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="意向等级" prop="level">
+        <el-select v-model="queryParams.level" placeholder="请选择" clearable size="small">
+          <el-option
+              v-for="item in levelOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <!-- <el-form-item label="是否绑定BP账号" prop="isBp">
+        <el-input
+          v-model="queryParams.isBp"
+          placeholder="请输入是否绑定BP账号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <!-- <el-form-item label="BP账户" prop="bpAccount">
+        <el-input
+          v-model="queryParams.bpAccount"
+          placeholder="请输入BP账户"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <!-- <el-form-item label="预计成单时间" prop="preTime">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.preTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择预计成单时间">
+        </el-date-picker>
+      </el-form-item> -->
+      <!-- <el-form-item label="预计付费(元)" prop="preMoney">
+        <el-input
+          v-model="queryParams.preMoney"
+          placeholder="请输入预计付费(元)"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <!-- <el-form-item label="挖掘时间" prop="createTime">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.createTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择挖掘时间">
+        </el-date-picker>
+      </el-form-item> -->
+      <el-form-item label="挖掘时间" prop="createTimeRange">
+        <el-date-picker
+          style="width:205.4px"
+          clearable size="small"
+          v-model="createTimeRange"
+          type="daterange"
+          value-format="yyyy-MM-dd"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期">
+        </el-date-picker>
+      </el-form-item>
+      <!-- <el-form-item label="下次跟进时间" prop="nextTime">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.nextTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择下次跟进时间">
+        </el-date-picker>
+      </el-form-item> -->
+      <el-form-item label="下次跟进时间" prop="nextTimeRange">
+        <el-date-picker
+          style="width:205.4px"
+          clearable size="small"
+          v-model="nextTimeRange"
+          type="daterange"
+          value-format="yyyy-MM-dd"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期">
+        </el-date-picker>
+      </el-form-item>
+      <!-- <el-form-item label="回收时间" prop="recoveryTime">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.recoveryTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择回收时间">
+        </el-date-picker>
+      </el-form-item> -->
+      <el-form-item label="回收时间" prop="recoveryRange">
+        <el-date-picker
+          style="width:205.4px"
+          clearable size="small"
+          v-model="recoveryRange"
+          type="daterange"
+          value-format="yyyy-MM-dd"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期">
+        </el-date-picker>
+      </el-form-item>
+      <!-- <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
+          <el-option
+              v-for="item in statusOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item> -->
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['crm:business:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button size="mini" icon="el-icon-s-operation" @click="handleSetColumn()" >设置列表</el-button>
+      </el-col>
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <div>
+      <!-- <el-table v-loading="loading" :data="businessList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="商机ID" align="center" prop="businessId" />
+
+        <el-table-column label="线索来源" align="center" prop="source">
+          <template slot-scope="scope">
+              <span prop="source" v-for="(item, index) in clueSourceOptions"    v-if="scope.row.source==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="客户经理" align="center" prop="manager" />
+        <el-table-column label="公司名称" align="center" prop="companyName" />
+        <el-table-column label="电话号码" align="center" width="100">
+          <template slot-scope="scope">
+            <div v-if="scope.row.mobileList">
+              <el-tag v-for="(phone, index) in scope.row.mobileList" :key="index">
+                {{ phone.mobile }}
+              </el-tag>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="接口人角色" align="center" prop="contactRole" width="120">
+          <template slot-scope="scope">
+              <span prop="contactRole" v-for="(item, index) in contactRoleOptions"    v-if="scope.row.contactRole==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="业务场景" align="center" prop="businessScenario">
+          <template slot-scope="scope">
+              <span prop="businessScenario" v-for="(item, index) in businessScenarioOptions"    v-if="scope.row.businessScenario==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="方案涉及产品" align="center" prop="product"  width="100"/>
+        <el-table-column label="采购周期" align="center" prop="purchaseCycle">
+          <template slot-scope="scope">
+              <span>{{scope.row.purchaseCycle}}天</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="跟进状态" align="center" prop="businessStatus">
+          <template slot-scope="scope">
+              <span prop="businessStatus" v-for="(item, index) in businessStatusOptions"    v-if="scope.row.businessStatus==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="项目阶段" align="center" prop="projectPhase" width="200">
+          <template slot-scope="scope">
+              <span prop="projectPhase" v-for="(item, index) in projectPhaseOptions" v-if="scope.row.projectPhase==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="意向等级" align="center" prop="level" width="100">
+          <template slot-scope="scope">
+              <span prop="level" v-for="(item, index) in levelOptions" v-if="scope.row.level==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="是否绑定BP账号" align="center" prop="isBp" width="120">
+          <template slot-scope="scope">
+              <span prop="isBp" v-for="(item, index) in isBpOptions" v-if="scope.row.isBp==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="BP账户" align="center" prop="bpAccount" />
+        <el-table-column label="预计成单时间" align="center" prop="preTime" width="180">
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.preTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="预计付费" align="center" prop="preMoney">
+          <template slot-scope="scope">
+              <span>{{scope.row.preMoney}}元</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="挖掘时间" align="center" prop="createTime" width="180"/>
+        <el-table-column label="下次跟进时间" align="center" prop="nextTime" width="180">
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.nextTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-edit"
+              @click="handleUpdate(scope.row)"
+              v-hasPermi="['crm:business:edit']"
+            >修改</el-button>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-delete"
+              @click="handleDelete(scope.row)"
+              v-hasPermi="['crm:business:remove']"
+            >退回公海</el-button>
+          </template>
+        </el-table-column>
+      </el-table> -->
+
+      <!-- 动态表格 -->
+    <el-table height="500" border v-loading="loading" :data="businessList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <!-- 动态渲染列 -->
+      <el-table-column
+        v-for="col in visibleColumns"
+        :key="col.prop"
+        :label="col.label"
+        :width="col.width"
+        align="center"
+        :prop="col.prop">
+
+        <template slot-scope="scope">
+          <!-- 如果是线索来源字段 -->
+          <template v-if="col.prop === 'source'">
+            <el-tag v-for="item in clueSourceOptions" :key="item.dictValue" v-show="scope.row.source == item.dictValue">
+              {{ item.dictLabel }}
+            </el-tag>
+          </template>
+
+
+          <!-- 如果是电话号码字段 -->
+          <template v-else-if="col.prop === 'mobileList'">
+            <el-tag v-for="(phone, index) in scope.row.mobileList" :key="index" v-show="scope.row.mobileList">
+              {{ phone.mobile }}
+            </el-tag>
+          </template>
+
+          <!-- 如果是接口人角色字段 -->
+          <template v-else-if="col.prop === 'contactRole'">
+            <span v-for="item in contactRoleOptions" :key="item.dictValue" v-show="scope.row.contactRole == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是业务场景字段 -->
+          <template v-else-if="col.prop === 'businessScenario'">
+            <span v-for="(item, index) in businessScenarioOptions" :key="item.dictValue" v-show="scope.row.businessScenario == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是采购周期字段 -->
+          <template v-else-if="col.prop === 'purchaseCycle'">
+            <span>{{scope.row.purchaseCycle||'--'}}天</span>
+          </template>
+
+
+          <!-- 如果是跟进状态字段 -->
+          <template v-else-if="col.prop === 'businessStatus'">
+            <span v-for="item in businessStatusOptions" :key="item.dictValue" v-show="scope.row.businessStatus == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是项目阶段字段 -->
+          <template v-else-if="col.prop === 'projectPhase'">
+            <span v-for="(item, index) in projectPhaseOptions" :key="item.dictValue" v-show="scope.row.projectPhase == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是意向等级字段 -->
+          <template v-else-if="col.prop === 'level'">
+            <span v-for="(item, index) in levelOptions" :key="item.dictValue" v-show="scope.row.level == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是预计成单时间字段 -->
+          <template v-else-if="col.prop === 'preTime'">
+            <span>{{ parseTime(scope.row.preTime, '{y}-{m}-{d}') }}</span>
+          </template>
+
+          <!-- 如果是是否绑定BP账号字段 -->
+          <template v-else-if="col.prop === 'isBp'">
+            <span v-for="(item, index) in isBpOptions" :key="item.dictValue" v-show="scope.row.isBp == item.dictValue">
+              {{ item.dictLabel }}
+            </span>
+          </template>
+
+          <!-- 如果是预计付费字段 -->
+          <template v-else-if="col.prop === 'preMoney'">
+            <span>{{scope.row.preMoney||'--'}}元</span>
+          </template>
+
+          <!-- 如果是下次跟进时间字段 -->
+          <template v-else-if="col.prop === 'nextTime'">
+            <span>{{ parseTime(scope.row.nextTime, '{y}-{m}-{d}') }}</span>
+          </template>
+
+          <!-- 其他普通字段 -->
+          <span v-else>{{ scope.row[col.prop] }}</span>
+        </template>
+
+
+      </el-table-column>
+
+      <!-- 操作列,始终显示 -->
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope" v-if="scope.row.customerId !== ''">
+          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleShow(scope.row)" v-hasPermi="['crm:customer:query']" >查看相关客户</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+      <pagination
+        v-show="total>0"
+        :total="total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+
+      <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+        <add-business @closeBusiness="closeBusiness"   ref="addBusiness" />
+      </el-dialog>
+      <!-- 显示 allColumns 数据的对话框 -->
+      <el-dialog :visible.sync="columns.open" :title="columns.title">
+        <set-column ref="setColumn" @close="closeSetColumn"/>
+      </el-dialog>
+      <el-drawer
+      size="75%"
+        :title="show.title" :visible.sync="show.open">
+          <customer-details  ref="customerDetails" />
+      </el-drawer>
+
+    </div>
+  </div>
+</template>
+
+<script>
+import { listBusiness, getBusiness, exportBusiness,setBusinessPool } from "@/api/crm/business";
+import addBusiness from '../components/addBusiness.vue';
+import setColumn from '../components/setColumn.vue';
+import {getShow} from "@/api/system/show";
+import customerDetails from '../components/customerDetails.vue';
+export default {
+  name: "Business",
+  components: {addBusiness,setColumn,customerDetails},
+  data() {
+    return {
+      show:{
+        title:"客户详情",
+        open:false,
+      },
+      columns:{
+        title:"设置列表",
+        open:false,
+      },
+      columnType:'business',
+      visibleColumns: [],
+       // 可选字段
+       allColumns: [
+        { prop: "businessId", label: "商机ID" },
+        // { prop: "companyName", label: "客户名称" },
+        { prop: "customerId", label: "客户ID" },
+        { prop: "source", label: "线索来源" },
+        { prop: "manager", label: "客户经理" },
+        { prop: "companyName", label: "公司名称" },
+        { prop: "mobileList", label: "电话号码" },
+        { prop: "contactRole", label: "接口人角色" },
+        { prop: "businessScenario", label: "业务场景" },
+        { prop: "product", label: "方案涉及产品" },
+        { prop: "purchaseCycle", label: "采购周期" },
+        { prop: "businessStatus", label: "跟进状态" },
+        { prop: "projectPhase", label: "项目阶段" },
+        { prop: "level", label: "意向等级" },
+        { prop: "isBp", label: "是否绑定BP账号" },
+        { prop: "bpAccount", label: "BP账户" },
+        { prop: "preTime", label: "预计成单时间", width: 105 },
+        { prop: "preMoney", label: "预计付费" },
+        { prop: "createTime", label: "挖掘时间", width: 105 },
+        { prop: "nextTime", label: "下次跟进时间", width: 105 }
+
+      ],
+      // 选中的字段(默认显示所有字段)
+      selectedKeys: [
+        "businessId",
+        "customerId",
+        "source",
+        "manager",
+        "companyName",
+        "mobileList",
+        "contactRole",
+        "businessScenario",
+        "product",
+        "purchaseCycle",
+        "businessStatus",
+        "projectPhase",
+        "level",
+        "isBp",
+        "bpAccount",
+        "preTime",
+        "preMoney",
+        "createTime",
+        "nextTime"
+      ],
+      createTimeRange:[],
+      nextTimeRange:[],
+      recoveryRange:[],
+      statusOptions:[
+        {
+          dictValue:1,
+          dictLabel:"已分配"
+        },
+        {
+          dictValue:2,
+          dictLabel:"进行中"
+        },
+        {
+          dictValue:3,
+          dictLabel:"回收"
+        }
+      ],
+      clueSourceOptions:[],
+      contactRoleOptions:[],
+      projectPhaseOptions:[],
+      businessScenarioOptions:[],
+      levelOptions:[],
+      businessStatusOptions:[],
+      isBpOptions:[
+        {
+          dictValue:0,
+          dictLabel:"未注册"
+        },
+        {
+          dictValue:1,
+          dictLabel:"已注册未绑定"
+        },
+        {
+          dictValue:2,
+          dictLabel:"不明确"
+        },
+        {
+          dictValue:3,
+          dictLabel:"已绑定"
+        }
+      ],
+
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 商机表格数据
+      businessList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        // customerId: null,
+        source: null,
+        manager: null,
+        companyName: null,
+        mobile: null,
+        contactRole: null,
+        businessScenario: null,
+        product: null,
+        purchaseCycle: null,
+        businessStatus: null,
+        projectPhase: null,
+        level: null,
+        isBp: null,
+        bpAccount: null,
+        preTime: null,
+        preMoney: null,
+        nextTime: null,
+        recoveryTime: null,
+        status: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        customerId: [
+          { required: true, message: "客户id不能为空", trigger: "blur" }
+        ],
+      },
+
+    };
+  },
+  created() {
+    this.getDicts("business_scenario_type").then((response) => {
+      this.businessScenarioOptions = response.data;
+    });
+    this.getDicts("crm_customer_source").then((response) => {
+      this.clueSourceOptions = response.data;
+    });
+    this.getDicts("crm_contact_role").then((response) => {
+      this.contactRoleOptions = response.data;
+    });
+    this.getDicts("crm_project_phase").then((response) => {
+      this.projectPhaseOptions = response.data;
+    });
+    this.getDicts("crm_level").then((response) => {
+      this.levelOptions = response.data;
+    });
+    this.getDicts("crm_business_status").then((response) => {
+      this.businessStatusOptions = response.data;
+    });
+    this.getList();
+  },
+  methods: {
+    handleShow(row){
+      var that=this;
+      that.show.open=true;
+      setTimeout(() => {
+          that.$refs.customerDetails.getDetails(row.customerId);
+      }, 200);
+    },
+    handleSetColumn(){
+      this.columns.open=true;
+      var that=this;
+      setTimeout(() => {
+            that.$refs.setColumn.reset(that.allColumns,that.selectedKeys,that.columnType);
+      }, 200);
+    },
+    closeSetColumn(){
+      this.columns.open=false;
+      this.getList();
+    },
+    handleBusiness(row){
+        this.business.open=true;
+        setTimeout(() => {
+            this.$refs.addBusiness.reset(row);
+        }, 200);
+    },
+    closeBusiness(){
+        this.business.open=false;
+        this.getList();
+    },
+    //查询显示字段
+    async getColumn(){
+      const response = await getShow(this.columnType);
+      if (response.code === 200 && response.data) {
+        this.selectedKeys = response.data.columns.split(",");
+        this.$set(this, "visibleColumns", this.allColumns.filter(col => this.selectedKeys.includes(col.prop)));
+
+      } else{
+        this.$set(this, "visibleColumns", this.allColumns);
+      }
+    },
+    /** 查询客户列表 */
+    async getList() {
+      this.loading = true;
+      await this.getColumn(); // 确保 visibleColumns 先获取到
+
+      if(this.createTimeRange!=null&&this.createTimeRange.length==2){
+        this.queryParams.createTimeRange=this.createTimeRange[0]+"--"+this.createTimeRange[1]
+      }
+      else{
+        this.queryParams.createTimeRange=null;
+      }
+      if(this.nextTimeRange!=null&&this.nextTimeRange.length==2){
+        this.queryParams.nextTimeRange=this.nextTimeRange[0]+"--"+this.nextTimeRange[1]
+      }
+      else{
+        this.queryParams.nextTimeRange=null;
+      }
+      if(this.recoveryRange!=null&&this.recoveryRange.length==2){
+        this.queryParams.recoveryRange=this.recoveryRange[0]+"--"+this.recoveryRange[1]
+      }
+      else{
+        this.queryParams.recoveryRange=null;
+      }
+      const response = await listBusiness(this.addDateRange(this.queryParams, this.dateRange));
+
+      // this.customerList = response.rows;
+      // 处理 customerList 数据,确保不会有 undefined 或 null
+      this.businessList = response.rows.map(row => {
+        let newRow = {};
+        this.visibleColumns.forEach(col => {
+          newRow[col.prop] = row[col.prop] != null ? row[col.prop] : ""; // 如果是 null 或 undefined,就填 "-"
+        });
+        return newRow;
+      });
+      this.total = response.total;
+      this.loading = false;
+
+    },
+    // /** 查询商机列表 */
+    // getList() {
+    //   this.loading = true;
+    //   if(this.createTimeRange!=null&&this.createTimeRange.length==2){
+    //     this.queryParams.createTimeRange=this.createTimeRange[0]+"--"+this.createTimeRange[1]
+    //   }
+    //   else{
+    //     this.queryParams.createTimeRange=null;
+    //   }
+    //   if(this.nextTimeRange!=null&&this.nextTimeRange.length==2){
+    //     this.queryParams.nextTimeRange=this.nextTimeRange[0]+"--"+this.nextTimeRange[1]
+    //   }
+    //   else{
+    //     this.queryParams.nextTimeRange=null;
+    //   }
+    //   if(this.recoveryRange!=null&&this.recoveryRange.length==2){
+    //     this.queryParams.recoveryRange=this.recoveryRange[0]+"--"+this.recoveryRange[1]
+    //   }
+    //   else{
+    //     this.queryParams.recoveryRange=null;
+    //   }
+    //   listBusiness(this.queryParams).then(response => {
+    //     this.businessList = response.rows;
+    //     this.total = response.total;
+    //     this.loading = false;
+    //   });
+    // },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        businessId: null,
+        customerId: null,
+        source: null,
+        manager: null,
+        companyName: null,
+        mobile: null,
+        contactRole: null,
+        businessScenario: null,
+        product: null,
+        purchaseCycle: null,
+        businessStatus: 0,
+        remark: null,
+        projectPhase: null,
+        level: null,
+        isBp: null,
+        bpAccount: null,
+        preTime: null,
+        preMoney: null,
+        nextTime: null,
+        createTime: null,
+        updateTime: null,
+        createBy: null,
+        recoveryTime: null,
+        status: 0,
+        // createTimeRange:null,
+        // nextTimeRange:null,
+        // recoveryRange:null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.businessId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有商机数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportBusiness(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        }).catch(function() {});
+    }
+  }
+};
+</script>

+ 24 - 18
src/views/hisStore/components/productAfterSalesOrder.vue

@@ -84,6 +84,12 @@
                   {{afterSales.auditRemark}}
                 </span>
             </el-descriptions-item>
+            <el-descriptions-item label="售后一级原因" v-if="afterSales!=null">
+              <span>{{ afterSales.reasonValue1 || afterSales.reasonLevel1Text || '-' }}</span>
+            </el-descriptions-item>
+            <el-descriptions-item label="售后二级原因" v-if="afterSales!=null">
+              <span>{{ afterSales.reasonValue2 || afterSales.reasonLevel2Text || '-' }}</span>
+            </el-descriptions-item>
 
         </el-descriptions>
         <div style="margin: 20px 0px">
@@ -323,24 +329,24 @@ export default {
           { required: true, message: "退款金额不能为空", trigger: "blur" }
         ],
         // 新增一级原因必填
-        reasonId1: [
-          { required: true, message: "请选择一级原因", trigger: "change" }
-        ],
-        reasonId2: [
-          {
-            validator: (rule, value, callback) => {
-              if (this.form.reasonId1 && !value) {
-                callback(new Error("请选择二级原因"));
-              } else {
-                callback();
-              }
-            },
-            trigger: "change"
-          }
-        ],
-        auditRemark: [
-          { required: true, message: "请输入审核备注", trigger: "blur" }
-        ]
+        // reasonId1: [
+        //   { required: true, message: "请选择一级原因", trigger: "change" }
+        // ],
+        // reasonId2: [
+        //   {
+        //     validator: (rule, value, callback) => {
+        //       if (this.form.reasonId1 && !value) {
+        //         callback(new Error("请选择二级原因"));
+        //       } else {
+        //         callback();
+        //       }
+        //     },
+        //     trigger: "change"
+        //   }
+        // ],
+        // auditRemark: [
+        //   { required: true, message: "请输入审核备注", trigger: "blur" }
+        // ]
       },
       reasonList: [],
       reason2List: []

+ 5 - 0
src/views/hisStore/integralGoods/index.vue

@@ -142,6 +142,7 @@
         </template>
       </el-table-column>
       <el-table-column label="所需积分" align="center" prop="integral" />
+<!--      <el-table-column label="需支付金额" align="center" prop="cash" />-->
       <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="库存" align="center" prop="stock" />
       <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
@@ -208,6 +209,9 @@
         <el-form-item label="所需积分" prop="integral">
           <el-input-number v-model="form.integral"  :min="0"  label="所需积分"></el-input-number>
         </el-form-item>
+<!--        <el-form-item label="支付金额" prop="cash">-->
+<!--          <el-input-number v-model="form.cash"  :min="0" :precision="2" :step="0.1"  label="需支付金额"></el-input-number>-->
+<!--        </el-form-item>-->
         <el-form-item label="商品编号" prop="barCode">
           <el-input v-model="form.barCode" placeholder="请输入商品编号"  style="width: 200px;"/>
         </el-form-item>
@@ -457,6 +461,7 @@ export default {
         otPrice: null,
         goodsType: null,
         status: 0,
+        cash: 0,
         integral: null,
         sort: null,
         stock: null,

+ 1085 - 0
src/views/kdniao/eorder/index.vue

@@ -0,0 +1,1085 @@
+<template>
+  <div class="app-container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="110px"
+      @submit.native.prevent
+    >
+      <!-- 基础信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>电子面单下单</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="业务订单号" prop="bizOrderNo">
+              <el-input v-model="form.bizOrderNo" placeholder="请输入业务订单号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="快递公司" prop="shipperCode">
+              <el-select
+                v-model="form.shipperCode"
+                placeholder="请选择快递公司"
+                clearable
+                style="width: 100%"
+                @change="handleShipperChange"
+              >
+                <el-option label="顺丰 SF" value="SF" />
+                <el-option label="EMS" value="EMS" />
+                <el-option label="京东快运 JDKY" value="JDKY" />
+                <el-option label="京东快递 JOS" value="JOS" />
+                <el-option label="京东生鲜医药 JDSXYY" value="JDSXYY" />
+                <el-option label="中通 ZTO" value="ZTO" />
+                <el-option label="中通冷链 ZTOCOLD" value="ZTOCOLD" />
+                <el-option label="菜鸟橙运 CNCY" value="CNCY" />
+                <el-option label="菜鸟速递 CNSD" value="CNSD" />
+                <el-option label="笨鸟速运 BNSY" value="BNSY" />
+                <el-option label="丰云配 FYP" value="FYP" />
+                <el-option label="云集医药冷链 YJYYLL" value="YJYYLL" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="支付方式" prop="payType">
+              <el-select v-model="form.payType" placeholder="请选择支付方式" style="width: 100%">
+                <el-option label="现付" :value="1" />
+                <el-option label="到付" :value="2" />
+                <el-option label="月结" :value="3" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="业务类型" prop="expType">
+              <el-input v-model="form.expType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-alert
+          v-if="shipperTip"
+          :title="shipperTip"
+          type="info"
+          :closable="false"
+          show-icon
+        />
+      </el-card>
+
+      <!-- 收件人信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>收件人信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="收件人姓名" prop="receiverName">
+              <el-input v-model="form.receiverName" placeholder="请输入收件人姓名" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="手机号" prop="receiverMobile">
+              <el-input v-model="form.receiverMobile" placeholder="请输入手机号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="电话">
+              <el-input v-model="form.receiverTel" placeholder="请输入电话" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="邮编" :prop="needReceiverPostCode ? 'receiverPostCode' : ''">
+              <el-input
+                v-model="form.receiverPostCode"
+                :placeholder="needReceiverPostCode ? '当前快递要求填写邮编' : '非必填'"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="省" prop="receiverProvinceName">
+              <el-input v-model="form.receiverProvinceName" placeholder="如 广东省" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="市" prop="receiverCityName">
+              <el-input v-model="form.receiverCityName" placeholder="如 深圳市" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="区/县" prop="receiverExpAreaName">
+              <el-input v-model="form.receiverExpAreaName" placeholder="如 福田区" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="详细地址" prop="receiverAddress">
+              <el-input v-model="form.receiverAddress" placeholder="不要填写省市区" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 商品信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>商品与面单信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="商品名称" prop="goodsName">
+              <el-input v-model="form.goodsName" placeholder="如 文件、电子产品" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品数量">
+              <el-input-number v-model="form.goodsQuantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品价格">
+              <el-input-number v-model="form.goodsPrice" :min="0" :precision="2" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品重量">
+              <el-input-number v-model="form.goodsWeight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="包裹数量">
+              <el-input-number v-model="form.quantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总重量" :prop="needWeight ? 'weight' : ''">
+              <el-input-number v-model="form.weight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总体积" :prop="needVolume ? 'volume' : ''">
+              <el-input-number v-model="form.volume" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="模板尺寸">
+              <el-input v-model="form.templateSize" placeholder="默认130" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="备注">
+              <el-input
+                v-model="form.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入备注"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="商品描述">
+              <el-input
+                v-model="form.goodsDesc"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 快递专属字段 -->
+      <el-card shadow="never" class="mb12" v-if="showCarrierSpecific">
+        <div slot="header">
+          <span>快递专属参数</span>
+        </div>
+
+        <!-- 顺丰 -->
+        <template v-if="isSF">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="币种">
+                <el-select v-model="form.sf.currencyCode" placeholder="请选择币种" clearable style="width: 100%">
+                  <el-option label="人民币 CNY" value="CNY" />
+                  <el-option label="港币 HKD" value="HKD" />
+                  <el-option label="新台币 NTD" value="NTD" />
+                  <el-option label="澳门元 MOP" value="MOP" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- EMS -->
+        <template v-if="isEMS">
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-alert
+                title="EMS要求收件人邮编必填,发件人邮编需在后端配置里配置。"
+                type="warning"
+                :closable="false"
+                show-icon
+              />
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东快运 -->
+        <template v-if="isJDKY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="是否通知取件" prop="jdkyIsNotice">
+                <el-select v-model="form.jdky.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="配送方式">
+                <el-input-number v-model="form.jdky.deliveryMethod" :min="0" style="width: 100%" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="上门开始" :prop="needPickupTime ? 'jdkyStartDate' : ''">
+                <el-input v-model="form.jdky.startDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="上门结束" :prop="needPickupTime ? 'jdkyEndDate' : ''">
+                <el-input v-model="form.jdky.endDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东快递 -->
+        <template v-if="isJOS">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.jos.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东生鲜医药 -->
+        <template v-if="isJDSXYY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.jdsxyy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 中通 -->
+        <template v-if="isZTO">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="操作指令">
+                <el-input v-model="form.zto.operateDirective" placeholder="如有需要可填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 中通冷链 -->
+        <template v-if="isZTOCOLD">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="操作指令">
+                <el-input v-model="form.ztocold.operateDirective" placeholder="如有需要可填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 菜鸟橙运 -->
+        <template v-if="isCNCY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.cncy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="是否通知取件">
+                <el-select v-model="form.cncy.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 菜鸟速递 -->
+        <template v-if="isCNSD">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.cnsd.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="是否通知取件">
+                <el-select v-model="form.cnsd.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 笨鸟速运 -->
+        <template v-if="isBNSY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.bnsy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 丰云配 -->
+        <template v-if="isFYP">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.fyp.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 云集医药冷链 -->
+        <template v-if="isYJYYLL">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.yjyyll.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+      </el-card>
+
+      <!-- 按钮 -->
+      <el-row :gutter="10" class="mb8">
+        <el-button type="primary" icon="el-icon-s-promotion" :loading="submitLoading" @click="handleSubmit">
+          提交下单
+        </el-button>
+
+        <el-button icon="el-icon-refresh" @click="resetFormData">
+          重置
+        </el-button>
+
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-view"
+          :disabled="!result.printTemplate"
+          @click="handlePreview"
+        >
+          查看面单
+        </el-button>
+
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-printer"
+          :disabled="!result.printTemplate"
+          @click="handlePrintDirect"
+        >
+          打印面单
+        </el-button>
+
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-document"
+          :disabled="!result.printTemplate"
+          @click="openNewWindow"
+        >
+          新窗口打开
+        </el-button>
+
+        <el-button
+          plain
+          icon="el-icon-download"
+          :disabled="!result.printTemplate"
+          @click="downloadHtml"
+        >
+          下载HTML
+        </el-button>
+      </el-row>
+    </el-form>
+
+    <!-- 返回结果 -->
+    <el-card shadow="never" v-if="result.raw">
+      <div slot="header">
+        <span>下单结果</span>
+      </div>
+
+      <el-row :gutter="20" class="mb8">
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">是否成功:</span>
+            <el-tag :type="result.success ? 'success' : 'danger'" size="mini">
+              {{ result.success ? "成功" : "失败" }}
+            </el-tag>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">返回码:</span>
+            <span>{{ result.resultCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">运单号:</span>
+            <span>{{ result.logisticCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">订单号:</span>
+            <span>{{ result.orderCode || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="24">
+          <div class="result-item">
+            <span class="label">返回信息:</span>
+            <span>{{ result.reason || serverMsg || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-divider content-position="left">原始返回</el-divider>
+
+      <el-input type="textarea" :rows="12" :value="result.raw" readonly />
+    </el-card>
+
+    <!-- 面单预览 -->
+    <el-dialog
+      title="面单预览"
+      :visible.sync="previewOpen"
+      width="1000px"
+      append-to-body
+      top="3vh"
+    >
+      <div class="preview-toolbar">
+        <el-button size="mini" type="primary" icon="el-icon-printer" @click="printIframe">
+          打印
+        </el-button>
+        <el-button size="mini" icon="el-icon-document" @click="openNewWindow">
+          新窗口打开
+        </el-button>
+        <el-button size="mini" icon="el-icon-download" @click="downloadHtml">
+          下载HTML
+        </el-button>
+      </div>
+
+      <iframe ref="previewFrame" class="preview-frame"></iframe>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { submitKdniaoUniversalEOrder } from "@/api/kdniao/eorder";
+
+export default {
+  name: "KdniaoEOrder",
+  data() {
+    return {
+      submitLoading: false,
+      previewOpen: false,
+      serverMsg: "",
+      form: this.getDefaultForm(),
+      result: {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      },
+      rules: {
+        bizOrderNo: [{ required: true, message: "业务订单号不能为空", trigger: "blur" }],
+        shipperCode: [{ required: true, message: "快递公司不能为空", trigger: "change" }],
+        payType: [{ required: true, message: "支付方式不能为空", trigger: "change" }],
+        expType: [{ required: true, message: "业务类型不能为空", trigger: "blur" }],
+        receiverName: [{ required: true, message: "收件人姓名不能为空", trigger: "blur" }],
+        receiverMobile: [{ required: true, message: "收件人手机号不能为空", trigger: "blur" }],
+        receiverProvinceName: [{ required: true, message: "省不能为空", trigger: "blur" }],
+        receiverCityName: [{ required: true, message: "市不能为空", trigger: "blur" }],
+        receiverExpAreaName: [{ required: true, message: "区/县不能为空", trigger: "blur" }],
+        receiverAddress: [{ required: true, message: "详细地址不能为空", trigger: "blur" }],
+        receiverPostCode: [{ required: true, message: "当前快递要求邮编必填", trigger: "blur" }],
+        goodsName: [{ required: true, message: "商品名称不能为空", trigger: "blur" }],
+        weight: [{ required: true, message: "当前快递要求总重量必填", trigger: "blur" }],
+        volume: [{ required: true, message: "当前快递要求总体积必填", trigger: "blur" }],
+        jdkyIsNotice: [{ required: true, message: "京东快运要求是否通知取件必填", trigger: "change" }],
+        jdkyStartDate: [{ required: true, message: "请填写上门开始时间", trigger: "blur" }],
+        jdkyEndDate: [{ required: true, message: "请填写上门结束时间", trigger: "blur" }]
+      }
+    };
+  },
+  computed: {
+    isSF() {
+      return this.form.shipperCode === "SF";
+    },
+    isEMS() {
+      return this.form.shipperCode === "EMS";
+    },
+    isJDKY() {
+      return this.form.shipperCode === "JDKY";
+    },
+    isJOS() {
+      return this.form.shipperCode === "JOS";
+    },
+    isJDSXYY() {
+      return this.form.shipperCode === "JDSXYY";
+    },
+    isZTO() {
+      return this.form.shipperCode === "ZTO";
+    },
+    isZTOCOLD() {
+      return this.form.shipperCode === "ZTOCOLD";
+    },
+    isCNCY() {
+      return this.form.shipperCode === "CNCY";
+    },
+    isCNSD() {
+      return this.form.shipperCode === "CNSD";
+    },
+    isBNSY() {
+      return this.form.shipperCode === "BNSY";
+    },
+    isFYP() {
+      return this.form.shipperCode === "FYP";
+    },
+    isYJYYLL() {
+      return this.form.shipperCode === "YJYYLL";
+    },
+    showCarrierSpecific() {
+      return true;
+    },
+    needReceiverPostCode() {
+      return this.isEMS;
+    },
+    needWeight() {
+      return this.isJDKY;
+    },
+    needVolume() {
+      return this.isJDKY;
+    },
+    needPickupTime() {
+      return this.isJDKY && this.form.jdky.isNotice === 0;
+    },
+    shipperTip() {
+      if (this.isEMS) {
+        return "EMS:收件人邮编必填。";
+      }
+      if (this.isJDKY) {
+        return "京东快运:重量、体积、是否通知取件必填;通知取件时需填写上门时间。";
+      }
+      if (this.isSF) {
+        return "顺丰:港澳台等场景可填写币种。";
+      }
+      return "";
+    }
+  },
+  methods: {
+    getDefaultForm() {
+      return {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "李四",
+        receiverMobile: "13900000000",
+        receiverTel: "",
+        receiverProvinceName: "广东省",
+        receiverCityName: "深圳市",
+        receiverExpAreaName: "福田区",
+        receiverAddress: "华强北赛格广场",
+        receiverPostCode: "518000",
+
+        goodsName: "电子产品",
+        goodsQuantity: 1,
+        goodsPrice: 1000,
+        goodsWeight: 0.5,
+        goodsDesc: "测试商品",
+
+        quantity: 1,
+        weight: 0.5,
+        volume: 0.001,
+        cost: null,
+        otherCost: null,
+
+        remark: "测试单请勿接",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        sf: {
+          currencyCode: ""
+        },
+        jdky: {
+          isNotice: null,
+          deliveryMethod: null,
+          startDate: "",
+          endDate: ""
+        },
+        jos: {
+          transType: ""
+        },
+        jdsxyy: {
+          transType: ""
+        },
+        zto: {
+          operateDirective: ""
+        },
+        ztocold: {
+          operateDirective: ""
+        },
+        cncy: {
+          transType: "",
+          isNotice: null
+        },
+        cnsd: {
+          transType: "",
+          isNotice: null
+        },
+        bnsy: {
+          transType: ""
+        },
+        fyp: {
+          transType: ""
+        },
+        yjyyll: {
+          transType: ""
+        }
+      };
+    },
+
+    handleShipperChange() {
+      // 切换快递时不重置通用字段,只重置专属字段
+      const oldCommon = {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiverName: this.form.receiverName,
+        receiverMobile: this.form.receiverMobile,
+        receiverTel: this.form.receiverTel,
+        receiverProvinceName: this.form.receiverProvinceName,
+        receiverCityName: this.form.receiverCityName,
+        receiverExpAreaName: this.form.receiverExpAreaName,
+        receiverAddress: this.form.receiverAddress,
+        receiverPostCode: this.form.receiverPostCode,
+
+        goodsName: this.form.goodsName,
+        goodsQuantity: this.form.goodsQuantity,
+        goodsPrice: this.form.goodsPrice,
+        goodsWeight: this.form.goodsWeight,
+        goodsDesc: this.form.goodsDesc,
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe
+      };
+
+      this.form = Object.assign(this.getDefaultForm(), oldCommon, {
+        shipperCode: this.form.shipperCode
+      });
+    },
+
+    buildSubmitParams() {
+      const carrierFields = {};
+
+      if (this.isSF && this.form.sf.currencyCode) {
+        carrierFields.CurrencyCode = this.form.sf.currencyCode;
+      }
+
+      if (this.isJDKY) {
+        if (this.form.jdky.deliveryMethod !== null && this.form.jdky.deliveryMethod !== undefined && this.form.jdky.deliveryMethod !== "") {
+          carrierFields.DeliveryMethod = this.form.jdky.deliveryMethod;
+        }
+      }
+
+      if (this.isJOS && this.form.jos.transType) {
+        carrierFields.TransType = this.form.jos.transType;
+      }
+
+      if (this.isJDSXYY && this.form.jdsxyy.transType) {
+        carrierFields.TransType = this.form.jdsxyy.transType;
+      }
+
+      if (this.isZTO && this.form.zto.operateDirective) {
+        carrierFields.OperateDirective = this.form.zto.operateDirective;
+      }
+
+      if (this.isZTOCOLD && this.form.ztocold.operateDirective) {
+        carrierFields.OperateDirective = this.form.ztocold.operateDirective;
+      }
+
+      if (this.isCNCY) {
+        if (this.form.cncy.transType) {
+          carrierFields.TransType = this.form.cncy.transType;
+        }
+      }
+
+      if (this.isCNSD) {
+        if (this.form.cnsd.transType) {
+          carrierFields.TransType = this.form.cnsd.transType;
+        }
+      }
+
+      if (this.isBNSY && this.form.bnsy.transType) {
+        carrierFields.TransType = this.form.bnsy.transType;
+      }
+
+      if (this.isFYP && this.form.fyp.transType) {
+        carrierFields.TransType = this.form.fyp.transType;
+      }
+
+      if (this.isYJYYLL && this.form.yjyyll.transType) {
+        carrierFields.TransType = this.form.yjyyll.transType;
+      }
+
+      let isNotice = null;
+      let startDate = "";
+      let endDate = "";
+
+      if (this.isJDKY) {
+        isNotice = this.form.jdky.isNotice;
+        startDate = this.form.jdky.startDate;
+        endDate = this.form.jdky.endDate;
+      } else if (this.isCNCY) {
+        isNotice = this.form.cncy.isNotice;
+      } else if (this.isCNSD) {
+        isNotice = this.form.cnsd.isNotice;
+      }
+
+      return {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiver: {
+          name: this.form.receiverName,
+          mobile: this.form.receiverMobile,
+          tel: this.form.receiverTel,
+          provinceName: this.form.receiverProvinceName,
+          cityName: this.form.receiverCityName,
+          expAreaName: this.form.receiverExpAreaName,
+          address: this.form.receiverAddress,
+          postCode: this.form.receiverPostCode
+        },
+
+        commodity: [
+          {
+            goodsName: this.form.goodsName,
+            goodsQuantity: this.form.goodsQuantity,
+            goodsPrice: this.form.goodsPrice,
+            goodsWeight: this.form.goodsWeight,
+            goodsDesc: this.form.goodsDesc
+          }
+        ],
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe,
+
+        isNotice: isNotice,
+        startDate: startDate,
+        endDate: endDate,
+
+        carrierFields: carrierFields
+      };
+    },
+
+    handleSubmit() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) return;
+
+        this.submitLoading = true;
+        this.serverMsg = "";
+        this.result = {
+          success: false,
+          resultCode: "",
+          reason: "",
+          logisticCode: "",
+          orderCode: "",
+          printTemplate: "",
+          raw: ""
+        };
+
+        const submitParams = this.buildSubmitParams();
+
+        submitKdniaoUniversalEOrder(submitParams).then(res => {
+          this.serverMsg = res.msg || "";
+
+          const data = res.data || {};
+          this.result.success = data.success === true || data.Success === true;
+          this.result.resultCode = data.resultCode || data.ResultCode || "";
+          this.result.reason = data.reason || data.Reason || "";
+          this.result.printTemplate = data.printTemplate || data.PrintTemplate || "";
+
+          const order = data.order || data.Order || {};
+          this.result.logisticCode = order.logisticCode || order.LogisticCode || "";
+          this.result.orderCode = order.orderCode || order.OrderCode || "";
+          this.result.raw = JSON.stringify(data, null, 2);
+
+          if (res.code === 200) {
+            this.msgSuccess(res.msg || "下单成功");
+          } else {
+            this.msgError(res.msg || "下单失败");
+          }
+        }).catch(err => {
+          this.msgError((err && err.message) || "请求失败");
+        }).finally(() => {
+          this.submitLoading = false;
+        });
+      });
+    },
+
+    handlePreview() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+      });
+    },
+
+    handlePrintDirect() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+        setTimeout(() => {
+          this.printIframe();
+        }, 200);
+      });
+    },
+
+    writePreviewFrame() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        return;
+      }
+      const doc = iframe.contentWindow.document;
+      doc.open();
+      doc.write(this.buildWaybillHtml(this.result.printTemplate));
+      doc.close();
+    },
+
+    buildWaybillHtml(templateHtml) {
+      const html = templateHtml || "";
+
+      if (/<html[\s\S]*?>/i.test(html)) {
+        return html;
+      }
+
+      return `
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>面单预览</title>
+  <style>
+    html, body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+    }
+    body {
+      overflow: auto;
+    }
+    @page {
+      margin: 0;
+    }
+  </style>
+</head>
+<body>
+${html}
+</body>
+</html>`;
+    },
+
+    openNewWindow() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      const win = window.open("", "_blank");
+      if (!win) {
+        this.msgError("浏览器拦截了新窗口,请允许弹窗后重试");
+        return;
+      }
+      win.document.open();
+      win.document.write(this.buildWaybillHtml(this.result.printTemplate));
+      win.document.close();
+    },
+
+    printIframe() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        this.msgError("打印窗口未初始化");
+        return;
+      }
+      try {
+        iframe.contentWindow.focus();
+        iframe.contentWindow.print();
+      } catch (e) {
+        this.msgError("打印失败:" + (e.message || "未知异常"));
+      }
+    },
+
+    downloadHtml() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      const html = this.buildWaybillHtml(this.result.printTemplate);
+      const blob = new Blob([html], { type: "text/html;charset=utf-8" });
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement("a");
+
+      a.href = url;
+      a.download = "waybill_" + (this.result.orderCode || new Date().getTime()) + ".html";
+      document.body.appendChild(a);
+      a.click();
+      document.body.removeChild(a);
+      window.URL.revokeObjectURL(url);
+    },
+
+    resetFormData() {
+      this.form = this.getDefaultForm();
+      this.result = {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      };
+      this.serverMsg = "";
+      this.resetForm("form");
+    }
+  }
+};
+</script>
+
+<style scoped>
+.app-container {
+  min-height: calc(100vh - 84px);
+}
+
+.mb8 {
+  margin-bottom: 8px;
+}
+
+.mb12 {
+  margin-bottom: 12px;
+}
+
+.result-item {
+  line-height: 28px;
+  word-break: break-all;
+}
+
+.result-item .label {
+  color: #606266;
+  font-weight: 600;
+}
+
+.preview-toolbar {
+  margin-bottom: 10px;
+}
+
+.preview-frame {
+  width: 100%;
+  height: 700px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  background: #fff;
+}
+</style>

+ 937 - 0
src/views/kdniao/eorder/indexNew.vue

@@ -0,0 +1,937 @@
+<template>
+  <div class="app-container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="110px"
+      @submit.native.prevent
+    >
+      <!-- 基础信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>电子面单下单</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="业务订单号" prop="bizOrderNo">
+              <el-input v-model="form.bizOrderNo" placeholder="请输入业务订单号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="快递公司" prop="shipperCode">
+              <el-select
+                v-model="form.shipperCode"
+                placeholder="请选择快递公司"
+                clearable
+                style="width: 100%"
+                @change="handleShipperChange"
+              >
+                <el-option label="顺丰 SF" value="SF" />
+                <el-option label="邮政 EMS" value="EMS" />
+                <el-option label="京东快运 JDKY" value="JDKY" />
+                <el-option label="京东快递 JOS" value="JOS" />
+                <el-option label="京东生鲜医药 JDSXYY" value="JDSXYY" />
+                <el-option label="中通 ZTO" value="ZTO" />
+                <el-option label="中通冷链 ZTOCOLD" value="ZTOCOLD" />
+                <el-option label="菜鸟橙运 CNCY" value="CNCY" />
+                <el-option label="菜鸟速递 CNSD" value="CNSD" />
+                <el-option label="笨鸟速运 BNSY" value="BNSY" />
+                <el-option label="丰云配 FYP" value="FYP" />
+                <el-option label="云集医药冷链 YJYYLL" value="YJYYLL" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="支付方式" prop="payType">
+              <el-select v-model="form.payType" placeholder="请选择支付方式" style="width: 100%">
+                <el-option label="现付" :value="1" />
+                <el-option label="到付" :value="2" />
+                <el-option label="月结" :value="3" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="业务类型" prop="expType">
+              <el-input v-model="form.expType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-alert
+          v-if="shipperTip"
+          :title="shipperTip"
+          type="info"
+          :closable="false"
+          show-icon
+        />
+      </el-card>
+
+      <!-- 收件人 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>收件人信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="收件人姓名" prop="receiverName">
+              <el-input v-model="form.receiverName" placeholder="请输入收件人姓名" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="手机号" prop="receiverMobile">
+              <el-input v-model="form.receiverMobile" placeholder="请输入手机号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="电话">
+              <el-input v-model="form.receiverTel" placeholder="请输入电话" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="邮编" :prop="needReceiverPostCode ? 'receiverPostCode' : ''">
+              <el-input
+                v-model="form.receiverPostCode"
+                :placeholder="needReceiverPostCode ? '当前快递要求填写邮编' : '非必填'"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="省" prop="receiverProvinceName">
+              <el-input v-model="form.receiverProvinceName" placeholder="如 广东省" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="市" prop="receiverCityName">
+              <el-input v-model="form.receiverCityName" placeholder="如 深圳市" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="区/县" prop="receiverExpAreaName">
+              <el-input v-model="form.receiverExpAreaName" placeholder="如 福田区" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="详细地址" prop="receiverAddress">
+              <el-input v-model="form.receiverAddress" placeholder="不要填写省市区" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 商品信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>商品与面单信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="商品名称" prop="goodsName">
+              <el-input v-model="form.goodsName" placeholder="如 文件、电子产品" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品数量">
+              <el-input-number v-model="form.goodsQuantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品价格">
+              <el-input-number v-model="form.goodsPrice" :min="0" :precision="2" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品重量">
+              <el-input-number v-model="form.goodsWeight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="包裹数量">
+              <el-input-number v-model="form.quantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总重量" :prop="needWeight ? 'weight' : ''">
+              <el-input-number v-model="form.weight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总体积" :prop="needVolume ? 'volume' : ''">
+              <el-input-number v-model="form.volume" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="模板尺寸">
+              <el-input v-model="form.templateSize" placeholder="默认130" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="备注">
+              <el-input
+                v-model="form.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入备注"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="商品描述">
+              <el-input
+                v-model="form.goodsDesc"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 动态差异字段 -->
+      <el-card shadow="never" class="mb12" v-if="showCarrierFields">
+        <div slot="header">
+          <span>快递差异字段</span>
+        </div>
+
+        <!-- 顺丰 -->
+        <el-row :gutter="20" v-if="isSF">
+          <el-col :span="6">
+            <el-form-item label="币种">
+              <el-select v-model="form.currencyCode" placeholder="请选择币种" clearable style="width: 100%">
+                <el-option label="人民币 CNY" value="CNY" />
+                <el-option label="港币 HKD" value="HKD" />
+                <el-option label="新台币 NTD" value="NTD" />
+                <el-option label="澳门元 MOP" value="MOP" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- EMS -->
+        <el-row :gutter="20" v-if="isEMS">
+          <el-col :span="12">
+            <el-alert
+              title="EMS/邮政要求收件人邮编必填,发件人邮编需在后端配置中填写。"
+              type="warning"
+              :closable="false"
+              show-icon
+            />
+          </el-col>
+        </el-row>
+
+        <!-- 京东快运 -->
+        <el-row :gutter="20" v-if="isJDKY">
+          <el-col :span="6">
+            <el-form-item label="是否通知取件" prop="isNotice">
+              <el-select v-model="form.isNotice" placeholder="请选择" clearable style="width: 100%">
+                <el-option label="通知(0)" :value="0" />
+                <el-option label="不通知(1)" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="配送方式">
+              <el-input-number v-model="form.deliveryMethod" :min="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="上门开始" :prop="needPickupTime ? 'startDate' : ''">
+              <el-input v-model="form.startDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="上门结束" :prop="needPickupTime ? 'endDate' : ''">
+              <el-input v-model="form.endDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 京东快递 / 京东生鲜医药 / 部分快运 -->
+        <el-row :gutter="20" v-if="isJOS || isJDSXYY">
+          <el-col :span="6">
+            <el-form-item label="运输类型">
+              <el-input v-model="form.transType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 中通 / 中通冷链 -->
+        <el-row :gutter="20" v-if="isZTO || isZTOCOLD">
+          <el-col :span="6">
+            <el-form-item label="操作指令">
+              <el-input v-model="form.operateDirective" placeholder="可按业务填写" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 菜鸟/笨鸟/丰云配/云集 -->
+        <el-row :gutter="20" v-if="isCNCY || isCNSD || isBNSY || isFYP || isYJYYLL">
+          <el-col :span="6">
+            <el-form-item label="运输类型">
+              <el-input v-model="form.transType" placeholder="按业务填写" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="是否通知取件">
+              <el-select v-model="form.isNotice" placeholder="请选择" clearable style="width: 100%">
+                <el-option label="通知(0)" :value="0" />
+                <el-option label="不通知(1)" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 操作按钮 -->
+      <el-row :gutter="10" class="mb8">
+        <el-button type="primary" icon="el-icon-s-promotion" :loading="submitLoading" @click="handleSubmit">
+          提交下单
+        </el-button>
+
+        <el-button icon="el-icon-refresh" @click="resetFormData">
+          重置
+        </el-button>
+
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-view"
+          :disabled="!result.printTemplate"
+          @click="handlePreview"
+        >
+          查看面单
+        </el-button>
+
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-printer"
+          :disabled="!result.printTemplate"
+          @click="handlePrintDirect"
+        >
+          打印面单
+        </el-button>
+
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-document"
+          :disabled="!result.printTemplate"
+          @click="openNewWindow"
+        >
+          新窗口打开
+        </el-button>
+
+        <el-button
+          plain
+          icon="el-icon-download"
+          :disabled="!result.printTemplate"
+          @click="downloadHtml"
+        >
+          下载HTML
+        </el-button>
+      </el-row>
+    </el-form>
+
+    <!-- 返回结果 -->
+    <el-card shadow="never" v-if="result.raw">
+      <div slot="header">
+        <span>下单结果</span>
+      </div>
+
+      <el-row :gutter="20" class="mb8">
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">是否成功:</span>
+            <el-tag :type="result.success ? 'success' : 'danger'" size="mini">
+              {{ result.success ? "成功" : "失败" }}
+            </el-tag>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">返回码:</span>
+            <span>{{ result.resultCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">运单号:</span>
+            <span>{{ result.logisticCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">订单号:</span>
+            <span>{{ result.orderCode || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="24">
+          <div class="result-item">
+            <span class="label">返回信息:</span>
+            <span>{{ result.reason || serverMsg || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-divider content-position="left">原始返回</el-divider>
+
+      <el-input
+        type="textarea"
+        :rows="12"
+        :value="result.raw"
+        readonly
+      />
+    </el-card>
+
+    <!-- 面单预览 -->
+    <el-dialog
+      title="面单预览"
+      :visible.sync="previewOpen"
+      width="1000px"
+      append-to-body
+      top="3vh"
+    >
+      <div class="preview-toolbar">
+        <el-button size="mini" type="primary" icon="el-icon-printer" @click="printIframe">
+          打印
+        </el-button>
+        <el-button size="mini" icon="el-icon-document" @click="openNewWindow">
+          新窗口打开
+        </el-button>
+        <el-button size="mini" icon="el-icon-download" @click="downloadHtml">
+          下载HTML
+        </el-button>
+      </div>
+
+      <iframe ref="previewFrame" class="preview-frame"></iframe>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { submitKdniaoUniversalEOrder } from "@/api/kdniao/eorder";
+
+export default {
+  name: "KdniaoEOrder",
+  data() {
+    return {
+      submitLoading: false,
+      previewOpen: false,
+      serverMsg: "",
+      form: {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "李四",
+        receiverMobile: "13900000000",
+        receiverTel: "",
+        receiverProvinceName: "广东省",
+        receiverCityName: "深圳市",
+        receiverExpAreaName: "福田区",
+        receiverAddress: "华强北赛格广场",
+        receiverPostCode: "518000",
+
+        goodsName: "电子产品",
+        goodsQuantity: 1,
+        goodsPrice: 1000,
+        goodsWeight: 0.5,
+        goodsDesc: "测试商品",
+
+        quantity: 1,
+        weight: 0.5,
+        volume: 0.001,
+        cost: null,
+        otherCost: null,
+
+        remark: "测试单请勿接",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        isNotice: null,
+        startDate: "",
+        endDate: "",
+
+        currencyCode: "",
+        deliveryMethod: null,
+        transType: "",
+        operateDirective: ""
+      },
+      result: {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      },
+      rules: {
+        bizOrderNo: [{ required: true, message: "业务订单号不能为空", trigger: "blur" }],
+        shipperCode: [{ required: true, message: "快递公司不能为空", trigger: "change" }],
+        payType: [{ required: true, message: "支付方式不能为空", trigger: "change" }],
+        expType: [{ required: true, message: "业务类型不能为空", trigger: "blur" }],
+        receiverName: [{ required: true, message: "收件人姓名不能为空", trigger: "blur" }],
+        receiverMobile: [{ required: true, message: "收件人手机号不能为空", trigger: "blur" }],
+        receiverProvinceName: [{ required: true, message: "省不能为空", trigger: "blur" }],
+        receiverCityName: [{ required: true, message: "市不能为空", trigger: "blur" }],
+        receiverExpAreaName: [{ required: true, message: "区/县不能为空", trigger: "blur" }],
+        receiverAddress: [{ required: true, message: "详细地址不能为空", trigger: "blur" }],
+        receiverPostCode: [{ required: true, message: "当前快递要求邮编必填", trigger: "blur" }],
+        goodsName: [{ required: true, message: "商品名称不能为空", trigger: "blur" }],
+        weight: [{ required: true, message: "当前快递要求总重量必填", trigger: "blur" }],
+        volume: [{ required: true, message: "当前快递要求总体积必填", trigger: "blur" }],
+        isNotice: [{ required: true, message: "当前快递要求是否通知取件必填", trigger: "change" }],
+        startDate: [{ required: true, message: "当前快递要求上门开始时间必填", trigger: "blur" }],
+        endDate: [{ required: true, message: "当前快递要求上门结束时间必填", trigger: "blur" }]
+      }
+    };
+  },
+  computed: {
+    isSF() {
+      return this.form.shipperCode === "SF";
+    },
+    isEMS() {
+      return this.form.shipperCode === "EMS";
+    },
+    isJDKY() {
+      return this.form.shipperCode === "JDKY";
+    },
+    isJOS() {
+      return this.form.shipperCode === "JOS";
+    },
+    isJDSXYY() {
+      return this.form.shipperCode === "JDSXYY";
+    },
+    isZTO() {
+      return this.form.shipperCode === "ZTO";
+    },
+    isZTOCOLD() {
+      return this.form.shipperCode === "ZTOCOLD";
+    },
+    isCNCY() {
+      return this.form.shipperCode === "CNCY";
+    },
+    isCNSD() {
+      return this.form.shipperCode === "CNSD";
+    },
+    isBNSY() {
+      return this.form.shipperCode === "BNSY";
+    },
+    isFYP() {
+      return this.form.shipperCode === "FYP";
+    },
+    isYJYYLL() {
+      return this.form.shipperCode === "YJYYLL";
+    },
+    showCarrierFields() {
+      return true;
+    },
+    needReceiverPostCode() {
+      return this.isEMS;
+    },
+    needWeight() {
+      return this.isJDKY;
+    },
+    needVolume() {
+      return this.isJDKY;
+    },
+    needPickupTime() {
+      return this.isJDKY && this.form.isNotice === 0;
+    },
+    shipperTip() {
+      if (this.isEMS) {
+        return "EMS/邮政:收件人邮编必填,发件人邮编需后端配置。";
+      }
+      if (this.isJDKY) {
+        return "京东快运:总重量、总体积、是否通知取件必填;通知取件时需填写上门时间。";
+      }
+      if (this.isSF) {
+        return "顺丰:如港澳台场景,可填写币种。";
+      }
+      if (this.isZTO || this.isZTOCOLD) {
+        return "中通/中通冷链:如业务需要可填写操作指令。";
+      }
+      return "";
+    }
+  },
+  methods: {
+    handleShipperChange() {
+      // 按快递切换时清理差异字段,避免串值
+      this.form.currencyCode = "";
+      this.form.deliveryMethod = null;
+      this.form.transType = "";
+      this.form.operateDirective = "";
+      this.form.isNotice = null;
+      this.form.startDate = "";
+      this.form.endDate = "";
+
+      if (!this.isEMS) {
+        // 非EMS时邮编不强制,但保留用户已填内容,不强清
+      }
+    },
+
+    buildSubmitParams() {
+      const carrierFields = {};
+
+      if (this.form.currencyCode) {
+        carrierFields.CurrencyCode = this.form.currencyCode;
+      }
+      if (this.form.deliveryMethod !== null && this.form.deliveryMethod !== "" && this.form.deliveryMethod !== undefined) {
+        carrierFields.DeliveryMethod = this.form.deliveryMethod;
+      }
+      if (this.form.transType) {
+        carrierFields.TransType = this.form.transType;
+      }
+      if (this.form.operateDirective) {
+        carrierFields.OperateDirective = this.form.operateDirective;
+      }
+
+      return {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiver: {
+          name: this.form.receiverName,
+          mobile: this.form.receiverMobile,
+          tel: this.form.receiverTel,
+          provinceName: this.form.receiverProvinceName,
+          cityName: this.form.receiverCityName,
+          expAreaName: this.form.receiverExpAreaName,
+          address: this.form.receiverAddress,
+          postCode: this.form.receiverPostCode
+        },
+
+        commodity: [
+          {
+            goodsName: this.form.goodsName,
+            goodsQuantity: this.form.goodsQuantity,
+            goodsPrice: this.form.goodsPrice,
+            goodsWeight: this.form.goodsWeight,
+            goodsDesc: this.form.goodsDesc
+          }
+        ],
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe,
+
+        isNotice: this.form.isNotice,
+        startDate: this.form.startDate,
+        endDate: this.form.endDate,
+
+        carrierFields: carrierFields
+      };
+    },
+
+    handleSubmit() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) return;
+
+        this.submitLoading = true;
+        this.serverMsg = "";
+        this.result = {
+          success: false,
+          resultCode: "",
+          reason: "",
+          logisticCode: "",
+          orderCode: "",
+          printTemplate: "",
+          raw: ""
+        };
+
+        const submitParams = this.buildSubmitParams();
+
+        submitKdniaoUniversalEOrder(submitParams).then(res => {
+          this.serverMsg = res.msg || "";
+
+          const data = res.data || {};
+          this.result.success = data.success === true || data.Success === true;
+          this.result.resultCode = data.resultCode || data.ResultCode || "";
+          this.result.reason = data.reason || data.Reason || "";
+          this.result.printTemplate = data.printTemplate || data.PrintTemplate || "";
+
+          const order = data.order || data.Order || {};
+          this.result.logisticCode = order.logisticCode || order.LogisticCode || "";
+          this.result.orderCode = order.orderCode || order.OrderCode || "";
+          this.result.raw = JSON.stringify(data, null, 2);
+
+          if (res.code === 200) {
+            this.msgSuccess(res.msg || "下单成功");
+          } else {
+            this.msgError(res.msg || "下单失败");
+          }
+        }).catch(err => {
+          this.msgError((err && err.message) || "请求失败");
+        }).finally(() => {
+          this.submitLoading = false;
+        });
+      });
+    },
+
+    handlePreview() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+      });
+    },
+
+    handlePrintDirect() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+        setTimeout(() => {
+          this.printIframe();
+        }, 200);
+      });
+    },
+
+    writePreviewFrame() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        return;
+      }
+      const doc = iframe.contentWindow.document;
+      doc.open();
+      doc.write(this.buildWaybillHtml(this.result.printTemplate));
+      doc.close();
+    },
+
+    buildWaybillHtml(templateHtml) {
+      const html = templateHtml || "";
+
+      if (/<html[\s\S]*?>/i.test(html)) {
+        return html;
+      }
+
+      return `
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>面单预览</title>
+  <style>
+    html, body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+    }
+    body {
+      overflow: auto;
+    }
+    @page {
+      margin: 0;
+    }
+  </style>
+</head>
+<body>
+${html}
+</body>
+</html>`;
+    },
+
+    openNewWindow() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      const win = window.open("", "_blank");
+      if (!win) {
+        this.msgError("浏览器拦截了新窗口,请允许弹窗后重试");
+        return;
+      }
+      win.document.open();
+      win.document.write(this.buildWaybillHtml(this.result.printTemplate));
+      win.document.close();
+    },
+
+    printIframe() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        this.msgError("打印窗口未初始化");
+        return;
+      }
+      try {
+        iframe.contentWindow.focus();
+        iframe.contentWindow.print();
+      } catch (e) {
+        this.msgError("打印失败:" + (e.message || "未知异常"));
+      }
+    },
+
+    downloadHtml() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      const html = this.buildWaybillHtml(this.result.printTemplate);
+      const blob = new Blob([html], { type: "text/html;charset=utf-8" });
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement("a");
+
+      a.href = url;
+      a.download = "waybill_" + (this.result.orderCode || new Date().getTime()) + ".html";
+      document.body.appendChild(a);
+      a.click();
+      document.body.removeChild(a);
+      window.URL.revokeObjectURL(url);
+    },
+
+    resetFormData() {
+      this.form = {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "",
+        receiverMobile: "",
+        receiverTel: "",
+        receiverProvinceName: "",
+        receiverCityName: "",
+        receiverExpAreaName: "",
+        receiverAddress: "",
+        receiverPostCode: "",
+
+        goodsName: "",
+        goodsQuantity: 1,
+        goodsPrice: null,
+        goodsWeight: null,
+        goodsDesc: "",
+
+        quantity: 1,
+        weight: null,
+        volume: null,
+        cost: null,
+        otherCost: null,
+
+        remark: "",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        isNotice: null,
+        startDate: "",
+        endDate: "",
+
+        currencyCode: "",
+        deliveryMethod: null,
+        transType: "",
+        operateDirective: ""
+      };
+      this.result = {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      };
+      this.serverMsg = "";
+      this.resetForm("form");
+    }
+  }
+};
+</script>
+
+<style scoped>
+.app-container {
+  min-height: calc(100vh - 84px);
+}
+
+.mb8 {
+  margin-bottom: 8px;
+}
+
+.mb12 {
+  margin-bottom: 12px;
+}
+
+.result-item {
+  line-height: 28px;
+  word-break: break-all;
+}
+
+.result-item .label {
+  color: #606266;
+  font-weight: 600;
+}
+
+.preview-toolbar {
+  margin-bottom: 10px;
+}
+
+.preview-frame {
+  width: 100%;
+  height: 700px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  background: #fff;
+}
+</style>

+ 25 - 0
src/views/live/liveConfig/liveLotteryConf.vue

@@ -248,6 +248,7 @@
                   remote
                 reserve-keyword
                 placeholder="请输入关键字搜索"
+                :remote-method="fetchProducts"
                 :loading="loadingProducts"
                 size="small"
                 style="width: 180px"
@@ -521,6 +522,30 @@ export default {
     );
   },
   methods: {
+
+    async fetchProducts(query) {
+
+      if (!query) {
+        this.productOptions = [];
+        return;
+      }
+      const queryParam = {
+        liveId : this.liveId,
+        keywords: query
+      }
+      this.loadingProducts = true;
+      try {
+        listLiveGoods(queryParam).then(response => {
+          this.productOptions = response.rows;
+        })
+      } catch (err) {
+        console.error('查询商品失败:', err);
+        this.productOptions = [];
+      } finally {
+        this.loadingProducts = false;
+      }
+    },
+
     getProducts(){
       const queryParam = {
         liveId : this.liveId

+ 2 - 2
src/views/live/liveConfig/liveRedConf.vue

@@ -177,8 +177,8 @@
         <el-form-item label="直播间ID" prop="liveId">
           <el-input v-model="form.liveId" placeholder="请输入直播间ID" :disabled="canLiveId"/>
         </el-form-item>
-        <el-form-item label="芳华币数" prop="redNum">
-          <el-input v-model="form.redNum" placeholder="请输入芳华币数量" />
+        <el-form-item label="积分" prop="redNum">
+          <el-input v-model="form.redNum" placeholder="请输入积分数量" />
         </el-form-item>
         <el-form-item label="中奖份量" prop="totalLots">
           <el-input v-model="form.totalLots" placeholder="请输入可中奖份量" />

+ 6 - 6
src/views/live/liveCoupon/index.vue

@@ -57,7 +57,7 @@
           icon="el-icon-plus"
           size="mini"
           @click="handleAdd"
-          v-hasPermi="['store:storeCoupon:add']"
+          v-hasPermi="['live:liveCoupon:add']"
         >新增</el-button>
       </el-col>
 
@@ -68,7 +68,7 @@
           size="mini"
           :disabled="multiple"
           @click="handleDelete"
-          v-hasPermi="['store:storeCoupon:remove']"
+          v-hasPermi="['live:liveCoupon:remove']"
         >删除</el-button>
       </el-col>
       <el-col :span="1.5">
@@ -77,7 +77,7 @@
           size="mini"
           :disabled="multiple"
           @click="handleBatchPublish"
-          v-hasPermi="['store:storeCoupon:batchPublish']"
+          v-hasPermi="['live:liveCoupon:batchPublish']"
         >批量发布</el-button>
       </el-col>
 
@@ -116,21 +116,21 @@
             size="mini"
             type="text"
             @click="handlePublish(scope.row)"
-            v-hasPermi="['store:storeCoupon:publish']"
+            v-hasPermi="['live:liveCoupon:publish']"
           >发布</el-button>
           <el-button
             size="mini"
             type="text"
             icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
-            v-hasPermi="['store:storeCoupon:edit']"
+            v-hasPermi="['live:liveCoupon:edit']"
           >修改</el-button>
           <el-button
             size="mini"
             type="text"
             icon="el-icon-delete"
             @click="handleDelete(scope.row)"
-            v-hasPermi="['store:storeCoupon:remove']"
+            v-hasPermi="['live:liveCoupon:remove']"
           >删除</el-button>
         </template>
       </el-table-column>

+ 140 - 2
src/views/system/config/config.vue

@@ -2813,6 +2813,74 @@
           <el-button type="primary" @click="submitForm35">提 交</el-button>
         </div>
       </el-tab-pane>
+      <el-tab-pane label="AI打标签行业配置" name="aiTagTradeType.config">
+        <el-form ref="form37" :model="form37" :rules="rules36" label-width="300px">
+          <el-form-item label="行业选择" prop="tradeType">
+            <el-select
+              v-model="form37.tradeType"
+              clearable
+              filterable
+              placeholder="请选择行业"
+              style="width: 400px"
+            >
+              <el-option
+                v-for="item in tradeTypeOptions"
+                :key="item.dictValue"
+                :label="item.dictLabel"
+                :value="item.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+
+        <div class="footer">
+          <el-button type="primary" @click="submitForm37">提 交</el-button>
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="提现配置" name="his.AppRedPacket">
+        <!-- 添加 ref 和 rules -->
+        <el-form ref="form36" :model="form36" label-width="160px">
+
+          <el-form-item label="红包接口类型" prop="isNew">
+            <el-radio-group v-model="form36.isNew" >
+              <el-radio label="0">商家转账到零钱(旧)</el-radio>
+              <el-radio label="1">商家转账到零钱(新)</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="商户号" prop="mchId">
+            <el-input v-model="form36.mchId" placeholder="请输入商户号"></el-input>
+          </el-form-item>
+          <el-form-item label="商户密钥" prop="mchKey">
+            <el-input v-model="form36.mchKey" placeholder="请输入商户密钥" show-password></el-input>
+          </el-form-item>
+          <el-form-item label="p12证书路径" prop="keyPath">
+            <el-input v-model="form36.keyPath" placeholder="请输入p12证书路径"></el-input>
+          </el-form-item>
+          <el-form-item label="apiV3密钥" prop="apiV3Key">
+            <el-input v-model="form36.apiV3Key" placeholder="请输入apiV3密钥" show-password></el-input>
+          </el-form-item>
+          <el-form-item label="公钥ID" prop="publicKeyId">
+            <el-input v-model="form36.publicKeyId" placeholder="请输入公钥ID"></el-input>
+          </el-form-item>
+          <el-form-item label="公钥证书路径" prop="publicKeyPath">
+            <el-input v-model="form36.publicKeyPath" placeholder="请输入公钥证书路径"></el-input>
+          </el-form-item>
+          <el-form-item label="私钥路径" prop="privateKeyPath">
+            <el-input v-model="form36.privateKeyPath" placeholder="请输入私钥路径"></el-input>
+          </el-form-item>
+          <el-form-item label="证书路径" prop="privateCertPath">
+            <el-input v-model="form36.privateCertPath" placeholder="请输入证书路径"></el-input>
+          </el-form-item>
+          <el-form-item label="回调地址" prop="notifyUrl">
+            <el-input v-model="form36.notifyUrl" placeholder="请输入回调地址"></el-input>
+          </el-form-item>
+
+          <div class="footer">
+            <el-button type="primary" @click="submitForm36">提 交</el-button>
+          </div>
+        </el-form>
+      </el-tab-pane>
 
     </el-tabs>
 
@@ -3022,6 +3090,10 @@ export default {
       form33:{},
       form34:{},
       form35:{},
+      form36:{},
+      form37:{
+        tradeType: ''
+      },
       form40: {
         enablePhoneConfig: false,
         enablePhoneLimitConfig:false,
@@ -3103,9 +3175,18 @@ export default {
       rules33: {},
       rules34: {},
       rules35: {},
+      rules36: {
+        tradeType: [
+          { required: true, message: '请选择行业', trigger: 'change' }
+        ]
+      },
+      tradeTypeOptions: [],
     }
   },
   created() {
+    this.getDicts("trade_type").then((response) => {
+      this.tradeTypeOptions = response.data;
+    });
     this.checkProjectName();
     this.getConfigByKey(this.activeName)
     listStore().then(response => {
@@ -3378,8 +3459,8 @@ export default {
     },
     handleClick(tab, event) {
     this.getConfigByKey(tab.name)
-     
-      
+
+
     },
     handleAddProduct() {
       setTimeout(() => {
@@ -3434,6 +3515,8 @@ export default {
           this.configKey = key;
           if (key == 'cId.config') {
             this.form40 = { enablePhoneConfig: false, generateCount: 1 };
+          } else if (key == 'aiTagTradeType.config') {
+            this.form36 = { tradeType: '' };
           }
         }
         if (key == 'sys.oss.cloudStorage') {
@@ -3599,6 +3682,45 @@ export default {
         if(key=="courseAppConfig.config"){
           this.form35 =JSON.parse(response.data.configValue);
         }
+        if (key == 'his.AppRedPacket') {
+          if (response.data && response.data.configValue) {
+            this.form36 = JSON.parse(response.data.configValue);
+          } else {
+            // 如果没有配置,使用默认值
+            this.form36 = {
+              isNew: '1',
+              mchId: '',
+              notifyUrl: '',
+              mchKey: '',
+              keyPath: '',
+              apiV3Key: '',
+              serialNo: '',
+              privateCertPath: '',
+              privateKeyPath: '',
+              publicKeyPath: ''
+            };
+          }
+          this.configId = response.data?.configId || null;
+          this.configKey = response.data?.configKey || 'his.AppRedPacket';
+        }
+        if(key=="aiTagTradeType.config"){
+          if (response.data && response.data.configValue) {
+            const parsed = JSON.parse(response.data.configValue);
+            let tradeType = '';
+            if (Array.isArray(parsed)) {
+              tradeType = parsed[0] != null ? String(parsed[0]) : '';
+            } else if (parsed && typeof parsed === 'object') {
+              if (parsed.tradeType != null && parsed.tradeType !== '') {
+                tradeType = String(parsed.tradeType);
+              } else if (Array.isArray(parsed.tradeTypes) && parsed.tradeTypes.length) {
+                tradeType = String(parsed.tradeTypes[0]);
+              }
+            }
+            this.form37 = { tradeType };
+          } else {
+            this.form37 = { tradeType: '' };
+          }
+        }
         if(key == 'vc.config'){
           if(!!response.data){
             this.configId = response.data.configId
@@ -3938,6 +4060,22 @@ export default {
         }
       });
     },
+    submitForm36(){
+      var param={configId:this.configId,configName : "APP提现配置", configKey: this.configKey,configValue:JSON.stringify(this.form36)}
+      updateConfigByKey(param).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("修改成功");
+        }
+      });
+    },
+    submitForm37(){
+      var param={configId:this.configId,configName : "AI打标签行业配置", configKey: this.configKey,configValue:JSON.stringify(this.form37)}
+      updateConfigByKey(param).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("修改成功");
+        }
+      });
+    },
     submitForm32(){
       const param = { configId: this.configId, configName : "直播源配置", configKey: this.configKey, configValue: JSON.stringify(this.form32) }
       console.log(param)

+ 10 - 0
src/views/system/config/integralConfig.vue

@@ -130,6 +130,15 @@
           </el-form-item>
         </el-col>
       </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item  label="首次下载App获取积分" prop="downloadAppIntegral">
+            <el-tooltip class="item" effect="dark" content="首次下载App获取积分" placement="top-end">
+              <el-input-number  v-model="form11.downloadAppIntegral"   ></el-input-number>
+            </el-tooltip>
+          </el-form-item>
+        </el-col>
+      </el-row>
       <el-row>
         <el-col :span="12">
           <el-form-item  label="单日可获总积分" prop="integralByOneDay">
@@ -183,6 +192,7 @@ export default {
         integralFirstOrderPoint: null,
         integralTypeByOneDay: null,
         integralSubscriptCourse: null,
+        downloadAppIntegral: null,
       },
       saveLoading: false,
     }

+ 34 - 14
src/views/watch/deviceInfo/details.vue

@@ -64,7 +64,7 @@
           </div>
         </div>
         <div class="equipment-day">
-          已绑定 <span>400</span> 天, 佩戴 <span>4365</span> 天
+          <!-- 已绑定 <span>400</span> 天, 佩戴 <span>4365</span> 天 -->
         </div>
         <div class="equipment-btn">
           <el-button type="primary" size="small">一键上传</el-button>
@@ -83,7 +83,7 @@
       </el-tabs>
       
       <keep-alive>
-        <component :ref="currentTabComponent" :is="currentTabComponent" :deviceId="deviceId" :basicInfo="basicInfo" :detailsType="detailsType">
+        <component :ref="currentTabComponent" :is="currentTabComponent" :deviceId="deviceId" :basicInfo="basicInfo" :detailsType="detailsType" :userInfo="userInfo">
         </component>
       </keep-alive>
     </el-main>
@@ -115,6 +115,8 @@ import Overview from '@/components/DeviceInfo/Overview.vue';
 import Bloodoxygen from '@/components/DeviceInfo/Bloodoxygen.vue';
 import Uricacid from '@/components/DeviceInfo/Uricacid.vue';
 import Urineketones from '@/components/DeviceInfo/Urineketones.vue';
+import PressureValue from '@/components/DeviceInfo/PressureValue.vue';
+import Report from '@/components/DeviceInfo/Report.vue';
 
 export default {
   name: "DeviceDetails",
@@ -134,7 +136,9 @@ export default {
     Overview,
     Bloodoxygen,
     Uricacid,
-    Urineketones
+    Urineketones,
+    PressureValue,
+    Report
   },
   data() {
     return {
@@ -179,10 +183,10 @@ export default {
           label: '位置',
           name: 'location'
         },
-        {
-          label: '脉搏',
-          name: 'pulse'
-        },
+        // {
+        //   label: '脉搏',
+        //   name: 'pulse'
+        // },
         {
           label: '血糖',
           name: 'bloodsugar'
@@ -191,10 +195,10 @@ export default {
           label: '血压',
           name: 'bloodpressure'
         },
-        {
-          label: '血酮',
-          name: 'urineketones'
-        },
+        // {
+        //   label: '血酮',
+        //   name: 'urineketones'
+        // },
         {
           label: '尿酸',
           name: 'uricacid'
@@ -211,6 +215,14 @@ export default {
         //   label: '血脂',
         //   name: 'bloodfat'
         // },
+        {
+          label: '压力',
+          name: 'pressureValue'
+        },
+        {
+          label: '周报',
+          name: 'report'
+        },
       ]
     };
   },
@@ -238,8 +250,9 @@ export default {
         overview: Overview,
         bloodoxygen: Bloodoxygen,
         uricacid: Uricacid,
-        urineketones: Urineketones
-
+        urineketones: Urineketones,
+        pressureValue:PressureValue,
+        report:Report
 
 
       }
@@ -342,9 +355,12 @@ export default {
      */
     async getUserinfo(deviceId) {
       try {
-        const response = await getUser(deviceId);
+        const userId = this.$route.params.userId;
+        const selectType = this.$route.params.selectType;
+        const response = await getUser({deviceId:deviceId,userId:userId,selectType});
         if (response && response.data) {
           this.userInfo = response.data; // 确保数据被正确赋值
+          this.userInfo.userId = userId;
           // console.log("用户信息:", this.userInfo);
         } else {
           console.warn("用户信息返回为空或格式不正确:", response);
@@ -517,6 +533,10 @@ export default {
   // line-height: 160px;
   padding: 0;
 }
+::v-deep .el-main::-webkit-scrollbar {
+    width: 0;
+    display: none;  /* Chrome, Safari and Opera */
+  }
 
 body>.el-container {
   margin-bottom: 40px;

+ 94 - 47
src/views/watch/deviceInfo/index.vue

@@ -1,31 +1,31 @@
 <template>
   <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="所属公司" prop="companyId" v-if="shouldShowCompanySelect">
-        <el-select v-model="queryParams.companyId" placeholder="请选择所属公司" clearable size="small" @change="getCompanyUserList">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="所属技术主体" prop="companyId" v-if="shouldShowCompanySelect">
+        <el-select v-model="queryParams.companyId" placeholder="请选择所属技术主体" clearable filterable size="small" @change="getCompanyUserList">
           <el-option v-for="item in companyList" :key="item.companyId" :label="item.companyName"
             :value="item.companyId">
           </el-option>
         </el-select>
       </el-form-item>
-      <el-form-item label="所属员工" prop="companyUserId">
-        <el-select v-model="queryParams.companyUserId" placeholder="请选择所属员工" clearable size="small">
+      <el-form-item label="所属导医" prop="companyUserId">
+        <el-select v-model="queryParams.companyUserId" placeholder="请选择所属导医" clearable filterable size="small">
           <el-option v-for="item in companyUserList" :key="item.userId" :label="item.nickName"
             :value="item.userId">
           </el-option>
         </el-select>
       </el-form-item>
       <el-form-item label="所属医生" prop="doctorId">
-        <el-select v-model="queryParams.doctorId" placeholder="请选择所属医生" clearable size="small">
+        <el-select v-model="queryParams.doctorId" placeholder="请选择所属医生" clearable filterable size="small">
           <el-option v-for="item in doctorList" :key="item.doctorId" :label="item.doctorName"
             :value="item.doctorId">
           </el-option>
         </el-select>
       </el-form-item>
-      <el-form-item label="绑定用户" prop="watchUserName">
+      <!-- <el-form-item label="绑定用户" prop="watchUserName">
         <el-input v-model="queryParams.watchUserName" placeholder="请输入绑定用户" clearable size="small"
           @keyup.enter.native="handleQuery" />
-      </el-form-item>
+      </el-form-item> -->
       <el-form-item label="设备编号" prop="deviceNumber">
         <el-input v-model="queryParams.deviceNumber" placeholder="请输入设备编号" clearable size="small"
           @keyup.enter.native="handleQuery" />
@@ -52,6 +52,12 @@
           </el-option>
         </el-select>
       </el-form-item>
+      <el-form-item label="健康状态" prop="isNormal">
+        <el-select v-model="queryParams.isNormal" placeholder="请选择健康状态" clearable size="small">
+          <el-option v-for="item in healthOptions" :key="item.value" :label="item.label" :value="item.value">
+          </el-option>
+        </el-select>
+      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -140,10 +146,31 @@
     <el-table v-loading="loading" :data="deviceInfoList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="设备id" align="center" prop="deviceId" />
-      <el-table-column label="所属公司" align="center" prop="companyName" />
-      <el-table-column label="所属员工" align="center" prop="companyUserName" />
+      <el-table-column label="所属技术主体" align="center" prop="companyName" />
+      <el-table-column label="所属导医" align="center" prop="companyUserName" />
       <el-table-column label="所属医生" align="center" prop="doctorName" />
-      <el-table-column label="绑定用户" align="center" prop="watchUserName" />
+    
+      <el-table-column  label="绑定用户"  align="center" prop="watchUserName">
+        <!-- <template slot-scope="scope"> 
+          <div v-if="scope.row.users && scope.row.users!=null">
+              <el-link :underline="false" @click="handleDetails(scope.row.deviceId, scope.row.users.userId)">
+                {{ scope.row.users.nickName }}
+              </el-link>
+          </div>
+        </template> -->
+        
+      </el-table-column>
+      <el-table-column  label="绑定家人用户"  align="center" prop="familyUserName">
+          <!-- <template slot-scope="scope">
+            <div v-if="scope.row.familyUsers">
+              <div v-for="(item, index) in scope.row.familyUsers" :key="index">
+                <el-link :underline="false" @click="handleDetails(scope.row.deviceId, item.userId)">
+                  {{ item.nickName }}
+                </el-link>
+              </div>
+            </div>
+          </template> -->
+      </el-table-column>
       <el-table-column label="设备编号" align="center" prop="deviceNumber" width="150"/>
       <el-table-column label="电量" align="center" prop="battery" />
       <el-table-column label="信号" align="center" prop="rssi" />
@@ -158,13 +185,13 @@
           >
             最新数据
           </span>
-          <el-tag size="mini" :type="scope.row.isNormal == '异常'?'danger':'primary'">{{scope.row.isNormal}}</el-tag>
+          <el-tag size="mini" :type="scope.row.isNormal == 0?'danger':'primary'">{{ scope.row.isNormal === 1 ? '正常' : '异常' }}</el-tag>
 
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
-          <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetails(scope.row.deviceId)"
+          <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetailsAndType(scope.row)"
             v-hasPermi="['watchApi:deviceInfo:query']">查询详情</el-button>
           <el-button size="mini" type="text" icon="el-icon-phone-outline" @click="handleSoSPeople(scope.row.deviceNumber)"
             v-hasPermi="['watchApi:deviceInfo:query']">查看紧急联系人</el-button>
@@ -197,24 +224,7 @@
         <div slot="footer" class="dialog-footer">
           <el-button @click="healthDataDialogVisible = false">关闭</el-button>
         </div>
-      </el-dialog>
-
-      <!-- 历史数据对话框 -->
-    <!-- <el-dialog :visible.sync="historyDataDialogVisible" title="历史数据" width="50%">
-      <div v-if="historyData">
-        <el-table :data="historyData">
-          <el-table-column prop="type" label="类型" align="center" />
-          <el-table-column prop="data" label="数值" align="center" />
-          <el-table-column prop="date" label="时间" align="center" />
-        </el-table>
-      </div>
-      <div v-else>
-        <p>暂无历史数据</p>
-      </div>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="historyDataDialogVisible = false">关闭</el-button>
-      </div>
-    </el-dialog> -->
+    </el-dialog>
       
 
     <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@@ -222,22 +232,32 @@
 
     <!-- 添加或修改设备信息对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="所属公司" prop="companyId">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="所属技术主体">
           <template #label>
-            <span class="required-label">所属公司</span>
+            <span class="required-label">所属技术主体</span>
           </template>
-          <el-select v-model="form.companyId" placeholder="请选择所属公司" clearable size="small" @change="getAddCompanyUserList">
-            <el-option v-for="item in companyList" :key="item.companyId" :label="item.companyName"
-              :value="item.companyId">
-            </el-option>
+          <el-select
+            v-model="selectedCompanyIds"
+            placeholder="请选择所属技术主体"
+            clearable
+            filterable
+            multiple
+            size="small"
+          >
+            <el-option
+              v-for="item in companyList"
+              :key="item.companyId"
+              :label="item.companyName"
+              :value="Number(item.companyId)"
+            ></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="所属员工" prop="companyUserId">
+        <el-form-item label="所属导医">
           <template #label>
-            <span class="required-label">所属员工</span>
+            <span class="required-label">所属导医</span>
           </template>
-          <el-select v-model="form.companyUserId" placeholder="请选择所属员工" clearable size="small">
+          <el-select v-model="form.companyUserId" placeholder="请选择所属导医" clearable size="small">
             <el-option v-for="item in companyUserList" :key="item.userId" :label="item.nickName"
               :value="item.userId">
             </el-option>
@@ -369,7 +389,7 @@ export default {
       },
       // 表单参数
       form: {
-        companyId: '',
+        companyId: '', //原始的以逗号分隔的字符串
         companyUserId:"",
         doctorId:"",
         deviceNumber: '',
@@ -377,6 +397,7 @@ export default {
         rssi: '',
         status: 1,
       },
+      selectedCompanyIds: [], // 用于绑定选择框的数组
       // 表单校验
       rules: {
         // companyId: [
@@ -387,6 +408,13 @@ export default {
           { pattern: /^\d{15}$/, message: "设备编号必须是15位数字", trigger: "blur" }
         ],
       },
+      healthOptions: [{
+          value: 0,
+          label: '异常'
+        }, {
+          value: 1,
+          label: '正常'
+        }],
       options: [{
         value: '0',
         label: 'OFFLINE'
@@ -584,10 +612,16 @@ export default {
     this.getCompanyUserList();
     this.getDoctorList();
   },
+
   methods: {
+    parseCompanyId() {
+      // 将原始的以逗号分隔的字符串解析为数组
+      if (this.form.companyId) {
+        this.selectedCompanyIds = this.form.companyId.split(',').map(Number);
+      }
+    },
     rowClassName({row}) {
-      if (row.status && row.status.includes('偏')) {
-        // console.log("=============================" + JSON.stringify(row.status, null, 2))
+      if (row.status && (!row.status.includes('正常') && !row.status.includes('得分') && !row.status.includes('无结果') && !row.status.includes('窦性心率'))) {
         return 'text-red';
         // return true
       }
@@ -693,9 +727,18 @@ export default {
 
       }
     },
+    handleDetailsAndType(row){
+      if(row.userId != null){
+        this.handleDetails(row.deviceId,row.userId)
+      } else if(row.familyUsers !=null && row.familyUsers.length >0){
+        this.handleDetails(row.deviceId,row.familyUsers[0].userId)
+      } else {
+        this.handleDetails(row.deviceId,null);
+      }
+    },
     /** 处理查询详情按钮点击事件 */
-    handleDetails(deviceId) {
-      this.$router.push({ name: 'details', params: { deviceId } });
+    handleDetails(deviceId,userId) {
+      this.$router.push({ name: 'details', params: { deviceId:deviceId,userId:userId} });
     },
 
     /** 查询设备信息列表 */
@@ -726,7 +769,8 @@ export default {
         deviceNumber: null,
         battery: null,
         rssi: null,
-        status: 0
+        status: 0,
+        selectedCompanyIds: [],
       };
       this.resetForm("form");
     },
@@ -759,6 +803,7 @@ export default {
       const deviceId = row.deviceId || this.ids
       getDeviceInfo(deviceId).then(response => {
         this.form = response.data;
+        this.parseCompanyId()
         this.open = true;
         this.title = "修改设备信息";
       });
@@ -767,6 +812,8 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
+          // 将 selectedCompanyIds 数组转换为以逗号分隔的字符串
+          this.form.companyId = this.selectedCompanyIds.join(',');
           if (this.form.deviceId != null) {
             console.log("===============================" + JSON.stringify(this.form, null, 2))
             updateDeviceInfo(this.form).then(response => {

+ 11 - 3
src/views/watch/iot/index.vue

@@ -22,7 +22,15 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
-
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['store:storePayment:export']"
+        >导出</el-button>
+      </el-col>
     <!-- <el-row :gutter="10" class="mb8">
       
       <el-col :span="1.5">
@@ -139,7 +147,7 @@
 </template>
 
 <script>
-import {importTemplate, listDeviceInfo, getDeviceInfo, delDeviceInfo, addDeviceInfo, updateDeviceInfo, exportDeviceInfo,queryIotList } from "@/api/watch/deviceInfo";
+import {importTemplate, listDeviceInfo, getDeviceInfo, delDeviceInfo, addDeviceInfo, updateDeviceInfo, exportDeviceInfo,queryIotList,exportDeviceIdAndIotInfo} from "@/api/watch/deviceInfo";
 import { listCompany } from "@/api/company/company";
 
 import { getToken } from "@/utils/auth";
@@ -316,7 +324,7 @@ export default {
         type: "warning"
       }).then(() => {
         this.exportLoading = true;
-        return exportDeviceInfo(queryParams);
+        return exportDeviceIdAndIotInfo(queryParams);
       }).then(response => {
         this.download(response.msg);
         this.exportLoading = false;

+ 58 - 45
src/views/watch/isBind/index.vue

@@ -1,31 +1,31 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="所属公司" prop="companyId" v-if="shouldShowCompanySelect">
-        <el-select v-model="queryParams.companyId" placeholder="请选择所属公司" clearable size="small" @change="getCompanyUserList">
+      <el-form-item label="所属技术主体" prop="companyId" v-if="shouldShowCompanySelect">
+        <el-select v-model="queryParams.companyId" placeholder="请选择所属技术主体" clearable filterable size="small" @change="getCompanyUserList">
           <el-option v-for="item in companyList" :key="item.companyId" :label="item.companyName"
             :value="item.companyId">
           </el-option>
         </el-select>
       </el-form-item>
-      <el-form-item label="所属员工" prop="companyUserId">
-        <el-select v-model="queryParams.companyUserId" placeholder="请选择所属员工" clearable size="small">
+      <el-form-item label="所属导医" prop="companyUserId">
+        <el-select v-model="queryParams.companyUserId" placeholder="请选择所属导医" clearable filterable size="small">
           <el-option v-for="item in companyUserList" :key="item.userId" :label="item.nickName"
             :value="item.userId">
           </el-option>
         </el-select>
       </el-form-item>
       <el-form-item label="所属医生" prop="doctorId">
-        <el-select v-model="queryParams.doctorId" placeholder="请选择所属医生" clearable size="small">
+        <el-select v-model="queryParams.doctorId" placeholder="请选择所属医生" clearable filterable size="small">
           <el-option v-for="item in doctorList" :key="item.doctorId" :label="item.doctorName"
             :value="item.doctorId">
           </el-option>
         </el-select>
       </el-form-item>
-      <el-form-item label="绑定用户" prop="watchUserName">
+      <!-- <el-form-item label="绑定用户" prop="watchUserName">
         <el-input v-model="queryParams.watchUserName" placeholder="请输入绑定用户" clearable size="small"
           @keyup.enter.native="handleQuery" />
-      </el-form-item>
+      </el-form-item> -->
       <el-form-item label="设备编号" prop="deviceNumber">
         <el-input v-model="queryParams.deviceNumber" placeholder="请输入设备编号" clearable size="small"
           @keyup.enter.native="handleQuery" />
@@ -38,6 +38,13 @@
           </el-option>
         </el-select>
       </el-form-item>
+      
+      <el-form-item label="健康状态" prop="isNormal">
+        <el-select v-model="queryParams.isNormal" placeholder="请选择健康状态" clearable size="small">
+          <el-option v-for="item in healthOptions" :key="item.value" :label="item.label" :value="item.value">
+          </el-option>
+        </el-select>
+      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -50,35 +57,6 @@
           @click="handleExport" v-hasPermi="['watchApi:deviceInfo:export']">导出</el-button>
       </el-col>
 
-      <!-- <el-col :span="1.5">
-        <el-popover placement="right" width="400" trigger="click" @show="refreshPageAlarm">
-          <div>
-            <div class="clearfix" style="padding: 10px; border-bottom: 1px solid #ebeef5;">
-              <span>预警通知</span>
-              <el-button style="float: right; padding: 3px 0" type="text" @click="markAllAsRead"
-                v-if="alarmData && alarmData.length > 0">一键已读</el-button>
-            </div>
-            <div class="notification-container" v-infinite-scroll="load">
-              <el-card v-for="(notification, index) in alarmData" :key="index" class="notification-card">
-                <div v-if="notification.status === 0" class="red-dot"></div>
-                <el-row @click="goDetailPage(notification)">
-                  <el-col :span="20" :style="{ opacity: notification.status == 1 ? 0.6 : 1 }">
-                    <div class="notification-title">{{ notification.title }}</div>
-                    <div class="notification-desc">{{ notification.description }}</div>
-                    <div class="notification-time">{{ notification.dateTime }}</div>
-                  </el-col>
-                  <el-col :span="4" class="notification-status">
-                    <el-button style="float: right; padding: 3px 0" type="text" size="mini"
-                      @click="readItem(notification)">阅读</el-button>
-                  </el-col>
-                </el-row>
-              </el-card>
-
-            </div>
-          </div>
-          <el-button type="info" plain icon="el-icon-bell" size="mini" slot="reference">预警通知</el-button>
-        </el-popover>
-      </el-col> -->
 
       <el-col :span="1.5">
         <el-button plain icon="el-icon-setting" size="mini" @click="showSettingBtn = !showSettingBtn"
@@ -112,10 +90,30 @@
     <el-table v-loading="loading" :data="deviceInfoList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="设备id" align="center" prop="deviceId" />
-      <el-table-column label="所属公司" align="center" prop="companyName" />
-      <el-table-column label="所属员工" align="center" prop="companyUserName" />
+      <el-table-column label="所属技术主体" align="center" prop="companyName" />
+      <el-table-column label="所属导医" align="center" prop="companyUserName" />
       <el-table-column label="所属医生" align="center" prop="doctorName" />
-      <el-table-column label="绑定用户" align="center" prop="watchUserName" />
+      <el-table-column  label="绑定用户"  align="center" prop="watchUserName">
+        <!-- <template slot-scope="scope"> 
+          <div v-if="scope.row.users && scope.row.users!=null">
+              <el-link :underline="false" @click="handleDetails(scope.row.deviceId, scope.row.users.userId)">
+                {{ scope.row.users.nickName }}
+              </el-link>
+          </div>
+        </template> -->
+        
+      </el-table-column>
+      <el-table-column  label="绑定家人用户"  align="center" prop="familyUserName">
+          <!-- <template slot-scope="scope">
+            <div v-if="scope.row.familyUsers">
+              <div v-for="(item, index) in scope.row.familyUsers" :key="index">
+                <el-link :underline="false" @click="handleDetails(scope.row.deviceId, item.userId)">
+                  {{ item.nickName }}
+                </el-link>
+              </div>
+            </div>
+          </template> -->
+      </el-table-column>
       <el-table-column label="设备编号" align="center" prop="deviceNumber" width="150"/>
       <el-table-column label="电量" align="center" prop="battery" />
       <el-table-column label="信号" align="center" prop="rssi" />
@@ -130,13 +128,13 @@
           >
             最新数据
           </span>
-          <el-tag size="mini" :type="scope.row.isNormal == '异常'?'danger':'primary'">{{scope.row.isNormal}}</el-tag>
+          <el-tag size="mini" :type="scope.row.isNormal == 0?'danger':'primary'">{{ scope.row.isNormal === 1 ? '正常' : '异常' }}</el-tag>
 
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
-          <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetails(scope.row.deviceId)"
+          <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetailsAndType(scope.row)"
             v-hasPermi="['watchApi:deviceInfo:query']">查询详情</el-button>
           <el-button size="mini" type="text" icon="el-icon-phone-outline" @click="handleSoSPeople(scope.row.deviceNumber)"
             v-hasPermi="['watchApi:deviceInfo:query']">查看紧急联系人</el-button>
@@ -298,6 +296,13 @@ export default {
           { pattern: /^\d{15}$/, message: "设备编号必须是15位数字", trigger: "blur" }
         ],
       },
+      healthOptions: [{
+          value: 0,
+          label: '异常'
+        }, {
+          value: 1,
+          label: '正常'
+        }],
       options: [{
         value: '0',
         label: 'OFFLINE'
@@ -497,8 +502,7 @@ export default {
   },
   methods: {
     rowClassName({row}) {
-      if (row.status && row.status.includes('偏')) {
-        // console.log("=============================" + JSON.stringify(row.status, null, 2))
+      if (row.status && (!row.status.includes('正常') && !row.status.includes('得分') && !row.status.includes('无结果') && !row.status.includes('窦性心率'))) {
         return 'text-red';
         // return true
       }
@@ -604,9 +608,18 @@ export default {
 
     //   }
     // },
+    handleDetailsAndType(row){
+      if(row.userId != null){
+        this.handleDetails(row.deviceId,row.userId,0)
+      } else if(row.familyUsers !=null && row.familyUsers.length >0){
+        this.handleDetails(row.deviceId,row.familyUsers[0].userId,1)
+      } else {
+        this.handleDetails(row.deviceId,null,0);
+      }
+    },
     /** 处理查询详情按钮点击事件 */
-    handleDetails(deviceId) {
-      this.$router.push({ name: 'details', params: { deviceId } });
+    handleDetails(deviceId,userId,type) {
+      this.$router.push({ name: 'details', params: { deviceId:deviceId,userId:userId,selectType:type } });
     },
 
     /** 查询设备信息列表 */

+ 4 - 59
src/views/watch/isSend/index.vue

@@ -1,31 +1,6 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="所属公司" prop="companyId" v-if="shouldShowCompanySelect">
-        <el-select v-model="queryParams.companyId" placeholder="请选择所属公司" clearable size="small" @change="getCompanyUserList">
-          <el-option v-for="item in companyList" :key="item.companyId" :label="item.companyName"
-            :value="item.companyId">
-          </el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="所属员工" prop="companyUserId">
-        <el-select v-model="queryParams.companyUserId" placeholder="请选择所属员工" clearable size="small">
-          <el-option v-for="item in companyUserList" :key="item.userId" :label="item.nickName"
-            :value="item.userId">
-          </el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="所属医生" prop="doctorId">
-        <el-select v-model="queryParams.doctorId" placeholder="请选择所属医生" clearable size="small">
-          <el-option v-for="item in doctorList" :key="item.doctorId" :label="item.doctorName"
-            :value="item.doctorId">
-          </el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="绑定用户" prop="watchUserName">
-        <el-input v-model="queryParams.watchUserName" placeholder="请输入绑定用户" clearable size="small"
-          @keyup.enter.native="handleQuery" />
-      </el-form-item>
       <el-form-item label="设备编号" prop="deviceNumber">
         <el-input v-model="queryParams.deviceNumber" placeholder="请输入设备编号" clearable size="small"
           @keyup.enter.native="handleQuery" />
@@ -50,35 +25,6 @@
           @click="handleExport" v-hasPermi="['watchApi:deviceInfo:export']">导出</el-button>
       </el-col>
 
-      <!-- <el-col :span="1.5">
-        <el-popover placement="right" width="400" trigger="click" @show="refreshPageAlarm">
-          <div>
-            <div class="clearfix" style="padding: 10px; border-bottom: 1px solid #ebeef5;">
-              <span>预警通知</span>
-              <el-button style="float: right; padding: 3px 0" type="text" @click="markAllAsRead"
-                v-if="alarmData && alarmData.length > 0">一键已读</el-button>
-            </div>
-            <div class="notification-container" v-infinite-scroll="load">
-              <el-card v-for="(notification, index) in alarmData" :key="index" class="notification-card">
-                <div v-if="notification.status === 0" class="red-dot"></div>
-                <el-row @click="goDetailPage(notification)">
-                  <el-col :span="20" :style="{ opacity: notification.status == 1 ? 0.6 : 1 }">
-                    <div class="notification-title">{{ notification.title }}</div>
-                    <div class="notification-desc">{{ notification.description }}</div>
-                    <div class="notification-time">{{ notification.dateTime }}</div>
-                  </el-col>
-                  <el-col :span="4" class="notification-status">
-                    <el-button style="float: right; padding: 3px 0" type="text" size="mini"
-                      @click="readItem(notification)">阅读</el-button>
-                  </el-col>
-                </el-row>
-              </el-card>
-
-            </div>
-          </div>
-          <el-button type="info" plain icon="el-icon-bell" size="mini" slot="reference">预警通知</el-button>
-        </el-popover>
-      </el-col> -->
 
       <el-col :span="1.5">
         <el-button plain icon="el-icon-setting" size="mini" @click="showSettingBtn = !showSettingBtn"
@@ -99,10 +45,10 @@
     <el-table v-loading="loading" :data="deviceInfoList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="设备id" align="center" prop="deviceId" />
-      <el-table-column label="所属公司" align="center" prop="companyName" />
+      <!-- <el-table-column label="所属公司" align="center" prop="companyName" />
       <el-table-column label="所属员工" align="center" prop="companyUserName" />
       <el-table-column label="所属医生" align="center" prop="doctorName" />
-      <el-table-column label="绑定用户" align="center" prop="watchUserName" />
+      <el-table-column label="绑定用户" align="center" prop="watchUserName" /> -->
       <el-table-column label="设备编号" align="center" prop="deviceNumber" width="150"/>
       <el-table-column label="电量" align="center" prop="battery" />
       <el-table-column label="信号" align="center" prop="rssi" />
@@ -117,7 +63,7 @@
           >
             最新数据
           </span>
-          <el-tag size="mini" :type="scope.row.isNormal == '异常'?'danger':'primary'">{{scope.row.isNormal}}</el-tag>
+          <el-tag size="mini" :type="scope.row.isNormal == 0?'danger':'primary'">{{ scope.row.isNormal === 1 ? '正常' : '异常' }}</el-tag>
 
         </template>
       </el-table-column>
@@ -468,8 +414,7 @@ export default {
   },
   methods: {
     rowClassName({row}) {
-      if (row.status && row.status.includes('偏')) {
-        // console.log("=============================" + JSON.stringify(row.status, null, 2))
+      if (row.status && (!row.status.includes('正常') && !row.status.includes('得分') && !row.status.includes('无结果') && !row.status.includes('窦性心率'))) {
         return 'text-red';
         // return true
       }

+ 1 - 1
vue.config.js

@@ -34,7 +34,7 @@ module.exports = {
     proxy: {
       // 为 watch 模块单独设置的代理
       '/watch-api': {
-        target: 'http://localhost:8004', // 另一个目标服务器
+        target: 'http://localhost:8114', // 另一个目标服务器
         changeOrigin: true,
         pathRewrite: {
           '^/watch-api': '' // 将 /watch-api 替换为空