Преглед на файлове

传秀AI终端(智能玩具微信小程序端)首次提交

htc преди 1 седмица
ревизия
e18294a41c
променени са 100 файла, в които са добавени 10409 реда и са изтрити 0 реда
  1. 2 0
      .gitignore
  2. 20 0
      .hbuilderx/launch.json
  3. 4 0
      .idea/encodings.xml
  4. 6 0
      .idea/misc.xml
  5. 8 0
      .idea/modules.xml
  6. 6 0
      .idea/vcs.xml
  7. 12 0
      .idea/witcabonAPP.iml
  8. 618 0
      .idea/workspace.xml
  9. 145 0
      App.vue
  10. 33 0
      androidPrivacy.json
  11. 89 0
      components/CusHeader/index.vue
  12. 96 0
      components/CusTabbar/index.vue
  13. 37 0
      components/pageEmpty/index.vue
  14. 93 0
      http/Api.js
  15. 6 0
      http/baseApi.js
  16. 93 0
      http/index.js
  17. 120 0
      http/interface.js
  18. 112 0
      http/readme.md
  19. 20 0
      index.html
  20. 63 0
      main.js
  21. 139 0
      manifest.json
  22. 13 0
      node_modules/.package-lock.json
  23. 21 0
      node_modules/event-source-polyfill/LICENSE
  24. 21 0
      node_modules/event-source-polyfill/LICENSE.md
  25. 277 0
      node_modules/event-source-polyfill/README.md
  26. 34 0
      node_modules/event-source-polyfill/package.json
  27. 1048 0
      node_modules/event-source-polyfill/src/eventsource.js
  28. 6 0
      node_modules/event-source-polyfill/src/eventsource.min.js
  29. 25 0
      package-lock.json
  30. 5 0
      package.json
  31. 66 0
      pages.json
  32. 184 0
      pages/agentCreate.vue
  33. 173 0
      pages/agentDetail.vue
  34. 72 0
      pages/code.vue
  35. 115 0
      pages/device.vue
  36. 272 0
      pages/home.vue
  37. 81 0
      pages/login.vue
  38. 189 0
      pages/roleSet.vue
  39. 118 0
      pages/wifi - 副本.vue
  40. 99 0
      pages/wifi.vue
  41. 187 0
      pages/wifiSet.vue
  42. BIN
      static/add.png
  43. BIN
      static/ai_shape.png
  44. BIN
      static/avatar_big.png
  45. BIN
      static/avatar_small.png
  46. BIN
      static/bind.png
  47. BIN
      static/box_bg1.png
  48. BIN
      static/box_bg2.png
  49. BIN
      static/change.png
  50. BIN
      static/change2.png
  51. BIN
      static/connect_off.png
  52. BIN
      static/device_gl.png
  53. BIN
      static/lock.png
  54. BIN
      static/selected.png
  55. BIN
      static/top_bg.png
  56. BIN
      static/top_boxbg.png
  57. BIN
      static/user_set.png
  58. BIN
      static/wifi.png
  59. 118 0
      uni.scss
  60. 21 0
      uni_modules/uview-ui/LICENSE
  61. 66 0
      uni_modules/uview-ui/README.md
  62. 357 0
      uni_modules/uview-ui/changelog.md
  63. 78 0
      uni_modules/uview-ui/components/u--form/u--form.vue
  64. 47 0
      uni_modules/uview-ui/components/u--image/u--image.vue
  65. 73 0
      uni_modules/uview-ui/components/u--input/u--input.vue
  66. 44 0
      uni_modules/uview-ui/components/u--text/u--text.vue
  67. 48 0
      uni_modules/uview-ui/components/u--textarea/u--textarea.vue
  68. 54 0
      uni_modules/uview-ui/components/u-action-sheet/props.js
  69. 278 0
      uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue
  70. 59 0
      uni_modules/uview-ui/components/u-album/props.js
  71. 259 0
      uni_modules/uview-ui/components/u-album/u-album.vue
  72. 44 0
      uni_modules/uview-ui/components/u-alert/props.js
  73. 243 0
      uni_modules/uview-ui/components/u-alert/u-alert.vue
  74. 52 0
      uni_modules/uview-ui/components/u-avatar-group/props.js
  75. 103 0
      uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue
  76. 78 0
      uni_modules/uview-ui/components/u-avatar/props.js
  77. 172 0
      uni_modules/uview-ui/components/u-avatar/u-avatar.vue
  78. 54 0
      uni_modules/uview-ui/components/u-back-top/props.js
  79. 129 0
      uni_modules/uview-ui/components/u-back-top/u-back-top.vue
  80. 72 0
      uni_modules/uview-ui/components/u-badge/props.js
  81. 171 0
      uni_modules/uview-ui/components/u-badge/u-badge.vue
  82. 46 0
      uni_modules/uview-ui/components/u-button/nvue.scss
  83. 161 0
      uni_modules/uview-ui/components/u-button/props.js
  84. 490 0
      uni_modules/uview-ui/components/u-button/u-button.vue
  85. 80 0
      uni_modules/uview-ui/components/u-button/vue.scss
  86. 99 0
      uni_modules/uview-ui/components/u-calendar/header.vue
  87. 579 0
      uni_modules/uview-ui/components/u-calendar/month.vue
  88. 144 0
      uni_modules/uview-ui/components/u-calendar/props.js
  89. 384 0
      uni_modules/uview-ui/components/u-calendar/u-calendar.vue
  90. 85 0
      uni_modules/uview-ui/components/u-calendar/util.js
  91. 14 0
      uni_modules/uview-ui/components/u-car-keyboard/props.js
  92. 311 0
      uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue
  93. 14 0
      uni_modules/uview-ui/components/u-cell-group/props.js
  94. 61 0
      uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue
  95. 110 0
      uni_modules/uview-ui/components/u-cell/props.js
  96. 229 0
      uni_modules/uview-ui/components/u-cell/u-cell.vue
  97. 82 0
      uni_modules/uview-ui/components/u-checkbox-group/props.js
  98. 103 0
      uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue
  99. 69 0
      uni_modules/uview-ui/components/u-checkbox/props.js
  100. 0 0
      uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+test.apk
+/unpackage/release/apk/__UNI__FF40C0C__20230327163324.apk

+ 20 - 0
.hbuilderx/launch.json

@@ -0,0 +1,20 @@
+{
+    "version" : "1.0",
+    "configurations" : [
+        {
+            "playground" : "standard",
+            "type" : "uni-app:app-android"
+        },
+        {
+        	"app-plus" : 
+        	{
+        		"launchtype" : "local"
+        	},
+        	"mp-weixin" : 
+        	{
+        		"launchtype" : "local"
+        	},
+        	"type" : "uniCloud"
+        }
+    ]
+}

+ 4 - 0
.idea/encodings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" addBOMForNewFiles="with NO BOM" />
+</project>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/witcabonAPP.iml" filepath="$PROJECT_DIR$/.idea/witcabonAPP.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 12 - 0
.idea/witcabonAPP.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 618 - 0
.idea/workspace.xml

@@ -0,0 +1,618 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="b14f9fe1-d3c3-496a-8ebb-4789fe400212" name="默认更改列表" comment="">
+      <change beforePath="$PROJECT_DIR$/.hbuilderx/launch.json" beforeDir="false" afterPath="$PROJECT_DIR$/.hbuilderx/launch.json" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/http/baseApi.js" beforeDir="false" afterPath="$PROJECT_DIR$/http/baseApi.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/manifest.json" beforeDir="false" afterPath="$PROJECT_DIR$/manifest.json" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages.json" beforeDir="false" afterPath="$PROJECT_DIR$/pages.json" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/index/Workordertodone/Toberepaired/Toberepaired.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/index/Workordertodone/Toberepaired/Toberepaired.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/index/Workordertodone/hasrepaired/hasrepaired.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/index/Workordertodone/hasrepaired/hasrepaired.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/login/Privacyagreement.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/login/Privacyagreement.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/login/login.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/login/login.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/login/register.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/login/register.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pages/login/useragree.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/login/useragree.vue" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-config-service.js" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-config-service.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-service.js" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-service.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-view.js" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/app-view.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/manifest.json" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/cache/wgt/__UNI__FF40C0C/manifest.json" afterDir="false" />
+    </list>
+    <ignored path="$PROJECT_DIR$/.tmp/" />
+    <ignored path="$PROJECT_DIR$/temp/" />
+    <ignored path="$PROJECT_DIR$/tmp/" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/pages/Workorder/Remotecontrol/Remotecontrol.vue">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="6750">
+              <caret line="230" column="35" selection-start-line="230" selection-start-column="27" selection-end-line="230" selection-end-column="35" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/pages/Workorder/Tenantbill/Tenantbill.vue">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="270">
+              <caret line="9" column="34" selection-start-line="9" selection-start-column="34" selection-end-line="9" selection-end-column="34" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/pages/Workorder/Tenantbill/companybill/companybill.vue">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="279">
+              <caret line="250" column="59" selection-start-line="250" selection-start-column="51" selection-end-line="250" selection-end-column="59" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="JavaScript File" />
+      </list>
+    </option>
+  </component>
+  <component name="FindInProjectRecents">
+    <findStrings>
+      <find>columnsrenyuan</find>
+      <find>Workorderdetails3</find>
+      <find>editdata</find>
+      <find>fileList1</find>
+      <find>uploadBox</find>
+      <find>uploadImg</find>
+      <find>class</find>
+      <find>newimg</find>
+      <find>confirmtype</find>
+      <find>columnstype</find>
+      <find>postdata</find>
+      <find>uploadPart</find>
+      <find>Workordertodone</find>
+      <find>srcxianshang1</find>
+      <find>kongbai</find>
+      <find>Homepage_suspension</find>
+      <find>tuichu</find>
+      <find>Homepage_header</find>
+      <find>managelind</find>
+      <find>managename</find>
+      <find>首页</find>
+      <find>mjname</find>
+      <find>sfkq</find>
+      <find>mjkg</find>
+      <find>Videosurveillance</find>
+      <find>asyncChange1</find>
+      <find>mjzt1</find>
+      <find>buttons</find>
+      <find>uni</find>
+      <find>dataList</find>
+    </findStrings>
+    <replaceStrings>
+      <replace>repairRegioncontactPerson</replace>
+    </replaceStrings>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/pages/my/Mydistribution/distributionedit/distributionedit.vue" />
+        <option value="$PROJECT_DIR$/http/baseApi.js" />
+        <option value="$PROJECT_DIR$/utils/qrcode.js" />
+        <option value="$PROJECT_DIR$/pages/index/BillPending/BillPending.vue" />
+        <option value="$PROJECT_DIR$/pages/index/EquipmentException/EquipmentException.vue" />
+        <option value="$PROJECT_DIR$/pages/my/Personalinformation/Personalinformation.vue" />
+        <option value="$PROJECT_DIR$/pages/my/Personalinformation/Informationmodification/Informationmodification.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Workordertodone/hasrepaired/hasrepaired.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Workordertodone/Workordertodone.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Onlinewarranty/Onlinewarranty.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Inspectionrecord/Inspectionrecord.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Withholdingrecord/Withholdingrecord.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Immediateinspection/Immediateinspection.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Immediatecollection/Chargedetails/Chargedetails.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Immediatecollection/Immediatecollection.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Workordertodone/Workorderdetails/Workorderdetails.vue" />
+        <option value="$PROJECT_DIR$/pages/index/Workordertodone/Toberepaired/Toberepaired.vue" />
+        <option value="$PROJECT_DIR$/pages/index/index.vue" />
+        <option value="$PROJECT_DIR$/pages/login/login.vue" />
+        <option value="$PROJECT_DIR$/pages/my/my.vue" />
+        <option value="$PROJECT_DIR$/uni.scss" />
+        <option value="$PROJECT_DIR$/pages.json" />
+        <option value="$PROJECT_DIR$/pages/Workorder/Workorder.vue" />
+        <option value="$PROJECT_DIR$/pages/Workorder/AccessControl/AccessControl.vue" />
+        <option value="$PROJECT_DIR$/manifest.json" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectFrameBounds" extendedState="6">
+    <option name="x" value="1913" />
+    <option name="y" value="2" />
+    <option name="width" value="1932" />
+    <option name="height" value="1045" />
+  </component>
+  <component name="ProjectLevelVcsManager">
+    <ConfirmationsSetting value="1" id="添加" />
+  </component>
+  <component name="ProjectView">
+    <navigator proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="Scope" />
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="directive" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="http" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Arrearsrecord" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Intelligentdoorlock" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Intelligentdoorlock" type="462c0819:PsiDirectoryNode" />
+              <item name="Dooropeningrecord" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Intelligentdoorlock" type="462c0819:PsiDirectoryNode" />
+              <item name="Tenantdetails" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Remotecontrol" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Tenantbill" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="pages" type="462c0819:PsiDirectoryNode" />
+              <item name="Workorder" type="462c0819:PsiDirectoryNode" />
+              <item name="Tenantbill" type="462c0819:PsiDirectoryNode" />
+              <item name="companybill" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="双碳APP" type="b2602c69:ProjectViewProjectNode" />
+              <item name="双碳APP" type="462c0819:PsiDirectoryNode" />
+              <item name="utils" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
+    <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
+    <property name="nodejs_npm_path_reset_for_default_project" value="true" />
+    <property name="nodejs_package_manager_path" value="npm" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="b14f9fe1-d3c3-496a-8ebb-4789fe400212" name="默认更改列表" comment="" />
+      <created>1680248385794</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1680248385794</updated>
+      <workItem from="1680248387431" duration="1978000" />
+      <workItem from="1680571981170" duration="1053000" />
+      <workItem from="1680939102464" duration="5415000" />
+      <workItem from="1681086898029" duration="593000" />
+      <workItem from="1681105513481" duration="9579000" />
+      <workItem from="1681172833354" duration="23497000" />
+      <workItem from="1681258597059" duration="12277000" />
+      <workItem from="1681345236598" duration="1191000" />
+      <workItem from="1681432005487" duration="594000" />
+      <workItem from="1681691706962" duration="1414000" />
+      <workItem from="1681776390439" duration="13917000" />
+      <workItem from="1681862208704" duration="24333000" />
+      <workItem from="1682035999607" duration="2001000" />
+      <workItem from="1682208706300" duration="592000" />
+      <workItem from="1682296331955" duration="593000" />
+      <workItem from="1682382351913" duration="595000" />
+      <workItem from="1682468371664" duration="591000" />
+      <workItem from="1682554138603" duration="592000" />
+      <workItem from="1682641765071" duration="593000" />
+      <workItem from="1683160641474" duration="594000" />
+      <workItem from="1683265707499" duration="595000" />
+      <workItem from="1683332933669" duration="593000" />
+      <workItem from="1683364463682" duration="61000" />
+      <workItem from="1683505747555" duration="593000" />
+      <workItem from="1683594601602" duration="595000" />
+      <workItem from="1683852687927" duration="250000" />
+      <workItem from="1683880139423" duration="549000" />
+      <workItem from="1694136155539" duration="54000" />
+      <workItem from="1696897994141" duration="1666000" />
+      <workItem from="1697178000799" duration="203000" />
+      <workItem from="1698114744785" duration="45000" />
+      <workItem from="1698129213286" duration="636000" />
+      <workItem from="1698303813553" duration="608000" />
+      <workItem from="1710415419678" duration="165000" />
+      <workItem from="1710558035957" duration="594000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TimeTrackingManager">
+    <option name="totallyTimeSpent" value="109199000" />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="1912" y="-8" width="1936" height="1056" extended-state="6" />
+    <editor active="true" />
+    <layout>
+      <window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.25" />
+      <window_info id="Structure" order="1" side_tool="true" weight="0.25" />
+      <window_info id="Favorites" order="2" side_tool="true" />
+      <window_info anchor="bottom" id="Message" order="0" />
+      <window_info anchor="bottom" id="Find" order="1" />
+      <window_info anchor="bottom" id="Run" order="2" />
+      <window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
+      <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
+      <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
+      <window_info anchor="bottom" id="TODO" order="6" />
+      <window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
+      <window_info anchor="bottom" id="Version Control" order="8" />
+      <window_info anchor="bottom" id="Terminal" order="9" />
+      <window_info anchor="bottom" id="Event Log" order="10" side_tool="true" />
+      <window_info anchor="bottom" id="调试" order="11" />
+      <window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
+      <window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
+      <window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
+    </layout>
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="1" />
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/pages/index/BillPending/BillPending.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="489">
+          <caret line="446" lean-forward="true" selection-start-line="446" selection-end-line="446" />
+          <folding>
+            <element signature="n#style#0;n#view#1;n#view#1;n#view#0;n#template#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#span#0;n#view#1;n#view#1;n#view#0;n#template#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#view#2;n#view#0;n#u-popup#0;n#view#0;n#template#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#view#2;n#view#0;n#u-popup#1;n#view#0;n#template#0;n#!!top" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/my/Mydistribution/distributionedit/distributionedit.vue" />
+    <entry file="file://$PROJECT_DIR$/pages/index/EquipmentException/EquipmentException.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="549">
+          <caret line="185" column="30" selection-start-line="185" selection-start-column="30" selection-end-line="185" selection-end-column="30" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/my/Personalinformation/Informationmodification/Informationmodification.vue" />
+    <entry file="file://$PROJECT_DIR$/pages/my/Personalinformation/Personalinformation.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="390">
+          <caret line="28" column="29" selection-start-line="24" selection-start-column="8" selection-end-line="28" selection-end-column="29" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Inspectionrecord/Inspectionrecord.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="259" column="23" selection-start-line="259" selection-start-column="12" selection-end-line="259" selection-end-column="23" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Withholdingrecord/Withholdingrecord.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="423">
+          <caret line="44" column="54" selection-start-line="44" selection-start-column="54" selection-end-line="44" selection-end-column="54" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Immediatecollection/Chargedetails/Chargedetails.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="60">
+          <caret line="62" column="37" lean-forward="true" selection-start-line="62" selection-start-column="37" selection-end-line="62" selection-end-column="37" />
+          <folding>
+            <element signature="n#style#0;n#span#0;n#view#0;n#view#0;n#view#0;n#template#0;n#!!top" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Onlinewarranty/Onlinewarranty.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="119">
+          <caret line="293" column="14" selection-start-line="293" selection-start-column="14" selection-end-line="293" selection-end-column="14" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Workordertodone/Workorderdetails/Workorderdetails.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="339">
+          <caret line="45" column="90" selection-start-line="45" selection-start-column="60" selection-end-line="45" selection-end-column="90" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Workordertodone/hasrepaired/hasrepaired.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="510">
+          <caret line="68" column="19" selection-start-line="68" selection-start-column="19" selection-end-line="68" selection-end-column="19" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Workordertodone/Toberepaired/Toberepaired.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="369">
+          <caret line="61" column="55" selection-start-line="61" selection-start-column="31" selection-end-line="61" selection-end-column="55" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Immediateinspection/Immediateinspection.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="144">
+          <caret line="146" column="55" selection-start-line="146" selection-start-column="41" selection-end-line="146" selection-end-column="55" />
+          <folding>
+            <element signature="n#style#0;n#view#0;n#view#3;n#view#0;n#template#0;n#!!top" expanded="true" />
+            <element signature="n#style#0;n#span#0;n#view#0;n#view#3;n#view#0;n#template#0;n#!!top" expanded="true" />
+            <element signature="e#1561#1611#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Workorder.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="1209">
+          <caret line="124" column="32" lean-forward="true" selection-start-line="124" selection-start-column="32" selection-end-line="124" selection-end-column="32" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/AccessControl/AccessControl.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="22" column="85" selection-start-line="22" selection-start-column="78" selection-end-line="22" selection-end-column="85" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Immediatecollection/Immediatecollection.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="240">
+          <caret line="56" lean-forward="true" selection-end-line="443" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/http/interface.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="180">
+          <caret line="6" column="17" selection-start-line="6" selection-start-column="17" selection-end-line="6" selection-end-column="17" />
+          <folding>
+            <element signature="e#0#39#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/http/Api.js">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/http/baseApi.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state>
+          <caret column="62" selection-start-column="17" selection-end-column="62" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/directive/permission.js">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/manifest.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-2430">
+          <caret line="12" column="26" selection-start-line="12" selection-start-column="26" selection-end-line="12" selection-end-column="26" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/utils/index.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-450">
+          <caret line="27" column="52" lean-forward="true" selection-start-line="27" selection-start-column="52" selection-end-line="27" selection-end-column="52" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/utils/qrcode.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="7659">
+          <caret line="411" column="38" selection-start-line="411" selection-start-column="29" selection-end-line="411" selection-end-column="38" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/package.json">
+      <provider selected="true" editor-type-id="text-editor" />
+    </entry>
+    <entry file="file://$PROJECT_DIR$/uni.scss">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="1470">
+          <caret line="49" selection-start-line="51" selection-start-column="2" selection-end-line="53" selection-end-column="3" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/index.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="6690">
+          <caret line="223" column="16" selection-start-line="223" selection-start-column="16" selection-end-line="223" selection-end-column="16" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/http/index.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="300">
+          <caret line="10" column="25" selection-start-line="10" selection-start-column="25" selection-end-line="10" selection-end-column="25" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/main.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="360">
+          <caret line="38" selection-start-line="38" selection-end-line="40" selection-end-column="35" />
+          <folding>
+            <element signature="e#120#660#0" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/my/my.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="3450">
+          <caret line="115" column="114" selection-start-line="115" selection-start-column="114" selection-end-line="115" selection-end-column="114" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/login/login.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="4020">
+          <caret line="134" column="23" selection-start-line="134" selection-start-column="23" selection-end-line="134" selection-end-column="23" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="744">
+          <caret line="47" column="13" selection-start-line="44" selection-start-column="12" selection-end-line="47" selection-end-column="13" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/my/Myorder/Myorder.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="81" column="24" selection-start-line="81" selection-start-column="16" selection-end-line="81" selection-end-column="24" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/index/Workordertodone/Workordertodone.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="113" column="41" selection-start-line="113" selection-start-column="33" selection-end-line="113" selection-end-column="41" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Intelligentdoorlock/Dooropeningrecord/Dooropeningrecord.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="39" column="24" selection-start-line="39" selection-start-column="16" selection-end-line="39" selection-end-column="24" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Intelligentdoorlock/Intelligentdoorlock.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="42" column="24" selection-start-line="42" selection-start-column="16" selection-end-line="42" selection-end-column="24" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Arrearsrecord/Arrearsrecord.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="129" column="67" selection-start-line="129" selection-start-column="59" selection-end-line="129" selection-end-column="67" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Intelligentdoorlock/Tenantdetails/Tenantdetails.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-450" />
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Remotecontrol/Remotecontrol.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="6750">
+          <caret line="230" column="35" selection-start-line="230" selection-start-column="27" selection-end-line="230" selection-end-column="35" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Tenantbill/Tenantbill.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="270">
+          <caret line="9" column="34" selection-start-line="9" selection-start-column="34" selection-end-line="9" selection-end-column="34" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/pages/Workorder/Tenantbill/companybill/companybill.vue">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="279">
+          <caret line="250" column="59" selection-start-line="250" selection-start-column="51" selection-end-line="250" selection-end-column="59" />
+        </state>
+      </provider>
+    </entry>
+  </component>
+</project>

+ 145 - 0
App.vue

