瀏覽代碼

Merge branch 'master' of http://192.168.100.32:3000/fanfan/xt-front

Tong 2 年之前
父節點
當前提交
9adc0f0b25
共有 30 個文件被更改,包括 2284 次插入335 次删除
  1. 1 1
      package.json
  2. 713 275
      src/assets/iconfont/iconfont.css
  3. 2 0
      src/components/Form/src/componentMap.ts
  4. 72 0
      src/components/Form/src/components/RadioDescGroup.vue
  5. 1 0
      src/components/Form/src/types/index.ts
  6. 4 4
      src/components/SimpleMenu/src/components/menu.less
  7. 76 3
      src/components/Table/src/BasicTable.vue
  8. 4 3
      src/components/Table/src/components/EditTableHeaderIcon.vue
  9. 20 2
      src/components/Table/src/components/TableHeader.vue
  10. 21 1
      src/components/Table/src/components/TableTitle.vue
  11. 5 3
      src/components/Table/src/components/editable/EditableCell.vue
  12. 4 1
      src/components/Table/src/hooks/useTableHeader.ts
  13. 17 9
      src/components/Table/src/hooks/useTableScroll.ts
  14. 16 1
      src/components/Table/src/props.ts
  15. 14 10
      src/components/Table/src/types/table.ts
  16. 233 0
      src/components/XTCard/src/ChartsCard.vue
  17. 259 0
      src/components/XTCard/src/DescCard.vue
  18. 45 7
      src/components/XTForm/src/XTForm.vue
  19. 2 0
      src/components/XTForm/src/componentEnum.ts
  20. 0 0
      src/components/XTList/index.ts
  21. 182 0
      src/components/XTList/src/List.vue
  22. 53 0
      src/components/XTList/src/Menu.vue
  23. 27 3
      src/components/XTTab/src/XTTab.vue
  24. 0 0
      src/components/XTTimeLine/index.ts
  25. 142 0
      src/components/XTTimeLine/src/TimeLine.vue
  26. 2 2
      src/enums/colorEnum.ts
  27. 2 0
      src/main.ts
  28. 6 0
      src/utils/lib/echarts.ts
  29. 97 9
      src/views/biz/visit/ready/data.ts
  30. 264 1
      src/views/biz/visit/ready/index.vue

+ 1 - 1
package.json

@@ -44,7 +44,7 @@
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.12",
     "animate.css": "^4.1.1",
-    "ant-design-vue": "^3.2.15",
+    "ant-design-vue": "^3.2.20",
     "axios": "^1.2.2",
     "bpmn-js": "^11.5.0",
     "bpmn-js-properties-panel": "^1.20.0",

+ 713 - 275
src/assets/iconfont/iconfont.css

@@ -1,1088 +1,1526 @@
 @font-face {
-  font-family: "iconfont"; /* Project id 3806176 */
-  src: url('iconfont.woff2?t=1678070090615') format('woff2'),
-    url('iconfont.woff?t=1678070090615') format('woff'),
-    url('iconfont.ttf?t=1678070090615') format('truetype');
+  font-family: 'iconfont'; /* Project id 3806176 */
+  src: url('//at.alicdn.com/t/c/font_3806176_bwn3g1i4dft.woff2?t=1685318825278') format('woff2'),
+    url('//at.alicdn.com/t/c/font_3806176_bwn3g1i4dft.woff?t=1685318825278') format('woff'),
+    url('//at.alicdn.com/t/c/font_3806176_bwn3g1i4dft.ttf?t=1685318825278') format('truetype');
 }
 
 .iconfont {
-  font-family: "iconfont" !important;
+  font-family: 'iconfont' !important;
   font-size: 16px;
   font-style: normal;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
-  margin-right: 4px;
+}
+
+.icon-xt-dual-pump_default:before {
+  content: '\e73d';
+}
+
+.icon-xt-bed_disable:before {
+  content: '\e73e';
+}
+
+.icon-xt-bed_default:before {
+  content: '\e73a';
+}
+
+.icon-xt-edit-white_default:before {
+  content: '\e73b';
+}
+
+.icon-xt-bed_selected:before {
+  content: '\e73c';
+}
+
+.icon-xt-success:before {
+  content: '\e738';
+}
+
+.icon-xt-fail:before {
+  content: '\e739';
+}
+
+.icon-xt-details-refresh_selected:before {
+  content: '\e737';
+}
+
+.icon-xt-details_default:before {
+  content: '\e733';
+}
+
+.icon-xt-details_save_default:before {
+  content: '\e734';
+}
+
+.icon-xt-details_save_selected:before {
+  content: '\e735';
+}
+
+.icon-xt-details_selected:before {
+  content: '\e736';
+}
+
+.icon-xt-annex:before {
+  content: '\e729';
+}
+
+.icon-xt-details_screen_default:before {
+  content: '\e72a';
+}
+
+.icon-xt-numerical_up:before {
+  content: '\e72b';
+}
+
+.icon-xt-numerical_down:before {
+  content: '\e72c';
+}
+
+.icon-xt-details_upload_default:before {
+  content: '\e72d';
+}
+
+.icon-xt-details_upload_selected:before {
+  content: '\e72e';
+}
+
+.icon-xt-details_screen_selected:before {
+  content: '\e72f';
+}
+
+.icon-xt-abnormal:before {
+  content: '\e730';
+}
+
+.icon-xt-details-annex:before {
+  content: '\e731';
+}
+
+.icon-xt-details-refresh_default:before {
+  content: '\e732';
+}
+
+.icon-xt-add_disable:before {
+  content: '\e721';
+}
+
+.icon-xt-add_default:before {
+  content: '\e722';
+}
+
+.icon-xt-reduce_selected:before {
+  content: '\e723';
+}
+
+.icon-xt-add_selected:before {
+  content: '\e724';
+}
+
+.icon-xt-release_default:before {
+  content: '\e725';
+}
+
+.icon-xt-reduce_disable:before {
+  content: '\e726';
+}
+
+.icon-xt-release_selected:before {
+  content: '\e727';
+}
+
+.icon-xt-reduce_default:before {
+  content: '\e728';
+}
+
+.icon-xt-drawer-close_selected:before {
+  content: '\e71d';
+}
+
+.icon-xt-drawer-close_default:before {
+  content: '\e71e';
+}
+
+.icon-xt-drawer-return_default:before {
+  content: '\e71f';
+}
+
+.icon-xt-drawer-return_selected:before {
+  content: '\e720';
+}
+
+.icon-xt-details_edit_selected:before {
+  content: '\e713';
+}
+
+.icon-xt-details_more_selected:before {
+  content: '\e714';
+}
+
+.icon-xt-details_edit_disable:before {
+  content: '\e715';
+}
+
+.icon-xt-details_more_default:before {
+  content: '\e716';
+}
+
+.icon-xt-details_edit_default:before {
+  content: '\e717';
+}
+
+.icon-xt-details_delete_disable:before {
+  content: '\e718';
+}
+
+.icon-xt-details_increase_default:before {
+  content: '\e719';
+}
+
+.icon-xt-details_delete_selected:before {
+  content: '\e71a';
+}
+
+.icon-xt-details_increase_selected:before {
+  content: '\e71b';
+}
+
+.icon-xt-details_delete_default:before {
+  content: '\e71c';
+}
+
+.icon-xt-close_default:before {
+  content: '\e70d';
+}
+
+.icon-xt-import_default:before {
+  content: '\e70e';
+}
+
+.icon-xt-close_selected:before {
+  content: '\e70f';
+}
+
+.icon-xt-screen_selected:before {
+  content: '\e710';
+}
+
+.icon-xt-screen_default:before {
+  content: '\e711';
+}
+
+.icon-xt-import_selected:before {
+  content: '\e712';
+}
+
+.icon-xt-sort_up:before {
+  content: '\e708';
+}
+
+.icon-xt-medical_default:before {
+  content: '\e709';
+}
+
+.icon-xt-sort_down:before {
+  content: '\e70a';
+}
+
+.icon-xt-medical_selected:before {
+  content: '\e70b';
+}
+
+.icon-xt-sort:before {
+  content: '\e70c';
+}
+
+.icon-xt-edit_selected:before {
+  content: '\e6f2';
+}
+
+.icon-xt-edit_default:before {
+  content: '\e6f3';
+}
+
+.icon-xt-ward_selected:before {
+  content: '\e6f4';
+}
+
+.icon-xt-ward_default:before {
+  content: '\e6f5';
+}
+
+.icon-xt-print_selected:before {
+  content: '\e6f6';
+}
+
+.icon-xt-print_default:before {
+  content: '\e6f7';
+}
+
+.icon-xt-arrow_up_selected:before {
+  content: '\e6f8';
+}
+
+.icon-xt-arrow_down:before {
+  content: '\e6f9';
+}
+
+.icon-xt-download_default:before {
+  content: '\e6fa';
+}
+
+.icon-xt-export_default:before {
+  content: '\e6fb';
+}
+
+.icon-xt-export_selected:before {
+  content: '\e6fc';
+}
+
+.icon-xt-message_default:before {
+  content: '\e6fd';
+}
+
+.icon-xt-more_default:before {
+  content: '\e6fe';
+}
+
+.icon-xt-message_selected:before {
+  content: '\e6ff';
+}
+
+.icon-xt-search:before {
+  content: '\e700';
+}
+
+.icon-xt-download_selected:before {
+  content: '\e701';
+}
+
+.icon-xt-delete_default:before {
+  content: '\e702';
+}
+
+.icon-xt-more_selected:before {
+  content: '\e703';
+}
+
+.icon-xt-delete_selected:before {
+  content: '\e704';
+}
+
+.icon-xt-increase:before {
+  content: '\e705';
+}
+
+.icon-xt-arrow_up:before {
+  content: '\e706';
+}
+
+.icon-xt-logo_left:before {
+  content: '\e707';
+}
+
+.icon-xt-cost_selected:before {
+  content: '\e6d2';
+}
+
+.icon-xt-engineer_selected:before {
+  content: '\e6d3';
+}
+
+.icon-xt-cost_default:before {
+  content: '\e6d4';
+}
+
+.icon-xt-time:before {
+  content: '\e6d5';
+}
+
+.icon-xt-statistics_default:before {
+  content: '\e6d6';
+}
+
+.icon-xt-male_lg:before {
+  content: '\e6d7';
+}
+
+.icon-xt-today_selected:before {
+  content: '\e6d8';
+}
+
+.icon-xt-home_selected:before {
+  content: '\e6d9';
+}
+
+.icon-xt-health_selected:before {
+  content: '\e6da';
+}
+
+.icon-xt-home_default:before {
+  content: '\e6db';
+}
+
+.icon-xt-date:before {
+  content: '\e6dc';
+}
+
+.icon-xt-engineer_default:before {
+  content: '\e6dd';
+}
+
+.icon-xt-today_default:before {
+  content: '\e6de';
+}
+
+.icon-xt-male_sm:before {
+  content: '\e6df';
+}
+
+.icon-xt-reservation_selected:before {
+  content: '\e6e0';
+}
+
+.icon-xt-female_lg:before {
+  content: '\e6e1';
+}
+
+.icon-xt-female_sm:before {
+  content: '\e6e2';
+}
+
+.icon-xt-drug_default:before {
+  content: '\e6e3';
+}
+
+.icon-xt-record_selected:before {
+  content: '\e6e4';
+}
+
+.icon-xt-machine_default:before {
+  content: '\e6e5';
+}
+
+.icon-xt-quality_default:before {
+  content: '\e6e6';
+}
+
+.icon-xt-reservation_default:before {
+  content: '\e6e7';
+}
+
+.icon-xt-navigation_up:before {
+  content: '\e6e8';
+}
+
+.icon-xt-statistics_selected:before {
+  content: '\e6e9';
+}
+
+.icon-xt-health_default:before {
+  content: '\e6ea';
+}
+
+.icon-xt-navigation_down:before {
+  content: '\e6eb';
+}
+
+.icon-xt-set_default:before {
+  content: '\e6ec';
+}
+
+.icon-xt-machine_selected:before {
+  content: '\e6ed';
+}
+
+.icon-xt-quality_selected:before {
+  content: '\e6ee';
+}
+
+.icon-xt-set_selected:before {
+  content: '\e6ef';
+}
+
+.icon-xt-drug_selected:before {
+  content: '\e6f0';
+}
+
+.icon-xt-record_default:before {
+  content: '\e6f1';
+}
+
+.icon-lingdang:before {
+  content: '\e64a';
 }
 
 .icon-unlock:before {
-  content: "\e808";
+  content: '\e808';
 }
 
 .icon-lock:before {
-  content: "\e809";
+  content: '\e809';
 }
 
 .icon-customerservice:before {
-  content: "\e80a";
+  content: '\e80a';
 }
 
 .icon-medicinebox:before {
-  content: "\e80b";
+  content: '\e80b';
 }
 
 .icon-shop:before {
-  content: "\e80c";
+  content: '\e80c';
 }
 
 .icon-rocket:before {
-  content: "\e80d";
+  content: '\e80d';
 }
 
 .icon-shopping:before {
-  content: "\e80e";
+  content: '\e80e';
 }
 
 .icon-folder:before {
-  content: "\e80f";
+  content: '\e80f';
 }
 
 .icon-folder-open:before {
-  content: "\e810";
+  content: '\e810';
 }
 
 .icon-folder-add:before {
-  content: "\e811";
+  content: '\e811';
 }
 
 .icon-accountbook:before {
-  content: "\e812";
+  content: '\e812';
 }
 
 .icon-contacts:before {
-  content: "\e813";
+  content: '\e813';
 }
 
 .icon-carryout:before {
-  content: "\e814";
+  content: '\e814';
 }
 
 .icon-calendar-check:before {
-  content: "\e815";
+  content: '\e815';
 }
 
 .icon-calendar:before {
-  content: "\e816";
+  content: '\e816';
 }
 
 .icon-scan:before {
-  content: "\e817";
+  content: '\e817';
 }
 
 .icon-select:before {
-  content: "\e818";
+  content: '\e818';
 }
 
 .icon-laptop:before {
-  content: "\e819";
+  content: '\e819';
 }
 
 .icon-barcode:before {
-  content: "\e81a";
+  content: '\e81a';
 }
 
 .icon-camera:before {
-  content: "\e81b";
+  content: '\e81b';
 }
 
 .icon-cluster:before {
-  content: "\e81c";
+  content: '\e81c';
 }
 
 .icon-gateway:before {
-  content: "\e81d";
+  content: '\e81d';
 }
 
 .icon-car:before {
-  content: "\e81e";
+  content: '\e81e';
 }
 
 .icon-printer:before {
-  content: "\e81f";
+  content: '\e81f';
 }
 
 .icon-read:before {
-  content: "\e820";
+  content: '\e820';
 }
 
 .icon-cloud-server:before {
-  content: "\e821";
+  content: '\e821';
 }
 
 .icon-cloud-upload:before {
-  content: "\e822";
+  content: '\e822';
 }
 
 .icon-cloud:before {
-  content: "\e823";
+  content: '\e823';
 }
 
 .icon-cloud-download:before {
-  content: "\e824";
+  content: '\e824';
 }
 
 .icon-cloud-sync:before {
-  content: "\e825";
+  content: '\e825';
 }
 
 .icon-video:before {
-  content: "\e826";
+  content: '\e826';
 }
 
 .icon-notification:before {
-  content: "\e827";
+  content: '\e827';
 }
 
 .icon-sound:before {
-  content: "\e828";
+  content: '\e828';
 }
 
 .icon-radarchart:before {
-  content: "\e829";
+  content: '\e829';
 }
 
 .icon-qrcode:before {
-  content: "\e82a";
+  content: '\e82a';
 }
 
 .icon-fund:before {
-  content: "\e82b";
+  content: '\e82b';
 }
 
 .icon-image:before {
-  content: "\e82c";
+  content: '\e82c';
 }
 
 .icon-mail:before {
-  content: "\e82d";
+  content: '\e82d';
 }
 
 .icon-table:before {
-  content: "\e82e";
+  content: '\e82e';
 }
 
 .icon-idcard:before {
-  content: "\e82f";
+  content: '\e82f';
 }
 
 .icon-creditcard:before {
-  content: "\e830";
+  content: '\e830';
 }
 
 .icon-heart:before {
-  content: "\e831";
+  content: '\e831';
 }
 
 .icon-block:before {
-  content: "\e832";
+  content: '\e832';
 }
 
 .icon-error:before {
-  content: "\e833";
+  content: '\e833';
 }
 
 .icon-star:before {
-  content: "\e834";
+  content: '\e834';
 }
 
 .icon-gold:before {
-  content: "\e835";
+  content: '\e835';
 }
 
 .icon-heatmap:before {
-  content: "\e836";
+  content: '\e836';
 }
 
 .icon-wifi:before {
-  content: "\e837";
+  content: '\e837';
 }
 
 .icon-attachment:before {
-  content: "\e838";
+  content: '\e838';
 }
 
 .icon-edit:before {
-  content: "\e839";
+  content: '\e839';
 }
 
 .icon-key:before {
-  content: "\e83a";
+  content: '\e83a';
 }
 
 .icon-api:before {
-  content: "\e83b";
+  content: '\e83b';
 }
 
 .icon-highlight:before {
-  content: "\e83c";
+  content: '\e83c';
 }
 
 .icon-monitor:before {
-  content: "\e83d";
+  content: '\e83d';
 }
 
 .icon-link:before {
-  content: "\e83e";
+  content: '\e83e';
 }
 
 .icon-man:before {
-  content: "\e83f";
+  content: '\e83f';
 }
 
 .icon-percentage:before {
-  content: "\e840";
+  content: '\e840';
 }
 
 .icon-pushpin:before {
-  content: "\e841";
+  content: '\e841';
 }
 
 .icon-phone:before {
-  content: "\e842";
+  content: '\e842';
 }
 
 .icon-shake:before {
-  content: "\e843";
+  content: '\e843';
 }
 
 .icon-tag:before {
-  content: "\e844";
+  content: '\e844';
 }
 
 .icon-wrench:before {
-  content: "\e845";
+  content: '\e845';
 }
 
 .icon-tags:before {
-  content: "\e846";
+  content: '\e846';
 }
 
 .icon-scissor:before {
-  content: "\e847";
+  content: '\e847';
 }
 
 .icon-mr:before {
-  content: "\e848";
+  content: '\e848';
 }
 
 .icon-share:before {
-  content: "\e849";
+  content: '\e849';
 }
 
 .icon-branches:before {
-  content: "\e84a";
+  content: '\e84a';
 }
 
 .icon-fork:before {
-  content: "\e84b";
+  content: '\e84b';
 }
 
 .icon-shrink:before {
-  content: "\e84c";
+  content: '\e84c';
 }
 
 .icon-arrawsalt:before {
-  content: "\e84d";
+  content: '\e84d';
 }
 
 .icon-verticalright:before {
-  content: "\e84e";
+  content: '\e84e';
 }
 
 .icon-right:before {
-  content: "\e84f";
+  content: '\e84f';
 }
 
 .icon-left:before {
-  content: "\e850";
+  content: '\e850';
 }
 
 .icon-up:before {
-  content: "\e851";
+  content: '\e851';
 }
 
 .icon-down:before {
-  content: "\e852";
+  content: '\e852';
 }
 
 .icon-fullscreen:before {
-  content: "\e853";
+  content: '\e853';
 }
 
 .icon-fullscreen-exit:before {
-  content: "\e854";
+  content: '\e854';
 }
 
 .icon-doubleleft:before {
-  content: "\e855";
+  content: '\e855';
 }
 
 .icon-doubleright:before {
-  content: "\e856";
+  content: '\e856';
 }
 
 .icon-arrowright:before {
-  content: "\e857";
+  content: '\e857';
 }
 
 .icon-arrowup:before {
-  content: "\e858";
+  content: '\e858';
 }
 
 .icon-arrowleft:before {
-  content: "\e859";
+  content: '\e859';
 }
 
 .icon-arrowdown:before {
-  content: "\e85a";
+  content: '\e85a';
 }
 
 .icon-upload:before {
-  content: "\e85b";
+  content: '\e85b';
 }
 
 .icon-unorderedlist:before {
-  content: "\e85c";
+  content: '\e85c';
 }
 
 .icon-check:before {
-  content: "\e85d";
+  content: '\e85d';
 }
 
 .icon-ellipsis:before {
-  content: "\e85e";
+  content: '\e85e';
 }
 
 .icon-close:before {
-  content: "\e85f";
+  content: '\e85f';
 }
 
 .icon-pause:before {
-  content: "\e860";
+  content: '\e860';
 }
 
 .icon-crown:before {
-  content: "\e861";
+  content: '\e861';
 }
 
 .icon-drag:before {
-  content: "\e862";
+  content: '\e862';
 }
 
 .icon-desktop:before {
-  content: "\e863";
+  content: '\e863';
 }
 
 .icon-gift:before {
-  content: "\e864";
+  content: '\e864';
 }
 
 .icon-stop:before {
-  content: "\e865";
+  content: '\e865';
 }
 
 .icon-fire:before {
-  content: "\e866";
+  content: '\e866';
 }
 
 .icon-thunderbolt:before {
-  content: "\e867";
+  content: '\e867';
 }
 
 .icon-mail-fill:before {
-  content: "\e86f";
+  content: '\e86f';
 }
 
 .icon-printer-fill:before {
-  content: "\e870";
+  content: '\e870';
 }
 
 .icon-setting-fill:before {
-  content: "\e871";
+  content: '\e871';
 }
 
 .icon-USB-fill:before {
-  content: "\e872";
+  content: '\e872';
 }
 
 .icon-antdesign:before {
-  content: "\e879";
+  content: '\e879';
 }
 
 .icon-IE:before {
-  content: "\e87a";
+  content: '\e87a';
 }
 
 .icon-HTML:before {
-  content: "\e87d";
+  content: '\e87d';
 }
 
 .icon-apple:before {
-  content: "\e87e";
+  content: '\e87e';
 }
 
 .icon-android:before {
-  content: "\e87f";
+  content: '\e87f';
 }
 
 .icon-windows:before {
-  content: "\e880";
+  content: '\e880';
 }
 
 .icon-zoomout:before {
-  content: "\e897";
+  content: '\e897';
 }
 
 .icon-apartment:before {
-  content: "\e898";
+  content: '\e898';
 }
 
 .icon-audio:before {
-  content: "\e899";
+  content: '\e899';
 }
 
 .icon-robot:before {
-  content: "\e89a";
+  content: '\e89a';
 }
 
 .icon-zoomin:before {
-  content: "\e89b";
+  content: '\e89b';
 }
 
 .icon-robot-fill:before {
-  content: "\e8e8";
+  content: '\e8e8';
 }
 
 .icon-bug-fill:before {
-  content: "\e8e9";
+  content: '\e8e9';
 }
 
 .icon-bug:before {
-  content: "\e8ea";
+  content: '\e8ea';
 }
 
 .icon-audiostatic:before {
-  content: "\e8eb";
+  content: '\e8eb';
 }
 
 .icon-comment:before {
-  content: "\e8ec";
+  content: '\e8ec';
 }
 
 .icon-signal-fill:before {
-  content: "\e8ed";
+  content: '\e8ed';
 }
 
 .icon-videocameraadd:before {
-  content: "\e8ee";
+  content: '\e8ee';
 }
 
 .icon-switchuser:before {
-  content: "\e8ef";
+  content: '\e8ef';
 }
 
 .icon-appstoreadd:before {
-  content: "\e8f0";
+  content: '\e8f0';
 }
 
 .icon-search:before {
-  content: "\e8f1";
+  content: '\e8f1';
 }
 
 .icon-woman:before {
-  content: "\e8f2";
+  content: '\e8f2';
 }
 
 .icon-plus:before {
-  content: "\e8fe";
+  content: '\e8fe';
 }
 
 .icon-eyeclose-fill:before {
-  content: "\e8ff";
+  content: '\e8ff';
 }
 
 .icon-eye-close:before {
-  content: "\e900";
+  content: '\e900';
 }
 
 .icon-clear:before {
-  content: "\e901";
+  content: '\e901';
 }
 
 .icon-rotate-left:before {
-  content: "\e902";
+  content: '\e902';
 }
 
 .icon-rotate-right:before {
-  content: "\e903";
+  content: '\e903';
 }
 
 .icon-insertrowbelow:before {
-  content: "\e904";
+  content: '\e904';
 }
 
 .icon-insertrowabove:before {
-  content: "\e905";
+  content: '\e905';
 }
 
 .icon-table1:before {
-  content: "\e906";
+  content: '\e906';
 }
 
 .icon-solit-cells:before {
-  content: "\e907";
+  content: '\e907';
 }
 
 .icon-formatpainter:before {
-  content: "\e908";
+  content: '\e908';
 }
 
 .icon-insertrowright:before {
-  content: "\e909";
+  content: '\e909';
 }
 
 .icon-formatpainter-fill:before {
-  content: "\e90a";
+  content: '\e90a';
 }
 
 .icon-insertrowleft:before {
-  content: "\e90b";
+  content: '\e90b';
 }
 
 .icon-translate:before {
-  content: "\e90c";
+  content: '\e90c';
 }
 
 .icon-deleterow:before {
-  content: "\e90d";
+  content: '\e90d';
 }
 
 .icon-sisternode:before {
-  content: "\e90e";
+  content: '\e90e';
 }
 
 .icon-Field-number:before {
-  content: "\e90f";
+  content: '\e90f';
 }
 
 .icon-Field-String:before {
-  content: "\e910";
+  content: '\e910';
 }
 
 .icon-Function:before {
-  content: "\e911";
+  content: '\e911';
 }
 
 .icon-Field-time:before {
-  content: "\e912";
+  content: '\e912';
 }
 
 .icon-GIF:before {
-  content: "\e913";
+  content: '\e913';
 }
 
 .icon-Partition:before {
-  content: "\e914";
+  content: '\e914';
 }
 
 .icon-index:before {
-  content: "\e915";
+  content: '\e915';
 }
 
 .icon-Storedprocedure:before {
-  content: "\e916";
+  content: '\e916';
 }
 
 .icon-Field-Binary:before {
-  content: "\e917";
+  content: '\e917';
 }
 
 .icon-Console-SQL:before {
-  content: "\e918";
+  content: '\e918';
 }
 
 .icon-icon-test:before {
-  content: "\e919";
+  content: '\e919';
 }
 
 .icon-aim:before {
-  content: "\e91a";
+  content: '\e91a';
 }
 
 .icon-compress:before {
-  content: "\e91b";
+  content: '\e91b';
 }
 
 .icon-expend:before {
-  content: "\e91c";
+  content: '\e91c';
 }
 
 .icon-folder-view:before {
-  content: "\e91d";
+  content: '\e91d';
 }
 
 .icon-file-GIF:before {
-  content: "\e91e";
+  content: '\e91e';
 }
 
 .icon-group:before {
-  content: "\e91f";
+  content: '\e91f';
 }
 
 .icon-send:before {
-  content: "\e920";
+  content: '\e920';
 }
 
 .icon-Report:before {
-  content: "\e921";
+  content: '\e921';
 }
 
 .icon-View:before {
-  content: "\e922";
+  content: '\e922';
 }
 
 .icon-shortcut:before {
-  content: "\e923";
+  content: '\e923';
 }
 
 .icon-ungroup:before {
-  content: "\e924";
+  content: '\e924';
 }
 
 .icon-edit-square:before {
-  content: "\e7c6";
+  content: '\e7c6';
 }
 
 .icon-export:before {
-  content: "\e7c7";
+  content: '\e7c7';
 }
 
 .icon-save:before {
-  content: "\e7c8";
+  content: '\e7c8';
 }
 
 .icon-appstore:before {
-  content: "\e7c9";
+  content: '\e7c9';
 }
 
 .icon-minus-square:before {
-  content: "\e7ca";
+  content: '\e7ca';
 }
 
 .icon-plus-square:before {
-  content: "\e7cb";
+  content: '\e7cb';
 }
 
 .icon-right-square:before {
-  content: "\e7cc";
+  content: '\e7cc';
 }
 
 .icon-wallet:before {
-  content: "\e7cd";
+  content: '\e7cd';
 }
 
 .icon-adduser:before {
-  content: "\e7ce";
+  content: '\e7ce';
 }
 
 .icon-deleteteam:before {
-  content: "\e7cf";
+  content: '\e7cf';
 }
 
 .icon-deleteuser:before {
-  content: "\e7d0";
+  content: '\e7d0';
 }
 
 .icon-addteam:before {
-  content: "\e7d1";
+  content: '\e7d1';
 }
 
 .icon-user:before {
-  content: "\e7d2";
+  content: '\e7d2';
 }
 
 .icon-team:before {
-  content: "\e7d3";
+  content: '\e7d3';
 }
 
 .icon-areachart:before {
-  content: "\e7d4";
+  content: '\e7d4';
 }
 
 .icon-linechart:before {
-  content: "\e7d5";
+  content: '\e7d5';
 }
 
 .icon-barchart:before {
-  content: "\e7d6";
+  content: '\e7d6';
 }
 
 .icon-pointmap:before {
-  content: "\e7d7";
+  content: '\e7d7';
 }
 
 .icon-container:before {
-  content: "\e7d8";
+  content: '\e7d8';
 }
 
 .icon-database:before {
-  content: "\e7d9";
+  content: '\e7d9';
 }
 
 .icon-sever:before {
-  content: "\e7da";
+  content: '\e7da';
 }
 
 .icon-mobile:before {
-  content: "\e7db";
+  content: '\e7db';
 }
 
 .icon-tablet:before {
-  content: "\e7dc";
+  content: '\e7dc';
 }
 
 .icon-redenvelope:before {
-  content: "\e7dd";
+  content: '\e7dd';
 }
 
 .icon-book:before {
-  content: "\e7de";
+  content: '\e7de';
 }
 
 .icon-filedone:before {
-  content: "\e7df";
+  content: '\e7df';
 }
 
 .icon-reconciliation:before {
-  content: "\e7e0";
+  content: '\e7e0';
 }
 
 .icon-file-exception:before {
-  content: "\e7e1";
+  content: '\e7e1';
 }
 
 .icon-filesync:before {
-  content: "\e7e2";
+  content: '\e7e2';
 }
 
 .icon-filesearch:before {
-  content: "\e7e3";
+  content: '\e7e3';
 }
 
 .icon-solution:before {
-  content: "\e7e4";
+  content: '\e7e4';
 }
 
 .icon-fileprotect:before {
-  content: "\e7e5";
+  content: '\e7e5';
 }
 
 .icon-file-add:before {
-  content: "\e7e6";
+  content: '\e7e6';
 }
 
 .icon-file-excel:before {
-  content: "\e7e7";
+  content: '\e7e7';
 }
 
 .icon-file-exclamation:before {
-  content: "\e7e8";
+  content: '\e7e8';
 }
 
 .icon-file-pdf:before {
-  content: "\e7e9";
+  content: '\e7e9';
 }
 
 .icon-file-image:before {
-  content: "\e7ea";
+  content: '\e7ea';
 }
 
 .icon-file-markdown:before {
-  content: "\e7eb";
+  content: '\e7eb';
 }
 
 .icon-file-ppt:before {
-  content: "\e7ec";
+  content: '\e7ec';
 }
 
 .icon-file-word:before {
-  content: "\e7ed";
+  content: '\e7ed';
 }
 
 .icon-file:before {
-  content: "\e7ee";
+  content: '\e7ee';
 }
 
 .icon-file-zip:before {
-  content: "\e7ef";
+  content: '\e7ef';
 }
 
 .icon-file-text:before {
-  content: "\e7f0";
+  content: '\e7f0';
 }
 
 .icon-file-copy:before {
-  content: "\e7f1";
+  content: '\e7f1';
 }
 
 .icon-snippets:before {
-  content: "\e7f2";
+  content: '\e7f2';
 }
 
 .icon-audit:before {
-  content: "\e7f3";
+  content: '\e7f3';
 }
 
 .icon-diff:before {
-  content: "\e7f4";
+  content: '\e7f4';
 }
 
 .icon-Batchfolding:before {
-  content: "\e7f5";
+  content: '\e7f5';
 }
 
 .icon-securityscan:before {
-  content: "\e7f6";
+  content: '\e7f6';
 }
 
 .icon-propertysafety:before {
-  content: "\e7f7";
+  content: '\e7f7';
 }
 
 .icon-safetycertificate:before {
-  content: "\e7f8";
+  content: '\e7f8';
 }
 
 .icon-insurance:before {
-  content: "\e7f9";
+  content: '\e7f9';
 }
 
 .icon-alert:before {
-  content: "\e7fa";
+  content: '\e7fa';
 }
 
 .icon-delete:before {
-  content: "\e7fb";
+  content: '\e7fb';
 }
 
 .icon-hourglass:before {
-  content: "\e7fc";
+  content: '\e7fc';
 }
 
 .icon-bulb:before {
-  content: "\e7fd";
+  content: '\e7fd';
 }
 
 .icon-bell:before {
-  content: "\e7fe";
+  content: '\e7fe';
 }
 
 .icon-trophy:before {
-  content: "\e7ff";
+  content: '\e7ff';
 }
 
 .icon-rest:before {
-  content: "\e800";
+  content: '\e800';
 }
 
 .icon-USB:before {
-  content: "\e801";
+  content: '\e801';
 }
 
 .icon-skin:before {
-  content: "\e802";
+  content: '\e802';
 }
 
 .icon-home:before {
-  content: "\e803";
+  content: '\e803';
 }
 
 .icon-bank:before {
-  content: "\e804";
+  content: '\e804';
 }
 
 .icon-filter:before {
-  content: "\e805";
+  content: '\e805';
 }
 
 .icon-like:before {
-  content: "\e806";
+  content: '\e806';
 }
 
 .icon-unlike:before {
-  content: "\e807";
+  content: '\e807';
 }
 
 .icon-close-circle:before {
-  content: "\e782";
+  content: '\e782';
 }
 
 .icon-frown:before {
-  content: "\e783";
+  content: '\e783';
 }
 
 .icon-info-circle:before {
-  content: "\e784";
+  content: '\e784';
 }
 
 .icon-left-circle:before {
-  content: "\e785";
+  content: '\e785';
 }
 
 .icon-down-circle:before {
-  content: "\e786";
+  content: '\e786';
 }
 
 .icon-EURO:before {
-  content: "\e787";
+  content: '\e787';
 }
 
 .icon-copyright:before {
-  content: "\e788";
+  content: '\e788';
 }
 
 .icon-minus-circle:before {
-  content: "\e789";
+  content: '\e789';
 }
 
 .icon-meh:before {
-  content: "\e78a";
+  content: '\e78a';
 }
 
 .icon-plus-circle:before {
-  content: "\e78b";
+  content: '\e78b';
 }
 
 .icon-play-circle:before {
-  content: "\e78c";
+  content: '\e78c';
 }
 
 .icon-question-circle:before {
-  content: "\e78d";
+  content: '\e78d';
 }
 
 .icon-Pound:before {
-  content: "\e78e";
+  content: '\e78e';
 }
 
 .icon-right-circle:before {
-  content: "\e78f";
+  content: '\e78f';
 }
 
 .icon-smile:before {
-  content: "\e790";
+  content: '\e790';
 }
 
 .icon-trademark:before {
-  content: "\e791";
+  content: '\e791';
 }
 
 .icon-time-circle:before {
-  content: "\e792";
+  content: '\e792';
 }
 
 .icon-timeout:before {
-  content: "\e793";
+  content: '\e793';
 }
 
 .icon-earth:before {
-  content: "\e794";
+  content: '\e794';
 }
 
 .icon-YUAN:before {
-  content: "\e795";
+  content: '\e795';
 }
 
 .icon-up-circle:before {
-  content: "\e796";
+  content: '\e796';
 }
 
 .icon-warning-circle:before {
-  content: "\e797";
+  content: '\e797';
 }
 
 .icon-sync:before {
-  content: "\e798";
+  content: '\e798';
 }
 
 .icon-transaction:before {
-  content: "\e799";
+  content: '\e799';
 }
 
 .icon-undo:before {
-  content: "\e79a";
+  content: '\e79a';
 }
 
 .icon-redo:before {
-  content: "\e79b";
+  content: '\e79b';
 }
 
 .icon-reload:before {
-  content: "\e79c";
+  content: '\e79c';
 }
 
 .icon-reloadtime:before {
-  content: "\e79d";
+  content: '\e79d';
 }
 
 .icon-message:before {
-  content: "\e79e";
+  content: '\e79e';
 }
 
 .icon-dashboard:before {
-  content: "\e79f";
+  content: '\e79f';
 }
 
 .icon-issuesclose:before {
-  content: "\e7a0";
+  content: '\e7a0';
 }
 
 .icon-poweroff:before {
-  content: "\e7a1";
+  content: '\e7a1';
 }
 
 .icon-logout:before {
-  content: "\e7a2";
+  content: '\e7a2';
 }
 
 .icon-piechart:before {
-  content: "\e7a3";
+  content: '\e7a3';
 }
 
 .icon-setting:before {
-  content: "\e7a4";
+  content: '\e7a4';
 }
 
 .icon-eye:before {
-  content: "\e7a5";
+  content: '\e7a5';
 }
 
 .icon-location:before {
-  content: "\e7a6";
+  content: '\e7a6';
 }
 
 .icon-check-circle:before {
-  content: "\e77d";
+  content: '\e77d';
 }
 
 .icon-CI:before {
-  content: "\e77e";
+  content: '\e77e';
 }
 
 .icon-Dollar:before {
-  content: "\e77f";
+  content: '\e77f';
 }
 
 .icon-compass:before {
-  content: "\e780";
+  content: '\e780';
 }
-

+ 2 - 0
src/components/Form/src/componentMap.ts

@@ -36,6 +36,7 @@ import PlainText from './components/PlainText.vue';
 import Cron from './components/Cron.vue';
 import FormColorPicker from './components/FormColorPicker.vue';
 import InputNumberGroup from './components/InputNumberGroup.vue';
+import RadioDescGroup from './components/RadioDescGroup.vue';
 
 const componentMap = new Map<ComponentType, Component>();
 
@@ -55,6 +56,7 @@ componentMap.set('ApiTreeSelect', ApiTreeSelect);
 componentMap.set('ApiRadioGroup', ApiRadioGroup);
 componentMap.set('Switch', Switch);
 componentMap.set('RadioButtonGroup', RadioButtonGroup);
+componentMap.set('RadioDescGroup', RadioDescGroup);
 componentMap.set('TextEditor', TextEditor);
 componentMap.set('PlainText', PlainText);
 componentMap.set('Cron', Cron);

+ 72 - 0
src/components/Form/src/components/RadioDescGroup.vue

@@ -0,0 +1,72 @@
+<!--
+ * @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
+-->
+<template>
+  <RadioGroup
+    v-bind="attrs"
+    v-model:value="state"
+    :class="['radio-desc', getOptions.length > 3 ? 'justify-between' : 'justify-start']"
+    size="small"
+  >
+    <template v-for="item in getOptions" :key="`${item.value}`">
+      <RadioButton :value="item.value" :disabled="item.disabled" class="radio-desc_item">
+        {{ item.label }}
+      </RadioButton>
+    </template>
+  </RadioGroup>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, computed } from 'vue';
+  import { Radio } from 'ant-design-vue';
+  import { isString } from '/@/utils/is';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+
+  type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
+  type RadioItem = string | OptionsItem;
+
+  export default defineComponent({
+    name: 'RadioDescGroup',
+    components: {
+      RadioGroup: Radio.Group,
+      RadioButton: Radio.Button,
+      // eslint-disable-next-line vue/no-unused-components
+      Radio: Radio,
+    },
+    props: {
+      value: {
+        type: [String, Number, Boolean] as PropType<string | number | boolean>,
+      },
+      options: {
+        type: Array as PropType<RadioItem[]>,
+        default: () => [],
+      },
+    },
+    setup(props) {
+      const attrs = useAttrs();
+      // Embedded in the form, just use the hook binding to perform form verification
+      const [state] = useRuleFormItem(props);
+
+      // Processing options value
+      const getOptions = computed((): OptionsItem[] => {
+        const { options } = props;
+        if (!options || options?.length === 0) return [];
+
+        const isStringArr = options.some(item => isString(item));
+        if (!isStringArr) return options as OptionsItem[];
+
+        return options.map(item => ({ label: item, value: item })) as OptionsItem[];
+      });
+
+      return { state, getOptions, attrs };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  .radio-desc.justify-start {
+    .radio-desc_item {
+      margin-right: 16px;
+      background-color: #f4f6f9;
+    }
+  }
+</style>

+ 1 - 0
src/components/Form/src/types/index.ts

@@ -120,4 +120,5 @@ export type ComponentType =
   | 'PlainTitle'
   | 'FormColorPicker'
   | 'InputNumberGroup'
+  | 'RadioDescGroup'
   | 'Cron';

+ 4 - 4
src/components/SimpleMenu/src/components/menu.less

@@ -9,10 +9,10 @@
   &::after {
     position: absolute;
     top: 0;
-    right: 0;
+    left: 0;
     bottom: 0;
     display: block;
-    width: 2px;
+    width: 3px;
     content: '';
     background-color: var(--ant-primary-color);
   }
@@ -225,8 +225,8 @@
         // background-color: fade(@primary-color, 10);
         // color: var(--ant-primary-color);
         // background-color: var(--ant-primary-8);
-          color: var(--ant-primary-color);
-          background-color: var(--ant-primary-2);
+        color: var(--ant-primary-color);
+        background-color: var(--ant-primary-2);
         .light-border();
       }
       &-active.@{menu-prefix-cls}-submenu {

+ 76 - 3
src/components/Table/src/BasicTable.vue

@@ -11,7 +11,7 @@
       @advanced-change="redoHeight"
     >
       <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
-        <slot :name="item" v-bind="data || {}"></slot>
+        <slot :name="item" v-bind="data || {}" />
       </template>
     </BasicForm>
 
@@ -23,14 +23,14 @@
       @change="handleTableChange"
     >
       <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
-        <slot :name="item" v-bind="data || {}"></slot>
+        <slot :name="item" v-bind="data || {}" />
       </template>
       <template #headerCell="{ column }">
         <HeaderCell :column="column" />
       </template>
       <!-- 增加对antdv3.x兼容 -->
       <template #bodyCell="data">
-        <slot name="bodyCell" v-bind="data || {}"></slot>
+        <slot name="bodyCell" v-bind="data || {}" />
       </template>
       <!--      <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
       <!--        <HeaderCell :column="column" />-->
@@ -76,6 +76,7 @@
 
   export default defineComponent({
     components: {
+      // eslint-disable-next-line vue/no-reserved-component-names
       Table,
       BasicForm,
       HeaderCell,
@@ -451,5 +452,77 @@
         padding: 0;
       }
     }
+
+    .hide-scrollbar-y {
+      .ant-spin-nested-loading {
+        .ant-spin-container {
+          .ant-table {
+            .ant-table-content {
+              .ant-table-scroll {
+                .ant-table-hide-scrollbar {
+                  overflow-y: auto !important;
+                }
+
+                .ant-table-body {
+                  overflow-y: auto !important;
+                }
+              }
+
+              .ant-table-fixed-right {
+                .ant-table-body-outer {
+                  .ant-table-body-inner {
+                    overflow-y: auto !important;
+                  }
+                }
+              }
+
+              .ant-table-fixed-left {
+                .ant-table-body-outer {
+                  .ant-table-body-inner {
+                    overflow-y: auto !important;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .hide-scrollbar-x {
+      .ant-spin-nested-loading {
+        .ant-spin-container {
+          .ant-table {
+            .ant-table-content {
+              .ant-table-scroll {
+                .ant-table-hide-scrollbar {
+                  //overflow-x: auto !important;
+                }
+
+                .ant-table-body {
+                  overflow: auto !important;
+                }
+              }
+
+              .ant-table-fixed-right {
+                .ant-table-body-outer {
+                  .ant-table-body-inner {
+                    overflow-x: auto !important;
+                  }
+                }
+              }
+
+              .ant-table-fixed-left {
+                .ant-table-body-outer {
+                  .ant-table-body-inner {
+                    overflow-x: auto !important;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
   }
 </style>

+ 4 - 3
src/components/Table/src/components/EditTableHeaderIcon.vue

@@ -2,15 +2,16 @@
   <span>
     <slot />
     {{ title }}
-    <FormOutlined />
+    <!-- <FormOutlined /> -->
   </span>
 </template>
 <script lang="ts">
   import { defineComponent } from 'vue';
-  import { FormOutlined } from '@ant-design/icons-vue';
+  // import { FormOutlined } from '@ant-design/icons-vue';
   export default defineComponent({
     name: 'EditTableHeaderIcon',
-    components: { FormOutlined },
+    // components: { FormOutlined },
+    components: {},
     props: { title: { type: String, default: '' } },
   });
 </script>

+ 20 - 2
src/components/Table/src/components/TableHeader.vue

@@ -5,11 +5,21 @@
     </div>
     <div class="flex items-center">
       <slot name="tableTitle" v-if="$slots.tableTitle" />
-      <TableTitle :helpMessage="titleHelpMessage" :title="title" v-if="!$slots.tableTitle && title" />
+      <TableTitle
+        :helpMessage="titleHelpMessage"
+        :title="title"
+        v-if="!$slots.tableTitle && title"
+        :style="titleStyle"
+        :lined="titleLined"
+      />
       <div :class="`${prefixCls}__toolbar`">
         <slot name="toolbar" />
         <Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
-        <TableSetting :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
+        <TableSetting
+          :setting="tableSetting"
+          v-if="showTableSetting"
+          @columns-change="handleColumnChange"
+        />
       </div>
     </div>
   </div>
@@ -34,6 +44,14 @@
       title: {
         type: [Function, String] as PropType<string | ((data: Recordable) => string)>,
       },
+      titleStyle: {
+        type: Object,
+        default: () => {},
+      },
+      titleLined: {
+        type: Boolean,
+        default: false,
+      },
       tableSetting: {
         type: Object as PropType<TableSetting>,
       },

+ 21 - 1
src/components/Table/src/components/TableTitle.vue

@@ -1,5 +1,9 @@
 <template>
-  <BasicTitle :class="prefixCls" v-if="getTitle" :helpMessage="helpMessage">
+  <BasicTitle
+    :class="[prefixCls, lined ? prefixCls + '--lined' : '']"
+    v-if="getTitle"
+    :helpMessage="helpMessage"
+  >
     {{ getTitle }}
   </BasicTitle>
 </template>
@@ -16,6 +20,10 @@
       title: {
         type: [Function, String] as PropType<string | ((data: Recordable) => string)>,
       },
+      lined: {
+        type: Boolean,
+        default: false,
+      },
       getSelectRows: {
         type: Function as PropType<() => Recordable[]>,
       },
@@ -46,8 +54,20 @@
   @prefix-cls: ~'@{namespace}-basic-table-title';
 
   .@{prefix-cls} {
+    position: relative;
     display: flex;
     justify-content: space-between;
     align-items: center;
+
+    &--lined::after {
+      position: absolute;
+      content: '';
+      top: 4px;
+      left: 0;
+      height: 14px;
+      width: 2px;
+      background: #000a18;
+      border-radius: 1px;
+    }
   }
 </style>

+ 5 - 3
src/components/Table/src/components/editable/EditableCell.vue

@@ -138,7 +138,8 @@
           return {};
         }
         return {
-          width: 'calc(100% - 48px)',
+          // width: 'calc(100% - 48px)',
+          width: 'calc(100%)',
         };
       });
 
@@ -427,7 +428,7 @@
                   onOptionsChange={this.handleOptionsChange}
                   onPressEnter={this.handleEnter}
                 />
-                {!this.getRowEditable && !this.column?.editComponentProps?.editIconHidden && (
+                {/* {!this.getRowEditable && !this.column?.editComponentProps?.editIconHidden && (
                   <div class={`${this.prefixCls}__action`}>
                     <CheckOutlined
                       class={[`${this.prefixCls}__icon`, 'mx-2']}
@@ -435,7 +436,8 @@
                     />
                     <CloseOutlined class={`${this.prefixCls}__icon `} onClick={this.handleCancel} />
                   </div>
-                )}
+                )} */}
+                {!this.getRowEditable && !this.column?.editComponentProps?.editIconHidden}
               </div>
             </Spin>
           )}

+ 4 - 1
src/components/Table/src/hooks/useTableHeader.ts

@@ -11,7 +11,8 @@ export function useTableHeader(
   handlers: InnerHandlers,
 ) {
   const getHeaderProps = computed((): Recordable => {
-    const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef);
+    const { title, showTableSetting, titleHelpMessage, tableSetting, titleStyle, titleLined } =
+      unref(propsRef);
     const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
     if (hideTitle && !isString(title)) {
       return {};
@@ -28,6 +29,8 @@ export function useTableHeader(
                 titleHelpMessage,
                 showTableSetting,
                 tableSetting,
+                titleStyle,
+                titleLined,
                 onColumnsChange: handlers.onColumnsChange,
               } as Recordable,
               {

+ 17 - 9
src/components/Table/src/hooks/useTableScroll.ts

@@ -17,7 +17,7 @@ export function useTableScroll(
   wrapRef: Ref<HTMLElement | null>,
   formRef: Ref<ComponentRef>,
 ) {
-  const tableHeightRef: Ref<Nullable<number | string>> = ref(167);
+  const tableHeightRef: Ref<Nullable<number | string>> = ref(200);
   const modalFn = useModalContext();
 
   // Greater than animation time 280
@@ -71,7 +71,9 @@ export function useTableScroll(
       if (!bodyEl) return;
     }
 
-    const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
+    // const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
+    const hasScrollBarY =
+      bodyEl.scrollHeight > bodyEl.clientHeight && bodyEl.scrollHeight > maxHeight;
     const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth;
 
     if (hasScrollBarY) {
@@ -152,7 +154,6 @@ export function useTableScroll(
       const headerCellHeight =
         (tableEl.querySelector('.ant-table-title') as HTMLElement)?.offsetHeight ?? 0;
 
-      console.log(wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin);
       bottomIncludeBody =
         wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin;
     } else {
@@ -167,7 +168,9 @@ export function useTableScroll(
       paginationHeight -
       footerHeight -
       headerHeight;
-    height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
+
+    // height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
+    height = (height > maxHeight! ? height : (maxHeight as number)) ?? height;
     setHeight(height);
 
     bodyEl!.style.height = `${height}px`;
@@ -189,11 +192,11 @@ export function useTableScroll(
     // TODO props ?? 0;
     const NORMAL_WIDTH = 150;
 
-    const columns = unref(columnsRef).filter((item) => !item.defaultHidden);
-    columns.forEach((item) => {
+    const columns = unref(columnsRef).filter(item => !item.defaultHidden);
+    columns.forEach(item => {
       width += Number.parseFloat(item.width as string) || 0;
     });
-    const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width'));
+    const unsetWidthColumns = columns.filter(item => !Reflect.has(item, 'width'));
 
     const len = unsetWidthColumns.length;
     if (len !== 0) {
@@ -207,10 +210,15 @@ export function useTableScroll(
 
   const getScrollRef = computed(() => {
     const tableHeight = unref(tableHeightRef);
-    const { canResize, scroll } = unref(propsRef);
+    const { canResize, scroll, maxHeight } = unref(propsRef);
     return {
       x: unref(getScrollX),
-      y: canResize ? tableHeight : null,
+      // y: canResize ? tableHeight : null,
+      y: canResize
+        ? Number(tableHeight) > Number(maxHeight || 0)
+          ? Number(tableHeight)
+          : Number(maxHeight)
+        : null,
       scrollToFirstRowOnChange: false,
       ...scroll,
     };

+ 16 - 1
src/components/Table/src/props.ts

@@ -15,8 +15,13 @@ import { DEFAULT_FILTER_FN, DEFAULT_SORT_FN, FETCH_SETTING, DEFAULT_SIZE } from
 import { propTypes } from '/@/utils/propTypes';
 
 export const basicProps = {
+  // 新增 API, 定义table 唯一 id
   id: { type: String, default: '' },
-  storage: Boolean,
+  // 新增 API, 定义table 是否存储
+  storage: {
+    type: Boolean,
+    default: false,
+  },
   clickToRowSelect: { type: Boolean, default: true },
   isTreeTable: Boolean,
   tableSetting: propTypes.shape<TableSetting>({}),
@@ -116,6 +121,16 @@ export const basicProps = {
     type: [String, Function] as PropType<string | ((data: Recordable) => string)>,
     default: null,
   },
+  // 新增 API, 定义title样式
+  titleStyle: {
+    type: Object,
+    default: () => {},
+  },
+  // 新增 API, title前是否加竖线,主要用于弹窗
+  titleLined: {
+    type: Boolean,
+    default: false,
+  },
   titleHelpMessage: {
     type: [String, Array] as PropType<string | string[]>,
   },

+ 14 - 10
src/components/Table/src/types/table.ts

@@ -140,7 +140,7 @@ export interface BasicTableProps<T = any> {
   // table 唯一id
   id?: string;
   // 是否存储在 localstorage
-  storage: boolean;
+  storage?: boolean;
   // 基本搜索渲染,然后放入 searchInfo 参数中
   basicSearch: Recordable;
   // 点击行选中
@@ -213,6 +213,10 @@ export interface BasicTableProps<T = any> {
   dataSource?: Recordable[];
   // 标题右侧提示
   titleHelpMessage?: string | string[];
+  // 标题样式
+  titleStyle?: object;
+  // 标题前是否加 竖线
+  titleLined?: boolean;
   // 表格滚动最大高度
   maxHeight?: number;
   // 是否显示边框
@@ -425,8 +429,8 @@ export interface BasicColumn extends ColumnProps<Recordable> {
     text: string;
     value: string;
     children?:
-    | unknown[]
-    | (((props: Record<string, unknown>) => unknown[]) & (() => unknown[]) & (() => unknown[]));
+      | unknown[]
+      | (((props: Record<string, unknown>) => unknown[]) & (() => unknown[]) & (() => unknown[]));
   }[];
 
   //
@@ -449,13 +453,13 @@ export interface BasicColumn extends ColumnProps<Recordable> {
   editable?: boolean;
   editComponent?: ComponentType;
   editComponentProps?:
-  | ((opt: {
-    text: string | number | boolean | Recordable;
-    record: Recordable;
-    column: BasicColumn;
-    index: number;
-  }) => Recordable)
-  | Recordable;
+    | ((opt: {
+        text: string | number | boolean | Recordable;
+        record: Recordable;
+        column: BasicColumn;
+        index: number;
+      }) => Recordable)
+    | Recordable;
   editRule?: boolean | ((text: string, record: Recordable) => Promise<string>);
   editValueMap?: (value: any) => string;
   onEditRow?: () => void;

+ 233 - 0
src/components/XTCard/src/ChartsCard.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="card">
+    <div class="card-tit">
+      <div class="card-tit_label"> {{ title }}</div>
+      <div class="card-tit_value">
+        <div class="card-tit_value-item" v-for="item in colors" :key="item.color">
+          <div class="card-tit_value-color" :style="{ background: item.color }" />
+          <div class="card-tit_value-info">
+            {{ item.label }}
+          </div>
+        </div>
+        <div class="card-tit_value-item" v-if="hasSafe">
+          <div class="card-tit_value-color" :style="{ background: safeColor }" />
+          <div class="card-tit_value-info"> 正常范围 </div>
+        </div>
+      </div>
+    </div>
+    <div class="card-charts" ref="chartRef" :style="{ height, width }" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, PropType, ref, Ref } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  // import { clinicStats } from '/@/api/infusion/busClinicApi';
+  const props = defineProps({
+    // 宽
+    width: {
+      type: String as PropType<string>,
+      default: '100%',
+    },
+    // 高
+    height: {
+      type: String as PropType<string>,
+      default: '400px',
+    },
+    // 标题
+    title: {
+      type: String,
+      default: '',
+    },
+    // 标题右侧颜色值
+    colors: {
+      type: Array as any,
+      default: () => [],
+    },
+    // 是否有正常范围
+    hasSafe: {
+      type: Boolean,
+      default: false,
+    },
+    safeColor: {
+      type: String,
+      default: 'rgba(33, 201, 153, 0.12)',
+    },
+    safeRange: {
+      type: Array,
+      default: () => [
+        {
+          // name: '60分到80分',
+          yAxis: 0,
+        },
+        {
+          yAxis: 10,
+        },
+      ],
+    },
+    // 图表数据
+    data: {
+      type: Object,
+      default: () => {},
+    },
+  });
+  const chartRef = ref<HTMLDivElement | null>(null);
+  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+  // 各种信息数据集
+  const infoData = ref([]);
+  // 上传时间
+  const dateData = ref([]);
+  const start = ref();
+  const markArea = ref();
+  const series = ref([]);
+  onMounted(async () => {
+    // const resData = await clinicStats({
+    //   type: '',
+    //   id: '123',
+    // });
+    // console.log(resData);
+    // infoData.value = resData.validCount || [];
+    // dateData.value = resData.uploadTimes || [];
+    infoData.value = [10, 25, 30, 16, 36, 10];
+    dateData.value = [
+      '2023-05-10',
+      '2023-05-11',
+      '2023-05-12',
+      '2023-05-13',
+      '2023-05-14',
+      '2023-05-15',
+    ];
+    // 数据显示
+    start.value = infoData.value.length > 10 ? 80 : 0;
+    markArea.value = props.hasSafe
+      ? {
+          silent: false,
+          itemStyle: {
+            color: props.safeColor,
+          },
+          data: [props.safeRange],
+        }
+      : { silent: false };
+    series.value = props.colors.map(ele => {
+      return {
+        type: 'line',
+        symbolSize: 10,
+        symbol: 'circle',
+        name: ele.label,
+        // label: {
+        //   show: true,
+        //   position: 'top',
+        // },
+        lineStyle: {
+          color: ele.color,
+        },
+        itemStyle: {
+          color: ele.dot,
+        },
+        data: infoData.value || [],
+        markArea: markArea.value,
+      };
+    });
+    setOptions({
+      tooltip: {
+        trigger: 'axis',
+      },
+      toolbox: {
+        show: true,
+        feature: {
+          saveAsImage: {
+            title: '保存为图片',
+          },
+        },
+      },
+      grid: {
+        left: '2%',
+        right: '2%',
+        bottom: '10%',
+        containLabel: true,
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: dateData.value,
+      },
+      yAxis: {
+        type: 'value',
+        splitLine: {
+          lineStyle: {
+            color: ['rgba(235, 235, 235, 1)'],
+            type: 'dashed',
+          },
+        },
+      },
+      dataZoom: start.value
+        ? [
+            {
+              type: 'inside',
+              start: start.value,
+              end: 100,
+            },
+            {
+              start: start.value,
+              end: 100,
+            },
+          ]
+        : [],
+      series: series.value,
+    });
+  });
+</script>
+<style lang="less" scoped>
+  .card {
+    border-radius: 4px;
+    border: 1px solid #efefef;
+    background-color: #fff;
+
+    &-tit {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 0 18px;
+      height: 56px;
+      line-height: 56px;
+      background: #f6f8fa;
+      margin-bottom: 20px;
+
+      &_label {
+        font-size: 18px;
+        font-weight: 600;
+        color: #000a18;
+      }
+
+      &_value {
+        display: flex;
+        align-items: center;
+
+        &-item {
+          display: flex;
+          align-items: center;
+          margin-right: 40px;
+
+          &:last-child {
+            margin-right: 0;
+          }
+        }
+
+        &-color {
+          display: block;
+          width: 10px;
+          height: 10px;
+          border-radius: 2px;
+          margin-right: 6px;
+        }
+
+        &-info {
+          color: #818694;
+        }
+      }
+    }
+
+    &-charts {
+      padding: 10px;
+    }
+  }
+</style>

+ 259 - 0
src/components/XTCard/src/DescCard.vue

@@ -0,0 +1,259 @@
+<template>
+  <div class="card">
+    <div class="card-head">
+      <div class="card-head_label"> {{ title }}</div>
+      <div class="card-head_value">
+        <i :class="['iconfont', icon]" v-if="icon" @click="handleIcon" />
+        <slot name="headRight" />
+      </div>
+    </div>
+    <Row class="card-body" v-if="data.length">
+      <Col class="card-body_left" :span="right.show ? 20 : 24">
+        <Row>
+          <Col
+            :span="item.span || wrapSpan"
+            class="card-body-item"
+            v-for="item in data"
+            :key="item.label"
+          >
+            <div class="card-body-item_label" v-if="item.label">{{ item.label }}</div>
+            <div class="card-body-item_value" v-if="item.value">
+              <div>{{ item.value }}</div>
+              <div v-if="item.tags" class="flex">
+                <div
+                  v-for="t in item.tags"
+                  :key="t.id"
+                  :class="['card-body-item_tag', 'card-body-item_tag--' + t.type]"
+                >
+                  {{ t.label }}
+                </div>
+              </div>
+              <div>
+                <slot name="tags" />
+              </div>
+            </div>
+          </Col>
+        </Row>
+      </Col>
+      <Col class="card-body_right" v-if="right.show" span="4">
+        <div>{{ right.date ? right.date + '/' : '' }} {{ right.doctor }}</div>
+        <div class="flex">
+          <div v-if="right.edit" class="card-body_right-btn" @click="handleEdit">
+            <i class="iconfont icon-xt-details_edit_default" />
+          </div>
+          <div v-if="right.delete" class="card-body_right-btn" @click="handleDel">
+            <!-- 删除 -->
+            <i class="iconfont icon-xt-details_delete_default" />
+          </div>
+        </div>
+      </Col>
+    </Row>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { Row, Col } from 'ant-design-vue';
+  interface Props {
+    id?: string;
+    // 标题
+    title: string;
+    // icon
+    icon?: string;
+    iconType?: string;
+    // 每行宽度
+    wrapSpan?: number;
+    type?: string;
+    right?: {
+      show: boolean;
+      date?: string;
+      doctor?: string;
+      edit?: boolean;
+      delete?: boolean;
+      span?: number;
+    };
+    // 数据集
+    data: Array<{
+      label?: string;
+      value?: string;
+      span?: number;
+      tags?:
+        | Array<{
+            id: string;
+            label: string;
+            type: string | 'warning' | 'error' | 'success' | 'primary' | 'muted';
+          }>
+        | undefined
+        | null;
+    }>;
+  }
+  const props = withDefaults(defineProps<Props>(), {
+    title: '标题',
+    wrapSpan: 6,
+    type: '',
+    data: () => [],
+    right: () => {
+      return {
+        show: false,
+        span: 4,
+        edit: true,
+        delete: false,
+      };
+    },
+  });
+  const emit = defineEmits(['edit', 'delete', 'icon']);
+
+  function handleEdit() {
+    const info = { id: props.id, type: props.type, action: 'edit' };
+    console.log('🚀 ~ file: DescCard.vue:100 ~ handleEdit ~ info:', info);
+    emit('edit', info);
+  }
+  function handleDel() {
+    const info = { id: props.id, type: props.type, action: 'delete' };
+    emit('delete', info);
+    console.log('🚀 ~ file: DescCard.vue:105 ~ handleDel ~ info:', info);
+  }
+
+  function handleIcon() {
+    const info = { id: props.id, type: props.type, action: props.iconType || 'icon' };
+    emit('icon', info);
+    console.log('🚀 ~ file: DescCard.vue:113 ~ handleIcon ~ info:', info);
+  }
+</script>
+
+<style lang="less" scoped>
+  .card {
+    border-radius: 4px;
+    border: 1px solid #efefef;
+    background-color: #fff;
+
+    &-head {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      height: 56px;
+      background: #f6f8fa;
+      box-shadow: 0 1px 0 0 #efefef;
+      border-radius: 3px 3px 0 0;
+      padding: 0 20px;
+
+      &_label {
+        font-size: 18px;
+        font-weight: 600;
+        color: #000a18;
+      }
+
+      &_value {
+        & .iconfont {
+          display: block;
+          margin-right: 16px;
+          background: #fff;
+          border-radius: 50%;
+          width: 32px;
+          height: 32px;
+          text-align: center;
+          line-height: 32px;
+          cursor: pointer;
+
+          &:last-child {
+            margin-right: 0;
+          }
+
+          &:hover {
+            color: #0075ff;
+          }
+        }
+      }
+    }
+
+    &-body {
+      display: flex;
+      align-items: center;
+      padding: 20px;
+
+      &_right {
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        font-size: 12px;
+        color: #818694;
+
+        &-btn {
+          margin-right: 16px;
+          color: #3d4155;
+          background: #f4f6f9;
+          border-radius: 50%;
+          width: 32px;
+          height: 32px;
+          text-align: center;
+          line-height: 32px;
+          cursor: pointer;
+          margin-left: 10px;
+
+          &:last-child {
+            margin: 0;
+          }
+
+          &:hover {
+            color: #0075ff;
+          }
+        }
+      }
+
+      &-item {
+        display: flex;
+        flex-direction: column;
+
+        &_label {
+          font-size: 12px;
+          font-weight: 400;
+          color: #818694;
+        }
+
+        &_value {
+          display: flex;
+          font-size: 14px;
+          font-weight: 500;
+          color: #000a18;
+        }
+
+        &_tag {
+          font-size: 12px;
+          height: 20px;
+          border-radius: 12px;
+          padding: 1px 10px;
+          margin: 0 8px;
+          white-space: nowrap;
+
+          &:last-child {
+            margin-right: 0;
+          }
+
+          &--warning {
+            background: #fff6e7;
+            color: #f90;
+          }
+
+          &--error {
+            background: #ffeee3;
+            color: #ff5d39;
+          }
+
+          &--success {
+            color: #19be6b;
+            background: #ecf8f2;
+          }
+
+          &--primary {
+            color: #0075ff;
+            background: #e5f1ff;
+          }
+
+          &--muted {
+            color: #828890;
+            background: #e1e3e7;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 45 - 7
src/components/XTForm/src/XTForm.vue

@@ -1,13 +1,11 @@
 <template>
   <div v-if="formShow">
-    <Form :model="formRef" class="flex justify-end">
+    <Form :model="formRef" class="flex justify-end" layout="inline">
       <div class="ml-2" v-for="item in formData" :key="item.name">
         <FormItem class="flex">
-          <template #label v-if="item.label">
-            <span :title="item.label">
-              {{ item.label }}
-            </span>
-          </template>
+          <div :title="item.label" v-if="item.label" class="xt-from_label">
+            {{ item.label }}
+          </div>
           <div v-if="item.componentType == ComponentEnum.Input">
             <Input
               v-model:value="formRef[item.name]"
@@ -16,6 +14,7 @@
               :style="{ width: item.width + 'px' }"
               :defaultValue="item.defaultValue"
               @change="handleChange"
+              :size="item.size || 'default'"
             />
           </div>
           <div v-if="item.componentType == ComponentEnum.Select">
@@ -26,6 +25,7 @@
               :style="{ width: item.width + 'px' }"
               :defaultValue="item.defaultValue"
               @change="handleChange"
+              :size="item.size || 'default'"
             >
               <SelectOption
                 v-for="r in item.dicts"
@@ -55,8 +55,14 @@
               :style="{ width: item.width + 'px' }"
               :defaultValue="item.defaultValue"
               @change="handleChange"
+              :size="item.size || 'default'"
             />
           </div>
+          <div v-if="item.componentType == ComponentEnum.IconBtn">
+            <div class="icon-btn">
+              <i class="iconfont icon-lock" @click="handleClick(item)" />
+            </div>
+          </div>
         </FormItem>
       </div>
     </Form>
@@ -107,13 +113,14 @@
       }>;
       disabled?: boolean;
       defaultValue?: string;
+      size?: string;
     }>;
   }
   withDefaults(defineProps<Props>(), {
     formData: () => [],
     formShow: true,
   });
-  const emit = defineEmits(['change']);
+  const emit = defineEmits(['change', 'click']);
   // 表单
   const formRef = ref<any>({});
   function handleChange() {
@@ -125,6 +132,11 @@
 
     emit('change', formRef.value);
   }, 1000);
+
+  function handleClick(data) {
+    console.log('🚀 ~ file: XTForm.vue:138 ~ handleClick ~ data:', data);
+    emit('click', data);
+  }
 </script>
 
 <style lang="less" scoped>
@@ -134,4 +146,30 @@
     border-radius: 50%;
     margin-right: 4px;
   }
+
+  .icon-btn {
+    width: 40px;
+    height: 40px;
+    background: #fff;
+    box-shadow: 0 0 18px 0 rgb(0 37 74 / 12%);
+    border-radius: 4px;
+    text-align: center;
+    line-height: 40px;
+    cursor: pointer;
+
+    & .iconfont {
+      font-size: 24px;
+      color: #0075ff;
+      margin-right: 0;
+    }
+  }
+
+  ::v-deep(.ant-form-item-control-input-content) {
+    display: flex;
+    align-items: center;
+  }
+
+  .xt-from_label {
+    margin-right: 10px;
+  }
 </style>

+ 2 - 0
src/components/XTForm/src/componentEnum.ts

@@ -19,6 +19,8 @@ export enum ComponentEnum {
   TreeSelect = 'TreeSelect',
   // Upload 上传
   Upload = 'Upload',
+  // 按钮图标组件
+  IconBtn = 'IconBtn',
 }
 
 /**

+ 0 - 0
src/components/XTList/index.ts


+ 182 - 0
src/components/XTList/src/List.vue

@@ -0,0 +1,182 @@
+<template>
+  <div class="menu">
+    <div
+      :class="[
+        'menu-item',
+        item.id == selected ? 'menu-item--selected' : '',
+        type == 'attachment' ? 'menu-item--attachment' : '',
+      ]"
+      :style="{ width: width + 'px', height: height + 'px' }"
+      v-for="item in data"
+      :key="item.id"
+      @click="handleClick(item)"
+    >
+      <div class="menu-item_left">
+        <div class="menu-item_left-t">{{ item.title }}</div>
+        <div class="menu-item_left-b">
+          <span v-if="item.startTime">
+            <span>化疗时间: </span>
+            <span>{{ item.startTime }} </span>
+          </span>
+          <span v-if="item.doctor">
+            <span>{{ item.doctor }} </span>
+          </span>
+        </div>
+      </div>
+      <div class="menu-item_right">
+        <div class="menu-item_right-t" v-if="item.status">
+          {{ item.status }}
+        </div>
+        <div class="menu-item_right-t" v-if="item.attachment">
+          <i class="iconfont icon-attachemnt" />
+          {{ item.attachment }}
+        </div>
+        <div class="menu-item_right-b">
+          <span v-if="item.endTime">
+            <span v-if="type == 'default'">报告时间: </span>
+            <span>{{ item.endTime }} </span>
+          </span>
+        </div>
+      </div>
+
+      <div :class="['menu-item_right', 'menu-item_right--edit', type == 'default' ? 'hidden' : '']">
+        <div class="menu-item_right-btn" @click="handleEdit(item)">
+          <i class="iconfont icon-xt-details_edit_default" />
+        </div>
+        <div class="menu-item_right-btn" @click="handleDel(item)">
+          <!-- 删除 -->
+          <i class="iconfont icon-xt-details_delete_default" />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  interface Props {
+    data: Array<{
+      id: string;
+      // 标题
+      title: string;
+      // 化验时间
+      startTime?: string;
+      // 报告时间 | 创建时间
+      endTime?: string;
+      // 医生名字
+      doctor?: string;
+      // 附件
+      attachment?: string;
+      // 状态
+      status?: string;
+    }>;
+    // 选中项
+    selected: string;
+    // 类型
+    type: 'default' | 'attachment';
+    // 宽度
+    width?: number;
+    // 高度
+    height?: number;
+  }
+  withDefaults(defineProps<Props>(), {
+    data: () => [],
+    selected: '0',
+    type: 'default',
+    width: 445,
+    height: 82,
+  });
+  const emit = defineEmits(['itemClick']);
+  function handleClick(data) {
+    emit('itemClick', data);
+  }
+  // 编辑
+  function handleEdit(data) {
+    console.log('🚀 ~ file: handleEdit List.vue:70 ~ data:', data);
+  }
+  // 删除
+  function handleDel(data) {
+    console.log('🚀 ~ file: handleDel List.vue:74 ~ data:', data);
+  }
+</script>
+
+<style lang="less" scoped>
+  .menu {
+    &-item {
+      display: flex;
+      justify-content: space-between;
+      padding: 20px;
+      cursor: pointer;
+      color: #818694;
+      font-size: 12px;
+      font-weight: 400;
+      background-color: #fff;
+
+      &_left {
+        &-t {
+          font-size: 14px;
+          font-weight: 500;
+          color: #000a18;
+          margin-bottom: 6px;
+        }
+        // &-b {
+        // }
+      }
+
+      &_right {
+        text-align: right;
+        transition: all 0.3s ease-in-out;
+
+        &-t {
+          margin-bottom: 6px;
+        }
+
+        &-btn {
+          margin-right: 16px;
+          background: #fff;
+          border-radius: 50%;
+          width: 32px;
+          height: 32px;
+          text-align: center;
+          line-height: 32px;
+
+          &:last-child {
+            margin-right: 0;
+          }
+
+          &:hover {
+            color: #0075ff;
+          }
+        }
+
+        &--edit {
+          display: none;
+        }
+      }
+
+      &:hover {
+        background: #f6f8fa;
+      }
+
+      &--selected {
+        background: #f6f8fa;
+        box-shadow: 1px 0 0 0 #efefef, inset 3px 0 0 0 #0075ff;
+      }
+
+      &--attachment {
+        justify-content: space-between;
+        align-items: center;
+        transition: all 0.3s ease-in-out;
+
+        &:hover {
+          .menu-item_right {
+            display: none;
+          }
+
+          .menu-item_right--edit {
+            display: flex !important;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 53 - 0
src/components/XTList/src/Menu.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="menu">
+    <div
+      :class="['menu-item', item.value == selected ? 'menu-item--selected' : '']"
+      v-for="item in data"
+      :key="item.value"
+      @click="handleClick(item)"
+    >
+      {{ item.label }}
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  interface Props {
+    data: Array<{
+      label: string;
+      value: string;
+    }>;
+    selected: string;
+  }
+  withDefaults(defineProps<Props>(), {
+    data: () => [],
+    selected: '0',
+  });
+  const emit = defineEmits(['itemClick']);
+  function handleClick(data) {
+    emit('itemClick', data);
+  }
+</script>
+
+<style lang="less" scoped>
+  .menu {
+    &-item {
+      display: block;
+      width: 160px;
+      height: 48px;
+      cursor: pointer;
+
+      &:hover {
+        background: #f6f8fa;
+      }
+
+      &--selected {
+        background: #f6f8fa;
+        font-size: 14px;
+        font-weight: 600;
+        color: #0075ff;
+        box-shadow: 1px 0 0 0 #efefef, inset 3px 0 0 0 #0075ff;
+      }
+    }
+  }
+</style>

+ 27 - 3
src/components/XTTab/src/XTTab.vue

@@ -2,7 +2,11 @@
   <div class="xt-tab">
     <div class="tab-list">
       <div
-        :class="['tab-list_item', selected == item.key ? 'tab-list_item--selected' : '']"
+        :class="[
+          'tab-list_item',
+          selected == item.key ? 'tab-list_item--selected' : '',
+          item.disabled ? 'tab-list_item--disabled' : '',
+        ]"
         v-for="item in data"
         :key="item.key"
         @click="handleClick(item)"
@@ -39,6 +43,8 @@
     data: Array<{
       // 唯一值
       key: string;
+      // 是否disabled
+      disabled?: boolean;
       // 名称
       label: string;
       // 后缀的值
@@ -65,8 +71,10 @@
   const emit = defineEmits(['itemClick']);
 
   async function handleClick(data) {
-    console.log('🚀 ~ file: XTTab.vue:36 ~ handleClick ~ data:', data);
-    emit('itemClick', { type: props.type, value: data.key });
+    if (!data.disabled) {
+      console.log('🚀 ~ file: XTTab.vue:36 ~ handleClick ~ data:', data);
+      emit('itemClick', { type: props.type, value: data.key });
+    }
   }
 </script>
 
@@ -105,6 +113,22 @@
         }
       }
 
+      &--disabled {
+        color: #ccc;
+
+        &:hover {
+          color: #ccc !important;
+          font-size: 14px;
+          font-weight: 400;
+          background-color: transparent;
+          cursor: auto;
+
+          span {
+            color: #ccc !important;
+          }
+        }
+      }
+
       &-prefix {
         width: 10px;
         height: 10px;

+ 0 - 0
src/components/XTTimeLine/index.ts


+ 142 - 0
src/components/XTTimeLine/src/TimeLine.vue

@@ -0,0 +1,142 @@
+<template>
+  <div class="timeline">
+    <div class="timeline-head" v-if="head">
+      <i
+        class="iconfont icon-xt-add_default"
+        v-if="headIcon"
+        @mousemove="handleMouseMove"
+        @click="handleClick"
+      />
+      <slot name="head" />
+    </div>
+    <div class="timeline-body">
+      <div class="timeline-item" v-for="item in data" :key="item.id">
+        <!-- 线 -->
+        <div class="timeline-item-tail" />
+        <!-- 圆点 -->
+        <div class="timeline-item-dot"> {{ item.dot }} </div>
+        <div class="timeline-item-cnt">
+          <DescCard
+            :id="item.cnt.id"
+            icon="icon-xt-edit_default"
+            icon-type="edit"
+            :type="item.cnt.type"
+            :title="item.cnt.title"
+            :data="item.cnt.data"
+            @icon="handleIcon"
+          />
+        </div>
+        <div class="timeline-item-date"> {{ item.date }} </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import DescCard from '/@/components/XTCard/src/DescCard.vue';
+
+  interface Props {
+    head?: boolean;
+    headIcon?: boolean;
+    data: Array<{
+      id?: string;
+      dot?: string;
+      date: string;
+      cnt: any;
+    }>;
+  }
+  withDefaults(defineProps<Props>(), {
+    head: true,
+    headIcon: true,
+    data: () => [],
+  });
+
+  const emit = defineEmits(['icon', 'add', 'hover']);
+
+  function handleIcon(data) {
+    console.log('🚀 ~ file: TimeLine.vue:44 ~ handleIcon ~ data:', data);
+    emit('icon', data);
+  }
+  function handleMouseMove() {
+    console.log('mousemove');
+    emit('hover');
+  }
+
+  function handleClick() {
+    console.log('mousemove');
+    emit('add');
+  }
+</script>
+
+<style lang="less" scoped>
+  .timeline {
+    background-color: #fff;
+    padding: 10px 0;
+
+    &-head {
+      display: flex;
+      font-size: 12px;
+      line-height: 40px;
+      font-weight: 600;
+      text-align: center;
+      margin: 20px 0 20px 96px;
+
+      & .iconfont {
+        display: block;
+        width: 40px;
+        height: 40px;
+        border-radius: 50%;
+        color: #fff;
+        background: #0075ff;
+        cursor: pointer;
+      }
+    }
+
+    &-item {
+      position: relative;
+      margin: 0;
+      padding-bottom: 20px;
+      font-size: 14px;
+      list-style: none;
+
+      &-tail {
+        position: absolute;
+        top: 10px;
+        left: 115px;
+        height: calc(100% - 10px);
+        border-left: 2px solid #f0f0f0;
+      }
+
+      &-dot {
+        position: absolute;
+        width: 40px;
+        height: 40px;
+        font-size: 12px;
+        line-height: 40px;
+        font-weight: 600;
+        color: #fff;
+        background: #21c999;
+        border-radius: 50%;
+        left: 96px;
+      }
+
+      &-cnt {
+        position: relative;
+        left: 120px;
+        text-align: left;
+        top: -8px;
+        margin: 0 0 0 26px;
+        word-break: break-word;
+        width: calc(100% - 162px);
+      }
+
+      &-date {
+        width: 86px;
+        margin: 0;
+        text-align: right;
+        position: absolute;
+        top: 10px;
+      }
+    }
+  }
+</style>

+ 2 - 2
src/enums/colorEnum.ts

@@ -3,7 +3,7 @@
  */
 export enum ColorEnum {
   // 未称量 | 透析室待确认
-  PRIMARY = '#2E5BFF',
+  PRIMARY = '#2D5AFF',
   PRIMARY_BG = '#DCE4FF',
 
   // 待确认 | 透析室待核对
@@ -23,7 +23,7 @@ export enum ColorEnum {
   MUTED_DOT = '#D3D8DD',
 
   // 透析室准备上机
-  PURPLE = '#864AFF',
+  PURPLE = '#854AFF',
   PURPLE_BG = '#EEE6FF',
   // 非透中患者
   PURPLE_DOT = '#854AFF',

+ 2 - 0
src/main.ts

@@ -22,6 +22,8 @@ import 'ant-design-vue/dist/antd.variable.min.css';
 import './assets/iconfont/iconfont.js';
 import './assets/iconfont/iconfont.css';
 
+import 'animate.css';
+
 async function bootstrap() {
   const app = createApp(App);
   // Configure store

+ 6 - 0
src/utils/lib/echarts.ts

@@ -25,6 +25,9 @@ import {
   TimelineComponent,
   CalendarComponent,
   GraphicComponent,
+  MarkAreaComponent,
+  SingleAxisComponent,
+  AxisPointerComponent,
 } from 'echarts/components';
 
 import { SVGRenderer, CanvasRenderer } from 'echarts/renderers';
@@ -33,6 +36,9 @@ echarts.use([
   LegendComponent,
   TitleComponent,
   TooltipComponent,
+  MarkAreaComponent,
+  SingleAxisComponent,
+  AxisPointerComponent,
   GridComponent,
   PolarComponent,
   AriaComponent,

+ 97 - 9
src/views/biz/visit/ready/data.ts

@@ -1,3 +1,4 @@
+import { BasicColumn } from '/@/components/Table';
 import { FormSchema } from '/@/components/Form';
 import { radioBoolean } from '/@/utils/filters';
 
@@ -10,15 +11,21 @@ export const dataFormSchema: FormSchema[] = [
       span: 24,
     },
   },
-  // {
-  //   field: 'PlainText',
-  //   component: 'PlainText',
-  //   label: '检测时间',
-  //   defaultValue: '2023-12-12 12:12:12',
-  //   colProps: {
-  //     span: 24,
-  //   },
-  // },
+  {
+    field: 'PlainText1111',
+    component: 'RadioDescGroup',
+    label: '1、干体重在过去3~6个月总的变化',
+    componentProps: {
+      options: [
+        { label: '干体重没有减少或体重丢失 <0.5kg (0分)', value: 0 },
+        { label: '体重丢失 ≥0.5kg,但 <1kg (1分)', value: 1 },
+      ],
+    },
+    colProps: {
+      span: 24,
+    },
+    defaultValue: 1,
+  },
   {
     field: 'configName',
     label: '参数名称',
@@ -118,3 +125,84 @@ export const dataFormSchema: FormSchema[] = [
     },
   },
 ];
+export const columns: BasicColumn[] = [
+  {
+    title: '名称',
+    dataIndex: 'name',
+    width: 100,
+    edit: true,
+    editable: true,
+    editComponent: 'Input',
+  },
+  {
+    title: '类型',
+    dataIndex: 'type',
+    width: 150,
+  },
+  {
+    title: '联系人',
+    dataIndex: 'contractUser',
+    width: 150,
+  },
+  {
+    title: '联系人电话',
+    dataIndex: 'contactMobile',
+    width: 150,
+  },
+  {
+    title: '名称',
+    dataIndex: 'name',
+    width: 100,
+  },
+  {
+    title: '类型',
+    dataIndex: 'type',
+    width: 150,
+  },
+  {
+    title: '联系人',
+    dataIndex: 'contractUser',
+    width: 150,
+  },
+  {
+    title: '联系人电话',
+    dataIndex: 'contactMobile',
+    width: 150,
+  },
+  {
+    title: '管理账号',
+    dataIndex: 'username',
+    width: 150,
+  },
+  {
+    title: '套餐名称',
+    dataIndex: 'packageName',
+    width: 150,
+  },
+  {
+    title: '联系人',
+    dataIndex: 'contractUser',
+    width: 150,
+  },
+  {
+    title: '联系人电话',
+    dataIndex: 'contactMobile',
+    width: 150,
+  },
+  {
+    title: '管理账号',
+    dataIndex: 'username',
+    width: 150,
+  },
+  {
+    title: '套餐名称',
+    dataIndex: 'packageName',
+    width: 150,
+    fixed: 'right',
+  },
+  {
+    title: '状态',
+    dataIndex: 'disable',
+    fixed: 'right',
+  },
+];

+ 264 - 1
src/views/biz/visit/ready/index.vue

@@ -1,6 +1,9 @@
 <template>
   <div>
     透前准备
+    <a-button type="primary" @click="plusFn">
+      {{ countRef }}
+    </a-button>
     <XTTab
       type="illness"
       :width="120"
@@ -11,12 +14,64 @@
     <div class="mt-6" />
     <XTTab type="illness11" :selected="tabSelected" :data="tabData1" @item-click="callTab" />
     <div class="mt-6" />
+    <div class="mt-6" />
+    <XTTab type="illness11" :selected="tabSelected" :data="tabData2" @item-click="callTab" />
+    <div class="mt-6" />
     <div class="m-6">
       <BasicForm @register="registerForm" @field-value-change="filedChange" />
     </div>
+    <div>
+      <BasicTable @register="registerTable">
+        <template #headerTop>
+          <span>headerTop</span>
+        </template>
+        <template #toolbar>
+          <span>toolbar</span>
+        </template>
+      </BasicTable>
+    </div>
     <div>
       <XTForm :form-data="formData" />
     </div>
+    <div class="mx-6 my-2">
+      <ChartsCard
+        title="透前血压趋势"
+        :has-safe="true"
+        :colors="chartData.colors"
+        :safe-range="chartData.safeRange"
+      />
+    </div>
+    <div class="mx-6 my-2">
+      <DescCard
+        id="1"
+        icon="icon-xt-add_default"
+        title="透析测量"
+        type="touxi"
+        :data="descData"
+        :right="descRight"
+      />
+    </div>
+    <div class="mx-6 my-2">
+      <TimeLine :data="timeLineData" @hover="callHover">
+        <template #head>
+          <div class="timeline-outer" v-if="timeOuter">
+            <div
+              class="timeline-outer_item animate__animated animate__slideInLeft"
+              @click="handleAdd"
+              >AVF</div
+            >
+            <div class="timeline-outer_item animate__animated animate__slideInLeft">AVH</div>
+            <div class="timeline-outer_item animate__animated animate__slideInLeft">TCG</div>
+          </div>
+        </template>
+      </TimeLine>
+    </div>
+    <div class="mx-6 my-2">
+      <List type="default" :data="listData" selected="0" />
+    </div>
+    <div class="mx-6 my-2">
+      <List type="attachment" :data="listData1" selected="0" :width="320" />
+    </div>
     <div class="flex justify-between">
       <XTCard class="m-2" :data="cardData1" @item-click="cellCard" />
     </div>
@@ -33,9 +88,171 @@
   import { XTForm } from '/@/components/XTForm/index';
   import { ColorEnum } from '/@/enums/colorEnum';
   import { BasicForm, useForm } from '/@/components/Form';
-  import { dataFormSchema } from './data';
+  import { dataFormSchema, columns } from './data';
+  import { BasicTable, useTable } from '/@/components/Table';
+  import { onMounted } from 'vue';
+  import ChartsCard from '/@/components/XTCard/src/ChartsCard.vue';
+  import DescCard from '/@/components/XTCard/src/DescCard.vue';
+  import TimeLine from '/@/components/XTTimeLine/src/TimeLine.vue';
+  import List from '/@/components/XTList/src/List.vue';
+  import { TransitionPresets, useTransition } from '@vueuse/core';
 
   const tabSelected = ref('0');
+  const dataSource = ref([]);
+  const chartData = {
+    colors: [
+      {
+        color: 'rgba(0, 117, 255, 1)',
+        label: '化验值',
+        dot: 'rgba(0, 117, 255, 1)',
+      },
+    ],
+    safeRange: [
+      {
+        // name: '60分到80分',
+        yAxis: 0,
+      },
+      {
+        yAxis: 20,
+      },
+    ],
+  };
+  const listData = [
+    {
+      id: '0',
+      title: '红细胞笔迹测定',
+      startTime: '2023-11-11 12:00',
+      endTime: '2023-12-11 12:00',
+      status: 'default',
+    },
+    {
+      id: '2',
+      title: '红细胞笔迹测定2',
+      startTime: '2023-11-11 12:00',
+      endTime: '2023-12-11 12:00',
+      status: 'default',
+    },
+  ];
+  const listData1 = [
+    {
+      id: '0',
+      title: '红细胞笔迹测定',
+      startTime: '2023-11-11 12:00',
+      endTime: '2023-12-11 12:00',
+      status: 'default',
+    },
+    {
+      id: '2',
+      title: '红细胞笔迹测定2',
+      startTime: '2023-11-11 12:00',
+      endTime: '2023-12-11 12:00',
+      status: 'default',
+    },
+  ];
+
+  const descData = [
+    {
+      label: '诊断名称/病史/状态',
+      value: '乙肝 / 2022-01-19 ~',
+      tags: [{ id: '12', label: '活动中', type: 'error' }],
+    },
+    {
+      label: '备注',
+      value: '药物治疗或其他治疗',
+    },
+    {
+      label: '测试',
+      value: '药物治疗或其他治疗',
+    },
+    {
+      label: '诊断名称/病史/状态备份',
+      value: '结核 / 2022-02-10 ~ 20',
+      tags: [
+        { id: '1', label: '未活动', type: 'muted' },
+        { id: '2', label: '未活动1', type: 'primary' },
+      ],
+    },
+  ];
+  const descRight = {
+    show: true,
+    date: '2023-04-23',
+    doctor: '张医生',
+    edit: true,
+    delete: true,
+  };
+
+  const timeLineData = [
+    {
+      id: '1',
+      dot: '王医生',
+      date: '2023-05-20',
+      cnt: {
+        title: '测试' + Math.round(Math.random() * 1000),
+        id: '123',
+        type: '123',
+        data: descData,
+      },
+    },
+    {
+      id: '12',
+      dot: '范医生',
+      date: '2023-03-20',
+      cnt: {
+        title: '测试' + Math.round(Math.random() * 1000),
+        id: '123',
+        type: '123',
+        data: descData,
+      },
+    },
+  ];
+  const timeOuter = ref(false);
+  onMounted(() => {
+    for (let i = 0; i < 10; i++) {
+      const obj = {
+        createTime: '2023-05-23 10:09:48',
+        updateTime: '2023-05-23 19:00:32',
+        id: '1660830352886149125' + Math.round(Math.random() * 10000),
+        name: '驼人',
+        packageId: '1655202440997244930',
+        packageName: '测试套餐',
+        username: 'tuoren',
+        type: 'custom',
+        contractUser: 'Lf',
+        contactMobile: '18339543638',
+        remark: null,
+        disable: 0,
+      };
+      dataSource.value.push(obj);
+    }
+  });
+  const count = ref(0);
+  const countRef = useTransition(count, {
+    duration: 1000,
+    transition: TransitionPresets.easeInCirc,
+  });
+  function plusFn() {
+    count.value = Math.round(Math.random() * 100);
+  }
+  // const [registerTable, { reload, getCacheColumns, setColumns }] = useTable({
+  const [registerTable] = useTable({
+    id: 'sys_config',
+    title: '参数列表2121',
+    columns,
+    dataSource: dataSource,
+    useSearchForm: false,
+    titleStyle: {
+      fontSize: '14px',
+      color: '#000a18',
+      cursor: 'default',
+    },
+    titleLined: true,
+    bordered: false,
+    showIndexColumn: false,
+    pagination: false,
+    // maxHeight: 350,
+    canResize: true,
+  });
+
   const [registerForm] = useForm({
     layout: 'vertical',
     labelWidth: '100%',
@@ -84,6 +301,21 @@
       hasBracket: true,
     },
   ];
+  const tabData2 = [
+    {
+      key: '0',
+      label: 'A1',
+    },
+    {
+      key: '1',
+      label: 'A2',
+      disabled: true,
+    },
+    {
+      key: '2',
+      label: 'B3',
+    },
+  ];
   const tabData1 = [
     {
       key: '0',
@@ -102,6 +334,7 @@
   const formData = [
     {
       name: 'text',
+      label: '全部',
       componentType: 'Select',
       placeholder: '请选择',
       width: 80,
@@ -120,6 +353,7 @@
       placeholder: '请选择',
       width: 120,
       defaultValue: '1',
+      label: '班次',
       dicts: [
         { label: '第一班', value: '1' },
         { label: '第二班', value: '2' },
@@ -139,6 +373,10 @@
       format: 'YYYY-MM-DD',
       valueFormat: 'YYYY-MM-DD',
     },
+    {
+      name: 'filter',
+      componentType: 'IconBtn',
+    },
   ];
   // card 标签组
   const cardData = [
@@ -261,6 +499,10 @@
       ],
     },
   ];
+
+  function handleAdd() {
+    timeOuter.value = false;
+  }
   // 回调
   function callTab(data) {
     console.log('🚀 ~ file: index.vue:41 ~ callTab ~ data:', data);
@@ -270,10 +512,31 @@
   function cellCard(data) {
     console.log('🚀 ~ file: index.vue:106 ~ cellCard ~ data:', data);
   }
+
+  function callHover() {
+    timeOuter.value = true;
+  }
 </script>
 
 <style lang="less" scoped>
   ::v-deep(.ant-form-item-label > label) {
     width: 100% !important;
   }
+
+  .timeline-outer {
+    display: flex;
+    margin-left: 20px;
+    transition: all 0.3s ease-in-out;
+
+    &_item {
+      padding: 0 30px;
+      height: 40px;
+      line-height: 40px;
+      border-radius: 30px;
+      color: #fff;
+      background: #0075ff;
+      margin-right: 20px;
+      cursor: pointer;
+    }
+  }
 </style>