Browse Source

Merge remote-tracking branch 'origin/master' into master

肖路 3 tuần trước cách đây
mục cha
commit
de47ee41c9
71 tập tin đã thay đổi với 3076 bổ sung1226 xóa
  1. 1 1
      index.html
  2. 89 0
      src/api/home/index-enterprise.ts
  3. 11 0
      src/api/pc/enterprise/index.ts
  4. 16 0
      src/api/pc/organization/index.ts
  5. 51 0
      src/api/pc/valueAdded/index.ts
  6. BIN
      src/assets/jd/icons/icon_1.png
  7. 1 0
      src/assets/jd/icons/icon_1.svg
  8. BIN
      src/assets/jd/icons/icon_10.png
  9. 1 0
      src/assets/jd/icons/icon_10.svg
  10. BIN
      src/assets/jd/icons/icon_11.png
  11. 1 0
      src/assets/jd/icons/icon_11.svg
  12. BIN
      src/assets/jd/icons/icon_12.png
  13. 1 0
      src/assets/jd/icons/icon_12.svg
  14. BIN
      src/assets/jd/icons/icon_13.png
  15. 1 0
      src/assets/jd/icons/icon_13.svg
  16. BIN
      src/assets/jd/icons/icon_14.png
  17. 1 0
      src/assets/jd/icons/icon_14.svg
  18. BIN
      src/assets/jd/icons/icon_15.png
  19. 1 0
      src/assets/jd/icons/icon_15.svg
  20. BIN
      src/assets/jd/icons/icon_2.png
  21. 1 0
      src/assets/jd/icons/icon_2.svg
  22. BIN
      src/assets/jd/icons/icon_3.png
  23. 1 0
      src/assets/jd/icons/icon_3.svg
  24. BIN
      src/assets/jd/icons/icon_4.png
  25. 1 0
      src/assets/jd/icons/icon_4.svg
  26. BIN
      src/assets/jd/icons/icon_5.png
  27. 1 0
      src/assets/jd/icons/icon_5.svg
  28. BIN
      src/assets/jd/icons/icon_6.png
  29. 1 0
      src/assets/jd/icons/icon_6.svg
  30. BIN
      src/assets/jd/icons/icon_7.png
  31. 1 0
      src/assets/jd/icons/icon_7.svg
  32. BIN
      src/assets/jd/icons/icon_8.png
  33. 1 0
      src/assets/jd/icons/icon_8.svg
  34. BIN
      src/assets/jd/icons/icon_9.png
  35. 1 0
      src/assets/jd/icons/icon_9.svg
  36. 23 23
      src/assets/styles/index.scss
  37. 51 5
      src/layout/components/foot.vue
  38. 1 1
      src/router/index.ts
  39. 31 22
      src/utils/request.ts
  40. 10 1
      src/views/breg/index.vue
  41. 46 17
      src/views/enterprise/changePerson/index.vue
  42. 17 15
      src/views/enterprise/companyInfo/edit.vue
  43. 14 7
      src/views/enterprise/companyInfo/index.vue
  44. 5 5
      src/views/enterprise/purchasePlan/index.vue
  45. 98 20
      src/views/enterprise/securitySetting/changePhone.vue
  46. 11 0
      src/views/enterprise/securitySetting/index.vue
  47. 17 6
      src/views/enterprise/securitySetting/resetPassword.vue
  48. 7 5
      src/views/greg/index.vue
  49. 452 157
      src/views/home/index-enterprise.vue
  50. 0 1
      src/views/home/index.vue
  51. 297 153
      src/views/home/jdcomponents/JDBannerCards.vue
  52. 149 117
      src/views/home/jdcomponents/JDCategory.vue
  53. 29 32
      src/views/home/jdcomponents/JDFooter.vue
  54. 57 18
      src/views/home/jdcomponents/JDHeader.vue
  55. 414 116
      src/views/home/jdcomponents/JDProducts.vue
  56. 121 40
      src/views/home/jdcomponents/JDScene.vue
  57. 160 161
      src/views/home/jdcomponents/JDUserPanel.vue
  58. 84 50
      src/views/home/pccomponents/pages/floor.vue
  59. 0 1
      src/views/index.vue
  60. 9 2
      src/views/item/index.vue
  61. 73 67
      src/views/login.vue
  62. 93 27
      src/views/order/afterSale/index.vue
  63. 6 2
      src/views/order/orderAudit/index.vue
  64. 100 23
      src/views/reconciliation/billManage/index.vue
  65. 78 32
      src/views/reconciliation/invoiceManage/index.vue
  66. 3 4
      src/views/reg/index.vue
  67. 10 0
      src/views/search/index.vue
  68. 10 0
      src/views/search/special.vue
  69. 126 27
      src/views/valueAdded/complaint/index.vue
  70. 196 65
      src/views/valueAdded/maintenance/apply.vue
  71. 95 3
      src/views/valueAdded/maintenance/index.vue

+ 1 - 1
index.html

@@ -5,7 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
     <meta name="renderer" content="webkit" />
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
-    <link rel="icon" href="/favicon.ico" />
+    <link rel="icon" href="/src/assets/images/logo2.png" />
     <title>%VITE_APP_TITLE%</title>
     <!--[if lt IE 11
       ]><script>

+ 89 - 0
src/api/home/index-enterprise.ts

@@ -45,3 +45,92 @@ export const customerMessageDetail = (id: any) => {
     method: 'get'
   });
 };
+
+//推荐设置
+
+export const recommendThemeConfig = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/recommendThemeConfig',
+    method: 'get',
+    params: query
+  });
+};
+
+//头部分类
+export const headerCategoryList = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/headerCategoryList',
+    method: 'get',
+    params: query
+  });
+};
+
+//获取当前左侧广告设置
+
+export const getCurrentAdLeft = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/getCurrentAdLeft',
+    method: 'get',
+    params: query
+  });
+};
+
+//获取当前搜索页面设置
+
+export const currentSearchConfig = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/currentSearchConfig',
+    method: 'get',
+    params: query
+  });
+};
+
+//场景方案设置
+
+export const currentScenarioGlobalSetting = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/currentScenarioGlobalSetting',
+    method: 'get',
+    params: query
+  });
+};
+
+//快捷入口模块设置
+
+export const currentQuickEntryModule = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/currentQuickEntryModule',
+    method: 'get',
+    params: query
+  });
+};
+
+//分类设置
+
+export const categoryMainList = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/categoryMainList',
+    method: 'get',
+    params: query
+  });
+};
+
+//轮播图设置
+
+export const carouselList = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/carouselList',
+    method: 'get',
+    params: query
+  });
+};
+
+//广告模块
+
+export const adModuleConfigList = (query: any) => {
+  return request({
+    url: '/mall/pcEnterprisePurchase/adModuleConfigList',
+    method: 'get',
+    params: query
+  });
+};

+ 11 - 0
src/api/pc/enterprise/index.ts

@@ -57,6 +57,17 @@ export function changePhonenumber(data: any) {
   });
 }
 
+/**
+ * 获取验证码
+ */
+export function validateCode(data: any) {
+  return request({
+    url: '/customer/pcCustomer/validateCode', //更换手机号
+    method: 'put',
+    data: data
+  });
+}
+
 // ==================== 收货地址管理 ====================
 
 /**

+ 16 - 0
src/api/pc/organization/index.ts

@@ -130,6 +130,22 @@ export function deleteContact(ids: number[]) {
   });
 }
 
+/**
+ * 修改主联系人
+ * @param id
+ */
+export function changeIsPrimary(id: string | number, isPrimary: string) {
+  const data = {
+    id,
+    isPrimary
+  };
+  return request({
+    url: '/customer/organization/contact/changeIsPrimary',
+    method: 'put',
+    data: data
+  });
+}
+
 // ==================== 角色管理 ====================
 
 /**

+ 51 - 0
src/api/pc/valueAdded/index.ts

@@ -42,3 +42,54 @@ export function deleteComplaintsSuggestions(ids: number[]) {
     method: 'delete'
   });
 }
+
+/**
+ * 查询维保列表
+ */
+export function getMaintainInfoList(params?: any) {
+  return request({
+    url: '/customer/pcMaintainInfo/list',
+    method: 'get',
+    params: params
+  });
+}
+
+/**
+ * 查询维保详情
+ */
+export function getMaintainInfo(id: number) {
+  return request({
+    url: `/customer/pcMaintainInfo/${id}`,
+    method: 'get'
+  });
+}
+
+/**
+ * 新增维保
+ */
+export function addMaintainInfo(data: any) {
+  return request({
+    url: '/customer/pcMaintainInfo',
+    method: 'post',
+    data: data
+  });
+}
+
+/**
+ * 修改维保
+ */
+export function updateMaintainInfo(data: any) {
+  return request({
+    url: '/customer/pcMaintainInfo',
+    method: 'put',
+    data: data
+  });
+}
+
+export const listServerItem = (query) => {
+  return request({
+    url: '/customer/serverItem/list',
+    method: 'get',
+    params: query
+  });
+};

BIN
src/assets/jd/icons/icon_1.png


+ 1 - 0
src/assets/jd/icons/icon_1.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z" fill="#007AFF" /></svg>

BIN
src/assets/jd/icons/icon_10.png


+ 1 - 0
src/assets/jd/icons/icon_10.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z" fill="#34C759" /></svg>

BIN
src/assets/jd/icons/icon_11.png


+ 1 - 0
src/assets/jd/icons/icon_11.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z" fill="#007AFF" /></svg>

BIN
src/assets/jd/icons/icon_12.png


+ 1 - 0
src/assets/jd/icons/icon_12.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z" fill="#FF9900" /></svg>

BIN
src/assets/jd/icons/icon_13.png


+ 1 - 0
src/assets/jd/icons/icon_13.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z" fill="#E1251B" /></svg>

BIN
src/assets/jd/icons/icon_14.png


+ 1 - 0
src/assets/jd/icons/icon_14.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" fill="#5E5CE6" /></svg>

BIN
src/assets/jd/icons/icon_15.png


+ 1 - 0
src/assets/jd/icons/icon_15.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8 8h-2v3h2v-3zm-7.24-1.26L2.97 19.03l1.41 1.41 1.79-1.79-1.41-1.41zM12 5.5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z" fill="#FF3B30" /></svg>

BIN
src/assets/jd/icons/icon_2.png


+ 1 - 0
src/assets/jd/icons/icon_2.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z" fill="#FF9900" /></svg>

BIN
src/assets/jd/icons/icon_3.png


+ 1 - 0
src/assets/jd/icons/icon_3.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z" fill="#E1251B" /></svg>

BIN
src/assets/jd/icons/icon_4.png


+ 1 - 0
src/assets/jd/icons/icon_4.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z" fill="#5E5CE6" /></svg>

BIN
src/assets/jd/icons/icon_5.png


+ 1 - 0
src/assets/jd/icons/icon_5.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z" fill="#FF3B30" /></svg>

BIN
src/assets/jd/icons/icon_6.png


+ 1 - 0
src/assets/jd/icons/icon_6.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" fill="#34C759" /></svg>

BIN
src/assets/jd/icons/icon_7.png


+ 1 - 0
src/assets/jd/icons/icon_7.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="#FFCC00" /></svg>

BIN
src/assets/jd/icons/icon_8.png


+ 1 - 0
src/assets/jd/icons/icon_8.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2l-5.5 9h11L12 2zm0 3.84L13.93 9h-3.87L12 5.84zM17.5 13c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5 4.5-2.01 4.5-4.5-2.01-4.5-4.5-4.5zm0 7c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5zM3 21.5h8v-8H3v8zm2-6h4v4H5v-4z" fill="#E1251B" /></svg>

BIN
src/assets/jd/icons/icon_9.png


+ 1 - 0
src/assets/jd/icons/icon_9.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" fill="#FF3B30" /></svg>

+ 23 - 23
src/assets/styles/index.scss

@@ -113,29 +113,29 @@ div:focus {
   }
 }
 