@@ -0,0 +1,145 @@
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		methods: {
+			
+		},
+		onLaunch: function() {
+			
+		},
+		onShow: function() {
+
+		},
+		onHide: function() {
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "@/uni_modules/uview-ui/index.scss";
+
+	* {
+		box-sizing: border-box;
+	}
+
+	uni-page-body,
+	html,
+	body {
+		height: 100%;
+		width: 750rpx;
+		background: #F6F6F6;
+	}
+
+	.u-button--primary {
+		background-color: #5776E6;
+	}
+
+	.uni-input-placeholder {
+		text-align: right;
+	}
+
+	.ml5 {
+		margin-left: 10rpx;
+	}
+
+	.contain {
+		height: 100%;
+		padding-bottom: 120rpx;
+		padding-top: 24rpx;
+		overflow-y: auto;
+
+		.main {
+			padding: 0 32rpx;
+			background-color: #fff;
+		}
+
+		.tijiao {
+			position: fixed;
+			width: 100%;
+			bottom: 0;
+			left: 0;
+			background-color: #fff;
+			padding: 16rpx 32rpx;
+			box-sizing: border-box;
+			box-shadow: 0 -6px 12px 0 rgba(153, 153, 153, 0.10);
+		}
+
+		.u-cell__value {
+			color: #333 !important;
+		}
+
+	}
+	
+	.tabPage{
+		width: 100%;
+		padding-bottom: 172rpx;
+		box-sizing: border-box;
+	}
+	
+	.adf{
+	  display: flex;
+	}
+	.adfac{
+	  display: flex;
+	  align-items: center;
+	}
+	.adfacjc{
+	  display: flex;
+	  align-items: center;
+	  justify-content: center;
+	}
+	.adfacjb{
+	  display: flex;
+	  align-items: center;
+	  justify-content: space-between;
+	}
+	.adffcjb{
+	  display: flex;
+	  flex-direction: column;
+	justify-content: space-between;
+	}
+	.adffcacjc{
+	  display: flex;
+	  flex-direction: column;
+	  align-items: center;
+	  justify-content: center;
+	}
+	.adffcacjb{
+	  display: flex;
+	  flex-direction: column;
+	  align-items: center;
+	  justify-content: space-between;
+	}
+	.zt_btn{
+		width: 100%;
+		height: 88rpx;
+		background: #0066FE;
+		border-radius: 24rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 32rpx;
+		color: #FFFFFF;
+		line-height: 88rpx;
+		text-align: center;
+		letter-spacing: 2rpx;
+	}
+	.qx_btn{
+		width: 100%;
+		height: 88rpx;
+		background: #FAFAFA;
+		border-radius: 24rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 400;
+		font-size: 32rpx;
+		color: #393939;
+		line-height: 88rpx;
+		text-align: center;
+		letter-spacing: 2rpx;
+	}
+</style>

+ 33 - 0
androidPrivacy.json

@@ -0,0 +1,33 @@
+{
+    "version" : "2",
+    "prompt" : "template",
+    "title" : "用户协议和隐私政策",
+    "message" : "请你务必审慎阅读、充分理解“用户协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"./static/useragree.html\">《用户协议》</a>和<a href=\"./static/privacy.html\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
+    "buttonAccept" : "同意并接受",
+    "buttonRefuse" : "暂不同意",
+    "second" : {
+        "title" : "确认提示",
+        "message" : "  进入应用前,你需先同意<a href=\"./static/useragree.html\">《用户协议》</a>和<a href=\"./static/privacy.html\">《隐私政策》</a>,否则将退出应用。",
+        "buttonAccept" : "同意并继续",
+        "buttonRefuse" : "退出应用"
+    },
+    "styles" : {
+        "backgroundColor" : "#ffffff",
+        "borderRadius" : "5px",
+        "title" : {
+            "color" : "#000000",
+            "fontSize" : "32rpx"
+        },
+        "message" : {
+            "fontSize" : "26rpx"
+        },
+        "buttonAccept" : {
+            "color" : "#5cb26f",
+            "fontSize" : "28rpx"
+        },
+        "buttonRefuse" : {
+            "color" : "#606266",
+            "fontSize" : "28rpx"
+        }
+    }
+}

+ 89 - 0
components/CusHeader/index.vue

@@ -0,0 +1,89 @@
+<template>
+	<u-navbar :title="title" :bgColor="bgColor" :titleStyle="titleStyle">
+		<view class="u-nav-slot" slot="left" style="display: flex;background-color: transparent;">
+			<u-icon v-if="showback" name="arrow-left" size="44" :color="leftIconColor" @tap="toBack(backUrl)"></u-icon>
+		</view>
+	</u-navbar>
+</template>
+
+<script>
+	export default {
+		options: {
+			styleIsolation: 'shared'
+		},
+		props: {
+			title: {
+				typeof: String,
+				default: ''
+			},
+			showback: {
+				typeof: Boolean,
+				default: true
+			},
+			backUrl: {
+				typeof: String,
+				default: ''
+			},
+			bgColor: {
+				typeof: String,
+				default: '#ffffff'
+			},
+			leftIconColor: {
+				typeof: String,
+				default: '#111111'
+			},
+			titleStyle: {
+				typeof: Object,
+				default: {
+					fontSize: '36rpx',
+					fontWeight: "bold",
+					color: '#111111'
+				}
+			},
+		},
+		data() {
+			return {
+				tabUrls: [
+					'/pages/index/index',
+					'/pages/touristMap/index',
+					'/pages/oneCodePass/index',
+					'/pages/service/index',
+					'/pages/my/index'
+				]
+			}
+		},
+		methods: {
+			toBack(url) {
+				if (!url) {
+					if (uni.getStorageSync('options')) {
+						uni.redirectTo(JSON.parse(decodeURIComponent(uni.getStorageSync('options'))));
+						return uni.removeStorageSync('options');
+					}
+					let canNavBack = getCurrentPages();
+					if (canNavBack && canNavBack.length > 1) uni.navigateBack();
+					else uni.reLaunch({
+						url: '/pages/index/index'
+					})
+				} else {
+					if (this.tabUrls.find(u => u == url)) uni.reLaunch({
+						url
+					});
+					else uni.redirectTo({
+						url
+					});
+				}
+			},
+			toHome() {
+				uni.reLaunch({
+					url: '/pages/index/index'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.u-navbar--fixed {
+		z-index: 99999 !important;
+	}
+</style>

+ 96 - 0
components/CusTabbar/index.vue

@@ -0,0 +1,96 @@
+<template>
+	<view class="bottom_tabbar">
+		<view v-for="(item,index) in list" :key="index" @tap="changeTabbar(index)">
+			<image :src="tabbarIndex===index?item.activeImg:item.inactiveImg"></image>
+			<text :class="{'active':tabbarIndex===index}">{{item.text}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			tabbarIndex: 0
+		},
+		data() {
+			return {
+				tabbarValue: 0,
+				list: [
+					{
+						inactiveImg: require('@/static/tab_qa.png'),
+						activeImg: require('@/static/tab_qa_active.png'),
+						text: '问答',
+						path: '/pages/dialog'
+					},
+					{
+						inactiveImg: require('@/static/tab_wj.png'),
+						activeImg: require('@/static/tab_wj_active.png'),
+						text: '问卷',
+						path: '/pages/questionnaire'
+					},
+					{
+						inactiveImg: require('@/static/tab_my.png'),
+						activeImg: require('@/static/tab_my_active.png'),
+						text: '我的',
+						path: '/pages/my'
+					}
+				],
+				loginSuccessUrl:''
+			}
+		},
+		mounted() {
+			this.tabbarValue = this.tabbarIndex;
+		},
+		methods: {
+			changeTabbar(e) {
+				this.tabbarValue = e;
+				uni.reLaunch({
+					url: this.list[e].path
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.bottom_tabbar {
+		width: 100%;
+		height: 172rpx;
+		background: #FFFFFF;
+		display: flex;
+		position: fixed;
+		left: 0;
+		bottom: 0;
+		z-index: 999;
+		padding-top: 18rpx;
+		box-sizing: border-box;
+		box-shadow: 0rpx -2rpx 8rpx 0rpx rgba(0,0,0,0.06);
+
+		&>view {
+			width: calc(100% / 3);
+			height: 100%;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+
+			image {
+				width: 48rpx;
+				height: 48rpx;
+			}
+
+			text {
+				margin-top: 12rpx;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 22rpx;
+				color: #808080;
+				line-height: 22rpx;
+				text-align: center;
+				&.active{
+					font-weight: bold;
+					color: #761E6A;
+				}
+			}
+		}
+	}
+</style>

+ 37 - 0
components/pageEmpty/index.vue

@@ -0,0 +1,37 @@
+<template>
+	<div class="page-empty" :style="{'height':height,'margin-top':marginTop}">
+		<u-empty text="暂无数据" textSize="26rpx" width="332rpx" height="332rpx" mode="order" :icon="imgBase+'img_empty.jpg'"></u-empty>
+	</div>
+</template>
+
+<script>
+	export default {
+		props:{
+			height:{
+				typeof: String,
+				default: '100vh'
+			},
+			marginTop:{
+				typeof: String,
+				default: '0px'
+			}
+		},
+		data(){
+			return {
+				imgBase:this.$imgBase
+			}
+		},
+		methods:{
+			
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page-empty{
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+</style>

+ 93 - 0
http/Api.js

@@ -0,0 +1,93 @@
+import $api from '@/http/index.js'
+export default {
+
+    //账号密码登录
+    loginByPwd: (params) => $api.post("/login", params),
+
+    //用户信息
+    getUserInfo: (params) => $api.get("/user/userInfo", params),
+
+    //修改密码
+    updatePwd: (params) => $api.put("/user/password", params),
+
+    //数据字典
+    getDictList: (params) => $api.get("/all",params),
+
+    //首页信息
+    homeData: (params) => $api.get("/home/homedata", params),
+
+    // 获取空间树
+    getBuildInfo: (params) => $api.get("/control/getOrgStructureTree",params),
+
+    //上传图片
+    uploadFile: (params) => $api.post("/uploadFile", params, { header: { "Content-Type": "multipart/form-data" } }),
+
+    //空调设备控制分页
+    airconditioner: (params) => $api.get("/airconditioner/page", params),
+
+    //电表控制分页
+    getElec: (params) => $api.get("/relay/control/page", params),
+
+    //一键开闸关闸
+    elecControl: (params) => $api.post("/relay/control/command", params),
+
+    //空调下发指令
+    setControl: (params) => $api.post("/airconditioner/command", params),
+
+    //继电器下发指令
+    setControlElec: (params) => $api.post("/relay/control/command", params),
+
+    //工单维修--列表
+    repairList: (params) => $api.get("/repairorder/page", params),
+
+    //工单维修--详情
+    repairDetail: (id) => $api.get(`/repairorder/${id}`),
+
+    //工单维修--保存
+    repairSave: (params) => $api.post("/repairorder", params),
+
+    //工单维修--指派
+    repairDispatch: (params) => $api.put('/repairorder', params),
+
+    //工单维修--完成
+    repairFinish: (id) => $api.post(`/repairorder/finish/${id}`),
+
+    //工单维修--联系人列表
+    repairUserList: (params) => $api.get('/user/list',params),
+
+    // 设备异常--列表
+    deviceAlarmList: (params) => $api.get("/home/actualAlertList",params),
+
+    // 欠费待收--列表
+    lackFeeList: (params) => $api.get("/expeditrecordpage",params),
+
+    // 欠费待收--详情
+    lackFeeDetail: (params) => $api.get("/expeditrecord/recordpage",params),
+
+    // 欠费待收--催费
+    lackFeePay: (params) => $api.post("/expeditrecord/charge", params),
+
+    //扣缴记录
+    reduceRecordList: (params) => $api.get("/payrecord/page",params),
+
+    //租户账单
+    rentBillList: (params) => $api.get("/billinfo/paypage",params),
+
+    //租户账单明细--列表
+    rentBillDetail: (params) => $api.post("/billinfo/billDetail", params),
+
+    //获取缴费账户
+    getTenantAccount: (params) => $api.get(`/accountinfo/detail/${params}`),
+
+    //预存缴费
+    tenantPay: (params) => $api.post('/accountinfo/pay', params),
+
+    //立即支付
+    billPay: (params) => $api.post('/billinfo/billPay', params),
+
+    //巡检记录
+    reviewList: (params) => $api.get("/circuitrecord/page",params),
+
+    //巡检记录--打卡
+    reviewClock: (params) => $api.post("/circuitrecord", params),
+}

+ 6 - 0
http/baseApi.js

@@ -0,0 +1,6 @@
+// const BaseApi = 'https://glt.ringzle.com:8033/witcarbon-app/app' //线上
+const BaseApi = 'http://192.168.2.16:8003/xiaozhi-app/app' //严总
+
+export {
+	BaseApi
+}

+ 93 - 0
http/index.js

@@ -0,0 +1,93 @@
+import http from './interface'
+
+ 
+  
+export const $http = (url, method, data, json, isloading=true) => {
+	//设置请求前拦截器
+	http.interceptor.request = (config) => {
+		if(isloading){
+			uni.showLoading({
+				title:'加载中...'
+			})
+		}
+		
+		config.header = {
+			'content-type': json ? 'application/json' : 'application/x-www-form-urlencoded',
+			"token": uni.getStorageSync('token'),
+			"userId": uni.getStorageSync('userInfo')?JSON.parse(uni.getStorageSync('userInfo')).id:'',
+		}
+	}
+	//设置请求结束后拦截器
+	http.interceptor.response = async (response) => {
+		//判断返回状态 执行相应操作
+		if(isloading){
+			uni.hideLoading()
+		}
+		
+		if (response?.data?.code === 401 || response?.data?.msg.indexOf('未授权') > -1 || response?.data?.msg.indexOf('重新登录') > -1) {
+			return uni.showModal({
+				title: '温馨提示',
+				content:'当前登录已失效,是否返回重新登录',
+				success: (res) => {
+					if (res.confirm) {
+						uni.removeStorageSync('token');
+						uni.removeStorageSync('userInfo');
+						uni.reLaunch({
+							url: '/pages/login/wxLogin'
+						})
+					}
+				}
+			})
+		}
+		
+		// 请根据后端规定的状态码判定
+		if (response.data.code === 300) {//token失效
+		//	return response.data = await doRequest(response, url)//动态刷新token,并重新完成request请求
+		}else{
+			if(response.data.code==10021&&response.data.msg){
+				uni.showToast({
+					title:response.data.msg,
+					icon:'none',
+					duration:1500
+				})
+			}
+		}
+
+		return response;
+	}
+	return http.request({
+		method: method,
+		url: url,
+		dataType: 'json',
+		data,
+	})
+}
+
+function postJson(url, data, isloading=true) {
+	return $http(url, 'POST', data, isloading)
+}
+
+function get(url, data, isloading=true) {
+	
+	return $http(url, 'GET', data, true, isloading)
+}
+
+function post(url, data, isloading=true) {
+	return $http(url, 'POST', data, true, isloading)
+}
+
+function put(url, data, isloading=true) {
+	return $http(url, 'PUT', data, true, isloading)
+}
+
+function del(url, data, isloading=true) {
+	return $http(url, 'DELETE', data, true, isloading)
+}
+
+export default {
+	postJson,
+	get,
+	post,
+	put,
+	del
+}

+ 120 - 0
http/interface.js

@@ -0,0 +1,120 @@
+import {
+	BaseApi
+} from './baseApi.js'
+export default {
+	config: {
+		baseUrl: BaseApi,
+		header: {
+			'Content-Type':'application/json;charset=UTF-8',
+			'Content-Type':'application/x-www-form-urlencoded'
+		},  
+		data: {},
+		method: "GET",
+		dataType: "json",  /* 如设为json,会对返回的数据做一次 JSON.parse */
+		responseType: "text",
+		success() {},
+		fail() {},
+		complete() {}
+	},
+	interceptor: {
+		request: null,
+		response: null
+	},
+	request(options) {
+		if (!options) {
+			options = {}
+		}
+		options.baseUrl = options.baseUrl || this.config.baseUrl
+		options.dataType = options.dataType || this.config.dataType
+		options.url = options.baseUrl + options.url
+		options.data = options.data || {}
+		options.method = options.method || this.config.method
+		
+		return new Promise((resolve, reject) => {
+			let _config = null
+			
+			options.complete = (response) => {
+				let statusCode = response.statusCode
+				response.config = _config
+				if (process.env.NODE_ENV === 'development') {
+					if (statusCode === 200) {
+						////console.log("【" + _config.requestId + "】 结果:" + JSON.stringify(response.data))
+					}
+				}
+				if (this.interceptor.response) {
+					
+					let newResponse = this.interceptor.response(response)
+					if (newResponse) {
+						response = newResponse
+					}
+				}
+				// 统一的响应日志记录
+				//_reslog(response)
+				if (statusCode === 200) { //成功
+					resolve(response);
+				} else {
+					reject(response)
+				}
+			}
+
+			_config = Object.assign({}, this.config, options)
+			_config.requestId = new Date().getTime()
+
+			if (this.interceptor.request) {
+				this.interceptor.request(_config)
+			}
+			
+			// 统一的请求日志记录
+			//_reqlog(_config)
+
+			if (process.env.NODE_ENV === 'development') {
+				//console.log("【" + _config.requestId + "】 地址:" + _config.url)
+				if (_config.data) {
+					//console.log("【" + _config.requestId + "】 参数:" + JSON.stringify(_config.data))
+				}
+			}
+
+			uni.request(_config);
+		});
+	}	
+}
+
+
+/**
+ * 请求接口日志记录
+ */
+function _reqlog(req) {
+	if (process.env.NODE_ENV === 'development') {
+		//console.log("【" + req.requestId + "】 地址:" + req.url)
+		if (req.data) {
+			//console.log("【" + req.requestId + "】 请求参数:" + JSON.stringify(req.data))
+		}
+	}
+	//TODO 调接口异步写入日志数据库
+}
+
+/**
+ * 响应接口日志记录
+ */
+function _reslog(res) {
+	let _statusCode = res.statusCode;
+	if (process.env.NODE_ENV === 'development') {
+		//console.log("【" + res.config.requestId + "】 地址:" + res.config.url)
+		if (res.config.data) {
+			//console.log("【" + res.config.requestId + "】 请求参数:" + JSON.stringify(res.config.data))
+		}
+		//console.log("【" + res.config.requestId + "】 响应结果:" + JSON.stringify(res))
+	}
+	//TODO 除了接口服务错误外,其他日志调接口异步写入日志数据库
+	switch(_statusCode){
+		case 200:
+			break;
+		case 401:
+			break;
+		case 404:
+			break;
+		default:
+			break;
+	}
+}
+

+ 112 - 0
http/readme.md

@@ -0,0 +1,112 @@
+**插件使用说明**
+
+- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
+- 下载后把 http 文件夹 copy 到项目根目录下
+
+## 1. 配置 
+
+### 1.1 全局配置修改(修改http/baseApi.js中BaseApi和http/index.js中相关配置)
+	
+### 1.2 具体接口调用时修改(在http/index.js文件中具体业务接口中配置)
+``` javascript
+//设置token和content-type(区分json对象传输和formData传输)
+http.interceptor.request = (config) => {
+		config.header = {
+			'content-type': json ? 'application/json' : 'application/x-www-form-urlencoded',
+			"Authorization": uni.getStorageSync('token')
+		}
+	}
+//设置请求结束后拦截器
+	http.interceptor.response = async (response) => {
+		//判断返回状态 执行相应操作
+			if(response.data.code===401){//执行token过期的操作
+				return response.data = await doRequest(response,url)
+			}
+		return response;
+	}
+//配置刷新token的接口
+	var res=await postJson('/v1/miniprogram/oauth/wechat/refreshToken',{code:code})
+```	
+### 1.3 封装具体的业务请求(在http/index.js文件中具体业务接口中配置)
+``` javascript
+function postJson(url, data) {
+	return $http(url, 'POST', data)
+}
+function get(url, data) {
+	return $http(url, 'GET', data)
+}
+
+function post(url, data) {
+	return $http(url, 'POST', data, true)
+}
+
+function put(url, data) {
+	return $http(url, 'PUT', data, true)
+}
+
+function del(url,data){
+	return $http(url, 'DELETE', data, true)
+}
+``` 
+## 2. 使用
+
+### 2.1 全局使用(在main.js注册)
+
+``` //  main.js
+  import api from '@/http/'
+	
+	// 全局挂载后使用
+	Vue.prototype.$api = api
+```
+
+``` // pages/index/index.vue
+
+<template>
+	<view class="content">
+		测试api
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		onLoad(option) {
+			this.test()
+		},
+		methods: {
+			// 方式一
+			test(){
+							this.$api.get('/v1/miniprogram/device/'+'21040011515')
+							.then(res=>{
+								this.name = res.data.data.clazz_name
+							})
+						}
+			
+			//方式二
+			async test(){
+							let res = await this.$api.get('/v1/miniprogram/device/'+'21040011515')
+						}
+		}
+	}
+</script>
+```
+
+## 3. 接口数据加密、接口签名核验
+
+在http/interface.js文件中的request(Object)方法中补充修改相应的代码
+
+## 4. 接口请求/响应日志记录
+
+在http/interface.js文件中的request(Object)方法中补充修改相应的代码
+
+## 5. 业务相关接口编写
+
+在http/index.js文件中的编写具体业务相关的接口
+
+
+
+	

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 63 - 0
main.js

@@ -0,0 +1,63 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+Vue.config.productionTip = false
+App.mpType = 'app'
+
+import system from '@/utils/system.js'
+Vue.mixin(system);
+
+import CusHeader from '@/components/CusHeader'
+Vue.component('CusHeader',CusHeader);
+
+//日期格式处理
+import dateFormat from '@/utils/dateFormat.js'
+Vue.use(dateFormat);
+
+import regTest from './utils/reg.js'
+import api from '@/http/index.js'
+Vue.prototype.$api = api;
+Vue.prototype.$reg = regTest;
+
+//重载uni.showToast,简化调用
+Vue.prototype.$showToast = function(title, duration = 2000, icon = "none") {
+	return uni.showToast({
+		title,
+		duration,
+		icon
+	})
+}
+//重载uni.showModal,简化调用
+Vue.prototype.$showModal = function(content) {
+	return uni.showModal({
+		title:'温馨提示',
+		content,
+		showCancel:false,
+		confirmText: '确定',
+		confirmColor: '#007A69'
+	})
+}
+
+// 引入全局uView
+import uView from '@/uni_modules/uview-ui'
+Vue.use(uView)
+uni.$u.config.unit = 'rpx'
+const app = new Vue({
+	...App
+})
+
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	return {
+		app
+	}
+}
+// #endif

+ 139 - 0
manifest.json

@@ -0,0 +1,139 @@
+{
+    "name" : "传秀AI终端",
+    "appid" : "__UNI__A0C8B31",
+    "description" : "智慧生活",
+    "versionName" : "1.0.3",
+    "versionCode" : 102,
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {
+            "Barcode" : {},
+            "Camera" : {}
+        },
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
+                "autoSdkPermissions" : false,
+                "permissionExternalStorage" : {
+                    "request" : "none",
+                    "prompt" : "应用保存运行状态等信息,需要获取读写手机存储(系统提示为访问设备上的照片、媒体内容和文件)权限,请允许。"
+                },
+                "permissionPhoneState" : {
+                    "request" : "none",
+                    "prompt" : "为保证您正常、安全地使用,需要获取设备识别码(部分手机提示为获取手机号码)使用权限,请允许。"
+                }
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "dSYMs" : false,
+                "privacyDescription" : {
+                    "NSPhotoLibraryUsageDescription" : "报修文件上传",
+                    "NSPhotoLibraryAddUsageDescription" : "拍照需要报修的地方",
+                    "NSCameraUsageDescription" : "扫码二维码"
+                },
+                "idfa" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {},
+                "payment" : {
+                    "weixin" : {
+                        "__platform__" : [ "ios", "android" ],
+                        "appid" : "",
+                        "UniversalLinks" : ""
+                    }
+                }
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            },
+            "splashscreen" : {
+                "useOriginalMsgbox" : true
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wxb9a6bbfcc8449c16",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : false,
+            "minified" : true,
+            "bigPackageSizeSupport" : true
+        },
+        "usingComponents" : true,
+        "requiredPrivateInfos" : [ "getLocation" ],
+        "optimization" : {
+            "subPackages" : true
+        },
+        "permission" : {
+            "scope.wifi" : {
+                "desc" : "获取WiFi列表用于设备联网"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2",
+    "fallbackLocale" : "zh-Hans"
+}
+/* ios打包配置 *//* SDK配置 */
+

+ 13 - 0
node_modules/.package-lock.json

@@ -0,0 +1,13 @@
+{
+  "name": "ChuangHengWx",
+  "version": "1.0.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "node_modules/event-source-polyfill": {
+      "version": "1.0.31",
+      "resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
+      "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
+    }
+  }
+}

+ 21 - 0
node_modules/event-source-polyfill/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012 vic99999@yandex.ru
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 21 - 0
node_modules/event-source-polyfill/LICENSE.md

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Viktor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 277 - 0
node_modules/event-source-polyfill/README.md

@@ -0,0 +1,277 @@
+EventSource polyfill - https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events
+========================================================
+
+Пожалуйста не используйте эту обосранную библиотеку!
+
+Installing:
+-----------
+
+You can get the code from npm or bower:
+
+```
+npm install event-source-polyfill
+```
+
+```
+bower install event-source-polyfill
+```
+
+Just include `src/eventsource.js` or `src/eventsource.min.js` in your page to use the polyfill.
+
+
+Ionic2/Angular2 Installation:
+-----------------------------
+
+Unless a typescript definition file is created for this polyfill, this is how you would use it in an Ionic2 project.  It should (in theory) be very similar in an Angular2 project.
+
+```
+npm install event-source-polyfill
+```
+
+Add to (or create) src/app/polyfills.ts (path is relative to where polyfills.ts is) :
+```
+import 'path/to/event-source-polyfill/src/eventsource.min.js'
+```
+
+Add anywhere you need access to EventSourcePolyfill class : 
+
+```
+declare var EventSourcePolyfill: any;
+```
+
+Usage with webpack/browserify:
+------------------------------
+
+```javascript
+import { NativeEventSource, EventSourcePolyfill } from 'event-source-polyfill';
+
+const EventSource = NativeEventSource || EventSourcePolyfill;
+// OR: may also need to set as global property
+global.EventSource =  NativeEventSource || EventSourcePolyfill;
+```
+
+Browser support:
+----------------
+
+* IE 10+, Firefox 3.5+, Chrome 3+, Safari 4+, Opera 12+
+* IE 8 - IE 9: XDomainRequest is used internally, which has some limitations (2KB padding in the beginning is required, no way to send cookies, no way to use client certificates)
+* It works on Mobile Safari, Opera Mobile, Chrome for Android, Firefox for Android
+* It does not work on: Android Browser(requires 4 KB padding after every chunk), Opera Mini
+
+Advantages:
+-----------
+
+* Simple server-side code
+* Cross-domain requests support
+
+Server-side requirements:
+-------------------------
+
+* "Last-Event-ID" is sent in a query string (CORS + "Last-Event-ID" header is not supported by all browsers)
+* It is required to send 2 KB padding for IE < 10 and Chrome < 13 at the top of the response stream (the polyfill sends `padding=true` query argument)
+* You need to send "comment" messages each 15-30 seconds, these messages will be used as heartbeat to detect disconnects - see https://bugzilla.mozilla.org/show_bug.cgi?id=444328
+
+Specification:
+--------------
+
+* http://www.w3.org/TR/eventsource/
+
+Build:
+------
+
+* To build EventSource, just install npm modules (`npm install`) and then run the build (`npm run build`). It should generate a new version of `src/eventsource.min.js`.
+
+Notes:
+-----
+ * If you are using HTTP Basic Authentication, you can embed credentials into the URL - `http://username:password@github.com`.
+
+Custom Headers:
+---------------
+```
+var es = new EventSourcePolyfill('/events', {
+  headers: {
+    'X-Custom-Header': 'value'
+  }
+});
+```
+
+Custom query parameter name for the last event id:
+---------------
+ * Some server require a special query parameter name for last-event-id, you can change that via option
+ * The default is `lastEventId`
+ * Example for mercure-hub (https://mercure.rocks/)
+  
+```
+var es = new EventSourcePolyfill(hubUrl, {
+  lastEventIdQueryParameterName: 'Last-Event-Id'
+});
+```
+
+Other EventSource polyfills:
+----------------------------
+
+* https://github.com/remy/polyfills/blob/master/EventSource.js by Remy Sharp
+* https://github.com/rwldrn/jquery.eventsource by Rick Waldron
+* https://github.com/amvtek/EventSource by AmvTek
+
+EXAMPLE
+-------
+
+
+
+server-side (node.js)
+---------------------
+
+```javascript
+var PORT = 8081;
+
+var http = require("http");
+var fs = require("fs");
+var url = require("url");
+
+http.createServer(function (request, response) {
+  var parsedURL = url.parse(request.url, true);
+  var pathname = parsedURL.pathname;
+  if (pathname === "/events.php") {
+
+    response.writeHead(200, {
+      "Content-Type": "text/event-stream",
+      "Cache-Control": "no-store",
+      "Access-Control-Allow-Origin": "*"
+    });
+
+    var padding = new Array(2049);
+    response.write(":" + padding.join(" ") + "\n"); // 2kB padding for IE
+    response.write("retry: 2000\n");
+
+    var lastEventId = Number(request.headers["last-event-id"]) || Number(parsedURL.query.lastEventId) || 0;
+
+    var timeoutId = 0;
+    var i = lastEventId;
+    var c = i + 100;
+    var f = function () {
+      if (++i < c) {
+        response.write("id: " + i + "\n");
+        response.write("data: " + i + "\n\n");
+        timeoutId = setTimeout(f, 1000);
+      } else {
+        response.end();
+      }
+    };
+
+    f();
+
+    response.on("close", function () {
+      clearTimeout(timeoutId);
+    });
+
+  } else {
+    if (pathname === "/") {
+      pathname = "/index.html";
+    }
+    if (pathname === "/index.html" || pathname === "../src/eventsource.js") {
+      response.writeHead(200, {
+        "Content-Type": pathname === "/index.html" ? "text/html" : "text/javascript"
+      });
+      response.write(fs.readFileSync(__dirname + pathname));
+    }
+    response.end();
+  }
+}).listen(PORT);
+```
+
+or use PHP (see php/events.php)
+-------------------------------
+```php
+<?php
+
+  header("Content-Type: text/event-stream");
+  header("Cache-Control: no-store");
+  header("Access-Control-Allow-Origin: *");
+
+  $lastEventId = floatval(isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? $_SERVER["HTTP_LAST_EVENT_ID"] : 0);
+  if ($lastEventId == 0) {
+    $lastEventId = floatval(isset($_GET["lastEventId"]) ? $_GET["lastEventId"] : 0);
+  }
+
+  echo ":" . str_repeat(" ", 2048) . "\n"; // 2 kB padding for IE
+  echo "retry: 2000\n";
+
+  // event-stream
+  $i = $lastEventId;
+  $c = $i + 100;
+  while (++$i < $c) {
+    echo "id: " . $i . "\n";
+    echo "data: " . $i . ";\n\n";
+    ob_flush();
+    flush();
+    sleep(1);
+  }
+
+?>
+```
+
+index.html (php/index.html):
+----------------------------
+```html
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8" />
+    <title>EventSource example</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <script src="../src/eventsource.js"></script>
+    <script>
+      var es = new EventSource("events.php");
+      var listener = function (event) {
+        var div = document.createElement("div");
+        var type = event.type;
+        div.appendChild(document.createTextNode(type + ": " + (type === "message" ? event.data : es.url)));
+        document.body.appendChild(div);
+      };
+      es.addEventListener("open", listener);
+      es.addEventListener("message", listener);
+      es.addEventListener("error", listener);
+    </script>
+</head>
+<body>
+</body>
+</html>
+```
+
+Usage in node.js:
+=================
+With some dynamic imports it may work in node.js:
+
+Install the library and the dependency:
+`npm install @titelmedia/node-fetch`
+`npm install event-source-polyfill`
+
+x.js:
+```javascript
+// The @titelmedia/node-fetch is used instead of node-fetch as it supports ReadableStream Web API
+import('@titelmedia/node-fetch').then(function (fetch) {
+  globalThis.fetch = fetch.default;
+  globalThis.Response = fetch.default.Response;
+  import('event-source-polyfill').then(function (x) {
+    var es = new x.default.EventSourcePolyfill('http://localhost:8004/events');
+    es.onerror = es.onopen = es.onmessage = function (event) {
+      console.log(event.type + ': ' + event.data);
+    };
+  });
+});
+```
+
+`node --experimental-modules ./x.js`
+
+License
+-------
+The MIT License (MIT)
+
+Copyright (c) 2012 vic99999@yandex.ru
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 34 - 0
node_modules/event-source-polyfill/package.json

@@ -0,0 +1,34 @@
+{
+  "name": "event-source-polyfill",
+  "version": "1.0.31",
+  "description": "A polyfill for http://www.w3.org/TR/eventsource/ ",
+  "main": "src/eventsource.js",
+  "scripts": {
+    "build": "grunt"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/Yaffle/EventSource.git"
+  },
+  "keywords": [
+    "sse",
+    "server sent events",
+    "eventsource",
+    "event-source",
+    "polyfill"
+  ],
+  "files": [
+    "src/*"
+  ],
+  "author": "Yaffle (https://github.com/Yaffle)",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/Yaffle/EventSource/issues"
+  },
+  "homepage": "https://github.com/Yaffle/EventSource",
+  "devDependencies": {
+    "grunt": "^1.4.1",
+    "grunt-cli": "^1.4.3",
+    "grunt-contrib-uglify": "^5.0.1"
+  }
+}

Файловите разлики са ограничени, защото са твърде много
+ 1048 - 0
node_modules/event-source-polyfill/src/eventsource.js


Файловите разлики са ограничени, защото са твърде много
+ 6 - 0
node_modules/event-source-polyfill/src/eventsource.min.js


+ 25 - 0
package-lock.json

@@ -0,0 +1,25 @@
+{
+  "name": "ChuangHengWx",
+  "version": "1.0.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "event-source-polyfill": "^1.0.31"
+      }
+    },
+    "node_modules/event-source-polyfill": {
+      "version": "1.0.31",
+      "resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
+      "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
+    }
+  },
+  "dependencies": {
+    "event-source-polyfill": {
+      "version": "1.0.31",
+      "resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz",
+      "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA=="
+    }
+  }
+}

+ 5 - 0
package.json

@@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "event-source-polyfill": "^1.0.31"
+  }
+}

+ 66 - 0
pages.json

@@ -0,0 +1,66 @@
+{
+	"pages": [
+		{
+			"path": "pages/login",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/home",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/wifi",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/wifiSet",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/code",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/agentCreate",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/agentDetail",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/roleSet",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/device",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTitleText": "传秀AI终端",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"navigationBarTextStyle": "black"
+	},
+	"easycom": {
+		"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
+	}
+}

+ 184 - 0
pages/agentCreate.vue

@@ -0,0 +1,184 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='创建智能体' :showback="false" bgColor="transparent" backUrl="/pages/home"></cus-header>
+		<div class="title">角色昵称</div>
+		<div class="inp">
+			<input v-model="agentDto.agentName" type="text" placeholder="请输入昵称">
+		</div>
+		<div class="title">角色模板</div>
+		<div class="list">
+			<div class="pre" v-for="(item,index) in list" :key="index" :class="{'active':midx===index}" @tap="changeModel(item,index)">
+				<image src="@/static/selected.png" v-if="midx===index"></image>
+				{{item.agentName}}
+			</div>
+		</div>
+		<div class="title">角色介绍</div>
+		<div class="intro">
+			<u-parse :content="agentDto.systemPrompt"></u-parse>
+		</div>
+		<div class="title">角色音色</div>
+		<div class="inp" @tap="show=true">
+			<input type="text" placeholder="请选择音色" disabled v-if="!agentDto.ttsVoiceId">
+			<text v-else>{{voiceText}}</text>
+		</div>
+		<div class="zt_btn" @tap="comfirmSure">确定</div>
+		<u-picker :itemHeight="88" title="角色音色" :show="show" :columns="voiceList" keyName="name"
+			@cancel="show=false" @confirm="confirm" :immediateChange="true" style="height: 500rpx;">
+		</u-picker>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				list:[],
+				agentDto:{
+					"agentName": "",
+					"asrModelId": "",
+					"vadModelId": "",
+					"llmModelId": "",
+					"ttsModelId": "",
+					"ttsVoiceId": "",
+					"memModelId": "",
+					"intentModelId": "",
+					"systemPrompt": "",
+					"langCode": "",
+					"language": "",
+					"deviceId": ""
+				},
+				modelMap:new Map(),
+				midx:0,
+				voiceText:'',
+				voiceList:[],
+				show:false
+			}
+		},
+		async onLoad() {
+			await this.getAgentModelList()
+			this.getModelVoiceList()
+		},
+		methods:{
+			changeModel(item,index){
+				this.midx = index;
+				this.agentDto = this.modelMap.get(item.agentName);
+				this.getModelVoiceList()
+			},
+			getAgentModelList(){
+				return new Promise((resolve,reject)=>{
+					this.$api.get('/agent/template').then(res=>{
+						if(res.data.code!==0) return this.$showToast(res.data.msg)
+						this.list = res.data.data;
+						if(this.list.length){
+							this.agentDto = {...this.agentDto,...this.list[0]}
+							let map = new Map();
+							this.list.forEach(l=>{
+								map.set(l.agentName,l)
+							})
+							this.modelMap = map;
+						}
+						resolve();
+					})
+				})
+			},
+			getModelVoiceList(){
+				this.$api.get(`/models/${this.agentDto.ttsModelId}/voices`).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.voiceList = [res.data.data];
+					this.voiceText = res.data.data.find(v=>v.id===this.agentDto.ttsVoiceId)?.name||'';
+				})
+			},
+			comfirmSure(){
+				let dto = JSON.parse(JSON.stringify(this.agentDto));
+				this.$api.post('/agent',dto).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.$showToast('创建成功');
+					setTimeout(()=>{
+						uni.reLaunch({
+							url:'/pages/home'
+						})
+					},1500)
+				})
+			},
+			confirm(e){
+				this.show = false;
+				this.agentDto.ttsVoiceId = e.value[0].id;
+				this.voiceText = e.value[0].name;
+			},
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: linear-gradient( 180deg, #E0EEFF 0%, #F6FCFF 100%);
+		padding: 0 30rpx 30rpx;
+		box-sizing: border-box;
+		
+		.title{
+			font-family: PingFang-SC, PingFang-SC;
+			font-weight: bold;
+			font-size: 32rpx;
+			color: #111111;
+			line-height: 32rpx;
+			margin-top: 48rpx;
+		}
+		.inp{
+			margin-top: 40rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 30rpx 24rpx;
+			input{
+				border: none;
+			}
+		}
+		.list{
+			margin-top: 20rpx;
+			display: flex;
+			flex-wrap: wrap;
+			margin-left: -10rpx;
+			.pre{
+				margin-top: 20rpx;
+				margin-left: 10rpx;
+				width: calc(100% / 3 - 10rpx);
+				height: 80rpx;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #333333;
+				line-height: 80rpx;
+				text-align: center;
+				position: relative;
+				&.active{
+					background: #0066FE;
+					font-weight: bold;
+					color: #FFFFFF;
+				}
+				image{
+					width: 48rpx;
+					height: 48rpx;
+					position: absolute;
+					right: 0;
+					bottom: 0;
+					z-index: 22;
+				}
+			}
+		}
+		.intro{
+			margin-top: 40rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 21rpx 24rpx 100rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #616161;
+			line-height: 56rpx;
+		}
+		.zt_btn{
+			margin-top: 96rpx;
+		}
+	}
+</style>

+ 173 - 0
pages/agentDetail.vue

@@ -0,0 +1,173 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='智能体详情' bgColor="transparent"></cus-header>
+		<!-- <div class="top adffcacjc">
+			<image src="@/static/avatar_big.png"></image>
+			<div class="change adfacjb">
+				<image src="@/static/change2.png"></image>
+				<span>更换形象</span>
+			</div>
+		</div> -->
+		<div class="box b1 adfac">
+			<text>角色昵称</text>
+			<span>{{info.agentName||''}}</span>
+		</div>
+		<div class="box b2">
+			<text>角色介绍</text>
+			<p>{{info.systemPrompt||''}}</p>
+		</div>
+		<div class="box b1 adfac">
+			<text>角色音色</text>
+			<span>{{vname||''}}</span>
+		</div>
+		<div class="item adfacjb" @tap="toTurn('/pages/roleSet?id='+info.id+'&agentId='+id)">
+			<div class="i_l adfac">
+				<image src="@/static/user_set.png"></image>
+				<span>配置角色</span>
+			</div>
+			<div class="i_r adfac">
+				<span>编辑</span>
+				<u-icon name="arrow-right" color="#D3D2D2" size="28rpx"></u-icon>
+			</div>
+		</div>
+		<div class="item adfacjb" @tap="toTurn('/pages/device?agentId='+id)">
+			<div class="i_l adfac">
+				<image src="@/static/device_gl.png"></image>
+				<span>设备管理</span>
+			</div>
+			<div class="i_r adfac">
+				<span>编辑</span>
+				<u-icon name="arrow-right" color="#D3D2D2" size="28rpx"></u-icon>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				id:'',
+				vname:'',
+				info:null
+			}
+		},
+		onLoad(option) {
+			this.id = option.id;
+			this.vname = option.vname;
+			this.getDetail();
+		},
+		methods:{
+			getDetail(){
+				this.$api.get(`/agent/${this.id}`).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.info = res.data.data;
+				})
+			},
+			toTurn(url){
+				uni.navigateTo({
+					url
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: linear-gradient( 180deg, #E0EEFF 0%, #F6FCFF 100%);
+		padding: 0 31rpx 40rpx;
+		box-sizing: border-box;
+		
+		.top{
+			&>image{
+				margin-top: 20rpx;
+				width: 200rpx;
+				height: 233rpx;
+			}
+			.change{
+				margin-top: 40rpx;
+				width: 232rpx;
+				height: 72rpx;
+				background: #FFFFFF;
+				border-radius: 36rpx;
+				padding: 18rpx 36rpx;
+				box-sizing: border-box;
+				image{
+					width: 28rpx;
+					height: 25rpx;
+				}
+				span{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #0066FE;
+					line-height: 32rpx;
+				}
+			}
+		}
+		
+		.box{
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 35rpx 34rpx;
+			margin-top: 20rpx;
+			&.b1{
+				margin-top: 40rpx;
+			}
+			text{
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 30rpx;
+				color: #111111;
+				line-height: 32rpx;
+			}
+			span{
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #333333;
+				line-height: 42rpx;
+				margin-left: 52rpx;
+			}
+			p{
+				margin-top: 30rpx;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #616161;
+				line-height: 56rpx;
+			}
+		}
+		.item{
+			margin-top: 20rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 18rpx 34rpx;
+			.i_l{
+				image{
+					width: 64rpx;
+					height: 64rpx;
+				}
+				span{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 30rpx;
+					color: #111111;
+					line-height: 32rpx;
+					margin-left: 30rpx;
+				}
+			}
+			.i_r{
+				span{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 26rpx;
+					color: #0066FE;
+					line-height: 32rpx;
+					margin-right: 14rpx;
+				}
+			}
+		}
+	}
+</style>

+ 72 - 0
pages/code.vue

@@ -0,0 +1,72 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='验证码' bgColor="transparent"></cus-header>
+		<div class="box adffcacjc" :style="{'height':h-mt+'px'}">
+			<p>请输入6为验证码</p>
+			<text>验证码已发送至设备</text>
+			<div class="inps adfacjb">
+				<div class="pre adfacjc" v-for="(item,index) in 6" :key="index">
+					<input type="text" @blur="toTurn">
+				</div>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				
+			}
+		},
+		methods:{
+			toTurn(){
+				uni.navigateTo({
+					url:'/pages/agentCreate'
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F7F7F7;
+		box-sizing: border-box;
+		.box{
+			width: 100%;
+			height: 100vh;
+			p{
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 40rpx;
+				color: #111111;
+				line-height: 56rpx;
+			}
+			text{
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: rgba(17,17,17,0.4);
+				line-height: 28rpx;
+				margin-top: 24rpx;
+			}
+			.inps{
+				width: calc(100% - 100rpx);
+				margin: 80rpx 50rpx 0;
+				.pre{
+					width: calc(100% / 6 - 10rpx);
+					height: 110rpx;
+					background: #FFFFFF;
+					border-radius: 16rpx;
+					padding: 10rpx 20rpx;
+					box-sizing: border-box;
+					input{
+						border: none;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 115 - 0
pages/device.vue

@@ -0,0 +1,115 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='设备管理' bgColor="transparent"></cus-header>
+		<block v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index">
+				<div class="name">{{item.board}}</div>
+				<div class="btn" @tap="toUnBind(item)">解绑设备</div>
+			</div>
+		</block>
+		<block v-else>
+			<page-empty :height="'calc(100vh - 200px)'"></page-empty>
+		</block>
+		<div class="zt_btn" @tap="bindDevice">新增设备</div>
+	</view>
+</template>
+
+<script>
+	import pageEmpty from '@/components/pageEmpty/index.vue'
+	export default {
+		components:{pageEmpty},
+		data(){
+			return {
+				agentId:'',
+				list:[]
+			}
+		},
+		onLoad(option) {
+			this.agentId = option.agentId;
+			this.getList();
+		},
+		methods:{
+			getList(){
+				this.$api.get(`/device/bind/${this.agentId}`).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.list = res.data.data;
+				})
+			},
+			bindDevice(){
+				uni.scanCode({
+					success: (res) => {
+						let result = JSON.parse(res.result);
+						if(res.errMsg==='scanCode:ok'){
+							this.$api.post('/device/bindAdd',{
+								...result,
+								"type": "",
+								"userId": 0,
+								"agentId": this.agentId
+							}).then(res=>{
+								if(res.data.code!==0) return this.$showToast(res.data.msg)
+								this.$showToast('新增设备成功');
+								setTimeout(()=>{
+									this.getList();
+								},1000)
+							})
+						}
+					}
+				})
+			},
+			toUnBind(item){
+				uni.showModal({
+					title:'温馨提示',
+					content:'确定要解绑该设备吗?',
+					success: (res) => {
+						this.$api.post('/device/unbind',{deviceId:item.id}).then(res=>{
+							if(res.data.code!==0) return this.$showToast(res.data.msg)
+							this.$showToast('解绑成功');
+							setTimeout(()=>{
+								this.getList();
+							},1000)
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: linear-gradient( 180deg, #E0EEFF 0%, #F6FCFF 100%);
+		padding: 0 30rpx 200rpx;
+		box-sizing: border-box;
+		
+		.box{
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			margin-top: 20rpx;
+			.name{
+				padding: 36rpx 24rpx;
+				border-bottom: 1px solid #E2E2E2;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #111111;
+				line-height: 32rpx;
+			}
+			.btn{
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 28rpx;
+				color: #0066FE;
+				line-height: 32rpx;
+				text-align: center;
+				padding: 34rpx 0;
+			}
+		}
+		
+		.zt_btn{
+			position: fixed;
+			bottom: 64rpx;
+			width: calc(100% - 120rpx);
+			left: 60rpx;
+		}
+	}
+</style>

+ 272 - 0
pages/home.vue

@@ -0,0 +1,272 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<div class="top" :style="{'background-image':'url('+require('../static/top_bg.png')+')','margin-top':-mt+'px'}">
+			<div class="t_box" :style="{'background-image':'url('+require('../static/top_boxbg.png')+')'}">
+				<image src="@/static/ai_shape.png"></image>
+				<div class="text adffcac">
+					<text>Hi!我是甜心小兔</text>
+					<p>
+						支持语音唤醒 按键对话<br>
+						回应每一个奇思妙想<br>
+						好奇的孩子更聪明~
+					</p>
+				</div>
+			</div>
+		</div>
+		<div class="card">
+			<div class="title adfacjb">
+				<div class="t_l">我的设备</div>
+			</div>
+			<div class="device_boxs adfacjb">
+				<div class="db_pre adffcjb" :style="{'background-image':'url('+require('../static/box_bg2.png')+')'}" @tap="addBindDevice">
+					<div class="dp_top adfac">
+						<image src="@/static/bind.png"></image>
+						<span>绑定设备</span>
+					</div>
+					<div class="dp_tip">按照产品教程绑定设备,添加成功后选择角色</div>
+				</div>
+				<div class="db_pre adffcjb" :style="{'background-image':'url('+require('../static/box_bg1.png')+')'}" @tap="toTurn('/pages/wifi')">
+					<div class="dp_top adfac">
+						<image src="@/static/change.png"></image>
+						<span>网络连接</span>
+					</div>
+					<div class="dp_tip">请先连接热点,然后选择要连接的网络</div>
+				</div>
+			</div>
+			<template v-if="agentList.length">
+				<div class="title adfacjb" style="margin-top: 70rpx;margin-bottom: 22rpx;">
+					<div class="t_l">我的角色</div>
+					<div class="t_r">查看全部</div>
+				</div>
+				<div class="a_item adfacjb" v-for="(item,index) in agentList" :key="index">
+					<div class="ai_l">
+						{{item.agentName}}
+					</div>
+					<div class="ai_r" @tap="toAgentDetail(item)">查看</div>
+				</div>
+			</template>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				deviceList:[],
+				agentList:[]
+			}
+		},
+		onLoad() {
+			this.getAgentList();
+		},
+		methods:{
+			getAgentList(){
+				this.$api.get('/agent/list').then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.agentList = res.data.data.splice(0,5);
+				})
+			},
+			addBindDevice(){
+				uni.scanCode({
+					success: (res) => {
+						let result = JSON.parse(res.result);
+						if(res.errMsg==='scanCode:ok'){
+							this.$api.post('/device/bindAdd',{
+								...result,
+								"type": "",
+								"userId": 0,
+								"agentId": ""
+							}).then(res=>{
+								if(res.data.code!==0) return this.$showToast(res.data.msg)
+								uni.navigateTo({
+									url:'/pages/agentCreate'
+								})
+							})
+						}
+					}
+				})
+			},
+			toTurn(url){
+				uni.navigateTo({ url })
+			},
+			toAgentDetail(item){
+				uni.navigateTo({
+					url:'/pages/agentDetail?id='+item.id+'&vname='+item.ttsVoiceName
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: linear-gradient( 180deg, #E0EEFF 0%, #F6FCFF 100%);
+		box-sizing: border-box;
+		.top{
+			width: 100%;
+			height: 564rpx;
+			background-repeat: no-repeat;
+			background-size: 100% 100%;
+			position: relative;
+			display: flex;
+			flex-direction: column;
+			justify-content: flex-end;
+			.t_box{
+				width: calc(100% - 60rpx);
+				height: 296rpx;
+				background-repeat: no-repeat;
+				background-size: 100% 100%;
+				position: relative;
+				bottom: 30rpx;
+				left: 30rpx;    
+				image{
+					width: 256rpx;
+					height: 336rpx;
+					position: absolute;
+					left: 38rpx;
+					bottom: 14rpx;
+				}
+				.text{
+					padding: 66rpx 46rpx 0 332rpx;
+					text{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 500;
+						font-size: 40rpx;
+						color: #FFFFFF;
+						line-height: 56rpx;
+					}
+					p{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 26rpx;
+						color: #FFFFFF;
+						line-height: 48rpx;
+						margin-top: 12rpx;
+					}
+				}
+				
+			}
+		}
+		.card{
+			padding: 0 30rpx 30rpx;
+			.title{
+				margin-top: 36rpx;
+				.t_l{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 600;
+					font-size: 36rpx;
+					color: #111111;
+					line-height: 50rpx;
+					text-align: left;
+				}
+				.t_r{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #0066FE;
+					line-height: 40rpx;
+					text-align: right;
+				}
+			}
+			.device_empty{
+				background: #FFFFFF;
+				box-shadow: 0rpx 4rpx 20rpx 0rpx #E5EDFF;
+				border-radius: 24rpx;
+				margin-top: 32rpx;
+				padding: 48rpx 79rpx;
+				background: #FFFFFF;
+				p{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #5C5C5C;
+					line-height: 48rpx;
+					text-align: center;
+				}
+				.de_btn{
+					width: 320rpx;
+					height: 72rpx;
+					background: #0066FE;
+					border-radius: 16rpx;
+					margin-top: 40rpx;
+					image{
+						width: 30rpx;
+						height: 30rpx;
+					}
+					span{
+						font-family: PingFang-SC, PingFang-SC;
+						font-weight: bold;
+						font-size: 28rpx;
+						color: #FFFFFF;
+						line-height: 32rpx;
+						margin-left: 16rpx;
+					}
+				}
+			}
+			.device_boxs{
+				margin-top: 31rpx;
+				.db_pre{
+					width: calc(50% - 15rpx);
+					height: 138px;
+					padding: 36rpx 30rpx;
+					box-sizing: border-box;
+					background-repeat: no-repeat;
+					background-size: 100% 100%;
+					.dp_top{
+						image{
+							width: 40rpx;
+							height: 40rpx;
+						}
+						span{
+							font-family: PingFang-SC, PingFang-SC;
+							font-weight: bold;
+							font-size: 32rpx;
+							color: #333333;
+							line-height: 32rpx;
+							text-align: left;
+							margin-left: 40rpx;
+						}
+					}
+					.dp_tip{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #6E696F;
+						line-height: 40rpx;
+					}
+				}
+			}
+			.a_item{
+				width: 100%;
+				padding: 37rpx 20rpx;
+				box-sizing: border-box;
+				background: #FFFFFF;
+				margin-top: 30rpx;
+				.ai_l{
+					width: calc(100% - 114rpx);
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					color: #111111;
+					line-height: 32rpx;
+					overflow: hidden;
+					white-space: nowrap;
+					text-overflow: ellipsis;
+				}
+				.ai_r{
+					width: 94rpx;
+					height: 56rpx;
+					background: #0066FE;
+					border-radius: 16rpx;
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 24rpx;
+					color: #FFFFFF;
+					line-height: 56rpx;
+					text-align: center;
+				}
+			}
+		}
+	}
+</style>

+ 81 - 0
pages/login.vue

@@ -0,0 +1,81 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='AI玩具' :showback="false"></cus-header>
+		<div class="content">
+			<button class="zt_btn" open-type="getPhoneNumber" @getphonenumber="decryptPhoneNumber">手机号一键登录</button>
+			<div class="qx_btn" @click="cancelLogin">取消</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				
+			}
+		},
+		onShow() {
+			if(uni.getStorageSync('userInfo')){
+				uni.redirectTo({
+					url:'/pages/home'
+				})
+			}
+		},
+		methods:{
+			decryptPhoneNumber(e){
+				if(e.detail.code) this.wxLogin(e.detail.code);
+			},
+			wxLogin(code){
+				uni.showLoading({ title:'登录中' });
+				let that = this;
+				wx.login({
+					success(res){
+						that.$api.get('/sys/wx/login',{
+							code:res.code,
+							phoneCode:code
+						},false).then(res=>{
+							if(res.data.code===0){
+								uni.setStorageSync('token',res.data.data.token);
+								uni.setStorageSync('userInfo',JSON.stringify({
+									id:res.data.data.id,
+									mobile:res.data.data.mobile,
+									username:res.data.data.username
+								}));
+								
+								uni.hideLoading();
+								that.$showToast('登录成功');
+								setTimeout(()=>{
+									uni.reLaunch({ url:'/pages/home' })
+								},1500)
+							}else that.$showToast(res.data.msg)
+						})
+					}
+				})
+			},
+			cancelLogin(){
+				uni.exitMiniProgram();
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.page{
+		background: #FFFFFF;
+		display: flex;
+		flex-direction: column;
+		box-sizing: border-box;
+		.content{
+			padding: 0 45rpx;
+			flex: 1;
+			overflow-y: auto;
+			.zt_btn{
+				margin-top: 216rpx;
+			}
+			.qx_btn{
+				margin-top: 36rpx;
+			}
+		}
+	}
+</style>

+ 189 - 0
pages/roleSet.vue

@@ -0,0 +1,189 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='配置角色' bgColor="transparent"></cus-header>
+		<div class="title">助手昵称</div>
+		<div class="inp">
+			<input v-model="agentDto.agentName" type="text" placeholder="请输入昵称">
+		</div>
+		<div class="title">角色模板</div>
+		<div class="list">
+			<div class="pre" v-for="(item,index) in list" :key="index" :class="{'active':midx===index}" @tap="changeModel(item,index)">
+				<image src="@/static/selected.png" v-if="midx===index"></image>
+				{{item.agentName}}
+			</div>
+		</div>
+		<div class="title">角色介绍</div>
+		<div class="intro">
+			<u-parse :content="agentDto.systemPrompt"></u-parse>
+		</div>
+		<div class="title">角色音色</div>
+		<div class="inp" @tap="show=true">
+			<input type="text" placeholder="请选择音色" disabled v-if="!agentDto.ttsVoiceId">
+			<text v-else>{{voiceText}}</text>
+		</div>
+		<div class="zt_btn" @tap="comfirmSure">确定</div>
+		<u-picker :itemHeight="88" title="角色音色" :show="show" :columns="voiceList" keyName="name"
+			@cancel="show=false" @confirm="confirm" :immediateChange="true" style="height: 500rpx;">
+		</u-picker>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				id:'',
+				agentId:'',
+				agentDto:null,
+				list:[],
+				midx:0,
+				modelMap:new Map(),
+				midx:0,
+				voiceText:'',
+				voiceList:[],
+				show:false
+			}
+		},
+		async onLoad(option) {
+			this.id = option.id;
+			this.agentId = option.agentId;
+			await this.getDetail();
+			await this.getAgentModelList()
+			this.getModelVoiceList()
+		},
+		methods:{
+			getDetail(){
+				return new Promise((resolve,reject)=>{
+					this.$api.get(`/agent/${this.id}`).then(res=>{
+						if(res.data.code!==0) return this.$showToast(res.data.msg)
+						this.agentDto = res.data.data;
+							resolve();
+					})
+				})
+			},
+			getAgentModelList(){
+				return new Promise((resolve,reject)=>{
+					this.$api.get('/agent/template').then(res=>{
+						if(res.data.code!==0) return this.$showToast(res.data.msg)
+						this.list = res.data.data;
+						let idx = this.list.findIndex(l=>l.systemPrompt===this.agentDto.systemPrompt);
+						if(idx>-1){
+							this.midx = idx;
+						}
+						if(this.list.length){
+							let map = new Map();
+							this.list.forEach(l=>{
+								map.set(l.agentName,l)
+							})
+							this.modelMap = map;
+						}
+						resolve();
+					})
+				})
+			},
+			getModelVoiceList(){
+				this.$api.get(`/models/${this.agentDto.ttsModelId}/voices`).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.voiceList = [res.data.data];
+					this.voiceText = res.data.data.find(v=>v.id===this.agentDto.ttsVoiceId)?.name||'';
+				})
+			},
+			confirm(e){
+				this.show = false;
+				this.agentDto.ttsVoiceId = e.value[0].id;
+				this.voiceText = e.value[0].name;
+			},
+			changeModel(item,index){
+				this.midx = index;
+				this.agentDto = {...this.agentDto,...this.modelMap.get(item.agentName)};
+				this.getModelVoiceList()
+			},
+			comfirmSure(){
+				let dto = JSON.parse(JSON.stringify(this.agentDto));
+				this.$api.put('/agent/'+this.agentId,dto).then(res=>{
+					if(res.data.code!==0) return this.$showToast(res.data.msg)
+					this.$showToast('配置成功');
+					setTimeout(()=>{
+						uni.reLaunch({
+							url:'/pages/home'
+						})
+					},1500)
+				})
+			},
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: linear-gradient( 180deg, #E0EEFF 0%, #F6FCFF 100%);
+		padding: 0 30rpx 30rpx;
+		box-sizing: border-box;
+		
+		.title{
+			font-family: PingFang-SC, PingFang-SC;
+			font-weight: bold;
+			font-size: 32rpx;
+			color: #111111;
+			line-height: 32rpx;
+			margin-top: 48rpx;
+		}
+		.inp{
+			margin-top: 40rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 30rpx 24rpx;
+			input{
+				border: none;
+			}
+		}
+		.list{
+			margin-top: 20rpx;
+			display: flex;
+			flex-wrap: wrap;
+			margin-left: -10rpx;
+			.pre{
+				margin-top: 20rpx;
+				margin-left: 10rpx;
+				width: calc(100% / 3 - 10rpx);
+				height: 80rpx;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #333333;
+				line-height: 80rpx;
+				text-align: center;
+				position: relative;
+				&.active{
+					background: #0066FE;
+					font-weight: bold;
+					color: #FFFFFF;
+				}
+				image{
+					width: 48rpx;
+					height: 48rpx;
+					position: absolute;
+					right: 0;
+					bottom: 0;
+					z-index: 22;
+				}
+			}
+		}
+		.intro{
+			margin-top: 40rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 21rpx 24rpx 100rpx;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #616161;
+			line-height: 56rpx;
+		}
+		.zt_btn{
+			margin-top: 96rpx;
+		}
+	}
+</style>

+ 118 - 0
pages/wifi - 副本.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='无线局域网' bgColor="transparent"></cus-header>
+		<view class="content">
+		    <button @click="initWifi">初始化WiFi模块</button>
+		    <button @click="getConnectedWifi">获取已连接WiFi</button>
+		    <button @click="getWifiList">扫描WiFi列表</button>
+		    
+		    <view v-if="connectedWifi">
+		      <text>已连接: {{ connectedWifi.SSID }}</text>
+		      <text>信号强度: {{ connectedWifi.level }} dBm</text>
+		    </view>
+		
+		    <view v-for="(wifi, index) in wifiList" :key="index">
+		      <text>{{ wifi.SSID }}</text>
+		      <text>信号: {{ wifi.level }} dBm</text>
+		      <button @click="connectWifi(wifi)">连接</button>
+		    </view>
+		  </view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+		    return {
+		      wifiList: [],
+		      connectedWifi: null
+		    }
+		  },
+		  methods: {
+		    // 初始化WiFi模块
+		    initWifi() {
+		      uni.startWifi({
+		        success: () => {
+		          uni.showToast({ title: 'WiFi模块初始化成功' });
+		          this.startWifiListener();
+		        },
+		        fail: (err) => {
+		          console.error('初始化失败:', err);
+		        }
+		      });
+		    },
+		
+		    // 获取已连接WiFi
+		    getConnectedWifi() {
+		      uni.getConnectedWifi({
+		        success: (res) => {
+		          this.connectedWifi = res.wifi;
+		        },
+		        fail: (err) => {
+		          console.error('获取失败:', err);
+		        }
+		      });
+		    },
+		
+		    // 扫描WiFi列表
+		    getWifiList() {
+		      uni.startWifi({
+		        success: () => {
+		          uni.getWifiList({
+		            success: (res) => {
+		              console.log('获取列表成功');
+		            },
+		            fail: (err) => {
+		              console.error('获取列表失败:', err);
+		            }
+		          });
+		        }
+		      });
+		    },
+		
+		    // 监听WiFi变化
+		    startWifiListener() {
+		      uni.onWifiConnected((res) => {
+		        this.connectedWifi = res.wifi;
+		      });
+		
+		      uni.onGetWifiList((res) => {
+		        this.wifiList = res.wifiList.sort((a, b) => b.level - a.level);
+		      });
+		    },
+		
+		    // 连接指定WiFi
+		    connectWifi(wifi) {
+		      uni.connectWifi({
+		        SSID: wifi.SSID,
+		        password: 'your_password', // 需要实际密码
+		        success: () => {
+		          uni.showToast({ title: '连接成功' });
+		        },
+		        fail: (err) => {
+		          console.error('连接失败:', err);
+		        }
+		      });
+		    }
+		  }
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F1F3F5;
+		box-sizing: border-box;
+		
+		.content {
+		  padding: 20px;
+		}
+		
+		button {
+		  margin: 10px 0;
+		  padding: 8px;
+		  background-color: #007AFF;
+		  color: white;
+		  border-radius: 5px;
+		}
+	}
+</style>

+ 99 - 0
pages/wifi.vue

@@ -0,0 +1,99 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='无线局域网' bgColor="transparent"></cus-header>
+		<div class="box" @tap="toSet">
+			<div class="info adffcacjc">
+				<image src="@/static/wifi.png"></image>
+				<text>无线局域网</text>
+				<p>接入无线局域网、查看可用网络,并管理加入网络及附近热点设置。<span>进一步了解...</span></p>
+			</div>
+			<div class="pre adfacjb">
+				<div class="p_l">无线局域网</div>
+				<div class="p_r">
+					<u-switch></u-switch>
+				</div>
+			</div>
+			<div class="pre adfacjb">
+				<div class="p_l">
+					<p>{{'传秀科技_5G'}}</p>
+					<span>低安全性</span>
+				</div>
+				<div class="p_r"></div>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				
+			}
+		},
+		methods:{
+			toSet(){
+				uni.navigateTo({
+					url:'/pages/wifiSet'
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F1F3F5;
+		padding: 0 31rpx 30rpx;
+		box-sizing: border-box;
+		
+		.box{
+			width: 100%;
+			padding: 50rpx 30rpx 0;
+			box-sizing: border-box;
+			background: #FFFFFF;
+			
+			.info{
+				margin-bottom: 60rpx;
+				image{
+					width: 114rpx;
+					height: 114rpx;
+				}
+				text{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 40rpx;
+					color: #111111;
+					line-height: 40rpx;
+					margin-top: 20rpx;
+				}
+				p{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: 400;
+					font-size: 34rpx;
+					color: #111111;
+					line-height: 40rpx;
+					text-align: center;
+					margin-top: 20rpx;
+				}
+			}
+			
+			.pre{
+				padding: 20rpx 30rpx;
+				border-top:1rpx solid #E5E5E5;
+				.p_l{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: 400;
+					font-size: 32rpx;
+					color: #111111;
+					line-height: 40rpx;
+					display: flex;
+					flex-direction: column;
+					span{
+						font-size: 26rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 187 - 0
pages/wifiSet.vue

@@ -0,0 +1,187 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='WiFi配置' bgColor="transparent"></cus-header>
+		<div class="box">
+			<div class="b_top adffcacjc">
+				<image src="@/static/wifi.png"></image>
+				<text>WiFi配置</text>
+			</div>
+			<div class="b_pre">
+				<div class="bp_text">SSID:</div>
+				<div class="bp_inp">
+					<input type="text">
+				</div>
+			</div>
+			<div class="b_pre">
+				<div class="bp_text">密码:</div>
+				<div class="bp_inp">
+					<input type="password">
+				</div>
+			</div>
+			<div class="zt_btn" @tap="toConnectWiFi">连接</div>
+		</div>
+		<div class="box">
+			<div class="b_title">从下面列表选择2.4GWiFi:</div>
+			<div class="b_wifi adfac" v-for="(item,index) in wifi2gList" :key="index" @tap="toCode">
+				<text>{{'REITH SPAEC(-40 dBm)'}}</text>
+				<image src="@/static/lock.png"></image>
+			</div>
+		</div>
+		<u-popup :show="show" mode="center" @close="close">
+			<div class="fail_box adffcacjc">
+				<image src="@/static/connect_off.png"></image>
+				<text>网络连接失败</text>
+				<p>请检查你的网络设置后刷新</p>
+				<div class="zt_btn" @tap="toTryAgain">重试</div>
+			</div>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				wifi2gList:[1,1,1],
+				show:false
+			}
+		},
+		methods:{
+			toConnectWiFi(){
+				this.show = true;
+			},
+			toTryAgain(){
+				this.show = false;
+				uni.showToast({
+					icon:'success',
+					title:'网络连接成功'
+				})
+			},
+			close(){
+				this.show = false;
+			},
+			toCode(){
+				uni.scanCode({
+					success: (res) => {
+						console.log(res,'res');
+						uni.navigateTo({
+							url:'/pages/code'
+						})
+					},
+					fail: (err) => {
+						console.log(err,'err');
+					}
+				})
+				
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F7F7F7;
+		padding: 0 30rpx 30rpx;
+		box-sizing: border-box;
+		.box{
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 40rpx 30rpx;
+			margin-top: 20rpx;
+			.b_top{
+				image{
+					width: 114rpx;
+					height: 114rpx;
+				}
+				text{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 40rpx;
+					color: #111111;
+					line-height: 40rpx;
+					text-align: center;
+					margin-top: 30rpx;
+				}
+			}
+			.b_pre{
+				margin-top: 40rpx;
+				.bp_text{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 30rpx;
+					color: #111111;
+					line-height: 32rpx;
+				}
+				.bp_inp{
+					width: 100%;
+					height: 90rpx;
+					border-radius: 16rpx;
+					border: 1rpx solid #E2E2E2;
+					margin-top: 24rpx;
+					padding: 24rpx 40rpx;
+					box-sizing: border-box;
+					input{
+						border: none;
+					}
+				}
+			}
+			.zt_btn{
+				margin-top: 40rpx;
+			}
+		
+			.b_title{
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #111111;
+				line-height: 45rpx;
+			}
+			.b_wifi{
+				margin-top: 48rpx;
+				text{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 30rpx;
+					color: #0066FE;
+					line-height: 30rpx;
+				}
+				image{
+					width: 36rpx;
+					height: 36rpx;
+					margin-left: 10rpx;
+				}
+			}
+		}
+		
+		.fail_box{
+			width: 570rpx;
+			background: #FFFFFF;
+			border-radius: 28rpx;
+			padding: 34rpx 125rpx 54rpx;
+			box-sizing: border-box;
+			image{
+				width: 232rpx;
+				height: 232rpx;
+			}
+			text{
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #333333;
+				line-height: 42rpx;
+				margin-top: 10rpx;
+			}
+			p{
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #A6A6A6;
+				line-height: 24rpx;
+				margin-top: 24rpx;
+			}
+			.zt_btn{
+				margin-top: 40rpx;
+			}
+		}
+	}
+</style>

BIN
static/add.png


BIN
static/ai_shape.png


BIN
static/avatar_big.png


BIN
static/avatar_small.png


BIN
static/bind.png


BIN
static/box_bg1.png


BIN
static/box_bg2.png


BIN
static/change.png


BIN
static/change2.png


BIN
static/connect_off.png


BIN
static/device_gl.png


BIN
static/lock.png


BIN
static/selected.png


BIN
static/top_bg.png


BIN
static/top_boxbg.png


BIN
static/user_set.png


BIN
static/wifi.png


+ 118 - 0
uni.scss

@@ -0,0 +1,118 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+// @import 'uni_modules/uview-ui/theme.scss
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+@import '@/uni_modules/uview-ui/theme.scss';
+.topnames{
+	width: 750rpx;
+	height: 180rpx;
+	line-height:220rpx;
+	text-align: center;
+	font-weight: bold;
+	font-size: 38rpx;
+}
+
+.OutermostLayer {
+		width: 690rpx;
+		margin: 0rpx auto;
+		background-color: #fff;
+		border-radius: 9rpx;
+	}
+.u-subsection--button{
+	height: 80rpx;
+}
+.companylist {
+		h3 {
+			font-size: 32rpx;
+			color: #0C1935;
+		}
+	}
+.toptemplate{
+	background-color: #5C8FFF;
+}
+::v-deep .toptemplate {
+	.u-cell__title-text {
+		color: #ffffff;
+	}
+}
+::v-deep .u-picker__view__column__item{
+	font-size: 20px!important;
+}
+
+
+//.u-tabbar-item__text{
+//	font-size: 16px!important;
+//}
+
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#666;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;

+ 21 - 0
uni_modules/uview-ui/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 www.uviewui.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 66 - 0
uni_modules/uview-ui/README.md

@@ -0,0 +1,66 @@
+<p align="center">
+    <img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
+</p>
+<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView 2.0</h3>
+<h3 align="center">多平台快速开发的UI框架</h3>
+
+[![stars](https://img.shields.io/github/stars/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![forks](https://img.shields.io/github/forks/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0)
+[![issues](https://img.shields.io/github/issues/umicro/uView2.0?style=flat-square&logo=GitHub)](https://github.com/umicro/uView2.0/issues)
+[![Website](https://img.shields.io/badge/uView-up-blue?style=flat-square)](https://uviewui.com)
+[![release](https://img.shields.io/github/v/release/umicro/uView2.0?style=flat-square)](https://gitee.com/umicro/uView2.0/releases)
+[![license](https://img.shields.io/github/license/umicro/uView2.0?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License)
+
+## 说明
+
+uView UI,是[uni-app](https://uniapp.dcloud.io/)全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
+
+## [官方文档:https://uviewui.com](https://uviewui.com)
+
+
+## 预览
+
+您可以通过**微信**扫码,查看最佳的演示效果。
+<br>
+<br>
+<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
+
+
+## 链接
+
+- [官方文档](https://www.uviewui.com/)
+- [更新日志](https://www.uviewui.com/components/changelog.html)
+- [升级指南](https://www.uviewui.com/components/changeGuide.html)
+- [关于我们](https://www.uviewui.com/cooperation/about.html)
+
+## 交流反馈
+
+欢迎加入我们的QQ群交流反馈:[点此跳转](https://www.uviewui.com/components/addQQGroup.html)
+
+## 关于PR
+
+> 我们非常乐意接受各位的优质PR,但在此之前我希望您了解uView2.0是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括nvue页面、vue页面。
+> 所以希望在您修复bug并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
+
+## 安装
+
+#### **uni-app插件市场链接** —— [https://ext.dcloud.net.cn/plugin?id=1593](https://ext.dcloud.net.cn/plugin?id=1593)
+
+请通过[官网安装文档](https://www.uviewui.com/components/install.html)了解更详细的内容
+
+## 快速上手
+
+请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
+
+## 使用方法
+配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
+
+```html
+<template>
+	<u-button text="按钮"></u-button>
+</template>
+```
+
+## 版权信息
+uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。
+

+ 357 - 0
uni_modules/uview-ui/changelog.md

@@ -0,0 +1,357 @@
+## 2.0.34(2022-09-25)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-input`、`u-textarea`增加`ignoreCompositionEvent`属性
+2. 修复`route`方法调用可能报错的问题
+3. 修复`u-no-network`组件`z-index`无效的问题
+4. 修复`textarea`组件在h5上confirmType=""报错的问题
+5. `u-rate`适配`nvue`
+6. 优化验证手机号码的正则表达式(根据工信部发布的《电信网编号计划(2017年版)》进行修改。)
+7. `form-item`添加`labelPosition`属性
+8. `u-calendar`修复`maxDate`设置为当前日期,并且当前时间大于08:00时无法显示日期列表的问题 (#724)
+9. `u-radio`增加一个默认插槽用于自定义修改label内容 (#680)
+10. 修复`timeFormat`函数在safari重的兼容性问题 (#664)
+## 2.0.33(2022-06-17)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`loadmore`组件`lineColor`类型错误问题
+2. 修复`u-parse`组件`imgtap`、`linktap`不生效问题
+## 2.0.32(2022-06-16)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+1. `u-loadmore`新增自定义颜色、虚/实线
+2. 修复`u-swiper-action`组件部分平台不能上下滑动的问题
+3. 修复`u-list`回弹问题
+4. 修复`notice-bar`组件动画在低端安卓机可能会抖动的问题
+5. `u-loading-page`添加控制图标大小的属性`iconSize`
+6. 修复`u-tooltip`组件`color`参数不生效的问题
+7. 修复`u--input`组件使用`blur`事件输出为`undefined`的bug
+8. `u-code-input`组件新增键盘弹起时,是否自动上推页面参数`adjustPosition`
+9. 修复`image`组件`load`事件无回调对象问题
+10. 修复`button`组件`loadingSize`设置无效问题
+10. 其他修复
+## 2.0.31(2022-04-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题
+2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题
+3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能)
+4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题
+5. 其他修复
+## 2.0.30(2022-04-04)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. `u-rate`增加`readonly`属性
+2. `tabs`滑块支持设置背景图片
+3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题
+4. `u-code-input`添加光标效果动画
+5. 修复`popup`的`open`事件不触发
+6. 修复`u-flex-column`无效的问题
+7. 修复`u-datetime-picker`索引在特定场合异常问题
+8. 修复`u-datetime-picker`最小时间字符串模板错误问题
+9. `u-swiper`添加`m3u8`验证
+10. `u-swiper`修改判断image和video逻辑
+11. 修复`swiper`无法使用本地图片问题,增加`type`参数
+12. 修复`u-row-notice`格式错误问题
+13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题
+14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题
+15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题
+16. 修复`u-checkbox-group`设置`shape`属性无效的问题
+17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题
+18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题
+19. 修复`u-list`触顶事件的触发错误的问题
+20. 修复`u-text`只有手机号可拨打的问题
+21. 修复`u-textarea`不能换行的问题
+22. 其他修复
+## 2.0.29(2022-03-13)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复`u--text`组件设置`decoration`属性未生效的问题
+2. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+3. 修复`u-datetime-picker` `intercept` 可能为undefined
+4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度
+5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效
+6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的bug
+7. 修复`u-datetime-picker`使用`formatter`后返回值不正确
+8. 修复`u-image`组件`loading`无效果的问题
+9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题
+10. 修复`u-datetime-picker`组件`itemHeight`无效问题
+11. 其他修复
+## 2.0.28(2022-02-22)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. search组件新增searchIconSize属性
+2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56
+3. 修复text value.js 判断日期出format错误问题
+4. priceFormat格式化金额出现精度错误
+5. priceFormat在部分情况下出现精度损失问题
+6. 优化表单rules提示
+7. 修复avatar组件src为空时,展示状态不对
+8. 其他修复
+## 2.0.27(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.26(2022-01-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1.样式修复
+## 2.0.25(2022-01-27)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复text组件mode=price时,可能会导致精度错误的问题
+2. 添加$u.setConfig()方法,可设置uView内置的config, props, zIndex, color属性,详见:[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+3. 优化form组件在errorType=toast时,如果输入错误页面会有抖动的问题
+4. 修复$u.addUnit()对配置默认单位可能无效的问题
+## 2.0.24(2022-01-25)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复swiper在current指定非0时缩放有误
+2. 修复u-icon添加stop属性的时候报错
+3. 优化遗留的通过正则判断rpx单位的问题
+4. 优化Layout布局 vue使用gutter时,会超出固定区域
+5. 优化search组件高度单位问题(rpx -> px)
+6. 修复u-image slot 加载和错误的图片失去了高度
+7. 修复u-index-list中footer插槽与header插槽存在性判断错误
+8. 修复部分机型下u-popup关闭时会闪烁
+9. 修复u-image在nvue-app下失去宽高
+10. 修复u-popup运行报错
+11. 修复u-tooltip报错
+12. 修复box-sizing在app下的警告
+13. 修复u-navbar在小程序中报运行时错误
+14. 其他修复
+## 2.0.23(2022-01-24)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题
+2. 修复col组件gutter参数带rpx单位处理不正确的问题
+3. 修复text组件单行时无法显示省略号的问题
+4. navbar添加titleStyle参数
+5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题
+## 2.0.22(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. $u.page()方法优化,避免在特殊场景可能报错的问题
+2. picker组件添加immediateChange参数
+3. 新增$u.pages()方法
+## 2.0.21(2022-01-19)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化:form组件在用户设置rules的时候提示用户model必传
+2. 优化遗留的通过正则判断rpx单位的问题
+3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后,placeholder高度填充不正确
+4. 修复swiper在current指定非0时缩放有误
+5. 修复u-icon添加stop属性的时候报错
+6. 修复upload组件在accept=all的时候没有作用
+7. 修复在text组件mode为phone时call属性无效的问题
+8. 处理u-form clearValidate方法
+9. 其他修复
+## 2.0.20(2022-01-14)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复calendar默认会选择一个日期,如果直接点确定的话,无法取到值的问题
+2. 修复Slider缺少disabled props 还有注释
+3. 修复u-notice-bar点击事件无法拿到index索引值的问题
+4. 修复u-collapse-item在vue文件下,app端自定义插槽不生效的问题
+5. 优化头像为空时显示默认头像 
+6. 修复图片地址赋值后判断加载状态为完成问题
+7. 修复日历滚动到默认日期月份区域
+8. search组件暴露点击左边icon事件
+9. 修复u-form clearValidate方法不生效
+10. upload h5端增加返回文件参数(文件的name参数)
+11. 处理upload选择文件后url为blob类型无法预览的问题
+12. u-code-input 修复输入框没有往左移出一半屏幕
+13. 修复Upload上传 disabled为true时,控制台报hoverClass类型错误
+14. 临时处理ios app下grid点击坍塌问题
+15. 其他修复
+## 2.0.19(2021-12-29)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化微信小程序包体积可在微信中预览,请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码”
+2. 优化微信小程序setData性能,处理某些方法如$u.route()无法在模板中使用的问题
+3. navbar添加autoBack参数
+4. 允许avatar组件的事件冒泡
+5. 修复cell组件报错问题
+6. 其他修复
+## 2.0.18(2021-12-28)
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复app端编译报错问题
+2. 重新处理微信小程序端setData过大的性能问题
+3. 修复边框问题
+4. 修复最大最小月份不大于0则没有数据出现的问题
+5. 修复SwipeAction微信小程序端无法上下滑动问题
+6. 修复input的placeholder在小程序端默认显示为true问题
+7. 修复divider组件click事件无效问题
+8. 修复u-code-input maxlength 属性值为 String 类型时显示异常
+9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题
+10. 处理form-item的label为top时,取消错误提示的左边距
+11. 其他修复
+## 2.0.17(2021-12-26)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决HBuilderX3.3.3.20211225版本导致的样式问题
+2. calendar日历添加monthNum参数
+3. navbar添加center slot
+## 2.0.16(2021-12-25)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 解决微信小程序setData性能问题
+2. 修复count-down组件change事件不触发问题
+## 2.0.15(2021-12-21)
+## uView正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复Cell单元格titleWidth无效
+2. 修复cheakbox组件ischecked不更新
+3. 修复keyboard是否显示"."按键默认值问题
+4. 修复number-keyboard是否显示键盘的"."符号问题
+5. 修复Input输入框 readonly无效
+6. 修复u-avatar 导致打包app、H5时候报错问题
+7. 修复Upload上传deletable无效
+8. 修复upload当设置maxSize时无效的问题
+9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题
+10. 修复rate组件在有padding的view内,显示的星星位置和可触摸区域不匹配,无法正常选中星星
+## 2.0.13(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题
+## 2.0.12(2021-12-14)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复tabs组件在vue环境下划线消失的问题
+2. 修复upload组件在安卓小程序无法选择视频的问题
+3. 添加uni.$u.config.unit配置,用于配置参数默认单位,详见:[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
+4. 修复textarea组件在没绑定v-model时,字符统计不生效问题
+5. 修复nvue下控制是否出现滚动条失效问题
+## 2.0.11(2021-12-13)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. text组件align参数无效的问题
+2. subsection组件添加keyName参数
+3. upload组件无法判断[Object file]类型的问题
+4. 处理notify层级过低问题
+5. codeInput组件添加disabledDot参数
+6. 处理actionSheet组件round参数无效的问题
+7. calendar组件添加round参数用于控制圆角值
+8. 处理swipeAction组件在vue环境下默认被打开的问题
+9. button组件的throttleTime节流参数无效的问题
+10. 解决u-notify手动关闭方法close()无效的问题
+11. input组件readonly不生效问题
+12. tag组件type参数为info不生效问题
+## 2.0.10(2021-12-08)
+## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复button sendMessagePath属性不生效
+2. 修复DatetimePicker选择器title无效
+3. 修复u-toast设置loading=true不生效
+4. 修复u-text金额模式传0报错
+5. 修复u-toast组件的icon属性配置不生效
+6. button的icon在特殊场景下的颜色优化
+7. IndexList优化,增加#
+## 2.0.9(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 优化swiper的height支持100%值(仅vue有效),修复嵌入视频时click事件无法触发的问题
+2. 优化tabs组件对list值为空的判断,或者动态变化list时重新计算相关尺寸的问题
+3. 优化datetime-picker组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过v-model绑定值才有效
+4. 修复upload内嵌在其他组件中,选择图片可能不会换行的问题
+## 2.0.8(2021-12-01)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复toast的position参数无效问题
+2. 处理input在ios nvue上无法获得焦点的问题
+3. avatar-group组件添加extraValue参数,让剩余展示数量可手动控制
+4. tabs组件添加keyName参数用于配置从对象中读取的键名
+5. 处理text组件名字脱敏默认配置无效的问题
+6. 处理picker组件item文本太长换行问题
+## 2.0.7(2021-11-30)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 修复radio和checkbox动态改变v-model无效的问题。
+2. 优化form规则validator在微信小程序用法
+3. 修复backtop组件mode参数在微信小程序无效的问题
+4. 处理Album的previewFullImage属性无效的问题
+5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题
+## 2.0.6(2021-11-27)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. 处理tag组件在vue下边框无效的问题。
+2. 处理popup组件圆角参数可能无效的问题。
+3. 处理tabs组件lineColor参数可能无效的问题。
+4. propgress组件在值很小时,显示异常的问题。
+## 2.0.5(2021-11-25)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. calendar在vue下显示异常问题。 
+2. form组件labelPosition和errorType参数无效的问题
+3. input组件inputAlign无效的问题
+4. 其他一些修复
+## 2.0.4(2021-11-23)
+## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+0. input组件缺失@confirm事件,以及subfix和prefix无效问题
+1. component.scss文件样式在vue下干扰全局布局问题
+2. 修复subsection在vue环境下表现异常的问题
+3. tag组件的bgColor等参数无效的问题
+4. upload组件不换行的问题
+5. 其他的一些修复处理
+## 2.0.3(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 处理modal的confirm回调事件拼写错误问题
+6. 处理input组件@input事件参数错误问题
+7. 其他一些修复
+## 2.0.2(2021-11-16)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+6. 优化loading-icon组件的scss写法问题,防止不兼容新版本scss
+## 2.0.0(2020-11-15)
+## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
+
+# uView2.0重磅发布,利剑出鞘,一统江湖
+
+1. uView2.0已实现全面兼容nvue
+2. uView2.0对1.x进行了架构重构,细节和性能都有极大提升
+3. 目前uView2.0为公测阶段,相关细节可能会有变动
+4. 我们写了一份与1.x的对比指南,详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
+5. 修复input组件formatter参数缺失问题
+
+

+ 78 - 0
uni_modules/uview-ui/components/u--form/u--form.vue

@@ -0,0 +1,78 @@
+<template>
+	<uvForm
+		ref="uForm"
+		:model="model"
+		:rules="rules"
+		:errorType="errorType"
+		:borderBottom="borderBottom"
+		:labelPosition="labelPosition"
+		:labelWidth="labelWidth"
+		:labelAlign="labelAlign"
+		:labelStyle="labelStyle"
+		:customStyle="customStyle"
+	>
+		<slot />
+	</uvForm>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
+	 * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
+	 */
+	import uvForm from '../u-form/u-form.vue';
+	import props from '../u-form/props.js'
+	export default {
+		// #ifdef MP-WEIXIN
+		name: 'u-form',
+		// #endif
+		// #ifndef MP-WEIXIN
+		name: 'u--form',
+		// #endif
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvForm
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
+			setRules(rules) {
+				this.$refs.uForm.setRules(rules)
+			},
+			validate() {
+				/**
+				 * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
+				 * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
+				 * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
+				 */
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validate()
+			},
+			validateField(value, callback) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.validateField(value, callback)
+			},
+			resetFields() {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.resetFields()
+			},
+			clearValidate(props) {
+				// #ifdef MP-WEIXIN
+				this.setMpData()
+				// #endif
+				return this.$refs.uForm.clearValidate(props)
+			},
+			setMpData() {
+				this.$refs.uForm.children = this.children
+			}
+		},
+	}
+</script>

+ 47 - 0
uni_modules/uview-ui/components/u--image/u--image.vue

@@ -0,0 +1,47 @@
+<template>
+	<uvImage 
+		:src="src"
+		:mode="mode"
+		:width="width"
+		:height="height"
+		:shape="shape"
+		:radius="radius"
+		:lazyLoad="lazyLoad"
+		:showMenuByLongpress="showMenuByLongpress"
+		:loadingIcon="loadingIcon"
+		:errorIcon="errorIcon"
+		:showLoading="showLoading"
+		:showError="showError"
+		:fade="fade"
+		:webp="webp"
+		:duration="duration"
+		:bgColor="bgColor"
+		:customStyle="customStyle"
+		@click="$emit('click')"
+		@error="$emit('error')"
+		@load="$emit('load')"
+	>
+		<template v-slot:loading>
+			<slot name="loading"></slot>
+		</template>
+		<template v-slot:error>
+			<slot name="error"></slot>
+		</template>
+	</uvImage>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
+	 * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
+	 */
+	import uvImage from '../u-image/u-image.vue';
+	import props from '../u-image/props.js';
+	export default {
+		name: 'u--image',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvImage
+		},
+	}
+</script>

+ 73 - 0
uni_modules/uview-ui/components/u--input/u--input.vue

@@ -0,0 +1,73 @@
+<template>
+	<uvInput 
+		:value="value"
+		:type="type"
+		:fixed="fixed"
+		:disabled="disabled"
+		:disabledColor="disabledColor"
+		:clearable="clearable"
+		:password="password"
+		:maxlength="maxlength"
+		:placeholder="placeholder"
+		:placeholderClass="placeholderClass"
+		:placeholderStyle="placeholderStyle"
+		:showWordLimit="showWordLimit"
+		:confirmType="confirmType"
+		:confirmHold="confirmHold"
+		:holdKeyboard="holdKeyboard"
+		:focus="focus"
+		:autoBlur="autoBlur"
+		:disableDefaultPadding="disableDefaultPadding"
+		:cursor="cursor"
+		:cursorSpacing="cursorSpacing"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:inputAlign="inputAlign"
+		:fontSize="fontSize"
+		:color="color"
+		:prefixIcon="prefixIcon"
+		:suffixIcon="suffixIcon"
+		:suffixIconStyle="suffixIconStyle"
+		:prefixIconStyle="prefixIconStyle"
+		:border="border"
+		:readonly="readonly"
+		:shape="shape"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+		@focus="$emit('focus')"
+		@blur="e => $emit('blur', e)"
+		@keyboardheightchange="$emit('keyboardheightchange')"
+		@change="e => $emit('change', e)"
+		@input="e => $emit('input', e)"
+		@confirm="e => $emit('confirm', e)"
+		@clear="$emit('clear')"
+		@click="$emit('click')"
+	>
+		<!-- #ifdef MP -->
+		<slot name="prefix"></slot>
+		<slot name="suffix"></slot>
+		<!-- #endif -->
+		<!-- #ifndef MP -->
+		<slot name="prefix" slot="prefix"></slot>
+		<slot name="suffix" slot="suffix"></slot>
+		<!-- #endif -->
+	</uvInput>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
+	 * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
+	 */
+	import uvInput from '../u-input/u-input.vue';
+	import props from '../u-input/props.js'
+	export default {
+		name: 'u--input',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvInput
+		},
+	}
+</script>

+ 44 - 0
uni_modules/uview-ui/components/u--text/u--text.vue

@@ -0,0 +1,44 @@
+<template>
+    <uvText
+        :type="type"
+        :show="show"
+        :text="text"
+        :prefixIcon="prefixIcon"
+        :suffixIcon="suffixIcon"
+        :mode="mode"
+        :href="href"
+        :format="format"
+        :call="call"
+        :openType="openType"
+        :bold="bold"
+        :block="block"
+        :lines="lines"
+        :color="color"
+		:decoration="decoration"
+        :size="size"
+        :iconStyle="iconStyle"
+        :margin="margin"
+        :lineHeight="lineHeight"
+        :align="align"
+        :wordWrap="wordWrap"
+        :customStyle="customStyle"
+        @click="$emit('click')"
+    ></uvText>
+</template>
+
+<script>
+/**
+ * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
+ * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
+ * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
+ */
+import uvText from "../u-text/u-text.vue";
+import props from "../u-text/props.js";
+export default {
+    name: "u--text",
+    mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+    components: {
+        uvText,
+    },
+};
+</script>

+ 48 - 0
uni_modules/uview-ui/components/u--textarea/u--textarea.vue

@@ -0,0 +1,48 @@
+<template>
+	<uvTextarea
+		:value="value"
+		:placeholder="placeholder"
+		:height="height"
+		:confirmType="confirmType"
+		:disabled="disabled"
+		:count="count"
+		:focus="focus"
+		:autoHeight="autoHeight"
+		:fixed="fixed"
+		:cursorSpacing="cursorSpacing"
+		:cursor="cursor"
+		:showConfirmBar="showConfirmBar"
+		:selectionStart="selectionStart"
+		:selectionEnd="selectionEnd"
+		:adjustPosition="adjustPosition"
+		:disableDefaultPadding="disableDefaultPadding"
+		:holdKeyboard="holdKeyboard"
+		:maxlength="maxlength"
+		:border="border"
+		:customStyle="customStyle"
+		:formatter="formatter"
+		:ignoreCompositionEvent="ignoreCompositionEvent"
+		@focus="e => $emit('focus')"
+		@blur="e => $emit('blur')"
+		@linechange="e => $emit('linechange', e)"
+		@confirm="e => $emit('confirm')"
+		@input="e => $emit('input', e)"
+		@keyboardheightchange="e => $emit('keyboardheightchange')"
+	></uvTextarea>
+</template>
+
+<script>
+	/**
+	 * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
+	 * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
+	 */
+	import uvTextarea from '../u-textarea/u-textarea.vue';
+	import props from '../u-textarea/props.js'
+	export default {
+		name: 'u--textarea',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		components: {
+			uvTextarea
+		},
+	}
+</script>

+ 54 - 0
uni_modules/uview-ui/components/u-action-sheet/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 操作菜单是否展示 (默认false)
+        show: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.show
+        },
+        // 标题
+        title: {
+            type: String,
+            default: uni.$u.props.actionSheet.title
+        },
+        // 选项上方的描述信息
+        description: {
+            type: String,
+            default: uni.$u.props.actionSheet.description
+        },
+        // 数据
+        actions: {
+            type: Array,
+            default: uni.$u.props.actionSheet.actions
+        },
+        // 取消按钮的文字,不为空时显示按钮
+        cancelText: {
+            type: String,
+            default: uni.$u.props.actionSheet.cancelText
+        },
+        // 点击某个菜单项时是否关闭弹窗
+        closeOnClickAction: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickAction
+        },
+        // 处理底部安全区(默认true)
+        safeAreaInsetBottom: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.safeAreaInsetBottom
+        },
+        // 小程序的打开方式
+        openType: {
+            type: String,
+            default: uni.$u.props.actionSheet.openType
+        },
+        // 点击遮罩是否允许关闭 (默认true)
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: uni.$u.props.actionSheet.closeOnClickOverlay
+        },
+        // 圆角值
+        round: {
+            type: [Boolean, String, Number],
+            default: uni.$u.props.actionSheet.round
+        }
+    }
+}

+ 278 - 0
uni_modules/uview-ui/components/u-action-sheet/u-action-sheet.vue

@@ -0,0 +1,278 @@
+
+<template>
+	<u-popup
+	    :show="show"
+	    mode="bottom"
+	    @close="closeHandler"
+	    :safeAreaInsetBottom="safeAreaInsetBottom"
+	    :round="round"
+	>
+		<view class="u-action-sheet">
+			<view
+			    class="u-action-sheet__header"
+			    v-if="title"
+			>
+				<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
+				<view
+				    class="u-action-sheet__header__icon-wrap"
+				    @tap.stop="cancel"
+				>
+					<u-icon
+					    name="close"
+					    size="17"
+					    color="#c8c9cc"
+					    bold
+					></u-icon>
+				</view>
+			</view>
+			<text
+			    class="u-action-sheet__description"
+				:style="[{
+					marginTop: `${title && description ? 0 : '18px'}`
+				}]"
+			    v-if="description"
+			>{{description}}</text>
+			<slot>
+				<u-line v-if="description"></u-line>
+				<view class="u-action-sheet__item-wrap">
+					<template v-for="(item, index) in actions">
+						<!-- #ifdef MP -->
+						<button
+						    :key="index"
+						    class="u-reset-button"
+						    :openType="item.openType"
+						    @getuserinfo="onGetUserInfo"
+						    @contact="onContact"
+						    @getphonenumber="onGetPhoneNumber"
+						    @error="onError"
+						    @launchapp="onLaunchApp"
+						    @opensetting="onOpenSetting"
+						    :lang="lang"
+						    :session-from="sessionFrom"
+						    :send-message-title="sendMessageTitle"
+						    :send-message-path="sendMessagePath"
+						    :send-message-img="sendMessageImg"
+						    :show-message-card="showMessageCard"
+						    :app-parameter="appParameter"
+						    @tap="selectHandler(index)"
+						    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+						>
+							<!-- #endif -->
+							<view
+							    class="u-action-sheet__item-wrap__item"
+							    @tap.stop="selectHandler(index)"
+							    :hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
+							    :hover-stay-time="150"
+							>
+								<template v-if="!item.loading">
+									<text
+									    class="u-action-sheet__item-wrap__item__name"
+									    :style="[itemStyle(index)]"
+									>{{ item.name }}</text>
+									<text
+									    v-if="item.subname"
+									    class="u-action-sheet__item-wrap__item__subname"
+									>{{ item.subname }}</text>
+								</template>
+								<u-loading-icon
+								    v-else
+								    custom-class="van-action-sheet__loading"
+								    size="18"
+								    mode="circle"
+								/>
+							</view>
+							<!-- #ifdef MP -->
+						</button>
+						<!-- #endif -->
+						<u-line v-if="index !== actions.length - 1"></u-line>
+					</template>
+				</view>
+			</slot>
+			<u-gap
+			    bgColor="#eaeaec"
+			    height="6"
+			    v-if="cancelText"
+			></u-gap>
+			<view hover-class="u-action-sheet--hover">
+				<text
+				    @touchmove.stop.prevent
+				    :hover-stay-time="150"
+				    v-if="cancelText"
+				    class="u-action-sheet__cancel-text"
+				    @tap="cancel"
+				>{{cancelText}}</text>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import openType from '../../libs/mixin/openType'
+	import button from '../../libs/mixin/button'
+	import props from './props.js';
+	/**
+	 * ActionSheet 操作菜单
+	 * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
+	 * @tutorial https://www.uviewui.com/components/actionSheet.html
+	 * 
+	 * @property {Boolean}			show				操作菜单是否展示 (默认 false )
+	 * @property {String}			title				操作菜单标题
+	 * @property {String}			description			选项上方的描述信息
+	 * @property {Array<Object>}	actions				按钮的文字数组,见官方文档示例
+	 * @property {String}			cancelText			取消按钮的提示文字,不为空时显示按钮
+	 * @property {Boolean}			closeOnClickAction	点击某个菜单项时是否关闭弹窗 (默认 true )
+	 * @property {Boolean}			safeAreaInsetBottom	处理底部安全区 (默认 true )
+	 * @property {String}			openType			小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
+	 * @property {Boolean}			closeOnClickOverlay	点击遮罩是否允许关闭  (默认 true )
+	 * @property {Number|String}	round				圆角值,默认无圆角  (默认 0 )
+	 * @property {String}			lang				指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
+	 * @property {String}			sessionFrom			会话来源,openType="contact"时有效
+	 * @property {String}			sendMessageTitle	会话内消息卡片标题,openType="contact"时有效
+	 * @property {String}			sendMessagePath		会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+	 * @property {String}			sendMessageImg		会话内消息卡片图片,openType="contact"时有效
+	 * @property {Boolean}			showMessageCard		是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
+	 * @property {String}			appParameter		打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
+	 * 
+	 * @event {Function} select			点击ActionSheet列表项时触发 
+	 * @event {Function} close			点击取消按钮时触发
+	 * @event {Function} getuserinfo	用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
+	 * @event {Function} contact		客服消息回调,openType="contact"时有效
+	 * @event {Function} getphonenumber	获取用户手机号回调,openType="getPhoneNumber"时有效
+	 * @event {Function} error			当使用开放能力时,发生错误的回调,openType="error"时有效
+	 * @event {Function} launchapp		打开 APP 成功的回调,openType="launchApp"时有效
+	 * @event {Function} opensetting	在打开授权设置页后回调,openType="openSetting"时有效
+	 * @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
+	 */
+	export default {
+		name: "u-action-sheet",
+		// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
+		mixins: [openType, button, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			// 操作项目的样式
+			itemStyle() {
+				return (index) => {
+					let style = {};
+					if (this.actions[index].color) style.color = this.actions[index].color
+					if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize)
+					// 选项被禁用的样式
+					if (this.actions[index].disabled) style.color = '#c0c4cc'
+					return style;
+				}
+			},
+		},
+		methods: {
+			closeHandler() {
+				// 允许点击遮罩关闭时,才发出close事件
+				if(this.closeOnClickOverlay) {
+					this.$emit('close')
+				}
+			},
+			// 点击取消按钮
+			cancel() {
+				this.$emit('close')
+			},
+			selectHandler(index) {
+				const item = this.actions[index]
+				if (item && !item.disabled && !item.loading) {
+					this.$emit('select', item)
+					if (this.closeOnClickAction) {
+						this.$emit('close')
+					}
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	$u-action-sheet-reset-button-width:100% !default;
+	$u-action-sheet-title-font-size: 16px !default;
+	$u-action-sheet-title-padding: 12px 30px !default;
+	$u-action-sheet-title-color: $u-main-color !default;
+	$u-action-sheet-header-icon-wrap-right:15px !default;
+	$u-action-sheet-header-icon-wrap-top:15px !default;
+	$u-action-sheet-description-font-size:13px !default;
+	$u-action-sheet-description-color:14px !default;
+	$u-action-sheet-description-margin: 18px 15px !default;
+	$u-action-sheet-item-wrap-item-padding:15px !default;
+	$u-action-sheet-item-wrap-name-font-size:16px !default;
+	$u-action-sheet-item-wrap-subname-font-size:13px !default;
+	$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
+	$u-action-sheet-item-wrap-subname-margin-top:10px !default;
+	$u-action-sheet-cancel-text-font-size:16px !default;
+	$u-action-sheet-cancel-text-color:$u-content-color !default;
+	$u-action-sheet-cancel-text-font-size:15px !default;
+	$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
+
+	.u-reset-button {
+		width: $u-action-sheet-reset-button-width;
+	}
+
+	.u-action-sheet {
+		text-align: center;
+		&__header {
+			position: relative;
+			padding: $u-action-sheet-title-padding;
+			&__title {
+				font-size: $u-action-sheet-title-font-size;
+				color: $u-action-sheet-title-color;
+				font-weight: bold;
+				text-align: center;
+			}
+
+			&__icon-wrap {
+				position: absolute;
+				right: $u-action-sheet-header-icon-wrap-right;
+				top: $u-action-sheet-header-icon-wrap-top;
+			}
+		}
+
+		&__description {
+			font-size: $u-action-sheet-description-font-size;
+			color: $u-tips-color;
+			margin: $u-action-sheet-description-margin;
+			text-align: center;
+		}
+
+		&__item-wrap {
+
+			&__item {
+				padding: $u-action-sheet-item-wrap-item-padding;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				flex-direction: column;
+
+				&__name {
+					font-size: $u-action-sheet-item-wrap-name-font-size;
+					color: $u-main-color;
+					text-align: center;
+				}
+
+				&__subname {
+					font-size: $u-action-sheet-item-wrap-subname-font-size;
+					color: $u-action-sheet-item-wrap-subname-color;
+					margin-top: $u-action-sheet-item-wrap-subname-margin-top;
+					text-align: center;
+				}
+			}
+		}
+
+		&__cancel-text {
+			font-size: $u-action-sheet-cancel-text-font-size;
+			color: $u-action-sheet-cancel-text-color;
+			text-align: center;
+			padding: $u-action-sheet-cancel-text-font-size;
+		}
+
+		&--hover {
+			background-color: $u-action-sheet-cancel-text-hover-background-color;
+		}
+	}
+</style>

+ 59 - 0
uni_modules/uview-ui/components/u-album/props.js

@@ -0,0 +1,59 @@
+export default {
+    props: {
+        // 图片地址,Array<String>|Array<Object>形式
+        urls: {
+            type: Array,
+            default: uni.$u.props.album.urls
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.album.keyName
+        },
+        // 单图时,图片长边的长度
+        singleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.singleSize
+        },
+        // 多图时,图片边长
+        multipleSize: {
+            type: [String, Number],
+            default: uni.$u.props.album.multipleSize
+        },
+        // 多图时,图片水平和垂直之间的间隔
+        space: {
+            type: [String, Number],
+            default: uni.$u.props.album.space
+        },
+        // 单图时,图片缩放裁剪的模式
+        singleMode: {
+            type: String,
+            default: uni.$u.props.album.singleMode
+        },
+        // 多图时,图片缩放裁剪的模式
+        multipleMode: {
+            type: String,
+            default: uni.$u.props.album.multipleMode
+        },
+        // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.maxCount
+        },
+        // 是否可以预览图片
+        previewFullImage: {
+            type: Boolean,
+            default: uni.$u.props.album.previewFullImage
+        },
+        // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+        rowCount: {
+            type: [String, Number],
+            default: uni.$u.props.album.rowCount
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.album.showMore
+        }
+    }
+}

+ 259 - 0
uni_modules/uview-ui/components/u-album/u-album.vue

@@ -0,0 +1,259 @@
+<template>
+    <view class="u-album">
+        <view
+            class="u-album__row"
+            ref="u-album__row"
+            v-for="(arr, index) in showUrls"
+            :forComputedUse="albumWidth"
+            :key="index"
+        >
+            <view
+                class="u-album__row__wrapper"
+                v-for="(item, index1) in arr"
+                :key="index1"
+                :style="[imageStyle(index + 1, index1 + 1)]"
+                @tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
+            >
+                <image
+                    :src="getSrc(item)"
+                    :mode="
+                        urls.length === 1
+                            ? imageHeight > 0
+                                ? singleMode
+                                : 'widthFix'
+                            : multipleMode
+                    "
+                    :style="[
+                        {
+                            width: imageWidth,
+                            height: imageHeight
+                        }
+                    ]"
+                ></image>
+                <view
+                    v-if="
+                        showMore &&
+                        urls.length > rowCount * showUrls.length &&
+                        index === showUrls.length - 1 &&
+                        index1 === showUrls[showUrls.length - 1].length - 1
+                    "
+                    class="u-album__row__wrapper__text"
+                >
+                    <u--text
+                        :text="`+${urls.length - maxCount}`"
+                        color="#fff"
+                        :size="multipleSize * 0.3"
+                        align="center"
+                        customStyle="justify-content: center"
+                    ></u--text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import props from './props.js'
+// #ifdef APP-NVUE
+// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+const dom = uni.requireNativePlugin('dom')
+// #endif
+
+/**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://www.uviewui.com/components/album.html
+ *
+ * @property {Array}           urls             图片地址列表 Array<String>|Array<Object>形式
+ * @property {String}          keyName          指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} singleSize       单图时,图片长边的长度  (默认 180 )
+ * @property {String | Number} multipleSize     多图时,图片边长 (默认 70 )
+ * @property {String | Number} space            多图时,图片水平和垂直之间的间隔 (默认 6 )
+ * @property {String}          singleMode       单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
+ * @property {String}          multipleMode     多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
+ * @property {String | Number} maxCount         取消按钮的提示文字 (默认 9 )
+ * @property {Boolean}         previewFullImage 是否可以预览图片 (默认 true )
+ * @property {String | Number} rowCount         每行展示图片数量,如设置,singleSize和multipleSize将会无效	(默认 3 )
+ * @property {Boolean}         showMore         超出maxCount时是否显示查看更多的提示 (默认 true )
+ *
+ * @event    {Function}        albumWidth       某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送  (回调参数 width )
+ * @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
+ */
+export default {
+    name: 'u-album',
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+    data() {
+        return {
+            // 单图的宽度
+            singleWidth: 0,
+            // 单图的高度
+            singleHeight: 0,
+            // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
+            singlePercent: 0.6
+        }
+    },
+    watch: {
+        urls: {
+            immediate: true,
+            handler(newVal) {
+                if (newVal.length === 1) {
+                    this.getImageRect()
+                }
+            }
+        }
+    },
+    computed: {
+        imageStyle() {
+            return (index1, index2) => {
+                const { space, rowCount, multipleSize, urls } = this,
+                    { addUnit, addStyle } = uni.$u,
+                    rowLen = this.showUrls.length,
+                    allLen = this.urls.length
+                const style = {
+                    marginRight: addUnit(space),
+                    marginBottom: addUnit(space)
+                }
+                // 如果为最后一行,则每个图片都无需下边框
+                if (index1 === rowLen) style.marginBottom = 0
+                // 每行的最右边一张和总长度的最后一张无需右边框
+                if (
+                    index2 === rowCount ||
+                    (index1 === rowLen &&
+                        index2 === this.showUrls[index1 - 1].length)
+                )
+                    style.marginRight = 0
+                return style
+            }
+        },
+        // 将数组划分为二维数组
+        showUrls() {
+            const arr = []
+            this.urls.map((item, index) => {
+                // 限制最大展示数量
+                if (index + 1 <= this.maxCount) {
+                    // 计算该元素为第几个素组内
+                    const itemIndex = Math.floor(index / this.rowCount)
+                    // 判断对应的索引是否存在
+                    if (!arr[itemIndex]) {
+                        arr[itemIndex] = []
+                    }
+                    arr[itemIndex].push(item)
+                }
+            })
+            return arr
+        },
+        imageWidth() {
+            return uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleWidth : this.multipleSize
+            )
+        },
+        imageHeight() {
+            return uni.$u.addUnit(
+                this.urls.length === 1 ? this.singleHeight : this.multipleSize
+            )
+        },
+        // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+        // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+        albumWidth() {
+            let width = 0
+            if (this.urls.length === 1) {
+                width = this.singleWidth
+            } else {
+                width =
+                    this.showUrls[0].length * this.multipleSize +
+                    this.space * (this.showUrls[0].length - 1)
+            }
+            this.$emit('albumWidth', width)
+            return width
+        }
+    },
+    methods: {
+        // 预览图片
+        onPreviewTap(url) {
+            const urls = this.urls.map((item) => {
+                return this.getSrc(item)
+            })
+            uni.previewImage({
+                current: url,
+                urls
+            })
+        },
+        // 获取图片的路径
+        getSrc(item) {
+            return uni.$u.test.object(item)
+                ? (this.keyName && item[this.keyName]) || item.src
+                : item
+        },
+        // 单图时,获取图片的尺寸
+        // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+        // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+        getImageRect() {
+            const src = this.getSrc(this.urls[0])
+            uni.getImageInfo({
+                src,
+                success: (res) => {
+                    // 判断图片横向还是竖向展示方式
+                    const isHorizotal = res.width >= res.height
+                    this.singleWidth = isHorizotal
+                        ? this.singleSize
+                        : (res.width / res.height) * this.singleSize
+                    this.singleHeight = !isHorizotal
+                        ? this.singleSize
+                        : (res.height / res.width) * this.singleWidth
+                },
+                fail: () => {
+                    this.getComponentWidth()
+                }
+            })
+        },
+        // 获取组件的宽度
+        async getComponentWidth() {
+            // 延时一定时间,以获取dom尺寸
+            await uni.$u.sleep(30)
+            // #ifndef APP-NVUE
+            this.$uGetRect('.u-album__row').then((size) => {
+                this.singleWidth = size.width * this.singlePercent
+            })
+            // #endif
+
+            // #ifdef APP-NVUE
+            // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
+            const ref = this.$refs['u-album__row'][0]
+            ref &&
+                dom.getComponentRect(ref, (res) => {
+                    this.singleWidth = res.size.width * this.singlePercent
+                })
+            // #endif
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../libs/css/components.scss';
+
+.u-album {
+    @include flex(column);
+
+    &__row {
+        @include flex(row);
+        flex-wrap: wrap;
+
+        &__wrapper {
+            position: relative;
+
+            &__text {
+                position: absolute;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                background-color: rgba(0, 0, 0, 0.3);
+                @include flex(row);
+                justify-content: center;
+                align-items: center;
+            }
+        }
+    }
+}
+</style>

+ 44 - 0
uni_modules/uview-ui/components/u-alert/props.js

@@ -0,0 +1,44 @@
+export default {
+    props: {
+        // 显示文字
+        title: {
+            type: String,
+            default: uni.$u.props.alert.title
+        },
+        // 主题,success/warning/info/error
+        type: {
+            type: String,
+            default: uni.$u.props.alert.type
+        },
+        // 辅助性文字
+        description: {
+            type: String,
+            default: uni.$u.props.alert.description
+        },
+        // 是否可关闭
+        closable: {
+            type: Boolean,
+            default: uni.$u.props.alert.closable
+        },
+        // 是否显示图标
+        showIcon: {
+            type: Boolean,
+            default: uni.$u.props.alert.showIcon
+        },
+        // 浅或深色调,light-浅色,dark-深色
+        effect: {
+            type: String,
+            default: uni.$u.props.alert.effect
+        },
+        // 文字是否居中
+        center: {
+            type: Boolean,
+            default: uni.$u.props.alert.center
+        },
+        // 字体大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.alert.fontSize
+        }
+    }
+}

+ 243 - 0
uni_modules/uview-ui/components/u-alert/u-alert.vue

@@ -0,0 +1,243 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :show="show"
+	>
+		<view
+		    class="u-alert"
+		    :class="[`u-alert--${type}--${effect}`]"
+		    @tap.stop="clickHandler"
+		    :style="[$u.addStyle(customStyle)]"
+		>
+			<view
+			    class="u-alert__icon"
+			    v-if="showIcon"
+			>
+				<u-icon
+				    :name="iconName"
+				    size="18"
+				    :color="iconColor"
+				></u-icon>
+			</view>
+			<view
+			    class="u-alert__content"
+			    :style="[{
+					paddingRight: closable ? '20px' : 0
+				}]"
+			>
+				<text
+				    class="u-alert__content__title"
+				    v-if="title"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ title }}</text>
+				<text
+				    class="u-alert__content__desc"
+					v-if="description"
+					:style="[{
+						fontSize: $u.addUnit(fontSize),
+						textAlign: center ? 'center' : 'left'
+					}]"
+				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
+				>{{ description }}</text>
+			</view>
+			<view
+			    class="u-alert__close"
+			    v-if="closable"
+			    @tap.stop="closeHandler"
+			>
+				<u-icon
+				    name="close"
+				    :color="iconColor"
+				    size="15"
+				></u-icon>
+			</view>
+		</view>
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * Alert  警告提示
+	 * @description 警告提示,展现需要关注的信息。
+	 * @tutorial https://www.uviewui.com/components/alertTips.html
+	 * 
+	 * @property {String}			title       显示的文字 
+	 * @property {String}			type        使用预设的颜色  (默认 'warning' )
+	 * @property {String}			description 辅助性文字,颜色比title浅一点,字号也小一点,可选  
+	 * @property {Boolean}			closable    关闭按钮(默认为叉号icon图标)  (默认 false )
+	 * @property {Boolean}			showIcon    是否显示左边的辅助图标   ( 默认 false )
+	 * @property {String}			effect      多图时,图片缩放裁剪的模式  (默认 'light' )
+	 * @property {Boolean}			center		文字是否居中  (默认 false )
+	 * @property {String | Number}	fontSize    字体大小  (默认 14 )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @event    {Function}        click       点击组件时触发
+	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 */
+	export default {
+		name: 'u-alert',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				show: true
+			}
+		},
+		computed: {
+			iconColor() {
+				return this.effect === 'light' ? this.type : '#fff'
+			},
+			// 不同主题对应不同的图标
+			iconName() {
+				switch (this.type) {
+					case 'success':
+						return 'checkmark-circle-fill';
+						break;
+					case 'error':
+						return 'close-circle-fill';
+						break;
+					case 'warning':
+						return 'error-circle-fill';
+						break;
+					case 'info':
+						return 'info-circle-fill';
+						break;
+					case 'primary':
+						return 'more-circle-fill';
+						break;
+					default: 
+						return 'error-circle-fill';
+				}
+			}
+		},
+		methods: {
+			// 点击内容
+			clickHandler() {
+				this.$emit('click')
+			},
+			// 点击关闭按钮
+			closeHandler() {
+				this.show = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-alert {
+		position: relative;
+		background-color: $u-primary;
+		padding: 8px 10px;
+		@include flex(row);
+		align-items: center;
+		border-top-left-radius: 4px;
+		border-top-right-radius: 4px;
+		border-bottom-left-radius: 4px;
+		border-bottom-right-radius: 4px;
+
+		&--primary--dark {
+			background-color: $u-primary;
+		}
+
+		&--primary--light {
+			background-color: #ecf5ff;
+		}
+
+		&--error--dark {
+			background-color: $u-error;
+		}
+
+		&--error--light {
+			background-color: #FEF0F0;
+		}
+
+		&--success--dark {
+			background-color: $u-success;
+		}
+
+		&--success--light {
+			background-color: #f5fff0;
+		}
+
+		&--warning--dark {
+			background-color: $u-warning;
+		}
+
+		&--warning--light {
+			background-color: #FDF6EC;
+		}
+
+		&--info--dark {
+			background-color: $u-info;
+		}
+
+		&--info--light {
+			background-color: #f4f4f5;
+		}
+
+		&__icon {
+			margin-right: 5px;
+		}
+
+		&__content {
+			@include flex(column);
+			flex: 1;
+
+			&__title {
+				color: $u-main-color;
+				font-size: 14px;
+				font-weight: bold;
+				color: #fff;
+				margin-bottom: 2px;
+			}
+
+			&__desc {
+				color: $u-main-color;
+				font-size: 14px;
+				flex-wrap: wrap;
+				color: #fff;
+			}
+		}
+
+		&__title--dark,
+		&__desc--dark {
+			color: #FFFFFF;
+		}
+
+		&__text--primary--light,
+		&__text--primary--light {
+			color: $u-primary;
+		}
+
+		&__text--success--light,
+		&__text--success--light {
+			color: $u-success;
+		}
+
+		&__text--warning--light,
+		&__text--warning--light {
+			color: $u-warning;
+		}
+
+		&__text--error--light,
+		&__text--error--light {
+			color: $u-error;
+		}
+
+		&__text--info--light,
+		&__text--info--light {
+			color: $u-info;
+		}
+
+		&__close {
+			position: absolute;
+			top: 11px;
+			right: 10px;
+		}
+	}
+</style>

+ 52 - 0
uni_modules/uview-ui/components/u-avatar-group/props.js

@@ -0,0 +1,52 @@
+export default {
+    props: {
+        // 头像图片组
+        urls: {
+            type: Array,
+            default: uni.$u.props.avatarGroup.urls
+        },
+        // 最多展示的头像数量
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.maxCount
+        },
+        // 头像形状
+        shape: {
+            type: String,
+            default: uni.$u.props.avatarGroup.shape
+        },
+        // 图片裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatarGroup.mode
+        },
+        // 超出maxCount时是否显示查看更多的提示
+        showMore: {
+            type: Boolean,
+            default: uni.$u.props.avatarGroup.showMore
+        },
+        // 头像大小
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatarGroup.size
+        },
+        // 指定从数组的对象元素中读取哪个属性作为图片地址
+        keyName: {
+            type: String,
+            default: uni.$u.props.avatarGroup.keyName
+        },
+		// 头像之间的遮挡比例
+        gap: {
+            type: [String, Number],
+            validator(value) {
+                return value >= 0 && value <= 1
+            },
+            default: uni.$u.props.avatarGroup.gap
+        },
+		// 需额外显示的值
+		extraValue: {
+			type: [Number, String],
+			default: uni.$u.props.avatarGroup.extraValue
+		}
+    }
+}

+ 103 - 0
uni_modules/uview-ui/components/u-avatar-group/u-avatar-group.vue

@@ -0,0 +1,103 @@
+<template>
+	<view class="u-avatar-group">
+		<view
+		    class="u-avatar-group__item"
+		    v-for="(item, index) in showUrl"
+		    :key="index"
+		    :style="{
+				marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap)
+			}"
+		>
+			<u-avatar
+			    :size="size"
+			    :shape="shape"
+			    :mode="mode"
+			    :src="$u.test.object(item) ? keyName && item[keyName] || item.url : item"
+			></u-avatar>
+			<view
+			    class="u-avatar-group__item__show-more"
+			    v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
+				@tap="clickHandler"
+			>
+				<u--text
+				    color="#ffffff"
+				    :size="size * 0.4"
+				    :text="`+${extraValue || urls.length - showUrl.length}`"
+					align="center"
+					customStyle="justify-content: center"
+				></u--text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * AvatarGroup  头像组
+	 * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+	 * @tutorial https://www.uviewui.com/components/avatar.html
+	 * 
+	 * @property {Array}           urls     头像图片组 (默认 [] )
+	 * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
+	 * @property {String}          shape    头像形状( 'circle' (默认) | 'square' )
+	 * @property {String}          mode     图片裁剪模式(默认 'scaleToFill' )
+	 * @property {Boolean}         showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+	 * @property {String | Number} size      头像大小 (默认 40 )
+	 * @property {String}          keyName  指定从数组的对象元素中读取哪个属性作为图片地址 
+	 * @property {String | Number} gap      头像之间的遮挡比例(0.4代表遮挡40%)  (默认 0.5 )
+	 * @property {String | Number} extraValue  需额外显示的值
+	 * @event    {Function}        showMore 头像组更多点击
+	 * @example  <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
+	 */
+	export default {
+		name: 'u-avatar-group',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+
+			}
+		},
+		computed: {
+			showUrl() {
+				return this.urls.slice(0, this.maxCount)
+			}
+		},
+		methods: {
+			clickHandler() {
+				this.$emit('showMore')
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-avatar-group {
+		@include flex;
+
+		&__item {
+			margin-left: -10px;
+			position: relative;
+
+			&--no-indent {
+				// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
+				margin-left: 0;
+			}
+
+			&__show-more {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				background-color: rgba(0, 0, 0, 0.3);
+				@include flex;
+				align-items: center;
+				justify-content: center;
+				border-radius: 100px;
+			}
+		}
+	}
+</style>

+ 78 - 0
uni_modules/uview-ui/components/u-avatar/props.js

@@ -0,0 +1,78 @@
+export default {
+    props: {
+        // 头像图片路径(不能为相对路径)
+        src: {
+            type: String,
+            default: uni.$u.props.avatar.src
+        },
+        // 头像形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: uni.$u.props.avatar.shape
+        },
+        // 头像尺寸
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.size
+        },
+        // 裁剪模式
+        mode: {
+            type: String,
+            default: uni.$u.props.avatar.mode
+        },
+        // 显示的文字
+        text: {
+            type: String,
+            default: uni.$u.props.avatar.text
+        },
+        // 背景色
+        bgColor: {
+            type: String,
+            default: uni.$u.props.avatar.bgColor
+        },
+        // 文字颜色
+        color: {
+            type: String,
+            default: uni.$u.props.avatar.color
+        },
+        // 文字大小
+        fontSize: {
+            type: [String, Number],
+            default: uni.$u.props.avatar.fontSize
+        },
+        // 显示的图标
+        icon: {
+            type: String,
+            default: uni.$u.props.avatar.icon
+        },
+        // 显示小程序头像,只对百度,微信,QQ小程序有效
+        mpAvatar: {
+            type: Boolean,
+            default: uni.$u.props.avatar.mpAvatar
+        },
+        // 是否使用随机背景色
+        randomBgColor: {
+            type: Boolean,
+            default: uni.$u.props.avatar.randomBgColor
+        },
+        // 加载失败的默认头像(组件有内置默认图片)
+        defaultUrl: {
+            type: String,
+            default: uni.$u.props.avatar.defaultUrl
+        },
+        // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+        colorIndex: {
+            type: [String, Number],
+            // 校验参数规则,索引在0-19之间
+            validator(n) {
+                return uni.$u.test.range(n, [0, 19]) || n === ''
+            },
+            default: uni.$u.props.avatar.colorIndex
+        },
+        // 组件标识符
+        name: {
+            type: String,
+            default: uni.$u.props.avatar.name
+        }
+    }
+}

Файловите разлики са ограничени, защото са твърде много
+ 172 - 0
uni_modules/uview-ui/components/u-avatar/u-avatar.vue


+ 54 - 0
uni_modules/uview-ui/components/u-back-top/props.js

@@ -0,0 +1,54 @@
+export default {
+    props: {
+        // 返回顶部的形状,circle-圆形,square-方形
+        mode: {
+            type: String,
+            default: uni.$u.props.backtop.mode
+        },
+        // 自定义图标
+        icon: {
+            type: String,
+            default: uni.$u.props.backtop.icon
+        },
+        // 提示文字
+        text: {
+            type: String,
+            default: uni.$u.props.backtop.text
+        },
+        // 返回顶部滚动时间
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.duration
+        },
+        // 滚动距离
+        scrollTop: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.scrollTop
+        },
+        // 距离顶部多少距离显示,单位px
+        top: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.top
+        },
+        // 返回顶部按钮到底部的距离,单位px
+        bottom: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.bottom
+        },
+        // 返回顶部按钮到右边的距离,单位px
+        right: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.right
+        },
+        // 层级
+        zIndex: {
+            type: [String, Number],
+            default: uni.$u.props.backtop.zIndex
+        },
+        // 图标的样式,对象形式
+        iconStyle: {
+            type: Object,
+            default: uni.$u.props.backtop.iconStyle
+        }
+    }
+}

+ 129 - 0
uni_modules/uview-ui/components/u-back-top/u-back-top.vue

@@ -0,0 +1,129 @@
+<template>
+	<u-transition
+	    mode="fade"
+	    :customStyle="backTopStyle"
+	    :show="show"
+	>
+		<view
+		    class="u-back-top"
+			:style="[contentStyle]"
+		    v-if="!$slots.default && !$slots.$default"
+			@click="backToTop"
+		>
+			<u-icon
+			    :name="icon"
+			    :custom-style="iconStyle"
+			></u-icon>
+			<text
+			    v-if="text"
+			    class="u-back-top__text"
+			>{{text}}</text>
+		</view>
+		<slot v-else />
+	</u-transition>
+</template>
+
+<script>
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom')
+	// #endif
+	/**
+	 * backTop 返回顶部
+	 * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+	 * @tutorial https://uviewui.com/components/backTop.html
+	 * 
+	 * @property {String}			mode  		返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
+	 * @property {String} 			icon 		自定义图标 (默认 'arrow-upward' ) 见官方文档示例
+	 * @property {String} 			text 		提示文字 
+	 * @property {String | Number}  duration	返回顶部滚动时间 (默认 100)
+	 * @property {String | Number}  scrollTop	滚动距离 (默认 0 )
+	 * @property {String | Number}  top  		距离顶部多少距离显示,单位px (默认 400 )
+	 * @property {String | Number}  bottom  	返回顶部按钮到底部的距离,单位px (默认 100 )
+	 * @property {String | Number}  right  		返回顶部按钮到右边的距离,单位px (默认 20 )
+	 * @property {String | Number}  zIndex 		层级   (默认 9 )
+	 * @property {Object<Object>}  	iconStyle 	图标的样式,对象形式   (默认 {color: '#909399',fontSize: '19px'})
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <u-back-top :scrollTop="scrollTop"></u-back-top>
+	 */
+	export default {
+		name: 'u-back-top',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
+		computed: {
+			backTopStyle() {
+				// 动画组件样式
+				const style = {
+					bottom: uni.$u.addUnit(this.bottom),
+					right: uni.$u.addUnit(this.right),
+					width: '40px',
+					height: '40px',
+					position: 'fixed',
+					zIndex: 10,
+				}
+				return style
+			},
+			show() {
+				return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top)
+			},
+			contentStyle() {
+				const style = {}
+				let radius = 0
+				// 是否圆形
+				if(this.mode === 'circle') {
+					radius = '100px'
+				} else {
+					radius = '4px'
+				}
+				// 为了兼容安卓nvue,只能这么分开写
+				style.borderTopLeftRadius = radius
+				style.borderTopRightRadius = radius
+				style.borderBottomLeftRadius = radius
+				style.borderBottomRightRadius = radius
+				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
+			}
+		},
+		methods: {
+			backToTop() {
+				// #ifdef APP-NVUE
+				if (!this.$parent.$refs['u-back-top']) {
+					uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
+				}
+				dom.scrollToElement(this.$parent.$refs['u-back-top'], {
+					offset: 0
+				})
+				// #endif
+				
+				// #ifndef APP-NVUE
+				uni.pageScrollTo({
+					scrollTop: 0,
+					duration: this.duration
+				});
+				// #endif
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '../../libs/css/components.scss';
+     $u-back-top-flex:1 !default;
+     $u-back-top-height:100% !default;
+     $u-back-top-background-color:#E1E1E1 !default;
+     $u-back-top-tips-font-size:12px !default;
+	.u-back-top {
+		@include flex;
+		flex-direction: column;
+		align-items: center;
+		flex:$u-back-top-flex;
+		height: $u-back-top-height;
+		justify-content: center;
+		background-color: $u-back-top-background-color;
+
+		&__tips {
+			font-size:$u-back-top-tips-font-size;
+			transform: scale(0.8);
+		}
+	}
+</style>

+ 72 - 0
uni_modules/uview-ui/components/u-badge/props.js

@@ -0,0 +1,72 @@
+export default {
+    props: {
+        // 是否显示圆点
+        isDot: {
+            type: Boolean,
+            default: uni.$u.props.badge.isDot
+        },
+        // 显示的内容
+        value: {
+            type: [Number, String],
+            default: uni.$u.props.badge.value
+        },
+        // 是否显示
+        show: {
+            type: Boolean,
+            default: uni.$u.props.badge.show
+        },
+        // 最大值,超过最大值会显示 '{max}+'
+        max: {
+            type: [Number, String],
+            default: uni.$u.props.badge.max
+        },
+        // 主题类型,error|warning|success|primary
+        type: {
+            type: String,
+            default: uni.$u.props.badge.type
+        },
+        // 当数值为 0 时,是否展示 Badge
+        showZero: {
+            type: Boolean,
+            default: uni.$u.props.badge.showZero
+        },
+        // 背景颜色,优先级比type高,如设置,type参数会失效
+        bgColor: {
+            type: [String, null],
+            default: uni.$u.props.badge.bgColor
+        },
+        // 字体颜色
+        color: {
+            type: [String, null],
+            default: uni.$u.props.badge.color
+        },
+        // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+        shape: {
+            type: String,
+            default: uni.$u.props.badge.shape
+        },
+        // 设置数字的显示方式,overflow|ellipsis|limit
+        // overflow会根据max字段判断,超出显示`${max}+`
+        // ellipsis会根据max判断,超出显示`${max}...`
+        // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
+        numberType: {
+            type: String,
+            default: uni.$u.props.badge.numberType
+        },
+        // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+        offset: {
+            type: Array,
+            default: uni.$u.props.badge.offset
+        },
+        // 是否反转背景和字体颜色
+        inverted: {
+            type: Boolean,
+            default: uni.$u.props.badge.inverted
+        },
+        // 是否绝对定位
+        absolute: {
+            type: Boolean,
+            default: uni.$u.props.badge.absolute
+        }
+    }
+}

+ 171 - 0
uni_modules/uview-ui/components/u-badge/u-badge.vue

@@ -0,0 +1,171 @@
+<template>
+	<text
+		v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
+		:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
+		:style="[$u.addStyle(customStyle), badgeStyle]"
+		class="u-badge"
+	>{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * badge 徽标数
+	 * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+	 * @tutorial https://uviewui.com/components/badge.html
+	 * 
+	 * @property {Boolean} 			isDot 		是否显示圆点 (默认 false )
+	 * @property {String | Number} 	value 		显示的内容
+	 * @property {Boolean} 			show 		是否显示 (默认 true )
+	 * @property {String | Number} 	max 		最大值,超过最大值会显示 '{max}+'  (默认999)
+	 * @property {String} 			type 		主题类型,error|warning|success|primary (默认 'error' )
+	 * @property {Boolean} 			showZero	当数值为 0 时,是否展示 Badge (默认 false )
+	 * @property {String} 			bgColor 	背景颜色,优先级比type高,如设置,type参数会失效
+	 * @property {String} 			color 		字体颜色 (默认 '#ffffff' )
+	 * @property {String} 			shape 		徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
+	 * @property {String} 			numberType	设置数字的显示方式,overflow|ellipsis|limit  (默认 'overflow' )
+	 * @property {Array}} 			offset		设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+	 * @property {Boolean} 			inverted	是否反转背景和字体颜色(默认 false )
+	 * @property {Boolean} 			absolute	是否绝对定位(默认 false )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @example <u-badge :type="type" :count="count"></u-badge>
+	 */
+	export default {
+		name: 'u-badge',
+		mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
+		computed: {
+			// 是否将badge中心与父组件右上角重合
+			boxStyle() {
+				let style = {};
+				return style;
+			},
+			// 整个组件的样式
+			badgeStyle() {
+				const style = {}
+				if(this.color) {
+					style.color = this.color
+				}
+				if (this.bgColor && !this.inverted) {
+					style.backgroundColor = this.bgColor
+				}
+				if (this.absolute) {
+					style.position = 'absolute'
+					// 如果有设置offset参数
+					if(this.offset.length) {
+						// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
+						const top = this.offset[0]
+						const right = this.offset[1] || top
+						style.top = uni.$u.addUnit(top)
+						style.right = uni.$u.addUnit(right)
+					}
+				}
+				return style
+			},
+			showValue() {
+				switch (this.numberType) {
+					case "overflow":
+						return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
+						break;
+					case "ellipsis":
+						return Number(this.value) > Number(this.max) ? "..." : this.value
+						break;
+					case "limit":
+						return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
+							Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
+								1e3 * 100) / 100 + "k" : this.value
+						break;
+					default:
+						return Number(this.value)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	$u-badge-primary: $u-primary !default;
+	$u-badge-error: $u-error !default;
+	$u-badge-success: $u-success !default;
+	$u-badge-info: $u-info !default;
+	$u-badge-warning: $u-warning !default;
+	$u-badge-dot-radius: 100px !default;
+	$u-badge-dot-size: 8px !default;
+	$u-badge-dot-right: 4px !default;
+	$u-badge-dot-top: 0 !default;
+	$u-badge-text-font-size: 11px !default;
+	$u-badge-text-right: 10px !default;
+	$u-badge-text-padding: 2px 5px !default;
+	$u-badge-text-align: center !default;
+	$u-badge-text-color: #FFFFFF !default;
+
+	.u-badge {
+		border-top-right-radius: $u-badge-dot-radius;
+		border-top-left-radius: $u-badge-dot-radius;
+		border-bottom-left-radius: $u-badge-dot-radius;
+		border-bottom-right-radius: $u-badge-dot-radius;
+		@include flex;
+		line-height: $u-badge-text-font-size;
+		text-align: $u-badge-text-align;
+		font-size: $u-badge-text-font-size;
+		color: $u-badge-text-color;
+
+		&--dot {
+			height: $u-badge-dot-size;
+			width: $u-badge-dot-size;
+		}
+		
+		&--inverted {
+			font-size: 13px;
+		}
+		
+		&--not-dot {
+			padding: $u-badge-text-padding;
+		}
+
+		&--horn {
+			border-bottom-left-radius: 0;
+		}
+
+		&--primary {
+			background-color: $u-badge-primary;
+		}
+		
+		&--primary--inverted {
+			color: $u-badge-primary;
+		}
+
+		&--error {
+			background-color: $u-badge-error;
+		}
+		
+		&--error--inverted {
+			color: $u-badge-error;
+		}
+
+		&--success {
+			background-color: $u-badge-success;
+		}
+		
+		&--success--inverted {
+			color: $u-badge-success;
+		}
+
+		&--info {
+			background-color: $u-badge-info;
+		}
+		
+		&--info--inverted {
+			color: $u-badge-info;
+		}
+
+		&--warning {
+			background-color: $u-badge-warning;
+		}
+		
+		&--warning--inverted {
+			color: $u-badge-warning;
+		}
+	}
+</style>

+ 46 - 0
uni_modules/uview-ui/components/u-button/nvue.scss

@@ -0,0 +1,46 @@
+$u-button-active-opacity:0.75 !default;
+$u-button-loading-text-margin-left:4px !default;
+$u-button-text-color: #FFFFFF !default;
+$u-button-text-plain-error-color:$u-error !default;
+$u-button-text-plain-warning-color:$u-warning !default;
+$u-button-text-plain-success-color:$u-success !default;
+$u-button-text-plain-info-color:$u-info !default;
+$u-button-text-plain-primary-color:$u-primary !default;
+.u-button {
+	&--active {
+		opacity: $u-button-active-opacity;
+	}
+	
+	&--active--plain {
+		background-color: rgb(217, 217, 217);
+	}
+	
+	&__loading-text {
+		margin-left:$u-button-loading-text-margin-left;
+	}
+	
+	&__text,
+	&__loading-text {
+		color:$u-button-text-color;
+	}
+	
+	&__text--plain--error {
+		color:$u-button-text-plain-error-color;
+	}
+	
+	&__text--plain--warning {
+		color:$u-button-text-plain-warning-color;
+	}
+	
+	&__text--plain--success{
+		color:$u-button-text-plain-success-color;
+	}
+	
+	&__text--plain--info {
+		color:$u-button-text-plain-info-color;
+	}
+	
+	&__text--plain--primary {
+		color:$u-button-text-plain-primary-color;
+	}
+}

+ 161 - 0
uni_modules/uview-ui/components/u-button/props.js

@@ -0,0 +1,161 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 1.0
+ * @Date         : 2021-08-16 10:04:04
+ * @LastAuthor   : LQ
+ * @lastTime     : 2021-08-16 10:04:24
+ * @FilePath     : /u-view2.0/uview-ui/components/u-button/props.js
+ */
+export default {
+    props: {
+        // 是否细边框
+        hairline: {
+            type: Boolean,
+            default: uni.$u.props.button.hairline
+        },
+        // 按钮的预置样式,info,primary,error,warning,success
+        type: {
+            type: String,
+            default: uni.$u.props.button.type
+        },
+        // 按钮尺寸,large,normal,small,mini
+        size: {
+            type: String,
+            default: uni.$u.props.button.size
+        },
+        // 按钮形状,circle(两边为半圆),square(带圆角)
+        shape: {
+            type: String,
+            default: uni.$u.props.button.shape
+        },
+        // 按钮是否镂空
+        plain: {
+            type: Boolean,
+            default: uni.$u.props.button.plain
+        },
+        // 是否禁止状态
+        disabled: {
+            type: Boolean,
+            default: uni.$u.props.button.disabled
+        },
+        // 是否加载中
+        loading: {
+            type: Boolean,
+            default: uni.$u.props.button.loading
+        },
+        // 加载中提示文字
+        loadingText: {
+            type: [String, Number],
+            default: uni.$u.props.button.loadingText
+        },
+        // 加载状态图标类型
+        loadingMode: {
+            type: String,
+            default: uni.$u.props.button.loadingMode
+        },
+        // 加载图标大小
+        loadingSize: {
+            type: [String, Number],
+            default: uni.$u.props.button.loadingSize
+        },
+        // 开放能力,具体请看uniapp稳定关于button组件部分说明
+        // https://uniapp.dcloud.io/component/button
+        openType: {
+            type: String,
+            default: uni.$u.props.button.openType
+        },
+        // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+        // 取值为submit(提交表单),reset(重置表单)
+        formType: {
+            type: String,
+            default: uni.$u.props.button.formType
+        },
+        // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
+        // 只微信小程序、QQ小程序有效
+        appParameter: {
+            type: String,
+            default: uni.$u.props.button.appParameter
+        },
+        // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
+        hoverStopPropagation: {
+            type: Boolean,
+            default: uni.$u.props.button.hoverStopPropagation
+        },
+        // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
+        lang: {
+            type: String,
+            default: uni.$u.props.button.lang
+        },
+        // 会话来源,open-type="contact"时有效。只微信小程序有效
+        sessionFrom: {
+            type: String,
+            default: uni.$u.props.button.sessionFrom
+        },
+        // 会话内消息卡片标题,open-type="contact"时有效
+        // 默认当前标题,只微信小程序有效
+        sendMessageTitle: {
+            type: String,
+            default: uni.$u.props.button.sendMessageTitle
+        },
+        // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
+        // 默认当前分享路径,只微信小程序有效
+        sendMessagePath: {
+            type: String,
+            default: uni.$u.props.button.sendMessagePath
+        },
+        // 会话内消息卡片图片,open-type="contact"时有效
+        // 默认当前页面截图,只微信小程序有效
+        sendMessageImg: {
+            type: String,
+            default: uni.$u.props.button.sendMessageImg
+        },
+        // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
+        // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
+        showMessageCard: {
+            type: Boolean,
+            default: uni.$u.props.button.showMessageCard
+        },
+        // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+        dataName: {
+            type: String,
+            default: uni.$u.props.button.dataName
+        },
+        // 节流,一定时间内只能触发一次
+        throttleTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.throttleTime
+        },
+        // 按住后多久出现点击态,单位毫秒
+        hoverStartTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.hoverStartTime
+        },
+        // 手指松开后点击态保留时间,单位毫秒
+        hoverStayTime: {
+            type: [String, Number],
+            default: uni.$u.props.button.hoverStayTime
+        },
+        // 按钮文字,之所以通过props传入,是因为slot传入的话
+        // nvue中无法控制文字的样式
+        text: {
+            type: [String, Number],
+            default: uni.$u.props.button.text
+        },
+        // 按钮图标
+        icon: {
+            type: String,
+            default: uni.$u.props.button.icon
+        },
+        // 按钮图标
+        iconColor: {
+            type: String,
+            default: uni.$u.props.button.icon
+        },
+        // 按钮颜色,支持传入linear-gradient渐变色
+        color: {
+            type: String,
+            default: uni.$u.props.button.color
+        }
+    }
+}

+ 490 - 0
uni_modules/uview-ui/components/u-button/u-button.vue

@@ -0,0 +1,490 @@
+<template>
+    <!-- #ifndef APP-NVUE -->
+    <button
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        :form-type="formType"
+        :open-type="openType"
+        :app-parameter="appParameter"
+        :hover-stop-propagation="hoverStopPropagation"
+        :send-message-title="sendMessageTitle"
+        :send-message-path="sendMessagePath"
+        :lang="lang"
+        :data-name="dataName"
+        :session-from="sessionFrom"
+        :send-message-img="sendMessageImg"
+        :show-message-card="showMessageCard"
+        @getphonenumber="getphonenumber"
+        @getuserinfo="getuserinfo"
+        @error="error"
+        @opensetting="opensetting"
+        @launchapp="launchapp"
+        :hover-class="!disabled && !loading ? 'u-button--active' : ''"
+        class="u-button u-reset-button"
+        :style="[baseColor, $u.addStyle(customStyle)]"
+        @tap="clickHandler"
+        :class="bemClass"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="loadingSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[{ fontSize: textSize + 'px' }]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+                :customStyle="{ marginRight: '2px' }"
+            ></u-icon>
+            <slot>
+                <text
+                    class="u-button__text"
+                    :style="[{ fontSize: textSize + 'px' }]"
+                    >{{ text }}</text
+                >
+            </slot>
+        </template>
+    </button>
+    <!-- #endif -->
+
+    <!-- #ifdef APP-NVUE -->
+    <view
+        :hover-start-time="Number(hoverStartTime)"
+        :hover-stay-time="Number(hoverStayTime)"
+        class="u-button"
+        :hover-class="
+            !disabled && !loading && !color && (plain || type === 'info')
+                ? 'u-button--active--plain'
+                : !disabled && !loading && !plain
+                ? 'u-button--active'
+                : ''
+        "
+        @tap="clickHandler"
+        :class="bemClass"
+        :style="[baseColor, $u.addStyle(customStyle)]"
+    >
+        <template v-if="loading">
+            <u-loading-icon
+                :mode="loadingMode"
+                :size="loadingSize * 1.15"
+                :color="loadingColor"
+            ></u-loading-icon>
+            <text
+                class="u-button__loading-text"
+                :style="[nvueTextStyle]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ loadingText || text }}</text
+            >
+        </template>
+        <template v-else>
+            <u-icon
+                v-if="icon"
+                :name="icon"
+                :color="iconColorCom"
+                :size="textSize * 1.35"
+            ></u-icon>
+            <text
+                class="u-button__text"
+                :style="[
+                    {
+                        marginLeft: icon ? '2px' : 0,
+                    },
+                    nvueTextStyle,
+                ]"
+                :class="[plain && `u-button__text--plain--${type}`]"
+                >{{ text }}</text
+            >
+        </template>
+    </view>
+    <!-- #endif -->
+</template>
+
+<script>
+import button from "../../libs/mixin/button.js";
+import openType from "../../libs/mixin/openType.js";
+import props from "./props.js";
+/**
+ * button 按钮
+ * @description Button 按钮
+ * @tutorial https://www.uviewui.com/components/button.html
+ *
+ * @property {Boolean}			hairline				是否显示按钮的细边框 (默认 true )
+ * @property {String}			type					按钮的预置样式,info,primary,error,warning,success (默认 'info' )
+ * @property {String}			size					按钮尺寸,large,normal,mini (默认 normal)
+ * @property {String}			shape					按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
+ * @property {Boolean}			plain					按钮是否镂空,背景色透明 (默认 false)
+ * @property {Boolean}			disabled				是否禁用 (默认 false)
+ * @property {Boolean}			loading					按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
+ * @property {String | Number}	loadingText				加载中提示文字
+ * @property {String}			loadingMode				加载状态图标类型 (默认 'spinner' )
+ * @property {String | Number}	loadingSize				加载图标大小 (默认 15 )
+ * @property {String}			openType				开放能力,具体请看uniapp稳定关于button组件部分说明
+ * @property {String}			formType				用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
+ * @property {String}			appParameter			打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
+ * @property {Boolean}			hoverStopPropagation	指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
+ * @property {String}			lang					指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
+ * @property {String}			sessionFrom				会话来源,openType="contact"时有效
+ * @property {String}			sendMessageTitle		会话内消息卡片标题,openType="contact"时有效
+ * @property {String}			sendMessagePath			会话内消息卡片点击跳转小程序路径,openType="contact"时有效
+ * @property {String}			sendMessageImg			会话内消息卡片图片,openType="contact"时有效
+ * @property {Boolean}			showMessageCard			是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
+ * @property {String}			dataName				额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
+ * @property {String | Number}	throttleTime			节流,一定时间内只能触发一次 (默认 0 )
+ * @property {String | Number}	hoverStartTime			按住后多久出现点击态,单位毫秒 (默认 0 )
+ * @property {String | Number}	hoverStayTime			手指松开后点击态保留时间,单位毫秒 (默认 200 )
+ * @property {String | Number}	text					按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
+ * @property {String}			icon					按钮图标
+ * @property {String}			iconColor				按钮图标颜色
+ * @property {String}			color					按钮颜色,支持传入linear-gradient渐变色
+ * @property {Object}			customStyle				定义需要用到的外部样式
+ *
+ * @event {Function}	click			非禁止并且非加载中,才能点击
+ * @event {Function}	getphonenumber	open-type="getPhoneNumber"时有效
+ * @event {Function}	getuserinfo		用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
+ * @event {Function}	error			当使用开放能力时,发生错误的回调
+ * @event {Function}	opensetting		在打开授权设置页并关闭后回调
+ * @event {Function}	launchapp		打开 APP 成功的回调
+ * @example <u-button>月落</u-button>
+ */
+export default {
+    name: "u-button",
+    // #ifdef MP
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props],
+    // #endif
+    // #ifndef MP
+    mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+    // #endif
+    data() {
+        return {};
+    },
+    computed: {
+        // 生成bem风格的类名
+        bemClass() {
+            // this.bem为一个computed变量,在mixin中
+            if (!this.color) {
+                return this.bem(
+                    "button",
+                    ["type", "shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            } else {
+                // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
+                return this.bem(
+                    "button",
+                    ["shape", "size"],
+                    ["disabled", "plain", "hairline"]
+                );
+            }
+        },
+        loadingColor() {
+            if (this.plain) {
+                // 如果有设置color值,则用color值,否则使用type主题颜色
+                return this.color
+                    ? this.color
+                    : uni.$u.config.color[`u-${this.type}`];
+            }
+            if (this.type === "info") {
+                return "#c9c9c9";
+            }
+            return "rgb(200, 200, 200)";
+        },
+        iconColorCom() {
+            // 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
+            // u-icon的color能接受一个主题颜色的值
+			if (this.iconColor) return this.iconColor;
+			if (this.plain) {
+                return this.color ? this.color : this.type;
+            } else {
+                return this.type === "info" ? "#000000" : "#ffffff";
+            }
+        },
+        baseColor() {
+            let style = {};
+            if (this.color) {
+                // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+                style.color = this.plain ? this.color : "white";
+                if (!this.plain) {
+                    // 非镂空,背景色使用自定义的颜色
+                    style["background-color"] = this.color;
+                }
+                if (this.color.indexOf("gradient") !== -1) {
+                    // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
+                    // weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
+                    // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
+                    style.borderTopWidth = 0;
+                    style.borderRightWidth = 0;
+                    style.borderBottomWidth = 0;
+                    style.borderLeftWidth = 0;
+                    if (!this.plain) {
+                        style.backgroundImage = this.color;
+                    }
+                } else {
+                    // 非渐变色,则设置边框相关的属性
+                    style.borderColor = this.color;
+                    style.borderWidth = "1px";
+                    style.borderStyle = "solid";
+                }
+            }
+            return style;
+        },
+        // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
+        nvueTextStyle() {
+            let style = {};
+            // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
+            if (this.type === "info") {
+                style.color = "#323233";
+            }
+            if (this.color) {
+                style.color = this.plain ? this.color : "white";
+            }
+            style.fontSize = this.textSize + "px";
+            return style;
+        },
+        // 字体大小
+        textSize() {
+            let fontSize = 14,
+                { size } = this;
+            if (size === "large") fontSize = 16;
+            if (size === "normal") fontSize = 14;
+            if (size === "small") fontSize = 12;
+            if (size === "mini") fontSize = 10;
+            return fontSize;
+        },
+    },
+    methods: {
+        clickHandler() {
+            // 非禁止并且非加载中,才能点击
+            if (!this.disabled && !this.loading) {
+				// 进行节流控制,每this.throttle毫秒内,只在开始处执行
+				uni.$u.throttle(() => {
+					this.$emit("click");
+				}, this.throttleTime);
+            }
+        },
+        // 下面为对接uniapp官方按钮开放能力事件回调的对接
+        getphonenumber(res) {
+            this.$emit("getphonenumber", res);
+        },
+        getuserinfo(res) {
+            this.$emit("getuserinfo", res);
+        },
+        error(res) {
+            this.$emit("error", res);
+        },
+        opensetting(res) {
+            this.$emit("opensetting", res);
+        },
+        launchapp(res) {
+            this.$emit("launchapp", res);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../../libs/css/components.scss";
+
+/* #ifndef APP-NVUE */
+@import "./vue.scss";
+/* #endif */
+
+/* #ifdef APP-NVUE */
+@import "./nvue.scss";
+/* #endif */
+
+$u-button-u-button-height: 40px !default;
+$u-button-text-font-size: 15px !default;
+$u-button-loading-text-font-size: 15px !default;
+$u-button-loading-text-margin-left: 4px !default;
+$u-button-large-width: 100% !default;
+$u-button-large-height: 50px !default;
+$u-button-normal-padding: 0 12px !default;
+$u-button-large-padding: 0 15px !default;
+$u-button-normal-font-size: 14px !default;
+$u-button-small-min-width: 60px !default;
+$u-button-small-height: 30px !default;
+$u-button-small-padding: 0px 8px !default;
+$u-button-mini-padding: 0px 8px !default;
+$u-button-small-font-size: 12px !default;
+$u-button-mini-height: 22px !default;
+$u-button-mini-font-size: 10px !default;
+$u-button-mini-min-width: 50px !default;
+$u-button-disabled-opacity: 0.5 !default;
+$u-button-info-color: #323233 !default;
+$u-button-info-background-color: #fff !default;
+$u-button-info-border-color: #ebedf0 !default;
+$u-button-info-border-width: 1px !default;
+$u-button-info-border-style: solid !default;
+$u-button-success-color: #fff !default;
+$u-button-success-background-color: $u-success !default;
+$u-button-success-border-color: $u-button-success-background-color !default;
+$u-button-success-border-width: 1px !default;
+$u-button-success-border-style: solid !default;
+$u-button-primary-color: #fff !default;
+$u-button-primary-background-color: $u-primary !default;
+$u-button-primary-border-color: $u-button-primary-background-color !default;
+$u-button-primary-border-width: 1px !default;
+$u-button-primary-border-style: solid !default;
+$u-button-error-color: #fff !default;
+$u-button-error-background-color: $u-error !default;
+$u-button-error-border-color: $u-button-error-background-color !default;
+$u-button-error-border-width: 1px !default;
+$u-button-error-border-style: solid !default;
+$u-button-warning-color: #fff !default;
+$u-button-warning-background-color: $u-warning !default;
+$u-button-warning-border-color: $u-button-warning-background-color !default;
+$u-button-warning-border-width: 1px !default;
+$u-button-warning-border-style: solid !default;
+$u-button-block-width: 100% !default;
+$u-button-circle-border-top-right-radius: 100px !default;
+$u-button-circle-border-top-left-radius: 100px !default;
+$u-button-circle-border-bottom-left-radius: 100px !default;
+$u-button-circle-border-bottom-right-radius: 100px !default;
+$u-button-square-border-top-right-radius: 3px !default;
+$u-button-square-border-top-left-radius: 3px !default;
+$u-button-square-border-bottom-left-radius: 3px !default;
+$u-button-square-border-bottom-right-radius: 3px !default;
+$u-button-icon-min-width: 1em !default;
+$u-button-plain-background-color: #fff !default;
+$u-button-hairline-border-width: 0.5px !default;
+
+.u-button {
+    height: $u-button-u-button-height;
+    position: relative;
+    align-items: center;
+    justify-content: center;
+    @include flex;
+    /* #ifndef APP-NVUE */
+    box-sizing: border-box;
+    /* #endif */
+    flex-direction: row;
+
+    &__text {
+        font-size: $u-button-text-font-size;
+    }
+
+    &__loading-text {
+        font-size: $u-button-loading-text-font-size;
+        margin-left: $u-button-loading-text-margin-left;
+    }
+
+    &--large {
+        /* #ifndef APP-NVUE */
+        width: $u-button-large-width;
+        /* #endif */
+        height: $u-button-large-height;
+        padding: $u-button-large-padding;
+    }
+
+    &--normal {
+        padding: $u-button-normal-padding;
+        font-size: $u-button-normal-font-size;
+    }
+
+    &--small {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-small-min-width;
+        /* #endif */
+        height: $u-button-small-height;
+        padding: $u-button-small-padding;
+        font-size: $u-button-small-font-size;
+    }
+
+    &--mini {
+        height: $u-button-mini-height;
+        font-size: $u-button-mini-font-size;
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-mini-min-width;
+        /* #endif */
+        padding: $u-button-mini-padding;
+    }
+
+    &--disabled {
+        opacity: $u-button-disabled-opacity;
+    }
+
+    &--info {
+        color: $u-button-info-color;
+        background-color: $u-button-info-background-color;
+        border-color: $u-button-info-border-color;
+        border-width: $u-button-info-border-width;
+        border-style: $u-button-info-border-style;
+    }
+
+    &--success {
+        color: $u-button-success-color;
+        background-color: $u-button-success-background-color;
+        border-color: $u-button-success-border-color;
+        border-width: $u-button-success-border-width;
+        border-style: $u-button-success-border-style;
+    }
+
+    &--primary {
+        color: $u-button-primary-color;
+        background-color: $u-button-primary-background-color;
+        border-color: $u-button-primary-border-color;
+        border-width: $u-button-primary-border-width;
+        border-style: $u-button-primary-border-style;
+    }
+
+    &--error {
+        color: $u-button-error-color;
+        background-color: $u-button-error-background-color;
+        border-color: $u-button-error-border-color;
+        border-width: $u-button-error-border-width;
+        border-style: $u-button-error-border-style;
+    }
+
+    &--warning {
+        color: $u-button-warning-color;
+        background-color: $u-button-warning-background-color;
+        border-color: $u-button-warning-border-color;
+        border-width: $u-button-warning-border-width;
+        border-style: $u-button-warning-border-style;
+    }
+
+    &--block {
+        @include flex;
+        width: $u-button-block-width;
+    }
+
+    &--circle {
+        border-top-right-radius: $u-button-circle-border-top-right-radius;
+        border-top-left-radius: $u-button-circle-border-top-left-radius;
+        border-bottom-left-radius: $u-button-circle-border-bottom-left-radius;
+        border-bottom-right-radius: $u-button-circle-border-bottom-right-radius;
+    }
+
+    &--square {
+        border-bottom-left-radius: $u-button-square-border-top-right-radius;
+        border-bottom-right-radius: $u-button-square-border-top-left-radius;
+        border-top-left-radius: $u-button-square-border-bottom-left-radius;
+        border-top-right-radius: $u-button-square-border-bottom-right-radius;
+    }
+
+    &__icon {
+        /* #ifndef APP-NVUE */
+        min-width: $u-button-icon-min-width;
+        line-height: inherit !important;
+        vertical-align: top;
+        /* #endif */
+    }
+
+    &--plain {
+        background-color: $u-button-plain-background-color;
+    }
+
+    &--hairline {
+        border-width: $u-button-hairline-border-width !important;
+    }
+}
+</style>

+ 80 - 0
uni_modules/uview-ui/components/u-button/vue.scss

@@ -0,0 +1,80 @@
+// nvue下hover-class无效
+$u-button-before-top:50% !default;
+$u-button-before-left:50% !default;
+$u-button-before-width:100% !default;
+$u-button-before-height:100% !default;
+$u-button-before-transform:translate(-50%, -50%) !default;
+$u-button-before-opacity:0 !default;
+$u-button-before-background-color:#000 !default;
+$u-button-before-border-color:#000 !default;
+$u-button-active-before-opacity:.15 !default;
+$u-button-icon-margin-left:4px !default;
+$u-button-plain-u-button-info-color:$u-info;
+$u-button-plain-u-button-success-color:$u-success;
+$u-button-plain-u-button-error-color:$u-error;
+$u-button-plain-u-button-warning-color:$u-error;
+
+.u-button {
+	width: 100%;
+	
+	&__text {
+		white-space: nowrap;
+		line-height: 1;
+	}
+	
+	&:before {
+		position: absolute;
+		top:$u-button-before-top;
+		left:$u-button-before-left;
+		width:$u-button-before-width;
+		height:$u-button-before-height;
+		border: inherit;
+		border-radius: inherit;
+		transform:$u-button-before-transform;
+		opacity:$u-button-before-opacity;
+		content: " ";
+		background-color:$u-button-before-background-color;
+		border-color:$u-button-before-border-color;
+	}
+	
+	&--active {
+		&:before {
+			opacity: .15
+		}
+	}
+	
+	&__icon+&__text:not(:empty),
+	&__loading-text {
+		margin-left:$u-button-icon-margin-left;
+	}
+	
+	&--plain {
+		&.u-button--primary {
+			color: $u-primary;
+		}
+	}
+	
+	&--plain {
+		&.u-button--info {
+			color:$u-button-plain-u-button-info-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--success {
+			color:$u-button-plain-u-button-success-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--error {
+			color:$u-button-plain-u-button-error-color;
+		}
+	}
+	
+	&--plain {
+		&.u-button--warning {
+			color:$u-button-plain-u-button-warning-color;
+		}
+	}
+}

+ 99 - 0
uni_modules/uview-ui/components/u-calendar/header.vue

@@ -0,0 +1,99 @@
+<template>
+	<view class="u-calendar-header u-border-bottom">
+		<text
+			class="u-calendar-header__title"
+			v-if="showTitle"
+		>{{ title }}</text>
+		<text
+			class="u-calendar-header__subtitle"
+			v-if="showSubtitle"
+		>{{ subtitle }}</text>
+		<view class="u-calendar-header__weekdays">
+			<text class="u-calendar-header__weekdays__weekday">一</text>
+			<text class="u-calendar-header__weekdays__weekday">二</text>
+			<text class="u-calendar-header__weekdays__weekday">三</text>
+			<text class="u-calendar-header__weekdays__weekday">四</text>
+			<text class="u-calendar-header__weekdays__weekday">五</text>
+			<text class="u-calendar-header__weekdays__weekday">六</text>
+			<text class="u-calendar-header__weekdays__weekday">日</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'u-calendar-header',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin],
+		props: {
+			// 标题
+			title: {
+				type: String,
+				default: ''
+			},
+			// 副标题
+			subtitle: {
+				type: String,
+				default: ''
+			},
+			// 是否显示标题
+			showTitle: {
+				type: Boolean,
+				default: true
+			},
+			// 是否显示副标题
+			showSubtitle: {
+				type: Boolean,
+				default: true
+			},
+		},
+		data() {
+			return {
+
+			}
+		},
+		methods: {
+			name() {
+
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-calendar-header {
+		padding-bottom: 4px;
+
+		&__title {
+			font-size: 16px;
+			color: $u-main-color;
+			text-align: center;
+			height: 42px;
+			line-height: 42px;
+			font-weight: bold;
+		}
+
+		&__subtitle {
+			font-size: 14px;
+			color: $u-main-color;
+			height: 40px;
+			text-align: center;
+			line-height: 40px;
+			font-weight: bold;
+		}
+
+		&__weekdays {
+			@include flex;
+			justify-content: space-between;
+
+			&__weekday {
+				font-size: 13px;
+				color: $u-main-color;
+				line-height: 30px;
+				flex: 1;
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 579 - 0
uni_modules/uview-ui/components/u-calendar/month.vue

@@ -0,0 +1,579 @@
+<template>
+	<view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
+		<view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
+			:ref="`u-calendar-month-${index}`" :id="`month-${index}`">
+			<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
+			<view class="u-calendar-month__days">
+				<view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
+					<text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
+				</view>
+				<view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
+					:style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
+					:class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
+					<view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
+						<text class="u-calendar-month__days__day__select__info"
+							:class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']"
+							:style="[textStyle(item1)]">{{ item1.day }}</text>
+						<text v-if="getBottomInfo(index, index1, item1)"
+							class="u-calendar-month__days__day__select__buttom-info"
+							:class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']"
+							:style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
+						<text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	// 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	import dayjs from '../../libs/util/dayjs.js';
+	export default {
+		name: 'u-calendar-month',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin],
+		props: {
+			// 是否显示月份背景色
+			showMark: {
+				type: Boolean,
+				default: true
+			},
+			// 主题色,对底部按钮和选中日期有效
+			color: {
+				type: String,
+				default: '#3c9cff'
+			},
+			// 月份数据
+			months: {
+				type: Array,
+				default: () => []
+			},
+			// 日期选择类型
+			mode: {
+				type: String,
+				default: 'single'
+			},
+			// 日期行高
+			rowHeight: {
+				type: [String, Number],
+				default: 58
+			},
+			// mode=multiple时,最多可选多少个日期
+			maxCount: {
+				type: [String, Number],
+				default: Infinity
+			},
+			// mode=range时,第一个日期底部的提示文字
+			startText: {
+				type: String,
+				default: '开始'
+			},
+			// mode=range时,最后一个日期底部的提示文字
+			endText: {
+				type: String,
+				default: '结束'
+			},
+			// 默认选中的日期,mode为multiple或range是必须为数组格式
+			defaultDate: {
+				type: [Array, String, Date],
+				default: null
+			},
+			// 最小的可选日期
+			minDate: {
+				type: [String, Number],
+				default: 0
+			},
+			// 最大可选日期
+			maxDate: {
+				type: [String, Number],
+				default: 0
+			},
+			// 如果没有设置maxDate,则往后推多少个月
+			maxMonth: {
+				type: [String, Number],
+				default: 2
+			},
+			// 是否为只读状态,只读状态下禁止选择日期
+			readonly: {
+				type: Boolean,
+				default: uni.$u.props.calendar.readonly
+			},
+			// 日期区间最多可选天数,默认无限制,mode = range时有效
+			maxRange: {
+				type: [Number, String],
+				default: Infinity
+			},
+			// 范围选择超过最多可选天数时的提示文案,mode = range时有效
+			rangePrompt: {
+				type: String,
+				default: ''
+			},
+			// 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+			showRangePrompt: {
+				type: Boolean,
+				default: true
+			},
+			// 是否允许日期范围的起止时间为同一天,mode = range时有效
+			allowSameDay: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				// 每个日期的宽度
+				width: 0,
+				// 当前选中的日期item
+				item: {},
+				selected: []
+			}
+		},
+		watch: {
+			selectedChange: {
+				immediate: true,
+				handler(n) {
+					this.setDefaultDate()
+				}
+			}
+		},
+		computed: {
+			// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+			selectedChange() {
+				return [this.minDate, this.maxDate, this.defaultDate]
+			},
+			dayStyle(index1, index2, item) {
+				return (index1, index2, item) => {
+					const style = {}
+					let week = item.week
+					// 不进行四舍五入的形式保留2位小数
+					const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
+					// 得出每个日期的宽度
+					// #ifdef APP-NVUE
+					style.width = uni.$u.addUnit(dayWidth)
+					// #endif
+					style.height = uni.$u.addUnit(this.rowHeight)
+					if (index2 === 0) {
+						// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
+						week = (week === 0 ? 7 : week) - 1
+						style.marginLeft = uni.$u.addUnit(week * dayWidth)
+					}
+					if (this.mode === 'range') {
+						// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
+						style.paddingLeft = 0
+						style.paddingRight = 0
+						style.paddingBottom = 0
+						style.paddingTop = 0
+					}
+					return style
+				}
+			},
+			daySelectStyle() {
+				return (index1, index2, item) => {
+					let date = dayjs(item.date).format("YYYY-MM-DD"),
+						style = {}
+					// 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断
+					if (this.selected.some(item => this.dateSame(item, date))) {
+						style.backgroundColor = this.color
+					}
+					if (this.mode === 'single') {
+						if (date === this.selected[0]) {
+							// 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+							style.borderTopRightRadius = '3px'
+							style.borderBottomRightRadius = '3px'
+						}
+					} else if (this.mode === 'range') {
+						if (this.selected.length >= 2) {
+							const len = this.selected.length - 1
+							// 第一个日期设置左上角和左下角的圆角
+							if (this.dateSame(date, this.selected[0])) {
+								style.borderTopLeftRadius = '3px'
+								style.borderBottomLeftRadius = '3px'
+							}
+							// 最后一个日期设置右上角和右下角的圆角
+							if (this.dateSame(date, this.selected[len])) {
+								style.borderTopRightRadius = '3px'
+								style.borderBottomRightRadius = '3px'
+							}
+							// 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值
+							if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+									.selected[len]))) {
+								style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90]
+								// 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
+								style.opacity = 0.7
+							}
+						} else if (this.selected.length === 1) {
+							// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
+							// 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+						}
+					} else {
+						if (this.selected.some(item => this.dateSame(item, date))) {
+							style.borderTopLeftRadius = '3px'
+							style.borderBottomLeftRadius = '3px'
+							style.borderTopRightRadius = '3px'
+							style.borderBottomRightRadius = '3px'
+						}
+					}
+					return style
+				}
+			},
+			// 某个日期是否被选中
+			textStyle() {
+				return (item) => {
+					const date = dayjs(item.date).format("YYYY-MM-DD"),
+						style = {}
+					// 选中的日期,提示文字设置白色
+					if (this.selected.some(item => this.dateSame(item, date))) {
+						style.color = '#ffffff'
+					}
+					if (this.mode === 'range') {
+						const len = this.selected.length - 1
+						// 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色
+						if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+								.selected[len]))) {
+							style.color = this.color
+						}
+					}
+					return style
+				}
+			},
+			// 获取底部的提示文字
+			getBottomInfo() {
+				return (index1, index2, item) => {
+					const date = dayjs(item.date).format("YYYY-MM-DD")
+					const bottomInfo = item.bottomInfo
+					// 当为日期范围模式时,且选择的日期个数大于0时
+					if (this.mode === 'range' && this.selected.length > 0) {
+						if (this.selected.length === 1) {
+							// 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始”
+							if (this.dateSame(date, this.selected[0])) return this.startText
+							else return bottomInfo
+						} else {
+							const len = this.selected.length - 1
+							// 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期
+							if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
+								len === 1) {
+								// 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中
+								return `${this.startText}/${this.endText}`
+							} else if (this.dateSame(date, this.selected[0])) {
+								return this.startText
+							} else if (this.dateSame(date, this.selected[len])) {
+								return this.endText
+							} else {
+								return bottomInfo
+							}
+						}
+					} else {
+						return bottomInfo
+					}
+				}
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			init() {
+				// 初始化默认选中
+				this.$emit('monthSelected', this.selected)
+				this.$nextTick(() => {
+					// 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度
+					// 因为nvue下,$nextTick并不是100%可靠的
+					uni.$u.sleep(10).then(() => {
+						this.getWrapperWidth()
+						this.getMonthRect()
+					})
+				})
+			},
+			// 判断两个日期是否相等
+			dateSame(date1, date2) {
+				return dayjs(date1).isSame(dayjs(date2))
+			},
+			// 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度
+			getWrapperWidth() {
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
+					this.width = res.size.width
+				})
+				// #endif
+				// #ifndef APP-NVUE
+				this.$uGetRect('.u-calendar-month-wrapper').then(size => {
+					this.width = size.width
+				})
+				// #endif
+			},
+			getMonthRect() {
+				// 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份
+				const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
+					`u-calendar-month-${index}`))
+				// 一次性返回
+				Promise.all(promiseAllArr).then(
+					sizes => {
+						let height = 1
+						const topArr = []
+						for (let i = 0; i < this.months.length; i++) {
+							// 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份
+							topArr[i] = height
+							height += sizes[i].height
+						}
+						// 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出
+						this.$emit('updateMonthTop', topArr)
+					})
+			},
+			// 获取每个月份区域的尺寸
+			getMonthRectByPromise(el) {
+				// #ifndef APP-NVUE
+				// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html
+				// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
+				return new Promise(resolve => {
+					this.$uGetRect(`.${el}`).then(size => {
+						resolve(size)
+					})
+				})
+				// #endif
+
+				// #ifdef APP-NVUE
+				// nvue下,使用dom模块查询元素高度
+				// 返回一个promise,让调用此方法的主体能使用then回调
+				return new Promise(resolve => {
+					dom.getComponentRect(this.$refs[el][0], res => {
+						resolve(res.size)
+					})
+				})
+				// #endif
+			},
+			// 点击某一个日期
+			clickHandler(index1, index2, item) {
+				if (this.readonly) {
+					return;
+				}
+				this.item = item
+				const date = dayjs(item.date).format("YYYY-MM-DD")
+				if (item.disabled) return
+				// 对上一次选择的日期数组进行深度克隆
+				let selected = uni.$u.deepClone(this.selected)
+				if (this.mode === 'single') {
+					// 单选情况下,让数组中的元素为当前点击的日期
+					selected = [date]
+				} else if (this.mode === 'multiple') {
+					if (selected.some(item => this.dateSame(item, date))) {
+						// 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果
+						const itemIndex = selected.findIndex(item => item === date)
+						selected.splice(itemIndex, 1)
+					} else {
+						// 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去
+						if (selected.length < this.maxCount) selected.push(date)
+					}
+				} else {
+					// 选择区间形式
+					if (selected.length === 0 || selected.length >= 2) {
+						// 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期
+						selected = [date]
+					} else if (selected.length === 1) {
+						// 如果已经选择了开始日期
+						const existsDate = selected[0]
+						// 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期
+						if (dayjs(date).isBefore(existsDate)) {
+							selected = [date]
+						} else if (dayjs(date).isAfter(existsDate)) {
+							// 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示
+							if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) {
+								if(this.rangePrompt) {
+									uni.$u.toast(this.rangePrompt)
+								} else {
+									uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`)
+								}
+								return
+							}
+							// 如果当前日期大于已有日期,将当前的添加到数组尾部
+							selected.push(date)
+							const startDate = selected[0]
+							const endDate = selected[1]
+							const arr = []
+							let i = 0
+							do {
+								// 将开始和结束日期之间的日期添加到数组中
+								arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
+								i++
+								// 累加的日期小于结束日期时,继续下一次的循环
+							} while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
+							// 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来
+							arr.push(endDate)
+							selected = arr
+						} else {
+							// 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己
+							if (selected[0] === date && !this.allowSameDay) return
+							selected.push(date)
+						}
+					}
+				}
+				this.setSelected(selected)
+			},
+			// 设置默认日期
+			setDefaultDate() {
+				if (!this.defaultDate) {
+					// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+					const selected = [dayjs().format("YYYY-MM-DD")]
+					return this.setSelected(selected, false)
+				}
+				let defaultDate = []
+				const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
+				const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
+				if (this.mode === 'single') {
+					// 单选模式,可以是字符串或数组,Date对象等
+					if (!uni.$u.test.array(this.defaultDate)) {
+						defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
+					} else {
+						defaultDate = [this.defaultDate[0]]
+					}
+				} else {
+					// 如果为非数组,则不执行
+					if (!uni.$u.test.array(this.defaultDate)) return
+					defaultDate = this.defaultDate
+				}
+				// 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素
+				defaultDate = defaultDate.filter(item => {
+					return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
+						maxDate).add(1, 'day'))
+				})
+				this.setSelected(defaultDate, false)
+			},
+			setSelected(selected, event = true) {
+				this.selected = selected
+				event && this.$emit('monthSelected', this.selected)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-calendar-month-wrapper {
+		margin-top: 4px;
+	}
+
+	.u-calendar-month {
+
+		&__title {
+			font-size: 14px;
+			line-height: 42px;
+			height: 42px;
+			color: $u-main-color;
+			text-align: center;
+			font-weight: bold;
+		}
+
+		&__days {
+			position: relative;
+			@include flex;
+			flex-wrap: wrap;
+
+			&__month-mark-wrapper {
+				position: absolute;
+				top: 0;
+				bottom: 0;
+				left: 0;
+				right: 0;
+				@include flex;
+				justify-content: center;
+				align-items: center;
+
+				&__text {
+					font-size: 155px;
+					color: rgba(231, 232, 234, 0.83);
+				}
+			}
+
+			&__day {
+				@include flex;
+				padding: 2px;
+				/* #ifndef APP-NVUE */
+				// vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移
+				width: calc(100% / 7);
+				box-sizing: border-box;
+				/* #endif */
+
+				&__select {
+					flex: 1;
+					@include flex;
+					align-items: center;
+					justify-content: center;
+					position: relative;
+
+					&__dot {
+						width: 7px;
+						height: 7px;
+						border-radius: 100px;
+						background-color: $u-error;
+						position: absolute;
+						top: 12px;
+						right: 7px;
+					}
+
+					&__buttom-info {
+						color: $u-content-color;
+						text-align: center;
+						position: absolute;
+						bottom: 5px;
+						font-size: 10px;
+						text-align: center;
+						left: 0;
+						right: 0;
+
+						&--selected {
+							color: #ffffff;
+						}
+
+						&--disabled {
+							color: #cacbcd;
+						}
+					}
+
+					&__info {
+						text-align: center;
+						font-size: 16px;
+
+						&--selected {
+							color: #ffffff;
+						}
+
+						&--disabled {
+							color: #cacbcd;
+						}
+					}
+
+					&--selected {
+						background-color: $u-primary;
+						@include flex;
+						justify-content: center;
+						align-items: center;
+						flex: 1;
+						border-radius: 3px;
+					}
+
+					&--range-selected {
+						opacity: 0.3;
+						border-radius: 0;
+					}
+
+					&--range-start-selected {
+						border-top-right-radius: 0;
+						border-bottom-right-radius: 0;
+					}
+
+					&--range-end-selected {
+						border-top-left-radius: 0;
+						border-bottom-left-radius: 0;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 144 - 0
uni_modules/uview-ui/components/u-calendar/props.js

@@ -0,0 +1,144 @@
+export default {
+    props: {
+        // 日历顶部标题
+        title: {
+            type: String,
+            default: uni.$u.props.calendar.title
+        },
+        // 是否显示标题
+        showTitle: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showTitle
+        },
+        // 是否显示副标题
+        showSubtitle: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showSubtitle
+        },
+        // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围
+        mode: {
+            type: String,
+            default: uni.$u.props.calendar.mode
+        },
+        // mode=range时,第一个日期底部的提示文字
+        startText: {
+            type: String,
+            default: uni.$u.props.calendar.startText
+        },
+        // mode=range时,最后一个日期底部的提示文字
+        endText: {
+            type: String,
+            default: uni.$u.props.calendar.endText
+        },
+        // 自定义列表
+        customList: {
+            type: Array,
+            default: uni.$u.props.calendar.customList
+        },
+        // 主题色,对底部按钮和选中日期有效
+        color: {
+            type: String,
+            default: uni.$u.props.calendar.color
+        },
+        // 最小的可选日期
+        minDate: {
+            type: [String, Number],
+            default: uni.$u.props.calendar.minDate
+        },
+        // 最大可选日期
+        maxDate: {
+            type: [String, Number],
+            default: uni.$u.props.calendar.maxDate
+        },
+        // 默认选中的日期,mode为multiple或range是必须为数组格式
+        defaultDate: {
+            type: [Array, String, Date, null],
+            default: uni.$u.props.calendar.defaultDate
+        },
+        // mode=multiple时,最多可选多少个日期
+        maxCount: {
+            type: [String, Number],
+            default: uni.$u.props.calendar.maxCount
+        },
+        // 日期行高
+        rowHeight: {
+            type: [String, Number],
+            default: uni.$u.props.calendar.rowHeight
+        },
+        // 日期格式化函数
+        formatter: {
+            type: [Function, null],
+            default: uni.$u.props.calendar.formatter
+        },
+        // 是否显示农历
+        showLunar: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showLunar
+        },
+        // 是否显示月份背景色
+        showMark: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showMark
+        },
+        // 确定按钮的文字
+        confirmText: {
+            type: String,
+            default: uni.$u.props.calendar.confirmText
+        },
+        // 确认按钮处于禁用状态时的文字
+        confirmDisabledText: {
+            type: String,
+            default: uni.$u.props.calendar.confirmDisabledText
+        },
+        // 是否显示日历弹窗
+        show: {
+            type: Boolean,
+            default: uni.$u.props.calendar.show
+        },
+        // 是否允许点击遮罩关闭日历
+        closeOnClickOverlay: {
+            type: Boolean,
+            default: uni.$u.props.calendar.closeOnClickOverlay
+        },
+        // 是否为只读状态,只读状态下禁止选择日期
+        readonly: {
+            type: Boolean,
+            default: uni.$u.props.calendar.readonly
+        },
+        // 	是否展示确认按钮
+        showConfirm: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showConfirm
+        },
+        // 日期区间最多可选天数,默认无限制,mode = range时有效
+        maxRange: {
+            type: [Number, String],
+            default: uni.$u.props.calendar.maxRange
+        },
+        // 范围选择超过最多可选天数时的提示文案,mode = range时有效
+        rangePrompt: {
+            type: String,
+            default: uni.$u.props.calendar.rangePrompt
+        },
+        // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+        showRangePrompt: {
+            type: Boolean,
+            default: uni.$u.props.calendar.showRangePrompt
+        },
+        // 是否允许日期范围的起止时间为同一天,mode = range时有效
+        allowSameDay: {
+            type: Boolean,
+            default: uni.$u.props.calendar.allowSameDay
+        },
+		// 圆角值
+		round: {
+		    type: [Boolean, String, Number],
+		    default: uni.$u.props.calendar.round
+		},
+		// 最多展示月份数量
+		monthNum: {
+			type: [Number, String],
+			default: 3
+		}	
+    }
+}

+ 384 - 0
uni_modules/uview-ui/components/u-calendar/u-calendar.vue

@@ -0,0 +1,384 @@
+<template>
+	<u-popup
+		:show="show"
+		mode="bottom"
+		closeable
+		@close="close"
+		:round="round"
+		:closeOnClickOverlay="closeOnClickOverlay"
+	>
+		<view class="u-calendar">
+			<uHeader
+				:title="title"
+				:subtitle="subtitle"
+				:showSubtitle="showSubtitle"
+				:showTitle="showTitle"
+			></uHeader>
+			<scroll-view
+				:style="{
+                    height: $u.addUnit(listHeight)
+                }"
+				scroll-y
+				@scroll="onScroll"
+				:scroll-top="scrollTop"
+				:scrollIntoView="scrollIntoView"
+			>
+				<uMonth
+					:color="color"
+					:rowHeight="rowHeight"
+					:showMark="showMark"
+					:months="months"
+					:mode="mode"
+					:maxCount="maxCount"
+					:startText="startText"
+					:endText="endText"
+					:defaultDate="defaultDate"
+					:minDate="innerMinDate"
+					:maxDate="innerMaxDate"
+					:maxMonth="monthNum"
+					:readonly="readonly"
+					:maxRange="maxRange"
+					:rangePrompt="rangePrompt"
+					:showRangePrompt="showRangePrompt"
+					:allowSameDay="allowSameDay"
+					ref="month"
+					@monthSelected="monthSelected"
+					@updateMonthTop="updateMonthTop"
+				></uMonth>
+			</scroll-view>
+			<slot name="footer" v-if="showConfirm">
+				<view class="u-calendar__confirm">
+					<u-button
+						shape="circle"
+						:text="
+                            buttonDisabled ? confirmDisabledText : confirmText
+                        "
+						:color="color"
+						@click="confirm"
+						:disabled="buttonDisabled"
+					></u-button>
+				</view>
+			</slot>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import uHeader from './header.vue'
+import uMonth from './month.vue'
+import props from './props.js'
+import util from './util.js'
+import dayjs from '../../libs/util/dayjs.js'
+import Calendar from '../../libs/util/calendar.js'
+/**
+ * Calendar 日历
+ * @description  此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
+ * @tutorial https://www.uviewui.com/components/calendar.html
+ *
+ * @property {String}				title				标题内容 (默认 日期选择 )
+ * @property {Boolean}				showTitle			是否显示标题  (默认 true )
+ * @property {Boolean}				showSubtitle		是否显示副标题	(默认 true )
+ * @property {String}				mode				日期类型选择  single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' )
+ * @property {String}				startText			mode=range时,第一个日期底部的提示文字  (默认 '开始' )
+ * @property {String}				endText				mode=range时,最后一个日期底部的提示文字 (默认 '结束' )
+ * @property {Array}				customList			自定义列表
+ * @property {String}				color				主题色,对底部按钮和选中日期有效  (默认 ‘#3c9cff' )
+ * @property {String | Number}		minDate				最小的可选日期	 (默认 0 )
+ * @property {String | Number}		maxDate				最大可选日期  (默认 0 )
+ * @property {Array | String| Date}	defaultDate			默认选中的日期,mode为multiple或range是必须为数组格式
+ * @property {String | Number}		maxCount			mode=multiple时,最多可选多少个日期  (默认 	Number.MAX_SAFE_INTEGER  )
+ * @property {String | Number}		rowHeight			日期行高 (默认 56 )
+ * @property {Function}				formatter			日期格式化函数
+ * @property {Boolean}				showLunar			是否显示农历  (默认 false )
+ * @property {Boolean}				showMark			是否显示月份背景色 (默认 true )
+ * @property {String}				confirmText			确定按钮的文字 (默认 '确定' )
+ * @property {String}				confirmDisabledText	确认按钮处于禁用状态时的文字 (默认 '确定' )
+ * @property {Boolean}				show				是否显示日历弹窗 (默认 false )
+ * @property {Boolean}				closeOnClickOverlay	是否允许点击遮罩关闭日历 (默认 false )
+ * @property {Boolean}				readonly	        是否为只读状态,只读状态下禁止选择日期 (默认 false )
+ * @property {String | Number}		maxRange	        日期区间最多可选天数,默认无限制,mode = range时有效
+ * @property {String}				rangePrompt	        范围选择超过最多可选天数时的提示文案,mode = range时有效
+ * @property {Boolean}				showRangePrompt	    范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true )
+ * @property {Boolean}				allowSameDay	    是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false )
+ * @property {Number|String}	    round				圆角值,默认无圆角  (默认 0 )
+ * @property {Number|String}	    monthNum			最多展示的月份数量  (默认 3 )
+ *
+ * @event {Function()} confirm 		点击确定按钮时触发		选择日期相关的返回参数
+ * @event {Function()} close 		日历关闭时触发			可定义页面关闭时的回调事件
+ * @example <u-calendar  :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
+	</u-calendar>
+ * */
+export default {
+	name: 'u-calendar',
+	mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+	components: {
+		uHeader,
+		uMonth
+	},
+	data() {
+		return {
+			// 需要显示的月份的数组
+			months: [],
+			// 在月份滚动区域中,当前视图中月份的index索引
+			monthIndex: 0,
+			// 月份滚动区域的高度
+			listHeight: 0,
+			// month组件中选择的日期数组
+			selected: [],
+			scrollIntoView: '',
+			scrollTop:0,
+			// 过滤处理方法
+			innerFormatter: (value) => value
+		}
+	},
+	watch: {
+		selectedChange: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		},
+		// 打开弹窗时,设置月份数据
+		show: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		}
+	},
+	computed: {
+		// 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理
+		innerMaxDate() {
+			return uni.$u.test.number(this.maxDate)
+				? Number(this.maxDate)
+				: this.maxDate
+		},
+		innerMinDate() {
+			return uni.$u.test.number(this.minDate)
+				? Number(this.minDate)
+				: this.minDate
+		},
+		// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+		selectedChange() {
+			return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
+		},
+		subtitle() {
+			// 初始化时,this.months为空数组,所以需要特别判断处理
+			if (this.months.length) {
+				return `${this.months[this.monthIndex].year}年${
+					this.months[this.monthIndex].month
+				}月`
+			} else {
+				return ''
+			}
+		},
+		buttonDisabled() {
+			// 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态
+			if (this.mode === 'range') {
+				if (this.selected.length <= 1) {
+					return true
+				} else {
+					return false
+				}
+			} else {
+				return false
+			}
+		}
+	},
+	mounted() {
+		this.start = Date.now()
+		this.init()
+	},
+	methods: {
+		// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
+		setFormatter(e) {
+			this.innerFormatter = e
+		},
+		// month组件内部选择日期后,通过事件通知给父组件
+		monthSelected(e) {
+			this.selected = e
+			if (!this.showConfirm) {
+				// 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还
+				if (
+					this.mode === 'multiple' ||
+					this.mode === 'single' ||
+					(this.mode === 'range' && this.selected.length >= 2)
+				) {
+					this.$emit('confirm', this.selected)
+				}
+			}
+		},
+		init() {
+			// 校验maxDate,不能小于minDate
+			if (
+				this.innerMaxDate &&
+				this.innerMinDate &&
+				new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime()
+			) {
+				return uni.$u.error('maxDate不能小于minDate')
+			}
+			// 滚动区域的高度
+			this.listHeight = this.rowHeight * 5 + 30
+			this.setMonth()
+		},
+		close() {
+			this.$emit('close')
+		},
+		// 点击确定按钮
+		confirm() {
+			if (!this.buttonDisabled) {
+				this.$emit('confirm', this.selected)
+			}
+		},
+		// 获得两个日期之间的月份数
+		getMonths(minDate, maxDate) {
+			const minYear = dayjs(minDate).year()
+			const minMonth = dayjs(minDate).month() + 1
+			const maxYear = dayjs(maxDate).year()
+			const maxMonth = dayjs(maxDate).month() + 1
+			return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
+		},
+		// 设置月份数据
+		setMonth() {
+			// 最小日期的毫秒数
+			const minDate = this.innerMinDate || dayjs().valueOf()
+			// 如果没有指定最大日期,则往后推3个月
+			const maxDate =
+				this.innerMaxDate ||
+				dayjs(minDate)
+					.add(this.monthNum - 1, 'month')
+					.valueOf()
+			// 最大最小月份之间的共有多少个月份,
+			const months = uni.$u.range(
+				1,
+				this.monthNum,
+				this.getMonths(minDate, maxDate)
+			)
+			// 先清空数组
+			this.months = []
+			for (let i = 0; i < months; i++) {
+				this.months.push({
+					date: new Array(
+						dayjs(minDate).add(i, 'month').daysInMonth()
+					)
+						.fill(1)
+						.map((item, index) => {
+							// 日期,取值1-31
+							let day = index + 1
+							// 星期,0-6,0为周日
+							const week = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.day()
+							const date = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.format('YYYY-MM-DD')
+							let bottomInfo = ''
+							if (this.showLunar) {
+								// 将日期转为农历格式
+								const lunar = Calendar.solar2lunar(
+									dayjs(date).year(),
+									dayjs(date).month() + 1,
+									dayjs(date).date()
+								)
+								bottomInfo = lunar.IDayCn
+							}
+							let config = {
+								day,
+								week,
+								// 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态
+								disabled:
+									dayjs(date).isBefore(
+										dayjs(minDate).format('YYYY-MM-DD')
+									) ||
+									dayjs(date).isAfter(
+										dayjs(maxDate).format('YYYY-MM-DD')
+									),
+								// 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理
+								date: new Date(date),
+								bottomInfo,
+								dot: false,
+								month:
+									dayjs(minDate).add(i, 'month').month() + 1
+							}
+							const formatter =
+								this.formatter || this.innerFormatter
+							return formatter(config)
+						}),
+					// 当前所属的月份
+					month: dayjs(minDate).add(i, 'month').month() + 1,
+					// 当前年份
+					year: dayjs(minDate).add(i, 'month').year()
+				})
+			}
+
+		},
+		// 滚动到默认设置的月份
+		scrollIntoDefaultMonth(selected) {
+			// 查询默认日期在可选列表的下标
+			const _index = this.months.findIndex(({
+				  year,
+				  month
+			  }) => {
+				month = uni.$u.padZero(month)
+				return `${year}-${month}` === selected
+			})
+			if (_index !== -1) {
+				// #ifndef MP-WEIXIN
+				this.$nextTick(() => {
+					this.scrollIntoView = `month-${_index}`
+				})
+				// #endif
+				// #ifdef MP-WEIXIN
+				this.scrollTop = this.months[_index].top || 0;
+				// #endif
+			}
+		},
+		// scroll-view滚动监听
+		onScroll(event) {
+			// 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值
+			const scrollTop = Math.max(0, event.detail.scrollTop)
+			// 将当前滚动条数值,除以滚动区域的高度,可以得出当前滚动到了哪一个月份的索引
+			for (let i = 0; i < this.months.length; i++) {
+				if (scrollTop >= (this.months[i].top || this.listHeight)) {
+					this.monthIndex = i
+				}
+			}
+		},
+		// 更新月份的top值
+		updateMonthTop(topArr = []) {
+			// 设置对应月份的top值,用于onScroll方法更新月份
+			topArr.map((item, index) => {
+				this.months[index].top = item
+			})
+
+			// 获取默认日期的下标
+			if (!this.defaultDate) {
+				// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+				const selected = dayjs().format("YYYY-MM")
+				this.scrollIntoDefaultMonth(selected)
+				return
+			}
+			let selected = dayjs().format("YYYY-MM");
+			// 单选模式,可以是字符串或数组,Date对象等
+			if (!uni.$u.test.array(this.defaultDate)) {
+				selected = dayjs(this.defaultDate).format("YYYY-MM")
+			} else {
+				selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
+			}
+			this.scrollIntoDefaultMonth(selected)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../libs/css/components.scss';
+
+.u-calendar {
+	&__confirm {
+		padding: 7px 18px;
+	}
+}
+</style>

+ 85 - 0
uni_modules/uview-ui/components/u-calendar/util.js

@@ -0,0 +1,85 @@
+export default {
+    methods: {
+        // 设置月份数据
+        setMonth() {
+            // 月初是周几
+            const day = dayjs(this.date).date(1).day()
+            const start = day == 0 ? 6 : day - 1
+
+            // 本月天数
+            const days = dayjs(this.date).endOf('month').format('D')
+
+            // 上个月天数
+            const prevDays = dayjs(this.date).endOf('month').subtract(1, 'month').format('D')
+
+            // 日期数据
+            const arr = []
+            // 清空表格
+            this.month = []
+
+            // 添加上月数据
+            arr.push(
+                ...new Array(start).fill(1).map((e, i) => {
+                    const day = prevDays - start + i + 1
+
+                    return {
+                        value: day,
+                        disabled: true,
+                        date: dayjs(this.date).subtract(1, 'month').date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 添加本月数据
+            arr.push(
+                ...new Array(days - 0).fill(1).map((e, i) => {
+                    const day = i + 1
+
+                    return {
+                        value: day,
+                        date: dayjs(this.date).date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 添加下个月
+            arr.push(
+                ...new Array(42 - days - start).fill(1).map((e, i) => {
+                    const day = i + 1
+
+                    return {
+                        value: day,
+                        disabled: true,
+                        date: dayjs(this.date).add(1, 'month').date(day).format('YYYY-MM-DD')
+                    }
+                })
+            )
+
+            // 分割数组
+            for (let n = 0; n < arr.length; n += 7) {
+                this.month.push(
+                    arr.slice(n, n + 7).map((e, i) => {
+                        e.index = i + n
+
+                        // 自定义信息
+                        const custom = this.customList.find((c) => c.date == e.date)
+
+                        // 农历
+                        if (this.lunar) {
+                            const {
+                                IDayCn,
+                                IMonthCn
+                            } = this.getLunar(e.date)
+                            e.lunar = IDayCn == '初一' ? IMonthCn : IDayCn
+                        }
+
+                        return {
+                            ...e,
+                            ...custom
+                        }
+                    })
+                )
+            }
+        }
+    }
+}

+ 14 - 0
uni_modules/uview-ui/components/u-car-keyboard/props.js

@@ -0,0 +1,14 @@
+export default {
+    props: {
+        // 是否打乱键盘按键的顺序
+        random: {
+            type: Boolean,
+            default: false
+        },
+        // 输入一个中文后,是否自动切换到英文
+        autoChange: {
+            type: Boolean,
+            default: false
+        }
+    }
+}

+ 311 - 0
uni_modules/uview-ui/components/u-car-keyboard/u-car-keyboard.vue

@@ -0,0 +1,311 @@
+<template>
+	<view
+		class="u-keyboard"
+		@touchmove.stop.prevent="noop"
+	>
+		<view
+			v-for="(group, i) in abc ? engKeyBoardList : areaList"
+			:key="i"
+			class="u-keyboard__button"
+			:index="i"
+			:class="[i + 1 === 4 && 'u-keyboard__button--center']"
+		>
+			<view
+				v-if="i === 3"
+				class="u-keyboard__button__inner-wrapper"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__left"
+					hover-class="u-hover-class"
+					:hover-stay-time="200"
+					@tap="changeCarInputMode"
+				>
+					<text
+						class="u-keyboard__button__inner-wrapper__left__lang"
+						:class="[!abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
+					>中</text>
+					<text class="u-keyboard__button__inner-wrapper__left__line">/</text>
+					<text
+						class="u-keyboard__button__inner-wrapper__left__lang"
+						:class="[abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
+					>英</text>
+				</view>
+			</view>
+			<view
+				class="u-keyboard__button__inner-wrapper"
+				v-for="(item, j) in group"
+				:key="j"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__inner"
+					:hover-stay-time="200"
+					@tap="carInputClick(i, j)"
+					hover-class="u-hover-class"
+				>
+					<text class="u-keyboard__button__inner-wrapper__inner__text">{{ item }}</text>
+				</view>
+			</view>
+			<view
+				v-if="i === 3"
+				@touchstart="backspaceClick"
+				@touchend="clearTimer"
+				class="u-keyboard__button__inner-wrapper"
+			>
+				<view
+					class="u-keyboard__button__inner-wrapper__right"
+					hover-class="u-hover-class"
+					:hover-stay-time="200"
+				>
+					<u-icon
+						size="28"
+						name="backspace"
+						color="#303133"
+					></u-icon>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * keyboard 键盘组件
+	 * @description 此为uView自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。
+	 * @tutorial https://uviewui.com/components/keyboard.html
+	 * @property {Boolean} random 是否打乱键盘的顺序
+	 * @event {Function} change 点击键盘触发
+	 * @event {Function} backspace 点击退格键触发
+	 * @example <u-keyboard ref="uKeyboard" mode="car" v-model="show"></u-keyboard>
+	 */
+	export default {
+		name: "u-keyboard",
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
+				abc: false
+			};
+		},
+		computed: {
+			areaList() {
+				let data = [
+					'京',
+					'沪',
+					'粤',
+					'津',
+					'冀',
+					'豫',
+					'云',
+					'辽',
+					'黑',
+					'湘',
+					'皖',
+					'鲁',
+					'苏',
+					'浙',
+					'赣',
+					'鄂',
+					'桂',
+					'甘',
+					'晋',
+					'陕',
+					'蒙',
+					'吉',
+					'闽',
+					'贵',
+					'渝',
+					'川',
+					'青',
+					'琼',
+					'宁',
+					'挂',
+					'藏',
+					'港',
+					'澳',
+					'新',
+					'使',
+					'学'
+				];
+				let tmp = [];
+				// 打乱顺序
+				if (this.random) data = uni.$u.randomArray(data);
+				// 切割成二维数组
+				tmp[0] = data.slice(0, 10);
+				tmp[1] = data.slice(10, 20);
+				tmp[2] = data.slice(20, 30);
+				tmp[3] = data.slice(30, 36);
+				return tmp;
+			},
+			engKeyBoardList() {
+				let data = [
+					1,
+					2,
+					3,
+					4,
+					5,
+					6,
+					7,
+					8,
+					9,
+					0,
+					'Q',
+					'W',
+					'E',
+					'R',
+					'T',
+					'Y',
+					'U',
+					'I',
+					'O',
+					'P',
+					'A',
+					'S',
+					'D',
+					'F',
+					'G',
+					'H',
+					'J',
+					'K',
+					'L',
+					'Z',
+					'X',
+					'C',
+					'V',
+					'B',
+					'N',
+					'M'
+				];
+				let tmp = [];
+				if (this.random) data = uni.$u.randomArray(data);
+				tmp[0] = data.slice(0, 10);
+				tmp[1] = data.slice(10, 20);
+				tmp[2] = data.slice(20, 30);
+				tmp[3] = data.slice(30, 36);
+				return tmp;
+			}
+		},
+		methods: {
+			// 点击键盘按钮
+			carInputClick(i, j) {
+				let value = '';
+				// 不同模式,获取不同数组的值
+				if (this.abc) value = this.engKeyBoardList[i][j];
+				else value = this.areaList[i][j];
+				// 如果允许自动切换,则将中文状态切换为英文
+				if (!this.abc && this.autoChange) uni.$u.sleep(200).then(() => this.abc = true)
+				this.$emit('change', value);
+			},
+			// 修改汽车牌键盘的输入模式,中文|英文
+			changeCarInputMode() {
+				this.abc = !this.abc;
+			},
+			// 点击退格键
+			backspaceClick() {
+				this.$emit('backspace');
+				clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
+				this.timer = null;
+				this.timer = setInterval(() => {
+					this.$emit('backspace');
+				}, 250);
+			},
+			clearTimer() {
+				clearInterval(this.timer);
+				this.timer = null;
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	$u-car-keyboard-background-color: rgb(224, 228, 230) !default;
+	$u-car-keyboard-padding:6px 0 6px !default;
+	$u-car-keyboard-button-inner-width:64rpx !default;
+	$u-car-keyboard-button-inner-background-color:#FFFFFF !default;
+	$u-car-keyboard-button-height:80rpx !default;
+	$u-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default;
+	$u-car-keyboard-button-border-radius:4px !default;
+	$u-car-keyboard-button-inner-margin:8rpx 5rpx !default;
+	$u-car-keyboard-button-text-font-size:16px !default;
+	$u-car-keyboard-button-text-color:$u-main-color !default;
+	$u-car-keyboard-center-inner-margin: 0 4rpx !default;
+	$u-car-keyboard-special-button-width:134rpx !default;
+	$u-car-keyboard-lang-font-size:16px !default;
+	$u-car-keyboard-lang-color:$u-main-color !default;
+	$u-car-keyboard-active-color:$u-primary !default;
+	$u-car-keyboard-line-font-size:15px !default;
+	$u-car-keyboard-line-color:$u-main-color !default;
+	$u-car-keyboard-line-margin:0 1px !default;
+	$u-car-keyboard-u-hover-class-background-color:#BBBCC6 !default;
+
+	.u-keyboard {
+		@include flex(column);
+		justify-content: space-around;
+		background-color: $u-car-keyboard-background-color;
+		align-items: stretch;
+		padding: $u-car-keyboard-padding;
+
+		&__button {
+			@include flex;
+			justify-content: center;
+			flex: 1;
+			/* #ifndef APP-NVUE */
+			/* #endif */
+
+			&__inner-wrapper {
+				box-shadow: $u-car-keyboard-button-inner-box-shadow;
+				margin: $u-car-keyboard-button-inner-margin;
+				border-radius: $u-car-keyboard-button-border-radius;
+
+				&__inner {
+					@include flex;
+					justify-content: center;
+					align-items: center;
+					width: $u-car-keyboard-button-inner-width;
+					background-color: $u-car-keyboard-button-inner-background-color;
+					height: $u-car-keyboard-button-height;
+					border-radius: $u-car-keyboard-button-border-radius;
+
+					&__text {
+						font-size: $u-car-keyboard-button-text-font-size;
+						color: $u-car-keyboard-button-text-color;
+					}
+				}
+
+				&__left,
+				&__right {
+					border-radius: $u-car-keyboard-button-border-radius;
+					width: $u-car-keyboard-special-button-width;
+					height: $u-car-keyboard-button-height;
+					background-color: $u-car-keyboard-u-hover-class-background-color;
+					@include flex;
+					justify-content: center;
+					align-items: center;
+					box-shadow: $u-car-keyboard-button-inner-box-shadow;
+				}
+
+				&__left {
+					&__line {
+						font-size: $u-car-keyboard-line-font-size;
+						color: $u-car-keyboard-line-color;
+						margin: $u-car-keyboard-line-margin;
+					}
+
+					&__lang {
+						font-size: $u-car-keyboard-lang-font-size;
+						color: $u-car-keyboard-lang-color;
+
+						&--active {
+							color: $u-car-keyboard-active-color;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	.u-hover-class {
+		background-color: $u-car-keyboard-u-hover-class-background-color;
+	}
+</style>

+ 14 - 0
uni_modules/uview-ui/components/u-cell-group/props.js

@@ -0,0 +1,14 @@
+export default {
+    props: {
+        // 分组标题
+        title: {
+            type: String,
+            default: uni.$u.props.cellGroup.title
+        },
+        // 是否显示外边框
+        border: {
+            type: Boolean,
+            default: uni.$u.props.cellGroup.border
+        }
+    }
+}

+ 61 - 0
uni_modules/uview-ui/components/u-cell-group/u-cell-group.vue

@@ -0,0 +1,61 @@
+<template>
+    <view :style="[$u.addStyle(customStyle)]" :class="[customClass]" class="u-cell-group">
+        <view v-if="title" class="u-cell-group__title">
+            <slot name="title">
+				<text class="u-cell-group__title__text">{{ title }}</text>
+			</slot>
+        </view>
+        <view class="u-cell-group__wrapper">
+			<u-line v-if="border"></u-line>
+            <slot />
+        </view>
+    </view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * cellGroup  单元格
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。
+	 * @tutorial https://uviewui.com/components/cell.html
+	 * 
+	 * @property {String}	title		分组标题
+	 * @property {Boolean}	border		是否显示外边框 (默认 true )
+	 * @property {Object}	customStyle	定义需要用到的外部样式
+	 * 
+	 * @event {Function} click 	点击cell列表时触发
+	 * @example <u-cell-group title="设置喜好">
+	 */
+	export default {
+		name: 'u-cell-group',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+	
+	$u-cell-group-title-padding: 16px 16px 8px !default;
+	$u-cell-group-title-font-size: 15px !default;
+	$u-cell-group-title-line-height: 16px !default;
+	$u-cell-group-title-color: $u-main-color !default;
+
+    .u-cell-group {
+		flex: 1;
+		
+        &__title {
+            padding: $u-cell-group-title-padding;
+
+            &__text {
+                font-size: $u-cell-group-title-font-size;
+                line-height: $u-cell-group-title-line-height;
+                color: $u-cell-group-title-color;
+            }
+        }
+		
+		&__wrapper {
+			position: relative;
+		}
+    }
+</style>
+

+ 110 - 0
uni_modules/uview-ui/components/u-cell/props.js

@@ -0,0 +1,110 @@
+export default {
+    props: {
+        // 标题
+        title: {
+            type: [String, Number],
+            default: uni.$u.props.cell.title
+        },
+        // 标题下方的描述信息
+        label: {
+            type: [String, Number],
+            default: uni.$u.props.cell.label
+        },
+        // 右侧的内容
+        value: {
+            type: [String, Number],
+            default: uni.$u.props.cell.value
+        },
+        // 左侧图标名称,或者图片链接(本地文件建议使用绝对地址)
+        icon: {
+            type: String,
+            default: uni.$u.props.cell.icon
+        },
+        // 是否禁用cell
+        disabled: {
+            type: Boolean,
+            default: uni.$u.props.cell.disabled
+        },
+        // 是否显示下边框
+        border: {
+            type: Boolean,
+            default: uni.$u.props.cell.border
+        },
+        // 内容是否垂直居中(主要是针对右侧的value部分)
+        center: {
+            type: Boolean,
+            default: uni.$u.props.cell.center
+        },
+        // 点击后跳转的URL地址
+        url: {
+            type: String,
+            default: uni.$u.props.cell.url
+        },
+        // 链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作
+        linkType: {
+            type: String,
+            default: uni.$u.props.cell.linkType
+        },
+        // 是否开启点击反馈(表现为点击时加上灰色背景)
+        clickable: {
+            type: Boolean,
+            default: uni.$u.props.cell.clickable
+        },
+        // 是否展示右侧箭头并开启点击反馈
+        isLink: {
+            type: Boolean,
+            default: uni.$u.props.cell.isLink
+        },
+        // 是否显示表单状态下的必填星号(此组件可能会内嵌入input组件)
+        required: {
+            type: Boolean,
+            default: uni.$u.props.cell.required
+        },
+        // 右侧的图标箭头
+        rightIcon: {
+            type: String,
+            default: uni.$u.props.cell.rightIcon
+        },
+        // 右侧箭头的方向,可选值为:left,up,down
+        arrowDirection: {
+            type: String,
+            default: uni.$u.props.cell.arrowDirection
+        },
+        // 左侧图标样式
+        iconStyle: {
+            type: [Object, String],
+            default: () => {
+				return uni.$u.props.cell.iconStyle
+			}
+        },
+        // 右侧箭头图标的样式
+        rightIconStyle: {
+            type: [Object, String],
+            default: () => {
+				return uni.$u.props.cell.rightIconStyle
+			}
+        },
+        // 标题的样式
+        titleStyle: {
+            type: [Object, String],
+			default: () => {
+				return uni.$u.props.cell.titleStyle
+			}
+        },
+        // 单位元的大小,可选值为large
+        size: {
+            type: String,
+            default: uni.$u.props.cell.size
+        },
+        // 点击cell是否阻止事件传播
+        stop: {
+            type: Boolean,
+            default: uni.$u.props.cell.stop
+        },
+        // 标识符,cell被点击时返回
+        name: {
+            type: [Number, String],
+            default: uni.$u.props.cell.name
+        }
+    }
+}

+ 229 - 0
uni_modules/uview-ui/components/u-cell/u-cell.vue

@@ -0,0 +1,229 @@
+<template>
+	<view class="u-cell" :class="[customClass]" :style="[$u.addStyle(customStyle)]"
+		:hover-class="(!disabled && (clickable || isLink)) ? 'u-cell--clickable' : ''" :hover-stay-time="250"
+		@tap="clickHandler">
+		<view class="u-cell__body" :class="[ center && 'u-cell--center', size === 'large' && 'u-cell__body--large']">
+			<view class="u-cell__body__content">
+				<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
+					<slot name="icon" v-if="$slots.icon">
+					</slot>
+					<u-icon v-else :name="icon" :custom-style="iconStyle" :size="size === 'large' ? 22 : 18"></u-icon>
+				</view>
+				<view class="u-cell__title">
+					<slot name="title">
+						<text v-if="title" class="u-cell__title-text" :style="[titleTextStyle]"
+							:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{{ title }}</text>
+					</slot>
+					<slot name="label">
+						<text class="u-cell__label" v-if="label"
+							:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__label--large']">{{ label }}</text>
+					</slot>
+				</view>
+			</view>
+			<slot name="value">
+				<text class="u-cell__value"
+					:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__value--large']"
+					v-if="!$u.test.empty(value)">{{ value }}</text>
+			</slot>
+			<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
+				:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
+				<slot name="right-icon" v-if="$slots['right-icon']">
+				</slot>
+				<u-icon v-else :name="rightIcon" :custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
+					:size="size === 'large' ? 18 : 16"></u-icon>
+			</view>
+		</view>
+		<u-line v-if="border"></u-line>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * cell  单元格
+	 * @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。
+	 * @tutorial https://uviewui.com/components/cell.html
+	 * @property {String | Number}	title			标题
+	 * @property {String | Number}	label			标题下方的描述信息
+	 * @property {String | Number}	value			右侧的内容
+	 * @property {String}			icon			左侧图标名称,或者图片链接(本地文件建议使用绝对地址)
+	 * @property {Boolean}			disabled		是否禁用cell	
+	 * @property {Boolean}			border			是否显示下边框 (默认 true )
+	 * @property {Boolean}			center			内容是否垂直居中(主要是针对右侧的value部分) (默认 false )
+	 * @property {String}			url				点击后跳转的URL地址
+	 * @property {String}			linkType		链接跳转的方式,内部使用的是uView封装的route方法,可能会进行拦截操作 (默认 'navigateTo' )
+	 * @property {Boolean}			clickable		是否开启点击反馈(表现为点击时加上灰色背景) (默认 false ) 
+	 * @property {Boolean}			isLink			是否展示右侧箭头并开启点击反馈 (默认 false )
+	 * @property {Boolean}			required		是否显示表单状态下的必填星号(此组件可能会内嵌入input组件) (默认 false )
+	 * @property {String}			rightIcon		右侧的图标箭头 (默认 'arrow-right')
+	 * @property {String}			arrowDirection	右侧箭头的方向,可选值为:left,up,down
+	 * @property {Object | String}			rightIconStyle	右侧箭头图标的样式
+	 * @property {Object | String}			titleStyle		标题的样式
+	 * @property {Object | String}			iconStyle		左侧图标样式
+	 * @property {String}			size			单位元的大小,可选值为 large,normal,mini 
+	 * @property {Boolean}			stop			点击cell是否阻止事件传播 (默认 true )
+	 * @property {Object}			customStyle		定义需要用到的外部样式
+	 * 
+	 * @event {Function}			click			点击cell列表时触发
+	 * @example 该组件需要搭配cell-group组件使用,见官方文档示例
+	 */
+	export default {
+		name: 'u-cell',
+		data() {
+			return {
+
+			}
+		},
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		computed: {
+			titleTextStyle() {
+				return uni.$u.addStyle(this.titleStyle)
+			}
+		},
+		methods: {
+			// 点击cell
+			clickHandler(e) {
+				if (this.disabled) return
+				this.$emit('click', {
+					name: this.name
+				})
+				// 如果配置了url(此props参数通过mixin引入)参数,跳转页面
+				this.openPage()
+				// 是否阻止事件传播
+				this.stop && this.preventEvent(e)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	$u-cell-padding: 10px 15px !default;
+	$u-cell-font-size: 15px !default;
+	$u-cell-line-height: 24px !default;
+	$u-cell-color: $u-main-color !default;
+	$u-cell-icon-size: 16px !default;
+	$u-cell-title-font-size: 15px !default;
+	$u-cell-title-line-height: 22px !default;
+	$u-cell-title-color: $u-main-color !default;
+	$u-cell-label-font-size: 12px !default;
+	$u-cell-label-color: $u-tips-color !default;
+	$u-cell-label-line-height: 18px !default;
+	$u-cell-value-font-size: 14px !default;
+	$u-cell-value-color: $u-content-color !default;
+	$u-cell-clickable-color: $u-bg-color !default;
+	$u-cell-disabled-color: #c8c9cc !default;
+	$u-cell-padding-top-large: 13px !default;
+	$u-cell-padding-bottom-large: 13px !default;
+	$u-cell-value-font-size-large: 15px !default;
+	$u-cell-label-font-size-large: 14px !default;
+	$u-cell-title-font-size-large: 16px !default;
+	$u-cell-left-icon-wrap-margin-right: 4px !default;
+	$u-cell-right-icon-wrap-margin-left: 4px !default;
+	$u-cell-title-flex:1 !default;
+	$u-cell-label-margin-top:5px !default;
+
+
+	.u-cell {
+		&__body {
+			@include flex();
+			/* #ifndef APP-NVUE */
+			box-sizing: border-box;
+			/* #endif */
+			padding: $u-cell-padding;
+			font-size: $u-cell-font-size;
+			color: $u-cell-color;
+			// line-height: $u-cell-line-height;
+			align-items: center;
+
+			&__content {
+				@include flex(row);
+				align-items: center;
+				flex: 1;
+			}
+
+			&--large {
+				padding-top: $u-cell-padding-top-large;
+				padding-bottom: $u-cell-padding-bottom-large;
+			}
+		}
+
+		&__left-icon-wrap,
+		&__right-icon-wrap {
+			@include flex();
+			align-items: center;
+			// height: $u-cell-line-height;
+			font-size: $u-cell-icon-size;
+		}
+
+		&__left-icon-wrap {
+			margin-right: $u-cell-left-icon-wrap-margin-right;
+		}
+
+		&__right-icon-wrap {
+			margin-left: $u-cell-right-icon-wrap-margin-left;
+			transition: transform 0.3s;
+
+			&--up {
+				transform: rotate(-90deg);
+			}
+
+			&--down {
+				transform: rotate(90deg);
+			}
+		}
+
+		&__title {
+			flex: $u-cell-title-flex;
+
+			&-text {
+				font-size: $u-cell-title-font-size;
+				line-height: $u-cell-title-line-height;
+				color: $u-cell-title-color;
+
+				&--large {
+					font-size: $u-cell-title-font-size-large;
+				}
+			}
+
+		}
+
+		&__label {
+			margin-top: $u-cell-label-margin-top;
+			font-size: $u-cell-label-font-size;
+			color: $u-cell-label-color;
+			line-height: $u-cell-label-line-height;
+
+			&--large {
+				font-size: $u-cell-label-font-size-large;
+			}
+		}
+
+		&__value {
+			text-align: right;
+			font-size: $u-cell-value-font-size;
+			line-height: $u-cell-line-height;
+			color: $u-cell-value-color;
+
+			&--large {
+				font-size: $u-cell-value-font-size-large;
+			}
+		}
+
+		&--clickable {
+			background-color: $u-cell-clickable-color;
+		}
+
+		&--disabled {
+			color: $u-cell-disabled-color;
+			/* #ifndef APP-NVUE */
+			cursor: not-allowed;
+			/* #endif */
+		}
+
+		&--center {
+			align-items: center;
+		}
+	}
+</style>

+ 82 - 0
uni_modules/uview-ui/components/u-checkbox-group/props.js

@@ -0,0 +1,82 @@
+export default {
+    props: {
+        // 标识符
+        name: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.name
+        },
+        // 绑定的值
+        value: {
+            type: Array,
+            default: uni.$u.props.checkboxGroup.value
+        },
+        // 形状,circle-圆形,square-方形
+        shape: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.shape
+        },
+        // 是否禁用全部checkbox
+        disabled: {
+            type: Boolean,
+            default: uni.$u.props.checkboxGroup.disabled
+        },
+
+        // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+        activeColor: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.activeColor
+        },
+        // 未选中的颜色
+        inactiveColor: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.inactiveColor
+        },
+
+        // 整个组件的尺寸,默认px
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.checkboxGroup.size
+        },
+        // 布局方式,row-横向,column-纵向
+        placement: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.placement
+        },
+        // label的字体大小,px单位
+        labelSize: {
+            type: [String, Number],
+            default: uni.$u.props.checkboxGroup.labelSize
+        },
+        // label的字体颜色
+        labelColor: {
+            type: [String],
+            default: uni.$u.props.checkboxGroup.labelColor
+        },
+        // 是否禁止点击文本操作
+        labelDisabled: {
+            type: Boolean,
+            default: uni.$u.props.checkboxGroup.labelDisabled
+        },
+        // 图标颜色
+        iconColor: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.iconColor
+        },
+        // 图标的大小,单位px
+        iconSize: {
+            type: [String, Number],
+            default: uni.$u.props.checkboxGroup.iconSize
+        },
+        // 勾选图标的对齐方式,left-左边,right-右边
+        iconPlacement: {
+            type: String,
+            default: uni.$u.props.checkboxGroup.iconPlacement
+        },
+        // 竖向配列时,是否显示下划线
+        borderBottom: {
+            type: Boolean,
+            default: uni.$u.props.checkboxGroup.borderBottom
+        }
+
+    }
+}

+ 103 - 0
uni_modules/uview-ui/components/u-checkbox-group/u-checkbox-group.vue

@@ -0,0 +1,103 @@
+<template>
+	<view
+	    class="u-checkbox-group"
+	    :class="bemClass"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * checkboxGroup 复选框组
+	 * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
+	 * @tutorial https://www.uviewui.com/components/checkbox.html
+	 * @property {String}			name			标识符 
+	 * @property {Array}			value			绑定的值
+	 * @property {String}			shape			形状,circle-圆形,square-方形 (默认 'square' )
+	 * @property {Boolean}			disabled		是否禁用全部checkbox (默认 false )
+	 * @property {String}			activeColor		选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值 (默认 '#2979ff' )
+	 * @property {String}			inactiveColor	未选中的颜色 (默认 '#c8c9cc' )
+	 * @property {String | Number}	size			整个组件的尺寸 单位px (默认 18 )
+	 * @property {String}			placement		布局方式,row-横向,column-纵向 (默认 'row' )
+	 * @property {String | Number}	labelSize		label的字体大小,px单位  (默认 14 )
+	 * @property {String}			labelColor		label的字体颜色 (默认 '#303133' )
+	 * @property {Boolean}			labelDisabled	是否禁止点击文本操作 (默认 false )
+	 * @property {String}			iconColor		图标颜色 (默认 '#ffffff' )
+	 * @property {String | Number}	iconSize		图标的大小,单位px (默认 12 )
+	 * @property {String}			iconPlacement	勾选图标的对齐方式,left-左边,right-右边  (默认 'left' )
+	 * @property {Boolean}			borderBottom	placement为row时,是否显示下边框 (默认 false )
+	 * @event {Function}	change	任一个checkbox状态发生变化时触发,回调为一个对象
+	 * @event {Function}	input	修改通过v-model绑定的值时触发,回调为一个对象
+	 * @example <u-checkbox-group></u-checkbox-group>
+	 */
+	export default {
+		name: 'u-checkbox-group',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
+		computed: {
+			// 这里computed的变量,都是子组件u-checkbox需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化
+			// 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-checkbox-group)
+			// 拉取父组件新的变化后的参数
+			parentData() {
+				return [this.value, this.disabled, this.inactiveColor, this.activeColor, this.size, this.labelDisabled, this.shape,
+					this.iconSize, this.borderBottom, this.placement
+				]
+			},
+			bemClass() {
+				// this.bem为一个computed变量,在mixin中
+				return this.bem('checkbox-group', ['placement'])
+			},
+		},
+		watch: {
+			// 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+			parentData() {
+				if (this.children.length) {
+					this.children.map(child => {
+						// 判断子组件(u-checkbox)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+						typeof(child.init) === 'function' && child.init()
+					})
+				}
+			},
+		},
+		data() {
+			return {
+
+			}
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			// 将其他的checkbox设置为未选中的状态
+			unCheckedOther(childInstance) {
+				const values = []
+				this.children.map(child => {
+					// 将被选中的checkbox,放到数组中返回
+					if (child.isChecked) {
+						values.push(child.name)
+					}
+				})
+				// 发出事件
+				this.$emit('change', values)
+				// 修改通过v-model绑定的值
+				this.$emit('input', values)
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-checkbox-group {
+
+		&--row {
+			@include flex;
+		}
+
+		&--column {
+			@include flex(column);
+		}
+	}
+</style>

+ 69 - 0
uni_modules/uview-ui/components/u-checkbox/props.js

@@ -0,0 +1,69 @@
+export default {
+    props: {
+        // checkbox的名称
+        name: {
+            type: [String, Number, Boolean],
+            default: uni.$u.props.checkbox.name
+        },
+        // 形状,square为方形,circle为圆型
+        shape: {
+            type: String,
+            default: uni.$u.props.checkbox.shape
+        },
+        // 整体的大小
+        size: {
+            type: [String, Number],
+            default: uni.$u.props.checkbox.size
+        },
+        // 是否默认选中
+        checked: {
+            type: Boolean,
+            default: uni.$u.props.checkbox.checked
+        },
+        // 是否禁用
+        disabled: {
+            type: [String, Boolean],
+            default: uni.$u.props.checkbox.disabled
+        },
+        // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+        activeColor: {
+            type: String,
+            default: uni.$u.props.checkbox.activeColor
+        },
+        // 未选中的颜色
+        inactiveColor: {
+            type: String,
+            default: uni.$u.props.checkbox.inactiveColor
+        },
+        // 图标的大小,单位px
+        iconSize: {
+            type: [String, Number],
+            default: uni.$u.props.checkbox.iconSize
+        },
+        // 图标颜色
+        iconColor: {
+            type: String,
+            default: uni.$u.props.checkbox.iconColor
+        },
+        // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+        label: {
+            type: [String, Number],
+            default: uni.$u.props.checkbox.label
+        },
+        // label的字体大小,px单位
+        labelSize: {
+            type: [String, Number],
+            default: uni.$u.props.checkbox.labelSize
+        },
+        // label的颜色
+        labelColor: {
+            type: String,
+            default: uni.$u.props.checkbox.labelColor
+        },
+        // 是否禁止点击提示语选中复选框
+        labelDisabled: {
+            type: [String, Boolean],
+            default: uni.$u.props.checkbox.labelDisabled
+        }
+    }
+}

+ 0 - 0
uni_modules/uview-ui/components/u-checkbox/u-checkbox.vue


Някои файлове не бяха показани, защото твърде много файлове са промени