-aside {
-  background: #eef1f6;
-  padding: 8px 24px;
-  margin-bottom: 20px;
-  border-radius: 2px;
-  display: block;
-  line-height: 32px;
-  font-size: 16px;
-  font-family:
-    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-  color: #2c3e50;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-
-  a {
-    color: #337ab7;
-    cursor: pointer;
-
-    &:hover {
-      color: rgb(32, 160, 255);
-    }
-  }
-}
+// aside {
+//   background: #eef1f6;
+//   padding: 8px 24px;
+//   margin-bottom: 20px;
+//   border-radius: 2px;
+//   display: block;
+//   line-height: 32px;
+//   font-size: 16px;
+//   font-family:
+//     -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
+//   color: #2c3e50;
+//   -webkit-font-smoothing: antialiased;
+//   -moz-osx-font-smoothing: grayscale;
+
+//   a {
+//     color: #337ab7;
+//     cursor: pointer;
+
+//     &:hover {
+//       color: rgb(32, 160, 255);
+//     }
+//   }
+// }
 
 //main-container全局样式
 .app-container {

+ 51 - 5
src/layout/components/foot.vue

@@ -6,11 +6,11 @@
           <img class="head-img" src="@/assets/images/head.png" alt="" />
           <div class="flex-row-start">
             <div class="head-code flex-column-center">
-              <img src="@/assets/images/code.png" alt="" />
+              <img :src="wechatLink" alt="" />
               <div>关注公众号</div>
             </div>
             <div class="head-code flex-column-center" style="margin-left: 16px">
-              <img src="@/assets/images/code.png" alt="" />
+              <img :src="weiboLink" alt="" />
               <div>关注微博</div>
             </div>
           </div>
@@ -35,7 +35,7 @@
           </div>
           <div class="contact">
             <span>联系我们</span>
-            <span>400-111-0027</span>
+            <span>{{ servicePhone }}</span>
             <span>(周一至周五 8:30-17:30)</span>
           </div>
           <div class="logistics">
@@ -55,11 +55,57 @@
         </div>
       </div>
     </div>
-    <div class="foot-foot">ICP备案号: 哪ICP备06016915号-1增值电信业务经营许可证编号:鄂B2-20170182</div>
+    <div class="foot-foot">{{ icpNo }}增值电信业务经营许可证编号:{{ license }}</div>
   </div>
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { getPlatformConfigList } from '@/api/breg/index';
+const icpNo = ref<any>('');
+const license = ref<any>('');
+const wechatLink = ref<any>('');
+const weiboLink = ref<any>('');
+const servicePhone = ref<any>('');
+getPlatformConfigList({ configKey: 'icpNo' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      icpNo.value = res.rows[0].value;
+    }
+  }
+});
+
+getPlatformConfigList({ configKey: 'license' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      license.value = res.rows[0].value;
+    }
+  }
+});
+
+getPlatformConfigList({ configKey: 'wechatLink' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      wechatLink.value = res.rows[0].value;
+    }
+  }
+});
+
+getPlatformConfigList({ configKey: 'weiboLink' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      weiboLink.value = res.rows[0].value;
+    }
+  }
+});
+
+getPlatformConfigList({ configKey: 'servicePhone' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      servicePhone.value = res.rows[0].value;
+    }
+  }
+});
+</script>
 
 <style lang="scss" scoped>
 .foot {

+ 1 - 1
src/router/index.ts

@@ -120,7 +120,7 @@ export const constantRoutes: RouteRecordRaw[] = [
         path: '/indexEnterprise',
         component: () => import('@/views/home/index-enterprise.vue'),
         name: 'indexEnterprise',
-        meta: { title: '企业首页', search: 'hide' }
+        meta: { title: '企业首页', search: 'hide', header: 'hide', foot: 'hide' }
       },
       {
         path: '/theme',

+ 31 - 22
src/utils/request.ts

@@ -127,29 +127,38 @@ service.interceptors.response.use(
     if (code === 401) {
       if (!isRelogin.show) {
         isRelogin.show = true;
-        ElMessageBox.confirm('登录状态已过期,请重新登录', '登录提示', {
-          confirmButtonText: '重新登录',
-          type: 'warning'
-        })
-          .then(() => {
-            isRelogin.show = false;
-            useUserStore()
-              .logout()
-              .then(() => {
-                let url = `https://${import.meta.env.VITE_DOMAIN_PASSPORT}`;
+        let url = `https://${import.meta.env.VITE_DOMAIN_WWW}`;
+        if (import.meta.env.PROD) {
+          url = `${url}/index`;
+        } else {
+          const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
+          url = `${url}:${devPort}/index`;
+        }
+        window.location.href = url;
+        isRelogin.show = false;
+        // ElMessageBox.confirm('登录状态已过期,请重新登录', '登录提示', {
+        //   confirmButtonText: '重新登录',
+        //   type: 'warning'
+        // })
+        //   .then(() => {
+        //     isRelogin.show = false;
+        //     useUserStore()
+        //       .logout()
+        //       .then(() => {
+        //         let url = `https://${import.meta.env.VITE_DOMAIN_PASSPORT}`;
 
-                if (import.meta.env.PROD) {
-                  url = `${url}/login`;
-                } else {
-                  const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
-                  url = `${url}:${devPort}/login`;
-                }
-                window.location.href = url;
-              });
-          })
-          .catch(() => {
-            isRelogin.show = false;
-          });
+        //         if (import.meta.env.PROD) {
+        //           url = `${url}/login`;
+        //         } else {
+        //           const devPort = window.location.port || import.meta.env.VITE_APP_PORT;
+        //           url = `${url}:${devPort}/login`;
+        //         }
+        //         window.location.href = url;
+        //       });
+        //   })
+        //   .catch(() => {
+        //     isRelogin.show = false;
+        //   });
       }
       return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
     } else if (code === HttpStatus.SERVER_ERROR) {

+ 10 - 1
src/views/breg/index.vue

@@ -93,7 +93,7 @@
       <template v-if="nextNum == 4">
         <div class="register-success flex-column-center">
           <img src="@/assets/images/breg.png" alt="" />
-          <div class="success-text">您的账户还在审核中,如有疑问,请致电400-111-0027</div>
+          <div class="success-text">您的账户还在审核中,如有疑问,请致电{{ servicePhone }}</div>
           <el-button @click="goLogin" type="primary">返回登录</el-button>
         </div>
       </template>
@@ -150,6 +150,7 @@ const countdown = ref<number>(0);
 const timer = ref<any>(null);
 const enterprise = ref<any>({});
 const loading = ref(false);
+const servicePhone = ref<any>('');
 const form = ref<any>({
   purchaseName: '',
   purchasePhone: '',
@@ -169,6 +170,14 @@ getPlatformConfigList({ configKey: 'customerLoginAgreement' }).then((res) => {
   }
 });
 
+getPlatformConfigList({ configKey: 'servicePhone' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      servicePhone.value = res.rows[0].value;
+    }
+  }
+});
+
 // 启动倒计时
 const startCountdown = () => {
   countdown.value = 60;

+ 46 - 17
src/views/enterprise/changePerson/index.vue

@@ -43,9 +43,9 @@
         <el-form ref="step1FormRef" :model="step1Form" label-width="120px" class="verify-form">
           <el-form-item label="手机号码:">
             <div class="form-item-wrapper">
-              <el-input v-model="step1Form.phone" placeholder="请输入手机号码" class="form-input" />
-              <span class="form-tip">若该手机号已无法使用请联系客服</span>
+              <el-input v-model="step1Form.phone" placeholder="请输入手机号码" class="form-input" disabled />
             </div>
+            <span class="form-tip">若该手机号已无法使用请联系客服</span>
           </el-form-item>
 
           <el-form-item label="验证码:">
@@ -60,11 +60,11 @@
             </div>
           </el-form-item>
 
-          <el-form-item label=" ">
+          <!-- <el-form-item label=" ">
             <div class="captcha-box">
               <el-radio v-model="step1Form.verified" :label="true" class="captcha-radio">点击验证</el-radio>
             </div>
-          </el-form-item>
+          </el-form-item> -->
 
           <el-form-item label=" " class="action-item">
             <el-button type="primary" class="next-btn" @click="handleNextStep">下一步</el-button>
@@ -76,14 +76,13 @@
       <div class="step-content" v-if="currentStep === 2">
         <el-form label-width="120px" class="verify-form">
           <el-form-item label="选择新负责人:">
-            <el-select v-model="step2Form.person" placeholder="请选择新采购负责人" class="form-input">
-              <el-option label="张三" value="zhangsan" />
-              <el-option label="李四" value="lisi" />
+            <el-select v-model="step2Form.personId" placeholder="请选择新采购负责人" class="form-input">
+              <el-option v-for="item in contactList" :key="item.id" :label="`${item.contactName} , ${item.phone}`" :value="item.id" />
             </el-select>
           </el-form-item>
           <el-form-item label=" " class="action-item">
             <el-button @click="currentStep = 1">上一步</el-button>
-            <el-button type="primary" class="next-btn" @click="currentStep = 3" style="width: auto; padding: 0 40px">提交</el-button>
+            <el-button type="primary" class="next-btn" @click="changeContactBtn" style="width: auto; padding: 0 40px">提交</el-button>
           </el-form-item>
         </el-form>
       </div>
@@ -103,23 +102,32 @@
 <script setup lang="ts">
 import { ref, reactive } from 'vue';
 import { Check, CircleCheckFilled } from '@element-plus/icons-vue';
+import { getCurrentUserInfo } from '@/api/pc/organization/index';
 import { ElMessage } from 'element-plus';
 import { smsCode } from '@/api/breg/index';
-
+import { getContactList, changeIsPrimary } from '@/api/pc/organization/index';
+import { useRoute } from 'vue-router';
 const currentStep = ref(1);
+const route = useRoute();
 
 const step1Form = reactive({
-  phone: '',
+  phone: route.query.phone as any,
   code: '',
   verified: false
 });
 const timer = ref<any>(null);
 const step2Form = reactive({
-  person: ''
+  personId: ''
 });
+const contactList = ref([]);
 
 const countdown = ref(0);
 
+const getCurrentUser = async () => {
+  const res = await getCurrentUserInfo();
+  step1Form.phone = res.data.phone;
+};
+
 // 发送验证码
 const handleSendCode = () => {
   if (countdown.value > 0) return;
@@ -150,17 +158,39 @@ const startCountdown = () => {
     countdown.value--;
   }, 1000);
 };
+
+const loadContactList = async () => {
+  try {
+    const res = await getContactList();
+    contactList.value = res.rows || [];
+  } catch (e) {
+    ElMessage.error('加载联系人列表失败');
+  } finally {
+  }
+};
+
+const changeContactBtn = async () => {
+  try {
+    await changeIsPrimary(step2Form.personId, '0');
+    ElMessage.success('操作成功');
+    currentStep.value = 1;
+  } catch (err) {}
+};
 const handleNextStep = () => {
   if (!step1Form.phone || !step1Form.code) {
     ElMessage.warning('请填写完整的手机号和验证码');
     return;
   }
-  if (!step1Form.verified) {
-    ElMessage.warning('请完成点击验证');
-    return;
-  }
+  // if (!step1Form.verified) {
+  //   ElMessage.warning('请完成点击验证');
+  //   return;
+  // }
   currentStep.value = 2;
 };
+onMounted(() => {
+  loadContactList();
+  getCurrentUser();
+});
 </script>
 
 <style scoped lang="scss">
@@ -279,7 +309,6 @@ const handleNextStep = () => {
       :deep(.el-input__wrapper) {
         box-shadow: 0 0 0 1px #dcdfe6 inset;
         border-radius: 2px;
-        background-color: #f7f9fb;
         &.is-focus {
           box-shadow: 0 0 0 1px #e60012 inset !important;
         }
@@ -303,7 +332,7 @@ const handleNextStep = () => {
     font-weight: 500;
 
     &:hover {
-      color: #159c81;
+      color: #e60012;
     }
     &.is-disabled {
       color: #999;

+ 17 - 15
src/views/enterprise/companyInfo/edit.vue

@@ -93,24 +93,24 @@
           </el-row>
           <el-row :gutter="20">
             <el-col :span="12">
-              <el-form-item label="联系方式" prop="phone">
-                <el-input v-model="form.phone" placeholder="请输入" />
+              <el-form-item label="联系方式" prop="landline">
+                <el-input v-model="form.landline" placeholder="请输入" />
               </el-form-item>
             </el-col>
             <el-col :span="12">
-              <el-form-item label="邮箱" prop="email">
-                <el-input v-model="form.email" placeholder="请输入" />
+              <el-form-item label="邮箱" prop="fax">
+                <el-input v-model="form.fax" placeholder="请输入" />
               </el-form-item>
             </el-col>
           </el-row>
           <el-row :gutter="20">
             <el-col :span="24">
-              <el-form-item label="网址" prop="website">
-                <el-input v-model="form.website" placeholder="请输入" maxlength="50" show-word-limit />
+              <el-form-item label="网址" prop="url">
+                <el-input v-model="form.url" placeholder="请输入" maxlength="50" show-word-limit />
               </el-form-item>
             </el-col>
           </el-row>
-          <el-row :gutter="20">
+          <!-- <el-row :gutter="20">
             <el-col :span="12">
               <el-form-item label="营业执照" prop="businessLicense">
                 <el-upload
@@ -135,6 +135,8 @@
                 <el-upload
                   class="upload-box"
                   :action="action"
+                  :multiple="true"
+                  :limit="2"
                   :show-file-list="false"
                   :on-success="handleLegalPersonCardUrlSuccess"
                   :before-upload="beforeAvatarUpload"
@@ -149,7 +151,7 @@
                 </el-upload>
               </el-form-item>
             </el-col>
-          </el-row>
+          </el-row> -->
         </div>
       </el-form>
 
@@ -189,9 +191,9 @@ const form = reactive({
   address: '',
   industryCategoryId: '',
   enterpriseScaleId: '',
-  phone: '',
-  email: '',
-  website: '',
+  landline: '',
+  fax: '',
+  url: '',
   businessLicense: '',
   legalPersonCardUrl: ''
 });
@@ -279,7 +281,7 @@ const loadEnterpriseInfo = async () => {
       const businessInfo = data.customerBusinessInfoVo || {};
 
       form.companyName = data.customerName || data.businessCustomerName || '';
-      form.creditCode = businessInfo.socialCreditCode || '';
+      form.creditCode = data.socialCreditCode || '';
       form.legalPerson = businessInfo.legalPersonName || '';
       form.registeredCapital = businessInfo.registeredCapital || '';
       form.detailAddress = businessInfo.businessAddress || data.address || '';
@@ -289,9 +291,9 @@ const loadEnterpriseInfo = async () => {
       form.industryCategoryId = data.industryCategoryId || '';
       form.enterpriseScaleId = data.enterpriseScaleId || '';
       form.regionCodes = [data.regProvincialNo, data.regCityNo, data.regCountyNo];
-      form.phone = data.landline || '';
-      form.email = businessInfo.email || data.fax || '';
-      form.website = data.url || '';
+      form.landline = data.landline || '';
+      form.fax = data.fax || '';
+      form.url = data.url || '';
       form.businessLicense = businessInfo.businessLicense || '';
       form.legalPersonCardUrl = businessInfo.legalPersonCardUrl || '';
 

+ 14 - 7
src/views/enterprise/companyInfo/index.vue

@@ -101,7 +101,10 @@
     <div class="service-section">
       <div class="section-title"><i class="title-bar"></i>专属服务人员</div>
       <div class="service-list">
-        <div v-for="(person, index) in servicePersons" :key="index" class="service-card">
+        <span> 销售人员:{{ companyData.salesPerson }}</span
+        ><br />
+        <span> 客服支持:{{ companyData.serviceStaff }}</span>
+        <!-- <div v-for="(person, index) in servicePersons" :key="index" class="service-card">
           <el-avatar :size="50" :src="person.avatar">
             <el-icon :size="30"><User /></el-icon>
           </el-avatar>
@@ -112,9 +115,9 @@
             </div>
             <div class="person-contact">{{ person.department }} {{ person.phone }}</div>
           </div>
-        </div>
-        <div class="flex-row-center w-[100%] empty-bos" v-if="servicePersons.length === 0">
-          <el-empty image-size="100" description="暂无" />
+        </div>-->
+        <div class="flex-row-center w-[100%] empty-bos" v-if="!companyData.salesPerson">
+          <el-empty :image-size="100" description="暂无" />
         </div>
       </div>
     </div>
@@ -167,7 +170,9 @@ const companyData = reactive({
   scale: '-',
   phone: '-',
   email: '-',
-  address: '-'
+  address: '-',
+  salesPerson: '-',
+  serviceStaff: '-'
 });
 
 // 加载企业信息
@@ -184,15 +189,17 @@ const loadEnterpriseInfo = async () => {
 
       companyData.companyName = formatValue(data.customerName || data.businessCustomerName);
       companyData.companyCode = formatValue(data.customerNo);
-      companyData.creditCode = formatValue(businessInfo.socialCreditCode);
+      companyData.creditCode = formatValue(data.socialCreditCode);
       companyData.industry = formatValue(data.industryCategory);
       companyData.scale = formatValue(data.enterpriseScale);
       companyData.phone = formatValue(data.landline);
-      companyData.email = formatValue(businessInfo.email || data.fax);
+      companyData.email = formatValue(data.fax);
       companyData.address = formatValue(data.address);
       companyData.availableAmount = (parseInt(salesInfo.remainingQuota) || 0).toFixed(2); // 剩余额度
       // companyData.deptCredit = data.deptCredit || '0.00';部门额度
       companyData.creditAmount = salesInfo.creditAmount || '0.00';
+      companyData.salesPerson = formatValue(data.salesPersonName);
+      companyData.serviceStaff = formatValue(data.serviceStaffName);
     }
   } catch (error) {
     console.error('加载企业信息失败:', error);

+ 5 - 5
src/views/enterprise/purchasePlan/index.vue

@@ -45,16 +45,16 @@ import { onPath } from '@/utils/siteConfig';
 
 // 采购方案类型映射
 const typeMap: Record<string, string> = {
-  purchase: '1',      // 采购方案
-  exclusive: '2',     // 专属采购方案
-  collection: '3'     // 收藏的采购方案
+  purchase: '1', // 采购方案
+  exclusive: '2' // 专属采购方案
+  // collection: '3' // 收藏的采购方案
 };
 
 const activeTab = ref('purchase');
 const tabs = [
   { key: 'purchase', label: '采购方案' },
-  { key: 'exclusive', label: '专属采购方案' },
-  { key: 'collection', label: '收藏的采购方案' }
+  { key: 'exclusive', label: '专属采购方案' }
+  // { key: 'collection', label: '收藏的采购方案' }
 ];
 const queryParams = reactive({ pageNum: 1, pageSize: 10 });
 const total = ref(0);

+ 98 - 20
src/views/enterprise/securitySetting/changePhone.vue

@@ -56,9 +56,9 @@
               {{ countdown > 0 ? `${countdown}s后重发` : '发送验证码' }}
             </el-button>
           </el-form-item>
-          <el-form-item class="verify-checkbox">
+          <!-- <el-form-item class="verify-checkbox">
             <el-checkbox v-model="step1Form.verified">点击验证</el-checkbox>
-          </el-form-item>
+          </el-form-item> -->
         </el-form>
 
         <div class="step-actions">
@@ -109,10 +109,13 @@
 
 <script setup lang="ts">
 import { ref, reactive } from 'vue';
+import { useUserStore } from '@/store/modules/user';
+import { onPath } from '@/utils/siteConfig';
 import { Check, CircleCheckFilled, Phone } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 import { useRoute } from 'vue-router';
-import { changePhonenumber } from '@/api/pc/enterprise/index';
+import { smsCode } from '@/api/breg/index';
+import { changePhonenumber, validateCode } from '@/api/pc/enterprise/index';
 const currentStep = ref(1);
 const route = useRoute();
 // 步骤1表单
@@ -123,7 +126,9 @@ const step1Form = reactive({
   verified: false
 });
 
-const countdown = ref(0);
+const codeText = ref<string>('发送验证码');
+const countdown = ref<number>(0);
+const timer = ref<any>(null);
 
 const step1Rules = {
   code: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
@@ -137,6 +142,8 @@ const step2Form = reactive({
 });
 
 const newCountdown = ref(0);
+const newCodeText = ref<string>('发送验证码');
+const newTimer = ref<any>(null);
 
 const step2Rules = {
   newPhone: [
@@ -146,45 +153,108 @@ const step2Rules = {
   newCode: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
 };
 
-// 发送验证码(旧手机)
+// 发送验证码
 const handleSendCode = () => {
+  if (countdown.value > 0) return;
+  if (step1Form.phone) {
+    smsCode({ phonenumber: step1Form.phone }).then((res: any) => {
+      if (res.code == 200) {
+        ElMessage({
+          message: '验证码已发送',
+          type: 'success'
+        });
+        startCountdown();
+      }
+    });
+  } else {
+    ElMessage({
+      message: '请输入正确的手机号码',
+      type: 'warning'
+    });
+    return;
+  }
+};
+
+// 启动倒计时
+const startCountdown = () => {
   countdown.value = 60;
-  const timer = setInterval(() => {
+  codeText.value = `${countdown.value}s 后重新发送`;
+  timer.value = setInterval(() => {
     countdown.value--;
-    if (countdown.value <= 0) {
-      clearInterval(timer);
+    if (countdown.value > 0) {
+      codeText.value = `${countdown.value}s 后重新发送`;
+    } else {
+      clearInterval(timer.value);
+      timer.value = null;
+      codeText.value = '发送验证码';
     }
   }, 1000);
-  ElMessage.success('验证码已发送');
 };
 
-// 发送验证码(新手机)
+// 发送验证码
 const handleSendNewCode = () => {
-  if (!step2Form.newPhone) {
-    ElMessage.warning('请先输入新手机号码');
+  if (newCountdown.value > 0) return;
+  if (step2Form.newPhone) {
+    smsCode({ phonenumber: step2Form.newPhone }).then((res: any) => {
+      if (res.code == 200) {
+        ElMessage({
+          message: '验证码已发送',
+          type: 'success'
+        });
+        startCountdownNew();
+      }
+    });
+  } else {
+    ElMessage({
+      message: '请输入正确的手机号码',
+      type: 'warning'
+    });
     return;
   }
+};
+
+// 启动倒计时
+const startCountdownNew = () => {
   newCountdown.value = 60;
-  const timer = setInterval(() => {
+  newCodeText.value = `${newCountdown.value}s 后重新发送`;
+  newTimer.value = setInterval(() => {
     newCountdown.value--;
-    if (newCountdown.value <= 0) {
-      clearInterval(timer);
+    if (newCountdown.value > 0) {
+      newCodeText.value = `${newCountdown.value}s 后重新发送`;
+    } else {
+      clearInterval(newTimer.value);
+      newTimer.value = null;
+      newCodeText.value = '发送验证码';
     }
   }, 1000);
-  ElMessage.success('验证码已发送');
 };
 
 // 下一步
 const handleNextStep = async () => {
   const valid = await step1FormRef.value?.validate().catch(() => false);
   if (!valid) return;
+  try {
+    const submitData = {
+      purchasePhone: step1Form.phone,
+      code: step1Form.code
+    };
 
-  if (!step1Form.verified) {
-    ElMessage.warning('请先点击验证');
+    const res: any = await validateCode(submitData);
+    if (res.code === 200) {
+      currentStep.value = 2;
+    } else {
+      ElMessage.error(res.msg || '验证码验证失败');
+      return;
+    }
+  } catch (error) {
+    console.error('验证码验证失败:', error);
     return;
   }
 
-  currentStep.value = 2;
+  // if (!step1Form.verified) {
+  //   ElMessage.warning('请先点击验证');
+  //   return;
+  // }
 };
 
 // 提交
@@ -209,6 +279,14 @@ const handleSubmit = async () => {
     console.error('手机号修改失败:', error);
   }
 };
+
+const onlogout = () => {
+  useUserStore()
+    .logout()
+    .then(() => {
+      onPath('/login');
+    });
+};
 </script>
 
 <style scoped lang="scss">
@@ -216,6 +294,7 @@ const handleSubmit = async () => {
   background: #f5f5f5;
   min-height: 100%;
   padding: 0;
+  width: 100%;
 }
 
 // 面包屑
@@ -247,7 +326,6 @@ const handleSubmit = async () => {
 
 // 主体内容
 .change-content {
-  max-width: 800px;
   margin: 30px auto;
   padding: 30px 40px;
   background: #fff;

+ 11 - 0
src/views/enterprise/securitySetting/index.vue

@@ -120,11 +120,13 @@
 
 <script setup lang="ts">
 import { ref, reactive } from 'vue';
+import { useUserStore } from '@/store/modules/user';
 import { useRouter } from 'vue-router';
 import { ArrowLeft, OfficeBuilding, Grid } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 import { changePwd, normalChangePwd } from '@/api/pc/enterprise/index';
 import { getCurrentUserInfo, updateComStaff } from '@/api/pc/organization/index';
+import { onPath } from '@/utils/siteConfig';
 
 const router = useRouter();
 
@@ -222,6 +224,7 @@ const handleSaveNormalPassword = async () => {
     const res: any = await normalChangePwd(submitData);
     if (res.code === 200) {
       ElMessage.success('密码修改成功');
+      onlogout();
     }
   } catch (error) {
     // console.error('修改密码失败:', error);
@@ -271,6 +274,14 @@ const handleSavePhone = async () => {
   ElMessage.success('手机号修改成功');
   phoneDialogVisible.value = false;
 };
+
+const onlogout = () => {
+  useUserStore()
+    .logout()
+    .then(() => {
+      onPath('/login');
+    });
+};
 onMounted(() => {
   getCurrentUser();
 });

+ 17 - 6
src/views/enterprise/securitySetting/resetPassword.vue

@@ -52,9 +52,9 @@
             <el-input v-model="step1Form.code" placeholder="短信验证码" class="form-input code-input" />
             <el-button @click="handleSendCode" :class="['code', countdown > 0 ? 'disabled' : '']"> {{ codeText }}</el-button>
           </el-form-item>
-          <el-form-item label="" class="verify-checkbox">
+          <!-- <el-form-item label="" class="verify-checkbox">
             <el-checkbox v-model="step1Form.verified">点击验证</el-checkbox>
-          </el-form-item>
+          </el-form-item> -->
         </el-form>
         <div class="step-actions">
           <el-button type="danger" class="next-btn" @click="handleNextStep">下一步</el-button>
@@ -97,6 +97,8 @@ import { ElMessage } from 'element-plus';
 import { useRoute } from 'vue-router';
 import { changePwd } from '@/api/pc/enterprise/index';
 import { smsCode } from '@/api/breg/index';
+import { useUserStore } from '@/store/modules/user';
+import { onPath } from '@/utils/siteConfig';
 const currentStep = ref(1);
 const route = useRoute();
 
@@ -186,10 +188,10 @@ const handleNextStep = async () => {
   const valid = await step1FormRef.value?.validate().catch(() => false);
   if (!valid) return;
 
-  if (!step1Form.verified) {
-    ElMessage.warning('请先点击验证');
-    return;
-  }
+  // if (!step1Form.verified) {
+  //   ElMessage.warning('请先点击验证');
+  //   return;
+  // }
 
   currentStep.value = 2;
 };
@@ -212,6 +214,7 @@ const handleSubmit = async () => {
     if (res.code === 200) {
       ElMessage.success('密码重置成功');
       currentStep.value = 3;
+      onlogout();
     } else {
       ElMessage.error(res.msg || '密码修改失败');
     }
@@ -219,6 +222,14 @@ const handleSubmit = async () => {
     console.error('修改密码失败:', error);
   }
 };
+
+const onlogout = () => {
+  useUserStore()
+    .logout()
+    .then(() => {
+      onPath('/login');
+    });
+};
 </script>
 
 <style scoped lang="scss">

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 7 - 5
src/views/greg/index.vue


+ 452 - 157
src/views/home/index-enterprise.vue

@@ -1,184 +1,479 @@
 <template>
-  <div class="home-pages">
-    <!--  头部 -->
-    <comHead v-if="headData.show" :row="headData" :datas="realList"></comHead>
-    <!-- 导航 -->
-    <div class="nav-bos">
-      <div v-for="(item, index) in navList" :key="index" class="nav-list" @click="onPath(item.url)">
-        <img class="nav-image" :src="item.img" alt="" />
-        <div class="flex-row-between">
-          <div>
-            <div class="name1">{{ item.name1 }}</div>
-            <div class="name2">{{ item.name2 }}</div>
+  <div class="jd-app">
+    <JDHeader />
+    <!-- 2. 核心 Banner 区域 -->
+    <section class="banner-area w">
+      <!-- 极简而纯粹的悬挂式广告大图,挂于 1600px 外部左侧 -->
+      <!-- 悬停时通过 CSS 自动扩宽并享有绝对最高层级 9999 -->
+      <div class="expand-ad-layer" @click="onPath(adLeft.link)">
+        <img :src="adLeft.imageUrl" alt="促销大图" class="expand-img-full" />
+
+        <!-- 作为大图一部分直接漂浮在上面的文案,不破坏图片的整体性 -->
+        <div class="ad-left-text">
+          <div class="deco-inner-white">
+            <span class="d-txt">企业专享低至</span>
+            <span class="d-num">5</span>
+            <span class="d-txt">折</span>
+          </div>
+          <div class="play-btn">
+            <div class="triangle"></div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 右侧主内容区域,霸占 1600px 全宽 -->
+      <div class="banner-layout flex">
+        <!-- 左侧大列 (导航 + 分类与广告) -->
+        <div class="main-column flex-1 flex-column">
+          <!-- 顶部的导航条 -->
+          <div class="nav-bar flex-between relative">
+            <!-- 小屏时固定的场景化类目 -->
+            <div class="scene-cat-toggle" v-if="!isLargeScreen" @mouseenter="showSceneCat = true" @mouseleave="showSceneCat = false">
+              <span>场景化类目 <i class="arr-down"></i></span>
+            </div>
+            <div class="divider" v-if="!isLargeScreen">|</div>
+
+            <!-- 导航滚动包装器 (按钮贴靠视口两端) -->
+            <div class="nav-scroll-wrapper flex-1 relative">
+              <!-- 左滑按钮 -->
+              <div class="scroll-btn-wrap prev" v-show="showPrev">
+                <div class="more-btn prev-btn" @click="slide(-300)">
+                  <svg
+                    width="12"
+                    height="12"
+                    viewBox="0 0 24 24"
+                    fill="none"
+                    stroke="currentColor"
+                    stroke-width="3"
+                    stroke-linecap="round"
+                    stroke-linejoin="round"
+                  >
+                    <polyline points="15 18 9 12 15 6"></polyline>
+                  </svg>
+                </div>
+              </div>
+
+              <!-- 导航滚动视口 -->
+              <ul class="nav-list flex" ref="navListRef" @scroll="checkScroll">
+                <li v-for="(item, index) in navItems" :key="index" :class="{ active: index === 0 }" @click="onPath(item.link)">
+                  <el-image class="n-ic-img" :src="item.icon" />
+                  {{ item.title }}
+                </li>
+              </ul>
+
+              <!-- 右滑按钮 -->
+              <div class="scroll-btn-wrap next" v-show="showNext">
+                <div class="more-btn next-btn" @click="slide(300)">
+                  <svg
+                    width="12"
+                    height="12"
+                    viewBox="0 0 24 24"
+                    fill="none"
+                    stroke="currentColor"
+                    stroke-width="3"
+                    stroke-linecap="round"
+                    stroke-linejoin="round"
+                  >
+                    <polyline points="9 18 15 12 9 6"></polyline>
+                  </svg>
+                </div>
+              </div>
+            </div>
           </div>
-          <div class="nav-more flex-row-center">
-            <el-icon><ArrowRight /></el-icon>
+          <!-- 下方:分类与广告栏并排 -->
+          <div class="banner-bottom flex flex-1">
+            <!-- 左侧分类菜单 (大屏常驻,小屏悬停显示) -->
+            <aside
+              class="side-category"
+              v-show="isLargeScreen || showSceneCat"
+              :class="{ 'is-dropdown': !isLargeScreen }"
+              @mouseenter="showSceneCat = true"
+              @mouseleave="showSceneCat = false"
+            >
+              <JDCategory />
+            </aside>
+
+            <!-- 右侧广告组合 -->
+            <div class="ad-group flex-1">
+              <JDBannerCards />
+            </div>
           </div>
         </div>
+        <!-- 右侧大列:用户面板 (撑满高度) -->
+        <div class="user-col">
+          <JDUserPanel class="user-col" />
+        </div>
       </div>
-    </div>
-    <!-- 猜你喜欢 -->
-    <indexMorTitle :datas="{ title1: '猜你喜欢' }"></indexMorTitle>
-    <comGoods :row="{ styleType: 1, goodsShow: [1, 2, 3], goodsTitleType: 3 }" :datas="recommendList"></comGoods>
+    </section>
+
+    <!-- 3. 下方模块 -->
+    <JDScene />
+    <JDProducts />
+
+    <!-- 4. 页脚 -->
+    <!-- <JDFooter /> -->
   </div>
 </template>
 
 <script setup lang="ts">
-import indexMorTitle from '@/views/home/pccomponents/pages/indexMorTitle.vue';
-import comHead from '@/views/home/pccomponents/pages/headData.vue';
-import comGoods from '@/views/home/pccomponents/pages/goods.vue';
+import JDHeader from '@/views/home/jdcomponents/JDHeader.vue';
+import JDCategory from '@/views/home/jdcomponents/JDCategory.vue';
+import JDBannerCards from '@/views/home/jdcomponents/JDBannerCards.vue';
+import JDUserPanel from '@/views/home/jdcomponents/JDUserPanel.vue';
+import JDScene from '@/views/home/jdcomponents/JDScene.vue';
+import JDProducts from '@/views/home/jdcomponents/JDProducts.vue';
+import JDFooter from '@/views/home/jdcomponents/JDFooter.vue';
+import '@/views/home/jdcomponents/jd-repro.css';
+import { headerCategoryList, getCurrentAdLeft } from '@/api/home/index-enterprise';
 import { onPath } from '@/utils/siteConfig';
-import { getSearchTitle, getHomeNav } from '@/api/home/index';
-import {
-  getEnterpriseRightsMemberEquityList,
-  getEnterpriseHomeAdList,
-  getCustomerProductPage,
-  customerMessageList
-} from '@/api/home/index-enterprise';
-import enterprise1 from '@/assets/images/home/enterprise1.png';
-import enterprise2 from '@/assets/images/home/enterprise2.png';
-import enterprise3 from '@/assets/images/home/enterprise3.png';
-import enterprise4 from '@/assets/images/home/enterprise4.png';
-
-const headData = ref<any>({});
-const realList = ref<any>([]);
-const recommendList = ref<any>([]);
-//导航
-const navList = ref<any>([
-  { name1: '采购日历', name2: '提前制定和管理月度采购计划', url: '/enterprise/purchaseHistory', img: enterprise1 },
-  { name1: '自助开票', name2: '灵活自助开票 换开管票服务', url: '/enterprise/invoiceManage', img: enterprise2 },
-  { name1: '下载中心', name2: '批量订单打印', url: '/order/orderManage', img: enterprise3 },
-  { name1: '批量下单', name2: '智能识别多商品多地址一键下单', url: '/cart', img: enterprise4 }
-]);
 
-onMounted(() => {
-  document.documentElement.style.setProperty('--hover-color', '#E7000B');
-  getHead();
-});
+const navItems = ref<any>([]);
+const adLeft = ref<any>({});
 
-//头部
-const getHead = async () => {
-  headData.value.topStyle = 1;
-  headData.value.toplabel = [];
-  headData.value.classifyShow = true;
-  headData.value.headType = 3;
-  headData.value.leftStyle = 1;
-  headData.value.carouselList = [];
-  headData.value.advertList = [];
-  headData.value.realDataType = 1;
-  headData.value.realNumber = 6;
-  headData.value.navlList = [];
-  try {
-    const datas1 = await getSearchTitle(5);
-    if (datas1.code == 200) {
-      datas1.data.forEach((item: any) => {
-        headData.value.toplabel.push({
-          title: item.navigationName
-        });
-      });
-    }
-
-    const datas2 = await getHomeNav(5);
-    headData.value.topNav = [];
-    datas2.data.forEach((item: any) => {
-      headData.value.topNav.push({
-        title: item.navigationName,
-        url: item.url ? item.url : ''
-      });
-    });
-
-    const datas3 = await getEnterpriseHomeAdList({});
-    if (datas3.code == 200) {
-      datas3.data.forEach((item: any) => {
-        headData.value.carouselList.push({
-          imageUrl: item.imageUrl
-        });
-      });
-    }
-
-    const datas5 = await customerMessageList({ status: '0', pageSize: 20 });
-    if (datas5.code == 200) realList.value = datas5.rows;
-
-    const datas6 = await getEnterpriseRightsMemberEquityList({});
-    if (datas6.code == 200) {
-      datas6.data.forEach((item: any) => {
-        headData.value.navlList.push({
-          imageUrl: item.imageUrl,
-          title: item.title,
-          url: item.link ? item.link : ''
-        });
-      });
-    }
-
-    headData.value.show = true;
-  } catch (error) {}
+const isLargeScreen = ref(window.innerWidth >= 1600);
+const adHovered = ref(false);
+const check = () => {
+  isLargeScreen.value = window.innerWidth >= 1600;
+  setTimeout(checkScroll, 0);
 };
 
-//猜你喜欢
-getCustomerProductPage({}).then((res) => {
+headerCategoryList({ status: 1 }).then((res) => {
   if (res.code == 200) {
-    recommendList.value = res.rows;
+    navItems.value = res.rows;
   }
 });
+
+getCurrentAdLeft({}).then((res) => {
+  if (res.code == 200) {
+    adLeft.value = res.data;
+  }
+});
+
+const navListRef = ref(null);
+const showPrev = ref(false);
+const showNext = ref(true);
+const showSceneCat = ref(false);
+
+const checkScroll = () => {
+  if (!navListRef.value) return;
+  const { scrollLeft, scrollWidth, clientWidth } = navListRef.value;
+  showPrev.value = scrollLeft > 0;
+  // 给定一个容差值 2px,防止小数四舍五入导致判断失败
+  showNext.value = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 2;
+};
+
+const slide = (amount) => {
+  if (navListRef.value) {
+    navListRef.value.scrollBy({ left: amount, behavior: 'smooth' });
+  }
+};
+
+onMounted(() => {
+  window.addEventListener('resize', check);
+  setTimeout(checkScroll, 100); // 初始化时检查滚动状态
+});
+onUnmounted(() => window.removeEventListener('resize', check));
 </script>
 
 <style lang="scss" scoped>
-.home-pages {
+.jd-app {
   width: 100%;
-  padding-bottom: 30px;
+  background: #f8f8f8;
+  min-height: 100vh;
+}
 
-  :deep(.hover-color) {
-    cursor: pointer;
+.banner-area {
+  margin-top: 10px;
+  position: relative;
+  background: #fff;
+  border-radius: 12px;
+  z-index: 5;
+  display: flex;
+  height: 460px;
+}
 
-    &:hover {
-      color: var(--hover-color) !important;
+/* 极简式大图广告容器,初始 84px 宽,高度撑满 */
+.expand-ad-layer {
+  position: absolute;
+  left: -84px;
+  top: 0;
+  width: 84px;
+  height: 100%;
+  z-index: 100; /* 未展开时层级一般 */
+  overflow: hidden;
+  border-radius: 12px 0 0 12px;
+  transition:
+    width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
+    z-index 0s 0.3s;
+  cursor: pointer;
+  background: transparent;
+}
+/* 悬停时跃升为 9999 皇帝级图层,完美盖死右侧任何弹窗! */
+.expand-ad-layer:hover {
+  width: 790px;
+  z-index: 9999;
+  border-radius: 12px;
+  box-shadow: 6px 10px 30px rgba(0, 0, 0, 0.3);
+  transition:
+    width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
+    z-index 0s 0s;
+}
 
-      .zi-hover {
-        color: var(--hover-color) !important;
-      }
-    }
-  }
-  // 导航
-  .nav-bos {
-    margin: 0 auto;
-    width: 100%;
-    max-width: 1500px;
-    min-width: 1200px;
-    display: flex;
-    gap: 0 10px;
-    .nav-list {
-      margin-top: 15px;
-      flex: 0 0 calc((100% - 30px) / 4);
-      height: 150px;
-      background: #ffffff;
-      border-radius: 5px;
-      padding: 20px;
-      display: flex;
-      flex-direction: column;
-      justify-content: space-between;
-      cursor: pointer;
-      .nav-image {
-        width: 50px;
-        height: 50px;
-        background: #ffeeea;
-        border-radius: 6px 6px 6px 6px;
-      }
-      .name1 {
-        font-size: 15px;
-        margin-bottom: 5px;
-        color: #101828;
-      }
-      .name2 {
-        font-size: 13px;
-        color: #6a7282;
-      }
-      .nav-more {
-        width: 20px;
-        height: 20px;
-        border-radius: 20px;
-        border: 1px solid #3d3e3f;
-      }
-    }
+/* 一整张原生的大图片,定死 790px 宽 */
+.expand-img-full {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 790px;
+  height: 460px;
+  object-fit: cover;
+  z-index: 1;
+}
+
+/* 悬浮在图片最左侧的透明文案区,等同于图片本身 */
+.ad-left-text {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 84px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  z-index: 5;
+  pointer-events: none;
+}
+.deco-inner-white {
+  background: #fff;
+  width: 60px;
+  border-radius: 30px;
+  padding: 20px 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+.d-txt {
+  writing-mode: vertical-lr;
+  color: #2cb356;
+  font-weight: bold;
+  font-size: 16px;
+  letter-spacing: 2px;
+}
+.d-num {
+  font-size: 26px;
+  color: #2cb356;
+  font-weight: 900;
+  margin: 5px 0;
+}
+.play-btn {
+  width: 30px;
+  height: 30px;
+  background: #e1251b;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.triangle {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 8px solid #fff;
+  margin-left: 3px;
+}
+
+.banner-layout {
+  height: 100%;
+  width: 100%;
+  display: flex;
+}
+
+/* 主体列 */
+.main-column {
+  display: flex;
+  flex-direction: column;
+  overflow: visible;
+  min-width: 0;
+}
+
+/* 导航条样式 */
+.nav-bar {
+  height: 60px;
+  line-height: 60px;
+  padding: 0 20px;
+  background: #fff;
+  border-radius: 12px 12px 0 0;
+  position: relative;
+  box-sizing: border-box;
+  width: 100%;
+}
+.nav-scroll-wrapper {
+  display: flex;
+  overflow: hidden;
+  height: 100%;
+  align-items: center;
+}
+.nav-list {
+  overflow-x: auto;
+  overflow-y: hidden;
+  flex-wrap: nowrap;
+  flex: 1;
+  height: 100%;
+  scrollbar-width: none;
+  -ms-overflow-style: none; /* 隐藏原生滚动条 */
+}
+.nav-list::-webkit-scrollbar {
+  display: none;
+} /* Chrome 隐藏原生滚动条 */
+
+.nav-list li {
+  padding: 0 14px;
+  font-weight: bold;
+  cursor: pointer;
+  font-size: 18px;
+  white-space: nowrap;
+  display: flex;
+  align-items: center;
+  color: #333;
+  height: 100%;
+}
+.nav-list li.active {
+  color: #e1251b;
+}
+.n-ic-img {
+  width: 22px;
+  height: 22px;
+  margin-right: 6px;
+}
+
+/* 场景化类目下拉 (仅小屏) */
+.scene-cat-toggle {
+  position: relative;
+  font-weight: bold;
+  font-size: 18px;
+  cursor: pointer;
+  color: #333;
+}
+.scene-cat-toggle span {
+  display: flex;
+  align-items: center;
+  height: 60px;
+  padding: 0 8px 0 10px;
+}
+.arr-down {
+  display: inline-block;
+  width: 6px;
+  height: 6px;
+  border-right: 2px solid #666;
+  border-bottom: 2px solid #666;
+  transform: rotate(45deg);
+  margin-left: 6px;
+  margin-bottom: 3px;
+  transition: transform 0.2s;
+}
+.scene-cat-toggle:hover .arr-down {
+  transform: rotate(225deg);
+}
+.divider {
+  color: #ccc;
+  padding: 0 4px;
+  font-weight: normal;
+  font-size: 16px;
+}
+
+/* 导航左右滚动控制 */
+.scroll-btn-wrap {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+}
+.scroll-btn-wrap.prev {
+  left: 0;
+  padding-right: 24px;
+  background: linear-gradient(90deg, #fff 50%, rgba(255, 255, 255, 0));
+}
+.scroll-btn-wrap.next {
+  right: 0;
+  padding-left: 24px;
+  background: linear-gradient(270deg, #fff 50%, rgba(255, 255, 255, 0));
+}
+
+.more-btn {
+  width: 28px;
+  height: 28px;
+  border-radius: 50%;
+  background: rgba(0, 0, 0, 0.08);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  color: #666;
+  font-size: 14px;
+  font-weight: bold;
+  flex-shrink: 0;
+}
+.more-btn:hover {
+  background: rgba(0, 0, 0, 0.15);
+}
+
+/* 下方分类与广告 */
+.banner-bottom {
+  display: flex;
+  flex: 1;
+  overflow: visible;
+  padding: 0 10px 10px;
+  gap: 12px;
+  background: transparent;
+  position: relative;
+}
+.side-category {
+  width: 280px;
+  background: transparent;
+  overflow: visible;
+  position: relative;
+  z-index: 1000;
+}
+.side-category.is-dropdown {
+  position: absolute;
+  top: 0;
+  bottom: 10px; /* 补偿 banner-bottom 的 10px 底部 padding */
+  left: 10px; /* 补偿 banner-bottom 的 10px 左侧 padding */
+  z-index: 9999;
+  box-shadow: 4px 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.ad-group {
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  height: 100%;
+}
+
+/* 用户列 */
+.user-col {
+  width: 230px;
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+  flex-shrink: 0;
+  margin: 10px 10px 10px 0;
+  height: calc(100% - 20px);
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+@media screen and (max-width: 1599px) {
+  .banner-layout {
+    height: auto;
+    min-height: 394px;
   }
 }
 </style>

+ 0 - 1
src/views/home/index.vue

@@ -315,7 +315,6 @@ getClassificationFloorList({}).then(async (res) => {
           }
         }
         const datas2 = await getClassificationFloorLabel(item.floorNo);
-        console.log(datas2.data, '??????????????');
         if (datas2.code == 200) {
           datas2.data.forEach((item1: any) => {
             if (item1.position == 0) {

+ 297 - 153
src/views/home/jdcomponents/JDBannerCards.vue

@@ -5,50 +5,30 @@
       <!-- 智能选品 Banner (多图轮播) -->
       <div class="slider-box" @mouseenter="stopAuto" @mouseleave="startAuto">
         <div class="slider-list flex" :style="{ transform: `translateX(-${currentIdx * 100}%)` }">
-          <div class="slider-item" v-for="(slide, i) in bannerSlides" :key="i">
-            <img :src="slide.img" class="banner-img" />
-            <div class="slider-content">
-              <h1>{{ slide.h1 }}</h1>
-              <h2>{{ slide.h2 }}</h2>
-              <p>{{ slide.p }}</p>
-            </div>
+          <div class="slider-item" v-for="(slide, i) in carouseData" :key="i" @click="onPath(slide.link)">
+            <img :src="slide.imageUrl" class="banner-img" />
           </div>
         </div>
-        <div class="ai-logo">
-           <span>A</span><span>i</span>
-        </div>
         <div class="dots">
-          <span
-            v-for="(_, i) in bannerSlides"
-            :key="i"
-            :class="{ active: currentIdx === i }"
-            @click="currentIdx = i"
-          ></span>
+          <span v-for="(_, i) in carouseData" :key="i" :class="{ active: currentIdx === i }" @click="carouseData = i"></span>
         </div>
       </div>
 
       <!-- 百亿补贴 -->
       <div class="subsidy-box card-base">
         <div class="c-header flex-between">
-          <span class="title">企业购×百亿补贴</span>
-          <span class="tag">先采后付 享底价</span>
+          <span class="title" :style="{ color: dataInfo1.mainTitleColor }">{{ dataInfo1.mainTitle }}</span>
+          <span class="tag" :style="{ color: dataInfo1.subTitleColor }">{{ dataInfo1.subTitle }}</span>
         </div>
         <div class="p-list flex">
-          <div class="p-mini">
-             <img src="@/assets/jd/prod_it.png" />
-             <p class="price"><span>¥</span>69.9</p>
-          </div>
-          <div class="p-mini">
-             <img src="@/assets/jd/prod_office.png" />
-             <p class="price"><span>¥</span>84.8</p>
-          </div>
-          <div class="p-mini">
-             <img src="@/assets/jd/prod_lifestyle.png" />
-             <p class="price"><span>¥</span>139.9</p>
-          </div>
-          <div class="p-mini">
-             <img src="@/assets/jd/prod_it.png" />
-             <p class="price"><span>¥</span>1749</p>
+          <div
+            class="p-mini"
+            v-for="(item, index) in dataInfo1.adModuleItemList"
+            :key="index"
+            @click="onPath('/item?id=' + item.productId + '&productNo=' + item.productNo)"
+          >
+            <el-image class="p-img" :src="item.imageUrl" />
+            <p class="price"><span>¥</span>{{ item.price }}</p>
           </div>
         </div>
       </div>
@@ -56,99 +36,119 @@
 
     <!-- 下半部分 -->
     <div class="bottom-row flex">
-       <!-- 企采榜单 -->
-       <div class="card-base b-item">
-         <div class="c-header flex-between">
-           <span class="title">企采榜单</span>
-           <span class="tag-orange">同行都在买</span>
-         </div>
-         <div class="rank-list flex">
-            <div class="r-i">
-               <div class="r-title yellow-t">办公电脑榜<i class="arr"> &gt;</i></div>
-               <img src="@/assets/jd/prod_it.png" />
-               <span class="sold">已售1543件</span>
-            </div>
-            <div class="r-i">
-               <div class="r-title red-t">文具榜<i class="arr"> &gt;</i></div>
-               <img src="@/assets/jd/prod_office.png" />
-               <span class="sold">已售2.3万件</span>
-            </div>
-         </div>
-       </div>
-
-       <!-- 品牌好店 -->
-       <div class="card-base b-item">
-         <div class="c-header flex-between">
-           <span class="title">品牌好店</span>
-           <span class="tag-orange">返2000元E卡</span>
-         </div>
-         <div class="brand-box flex">
-            <div class="brand-i">
-              <img src="@/assets/jd/brand_logo_1.png" class="logo" />
-              <p class="name">鲁花京东自营旗舰店</p>
-              <span class="btn btn-red">品质保障</span>
-            </div>
-            <div class="brand-i">
-              <img src="@/assets/jd/brand_logo_2.png" class="logo" />
-              <p class="name">金龙鱼京东自营旗舰</p>
-              <span class="btn btn-red">热销品牌</span>
-            </div>
-         </div>
-       </div>
-
-       <!-- 企业精选 -->
-       <div class="card-base b-item">
-         <div class="c-header flex-between">
-           <span class="title">企业精选</span>
-           <span class="tag-gray">品牌专供 库存足</span>
-         </div>
-         <div class="p-list flex">
-            <div class="p-mini">
-               <img src="@/assets/jd/prod_it.png" />
-               <p class="price"><span>¥</span>10740 <span class="badge-blue">企业价</span></p>
-            </div>
-            <div class="p-mini">
-               <img src="@/assets/jd/prod_lifestyle.png" />
-               <p class="price"><span>¥</span>877</p>
-            </div>
-         </div>
-       </div>
-
-       <!-- 京东新品 -->
-       <div class="card-base b-item">
-         <div class="c-header flex-between">
-           <span class="title">企业购×京东新品</span>
-           <span class="tag-orange">美的新鲜</span>
-         </div>
-         <div class="p-list flex">
-            <div class="p-mini">
-               <img src="@/assets/jd/prod_lifestyle.png" />
-               <p class="price"><span>¥</span>7188</p>
-            </div>
-            <div class="p-mini">
-               <img src="@/assets/jd/prod_office.png" />
-               <p class="price"><span>¥</span>34.9</p>
-            </div>
-         </div>
-       </div>
+      <!-- 企采榜单 -->
+      <div class="card-base b-item">
+        <div class="c-header flex-between">
+          <span class="title" :style="{ color: dataInfo2.mainTitleColor }">{{ dataInfo2.mainTitle }}</span>
+          <span class="tag-orange" :style="{ color: dataInfo2.subTitleColor }">{{ dataInfo2.mainTitle }}</span>
+        </div>
+        <div class="rank-list flex">
+          <div class="r-i" v-for="(item, index) in dataInfo2.adModuleItemList" :key="index" @click="onPath(item.tagLink)">
+            <div class="r-title yellow-t">{{ item.tagText }}<i class="arr"> &gt;</i></div>
+            <el-image class="r-img" :src="item.imageUrl" />
+            <span class="sold">已售{{ item.salesCount }}件</span>
+          </div>
+        </div>
+      </div>
+
+      <!-- 品牌好店 -->
+      <div class="card-base b-item">
+        <div class="c-header flex-between">
+          <span class="title" :style="{ color: dataInfo3.mainTitleColor }">{{ dataInfo3.mainTitle }}</span>
+          <span class="tag-orange" :style="{ color: dataInfo3.subTitleColor }">{{ dataInfo3.mainTitle }}</span>
+        </div>
+        <div class="brand-box flex">
+          <div class="brand-i" v-for="(item, index) in dataInfo3.adModuleItemList" :key="index" @click="onPath(item.tagLink)">
+            <el-image class="logo" :src="item.imageUrl" />
+            <p class="name">{{ item.productName }}</p>
+            <span class="btn btn-red">{{ item.tagText }}</span>
+          </div>
+        </div>
+      </div>
+
+      <!-- 企业精选 -->
+      <div class="card-base b-item">
+        <div class="c-header flex-between">
+          <span class="title" :style="{ color: dataInfo4.mainTitleColor }">{{ dataInfo4.mainTitle }}</span>
+          <span class="tag-gray" :style="{ color: dataInfo4.subTitleColor }">{{ dataInfo4.mainTitle }}</span>
+        </div>
+        <div class="p-list flex">
+          <div
+            class="p-mini"
+            v-for="(item, index) in dataInfo4.adModuleItemList"
+            :key="index"
+            @click="onPath('/item?id=' + item.productId + '&productNo=' + item.productNo)"
+          >
+            <el-image class="p-img" :src="item.imageUrl" />
+            <p class="price">
+              <span>¥</span>{{ item.price }}
+              <!-- <span class="badge-blue">企业价</span> -->
+            </p>
+          </div>
+        </div>
+      </div>
+
+      <!-- 京东新品 -->
+      <div class="card-base b-item">
+        <div class="c-header flex-between">
+          <span class="title" :style="{ color: dataInfo5.mainTitleColor }">{{ dataInfo5.mainTitle }}</span>
+          <span class="tag-orange" :style="{ color: dataInfo5.subTitleColor }">{{ dataInfo5.mainTitle }}</span>
+        </div>
+        <div class="p-list flex">
+          <div
+            class="p-mini"
+            v-for="(item, index) in dataInfo5.adModuleItemList"
+            :key="index"
+            @click="onPath('/item?id=' + item.productId + '&productNo=' + item.productNo)"
+          >
+            <el-image class="p-img" :src="item.imageUrl" />
+            <p class="price"><span>¥</span>{{ item.price }}</p>
+          </div>
+        </div>
+      </div>
     </div>
   </div>
 </template>
-<script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
-
+<script setup lang="ts">
+import { carouselList, adModuleConfigList } from '@/api/home/index-enterprise';
+import { onPath } from '@/utils/siteConfig';
 const currentIdx = ref(0);
-const bannerSlides = [
-  { h1: '智能选品', h2: '让采购更高效', p: '需求清单极速匹配', img: '@/assets/jd/prod_it.png' }, // Use it as default or generate dedicated
-  { h1: '办公大促', h2: '职场焕新季', p: '精选文具低至5折', img: '@/assets/jd/banner_office.png' },
-  { h1: '工业采买', h2: '正品低价保障', p: '专业工具 一站购齐', img: '@/assets/jd/banner_industrial.png' },
-  { h1: '员工福利', h2: '温情定制礼', p: '打造有温度的职场', img: '@/assets/jd/banner_welfare.png' }
-];
+const carouseData = ref<any>([]);
+const dataInfo1 = ref<any>({});
+const dataInfo2 = ref<any>({});
+const dataInfo3 = ref<any>({});
+const dataInfo4 = ref<any>({});
+const dataInfo5 = ref<any>({});
+carouselList({}).then((res) => {
+  if (res.code == 200) {
+    carouseData.value = res.rows;
+  }
+});
+
+adModuleConfigList({}).then((res) => {
+  if (res.code == 200) {
+    if (res.rows.length > 0) {
+      dataInfo1.value = res.rows[0];
+    }
+    if (res.rows.length > 1) {
+      dataInfo2.value = res.rows[1];
+    }
+    if (res.rows.length > 2) {
+      dataInfo3.value = res.rows[2];
+    }
+    if (res.rows.length > 3) {
+      dataInfo4.value = res.rows[3];
+    }
+    if (res.rows.length > 4) {
+      dataInfo5.value = res.rows[4];
+    }
+  }
+});
 
 let timer = null;
 const startAuto = () => {
   timer = setInterval(() => {
-    currentIdx.value = (currentIdx.value + 1) % bannerSlides.length;
+    currentIdx.value = (currentIdx.value + 1) % carouseData.value.length;
   }, 4000);
 };
 const stopAuto = () => {
@@ -179,7 +179,7 @@ onUnmounted(() => stopAuto());
 
 .slider-box {
   flex: 5.5;
-  background: #187AF2;
+  background: #187af2;
   border-radius: 8px;
   position: relative;
   overflow: hidden;
@@ -196,7 +196,7 @@ onUnmounted(() => stopAuto());
   min-width: 100%;
   height: 100%;
   position: relative;
-  background: #187AF2;
+  background: #187af2;
 }
 
 .banner-img {
@@ -214,9 +214,25 @@ onUnmounted(() => stopAuto());
   z-index: 5;
 }
 
-.slider-content h1 { font-size: 26px; font-weight: normal; margin-bottom: 2px; letter-spacing: 1px; }
-.slider-content h2 { font-size: 26px; font-weight: 800; margin-bottom: 16px; letter-spacing: 1px; }
-.slider-content p { font-size: 13px; font-weight: 500; opacity: 0.9; }
+.slider-content h1 {
+  font-size: 26px;
+  font-weight: normal;
+  margin-bottom: 2px;
+  letter-spacing: 1px;
+}
+
+.slider-content h2 {
+  font-size: 26px;
+  font-weight: 800;
+  margin-bottom: 16px;
+  letter-spacing: 1px;
+}
+
+.slider-content p {
+  font-size: 13px;
+  font-weight: 500;
+  opacity: 0.9;
+}
 
 .ai-logo {
   position: absolute;
@@ -226,10 +242,22 @@ onUnmounted(() => stopAuto());
   font-weight: 900;
   display: flex;
   align-items: baseline;
-  text-shadow: 0 10px 20px rgba(0,0,0,0.2);
+  text-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
+}
+
+.ai-logo span:first-child {
+  font-size: 110px;
+  color: #fff;
+  background: linear-gradient(180deg, #e2f1ff, #ffffff);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+}
+
+.ai-logo span:last-child {
+  font-size: 80px;
+  color: #66b2ff;
+  margin-left: 5px;
 }
-.ai-logo span:first-child { font-size: 110px; color: #fff; background: linear-gradient(180deg, #E2F1FF, #FFFFFF); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
-.ai-logo span:last-child { font-size: 80px; color: #66B2FF; margin-left: 5px; }
 
 .dots {
   position: absolute;
@@ -238,26 +266,42 @@ onUnmounted(() => stopAuto());
   display: flex;
   gap: 6px;
 }
-.dots span { display: block; width: 6px; height: 6px; background: rgba(255,255,255,0.4); border-radius: 50%; }
-.dots span.active { width: 14px; background: #fff; border-radius: 3px; }
+
+.dots span {
+  display: block;
+  width: 6px;
+  height: 6px;
+  background: rgba(255, 255, 255, 0.4);
+  border-radius: 50%;
+}
+
+.dots span.active {
+  width: 14px;
+  background: #fff;
+  border-radius: 3px;
+}
 
 .subsidy-box {
   flex: 4.5;
 }
 
 .card-base {
-  background: #F8F9FA; /* 与侧边分类背景色相同 */
+  background: #f8f9fa;
+  /* 与侧边分类背景色相同 */
   border-radius: 8px;
   padding: 16px;
   display: flex;
   flex-direction: column;
-  transition: transform 0.2s, box-shadow 0.2s;
+  transition:
+    transform 0.2s,
+    box-shadow 0.2s;
   cursor: pointer;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.02);
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.02);
 }
+
 .card-base:hover {
   transform: translateY(-2px);
-  box-shadow: 0 6px 16px rgba(0,0,0,0.08);
+  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
 }
 
 .b-item {
@@ -275,9 +319,21 @@ onUnmounted(() => stopAuto());
   color: #000;
 }
 
-.tag { font-size: 12px; color: #D46B08; font-weight: bold; }
-.tag-orange { font-size: 11px; color: #D46B08; }
-.tag-gray { font-size: 11px; color: #8C8C8C; }
+.tag {
+  font-size: 12px;
+  color: #d46b08;
+  font-weight: bold;
+}
+
+.tag-orange {
+  font-size: 11px;
+  color: #d46b08;
+}
+
+.tag-gray {
+  font-size: 11px;
+  color: #8c8c8c;
+}
 
 .p-list {
   flex: 1;
@@ -292,20 +348,26 @@ onUnmounted(() => stopAuto());
   justify-content: space-between;
 }
 
-.p-mini img {
+.p-img {
   width: 90%;
+  aspect-ratio: 1;
   margin: 0 auto 6px;
 }
 
 .price {
-  color: #E1251B;
+  color: #e1251b;
   font-size: 16px;
   font-weight: bold;
 }
-.price span { font-size: 12px; font-weight: normal; margin-right: 1px; }
+
+.price span {
+  font-size: 12px;
+  font-weight: normal;
+  margin-right: 1px;
+}
 
 .badge-blue {
-  background: #1890FF;
+  background: #1890ff;
   color: #fff;
   font-size: 10px;
   padding: 1px 4px;
@@ -314,18 +376,100 @@ onUnmounted(() => stopAuto());
   vertical-align: middle;
 }
 
-.rank-list { gap: 12px; flex: 1; }
-.r-i { flex: 1; background: #fff; border-radius: 6px; display: flex; flex-direction: column; justify-content: space-between; position: relative; }
-.r-title { position: absolute; top: -10px; left: 50%; transform: translateX(-50%); padding: 2px 10px; border-radius: 12px; font-size: 11px; font-weight: bold; white-space: nowrap; z-index: 2; }
-.yellow-t { background: #FFF3E0; color: #E65100; }
-.red-t { background: #FFEBEE; color: #C62828; }
-.r-title .arr { font-style: normal; font-size: 9px; opacity: 0.8; }
-.r-i img { width: 80%; margin: 15px auto 5px; }
-.sold { font-size: 11px; font-weight: bold; color: #E1251B; background: #FFF0F0; border-radius: 0 0 6px 6px; padding: 4px 0; text-align: center; width: 100%; }
-
-.brand-box { gap: 12px; flex: 1; }
-.brand-i { flex: 1; text-align: center; display: flex; flex-direction: column; align-items: center; justify-content: space-between; }
-.logo { width: 50px; height: 50px; margin-top: 5px; }
-.name { font-size: 11px; color: #1890FF; white-space: nowrap; transform: scale(0.9); }
-.btn-red { font-size: 11px; color: #E1251B; border: 1px solid #E1251B; padding: 2px 8px; border-radius: 12px; margin-bottom: 5px; }
+.rank-list {
+  gap: 12px;
+  flex: 1;
+}
+
+.r-i {
+  flex: 1;
+  background: #fff;
+  border-radius: 6px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  position: relative;
+}
+
+.r-title {
+  position: absolute;
+  top: -10px;
+  left: 50%;
+  transform: translateX(-50%);
+  padding: 2px 10px;
+  border-radius: 12px;
+  font-size: 11px;
+  font-weight: bold;
+  white-space: nowrap;
+  z-index: 2;
+}
+
+.yellow-t {
+  background: #fff3e0;
+  color: #e65100;
+}
+
+.red-t {
+  background: #ffebee;
+  color: #c62828;
+}
+
+.r-title .arr {
+  font-style: normal;
+  font-size: 9px;
+  opacity: 0.8;
+}
+
+.r-img {
+  width: 80%;
+  aspect-ratio: 1;
+  margin: 15px auto 5px;
+}
+
+.sold {
+  font-size: 11px;
+  font-weight: bold;
+  color: #e1251b;
+  background: #fff0f0;
+  border-radius: 0 0 6px 6px;
+  padding: 4px 0;
+  text-align: center;
+  width: 100%;
+}
+
+.brand-box {
+  gap: 12px;
+  flex: 1;
+}
+
+.brand-i {
+  flex: 1;
+  text-align: center;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.logo {
+  width: 80%;
+  aspect-ratio: 120 / 50;
+  margin-top: 5px;
+}
+
+.name {
+  font-size: 11px;
+  color: #1890ff;
+  white-space: nowrap;
+  transform: scale(0.9);
+}
+
+.btn-red {
+  font-size: 11px;
+  color: #e1251b;
+  border: 1px solid #e1251b;
+  padding: 2px 8px;
+  border-radius: 12px;
+  margin-bottom: 5px;
+}
 </style>

+ 149 - 117
src/views/home/jdcomponents/JDCategory.vue

@@ -1,13 +1,7 @@
 <template>
   <div class="cate-menu" @mouseleave="activeIndex = -1">
     <ul class="cate-list">
-      <li 
-        v-for="(c, i) in categories" 
-        :key="i" 
-        class="cate-item"
-        :class="{ active: activeIndex === i }"
-        @mouseenter="activeIndex = i"
-      >
+      <li v-for="(c, i) in dataList" :key="i" class="cate-item" :class="{ active: activeIndex === i }" @mouseenter="onMouseenter(i)">
         <img :src="c.icon" class="cate-icon" />
         <a href="#">{{ c.name }}</a>
       </li>
@@ -15,116 +9,114 @@
 
     <!-- 巨大悬浮菜单面板 (根据图5精细刻画) -->
     <div class="category-pop" v-show="activeIndex !== -1">
-       <div class="pop-header flex-between">
-          <div class="pop-tags flex">
-             <span class="p-tag">设备固资</span>
-             <span class="p-tag">职场环境</span>
-             <span class="p-tag">3C数码电子城</span>
-          </div>
-          <div class="pop-logo">
-             <span class="red-t">京东</span><span class="black-t">3C数码</span><br/>
-             <span class="small-t">电脑 | 数码 | 手机 | 图书 | 文具</span>
-          </div>
-       </div>
-
-       <div class="pop-body">
-          <dl class="sub-dl flex">
-             <dt>办公电脑</dt>
-             <dd>
-                <a href="#">台式机</a><a href="#">平板电脑</a><a href="#">显示器</a><a href="#">服务器</a><a href="#">工作站</a><a href="#">一体机</a><a href="#">组装电脑</a><a href="#">游戏本</a><a href="#">设计师本</a><a href="#">笔记本配件</a><a href="#">平板电脑配件</a><a href="#">阅卷机</a><a href="#">办公本</a>
-             </dd>
-          </dl>
-          <dl class="sub-dl flex">
-             <dt>办公打印</dt>
-             <dd>
-                <a href="#">复合机</a><a href="#">打印机</a><a href="#">多功能一体机</a><a href="#">扫描仪</a><a href="#">条码打印机</a><a href="#">碎纸机</a><a href="#">装订机</a><a href="#">高拍仪</a><a href="#">标签打印机</a><a href="#">传真设备</a>
-             </dd>
-          </dl>
-          <dl class="sub-dl flex">
-             <dt>电脑组件</dt>
-             <dd>
-                <a href="#">显示器</a><a href="#">硬盘</a><a href="#">SSD固态硬盘</a><a href="#">机械硬盘</a><a href="#">显卡</a><a href="#">内存</a><a href="#">主板CPU套装</a><a href="#">组装机</a><a href="#">装机配件</a><a href="#">机箱</a><a href="#">刻录机</a><a href="#">光驱</a><a href="#">散热器</a><a href="#">电源</a>
-             </dd>
-          </dl>
-          <dl class="sub-dl flex">
-             <dt>电脑外设</dt>
-             <dd>
-                <a href="#">机柜</a><a href="#">键盘</a><a href="#">扩展坞</a><a href="#">手柄方向盘</a><a href="#">鼠标</a><a href="#">鼠标垫</a><a href="#">移动固态硬盘</a><a href="#">移动机械硬盘</a><a href="#">硬盘盒</a><a href="#">游戏耳机</a><a href="#">游戏机</a><a href="#">游戏软件</a><a href="#">游戏周边</a>
-             </dd>
-          </dl>
-          <dl class="sub-dl flex">
-             <dt>智能会议</dt>
-             <dd>
-                <a href="#">直播设备</a><a href="#">U盘</a><a href="#">UPS电源</a><br/>
-                <a href="#">办公大屏</a><a href="#">投影机</a><a href="#">会议平板</a><a href="#">会议摄像头</a><a href="#">音响</a><a href="#">会议音响</a><a href="#">麦克风</a><a href="#">路由器</a><a href="#">网络机顶盒</a><a href="#">交换机</a><a href="#">网络存储</a><a href="#">网线</a><a href="#">网卡</a><a href="#">话务耳机</a><br/>
-                <a href="#">安防监控</a><a href="#">电动幕布</a><a href="#">网络仪器仪表</a><a href="#">白板</a><a href="#">功放</a><a href="#">电视配件</a><a href="#">回音壁Soundbar</a><a href="#">会议音视频</a><a href="#">手写板</a><a href="#">投影配件</a><a href="#">录音笔</a><a href="#">翻译机</a><a href="#">翻译设备</a>
-             </dd>
-          </dl>
-          <dl class="sub-dl flex">
-             <dt>IT运维</dt>
-             <dd>
-                <a href="#">交换机</a><a href="#">路由器</a><a href="#">网络机顶盒</a><a href="#">网络存储</a><a href="#">网卡</a><a href="#">网线</a><a href="#">网络配件</a><a href="#">网络仪器仪表</a><a href="#">线缆</a><a href="#">普通网络设备</a>
-             </dd>
-          </dl>
-       </div>
+      <div class="pop-header flex-between">
+        <div class="pop-tags flex">
+          <span v-for="(item, index) in dataInfo.tags" :key="index" class="p-tag" @click="onPath(item.link)">{{ item.name }}</span>
+        </div>
+        <div class="pop-logo">
+          <span class="red-t">{{ dataInfo.panelMainTitle }}</span
+          ><span class="black-t">{{ dataInfo.panelSubTitle }}</span
+          ><br />
+          <span class="small-t" v-for="(item, index) in dataInfo.notes" :key="index" @click="onPath(item.link)"
+            >{{ index != 0 ? ' | ' : '' }}{{ item.name }}</span
+          >
+        </div>
+      </div>
+
+      <div class="pop-body">
+        <dl class="sub-dl flex" v-for="(item, index) in dataInfo.categoryList" :key="index">
+          <dt class="item-label" @click="onPath('/search?type=2&mediumCategoryId=' + item.id)">{{ item.label }}</dt>
+          <dd>
+            <div
+              @click="onPath('/search?type=3&bottomCategoryId=' + item1.id)"
+              class="item1-label"
+              v-for="(item1, index1) in item.children"
+              :key="index1"
+            >
+              {{ item1.label }}
+            </div>
+          </dd>
+        </dl>
+      </div>
     </div>
   </div>
 </template>
 
-<script setup>
-import { ref } from 'vue'
-
-const activeIndex = ref(-1)
-
-// 线条风格 SVG base64 图标
-const iconPC = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxyZWN0IHg9IjIiIHk9IjQiIHdpZHRoPSIyMCIgaGVpZ2h0PSIxNCIgcng9IjIiLz48cGF0aCBkPSJNOCAyMmgybS0yIDBsNC00bTQgNGgybS0yIDBsLTQtNCIvPjwvc3ZnPg=='
-const iconAC = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxyZWN0IHg9IjIiIHk9IjYiIHdpZHRoPSIyMCIgaGVpZ2h0PSIxMiIgcng9IjIiLz48cGF0aCBkPSJNMiAxMGgyMG0tMTYgNGg0Ii8+PC9zdmc+'
-const iconPrint = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik02IDlWMmgtNHY3bTggMEg0djExaDE2VjltLTYgMTB2LTVoOHY1Ii8+PC9zdmc+'
-const iconTea = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik00IDhoMTJ2OGExIDEgMCAwIDEtMSAxSDVhMSAxIDAgMCAxLTEtMVY4em0xMiAyYTQgNCAwIDAgMSAwIDhoLTJtLTItMTh2NG0tNCAwdjQiLz48L3N2Zz4='
-const iconPhone = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxyZWN0IHg9IjUiIHk9IjIiIHdpZHRoPSIxNCIgaGVpZ2h0PSIyMCIgcng9IjIiLz48cGF0aCBkPSJNMTIgMThoLjAxIi8+PC9zdmc+'
-const iconMask = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik0xMiAyMmM0LjQgMCA4LTMuNiA4LThzLTMuNi04LTgtOC04IDMuNi04IDhzMy42IDggOCA4em0wIDB2LThtLTQgNGg4Ii8+PC9zdmc+'
-const iconMed = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik0xMiA0djE2bS04LThoMTZtLTQgNGExIDEgMCAxIDEtMiAwIDEgMSAwIDAgMSA0IDB6Ii8+PC9zdmc+'
-const iconCar = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik0xOSA4bDEgNG0tMTYgMGwxLTRoMTJtLTIgMmgybTEgMTB2LTJIM3YybTEwIDBoNG0tNiAwSDRtMTQgMGgydjRINnYtNCIvPjwvc3ZnPg=='
-const iconTent = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjIiPjxwYXRoIGQ9Ik0xMiAzTDIgMjFoMjBtLTEwLTE4bDkgMTguNW0tMTggMGg4Ii8+PC9zdmc+'
-
-const categories = [
-  { name: '办公电脑 / 办公打印 / 电脑组件', icon: iconPC },
-  { name: '室温调节 / 冷藏保鲜 / 办公家具', icon: iconAC },
-  { name: '打印耗材 / 办公文具 / 清洁用品', icon: iconPrint },
-  { name: '茶歇福利 / 个护清洁 / 日用福利', icon: iconTea },
-  { name: '电子数码 / 茗茶酒水 / 美妆护肤', icon: iconPhone },
-  { name: '个人防护 / 清洁用品 / 电动工具', icon: iconMask },
-  { name: '养生茶饮 / 滋补礼盒 / 营养保健', icon: iconMed },
-  { name: '汽车用品 / 车辆养护 / 整车采购', icon: iconCar },
-  { name: '户外装备 / 体育用品 / 工装工服', icon: iconTent }
-];
+<script setup lang="ts">
+import { categoryMainList } from '@/api/home/index-enterprise';
+import { getProductCategoryTree } from '@/api/home/index';
+import { onPath } from '@/utils/siteConfig';
+const activeIndex = ref(-1);
+const dataList = ref<any>([]);
+const dataInfo = ref<any>({});
+const category = ref<any>([]);
+
+onMounted(() => {
+  getHead();
+});
+
+const getHead = async () => {
+  try {
+    const datas1 = await getProductCategoryTree({});
+    const datas2 = await categoryMainList({ status: 1 });
+    document.documentElement.style.setProperty('--enterprise-color', '#E7000B');
+    datas1.data.forEach((item1: any) => {
+      datas2.rows.forEach((item2: any) => {
+        if (item2.syncCategoryId == item1.id) {
+          item2.categoryList = item1.children ? item1.children : [];
+        }
+      });
+    });
+    dataList.value = datas2.rows;
+    if (datas2.rows.length > 0) {
+      dataInfo.value = datas2.rows[0];
+    }
+  } catch (error) {}
+};
+
+const onMouseenter = (index: any) => {
+  activeIndex.value = index;
+  dataInfo.value = dataList.value[index];
+};
 </script>
 
 <style scoped>
-.cate-menu { position: relative; width: 100%; height: 100%; z-index: 100; }
-.cate-list { padding: 10px 0; background: #F8F9FA; height: 100%; box-sizing: border-box; }
-
-.cate-item { 
-  height: 40px; 
-  padding: 0 16px; 
-  cursor: pointer; 
-  display: flex; 
-  align-items: center; 
+.cate-menu {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  z-index: 100;
+}
+.cate-list {
+  padding: 10px 0;
+  background: #f8f9fa;
+  height: 100%;
+  box-sizing: border-box;
+}
+
+.cate-item {
+  height: 40px;
+  padding: 0 16px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
   position: relative;
   box-sizing: border-box;
   border: 1px solid transparent; /* 为红框预留 */
 }
 
-.cate-item.active, .cate-item:hover { 
-  background: #fff; 
-  border-color: #E1251B; /* 四周边框变红 */
+.cate-item.active,
+.cate-item:hover {
+  background: #fff;
+  border-color: #e1251b; /* 四周边框变红 */
   border-right-color: #fff; /* 右边框变白打通 */
   z-index: 201; /* 盖在弹窗上方 */
   width: calc(100% + 1px); /* 向右延伸 1px */
 }
 
-.cate-item.active a, .cate-item:hover a { 
-  color: #E1251B; 
+.cate-item.active a,
+.cate-item:hover a {
+  color: #e1251b;
   font-weight: bold;
 }
 
@@ -139,34 +131,69 @@ const categories = [
   filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
 }
 
-.cate-item a { font-size: 14px; color: #333; text-decoration: none; }
+.cate-item a {
+  font-size: 14px;
+  color: #333;
+  text-decoration: none;
+}
 
 /* 右侧巨大弹出层 */
 .category-pop {
   position: absolute;
   left: 280px;
   top: 0;
-  width: 820px;
+  width: 950px;
   height: 440px;
   background: #fff;
-  border: 1px solid #E1251B;
+  border: 1px solid #e1251b;
   border-radius: 12px;
-  box-shadow: 4px 6px 20px rgba(0,0,0,0.12);
+  box-shadow: 4px 6px 20px rgba(0, 0, 0, 0.12);
   padding: 24px 30px;
   z-index: 200;
   box-sizing: border-box;
 }
 
 /* 顶部标签和 Logo */
-.pop-header { margin-bottom: 25px; align-items: flex-end; }
-.pop-tags { gap: 12px; }
-.p-tag { background: #F4F4F4; color: #333; font-size: 12px; padding: 6px 16px; border-radius: 4px; cursor: pointer; }
-.p-tag:hover { background: #E1251B; color: #fff; }
+.pop-header {
+  margin-bottom: 25px;
+  align-items: flex-end;
+}
+.pop-tags {
+  gap: 12px;
+}
+.p-tag {
+  background: #f4f4f4;
+  color: #333;
+  font-size: 12px;
+  padding: 6px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.p-tag:hover {
+  background: #e1251b;
+  color: #fff;
+}
 
-.pop-logo { text-align: right; line-height: 1.2; }
-.red-t { color: #E1251B; font-size: 18px; font-weight: 900; }
-.black-t { color: #000; font-size: 18px; font-weight: 900; }
-.small-t { font-size: 9px; color: #E1251B; letter-spacing: 1px; }
+.pop-logo {
+  text-align: right;
+  line-height: 1.2;
+}
+.red-t {
+  color: #e1251b;
+  font-size: 18px;
+  font-weight: 900;
+}
+.black-t {
+  color: #000;
+  font-size: 18px;
+  font-weight: 900;
+}
+.small-t {
+  font-size: 9px;
+  color: #e1251b;
+  letter-spacing: 1px;
+  cursor: pointer;
+}
 
 /* 主体列表区域 */
 .sub-dl {
@@ -174,7 +201,7 @@ const categories = [
   align-items: flex-start;
   line-height: 1.8;
 }
-.sub-dl dt {
+.item-label {
   width: 75px;
   font-weight: bold;
   font-size: 12px;
@@ -182,6 +209,10 @@ const categories = [
   margin-right: 15px;
   text-align: right;
   white-space: nowrap;
+  cursor: pointer;
+}
+.item-label:hover {
+  color: var(--enterprise-color);
 }
 .sub-dl dd {
   flex: 1;
@@ -189,13 +220,14 @@ const categories = [
   flex-wrap: wrap;
   gap: 0 16px;
 }
-.sub-dl dd a {
+.item1-label {
   font-size: 12px;
-  color: #8C8C8C;
+  color: #8c8c8c;
   text-decoration: none;
   white-space: nowrap;
+  cursor: pointer;
 }
-.sub-dl dd a:hover {
-  color: #E1251B;
+.item1-label:hover {
+  color: var(--enterprise-color);
 }
 </style>

+ 29 - 32
src/views/home/jdcomponents/JDFooter.vue

@@ -6,7 +6,7 @@
         <div class="slogan-item">
           <div class="s-icon">
             <svg viewBox="0 0 40 46" width="36" height="36" class="hex-svg">
-               <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
+              <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
             </svg>
             <span class="s-txt">多</span>
           </div>
@@ -15,7 +15,7 @@
         <div class="slogan-item">
           <div class="s-icon">
             <svg viewBox="0 0 40 46" width="36" height="36" class="hex-svg">
-               <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
+              <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
             </svg>
             <span class="s-txt">快</span>
           </div>
@@ -24,7 +24,7 @@
         <div class="slogan-item">
           <div class="s-icon">
             <svg viewBox="0 0 40 46" width="36" height="36" class="hex-svg">
-               <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
+              <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
             </svg>
             <span class="s-txt">好</span>
           </div>
@@ -33,7 +33,7 @@
         <div class="slogan-item">
           <div class="s-icon">
             <svg viewBox="0 0 40 46" width="36" height="36" class="hex-svg">
-               <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
+              <polygon points="20,2 38,12 38,34 20,44 2,34 2,12" fill="none" stroke="#E1251B" stroke-width="3"></polygon>
             </svg>
             <span class="s-txt">省</span>
           </div>
@@ -90,31 +90,28 @@
 
     <!-- 3. 版权信息与底部认证 -->
     <div class="w copyright">
-        <p class="links">
-          <a href="#">关于我们</a><span class="sep">|</span>
-          <a href="#">联系我们</a><span class="sep">|</span>
-          <a href="#">联系客服</a><span class="sep">|</span>
-          <a href="#">合作招商</a><span class="sep">|</span>
-          <a href="#">商家帮助</a><span class="sep">|</span>
-          <a href="#">营销中心</a><span class="sep">|</span>
-          <a href="#">手机京东</a><span class="sep">|</span>
-          <a href="#">友情链接</a><span class="sep">|</span>
-          <a href="#">销售联盟</a><span class="sep">|</span>
-          <a href="#">京东社区</a><span class="sep">|</span>
-          <a href="#">风险监测</a><span class="sep">|</span>
-          <a href="#">隐私政策</a><span class="sep">|</span>
-          <a href="#">京东公益</a><span class="sep">|</span>
-          <a href="#">Media & IR</a>
-        </p>
-        <p class="info-text">
-          京公网安备 11000002000088号 <span class="sep">|</span> 京ICP备11041704号 <span class="sep">|</span> ICP <span class="sep">|</span> 互联网药品信息服务资格证编号(京)-经营性-2014-0008 <span class="sep">|</span> 新出发京零 字第大120007号
-        </p>
-        <p class="info-text">
-          互联网出版许可证编号新出网证(京)字150号 <span class="sep">|</span> 出版物经营许可证 <span class="sep">|</span> 网络文化经营许可证京网文[2020]6112-1201号 <span class="sep">|</span> 违法和不良信息举报电话:4006561155
-        </p>
-        <p class="info-text">
-          Copyright © 2004 - 2026 京东JD.COM 版权所有 <span class="sep">|</span> 消费者维权热线:4006067733 <span class="sep">|</span> 经营证照 <span class="sep">|</span> (京)网械平台备字(2018)第00003号 <span class="sep">|</span> 营业执照 <span class="sep">|</span> 增值电信业务经营许可证
-        </p>
+      <p class="links">
+        <a href="#">关于我们</a><span class="sep">|</span> <a href="#">联系我们</a><span class="sep">|</span> <a href="#">联系客服</a
+        ><span class="sep">|</span> <a href="#">合作招商</a><span class="sep">|</span> <a href="#">商家帮助</a><span class="sep">|</span>
+        <a href="#">营销中心</a><span class="sep">|</span> <a href="#">手机京东</a><span class="sep">|</span> <a href="#">友情链接</a
+        ><span class="sep">|</span> <a href="#">销售联盟</a><span class="sep">|</span> <a href="#">京东社区</a><span class="sep">|</span>
+        <a href="#">风险监测</a><span class="sep">|</span> <a href="#">隐私政策</a><span class="sep">|</span> <a href="#">京东公益</a
+        ><span class="sep">|</span>
+        <a href="#">Media & IR</a>
+      </p>
+      <p class="info-text">
+        京公网安备 11000002000088号 <span class="sep">|</span> 京ICP备11041704号 <span class="sep">|</span> ICP
+        <span class="sep">|</span> 互联网药品信息服务资格证编号(京)-经营性-2014-0008 <span class="sep">|</span> 新出发京零 字第大120007号
+      </p>
+      <p class="info-text">
+        互联网出版许可证编号新出网证(京)字150号 <span class="sep">|</span> 出版物经营许可证
+        <span class="sep">|</span> 网络文化经营许可证京网文[2020]6112-1201号 <span class="sep">|</span> 违法和不良信息举报电话:4006561155
+      </p>
+      <p class="info-text">
+        Copyright © 2004 - 2026 京东JD.COM 版权所有 <span class="sep">|</span> 消费者维权热线:4006067733 <span class="sep">|</span> 经营证照
+        <span class="sep">|</span> (京)网械平台备字(2018)第00003号 <span class="sep">|</span> 营业执照
+        <span class="sep">|</span> 增值电信业务经营许可证
+      </p>
     </div>
   </div>
 </template>
@@ -162,7 +159,7 @@
   z-index: 2;
   font-size: 18px;
   font-weight: 800;
-  color: #E1251B;
+  color: #e1251b;
 }
 .s-desc {
   font-size: 18px;
@@ -201,7 +198,7 @@
   transition: color 0.2s;
 }
 .help-links dd a:hover {
-  color: #E1251B;
+  color: #e1251b;
 }
 
 /* copyright */
@@ -219,7 +216,7 @@
   transition: color 0.2s;
 }
 .copyright .links a:hover {
-  color: #E1251B;
+  color: #e1251b;
 }
 .copyright .sep {
   margin: 0 10px;

+ 57 - 18
src/views/home/jdcomponents/JDHeader.vue

@@ -1,51 +1,63 @@
 <template>
   <div class="jd-header">
     <!-- 顶部窄条 -->
-    <!-- <div class="header-top">
+    <div class="header-top">
       <div class="w flex-between">
         <div class="loc-wrap">
-          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px; margin-right: 2px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
+          <svg
+            width="12"
+            height="12"
+            viewBox="0 0 24 24"
+            fill="none"
+            stroke="currentColor"
+            stroke-width="2"
+            style="vertical-align: -2px; margin-right: 2px"
+          >
+            <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
+            <circle cx="12" cy="10" r="3"></circle>
+          </svg>
           湖北
         </div>
         <ul class="top-nav-links flex">
-          <li>我的订单</li>
+          <li @click="onPath('/order/orderManage')">我的订单</li>
           <li class="spacer"></li>
-          <li>我的工作台</li>
+          <li @click="onPath('/enterprise/companyInfo')">我的工作台</li>
           <li class="spacer"></li>
-          <li>企业会员</li>
+          <li @click="onPath('/enterprise/companyInfo')">企业会员</li>
           <li class="spacer"></li>
           <li class="red">企业采购</li>
           <li class="spacer"></li>
           <li>客户服务</li>
           <li class="spacer"></li>
-          <li class="hotline">咨询热线 400-028-0000</li>
+          <li class="hotline">咨询热线 {{ servicePhone }}</li>
         </ul>
       </div>
-    </div> -->
+    </div>
 
     <!-- 中间搜索行 -->
     <div :class="['header-mid-wrap', { 'is-fixed': isFixed }]">
       <div class="header-mid w flex">
         <div class="logo-box">
-          <div class="logo-text">优易企业购</div>
-          <p class="logo-desc">省钱 · 省心 · 省时间</p>
+          <div class="logo-text" :style="{ color: config.themeColor }">{{ config.mainTitle }}</div>
+          <p class="logo-desc">{{ config.subTitle }}</p>
         </div>
 
         <div class="search-box-wrap flex-1">
-          <div class="search-bar flex">
+          <div class="search-bar flex" :style="{ borderColor: config.themeColor }">
             <div class="input-group flex-1 flex">
-              <input type="text" placeholder="企业端午福利" />
+              <input v-model="input" type="text" :placeholder="config.placeholderText" />
             </div>
-            <button class="search-btn">搜 索</button>
+            <button class="search-btn" :style="{ backgroundColor: config.themeColor }" @click="onPath('/search?type=1&input=' + input)">搜 索</button>
           </div>
           <div class="hot-links">
-            <span>企业权益不止5折</span>
-            <span>定制礼盒低至83折</span>
+            <span @click="onPath(item.link)" v-for="(item, index) in config.hotWordList" :key="index">{{ item.name }}</span>
           </div>
         </div>
 
         <div class="header-actions">
-          <div class="btn-keeper">Ai 采购管家</div>
+          <div :style="{ borderColor: config.themeColor }" class="btn-keeper" @click="onPath(config.rightBtnLink)">
+            <img v-if="config.rightBtnIcon" :src="config.rightBtnIcon" alt="" />{{ config.rightBtnText }}
+          </div>
         </div>
       </div>
     </div>
@@ -54,9 +66,29 @@
   </div>
 </template>
 
-<script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
+<script setup lang="ts">
+import { currentSearchConfig } from '@/api/home/index-enterprise';
+import { getPlatformConfigList } from '@/api/breg/index';
+import { onPath } from '@/utils/siteConfig';
+const input = ref<any>('');
+const servicePhone = ref<any>('');
+const config = ref<any>({
+  hotWordList: []
+});
+
+currentSearchConfig({}).then((res) => {
+  if (res.code == 200) {
+    config.value = res.data;
+  }
+});
 
+getPlatformConfigList({ configKey: 'servicePhone' }).then((res) => {
+  if (res.code == 200) {
+    if (res.rows && res.rows.length > 0) {
+      servicePhone.value = res.rows[0].value;
+    }
+  }
+});
 const isFixed = ref(false);
 
 const handleScroll = () => {
@@ -72,7 +104,7 @@ onUnmounted(() => {
 });
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 .jd-header {
   background: #fff;
 }
@@ -237,6 +269,11 @@ onUnmounted(() => {
 .header-actions {
   margin-left: 30px;
   flex-shrink: 0;
+  img {
+    height: 16px;
+    width: 16px;
+    margin-right: 4px;
+  }
 }
 .btn-keeper {
   height: 40px;
@@ -251,6 +288,8 @@ onUnmounted(() => {
   cursor: pointer;
   transition: all 0.2s;
   box-sizing: border-box;
+  display: flex;
+  align-items: center;
 }
 .btn-keeper:hover {
   background: #fef0f0;

+ 414 - 116
src/views/home/jdcomponents/JDProducts.vue

@@ -3,29 +3,41 @@
     <!-- 顶部滑动的商品分类导航 -->
     <div class="cat-nav-wrapper">
       <div class="more-btn prev-btn" v-show="showPrev" @click="slide(-300)">
-        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
+        <svg
+          width="12"
+          height="12"
+          viewBox="0 0 24 24"
+          fill="none"
+          stroke="currentColor"
+          stroke-width="3"
+          stroke-linecap="round"
+          stroke-linejoin="round"
+        >
           <polyline points="15 18 9 12 15 6"></polyline>
         </svg>
       </div>
 
       <div class="cat-nav-list" ref="navListRef" @scroll="checkScroll">
-        <div
-          class="cat-item"
-          v-for="(cat, i) in categories"
-          :key="i"
-          :class="{ active: currentCat === i }"
-          @click="currentCat = i"
-        >
-          <img :src="`/icons/icon_${cat.icon}.svg`" class="cat-img" />
+        <div class="cat-item" v-for="(cat, i) in categories" :key="i" :class="{ active: currentCat === i }" @click="onCurrentCat(i)">
+          <img :src="cat.iconUrl" class="cat-img" />
           <div class="cat-text">
-            <span class="c-t">{{ cat.title }}</span>
-            <span class="c-s">{{ cat.sub }}</span>
+            <span class="c-t">{{ cat.name }}</span>
+            <span class="c-s">{{ cat.subTitle }}</span>
           </div>
         </div>
       </div>
 
       <div class="more-btn next-btn" v-show="showNext" @click="slide(300)">
-        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
+        <svg
+          width="12"
+          height="12"
+          viewBox="0 0 24 24"
+          fill="none"
+          stroke="currentColor"
+          stroke-width="3"
+          stroke-linecap="round"
+          stroke-linejoin="round"
+        >
           <polyline points="9 18 15 12 9 6"></polyline>
         </svg>
       </div>
@@ -33,88 +45,164 @@
 
     <!-- 商品列表 -->
     <div class="product-grid">
-      <div class="p-card" v-for="(p, i) in productsList" :key="i">
+      <div class="p-card" v-for="(p, i) in baseProducts" :key="i">
         <div class="p-img-box">
-          <img :src="p.img" @error="handleImgError" />
+          <img :src="p.image || p.productImage" @error="handleImgError" />
         </div>
         <div class="p-info">
           <div class="p-name">
-            <span class="tag-zy">自营</span>
-            {{ p.name }}
+            <span class="tag-zy">{{ p.isSelf == 1 ? '自营' : '非自营' }}</span>
+            {{ p.name || p.itemName || '' }}
           </div>
           <div class="p-price-row">
             <span class="p-currency">¥</span>
-            <span class="p-price">{{ p.price }}</span>
-            <span class="p-decimal" v-if="p.dec !== '00'">.{{ p.dec }}</span>
+            <template v-if="p.price">
+              <span class="p-price">{{ p.price.split('.')[0] }}</span>
+              <span class="p-decimal">.{{ p.price.split('.')[1] }}</span>
+            </template>
+            <template v-if="p.memberPrice">
+              <span class="p-price">{{ p.memberPrice.split('.')[0] }}</span>
+              <span class="p-decimal">.{{ p.memberPrice.split('.')[1] }}</span>
+            </template>
             <span class="tag-ep">企业价</span>
           </div>
           <div class="p-actions">
-            <button class="btn-cart-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg></button>
-            <button class="btn-buy">
-              <span class="txt-normal">加入采购清单</span>
-              <span class="txt-hover">注册企业用户可享</span>
+            <button class="btn-cart-icon" @click.stop="onCart(p)">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <circle cx="9" cy="21" r="1"></circle>
+                <circle cx="20" cy="21" r="1"></circle>
+                <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
+              </svg>
+            </button>
+            <button class="btn-buy" @click="onPath('/item?id=' + p.id + '&productNo=' + p.productNo)">
+              <span class="txt-normal">加入购物车</span>
+              <span class="txt-hover">加入购物车</span>
             </button>
           </div>
         </div>
       </div>
+
+      <!-- 加载更多提示 -->
+      <div v-if="loading" class="loading-more">
+        <el-icon class="is-loading"><Loading /></el-icon>
+        <span>加载中...</span>
+      </div>
+      <div v-if="noMore && baseProducts.length > 0" class="no-more">没有更多了</div>
+    </div>
+    <div class="empty-bos" v-if="baseProducts.length === 0">
+      <el-empty description="暂无数据" />
     </div>
   </div>
 </template>
 
-<script setup>
-import { ref, onMounted, computed } from 'vue';
-
-const categories = [
-  { title: '为你推荐', sub: '每日低价 领券', icon: '1' },
-  { title: '新客专享', sub: '精选爆品 低价', icon: '2' },
-  { title: '办公设备', sub: '电脑设备 批量', icon: '12' },
-  { title: '办公耗材', sub: '低价直供 囤货', icon: '13' },
-  { title: '工业生产', sub: '车间工具 规格', icon: '3' },
-  { title: '员工福利', sub: '节日礼盒 一键', icon: '10' },
-  { title: '办公环境', sub: '电器家具 焕新', icon: '7' },
-  { title: '营销礼赠', sub: '客户礼包 特惠', icon: '8' },
-  { title: '员工餐饮', sub: '茶水间 零食', icon: '5' },
-  { title: '个人防护', sub: '劳保用品 齐全', icon: '6' },
-  { title: '企业服务', sub: '工商注册 财税', icon: '4' },
-  { title: '包装耗材', sub: '纸箱胶带 批采', icon: '11' },
-  { title: '商用家电', sub: '空调冰洗 采购', icon: '9' },
-  { title: '安防监控', sub: '门禁摄像头 全', icon: '14' },
-  { title: '五金建材', sub: '管件工具 直供', icon: '15' }
-];
-
-const baseProducts = [
-  { name: '佛山照明 (FSL) LED投光灯 led户外灯室外防水大功率工程广告牌泛光灯', price: '185', dec: '27', img: '@/assets/jd/prod_light.png' },
-  { name: '初工 门锁卧室门锁室内门锁可调节通用门锁免改孔门锁 双孔可调孔距', price: '49', dec: '90', img: '@/assets/jd/prod_lock.png' },
-  { name: '潘洁擦机布 棉布工业擦机抹布 10KG(20斤)吸水吸油抹布40*60cm', price: '113', dec: '00', img: '@/assets/jd/prod_rags.png' },
-  { name: '德力西电气 (DELIXI ELECTRIC) 硬质合金开孔器不锈钢钻头扩孔', price: '18', dec: '79', img: '@/assets/jd/prod_switch.png' },
-  { name: '世达 (SATA) 150件小飞中飞大飞棘轮扳手套筒综合套装工具箱09510', price: '2025', dec: '78', img: '@/assets/jd/prod_toolset.png' },
-  { name: '普达防毒面具全面罩自吸过滤式防毒农药化工防甲苯醋酸等有机气体', price: '116', dec: '60', img: '@/assets/jd/prod_mask.png' },
-  { name: '华为平板电脑 MatePad 11.5英寸 120Hz高刷全面屏 8+128GB WIFI版', price: '1499', dec: '00', img: '@/assets/jd/prod_it.png' },
-  { name: '惠普(HP)打印机 激光打印机 办公商用家用黑白打印机', price: '899', dec: '00', img: '@/assets/jd/prod_office.png' },
-  { name: '福临门 食用油 精炼一级大豆油 5升*4桶/箱(企业专享)', price: '259', dec: '00', img: '@/assets/jd/prod_office.png' },
-  { name: '得力(deli) A4复印纸 70g 500张/包 5包/箱 办公打印纸', price: '129', dec: '00', img: '@/assets/jd/prod_rags.png' },
-  { name: '智能门禁系统 考勤机 打卡机 刷脸指纹密码识别', price: '399', dec: '00', img: '@/assets/jd/prod_lock.png' },
-  { name: '工业级大功率风扇 车间用强力落地扇 降温通风', price: '288', dec: '00', img: '@/assets/jd/prod_light.png' }
-];
-
-baseProducts.forEach(p => {
-  if (!p.img) p.img = '@/assets/jd/prod_office.png';
-});
-
-// 生成更多数据用于测试吸顶
-const productsList = computed(() => {
-  let list = [];
-  for(let i=0; i<3; i++) { // 36 items
-    list = list.concat(baseProducts.map(p => ({...p, id: i + '_' + p.name})));
+<script setup name="Index" lang="ts">
+import { recommendThemeConfig } from '@/api/home/index-enterprise';
+import { onPath } from '@/utils/siteConfig';
+import { addProductShoppingCart } from '@/api/goods/index';
+import { getPcProductPage } from '@/api/search/index';
+import { Loading } from '@element-plus/icons-vue';
+
+const categories = ref<any>([]);
+const baseProducts = ref<any>([]);
+const loading = ref(false);
+const noMore = ref(false);
+
+recommendThemeConfig({}).then((res) => {
+  if (res.code == 200) {
+    categories.value = res.data.categoryConfigList;
+    categories.value.forEach((item: any) => {
+      item.goodsList = [];
+      if (item.selectedProductIds) {
+        item.goodsList = JSON.parse(item.selectedProductIds);
+      }
+    });
+    if (categories.value[0].categoryPath) {
+      categories.value[0].pageNum = 1;
+      categories.value[0].pageSize = 20;
+      categories.value[0].topCategoryId = '';
+      categories.value[0].mediumCategoryId = '';
+      categories.value[0].bottomCategoryId = '';
+      const categoryList = categories.value[0].categoryPath.split(',');
+      if (categoryList.length > 0) {
+        categories.value[0].topCategoryId = categoryList[0];
+      }
+      if (categoryList.length > 1) {
+        categories.value[0].mediumCategoryId = categoryList[1];
+      }
+      if (categoryList.length > 2) {
+        categories.value[0].bottomCategoryId = categoryList[2];
+      }
+      baseProducts.value = [];
+      getList();
+    } else {
+      baseProducts.value = categories.value[0].goodsList;
+    }
   }
-  return list;
 });
 
-const currentCat = ref(0);
+const currentCat = ref<any>(0);
 const navListRef = ref(null);
 const showPrev = ref(false);
 const showNext = ref(true);
 
+const onCurrentCat = (index: any) => {
+  currentCat.value = index;
+  // 重置加载状态
+  noMore.value = false;
+  if (categories.value[index].categoryPath) {
+    categories.value[index].pageNum = 1;
+    categories.value[index].pageSize = 20;
+    categories.value[index].topCategoryId = '';
+    categories.value[index].mediumCategoryId = '';
+    categories.value[index].bottomCategoryId = '';
+    const categoryList = categories.value[index].categoryPath.split(',');
+    if (categoryList.length > 0) {
+      categories.value[index].topCategoryId = categoryList[0];
+    }
+    if (categoryList.length > 1) {
+      categories.value[index].mediumCategoryId = categoryList[1];
+    }
+    if (categoryList.length > 2) {
+      categories.value[index].bottomCategoryId = categoryList[2];
+    }
+    baseProducts.value = [];
+    getList();
+  } else {
+    baseProducts.value = categories.value[index].goodsList;
+  }
+};
+
+const getList = () => {
+  if (loading.value || noMore.value) return;
+
+  loading.value = true;
+  const datas = {
+    pageNum: categories.value[currentCat.value].pageNum,
+    pageSize: categories.value[currentCat.value].pageSize,
+    topCategoryId: categories.value[currentCat.value].topCategoryId,
+    mediumCategoryId: categories.value[currentCat.value].mediumCategoryId,
+    bottomCategoryId: categories.value[currentCat.value].bottomCategoryId
+  };
+  getPcProductPage(datas)
+    .then((res) => {
+      if (res.code == 200) {
+        const newProducts = res.rows || [];
+        baseProducts.value = baseProducts.value.concat(newProducts);
+
+        // 判断是否还有更多数据
+        if (res.total <= baseProducts.value.length) {
+          noMore.value = true;
+        } else {
+          // 还有更多数据,页码+1
+          categories.value[currentCat.value].pageNum++;
+        }
+      }
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+};
+
 const checkScroll = () => {
   if (!navListRef.value) return;
   const { scrollLeft, scrollWidth, clientWidth } = navListRef.value;
@@ -128,20 +216,60 @@ const slide = (amount) => {
   }
 };
 
-const defaultImg = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400"><rect width="100%" height="100%" fill="%23f4f4f4"/><text x="50%" y="50%" font-family="sans-serif" font-size="20" fill="%23ccc" dominant-baseline="middle" text-anchor="middle">暂无图片</text></svg>';
+const defaultImg =
+  'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400"><rect width="100%" height="100%" fill="%23f4f4f4"/><text x="50%" y="50%" font-family="sans-serif" font-size="20" fill="%23ccc" dominant-baseline="middle" text-anchor="middle">暂无图片</text></svg>';
 
 const handleImgError = (e) => {
   e.target.src = defaultImg;
   e.target.onerror = null;
 };
 
+import { cartStore } from '@/store/modules/cart';
+const cart = cartStore();
+//加入购物车
+const onCart = (row: any) => {
+  addProductShoppingCart({
+    productId: row.id,
+    productNum: row.minOrderQuantity
+  }).then((res) => {
+    if (res.code == 200) {
+      cart.onCartCount();
+      ElMessage.success('加入购物车成功');
+    }
+  });
+};
+
+// 监听整个页面滚动
+const handlePageScroll = () => {
+  const { scrollTop, scrollHeight, clientHeight } = document.documentElement || document.body;
+
+  // 距离底部100px时触发加载
+  if (scrollHeight - scrollTop - clientHeight < 100) {
+    if (categories.value[currentCat.value] && categories.value[currentCat.value].categoryPath) {
+      getList();
+    }
+  }
+};
+
 onMounted(() => {
   setTimeout(checkScroll, 100);
+  // 添加页面滚动监听
+  window.addEventListener('scroll', handlePageScroll);
+});
+
+onUnmounted(() => {
+  // 移除页面滚动监听
+  window.removeEventListener('scroll', handlePageScroll);
 });
 </script>
 
 <style scoped>
-.products-section { margin-top: 24px; background: #fff; border-radius: 12px; margin-bottom: 50px; }
+.products-section {
+  margin-top: 24px;
+  background: #fff;
+  border-radius: 12px;
+  margin-bottom: 50px;
+}
 
 /* 导航栏 (吸顶) */
 .cat-nav-wrapper {
@@ -165,7 +293,9 @@ onMounted(() => {
   flex: 1;
   padding: 16px 0;
 }
-.cat-nav-list::-webkit-scrollbar { display: none; }
+.cat-nav-list::-webkit-scrollbar {
+  display: none;
+}
 
 .cat-item {
   display: flex;
@@ -177,25 +307,69 @@ onMounted(() => {
   margin-right: 8px;
   transition: all 0.2s;
 }
-.cat-item:hover { background: #f9f9f9; }
-.cat-item.active .c-t, .cat-item.active .c-s { color: #E1251B; }
+.cat-item:hover {
+  background: #f9f9f9;
+}
+.cat-item.active .c-t,
+.cat-item.active .c-s {
+  color: #e1251b;
+}
 
-.cat-img { width: 32px; height: 32px; margin-right: 12px; border-radius: 50%; }
+.cat-img {
+  width: 32px;
+  height: 32px;
+  margin-right: 12px;
+  border-radius: 50%;
+}
 
-.cat-text { display: flex; flex-direction: column; }
-.c-t { font-size: 15px; font-weight: bold; color: #333; margin-bottom: 2px; }
-.c-s { font-size: 12px; color: #999; }
+.cat-text {
+  display: flex;
+  flex-direction: column;
+}
+.c-t {
+  font-size: 15px;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 2px;
+}
+.c-s {
+  font-size: 12px;
+  color: #999;
+}
 
 /* 左右箭头 */
 .more-btn {
-  width: 28px; height: 28px; border-radius: 50%;
-  background: #fff; border: 1px solid #eee; display: flex; align-items: center; justify-content: center;
-  cursor: pointer; color: #666; font-size: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.05);
+  width: 28px;
+  height: 28px;
+  border-radius: 50%;
+  background: #fff;
+  border: 1px solid #eee;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  color: #666;
+  font-size: 14px;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+}
+.more-btn:hover {
+  background: #f4f4f4;
+}
+.prev-btn {
+  position: absolute;
+  left: 10px;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 10;
+}
+.next-btn {
+  position: absolute;
+  right: 10px;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 10;
+  margin-left: 0;
 }
-.more-btn:hover { background: #f4f4f4; }
-.prev-btn { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); z-index: 10; }
-.next-btn { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); z-index: 10; margin-left: 0; }
-
 
 /* 商品网格 */
 .product-grid {
@@ -203,9 +377,21 @@ onMounted(() => {
   gap: 12px;
   padding: 0 20px 20px 20px;
 }
-@media screen and (min-width: 1600px) { .product-grid { grid-template-columns: repeat(6, 1fr); } }
-@media screen and (min-width: 1210px) and (max-width: 1599px) { .product-grid { grid-template-columns: repeat(5, 1fr); } }
-@media screen and (max-width: 1209px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
+@media screen and (min-width: 1600px) {
+  .product-grid {
+    grid-template-columns: repeat(6, 1fr);
+  }
+}
+@media screen and (min-width: 1210px) and (max-width: 1599px) {
+  .product-grid {
+    grid-template-columns: repeat(5, 1fr);
+  }
+}
+@media screen and (max-width: 1209px) {
+  .product-grid {
+    grid-template-columns: repeat(4, 1fr);
+  }
+}
 
 /* 商品卡片 */
 .p-card {
@@ -218,7 +404,10 @@ onMounted(() => {
   flex-direction: column;
   padding: 12px;
 }
-.p-card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
+.p-card:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+}
 
 .p-img-box {
   width: 100%;
@@ -229,21 +418,34 @@ onMounted(() => {
   margin-bottom: 12px;
   position: relative;
 }
-.p-img-box img { width: 100%; height: 100%; object-fit: cover; }
+.p-img-box img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
 
 /* 悬停时的透明遮罩 */
 .p-img-box::after {
   content: '';
   position: absolute;
-  top: 0; left: 0; right: 0; bottom: 0;
-  background: rgba(0,0,0,0.04);
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.04);
   opacity: 0;
   transition: opacity 0.2s;
   pointer-events: none;
 }
-.p-card:hover .p-img-box::after { opacity: 1; }
+.p-card:hover .p-img-box::after {
+  opacity: 1;
+}
 
-.p-info { flex: 1; display: flex; flex-direction: column; }
+.p-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
 .p-name {
   font-size: 13px;
   line-height: 1.4;
@@ -255,42 +457,138 @@ onMounted(() => {
   color: #333;
   margin-bottom: 10px;
 }
-.tag-zy { background: #E1251B; color: #fff; padding: 0 4px; border-radius: 2px; font-size: 11px; margin-right: 4px; vertical-align: baseline; display: inline-block; }
+.tag-zy {
+  background: #e1251b;
+  color: #fff;
+  padding: 0 4px;
+  border-radius: 2px;
+  font-size: 11px;
+  margin-right: 4px;
+  vertical-align: baseline;
+  display: inline-block;
+}
 
-.p-price-row { color: #E1251B; display: flex; align-items: baseline; margin-bottom: 12px; margin-top: auto; }
-.p-currency { font-size: 12px; font-weight: bold; }
-.p-price { font-size: 20px; font-weight: 800; font-family: tahoma,arial,Microsoft YaHei,Hiragino Sans GB,u5b8bu4f53,sans-serif; }
-.p-decimal { font-size: 12px; font-weight: bold; margin-right: 6px; }
-.tag-ep { font-size: 11px; color: #187AF2; border: 1px solid #187AF2; border-radius: 2px; padding: 0 4px; margin-left: 6px;}
+.p-price-row {
+  color: #e1251b;
+  display: flex;
+  align-items: baseline;
+  margin-bottom: 12px;
+  margin-top: auto;
+}
+.p-currency {
+  font-size: 12px;
+  font-weight: bold;
+}
+.p-price {
+  font-size: 20px;
+  font-weight: 800;
+  font-family:
+    tahoma,
+    arial,
+    Microsoft YaHei,
+    Hiragino Sans GB,
+    u5b8bu4f53,
+    sans-serif;
+}
+.p-decimal {
+  font-size: 12px;
+  font-weight: bold;
+  margin-right: 6px;
+}
+.tag-ep {
+  font-size: 11px;
+  color: #187af2;
+  border: 1px solid #187af2;
+  border-radius: 2px;
+  padding: 0 4px;
+  margin-left: 6px;
+}
 
-.p-actions { display: flex; gap: 8px; }
+.p-actions {
+  display: flex;
+  gap: 8px;
+}
 .btn-cart-icon {
-  width: 32px; height: 32px;
-  border: 1px solid #e0e0e0; background: #fff; border-radius: 4px;
-  color: #666; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; flex-shrink: 0;
+  width: 32px;
+  height: 32px;
+  border: 1px solid #e0e0e0;
+  background: #fff;
+  border-radius: 4px;
+  color: #666;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  transition: all 0.2s;
+  flex-shrink: 0;
+}
+.btn-cart-icon:hover {
+  border-color: #e1251b;
+  color: #e1251b;
 }
-.btn-cart-icon:hover { border-color: #E1251B; color: #E1251B; }
 
 .btn-buy {
-  flex: 1; height: 32px;
-  border: 1px solid #e0e0e0; background: #fff; border-radius: 4px;
-  color: #666; font-size: 12px; cursor: pointer; transition: all 0.2s;
-  display: flex; align-items: center; justify-content: center;
+  flex: 1;
+  height: 32px;
+  border: 1px solid #e0e0e0;
+  background: #fff;
+  border-radius: 4px;
+  color: #666;
+  font-size: 12px;
+  cursor: pointer;
+  transition: all 0.2s;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.txt-hover {
+  display: none;
+  font-weight: bold;
 }
-.txt-hover { display: none; font-weight: bold; }
 
 /* 悬停卡片时,按钮直接变为红色,文字改变 */
 .p-card:hover .btn-buy {
-  background: #E1251B;
-  border-color: #E1251B;
+  background: #e1251b;
+  border-color: #e1251b;
   color: #fff;
 }
-.p-card:hover .txt-normal { display: none; }
-.p-card:hover .txt-hover { display: inline; }
+.p-card:hover .txt-normal {
+  display: none;
+}
+.p-card:hover .txt-hover {
+  display: inline;
+}
 
 /* 鼠标悬停在按钮本身时,保持红色 */
 .p-card .btn-buy:hover {
-  background: #C81623;
-  border-color: #C81623;
+  background: #c81623;
+  border-color: #c81623;
+}
+
+.empty-bos {
+  width: 100%;
+  background-color: #ffffff;
+  border-radius: 10px;
+  margin-bottom: 20px;
+}
+
+/* 加载更多提示 */
+.loading-more {
+  grid-column: 1 / -1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+  color: #999;
+  font-size: 14px;
+  gap: 8px;
+}
+
+.no-more {
+  grid-column: 1 / -1;
+  text-align: center;
+  padding: 20px;
+  color: #999;
+  font-size: 14px;
 }
 </style>

+ 121 - 40
src/views/home/jdcomponents/JDScene.vue

@@ -1,32 +1,30 @@
 <template>
   <div class="scene-integrated w">
-    <div class="scene-inner flex">
+    <div class="scene-inner flex" :style="{ background: dataInfo.themeColor }">
       <!-- 左侧标题区 -->
       <div class="scene-left">
         <div class="s-h-group flex">
-          <h3 class="s-main-t">场景解决方案</h3>
-          <span class="s-sub-t">一站全买齐</span>
+          <h3 class="s-main-t">{{ dataInfo.mainTitle }}</h3>
+          <span class="s-sub-t">{{ dataInfo.subTitle }}</span>
         </div>
-        <a href="#" class="enter-pill">
-          进入全场景 <i class="arr-ic"></i>
-        </a>
+        <div class="enter-pill" @click="onPath(dataInfo.jumpLink)">{{ dataInfo.btnText }} <i class="arr-ic"></i></div>
       </div>
 
       <!-- 右侧卡片区 -->
       <div class="scene-right flex-1">
         <div class="s-grid-h flex">
-          <div class="s-item-card" v-for="(s, i) in scenes" :key="i">
+          <div class="s-item-card" :style="{ background: s.bgColor, opacity: s.opacity }" v-for="(s, i) in dataInfo.cardList" :key="i">
             <div class="item-top flex-between">
               <div class="item-info flex">
-                <p class="item-t">{{ s.title }}</p>
-                <p class="item-d">{{ s.desc }}</p>
+                <p class="item-t" :style="{ color: s.titleColor }">{{ s.title }}</p>
+                <p class="item-d" :style="{ color: s.subTitleColor }">{{ s.subTitle }}</p>
               </div>
               <div class="item-arrow">
                 <i class="icon-arr"></i>
               </div>
             </div>
             <div class="item-pic">
-              <img :src="s.img" />
+              <el-image class="item-img" :src="s.imageUrl" />
             </div>
           </div>
         </div>
@@ -35,7 +33,17 @@
   </div>
 </template>
 
-<script setup>
+<script setup name="Index" lang="ts">
+import { currentScenarioGlobalSetting } from '@/api/home/index-enterprise';
+import { onPath } from '@/utils/siteConfig';
+const dataInfo = ref<any>({
+  cardList: []
+});
+currentScenarioGlobalSetting({}).then((res) => {
+  if (res.code == 200) {
+    dataInfo.value = res.data;
+  }
+});
 const scenes = [
   { title: '耗材采购', desc: '纸墨随买随用', img: '@/assets/jd/scene_stationery.png' },
   { title: '防暑降温', desc: '守护员工健康', img: '@/assets/jd/scene_cooling.png' },
@@ -45,9 +53,11 @@ const scenes = [
 </script>
 
 <style scoped>
-.scene-integrated { margin-top: 15px; }
+.scene-integrated {
+  margin-top: 15px;
+}
 .scene-inner {
-  background: linear-gradient(90deg, #6BE7B5 0%, #39D696 100%);
+  background: linear-gradient(90deg, #6be7b5 0%, #39d696 100%);
   border-radius: 12px;
   padding: 14px 20px;
   align-items: center;
@@ -59,38 +69,66 @@ const scenes = [
   color: #fff;
   padding-right: 20px;
 }
-.s-h-group { align-items: baseline; margin-bottom: 15px; }
-.s-main-t { font-size: 24px; font-weight: 800; white-space: nowrap; }
-.s-sub-t { font-size: 24px; font-weight: 800; opacity: 0.95; margin-left: 12px; white-space: nowrap; }
+.s-h-group {
+  align-items: baseline;
+  margin-bottom: 15px;
+}
+.s-main-t {
+  font-size: 24px;
+  font-weight: 800;
+  white-space: nowrap;
+}
+.s-sub-t {
+  font-size: 24px;
+  font-weight: 800;
+  opacity: 0.95;
+  margin-left: 12px;
+  white-space: nowrap;
+}
 
 .enter-pill {
   display: inline-flex;
   align-items: center;
   background: #fff;
-  color: #E1251B;
+  color: #e1251b;
   padding: 6px 18px;
   border-radius: 20px;
   font-size: 13px;
   font-weight: bold;
   text-decoration: none;
+  cursor: pointer;
 }
 .arr-ic {
-  display: inline-block; width: 12px; height: 12px;
-  background: #E1251B; border-radius: 50%; margin-left: 6px;
+  display: inline-block;
+  width: 12px;
+  height: 12px;
+  background: #e1251b;
+  border-radius: 50%;
+  margin-left: 6px;
   position: relative;
 }
 .arr-ic::after {
-  content: ''; position: absolute; left: 3px; top: 3px;
-  width: 4px; height: 4px; border-top: 2px solid #fff; border-right: 2px solid #fff;
+  content: '';
+  position: absolute;
+  left: 3px;
+  top: 3px;
+  width: 4px;
+  height: 4px;
+  border-top: 2px solid #fff;
+  border-right: 2px solid #fff;
   transform: rotate(45deg);
 }
 
-.scene-right { overflow: hidden; }
-.s-grid-h { gap: 10px; }
+.scene-right {
+  overflow: hidden;
+}
+.s-grid-h {
+  gap: 10px;
+}
 
 .s-item-card {
   flex: 1;
-  background: #E8FBF1; /* Pale green background matching Figure 2 */
+  background: #e8fbf1; /* Pale green background matching Figure 2 */
   border-radius: 10px;
   height: 130px;
   display: flex;
@@ -98,17 +136,37 @@ const scenes = [
   overflow: hidden;
   transition: transform 0.3s;
 }
-.s-item-card:hover { transform: translateY(-3px); }
+.s-item-card:hover {
+  transform: translateY(-3px);
+}
 
-.item-top { padding: 8px 12px; align-items: center; }
-.item-info { align-items: baseline; gap: 8px; }
-.item-t { font-size: 18px; font-weight: 900; color: #026034; white-space: nowrap; }
-.item-d { font-size: 13px; font-weight: 600; color: #111; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.item-top {
+  padding: 8px 12px;
+  align-items: center;
+}
+.item-info {
+  align-items: baseline;
+  gap: 8px;
+}
+.item-t {
+  font-size: 18px;
+  font-weight: 900;
+  color: #026034;
+  white-space: nowrap;
+}
+.item-d {
+  font-size: 13px;
+  font-weight: 600;
+  color: #111;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
 
 .item-arrow {
   width: 16px;
   height: 16px;
-  background: #34C759;
+  background: #34c759;
   border-radius: 50%;
   display: flex;
   align-items: center;
@@ -116,22 +174,45 @@ const scenes = [
   flex-shrink: 0;
 }
 .icon-arr {
-  width: 4px; height: 4px;
-  border-top: 2px solid #fff; border-right: 2px solid #fff;
-  transform: rotate(45deg); margin-left: -1px;
+  width: 4px;
+  height: 4px;
+  border-top: 2px solid #fff;
+  border-right: 2px solid #fff;
+  transform: rotate(45deg);
+  margin-left: -1px;
 }
 
-.item-pic { flex: 1; overflow: hidden; padding: 0 8px 8px; }
-.item-pic img { width: 100%; height: 100%; object-fit: cover; border-radius: 6px; }
+.item-pic {
+  flex: 1;
+  overflow: hidden;
+  padding: 0 8px 8px;
+}
+.item-img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  border-radius: 6px;
+}
 
 @media screen and (max-width: 1599px) {
-  .scene-left { width: 340px; }
-  .s-main-t { font-size: 20px; }
-  .s-sub-t { font-size: 20px; font-weight: 800; }
-  .s-item-card:nth-child(4) { display: none; } /* 当宽度小于 1600px 时隐藏第4个卡片 */
+  .scene-left {
+    width: 340px;
+  }
+  .s-main-t {
+    font-size: 20px;
+  }
+  .s-sub-t {
+    font-size: 20px;
+    font-weight: 800;
+  }
+  .s-item-card:nth-child(4) {
+    display: none;
+  } /* 当宽度小于 1600px 时隐藏第4个卡片 */
 }
 
 @media screen and (max-width: 1209px) {
-  .s-item-card:nth-child(3) { display: none; } /* 宽度更小时隐藏第3个卡片 */
+  .s-item-card:nth-child(3) {
+    display: none;
+  } /* 宽度更小时隐藏第3个卡片 */
 }
 </style>

+ 160 - 161
src/views/home/jdcomponents/JDUserPanel.vue

@@ -6,123 +6,61 @@
     <!-- 用户信息区 -->
     <div class="u-auth flex">
       <div class="avatar">
-        <img src="@/assets/jd/user_avatar.png" />
+        <img :src="logo2" />
       </div>
       <div class="u-info">
-        <p class="name">52k9i2wwed3jx2</p>
-        <p class="links">切换企业账号<span class="divider">|</span>注册</p>
+        <p class="name">{{ userInfo.nickName }}</p>
+        <!-- <p @click="onPath('/breg')" class="links">切换企业账号<span class="divider">|</span>注册</p> -->
       </div>
     </div>
 
     <!-- 会员卡片 -->
     <div class="member-card">
-       <div class="c-tag">企业会员</div>
-       <div class="c-main flex-between">
-         <div class="c-left">
-            <div class="c-h">新人福利</div>
-            <p>领3000元采购补贴</p>
-         </div>
-         <div class="c-btn">立即开通 <i class="arr">&gt;</i></div>
-       </div>
-    </div>
-
-    <!-- 会员权益文本 -->
-    <div class="card-benefits">
-       <div class="b-row-1 flex-between">
-          <span>大额免息</span>
-          <span>充值返</span>
-          <span>免费领</span>
-          <span>购卡返</span>
-       </div>
-       <div class="b-row-2 flex-between">
-          <span>账期</span>
-          <span>余额</span>
-          <span>优惠券</span>
-          <span>礼品卡</span>
-       </div>
+      <div class="c-tag">企业会员</div>
+      <div class="c-main flex-between">
+        <div class="c-left">
+          <div class="c-h">授信余额</div>
+          <p class="credit-num">{{ salesInfo.creditAmount || '0.00' }}元</p>
+        </div>
+      </div>
     </div>
 
-    <!-- 免息广告条 -->
-    <div class="small-ad flex-center">
-       <div class="fire-icon">
-          <svg viewBox="0 0 1024 1024" width="10" height="10"><path d="M512 0C326.4 200 256 312 256 464c0 141.4 114.6 256 256 256s256-114.6 256-256c0-152-70.4-264-256-464z" fill="#fff"/></svg>
-       </div>
-       <span>申请免息账期 首单立减288</span>
+    <!-- 订单状态统计 -->
+    <div class="order-stats">
+      <div class="stat-item">
+        <div class="num">{{ countData.pendingapprovalCount || 0 }}</div>
+        <div class="label">待审批</div>
+      </div>
+      <div class="stat-item">
+        <div class="num">{{ countData.pendingPaymentCount || 0 }}</div>
+        <div class="label">待付款</div>
+      </div>
+      <div class="stat-item">
+        <div class="num">{{ countData.pendingShipmentCount || 0 }}</div>
+        <div class="label">待发货</div>
+      </div>
+      <div class="stat-item">
+        <div class="num">{{ countData.pendingReceiptCount || 0 }}</div>
+        <div class="label">待收货</div>
+      </div>
     </div>
 
     <!-- 企业工作台 -->
     <div class="tools-sec relative">
-      <div class="t-h flex-between">
-         <span>企业工作台</span>
-         <i class="icon-more">&gt;</i>
+      <div class="t-h flex-between" @click="onPath(dataInfo.jumpLink)">
+        <span>{{ dataInfo.moduleName }}</span>
+        <i class="icon-more">&gt;</i>
       </div>
 
       <!-- 滑动翻页组件 -->
       <div class="t-slider-wrap">
         <div class="t-track" :style="{ transform: `translateX(-${currentPage * 100}%)` }">
-
-          <!-- 第 1 页 -->
-          <div class="t-page">
-            <div class="t-grid">
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE5IDIxSDVDNC40IDIxIDQgMjAuNiA0IDIwVjRDNCAzLjQgNC40IDMgNSAzSDE0TDIwIDlWMjBDMjAgMjAuNiAxOS42IDIxIDE5IDIxWiIvPjxwYXRoIGQ9Ik0xNCAzVjloNiIvPjxwb2x5Z29uIHBvaW50cz0iOSAxMyAxMSAxNSAxNSAxMSA5IDEzIi8+PC9zdmc+" class="t-icon" />
-                 <span>订单中心</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE0IDJIMlYyMkgxNEwxOCAxOEwxNCAxNFYyWiIvPjxsaW5lIHgxPSI2IiB5MT0iNiIgeDI9IjEwIiB5Mj0iNiIvPjxsaW5lIHgxPSI2IiB5MT0iMTAiIHgyPSIxMCIgeTI9IjEwIi8+PC9zdmc+" class="t-icon" />
-                 <span>发票中心</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTkgMkgxNVY2SDlaTTUgNlYyMkgxOVY2SDVaIi8+PGxpbmUgeDE9IjkiIHkxPSIxMiIgeDI9IjE1IiB5Mj0iMTIiLz48bGluZSB4MT0iOSIgeTE9IjE2IiB4Mj0iMTUiIHkyPSIxNiIvPjwvc3ZnPg==" class="t-icon" />
-                 <span>采购清单</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTEyIDJDOC4xIDIgNSA1LjEgNSA5QzUgMTQuMiAxMiAyMiAxMiAyMiAxMkMxMiAyMiAxOSAxNC4yIDE5IDlDMTkgNS4xIDE1LjkgMiAxMiAyWiIvPjxjaXJjbGUgY3g9IjEyIiBjeT0iOSIgcj0iMiIvPjwvc3ZnPg==" class="t-icon" />
-                 <span>多地下单</span>
-               </div>
-               <div class="t-i">
-                 <div class="badge">返100</div>
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTUgMThWMTVIN0E1IDUgMCAwIDEgMTIgOUgxMmE1IDUgMCAwIDEgNSA1VjE4SDE3YTIgMiAwIDAgMSAyIDJWMjBIM2EwIDAgMCAwIDEtLS0yVjE4WiIvPjwvc3ZnPg==" class="t-icon" />
-                 <span>达量返</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHJlY3QgeD0iMyIgeT0iNSIgd2lkdGg9IjE4IiBoZWlnaHQ9IjE0IiByeD0iMiIvPjxwYXRoIGQ9Ik03IDlMMTEgMTVNMTUgOVYxNU0xNSA5TDEzIDEySDE3TTEzIDE1SDE3Ii8+PC9zdmc+" class="t-icon" />
-                 <span>智能选品</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTcgMThWMTRIN0E1IDUgMCAwIDEgMTIgOUgxMmE1IDUgMCAwIDEgNSA1VjE4SDE3YTIgMiAwIDAgMSAyIDJWMjBIM2EwIDAgMCAwIDEtLS0yVjE4WiIvPjwvc3ZnPg==" class="t-icon" />
-                 <span>电子合同</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHJlY3QgeD0iMiIgeT0iNiIgd2lkdGg9IjIwIiBoZWlnaHQ9IjEyIiByeD0iMiIvPjxsaW5lIHgxPSI2IiB5MT0iMTIiIHgyPSIxMCIgeTI9IjEyIi8+PC9zdmc+" class="t-icon" />
-                 <span>极速认款</span>
-               </div>
-            </div>
-          </div>
-
-          <!-- 第 2 页 -->
-          <div class="t-page">
+          <div class="t-page" v-for="(item1, index1) in dataList" :key="index1">
             <div class="t-grid">
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iOSIgY3k9IjIxIiByPSIxIi8+PGNpcmNsZSBjeD0iMjAiIGN5PSIyMSIgcj0iMSIvPjxwYXRoIGQ9Ik0xIDFoNGwyLjY4IDEzLjM5YTIgMiAwIDAgMCAyIDEuNjFoOS43MmEyIDIgMCAwIDAgMi0xLjYxTDIzIDZINiIvPjwvc3ZnPg==" class="t-icon" />
-                 <span>采购报告</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGxpbmUgeDE9IjEyIiB5MT0iMSIgeDI9IjEyIiB5Mj0iMjMiLz48cGF0aCBkPSJNMTcgNVYzbS0xMCAyeTJoMTAiLz48cGF0aCBkPSJNNSAxNXYyaDEwIi8+PHBhdGggZD0iTTEyIDljLTIuMjEgMC00IDEuNzktNCA0cyAxLjc5IDQgNCA0Ii8+PHBhdGggZD0iTTEyIDE3YzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00Ii8+PC9zdmc+" class="t-icon" />
-                 <span>对公转账</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE4IDhWMk0xOCAyMjEtdi02Ii8+PHBhdGggZD0iTTE0IDhWMk0xNCAyMnYtNiIvPjxwYXRoIGQ9Ik0xMCA4VjJNMTAgMjJ2LTYiLz48cGF0aCBkPSJNMjIgMnY4YTUgNSAwIDAgMS01IDVoLTE0YTUgNSAwIDAgMS01LTVWMloiLz48L3N2Zz4=" class="t-icon" />
-                 <span>员工饭卡</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE0IDJIMlYyMkgxNEwxOCAxOEwxNCAxNFYyWiIvPjxwb2x5Z29uIHBvaW50cz0iNiAxMCA4IDEyIDEyIDgiLz48L3N2Zz4=" class="t-icon" />
-                 <span>计划购</span>
-               </div>
-               <div class="t-i">
-                 <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzMzIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTUgMTJIMTlNMTEgMThWMk0xNiAybC01IDUtNSA1Ii8+PC9zdmc+" class="t-icon" />
-                 <span>预算管理</span>
-               </div>
+              <div class="t-i" v-for="(item2, index2) in item1" :key="index2" @click="onPath(item2.jumpLink)">
+                <el-image class="t-icon" :src="item2.iconUrl" />
+                <span>{{ item2.name }}</span>
+              </div>
             </div>
           </div>
         </div>
@@ -133,7 +71,7 @@
             <polyline points="15 18 9 12 15 6"></polyline>
           </svg>
         </div>
-        <div class="flip-btn next-btn" v-show="currentPage < 1" @click="currentPage++">
+        <div class="flip-btn next-btn" v-show="currentPage < 1 && dataList.length > 1" @click="currentPage++">
           <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
             <polyline points="9 18 15 12 9 6"></polyline>
           </svg>
@@ -143,8 +81,53 @@
   </div>
 </template>
 
-<script setup>
-import { ref } from 'vue';
+<script setup lang="ts">
+import { currentQuickEntryModule } from '@/api/home/index-enterprise';
+import { getEnterpriseInfo } from '@/api/pc/enterprise';
+import { getInfo } from '@/api/login';
+import { onPath } from '@/utils/siteConfig';
+import { countOrder } from '@/api/home/index';
+import logo2 from '@/assets/images/pcdiy/logo2.png';
+const dataList = ref<any>([]);
+const dataInfo = ref<any>({});
+const userInfo = ref<any>({});
+const countData = ref<any>({});
+const salesInfo = ref<any>({});
+
+getEnterpriseInfo().then((res) => {
+  if (res.code == 200) {
+    salesInfo.value = res.data.customerSalesInfoVo || {};
+  }
+});
+
+currentQuickEntryModule({}).then((res) => {
+  if (res.code == 200) {
+    dataInfo.value = res.data;
+    if (res.data && res.data.items.length > 0) {
+      dataList.value = chunkArray(res.data.items, 8);
+    }
+  }
+});
+
+const chunkArray = (arr, size) => {
+  const result = [];
+  for (let i = 0; i < arr.length; i += size) {
+    result.push(arr.slice(i, i + size));
+  }
+  return result;
+};
+
+getInfo().then((res) => {
+  if (res.code == 200) {
+    userInfo.value = res.data.user;
+  }
+});
+
+countOrder({}).then((res) => {
+  if (res.code == 200) {
+    countData.value = res.data || {};
+  }
+});
 
 const currentPage = ref(0);
 </script>
@@ -163,13 +146,20 @@ const currentPage = ref(0);
 /* 顶部极淡粉色渐变背景 */
 .top-bg {
   position: absolute;
-  top: 0; left: 0; right: 0; height: 120px;
-  background: linear-gradient(to bottom, #FFF5EE, #ffffff);
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 120px;
+  background: linear-gradient(to bottom, #fff5ee, #ffffff);
   z-index: 0;
 }
 
 /* 内容提升层级 */
-.u-auth, .member-card, .card-benefits, .small-ad, .tools-sec {
+.u-auth,
+.member-card,
+.card-benefits,
+.small-ad,
+.tools-sec {
   position: relative;
   z-index: 1;
 }
@@ -185,7 +175,7 @@ const currentPage = ref(0);
   height: 48px;
   border-radius: 50%;
   border: 2px solid #fff;
-  box-shadow: 0 2px 6px rgba(255,182,193,0.3);
+  box-shadow: 0 2px 6px rgba(255, 182, 193, 0.3);
   margin-right: 12px;
 }
 
@@ -199,23 +189,28 @@ const currentPage = ref(0);
   font-size: 11px;
   color: #666;
   margin-top: 4px;
+  cursor: pointer;
 }
 
-.divider { margin: 0 6px; color: #E0E0E0; }
+.divider {
+  margin: 0 6px;
+  color: #e0e0e0;
+}
 
 .member-card {
   margin: 0 16px;
-  background: linear-gradient(to right, #FDF0DE, #FCE3C5);
+  background: linear-gradient(to right, #fdf0de, #fce3c5);
   border-radius: 8px;
   position: relative;
-  padding: 16px 12px 12px;
+  padding: 22px 12px 12px; /* 增加顶部边距,避开标签 */
 }
 
 .c-tag {
   position: absolute;
-  top: 0; left: 0;
-  background: #11366F;
-  color: #F8D9A8;
+  top: 0;
+  left: 0;
+  background: #11366f;
+  color: #f8d9a8;
   font-size: 10px;
   padding: 2px 8px;
   border-radius: 8px 0 8px 0;
@@ -224,64 +219,57 @@ const currentPage = ref(0);
 
 .c-h {
   font-weight: 800;
-  font-size: 15px;
-  color: #5A3515;
+  font-size: 13px;
+  color: #11366f; /* 使用标签背景色 */
+  letter-spacing: 0.3px;
 }
 
-.c-left p {
-  font-size: 11px;
-  color: #7E5124;
-  margin-top: 3px;
+.c-left p.credit-num {
+  font-size: 20px;
+  font-weight: 900;
+  color: #e1251b; /* 使用红色 */
+  margin-top: 4px;
+  letter-spacing: 0.5px;
 }
 
-.c-btn {
-  font-size: 11px;
-  color: #613C1C;
-  border: 1px solid #7E5124;
-  padding: 3px 8px 3px 10px;
-  border-radius: 12px;
-  cursor: pointer;
+.order-stats {
+  margin: 12px 16px 18px;
+  background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(254, 242, 242, 0.8) 100%);
+  border: 1px solid rgba(225, 37, 27, 0.1);
+  border-radius: 10px;
   display: flex;
-  align-items: center;
+  justify-content: space-around;
+  padding: 14px 0;
+  box-shadow: 0 4px 15px rgba(225, 37, 27, 0.04);
 }
-.c-btn .arr { font-style: normal; margin-left: 2px; font-family: sans-serif; }
 
-.card-benefits {
-  margin: 12px 16px 8px;
+.stat-item {
+  text-align: center;
+  flex: 1;
+  position: relative;
 }
 
-.b-row-1 {
-  font-size: 12px;
-  font-weight: 800;
-  color: #000;
-  padding: 0 4px;
+.stat-item:not(:last-child)::after {
+  content: '';
+  position: absolute;
+  right: 0;
+  top: 20%;
+  height: 60%;
+  width: 1px;
+  background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.05), transparent);
 }
 
-.b-row-2 {
-  font-size: 11px;
-  color: #666;
-  padding: 0 8px;
-  margin-top: 4px;
+.stat-item .num {
+  font-size: 16px;
+  font-weight: 800;
+  color: #e1251b;
+  font-family: Arial, sans-serif;
 }
 
-.small-ad {
-  margin: 0 16px 16px;
-  background: #FFF7F5;
+.stat-item .label {
   font-size: 11px;
-  color: #333;
-  padding: 8px 0;
-  border-radius: 4px;
-}
-
-.fire-icon {
-  width: 14px;
-  height: 14px;
-  background: #E1251B;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 6px;
+  color: #666;
+  margin-top: 5px;
 }
 
 .tools-sec {
@@ -296,7 +284,12 @@ const currentPage = ref(0);
   margin-bottom: 15px;
 }
 
-.icon-more { font-size: 12px; color: #999; font-style: normal; }
+.icon-more {
+  font-size: 12px;
+  color: #999;
+  font-style: normal;
+  cursor: pointer;
+}
 
 .t-slider-wrap {
   position: relative;
@@ -326,13 +319,19 @@ const currentPage = ref(0);
   align-items: center;
   justify-content: center;
   cursor: pointer;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.15);
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
   z-index: 10;
   transition: background 0.2s;
 }
-.flip-btn:hover { background: #f4f4f4; }
-.prev-btn { left: 4px; }
-.next-btn { right: 4px; }
+.flip-btn:hover {
+  background: #f4f4f4;
+}
+.prev-btn {
+  left: 4px;
+}
+.next-btn {
+  right: 4px;
+}
 
 .t-grid {
   display: grid;
@@ -363,7 +362,7 @@ const currentPage = ref(0);
   position: absolute;
   top: -8px;
   right: -2px;
-  background: #FF5A5F;
+  background: #ff5a5f;
   color: #fff;
   font-size: 9px;
   padding: 1px 4px;

+ 84 - 50
src/views/home/pccomponents/pages/floor.vue

@@ -1,45 +1,48 @@
 <template>
   <div class="pcPages" :style="warpCss">
     <div class="floor-bos" :style="boxCss">
-      <el-image
-        @click="onPath(componentData.url)"
-        class="floor-one"
-        :src="componentData.imageUrl ? componentData.imageUrl : figure"
-        :fit="componentData.imageUrl ? (componentData.imgType == 1 ? 'fill' : componentData.imgType == 2 ? 'contain' : 'cover') : 'cover'"
-        :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
-      />
-      <div class="floor-box">
-        <div
-          v-for="(item, index) in componentData.goodsDefault ? componentData.goodsList : dataList"
-          :key="index"
-          class="goods-list flex-column-between hover-color"
-          @click="onPath('/item?id=' + item.id + '&productNo=' + item.productNo)"
-        >
-          <img
-            class="goods-img"
-            :src="item.productImage ? item.productImage : figure"
-            alt=""
-            :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
-          />
-          <div v-if="componentData.goodsShow.includes(1)" class="itemName zi-hover">{{ item.itemName || '商品名称' }}</div>
-          <div class="flex-row-between">
-            <div>
-              <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.btnbackgroundColor }"
-                >¥{{ item.memberPrice || '0.00' }}</span
-              >
-              <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
-            </div>
-            <template v-if="componentData.btnShow">
-              <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1 ellipsis">{{ componentData.btnText }}</div>
-              <div v-if="componentData.btnStyle == 2" :style="btnCss2" class="btn2 flex-row-center">
-                <el-icon size="14"><Plus /></el-icon>
-              </div>
-              <div v-if="componentData.btnStyle == 3" :style="btnCss2" class="btn2 flex-row-center">
-                <icon name="iconfont icongouwuche" size="14px" />
+      <div class="floor-one-bos">
+        <el-image
+          @click="onPath(componentData.url)"
+          class="floor-one"
+          :src="componentData.imageUrl ? componentData.imageUrl : figure"
+          :fit="componentData.imageUrl ? (componentData.imgType == 1 ? 'fill' : componentData.imgType == 2 ? 'contain' : 'cover') : 'cover'"
+          :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+        />
+      </div>
+      <div :style="{ 'width': boxWidth + 'px' }"></div>
+      <div class="floor-box" ref="floorBoxRef">
+        <template v-for="(item, index) in componentData.goodsDefault ? componentData.goodsList : dataList" :key="index">
+          <div
+            v-if="Number(index) < 8"
+            class="goods-list flex-column-between hover-color"
+            @click="onPath('/item?id=' + item.id + '&productNo=' + item.productNo)"
+          >
+            <img
+              class="goods-img"
+              :src="item.productImage ? item.productImage : figure"
+              alt=""
+              :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+            />
+            <div v-if="componentData.goodsShow.includes(1)" class="itemName zi-hover">{{ item.itemName || '商品名称' }}</div>
+            <div class="flex-row-between">
+              <div>
+                <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.btnbackgroundColor }"
+                  >¥{{ item.memberPrice || '0.00' }}</span
+                >
+                <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
               </div>
-            </template>
-          </div>
-        </div>
+              <template v-if="componentData.btnShow">
+                <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1 ellipsis">{{ componentData.btnText }}</div>
+                <div v-if="componentData.btnStyle == 2" :style="btnCss2" class="btn2 flex-row-center">
+                  <el-icon size="14"><Plus /></el-icon>
+                </div>
+                <div v-if="componentData.btnStyle == 3" :style="btnCss2" class="btn2 flex-row-center">
+                  <icon name="iconfont icongouwuche" size="14px" />
+                </div>
+              </template>
+            </div></div
+        ></template>
       </div>
       <div class="goods-brand flex-column-between" v-if="componentData.styleType == 2">
         <div class="brand-bos">
@@ -74,13 +77,35 @@ import { getDiyProductPage, getCustomerProductPage } from '@/api/home/diy';
 interface Props {
   row?: any;
 }
-
+const boxWidth = ref<any>(0);
 const props = defineProps<Props>();
 const componentData = props.row || {};
 const dataList = ref<any>([{}, {}, {}, {}, {}, {}, {}, {}]);
+const floorBoxRef = ref<any>(null);
+const clientHeight = ref<any>(0);
+// 新增:ResizeObserver 实例
+let resizeObserver: ResizeObserver | null = null;
 
 onMounted(() => {
   getDataList();
+
+  // 初始化 ResizeObserver 监听 floorBoxRef 尺寸变化
+  if (floorBoxRef.value) {
+    resizeObserver = new ResizeObserver((entries) => {
+      for (const entry of entries) {
+        clientHeight.value = Math.floor(entry.contentRect.height);
+        boxWidth.value = (240 / 568) * clientHeight.value;
+      }
+    });
+    resizeObserver.observe(floorBoxRef.value);
+  }
+});
+
+onUnmounted(() => {
+  if (resizeObserver) {
+    resizeObserver.disconnect();
+    resizeObserver = null;
+  }
 });
 
 const getDataList = () => {
@@ -183,15 +208,20 @@ const btnCss2 = computed(() => {
   min-width: 1200px;
   margin: 0 auto;
   .floor-bos {
-    height: 560px;
     display: flex;
     gap: 10px;
     width: 100%;
-    .floor-one {
-      width: 230px;
-      height: 560px;
+    .floor-one-bos {
+      position: absolute;
+      height: 100%;
+      aspect-ratio: 240 / 568;
       cursor: pointer;
+      .floor-one {
+        width: 100%;
+        height: 100%;
+      }
     }
+
     .floor-box {
       flex: 1;
       display: flex;
@@ -203,16 +233,15 @@ const btnCss2 = computed(() => {
       }
       .goods-list {
         padding: 15px 10px;
-        height: 275px;
         width: 0;
         flex: 0 0 calc((100% - 30px) / 4);
         background-color: #ffffff;
         overflow: hidden;
         border-radius: 5px;
         .goods-img {
-          width: 140px;
-          height: 140px;
-          margin: 0 auto;
+          width: 100%;
+          aspect-ratio: 1;
+          margin: 0 auto 10px auto;
         }
         .itemName {
           width: 100%;
@@ -225,6 +254,7 @@ const btnCss2 = computed(() => {
           -webkit-box-orient: vertical;
           overflow: hidden;
           text-overflow: ellipsis;
+          margin-bottom: 10px;
         }
         .memberPrice {
           font-size: 16px;
@@ -255,19 +285,22 @@ const btnCss2 = computed(() => {
       }
     }
     .goods-brand {
-      width: 140px;
-      height: 560px;
+      width: 180px;
       border-radius: 5px;
       background: #ffffff;
       padding: 15px 10px;
+      display: flex;
+      flex-direction: column;
       .brand-bos {
+        flex: 1;
         width: 100%;
         display: flex;
         flex-direction: column;
+        // justify-content: space-between;
         gap: 10px 0;
         .brand-img {
           width: 100%;
-          height: 74px;
+          aspect-ratio: 120/50;
           border-radius: 4px;
           border: 1px solid #e5e7eb;
           cursor: pointer;
@@ -277,6 +310,7 @@ const btnCss2 = computed(() => {
         color: var(--el-color-primary);
         font-size: 14px;
         cursor: pointer;
+        margin-top: 20px;
       }
     }
   }

+ 0 - 1
src/views/index.vue

@@ -532,7 +532,6 @@ getClassificationFloorList({}).then(async (res) => {
         }
 
         const datas2 = await getClassificationFloorLabel(item.floorNo);
-        console.log(datas2.'?????????????????');
         if (datas2.code == 200) {
           item.navList = datas2.data;
         }

+ 9 - 2
src/views/item/index.vue

@@ -9,6 +9,10 @@
       <div class="home" @click="onPath('/search?type=2&mediumCategoryId=' + dataInfo.mediumCategoryId)">{{ dataInfo.mediumCategoryName }}</div>
       <el-icon style="margin: 2px 4px 0 4px" size="14"><ArrowRight /></el-icon>
       <div class="home" @click="onPath('/search?type=3&bottomCategoryId=' + dataInfo.bottomCategoryId)">{{ dataInfo.bottomCategoryName }}</div>
+      <el-icon style="margin: 2px 4px 0 4px" size="14"><ArrowRight /></el-icon>
+      <div class="home" @click="onPath('/search?type=1&brandId=' + dataInfo.brandId)">{{ dataInfo.brandName }}</div>
+      <el-icon style="margin: 2px 4px 0 4px" size="14"><ArrowRight /></el-icon>
+      <div class="home1">{{ dataInfo.itemName }}</div>
     </div>
     <div class="shop-bos">
       <div class="shop-left">
@@ -506,6 +510,9 @@ const handleMouseMove = (e: MouseEvent) => {
         color: var(--el-color-primary);
       }
     }
+    .home1 {
+      font-size: 14px;
+    }
   }
 
   .shop-bos {
@@ -608,8 +615,8 @@ const handleMouseMove = (e: MouseEvent) => {
             position: absolute;
             width: 200px; /* 容器宽度的一半 */
             height: 200px; /* 容器高度的一半 */
-            background-color: rgba(255, 255, 255, 0.3);
-            border: 1px solid #ccc;
+            // background-color: rgba(255, 255, 255, 0.3);
+            // border: 1px solid #ccc;
             pointer-events: none;
             z-index: 5;
           }

+ 73 - 67
src/views/login.vue

@@ -1,68 +1,71 @@
 <template>
   <div class="login">
-    <img @click="onPath('/')" class="head" src="@/assets/images/head.png" alt="" />
-    <div class="login-info flex-row-between">
-      <div></div>
-      <div class="login-bos">
-        <el-form ref="loginRef" :model="loginForm" :rules="loginRules">
-          <div class="login-type flex-row-between">
-            <div :class="type == 1 ? 'hig' : ''" @click="onType(1)">账户登录</div>
-            <div class="border"></div>
-            <div :class="type == 2 ? 'hig' : ''" @click="onType(2)">验证码登录</div>
-          </div>
-          <template v-if="type == 1">
-            <el-form-item prop="username">
-              <el-input class="login-input" v-model="loginForm.username" placeholder="员工编号/手机号码">
-                <template #prefix>
-                  <el-icon><User /></el-icon>
-                </template>
-              </el-input>
+    <div>
+      <img @click="onPath('/')" class="head" src="@/assets/images/head.png" alt="" />
+      <div class="login-info flex-row-between">
+        <div></div>
+        <div class="login-bos">
+          <el-form ref="loginRef" :model="loginForm" :rules="loginRules">
+            <div class="login-type flex-row-between">
+              <div :class="type == 1 ? 'hig' : ''" @click="onType(1)">账户登录</div>
+              <div class="border"></div>
+              <div :class="type == 2 ? 'hig' : ''" @click="onType(2)">验证码登录</div>
+            </div>
+            <template v-if="type == 1">
+              <el-form-item prop="username">
+                <el-input class="login-input" v-model="loginForm.username" placeholder="员工编号/手机号码">
+                  <template #prefix>
+                    <el-icon><User /></el-icon>
+                  </template>
+                </el-input>
+              </el-form-item>
+              <el-form-item prop="password">
+                <el-input class="login-input" type="password" v-model="loginForm.password" placeholder="输入登录密码" @keyup.enter="handleLogin">
+                  <template #prefix>
+                    <el-icon><Lock /></el-icon>
+                  </template>
+                </el-input>
+              </el-form-item>
+            </template>
+            <template v-else>
+              <el-form-item prop="mobile">
+                <el-input :maxlength="11" class="login-input" v-model="loginForm.mobile" placeholder="手机号">
+                  <template #prefix>
+                    <el-icon><Iphone /></el-icon>
+                  </template>
+                </el-input>
+              </el-form-item>
+              <el-form-item prop="smsCode">
+                <el-input :maxlength="6" class="login-input" v-model="loginForm.smsCode" placeholder="验证码">
+                  <template #prefix>
+                    <el-icon><Message /></el-icon>
+                  </template>
+                  <template #suffix>
+                    <span @click="sendSmsCode" :class="['code', countdown > 0 ? 'disabled' : '']">
+                      {{ codeText }}
+                    </span>
+                  </template>
+                </el-input>
+              </el-form-item>
+            </template>
+            <el-form-item>
+              <el-button class="login-btn" type="primary" :loading="loading" @click.prevent="handleLogin">
+                <span v-if="!loading">登录</span>
+                <span v-else>登录中...</span>
+              </el-button>
             </el-form-item>
-            <el-form-item prop="password">
-              <el-input class="login-input" type="password" v-model="loginForm.password" placeholder="输入登录密码" @keyup.enter="handleLogin">
-                <template #prefix>
-                  <el-icon><Lock /></el-icon>
-                </template>
-              </el-input>
-            </el-form-item>
-          </template>
-          <template v-else>
-            <el-form-item prop="mobile">
-              <el-input :maxlength="11" class="login-input" v-model="loginForm.mobile" placeholder="手机号">
-                <template #prefix>
-                  <el-icon><Iphone /></el-icon>
-                </template>
-              </el-input>
-            </el-form-item>
-            <el-form-item prop="smsCode">
-              <el-input :maxlength="6" class="login-input" v-model="loginForm.smsCode" placeholder="验证码">
-                <template #prefix>
-                  <el-icon><Message /></el-icon>
-                </template>
-                <template #suffix>
-                  <span @click="sendSmsCode" :class="['code', countdown > 0 ? 'disabled' : '']">
-                    {{ codeText }}
-                  </span>
-                </template>
-              </el-input>
-            </el-form-item>
-          </template>
-          <el-form-item>
-            <el-button class="login-btn" type="primary" :loading="loading" @click.prevent="handleLogin">
-              <span v-if="!loading">登录</span>
-              <span v-else>登录中...</span>
-            </el-button>
-          </el-form-item>
-          <div class="login-text flex-row-between">
-            <div @click="handleForgetPassword">忘记密码?</div>
-            <div class="border"></div>
-            <div @click="onPath('/breg')">企业注册</div>
-            <div @click="onPath('/reg')">个人注册</div>
-            <!-- <router-link to="/register" class="register-link">新用户注册</router-link> -->
-          </div>
-        </el-form>
+            <div class="login-text flex-row-between">
+              <div @click="handleForgetPassword">忘记密码?</div>
+              <div class="border"></div>
+              <div @click="onPath('/breg')">企业注册</div>
+              <div @click="onPath('/reg')">个人注册</div>
+              <!-- <router-link to="/register" class="register-link">新用户注册</router-link> -->
+            </div>
+          </el-form>
+        </div>
       </div>
     </div>
+
     <div class="login-foot flex-column-between">
       <div class="font-bos flex-row-center">
         <div>客户管理</div>
@@ -287,11 +290,14 @@ onMounted(() => {
     padding: 0 5%;
 
     .login-bos {
-      width: 520px;
+      width: 420px;
       height: 420px;
       background: #ffffff;
       border-radius: 30px 30px 30px 30px;
-      padding: 40px 85px 0 85px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
 
       :deep(.el-form-item) {
         margin-bottom: 18px;
@@ -303,7 +309,7 @@ onMounted(() => {
 
       .login-type {
         font-weight: 600;
-        font-size: 22px;
+        font-size: 20px;
         color: #101828;
         padding: 0 67px;
         margin-bottom: 40px;
@@ -320,8 +326,8 @@ onMounted(() => {
         }
       }
       .login-input {
-        width: 350px;
-        height: 50px;
+        width: 100%;
+        height: 42px;
         font-size: 16px;
         .code {
           font-size: 14px;
@@ -340,8 +346,8 @@ onMounted(() => {
         color: #9ca3af;
       }
       .login-btn {
-        width: 350px;
-        height: 50px;
+        width: 100%;
+        height: 42px;
         margin-top: 20px;
         font-size: 16px;
         background-color: #c8102e;

+ 93 - 27
src/views/order/afterSale/index.vue

@@ -117,18 +117,18 @@
 
       <div class="search-filter-bar">
         <div class="left">
-          <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 150px" clearable>
+          <!-- <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 150px" clearable>
             <template #prefix
               ><el-icon><Search /></el-icon
             ></template>
-          </el-input>
-          <span class="filter-label">处理结果</span>
+          </el-input> -->
+          <!-- <span class="filter-label">处理结果</span>
           <el-select v-model="queryParams.handleResult" placeholder="请选择" style="width: 100px" clearable>
             <el-option label="已处理" value="handled" />
             <el-option label="未处理" value="unhandled" />
-          </el-select>
+          </el-select> -->
           <span class="filter-label">反馈类型</span>
-          <el-select v-model="queryParams.feedbackType" placeholder="请选择" style="width: 100px" clearable>
+          <el-select v-model="queryParams.feedbackType" placeholder="请选择" style="width: 100px" clearable @change="handleFeedbackTypeChange">
             <el-option v-for="dict in complaints_suggestion_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </div>
@@ -160,8 +160,8 @@
         <el-table-column prop="processResult" label="处理结果" min-width="100" align="center" />
         <el-table-column label="操作" min-width="120" align="center">
           <template #default="{ row }">
-            <el-button type="primary" link size="small" @click="handleEditComplaint(row)">修改</el-button>
-            <el-button type="danger" link size="small" @click="handleDeleteComplaint(row)">删除</el-button>
+            <el-button type="primary" link size="small" @click="handleViewComplaint(row)">查看</el-button>
+            <!-- <el-button type="danger" link size="small" @click="handleDeleteComplaint(row)">删除</el-button> -->
           </template>
         </el-table-column>
       </el-table>
@@ -171,20 +171,60 @@
       </div>
     </template>
 
-    <el-dialog v-model="complaintDialogVisible" :title="complaintDialogTitle" width="500px">
-      <el-form ref="complaintFormRef" :model="complaintForm" :rules="complaintRules" label-width="80px">
-        <el-form-item label="反馈类型" prop="feedbackType">
-          <el-select v-model="complaintForm.feedbackType" placeholder="请选择" style="width: 100%">
-            <el-option v-for="dict in complaints_suggestion_type" :key="dict.value" :label="dict.label" :value="dict.value" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="反馈内容" prop="feedbackContent">
-          <el-input v-model="complaintForm.feedbackContent" type="textarea" :rows="4" placeholder="请输入反馈内容" />
-        </el-form-item>
-      </el-form>
+    <el-dialog v-model="complaintDialogVisible" :title="complaintDialogTitle" width="700px">
+      <template v-if="isViewMode">
+        <el-descriptions :column="1" border>
+          <el-descriptions-item label="编号">{{ currentComplaint?.id || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="反馈类型">
+            {{ getDictLabel(complaints_suggestion_type, currentComplaint?.feedbackType) || '-' }}
+          </el-descriptions-item>
+          <el-descriptions-item label="反馈内容">{{ currentComplaint?.feedbackContent || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="反馈人">{{ currentComplaint?.createName || '-' }} {{ currentComplaint?.phone || '' }}</el-descriptions-item>
+          <el-descriptions-item label="相关凭证">
+            <template v-if="currentComplaint?.relatedPictures">
+              <div class="image-preview">
+                <el-image
+                  v-for="(img, index) in (currentComplaint.relatedPictures || '').split(',').filter(Boolean)"
+                  :key="index"
+                  :src="img"
+                  :preview-src-list="(currentComplaint.relatedPictures || '').split(',').filter(Boolean)"
+                  style="width: 80px; height: 80px; margin-right: 8px; border-radius: 4px"
+                  fit="cover"
+                />
+                <span v-if="(currentComplaint.relatedPictures || '').split(',').filter(Boolean).length === 0">-</span>
+              </div>
+            </template>
+            <span v-else>-</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="处理时间">
+            {{ currentComplaint?.processTime ? parseTime(currentComplaint.processTime, '{y}-{m}-{d} {h}:{i}') : '-' }}
+          </el-descriptions-item>
+          <el-descriptions-item label="处理结果">{{ currentComplaint?.processResult || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="处理反馈">{{ currentComplaint?.processFeedback || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="处理评价">{{ currentComplaint?.processEvaluation || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="结果反馈">{{ currentComplaint?.resultFeedback || '-' }}</el-descriptions-item>
+        </el-descriptions>
+      </template>
+      <template v-else>
+        <el-form ref="complaintFormRef" :model="complaintForm" :rules="complaintRules" label-width="80px">
+          <el-form-item label="反馈类型" prop="feedbackType">
+            <el-select v-model="complaintForm.feedbackType" placeholder="请选择" style="width: 100%">
+              <el-option v-for="dict in complaints_suggestion_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="反馈内容" prop="feedbackContent">
+            <el-input v-model="complaintForm.feedbackContent" type="textarea" :rows="4" placeholder="请输入反馈内容" />
+          </el-form-item>
+        </el-form>
+      </template>
       <template #footer>
-        <el-button @click="complaintDialogVisible = false">取消</el-button>
-        <el-button type="danger" @click="handleSubmitComplaint">确定</el-button>
+        <template v-if="isViewMode">
+          <el-button @click="complaintDialogVisible = false">关闭</el-button>
+        </template>
+        <template v-else>
+          <el-button @click="complaintDialogVisible = false">取消</el-button>
+          <el-button type="danger" @click="handleSubmitComplaint">确定</el-button>
+        </template>
       </template>
     </el-dialog>
 
@@ -234,7 +274,7 @@
 
 <script setup lang="ts">
 import { ref, reactive, computed, onMounted } from 'vue';
-import { useRouter } from 'vue-router';
+import { useRouter, useRoute } from 'vue-router';
 import { Search, RefreshRight, Tools, ChatLineSquare, Picture } from '@element-plus/icons-vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { PageTitle, SearchBar, TablePagination } from '@/components';
@@ -251,12 +291,14 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { service_type, complaints_suggestion_type } = toRefs<any>(proxy?.useDict('service_type', 'complaints_suggestion_type'));
 
 const router = useRouter();
+const route = useRoute();
 const activeMainTab = ref('return');
 const activeStatusTab = ref('all');
 const complaintDialogVisible = ref(false);
 const complaintDialogTitle = ref('新增投诉与建议');
 const complaintFormRef = ref();
 const currentComplaint = ref<any>(null);
+const isViewMode = ref(false);
 const loading = ref(false);
 const pagination = reactive({ page: 1, pageSize: 5, total: 0 });
 const returnDialogVisible = ref(false);
@@ -381,7 +423,15 @@ const handleStatusTabChange = (key: string) => {
 
 // 页面加载时获取数据
 onMounted(() => {
-  loadAfterSaleData();
+  // 检查是否有 tab=complaint 参数,如果有则切换到投诉标签
+  const tab = route.query.tab as string;
+  if (tab === 'complaint') {
+    activeMainTab.value = 'complaint';
+    activeStatusTab.value = 'all';
+    loadComplaintData();
+  } else {
+    loadAfterSaleData();
+  }
 });
 const getStatusClass = (status: string) => {
   const map: Record<string, string> = { 0: 'warning', 1: 'success', 2: 'danger', 3: 'success', 4: 'info' };
@@ -434,17 +484,16 @@ const getItemSubtotal = (item: any) => {
 };
 const handleAddComplaint = () => {
   currentComplaint.value = null;
+  isViewMode.value = false;
   complaintDialogTitle.value = '新增投诉与建议';
   complaintForm.feedbackType = '';
   complaintForm.feedbackContent = '';
   complaintDialogVisible.value = true;
 };
-const handleEditComplaint = (row: any) => {
+const handleViewComplaint = (row: any) => {
   currentComplaint.value = row;
-  complaintDialogTitle.value = '修改投诉与建议';
-  complaintForm.id = row.id;
-  complaintForm.feedbackType = row.feedbackType;
-  complaintForm.feedbackContent = row.feedbackContent || '';
+  isViewMode.value = true;
+  complaintDialogTitle.value = '查看投诉与建议';
   complaintDialogVisible.value = true;
 };
 const handleDeleteComplaint = (row: any) => {
@@ -475,12 +524,23 @@ const handleSubmitComplaint = async () => {
 
 const loadComplaintData = async () => {
   const params: any = { pageNum: pagination.page, pageSize: pagination.pageSize };
+  if (queryParams.feedbackType) {
+    params.feedbackType = queryParams.feedbackType;
+  }
+  if (queryParams.keyword) {
+    params.keyword = queryParams.keyword;
+  }
   const res = await getComplaintsSuggestionsList(params);
   if (res.code === 200 && res.rows) {
     complaintList.value = res.rows;
     pagination.total = res.total || 0;
   }
 };
+
+const handleFeedbackTypeChange = () => {
+  pagination.page = 1;
+  loadComplaintData();
+};
 </script>
 
 <style scoped lang="scss">
@@ -806,4 +866,10 @@ const loadComplaintData = async () => {
     }
   }
 }
+
+.image-preview {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
 </style>

+ 6 - 2
src/views/order/orderAudit/index.vue

@@ -69,10 +69,10 @@
               </div>
               <div class="product-detail">
                 <div class="product-name">{{ item.name }}</div>
-                <div class="product-spec">{{ item.spec1 }} {{ item.spec2 }}</div>
+                <div class="product-spec">{{ item.spec1 }} x{{ item.quantity }} | {{ item.spec2 }}</div>
                 <div class="product-price">¥{{ item.price }}</div>
               </div>
-              <div class="product-quantity">x{{ item.quantity }}</div>
+              <div class="product-quantity"></div>
             </div>
             <div class="product-cell amount-cell" v-if="itemIndex === 0">
               <div class="amount-info">
@@ -595,6 +595,10 @@ const handleSubmitAudit = async () => {
         .product-quantity {
           font-size: 13px;
           color: #666;
+          width: 80px;
+          text-align: right;
+          flex-shrink: 0;
+          padding-left: 10px;
         }
       }
 

+ 100 - 23
src/views/reconciliation/billManage/index.vue

@@ -1,7 +1,44 @@
 <template>
   <div class="page-container">
     <PageTitle title="对账单管理" />
-    <SearchBar :form="searchForm" :filters="filters" placeholder="对账编号" />
+    <!-- 搜索条件区域 -->
+    <div class="search-area">
+      <el-form :model="searchForm" inline>
+        <el-form-item label="对账编号">
+          <el-input v-model="searchForm.keyword" placeholder="请输入对账编号" clearable @clear="handleSearch" />
+        </el-form-item>
+        <el-form-item label="对账日期">
+          <el-date-picker
+            v-model="searchForm.dateRange"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="handleSearch"
+          />
+        </el-form-item>
+        <el-form-item label="对账状态">
+          <el-select v-model="searchForm.billStatus" placeholder="请选择对账状态" clearable @change="handleSearch">
+            <el-option v-for="item in billStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="开票状态">
+          <el-select v-model="searchForm.invoiceStatus" placeholder="请选择开票状态" clearable @change="handleSearch">
+            <el-option v-for="item in invoiceStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="支付状态">
+          <el-select v-model="searchForm.payStatus" placeholder="请选择支付状态" clearable @change="handleSearch">
+            <el-option v-for="item in payStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">搜索</el-button>
+          <el-button @click="handleReset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
     <el-table
       v-loading="loading"
       :data="tableData"
@@ -62,10 +99,10 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, onMounted, watch } from 'vue';
+import { reactive, ref, onMounted, watch, computed, getCurrentInstance, toRefs } from 'vue';
 import { useRouter } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
-import { PageTitle, SearchBar, TablePagination } from '@/components';
+import { PageTitle, TablePagination } from '@/components';
 import { getStatementList, getStatementInfo, confirmStatement, applyForInvoice } from '@/api/pc/enterprise/statement';
 import type { StatementOrder } from '@/api/pc/enterprise/statementTypes';
 
@@ -84,20 +121,10 @@ const searchForm = reactive({
   payStatus: ''
 });
 
-const billStatusOptions = ref([{ label: '全部', value: '' }]);
-const invoiceStatusOptions = ref([{ label: '全部', value: '' }]);
-const payStatusOptions = ref([{ label: '全部', value: '' }]);
-
-// 状态值到文本的映射
-const billStatusMap = ref<Record<string, string>>({});
-const invoiceStatusMap = ref<Record<string, string>>({});
-const payStatusMap = ref<Record<string, string>>({});
-
-const filters = ref([
-  { field: 'billStatus', label: '对账状态', options: billStatusOptions.value },
-  { field: 'invoiceStatus', label: '开票状态', options: invoiceStatusOptions.value },
-  { field: 'payStatus', label: '支付状态', options: payStatusOptions.value }
-]);
+// 从字典生成选项,添加"全部"选项
+const billStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(statement_status.value || [])]);
+const invoiceStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(invoice_issuance_status.value || [])]);
+const payStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(payment_status.value || [])]);
 
 const pagination = reactive({ page: 1, pageSize: 5, total: 0 });
 const tableData = ref<any[]>([]);
@@ -112,14 +139,23 @@ const form = reactive({
 const loadStatementList = async () => {
   try {
     loading.value = true;
-    const res = await getStatementList({
+    const queryParams = {
       pageNum: pagination.page,
       pageSize: pagination.pageSize,
       statementOrderNo: searchForm.keyword,
       statementStatus: searchForm.billStatus,
       isInvoiceStatus: searchForm.invoiceStatus,
-      isPaymentStatus: searchForm.payStatus
-    });
+      isPaymentStatus: searchForm.payStatus,
+      params: {} as any
+    };
+
+    // 添加日期范围参数
+    if (searchForm.dateRange && searchForm.dateRange.length === 2) {
+      queryParams.params.beginTime = searchForm.dateRange[0];
+      queryParams.params.endTime = searchForm.dateRange[1];
+    }
+
+    const res = await getStatementList(queryParams);
 
     if (res.code === 200 && res.rows) {
       tableData.value = res.rows.map((item: StatementOrder) => ({
@@ -189,7 +225,7 @@ watch(
 
 // 监听搜索条件变化
 watch(
-  () => [searchForm.keyword, searchForm.billStatus, searchForm.invoiceStatus, searchForm.payStatus],
+  () => [searchForm.keyword, searchForm.dateRange, searchForm.billStatus, searchForm.invoiceStatus, searchForm.payStatus],
   () => {
     pagination.page = 1;
     loadStatementList();
@@ -202,8 +238,6 @@ const getDictLabel = (dictOptions: any[], value: string) => {
   return dict ? dict.label : value;
 };
 
-// 加载字典数据
-
 // 页面加载时获取数据
 onMounted(() => {
   loadStatementList();
@@ -258,9 +292,52 @@ const handleApplyInvoice = async () => {
     }
   }
 };
+
+// 搜索
+const handleSearch = () => {
+  pagination.page = 1;
+  loadStatementList();
+};
+
+// 重置
+const handleReset = () => {
+  searchForm.keyword = '';
+  searchForm.dateRange = [];
+  searchForm.billStatus = '';
+  searchForm.invoiceStatus = '';
+  searchForm.payStatus = '';
+  pagination.page = 1;
+  loadStatementList();
+};
 </script>
 
 <style scoped>
+.search-area {
+  margin-bottom: 16px;
+  padding: 16px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+}
+
+.search-area .el-form {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16px;
+  align-items: flex-end;
+}
+
+.search-area .el-form-item {
+  margin-bottom: 0;
+  margin-right: 0;
+}
+
+.search-area .el-input,
+.search-area .el-select,
+.search-area .el-date-picker {
+  width: 200px;
+}
+
 .pagination-wrapper {
   margin-top: 16px;
   display: flex;

+ 78 - 32
src/views/reconciliation/invoiceManage/index.vue

@@ -2,7 +2,34 @@
   <div class="page-container">
     <PageTitle title="开票管理" />
 
-    <SearchBar :form="searchForm" :filters="filters" placeholder="开票编号" />
+    <!-- 搜索条件区域 -->
+    <div class="search-area">
+      <el-form :model="searchForm" inline>
+        <el-form-item label="开票编号">
+          <el-input v-model="searchForm.keyword" placeholder="请输入开票编号" clearable @clear="handleSearch" />
+        </el-form-item>
+        <el-form-item label="开票日期">
+          <el-date-picker
+            v-model="searchForm.dateRange"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="YYYY-MM-DD"
+            @change="handleSearch"
+          />
+        </el-form-item>
+        <el-form-item label="开票状态">
+          <el-select v-model="searchForm.invoiceStatus" placeholder="请选择开票状态" clearable @change="handleSearch">
+            <el-option v-for="item in invoiceStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">搜索</el-button>
+          <el-button @click="handleReset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
 
     <el-table v-loading="loading" :data="tableData" border style="width: 100%">
       <el-table-column prop="statementInvoiceNo" label="开票编号" min-width="130" align="center" />
@@ -33,13 +60,14 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, computed, onMounted, watch } from 'vue';
+import { reactive, ref, onMounted, watch, computed, getCurrentInstance, toRefs } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { useRouter } from 'vue-router';
-import { PageTitle, SearchBar, TablePagination } from '@/components';
+import { PageTitle, TablePagination } from '@/components';
 import { getStatementInvoiceList } from '@/api/pc/enterprise/statement';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { invoice_status } = toRefs<any>(proxy?.useDict('invoice_issuance_status', 'statement_status', 'invoice_status'));
+const { invoice_issuance_status, invoice_status } = toRefs<any>(proxy?.useDict('invoice_issuance_status', 'invoice_status'));
+// const { invoice_status } = toRefs<any>(proxy?.useDict('invoice_issuance_status', 'statement_status', 'invoice_status'));
 const searchForm = reactive({
   keyword: '',
   dateRange: [],
@@ -49,24 +77,32 @@ const form = reactive({
   statementOrderIds: []
 });
 
-const invoiceStatusOptions = ref([{ label: '全部', value: '' }]);
-
-const filters = ref([{ field: 'invoiceStatus', label: '开票状态', options: invoiceStatusOptions.value }]);
+// 从字典生成选项,添加"全部"选项
+const invoiceStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(invoice_status.value || [])]);
 
 const pagination = reactive({ page: 1, pageSize: 5, total: 0 });
 const tableData = ref<any[]>([]);
 const loading = ref(false);
 const router = useRouter();
-// 加载对账单列表
+// 加载开票列表
 const loadStatementInvoice = async () => {
   try {
     loading.value = true;
-    const res = await getStatementInvoiceList({
+    const queryParams = {
       pageNum: pagination.page,
       pageSize: pagination.pageSize,
       statementInvoiceNo: searchForm.keyword,
-      isInvoiceStatus: searchForm.invoiceStatus
-    });
+      invoiceStatus: searchForm.invoiceStatus,
+      params: {} as any
+    };
+
+    // 添加日期范围参数
+    if (searchForm.dateRange && searchForm.dateRange.length === 2) {
+      queryParams.params.beginTime = searchForm.dateRange[0];
+      queryParams.params.endTime = searchForm.dateRange[1];
+    }
+
+    const res = await getStatementInvoiceList(queryParams);
 
     if (res.code === 200 && res.rows) {
       tableData.value = res.rows.map((item: any) => ({
@@ -104,13 +140,28 @@ watch(
 
 // 监听搜索条件变化
 watch(
-  () => [searchForm.keyword, searchForm.invoiceStatus],
+  () => [searchForm.keyword, searchForm.dateRange, searchForm.invoiceStatus],
   () => {
     pagination.page = 1;
     loadStatementInvoice();
   }
 );
 
+// 搜索
+const handleSearch = () => {
+  pagination.page = 1;
+  loadStatementInvoice();
+};
+
+// 重置
+const handleReset = () => {
+  searchForm.keyword = '';
+  searchForm.dateRange = [];
+  searchForm.invoiceStatus = '';
+  pagination.page = 1;
+  loadStatementInvoice();
+};
+
 // 页面加载时获取数据
 onMounted(() => {
   // loadDictData();
@@ -123,35 +174,30 @@ const handleView = (row: any) => {
 </script>
 
 <style scoped>
-.bottom-bar {
-  margin-top: 16px;
+.search-area {
+  margin-bottom: 16px;
   padding: 16px;
-  background: #fafafa;
-  border: 1px solid #eee;
+  background: #fff;
+  border: 1px solid #e4e7ed;
   border-radius: 4px;
-  display: flex;
-  justify-content: flex-end;
 }
 
-.summary {
+.search-area .el-form {
   display: flex;
-  align-items: center;
-  gap: 20px;
-}
-
-.summary span {
-  font-size: 14px;
-  color: #666;
+  flex-wrap: wrap;
+  gap: 16px;
+  align-items: flex-end;
 }
 
-.summary em {
-  color: #e60012;
-  font-style: normal;
-  font-weight: bold;
+.search-area .el-form-item {
+  margin-bottom: 0;
+  margin-right: 0;
 }
 
-.summary .total em {
-  font-size: 16px;
+.search-area .el-input,
+.search-area .el-select,
+.search-area .el-date-picker {
+  width: 200px;
 }
 
 .pagination-wrapper {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 4
src/views/reg/index.vue


+ 10 - 0
src/views/search/index.vue

@@ -487,6 +487,16 @@ onMounted(() => {
       padding: 20px;
       cursor: pointer;
       border-radius: 5px;
+      transition:
+        transform 0.2s ease,
+        box-shadow 0.2s ease;
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+        .itemName {
+          color: #e7000b !important;
+        }
+      }
       .data-img {
         width: 100%;
         aspect-ratio: 1;

+ 10 - 0
src/views/search/special.vue

@@ -292,6 +292,16 @@ onMounted(() => {
       padding: 20px;
       cursor: pointer;
       border-radius: 5px;
+      transition:
+        transform 0.2s ease,
+        box-shadow 0.2s ease;
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+        .itemName {
+          color: #e7000b !important;
+        }
+      }
       .data-img {
         width: 100%;
         aspect-ratio: 1;

+ 126 - 27
src/views/valueAdded/complaint/index.vue

@@ -24,16 +24,28 @@
 
       <!-- 上传照片 -->
       <el-form-item label="上传照片">
-        <el-upload
-          class="avatar-uploader"
-          :action="action"
-          :show-file-list="false"
-          :on-success="handleAvatarSuccess"
-          :before-upload="beforeAvatarUpload"
-        >
-          <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
-        </el-upload>
-        <img class="upload-img" v-if="form.relatedPictures" :src="form.relatedPictures" />
+        <div class="upload-row">
+          <el-upload
+            v-if="!form.relatedPictures || form.relatedPictures.length < 3"
+            class="upload-box"
+            :action="action"
+            :show-file-list="false"
+            :on-success="handleAvatarSuccess"
+            :before-upload="beforeAvatarUpload"
+            accept="image/*"
+          >
+            <div class="upload-placeholder">
+              <el-icon :size="24"><Plus /></el-icon>
+            </div>
+          </el-upload>
+          <div v-if="form.relatedPictures && form.relatedPictures.length > 0" class="image-list">
+            <div v-for="(img, imgIndex) in form.relatedPictures" :key="imgIndex" class="image-item">
+              <el-image :src="img" fit="cover" style="width: 100px; height: 100px; border-radius: 6px" />
+              <el-icon class="delete-icon" @click="handleDeleteImage(imgIndex as any)"><Close /></el-icon>
+            </div>
+          </div>
+        </div>
+        <div class="upload-tip">最多上传3张图片</div>
       </el-form-item>
 
       <!-- 联系方式 -->
@@ -51,15 +63,15 @@
 
 <script setup lang="ts">
 import { reactive } from 'vue';
-import { Plus } from '@element-plus/icons-vue';
+import { Plus, Close } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 import { PageTitle } from '@/components';
 import { addComplaintsSuggestions } from '@/api/pc/valueAdded';
+import { onPath } from '@/utils/siteConfig';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { complaints_suggestion_type } = toRefs<any>(proxy?.useDict('complaints_suggestion_type'));
 const action = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
 import type { UploadProps } from 'element-plus';
-
 // 反馈类型选项
 const feedbackTypes = computed(() => complaints_suggestion_type.value || []);
 
@@ -67,7 +79,7 @@ const feedbackTypes = computed(() => complaints_suggestion_type.value || []);
 const form = reactive({
   feedbackType: '0',
   feedbackContent: '',
-  relatedPictures: '',
+  relatedPictures: [] as string[],
   phone: ''
 });
 const rules = {
@@ -75,13 +87,17 @@ const rules = {
   phone: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }]
 };
 
-// 上传图片
-const handleUploadChange = (file, fileList) => {};
-
 //上传成功
 const handleAvatarSuccess = (res: any) => {
   if (res.code == 200) {
-    form.relatedPictures = res.data.url;
+    if (!form.relatedPictures) {
+      form.relatedPictures = [];
+    }
+    if (form.relatedPictures.length >= 3) {
+      ElMessage.warning('最多上传3张图片');
+      return;
+    }
+    form.relatedPictures.push(res.data.url);
   } else {
     ElMessage({
       message: res.msg,
@@ -91,9 +107,19 @@ const handleAvatarSuccess = (res: any) => {
   console.log(res);
 };
 
+// 删除图片
+const handleDeleteImage = (imageIndex: number) => {
+  form.relatedPictures.splice(imageIndex, 1);
+};
+
 const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
+  const isImage = rawFile.type.startsWith('image/');
+  if (!isImage) {
+    ElMessage.error('只能上传图片文件!');
+    return false;
+  }
   if (rawFile.size / 1024 / 1024 > 2) {
-    ElMessage.error('不能大于2MB!');
+    ElMessage.error('图片大小不能超过2MB!');
     return false;
   }
   return true;
@@ -105,8 +131,12 @@ const handleSubmit = async () => {
     return;
   }
   try {
-    await addComplaintsSuggestions(form);
-    ElMessage.success('提交成功');
+    const submitData = {
+      ...form,
+      relatedPictures: form.relatedPictures && form.relatedPictures.length > 0 ? form.relatedPictures.join(',') : ''
+    };
+    await addComplaintsSuggestions(submitData);
+    onPath('/order/afterSale?tab=complaint');
   } catch (error) {
     ElMessage.error('提交失败');
   }
@@ -140,21 +170,90 @@ const handleSubmit = async () => {
   }
 }
 
-:deep(.el-upload--picture-card) {
-  width: 100px;
-  height: 100px;
+.upload-row {
+  display: flex;
+  align-items: flex-start;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+.upload-box {
+  :deep(.el-upload) {
+    width: 100px;
+    height: 100px;
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    overflow: hidden;
+
+    &:hover {
+      border-color: #e60012;
+    }
+  }
+
+  .upload-placeholder {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    color: #999;
+    font-size: 12px;
+    gap: 5px;
+  }
+
+  .upload-preview {
+    width: 100%;
+    height: 100%;
+
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+  }
 }
 
-:deep(.el-upload-list--picture-card .el-upload-list__item) {
-  width: 100px;
-  height: 100px;
+.image-list {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+  margin-top: 0;
+
+  .image-item {
+    position: relative;
+    width: 100px;
+    height: 100px;
+    flex-shrink: 0;
+
+    .delete-icon {
+      position: absolute;
+      top: 2px;
+      right: 2px;
+      width: 20px;
+      height: 20px;
+      background: rgba(0, 0, 0, 0.5);
+      color: #fff;
+      border-radius: 50%;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 14px;
+
+      &:hover {
+        background: rgba(0, 0, 0, 0.7);
+      }
+    }
+  }
 }
 
 .upload-tip {
   font-size: 12px;
   color: #999;
-  text-align: center;
+  margin-top: 5px;
 }
+
 .tag-group {
   display: flex;
   flex-wrap: wrap;

+ 196 - 65
src/views/valueAdded/maintenance/apply.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="maintenance-apply-container">
     <div class="page-header">
-      <div class="header-left">维保申请</div>
+      <div class="header-left">{{ pageTitle }}</div>
       <div class="header-right" @click="handleBack">返回</div>
     </div>
 
@@ -25,24 +25,29 @@
       </div>
 
       <div class="form-container">
-        <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="apply-form" hide-required-asterisk>
+        <el-form :model="form" :rules="mode === 'view' ? {} : rules" ref="formRef" label-width="120px" class="apply-form" hide-required-asterisk>
           <el-form-item label="申请人名称:" prop="applicantName">
-            <el-input v-model="form.applicantName" placeholder="请输入申请人名称" clearable />
+            <el-input v-model="form.applicantName" placeholder="请输入申请人名称" clearable :disabled="mode === 'view'" />
           </el-form-item>
 
-          <!-- The mockup shows a validation error explicitly for phone -->
-          <el-form-item label="手机号码:" prop="phone" class="phone-item">
-            <el-input v-model="form.phone" placeholder="请输入申请人手机号码" clearable />
+          <!-- The mockup shows a validation error explicitly for applicantPhone -->
+          <el-form-item label="手机号码:" prop="applicantPhone" class="applicantPhone-item">
+            <el-input v-model="form.applicantPhone" placeholder="请输入申请人手机号码" clearable :disabled="mode === 'view'" />
           </el-form-item>
 
-          <el-form-item label="验证码:" prop="verifyCode">
+          <el-form-item label="验证码:" prop="verifyCode" v-if="mode !== 'view'">
             <div class="verify-code-wrapper">
-              <el-input v-model="form.verifyCode" placeholder="短信验证码" class="code-input" clearable />
-              <div class="send-btn" @click="handleSendCode">发送验证码</div>
+              <el-input v-model="form.verifyCode" placeholder="短信验证码" class="code-input" clearable>
+                <template #suffix>
+                  <span @click="handleSendCode" :class="['code', countdown > 0 ? 'disabled' : '']">
+                    {{ codeText }}
+                  </span>
+                </template>
+              </el-input>
             </div>
           </el-form-item>
 
-          <el-form-item label="">
+          <!-- <el-form-item label="">
             <div class="drag-verify-bar" @click="handleVerify">
               <div class="verify-btn" :class="{ verified: isVerified }">
                 <div class="inner-dot" v-if="!isVerified"></div>
@@ -50,43 +55,44 @@
               </div>
               <div class="verify-text">{{ isVerified ? '验证通过' : '点击验证' }}</div>
             </div>
-          </el-form-item>
+          </el-form-item> -->
 
           <el-form-item label="服务时间:" prop="serviceTime">
-            <el-select v-model="form.serviceTime" placeholder="请选择服务时间" class="w-full" filterable>
+            <el-select v-model="form.serviceTime" placeholder="请选择服务时间" class="w-full" filterable :disabled="mode === 'view'">
               <el-option v-for="dict in service_time_type" :key="dict.value" :label="dict.label" :value="dict.value" />
             </el-select>
           </el-form-item>
 
-          <el-form-item label="月度维保次数:" prop="monthlyTimes">
-            <el-input v-model="form.monthlyTimes" placeholder="请输入月度维保次数" clearable />
+          <el-form-item label="月度维保次数:" prop="monthMainten">
+            <el-input v-model="form.monthMainten" placeholder="请输入月度维保次数" clearable :disabled="mode === 'view'" />
           </el-form-item>
 
           <el-form-item label="维保类型:" prop="maintainType">
-            <el-select v-model="form.maintainType" placeholder="请选择" class="w-full" filterable>
+            <el-select v-model="form.maintainType" placeholder="请选择" class="w-full" filterable :disabled="mode === 'view'">
               <el-option v-for="dict in maintenance_type" :key="dict.value" :label="dict.label" :value="dict.value" />
             </el-select>
           </el-form-item>
 
-          <el-form-item label="服务内容:" prop="serviceContent">
+          <el-form-item label="服务内容:" prop="serviceContentArr">
             <div class="service-content-wrapper">
               <div class="service-tags">
-                <div class="tag-item" :class="{ active: form.serviceContent.includes('express') }" @click="toggleServiceContent('express')">
-                  急速快递
-                </div>
-                <div class="tag-item" :class="{ active: form.serviceContent.includes('repair') }" @click="toggleServiceContent('repair')">
-                  售后维修
-                </div>
-                <div class="tag-item" :class="{ active: form.serviceContent.includes('warranty') }" @click="toggleServiceContent('warranty')">
-                  产品质保
-                </div>
+                <el-checkbox-group v-model="form.serviceContentArr" :disabled="mode === 'view'">
+                  <el-checkbox v-for="item in serviceContentList" :key="item.id" :label="item.id">{{ item.itemName }}</el-checkbox>
+                </el-checkbox-group>
               </div>
-              <el-input v-model="form.otherService" type="textarea" :rows="4" placeholder="请输入其他服务" class="other-service-input" />
+              <el-input
+                v-model="form.otherService"
+                type="textarea"
+                :rows="4"
+                placeholder="请输入其他服务"
+                class="other-service-input"
+                :disabled="mode === 'view'"
+              />
             </div>
           </el-form-item>
 
-          <el-form-item>
-            <el-button type="primary" class="next-btn" @click="handleNext">下一步</el-button>
+          <el-form-item v-if="mode !== 'view'">
+            <el-button type="primary" class="next-btn" @click="handleNext">{{ mode === 'edit' ? '保存修改' : '下一步' }}</el-button>
           </el-form-item>
         </el-form>
       </div>
@@ -95,30 +101,53 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue';
-import { useRouter } from 'vue-router';
+import { smsCode } from '@/api/breg/index';
+import { addMaintainInfo, updateMaintainInfo, listServerItem, getMaintainInfo } from '@/api/pc/valueAdded';
+import { ref, reactive, onMounted, computed } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
 import { Check } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { service_time_type, maintenance_type } = toRefs<any>(proxy?.useDict('service_time_type', 'maintenance_type'));
 
 const router = useRouter();
+const route = useRoute();
 const formRef = ref();
 
-const isVerified = ref(false);
+// 模式:add-新增,view-查看,edit-修改
+const mode = ref<'add' | 'view' | 'edit'>('add');
+
+// 根据模式显示不同的页面标题
+const pageTitle = computed(() => {
+  switch (mode.value) {
+    case 'view':
+      return '查看维保';
+    case 'edit':
+      return '修改维保';
+    default:
+      return '维保申请';
+  }
+});
 
+const isVerified = ref(false);
+const codeText = ref<string>('发送验证码');
+const countdown = ref<number>(0);
+const timer = ref<any>(null);
+const serviceContentList = ref<any[]>([]);
 const form = reactive({
+  id: null,
   applicantName: '',
-  phone: '',
+  applicantPhone: '',
   verifyCode: '',
   serviceTime: '',
-  monthlyTimes: '',
+  monthMainten: '',
   maintainType: '',
-  serviceContent: [] as string[],
+  serviceContentArr: [],
+  serviceContent: '',
   otherService: ''
 });
 
-// To perfectly match the mockup which shows a persistent validation error on phone
+// To perfectly match the mockup which shows a persistent validation error on applicantPhone
 const validatePhone = (rule: any, value: string, callback: any) => {
   if (!value) {
     callback(new Error('请输入手机号码'));
@@ -131,59 +160,152 @@ const validatePhone = (rule: any, value: string, callback: any) => {
 
 const rules = {
   applicantName: [{ required: true, message: '请输入申请人名称', trigger: 'blur' }],
-  phone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
+  applicantPhone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
   verifyCode: [{ required: true, message: '请输入短信验证码', trigger: 'blur' }],
   serviceTime: [{ required: true, message: '请选择服务时间', trigger: 'change' }],
-  monthlyTimes: [{ required: true, message: '请输入月度维保次数', trigger: 'blur' }],
+  monthMainten: [{ required: true, message: '请输入月度维保次数', trigger: 'blur' }],
   maintainType: [{ required: true, message: '请选择维保类型', trigger: 'change' }]
 };
 
 const handleBack = () => {
   router.back();
 };
-
+// 启动倒计时
+const startCountdown = () => {
+  countdown.value = 60;
+  codeText.value = `${countdown.value}s 后重新发送`;
+
+  timer.value = setInterval(() => {
+    countdown.value--;
+    if (countdown.value > 0) {
+      codeText.value = `${countdown.value}s 后重新发送`;
+    } else {
+      clearInterval(timer.value);
+      timer.value = null;
+      codeText.value = '发送验证码';
+    }
+  }, 1000);
+};
 const handleSendCode = () => {
-  if (!form.phone) {
-    ElMessage.warning('请先输入手机号码');
+  // 防止倒计时期间重复点击
+  if (countdown.value > 0) return;
+
+  const applicantPhone = form.applicantPhone;
+
+  // 1. 基础格式校验
+  if (!validateMobile(applicantPhone)) {
+    ElMessage({
+      message: '请输入正确的手机号码',
+      type: 'warning'
+    });
     return;
   }
-  ElMessage.success('验证码已发送');
+
+  // 2. 【新增】先调用接口查询用户是否存在
+
+  smsCode({ phonenumber: applicantPhone })
+    .then((smsRes: any) => {
+      if (smsRes.code === 200) {
+        ElMessage({
+          message: '验证码已发送',
+          type: 'success'
+        });
+        startCountdown(); // 开始倒计时
+      } else {
+        // 发送短信接口业务失败
+        ElMessage.error(smsRes.msg || '发送验证码失败');
+      }
+    })
+    .catch((err: any) => {
+      // 发送短信接口网络错误或异常
+      ElMessage.error(err.msg || '发送验证码请求异常');
+    });
 };
 
+// 验证手机号
+const validateMobile = (applicantPhone: any) => {
+  const reg = /^1[3-9]\d{9}$/;
+  return reg.test(applicantPhone);
+};
 const handleVerify = () => {
   isVerified.value = true;
 };
 
-const toggleServiceContent = (type: string) => {
-  const index = form.serviceContent.indexOf(type);
-  if (index > -1) {
-    form.serviceContent.splice(index, 1);
-  } else {
-    form.serviceContent.push(type);
+const handleNext = async () => {
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
+  if (valid) {
+    // 将服务内容字符串(ID列表)转换为数组
+    form.serviceContent = Array.isArray(form.serviceContentArr) ? form.serviceContentArr.join(',') : form.serviceContentArr;
+    if (form.id) {
+      await updateMaintainInfo(form);
+      ElMessage.success('修改成功');
+    } else {
+      await addMaintainInfo(form);
+      ElMessage.success('提交成功');
+    }
+    router.back();
   }
 };
 
-const handleNext = async () => {
-  if (!formRef.value) return;
-  await formRef.value.validate((valid: boolean) => {
-    if (valid) {
-      if (!isVerified.value) {
-        ElMessage.warning('请先点击验证');
-        return;
+// 加载维保详情
+const loadMaintainInfo = async (id: string) => {
+  try {
+    const res = await getMaintainInfo(id as any);
+    if (res.code === 200 && res.data) {
+      const data = res.data;
+      form.id = data.id;
+      form.applicantName = data.applicantName || '';
+      form.applicantPhone = data.applicantPhone || '';
+      form.serviceTime = data.serviceTime || '';
+      form.monthMainten = data.monthMainten || '';
+      form.maintainType = data.maintainType || '';
+      form.otherService = data.otherService || '';
+
+      // 将服务内容字符串(逗号拼接的id)转换为数字数组
+      if (data.serviceContent) {
+        const serviceIds = data.serviceContent.split(',');
+        form.serviceContentArr = serviceIds;
+      } else {
+        form.serviceContentArr = [];
       }
-      ElMessage.success('提交成功');
-      // router.push('...');
     }
-  });
+  } catch (error) {
+    ElMessage.error('加载维保详情失败');
+  }
 };
+// 加载服务内容列表
+const loadServiceContentList = async () => {
+  try {
+    const res = await listServerItem({});
+    serviceContentList.value = res.rows || [];
+  } catch (error) {
+    ElMessage.error('加载服务内容列表失败');
+  }
+};
+onMounted(async () => {
+  // 先加载服务内容列表,确保复选框选项可用
+  await loadServiceContentList();
 
-onMounted(() => {
-  // To trigger the phone error message immediately as shown in the mockup
-  setTimeout(() => {
-    if (formRef.value) {
-      formRef.value.validateField('phone').catch(() => {});
-    }
-  }, 500);
+  // 从路由参数获取模式和ID
+  const queryMode = route.query.mode as string;
+  const queryId = route.query.id as string;
+
+  if (queryMode && (queryMode === 'view' || queryMode === 'edit')) {
+    mode.value = queryMode;
+  }
+
+  // 如果有ID,加载维保详情
+  if (queryId) {
+    await loadMaintainInfo(queryId);
+  } else {
+    // To trigger the applicantPhone error message immediately as shown in the mockup
+    setTimeout(() => {
+      if (formRef.value) {
+        formRef.value.validateField('applicantPhone').catch(() => {});
+      }
+    }, 500);
+  }
 });
 </script>
 
@@ -445,7 +567,16 @@ onMounted(() => {
     width: 100%;
   }
 }
+.code {
+  font-size: 14px;
+  color: #e7000b;
+  cursor: pointer;
 
+  &.disabled {
+    color: #999;
+    cursor: not-allowed;
+  }
+}
 .next-btn {
   // background-color: #0fb881;
   // border-color: #0fb881;
@@ -463,7 +594,7 @@ onMounted(() => {
 }
 
 /* Override error styles to match exactly the mockup */
-:deep(.phone-item.is-error) {
+:deep(.applicantPhone-item.is-error) {
   .el-input__wrapper {
     box-shadow: 0 0 0 1px #f56c6c inset !important;
   }

+ 95 - 3
src/views/valueAdded/maintenance/index.vue

@@ -1,8 +1,38 @@
 <template>
   <div class="maintenance-container">
     <PageTitle title="维保服务" />
+    <el-button type="danger" plain @click="handleApply" style="float: right; margin-bottom: 10px">新增维保</el-button>
+    <div v-if="tableData.length">
+      <el-table v-loading="loading" :data="tableData" border style="width: 100%" ref="tableRef">
+        <el-table-column prop="applicantName" label="申请人名称" min-width="130" align="center" />
+        <el-table-column prop="applicantPhone" label="手机号码" min-width="110" align="center" />
+        <el-table-column prop="serviceTime" label="服务时间" align="center">
+          <template #default="{ row }">
+            {{ getDictLabel(service_time_type, row.serviceTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="monthMainten" label="月度维保次数" align="center" />
 
-    <div class="empty-state">
+        <el-table-column prop="maintainType" label="维保类型" min-width="90" align="center">
+          <template #default="{ row }">
+            {{ getDictLabel(maintenance_type, row.maintainType) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="serviceContentStr" label="维保类型" align="center" />
+        <el-table-column prop="status" label="维保类型" min-width="90" align="center">
+          <template #default="{ row }">
+            {{ row.status === '0' ? '待审核' : row.status === '1' ? '已通过' : row.status === '2' ? '已驳回' : '取消' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="100" align="center">
+          <template #default="{ row }">
+            <el-button type="primary" link size="small" @click="handleView(row)">查看</el-button>
+            <el-button type="danger" link size="small" @click="handleUpdate(row)">修改</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="empty-state" v-else>
       <div class="empty-icon">
         <img
           src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 160'%3E%3Crect fill='%23f5f5f5' width='200' height='160' rx='8'/%3E%3Crect x='50' y='60' width='100' height='70' fill='%23e0e0e0' rx='4'/%3E%3Crect x='60' y='70' width='80' height='8' fill='%23ccc'/%3E%3Crect x='60' y='85' width='60' height='6' fill='%23ccc'/%3E%3Crect x='60' y='98' width='70' height='6' fill='%23ccc'/%3E%3Cpath d='M85 45 L95 55 L115 35' stroke='%23ccc' stroke-width='4' fill='none'/%3E%3C/svg%3E"
@@ -16,14 +46,76 @@
 </template>
 
 <script setup lang="ts">
+import { getMaintainInfoList, listServerItem } from '@/api/pc/valueAdded';
 import { PageTitle } from '@/components';
 import { useRouter } from 'vue-router';
-
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { service_time_type, maintenance_type } = toRefs<any>(proxy?.useDict('service_time_type', 'maintenance_type'));
 const router = useRouter();
-
+const pagination = reactive({ page: 1, pageSize: 5, total: 0 });
+const tableData = ref<any[]>([]);
+const loading = ref(false);
+const serviceContentList = ref<any[]>([]);
 const handleApply = () => {
   router.push(`/valueAdded/maintenanceApply`);
 };
+
+// 查看维保详情
+const handleView = (row: any) => {
+  router.push({
+    path: `/valueAdded/maintenanceApply`,
+    query: { id: row.id, mode: 'view' }
+  });
+};
+
+// 修改维保信息
+const handleUpdate = (row: any) => {
+  router.push({
+    path: `/valueAdded/maintenanceApply`,
+    query: { id: row.id, mode: 'edit' }
+  });
+};
+
+// 加载对账单列表
+const loadMaintainInfoList = async () => {
+  try {
+    loading.value = true;
+    const queryParams = {
+      pageNum: pagination.page,
+      pageSize: pagination.pageSize
+    };
+
+    const res = await getMaintainInfoList(queryParams);
+
+    if (res.code === 200 && res.rows) {
+      tableData.value = res.rows;
+      pagination.total = res.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('加载维保列表失败');
+  } finally {
+    loading.value = false;
+  }
+};
+const getDictLabel = (dictOptions: any[], value: string) => {
+  if (!dictOptions || !value) return value;
+  const dict = dictOptions.find((item) => item.value === value);
+  return dict ? dict.label : value;
+};
+
+// 加载服务内容列表
+const loadServiceContentList = async () => {
+  try {
+    const res = await listServerItem({});
+    serviceContentList.value = res.rows || [];
+  } catch (error) {
+    ElMessage.error('加载服务内容列表失败');
+  }
+};
+onMounted(() => {
+  loadServiceContentList();
+  loadMaintainInfoList();
+});
 </script>
 
 <style scoped lang="scss">

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác