Prechádzať zdrojové kódy

项目过程管理h5页面

嘀嘀嘀 1 rok pred
rodič
commit
ec7cda47b4
100 zmenil súbory, kde vykonal 15119 pridanie a 0 odobranie
  1. 25 0
      App.vue
  2. 20 0
      index.html
  3. 22 0
      main.js
  4. 72 0
      manifest.json
  5. 61 0
      pages.json
  6. 452 0
      pages/index/index.vue
  7. 276 0
      pages/project/addProject.vue
  8. 246 0
      pages/project/addRenwu.vue
  9. 335 0
      pages/project/index.vue
  10. 341 0
      pages/project/projectChange.vue
  11. 73 0
      pages/project/renwuX.vue
  12. 20 0
      static/css/iconfont.css
  13. BIN
      static/img/shouye.png
  14. BIN
      static/img/shouye_select.png
  15. BIN
      static/logo.png
  16. BIN
      static/project/add.png
  17. BIN
      static/project/project.png
  18. BIN
      static/project/project_select.png
  19. BIN
      static/project/upload.png
  20. BIN
      static/project/warn.png
  21. 10 0
      uni.promisify.adaptor.js
  22. 76 0
      uni.scss
  23. 39 0
      uni_modules/uni-data-select/changelog.md
  24. 562 0
      uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue
  25. 86 0
      uni_modules/uni-data-select/package.json
  26. 8 0
      uni_modules/uni-data-select/readme.md
  27. 168 0
      uni_modules/uni-datetime-picker/changelog.md
  28. 177 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
  29. 947 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
  30. 22 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
  31. 8 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
  32. 22 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
  33. 22 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
  34. 940 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
  35. 1066 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
  36. 421 0
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
  37. 88 0
      uni_modules/uni-datetime-picker/package.json
  38. 21 0
      uni_modules/uni-datetime-picker/readme.md
  39. 81 0
      uni_modules/uni-file-picker/changelog.md
  40. 287 0
      uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js
  41. 672 0
      uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue
  42. 332 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue
  43. 292 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue
  44. 110 0
      uni_modules/uni-file-picker/components/uni-file-picker/utils.js
  45. 84 0
      uni_modules/uni-file-picker/package.json
  46. 11 0
      uni_modules/uni-file-picker/readme.md
  47. 42 0
      uni_modules/uni-icons/changelog.md
  48. 91 0
      uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
  49. 110 0
      uni_modules/uni-icons/components/uni-icons/uni-icons.vue
  50. 664 0
      uni_modules/uni-icons/components/uni-icons/uniicons.css
  51. BIN
      uni_modules/uni-icons/components/uni-icons/uniicons.ttf
  52. 664 0
      uni_modules/uni-icons/components/uni-icons/uniicons_file.ts
  53. 649 0
      uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js
  54. 89 0
      uni_modules/uni-icons/package.json
  55. 8 0
      uni_modules/uni-icons/readme.md
  56. 25 0
      uni_modules/uni-load-more/changelog.md
  57. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/en.json
  58. 8 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/index.js
  59. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json
  60. 5 0
      uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json
  61. 404 0
      uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue
  62. 84 0
      uni_modules/uni-load-more/package.json
  63. 14 0
      uni_modules/uni-load-more/readme.md
  64. 92 0
      uni_modules/uni-popup/changelog.md
  65. 45 0
      uni_modules/uni-popup/components/uni-popup-dialog/keypress.js
  66. 316 0
      uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
  67. 143 0
      uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue
  68. 187 0
      uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
  69. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/en.json
  70. 8 0
      uni_modules/uni-popup/components/uni-popup/i18n/index.js
  71. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json
  72. 7 0
      uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json
  73. 45 0
      uni_modules/uni-popup/components/uni-popup/keypress.js
  74. 26 0
      uni_modules/uni-popup/components/uni-popup/popup.js
  75. 90 0
      uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
  76. 518 0
      uni_modules/uni-popup/components/uni-popup/uni-popup.vue
  77. 88 0
      uni_modules/uni-popup/package.json
  78. 17 0
      uni_modules/uni-popup/readme.md
  79. 8 0
      uni_modules/uni-scss/changelog.md
  80. 1 0
      uni_modules/uni-scss/index.scss
  81. 82 0
      uni_modules/uni-scss/package.json
  82. 4 0
      uni_modules/uni-scss/readme.md
  83. 7 0
      uni_modules/uni-scss/styles/index.scss
  84. 3 0
      uni_modules/uni-scss/styles/setting/_border.scss
  85. 66 0
      uni_modules/uni-scss/styles/setting/_color.scss
  86. 55 0
      uni_modules/uni-scss/styles/setting/_radius.scss
  87. 56 0
      uni_modules/uni-scss/styles/setting/_space.scss
  88. 167 0
      uni_modules/uni-scss/styles/setting/_styles.scss
  89. 24 0
      uni_modules/uni-scss/styles/setting/_text.scss
  90. 146 0
      uni_modules/uni-scss/styles/setting/_variables.scss
  91. 19 0
      uni_modules/uni-scss/styles/tools/functions.scss
  92. 31 0
      uni_modules/uni-scss/theme.scss
  93. 62 0
      uni_modules/uni-scss/variables.scss
  94. 33 0
      uni_modules/uni-table/changelog.md
  95. 462 0
      uni_modules/uni-table/components/uni-table/uni-table.vue
  96. 34 0
      uni_modules/uni-table/components/uni-tbody/uni-tbody.vue
  97. 95 0
      uni_modules/uni-table/components/uni-td/uni-td.vue
  98. 511 0
      uni_modules/uni-table/components/uni-th/filter-dropdown.vue
  99. 295 0
      uni_modules/uni-table/components/uni-th/uni-th.vue
  100. 0 0
      uni_modules/uni-table/components/uni-thead/uni-thead.vue

+ 25 - 0
App.vue

@@ -0,0 +1,25 @@
+<script>
+	export default {
+		onLaunch: function() {
+			console.log('App Launch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	/* 全局引入iconfont */
+	@import 'static/css/iconfont.css';
+	/*每个页面公共css */
+	page{
+		background-color: #F2F3F5;
+	}
+	uni-page-body,html,body{
+	height: 100%;
+	}
+</style>

+ 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>

+ 22 - 0
main.js

@@ -0,0 +1,22 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+  ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+  const app = createSSRApp(App)
+  return {
+    app
+  }
+}
+// #endif

+ 72 - 0
manifest.json

@@ -0,0 +1,72 @@
+{
+    "name" : "ProjectProcess",
+    "appid" : "__UNI__D5640AA",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2"
+}

+ 61 - 0
pages.json

@@ -0,0 +1,61 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "项目过程管理系统",
+				"enablePullDownRefresh": true
+			}
+		},{
+			"path": "pages/project/index",
+			"style": {
+				"navigationBarTitleText": "项目管理",
+				"enablePullDownRefresh": true
+			}
+		},{
+			"path": "pages/project/projectChange",
+			"style": {
+				"navigationBarTitleText": "编辑项目"
+			}
+		},{
+			"path": "pages/project/addProject",
+			"style": {
+				"navigationBarTitleText": "新建项目"
+			}
+		},{
+			"path": "pages/project/addRenwu",
+			"style": {
+				"navigationBarTitleText": "新建任务"
+			}
+		},{
+			"path": "pages/project/renwuX",
+			"style": {
+				"navigationBarTitleText": "任务详情"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "white",
+		"navigationBarTitleText": "项目过程管理系统",
+		"navigationBarBackgroundColor": "#054DF3",
+		"backgroundColor": "#54A4FF"
+	},
+	"uniIdRouter": {},
+	"tabBar": {//底部导航
+		"color": "#A6A6A6",
+		"selectedColor": "#0061FF",
+		"borderStyle": "black",
+		"backgroundColor": "#ffffff",
+		"list": [{
+			"pagePath": "pages/index/index",
+			"iconPath": "static/img/shouye.png",
+			"selectedIconPath": "static/img/shouye_select.png",//选中时的图片路径
+			"text": "首页"
+		}, {
+			"pagePath": "pages/project/index",
+			"iconPath": "static/project/project.png",
+			"selectedIconPath": "static/project/project_select.png",
+			"text": "项目管理"
+		}]
+	}
+}

+ 452 - 0
pages/index/index.vue

@@ -0,0 +1,452 @@
+<template>
+	<view class="content">
+		<!-- 项目总览 -->
+		<view class="projectAll">
+			<view class="all-text">项目总览</view>
+			<view class="all-more">查看更多</view>
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">今日</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">今日</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==1">本周</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(1)">本周</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==2">本月</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(2)">本月</view>
+			</view>
+			<view class="all-num">
+				<view class="shuzi" v-for="(item,index) in allList">
+					<view class="shu-num">{{item.num}}</view>
+					<view>{{item.txt}}</view>
+				</view>
+			</view>
+		</view>
+		<!-- 项目列表 -->
+		<view class="projectlist">
+			<view class="all-text">项目列表</view>
+			<view class="all-cui">催办</view>
+			<view class="all-more" style="margin: -59rpx 0 0 581rpx;">查看更多</view>
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">今日</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">今日</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==1">本周</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(1)">本周</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==2">本月</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(2)">本月</view>
+			</view>
+			<!-- 列表 -->
+			<view class="projectL" v-for="(item,index) in list" :key="index">
+				<view class="projectName">{{item.name}}</view>
+				<view class="all-cui" style="margin: -44rpx 0 0 581rpx;">催办</view>
+				<view style="margin: 10rpx 0 0 19rpx;">结束时间:{{item.time}}</view>
+				<view style="margin: 20rpx 0 0 19rpx;">参与成员:{{item.people}}</view>
+				<progress v-if="item.batteryName!='/'" :percent="item.batteryName" activeColor="rgba(0, 97, 255, 1)" border-radius="50" stroke-width="12" show-info font-size='12' active='true' />
+				<view class="projectLine" v-if="list.length>1&&(index<=list.length-1)"></view>
+			</view>
+		</view>
+		<!-- 未完成清单 -->
+		<view class="projectlist" style="height: 100%;">
+			<view class="all-text">未完成清单</view>
+			<view class="wei-title1 title1" v-if="qingdan==0">
+				<view>问题清单</view>
+				<view class="wei-line1"></view>
+			</view>
+			<view class="title1" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=0">
+				问题清单
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="wei-title1 title2" v-if="qingdan==1">
+				<view>预警清单</view>
+				<view class="wei-line1"></view>
+			</view>
+			<view class="title2" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=1">
+				预警清单
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="all-cui" style="margin: -35px 0 0 581rpx;">催办</view>
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">今日</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">今日</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==1">本周</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(1)">本周</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==2">本月</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(2)">本月</view>
+			</view>
+			<!-- 未完成清单表格 -->
+			<view style="margin: 24rpx 0 0 20rpx;width: 710rpx;">
+				<uni-table ref="table" :loading="loading" emptyText="暂无更多数据"
+				 style="width:710rpx;table-layout: fixed; word-wrap: break-word;">
+					<uni-tr style="background-color: rgba(245, 246, 250, 1);color: rgba(90, 96, 127, 1);font-size: 24rpx;">
+						<uni-th align="center">姓名</uni-th>
+						<uni-th align="center">项目名称</uni-th>
+						<uni-th align="center">项目任务</uni-th>
+						<uni-th align="center">情况状态</uni-th>
+					</uni-tr>
+					<uni-tr v-for="(item, index) in tableData" :key="index" style="color: rgba(0, 0, 0, 1);">
+						<uni-td align="center">{{ item.name }}</uni-td>
+						<uni-td align="center">
+							<view class="name">{{ item.proname }}</view>
+						</uni-td>
+						<uni-td align="center">{{ item.proren }}</uni-td>
+						<uni-td align="center">{{ item.prozhuang }}</uni-td>
+					</uni-tr>
+				</uni-table>
+			</view>
+			
+		</view>
+		<!-- 部门排行榜 -->
+		<view class="projectlist" style="height: 100%;">
+			<view class="all-text">部门排行榜</view>
+			<view class="wei-title1 title1" v-if="qingdan==0">
+				<view>评分</view>
+				<view class="wei-line1" style="margin: 10rpx 0 0 -8rpx;"></view>
+			</view>
+			<view class="title1" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=0">
+				评分
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="wei-title1 title2" v-if="qingdan==1">
+				<view>完成率</view>
+				<view class="wei-line1" style="margin: 10rpx 0 0 4rpx;"></view>
+			</view>
+			<view class="title2" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=1">
+				完成率
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">今日</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">今日</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==1">本周</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(1)">本周</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==2">本月</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(2)">本月</view>
+			</view>
+			<!-- 部门排行榜表格 -->
+			<view style="margin: 24rpx 0 0 20rpx;width: 710rpx;">
+				<uni-table ref="table" :loading="loading" emptyText="暂无更多数据"
+				 style="width:710rpx;table-layout: fixed; word-wrap: break-word;">
+					<uni-tr style="background-color: rgba(245, 246, 250, 1);color: rgba(90, 96, 127, 1);font-size: 24rpx;">
+						<uni-th align="center">排行</uni-th>
+						<uni-th align="center">部门名称</uni-th>
+						<uni-th align="center">评分</uni-th>
+					</uni-tr>
+					<uni-tr v-for="(item, index) in tableData" :key="index" style="color: rgba(0, 0, 0, 1);">
+						<uni-td align="center">{{ item.name }}</uni-td>
+						<uni-td align="center">
+							<view class="name">{{ item.proname }}</view>
+						</uni-td>
+						<uni-td align="center">{{ item.proren }}</uni-td>
+					</uni-tr>
+				</uni-table>
+			</view>
+			
+		</view>
+		<!-- 消息预警推送汇总 -->
+		<view class="projectlist" style="height: 100%;">
+			<view class="all-text">消息预警推送汇总</view>
+			<view class="wei-title1 title1" v-if="qingdan==0">
+				<view>汇总</view>
+				<view class="wei-line1" style="margin: 10rpx 0 0 -8rpx;"></view>
+			</view>
+			<view class="title1" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=0">
+				汇总
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="wei-title1 title2" v-if="qingdan==1">
+				<view>明细</view>
+				<view class="wei-line1" style="margin: 10rpx 0 0 -8rpx;"></view>
+			</view>
+			<view class="title2" style="rgba(0, 0, 0, 1)" v-else @click="qingdan=1">
+				明细
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">今日</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">今日</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==1">本周</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(1)">本周</view>
+				<view class="all-list" style="margin-left: 25rpx;" v-if="change==2">本月</view>
+				<view class="all-list2" style="margin-left: 25rpx;" v-else @click="changeTime(2)">本月</view>
+			</view>
+			<!-- 汇总表格 -->
+			<view style="margin: 24rpx 0 0 20rpx;width: 710rpx;" v-if="qingdan==0">
+				<view class="hui-kuang" v-for="(item,index) in xiaoban" :key="index">
+					<view class="hui-left">{{item.dan}}</view>
+					<view>
+						<view class="hui-center">提醒次数</view>
+						<view class="hui-center">{{item.num1}}次</view>
+					</view>
+					<view>
+						<view class="hui-center">预警次数</view>
+						<view class="hui-center">{{item.num2}}次</view>
+					</view>
+				</view>
+			</view>
+			<!-- 消息预警推送汇总明细表格 -->
+			<view style="margin: 24rpx 0 0 20rpx;width: 710rpx;" v-if="qingdan==1">
+				<uni-table ref="table" :loading="loading" emptyText="暂无更多数据"
+				 style="width:710rpx;table-layout: fixed; word-wrap: break-word;">
+					<uni-tr style="background-color: rgba(245, 246, 250, 1);color: rgba(90, 96, 127, 1);font-size: 24rpx;">
+						<uni-th align="center">项目</uni-th>
+						<uni-th align="center">任务</uni-th>
+						<uni-th align="center">类型</uni-th>
+						<uni-th align="center">发生时间</uni-th>
+						<uni-th align="center">部门</uni-th>
+						<uni-th align="center">姓名</uni-th>
+					</uni-tr>
+					<uni-tr v-for="(item, index) in tableData" :key="index" style="color: rgba(0, 0, 0, 1);">
+						<uni-td align="center">{{ item.name }}</uni-td>
+						<uni-td align="center">
+							<view class="name">{{ item.proname }}</view>
+						</uni-td>
+						<uni-td align="center">{{ item.proren }}</uni-td>
+						<uni-td align="center">2024-01-11 12:11:11</uni-td>
+						<uni-td align="center">
+							<view class="name">{{ item.proname }}</view>
+						</uni-td>
+						<uni-td align="center">{{ item.proren }}</uni-td>
+					</uni-tr>
+				</uni-table>
+			</view>
+			<view style="width: 100%;height:120rpx;"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				allList:[
+					{num:5,txt:'超时在办'},
+					{num:5,txt:'正常在办'},
+					{num:5,txt:'预警在办'},
+					{num:5,txt:'超时办结'},
+					{num:5,txt:'正常办结'},
+				],
+				change:0,
+				list:[
+					{name:'a项目',time:'2024-06-25  15:00',people:'参与成员:张三  |  李四',batteryName:70,},
+					{name:'a项目',time:'2024-06-25  15:00',people:'参与成员:张三  |  李四',batteryName:60,},
+				],
+				qingdan:0,
+				// 未完成清单
+				tableData:[
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+					{name:'张三',proname:'xx项目',proren:'xx任务',prozhuang:'紧急'},
+				],
+				
+				// 校办
+				xiaoban:[
+					{dan:'校办',num1:'3',num2:'5'},
+					{dan:'校办',num1:'3',num2:'5'},
+					{dan:'校办',num1:'3',num2:'5'},
+					{dan:'校办',num1:'3',num2:'5'},
+				],
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			//修改时间框
+			changeTime(a){
+				this.change=a
+				if(a==1){
+					
+				}
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	/* 项目总览 */
+	.projectAll{
+		margin-top: 26rpx;
+		width: 100%;
+		height: 350rpx;
+		background: #FFFFFF;
+	}
+	.all-text{
+		margin: 27rpx 0 0 19rpx;
+		font-size: 32rpx;
+		font-weight: 500;
+		color: rgba(0, 0, 0, 1);
+	}
+	.all-more{
+		margin: -47rpx 0 0 581rpx;
+		width: 149rpx;
+		height: 59rpx;
+		border-radius: 92rpx;
+		background: #0061FF;
+		font-size: 28rpx;
+		color: rgba(255, 255, 255, 1);
+		line-height: 59rpx;
+		text-align: center;
+	}
+	.all-select{
+		display: flex;
+		margin: 31rpx 0 0  19rpx;
+		width: 712rpx;
+		height: 90rpx;
+		border-radius: 7rpx;
+		background: rgba(242, 247, 255, 1);
+		align-items: center;
+	}
+	.all-list{
+		width: 211rpx;
+		height: 71rpx;
+		border-radius: 7rpx;
+		background: rgba(0, 97, 255, 0.1);
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		font-size: 28rpx;
+		line-height: 71rpx;
+		color: rgba(0, 97, 255, 1);
+		text-align: center;
+	}
+	.all-list2{
+		width: 211rpx;
+		height: 71rpx;
+		border-radius: 7rpx;
+		font-size: 28rpx;
+		line-height: 71rpx;
+		color: rgba(56, 56, 56, 1);
+		text-align: center;
+	}
+	.all-num{
+		display: flex;
+		margin: 31rpx 0 0  19rpx;
+		width: 712rpx;
+	}
+	.shuzi{
+		width: 20%;
+		text-align: center;
+		font-size: 24rpx;
+		font-weight: 400;
+		color: rgba(128, 128, 128, 1);
+	}
+	.shu-num{
+		font-size: 40rpx;
+		color: rgba(0, 0, 0, 1);
+		font-weight: 500;
+	}
+	/* 项目列表 */
+	.projectlist{
+		margin-top: 20rpx;
+		width: 100%;
+		height: 741rpx;
+		background: #FFFFFF;
+	}
+	.all-cui{
+		margin: -47rpx 0 0 412rpx;
+		width: 149rpx;
+		height: 59rpx;
+		border-radius: 92rpx;
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		font-size: 28rpx;
+		color: rgba(0, 97, 255, 1);
+		line-height: 59rpx;
+		text-align: center;
+	}
+	/* 列表 */
+	.projectL{
+		margin-top: 10rpx;
+		width: 100%;
+		font-size: 24rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	.projectName{
+		margin: 45rpx 0 0 20rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+	}
+	>>>.uni-progress-bar {
+	    border-radius: 129rpx;  
+	}  
+	>>>.uni-progress-inner-bar {  
+	    border-radius: 129rpx;  
+	}
+	>>>.uni-progress-info {
+		margin-top: 0;
+		margin-bottom: 0;
+		min-width: 2em;
+		margin-left: 15px;
+		font-size: 24rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	>>>uni-progress {
+	    width: 90%;
+		margin: 25rpx 0 0 20rpx;
+	}
+	.projectLine{
+		margin: 40rpx 0 0 20rpx;
+		width: 711rpx;
+		height: 1rpx;
+		background: rgba(230, 230, 230, 1);
+	}
+	/* 未完成清单 */
+	.wei-title1{
+		color: rgba(0, 97, 255, 1);
+	}
+	.title1{
+		margin: 37rpx 0 0 20rpx;
+		font-size: 28rpx;
+	}
+	.title2{
+		margin: -29px 0 0 197rpx;
+		font-size: 28rpx;
+	}
+	.wei-line1{
+		margin: 10rpx 0 0 15rpx;
+		width: 79rpx;
+		height: 8rpx;
+		border-radius: 75rpx;
+		background: rgba(0, 97, 255, 1);
+	}
+	>>>.uni-table[data-v-6cd49106] {
+		position: relative;
+		width: 710rpx;
+		border-radius: 5px;
+		background-color: #fff;
+		box-sizing: border-box;
+		display: table;
+    /* overflow-x: inherit; */
+}
+
+/* 消息预警汇总表格 */
+.hui-kuang{
+	display: flex;
+	width: 710rpx;
+	height: 120rpx;
+	/* border: 1rpx solid rgba(215, 219, 236, 1); */
+	font-size: 24rpx;
+	color: rgba(90, 96, 127, 1);
+}
+.hui-left{
+	width: 171rpx;
+	height: 120rpx;
+	background: rgba(245, 246, 250, 1);
+	line-height: 120rpx;
+	text-align: center;
+}
+.hui-center{
+	width: 270rpx;
+	height: 60rpx;
+	border: 1rpx solid rgba(215, 219, 236, 1);
+	line-height: 60rpx;
+	text-align: center;
+}
+</style>

+ 276 - 0
pages/project/addProject.vue

@@ -0,0 +1,276 @@
+<template>
+	<view class="content">
+		<view class="param">
+			<view class="all-text">创建项目需求</view>
+			<view class="proName">项目标题</view>
+			<input class="proINname" placeholder="请输入项目标题"/>
+			<view class="proMiao">项目描述</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目描述"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">附件</view>
+			<view class="upload-img">
+				<uni-file-picker limit="3" 
+					@select='selectUpload' 
+					:auto-upload='false' 
+					file-extname='pdf,docx,xlsx,' 
+					file-mediatype="all" 
+					@success='uploadSuccess'	
+					@fail='uploadFail' 
+					:list-styles='listStyles' 
+					v-model='fileList'
+				></uni-file-picker>
+			</view>
+			<span style="margin: 20rpx 0 0 20rpx;">支持pdf/word/xlsx格式,单个文件大小不超过5MB</span>
+			<view class="proMiao">主办单位</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择主办单位"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">协办单位</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择协办单位"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			
+			<view class="proMiao">项目级别</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目级别"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">关联流程</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择关联流程"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">项目来源</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目来源"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao jindu">完成进度</view>
+			<view class="weidu">维度权重40%</view>
+			<view class="addRen" @click="addRen">新增任务</view>
+			<!-- 任务列表 -->
+			<view style="width: 100%;margin: 50rpx 0 0 20rpx;">
+				<view class="renwu" v-for="(item,index) in renwu" :key="index" @click="clickRenwu">
+					<view>{{item.name}}</view>
+					<view>{{item.time}}</view>
+					<view style="display: flex;">
+						<view class="renwu-bu bianji">编辑</view>
+						<view class="renwu-bu delete">删除</view>
+					</view>
+				</view>
+			</view>
+			<view class="project">
+				<view class="butt quxiao">取消</view>
+				<view class="butt queren">确认创建</view>
+			</view>
+			<view style="width: 100%;height: 60rpx;"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				value: 1,
+				  range: [
+					{ value: 0, text: "篮球" },
+					{ value: 1, text: "足球" },
+					{ value: 2, text: "游泳" },
+				  ],
+				fileList: [],
+				listStyles: {
+					"borderStyle": {
+						"width": "0", // 边框宽度
+					},
+					"border": false, // 是否显示边框
+					"dividline": false
+				},
+				renwu:[
+					{name:'xxx任务',time:'2024-04-11'},
+					{name:'xxx任务',time:'2024-04-11'},
+					{name:'xxx任务',time:'2024-04-11'},
+				],
+			}
+		},
+		onLoad() {
+	
+		},
+		methods: {
+			 change(e) {
+			  console.log("e:", e);
+			},
+			//新增任务
+			addRen(){
+				uni.navigateTo({
+					url:'/pages/project/addRenwu'
+				})
+			},
+			//任务详情
+			clickRenwu(){
+				uni.navigateTo({
+					url:'/pages/project/renwuX'
+				})
+			},
+			// 上传附件
+			uploadSuccess(e) {
+			console.log('上传成功', e)
+			},
+			uploadFail(e) {
+				console.log('上传失败:', e)
+			},
+			selectUpload(e) {
+				console.log('上传:', e)
+			
+				uni.uploadFile({
+					url: 'url', //仅为示例,非真实的接口地址
+					filePath: e.tempFilePaths[0],
+					name: 'file',
+					// formData: {
+					// 	'file': ''
+					// },
+					success: (uploadFileRes) => {
+						console.log(uploadFileRes.data);
+					},
+					fail: (err) => {
+						console.log(err);
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	.param{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		background-color: rgba(255, 255, 255, 1);
+		font-size: 28rpx;
+		font-weight: 400;
+		line-height: 41rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	.all-text{
+		margin: 22rpx 0 0 20rpx;
+		font-size: 32rpx;
+		font-weight: 500;
+		color: rgba(0, 0, 0, 1);
+	}
+	.proName{
+		margin: 23rpx 0 0 20rpx;
+	}
+	.proINname{
+		margin: 25rpx 0 0 21rpx;
+		width: 709rpx;
+		height: 90rpx;
+		border-radius: 13rpx;
+		background: rgba(245, 248, 252, 1);
+		border: 1rpx solid rgba(229, 229, 229, 1);
+		font-size: 28rpx;
+		line-height: 90rpx;
+		color: rgba(179, 179, 179, 1);
+	}
+	.proMiao{
+		margin: 31rpx 0 0 20rpx;
+	}
+	/* 上传附件 */
+	.upload-img{
+		margin: 25rpx 0 0 21rpx;
+		width: 200rpx;
+		height: 80rpx;
+		opacity: 1;
+		border-radius: 13rpx;
+		background: rgba(0, 97, 255, 0.1);
+		
+		border: 1rpx solid rgba(0, 97, 255, 1);
+	}
+	.jindu{
+		font-size: 32rpx;
+		font-weight: 500;
+		line-height: 46rpx;
+	}
+	.weidu{
+		margin: 7rpx 0 0 20rpx;
+		font-size: 24rpx;
+		line-height: 35rpx;
+		color: rgba(128, 128, 128, 1);
+	}
+	.addRen{
+		margin: -80rpx 0 0 581rpx;
+		width: 149rpx;
+		height: 59rpx;
+		border-radius: 92rpx;
+		background: rgba(0, 97, 255, 1);
+		font-size: 28rpx;
+		line-height: 59rpx;
+		color: rgba(255, 255, 255, 1);
+		text-align: center;
+	}
+	/* 任务列表 */
+	.renwu{
+		display: flex;
+		margin-top: 6rpx;
+		width: 709rpx;
+		height: 90rpx;
+		border-radius: 13rpx;
+		background: rgba(245, 248, 252, 1);
+		border: 1rpx solid rgba(229, 229, 229, 1);
+		font-size: 28rpx;
+		justify-content: space-evenly;
+		align-items: center;
+	}
+	.renwu-bu{
+		width: 105rpx;
+		height: 50rpx;
+		border-radius: 110rpx;
+		line-height:50rpx;
+		text-align: center;
+	}
+	.bianji{
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		color: rgba(0, 97, 255, 1);
+	}
+	.delete{
+		margin-left: 20rpx;
+		border: 1rpx solid rgba(212, 48, 48, 1);
+		color: rgba(212, 48, 48, 1);
+	}
+	.project{
+		display: flex;
+		margin: 61rpx 0 0 0;
+		justify-content: space-evenly;
+	}
+	.butt{
+		width: 330rpx;
+		height: 90rpx;
+		border-radius: 117rpx;
+		font-size: 32rpx;
+		line-height: 90rpx;
+		text-align: center;
+	}
+	.quxiao{
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		color: rgba(0, 97, 255, 1);
+	}
+	.queren{
+		background: rgba(0, 97, 255, 1);
+		color: rgba(255, 255, 255, 1);
+	}
+</style>

+ 246 - 0
pages/project/addRenwu.vue

@@ -0,0 +1,246 @@
+<template>
+	<view class="content">
+		<view class="param">
+			<view class="proMiao">任务名称</view>
+			<input class="proINname" placeholder="请输入任务名称"/>
+			<view class="proMiao">任务描述</view>
+			<textarea class="proINname proIMi" placeholder="请输入评分标准"/>
+			<view class="proMiao">附件</view>
+			<view class="upload-img">
+				<uni-file-picker limit="3" 
+					@select='selectUpload' 
+					:auto-upload='false' 
+					file-extname='pdf,docx,xlsx,' 
+					file-mediatype="all" 
+					@success='uploadSuccess'	
+					@fail='uploadFail' 
+					:list-styles='listStyles' 
+					v-model='fileList'
+				></uni-file-picker>
+			</view>
+			<span style="margin: 20rpx 0 0 20rpx;">支持pdf/word/xlsx格式,单个文件大小不超过5MB</span>
+			<view class="proMiao">评分上限</view>
+			<input class="proINname" placeholder="请输入评分上限"/>
+			<view class="proMiao">权重</view>
+			<input class="proINname" placeholder="请输入权重"/>
+			<view class="proMiao">任务负责人</view>
+			<view @click="toggle()">
+				<uni-data-select class="proINname" :disabled="true"
+					placeholder="请选择任务负责人"
+				></uni-data-select>
+			</view>
+			
+			<view class="proMiao">提醒时间</view>
+			<uni-datetime-picker class="proINname" type="datetime" v-model="datetimesingle" @change="changeLog" />
+			<view class="proMiao">预警时间</view>
+			<uni-datetime-picker class="proINname" type="datetime" v-model="datetimesingle" @change="changeLog" />
+			<view class="proMiao">任务周期</view>
+			<uni-datetime-picker class="proINname" v-model="datetimerange" type="datetimerange" rangeSeparator="至" />
+			<!-- 操作键 -->
+			<view class="project">
+				<view class="butt quxiao">取消</view>
+				<view class="butt queren">确认创建</view>
+			</view>
+			<view style="width: 100%;height: 60rpx;"></view>
+			
+			<!-- 任务负责人弹出框-->
+			<uni-popup ref="popup" background-color="#fff">
+				<view class="popup-content">
+					<view class="popup-title">
+						<view @click="quPopup">取消</view>
+						<view>选择任务负责人</view>
+						<view style="color: rgba(0, 97, 255, 1);">确定</view>
+					</view>
+					<view class="popup-list" v-for="(item,index) in people" :key="index">
+						<view class="l-line"></view>
+						<view class="l-kuang">
+							<view class="l-circle"></view>
+							<view style="margin-left: 20rpx;">
+								<view style="font-size: 28rpx;">{{item.name}}</view>
+								<view style="font-size: 24rpx;">{{item.danwei}}</view>
+							</view>
+							<label class="l-label">
+								<view>
+									<radio :id="item.name" :value="item.name" :checked="item.checked"></radio>
+								</view>
+							</label>
+						</view>
+					</view>
+				</view>
+			</uni-popup>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				datetimesingle: '',
+				datetimerange: [],
+				people:[
+					{name:'张三',danwei:'校办'},
+					{name:'张三',danwei:'校办'},
+					{name:'张三',danwei:'校办',checked:'true'},
+				],
+				// 附件
+				fileList: [],
+				listStyles: {
+					"borderStyle": {
+						"width": "0", // 边框宽度
+					},
+					"border": false, // 是否显示边框
+					"dividline": false
+				},
+			}
+		},
+		onLoad() {
+	
+		},
+		methods: {
+			// 任务负责人弹框
+			toggle() {
+				console.log('dd')
+				// open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
+				this.$refs.popup.open('bottom')
+			},
+			//取消弹框
+			quPopup(){
+				this.$refs.popup.close()
+			},
+			// 选择负责人
+			checkboxChange: function(e) {
+				console.log(e)
+				var checked = e.target.value
+				var changed = {}
+				for (var i = 0; i < this.people.length; i++) {
+					if (checked.indexOf(this.people[i].name) !== -1) {
+						changed['people[' + i + '].checked'] = true
+					} else {
+						changed['people[' + i + '].checked'] = false
+					}
+				}
+			},
+			// 上传附件
+			uploadSuccess(e) {
+			console.log('上传成功', e)
+			},
+			uploadFail(e) {
+				console.log('上传失败:', e)
+			},
+			selectUpload(e) {
+				console.log('上传:', e)
+			
+				uni.uploadFile({
+					url: 'url', //仅为示例,非真实的接口地址
+					filePath: e.tempFilePaths[0],
+					name: 'file',
+					// formData: {
+					// 	'file': ''
+					// },
+					success: (uploadFileRes) => {
+						console.log(uploadFileRes.data);
+					},
+					fail: (err) => {
+						console.log(err);
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	.param{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		background-color: rgba(255, 255, 255, 1);
+		font-size: 28rpx;
+		font-weight: 400;
+		line-height: 41rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	.proMiao{
+		margin: 31rpx 0 0 20rpx;
+	}
+	/* 上传附件 */
+	.upload-img{
+		margin: 25rpx 0 0 21rpx;
+		width: 200rpx;
+		height: 80rpx;
+		opacity: 1;
+		border-radius: 13rpx;
+		background: rgba(0, 97, 255, 0.1);
+		
+		border: 1rpx solid rgba(0, 97, 255, 1);
+	}
+	.proINname{
+		margin: 25rpx 0 0 21rpx;
+		width: 709rpx;
+		height: 90rpx;
+		border-radius: 13rpx;
+		background: rgba(245, 248, 252, 1);
+		border: 1rpx solid rgba(229, 229, 229, 1);
+		font-size: 28rpx;
+		line-height: 90rpx;
+		color: rgba(179, 179, 179, 1);
+	}
+	.proIMi{
+		height: 315rpx;
+	}
+	/* 操作键 */
+	.project{
+		display: flex;
+		margin: 61rpx 0 0 0;
+		justify-content: space-evenly;
+	}
+	.butt{
+		width: 330rpx;
+		height: 90rpx;
+		border-radius: 117rpx;
+		font-size: 32rpx;
+		line-height: 90rpx;
+		text-align: center;
+	}
+	.quxiao{
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		color: rgba(0, 97, 255, 1);
+	}
+	.queren{
+		background: rgba(0, 97, 255, 1);
+		color: rgba(255, 255, 255, 1);
+	}
+	/* 任务负责人弹出框 */
+	.popup-title{
+		display: flex;
+		width: 100%;
+		height: 104rpx;
+		font-size: 32rpx;
+		line-height: 104rpx;
+		justify-content: space-around;
+		color: rgba(0, 0, 0, 1);
+	}
+	.l-line{
+		width: 100%;
+		border: 1rpx solid rgba(230, 230, 230, 1);
+	}
+	.l-kuang{
+		margin: 40rpx 0 0 20rpx;
+		display: flex;
+	}
+	.l-circle{
+		width: 80rpx;
+		height: 80rpx;
+		border-radius: 50%;
+		background-color:rgba(204, 204, 204, 1);
+	}
+	.l-label{
+		margin-left: 504rpx;
+	}
+</style>

+ 335 - 0
pages/project/index.vue

@@ -0,0 +1,335 @@
+<template>
+	<view class="content">
+		<!-- 搜索框 -->
+		<view class="sousuo">
+			<view class="iconfont icon-sousuo" style="margin: 0 30rpx 0 30rpx;"></view>
+			<input class="sou-text" placeholder="请输入查询内容" v-model="keyword"></input>
+		</view>
+		<!-- 筛选 -->
+		<view class="select2">
+			<view class="wei-title1 title1" v-if="select==0">
+				<view>我收藏的</view>
+				<view class="wei-line1"></view>
+			</view>
+			<view class="title1" style="rgba(0, 0, 0, 1)" v-else @click="select=0">
+				我收藏的
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="wei-title1 title2" v-if="select==1">
+				<view>我负责的</view>
+				<view class="wei-line1"></view>
+			</view>
+			<view class="title2" style="rgba(0, 0, 0, 1)" v-else @click="select=1">
+				我负责的
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+			<view class="wei-title1 title3" v-if="select==2">
+				<view>我参与的</view>
+				<view class="wei-line1"></view>
+			</view>
+			<view class="title3" style="rgba(0, 0, 0, 1)" v-else @click="select=2">
+				我参与的
+				<view class="wei-line1" style="background: rgba(255, 255, 255, 1);"></view>
+			</view>
+		</view>
+		<!-- 数据列表 -->
+		<view class="kuang">
+			<view class="all-select">
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==0">超时在办</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(0)">超时在办</view>
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==1">预警在办</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(1)">预警在办</view>
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==2">正常在办</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(2)">正常在办</view>
+				<view class="all-list" style="margin-left: 15rpx;" v-if="change==3">正常办结</view>
+				<view class="all-list2" style="margin-left: 15rpx;" v-else @click="changeTime(3)">正常办结</view>
+			</view>
+			<!-- 数据列表循环 -->
+			<view class="list" v-for="(item,index) in list" :key="index">
+				<view class="list-proname">
+					{{item.proName}}
+					<view class="list-kua">{{item.txt}}</view>
+					<view class="bu-shou yishou" v-if="item.shouc==1">已收藏</view>
+					<view class="bu-shou shou" v-else>收藏</view>
+					<view class="bu-shou jinji" v-if="item.jinji==1">紧急</view>
+					<view class="bu-shou norl" v-else>正常</view>
+					<view class="bu-shou more">
+						<view>更多</view>
+						<view class="xiala">
+							<view>复制</view>
+							<view @click.stop="changePro">编辑</view>
+							<view>删除</view>
+							<view>评分</view>
+						</view>
+					</view>
+				</view>
+				<view class="list-dan">主办单位:{{item.danwei}}</view>
+				<view class="list-dan">主办人:周三</view>
+				<view class="list-dan">项目周期:{{item.danwei}}</view>
+				<view class="list-dan">任务:30个</view>
+				<view class="list-dan">预警次数:30次</view>
+				<view class="list-dan">提醒次数:30次</view>
+				<view class="list-dan" style="width: 620rpx;display: flex;">
+					项目进度:
+					<progress v-if="item.batteryName!='/'" style="width: 380rpx;" :percent="item.batteryName" activeColor="rgba(0, 97, 255, 1)" border-radius="50" stroke-width="9" show-info font-size='12' active='true' />
+					<image src="../../static/project/warn.png" style="width: 43rpx;height: 43rpx;margin-top: 10rpx;"></image>
+				</view>
+			</view>
+			<view style="width: 100%;height: 100rpx;"></view>
+		</view>
+		
+		<!-- 添加项目 -->
+		<view class="serviceBtn" @click.stop="addProject"></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				select:0,
+				change:0,
+				list:[
+					{proName:'xx项目',txt:'涉外',danwei:'交通学院',jinji:1,shouc:1,batteryName:'60'},
+					{proName:'xx项目',txt:'涉外',danwei:'交通学院',jinji:0,shouc:0,batteryName:'70'},
+				],
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			//修改时间框
+			changeTime(a){
+				this.change=a
+				if(a==1){
+					
+				}
+			},
+			// 新建项目
+			addProject(){
+				uni.navigateTo({
+					url:'/pages/project/addProject'
+				})
+			},
+			//编辑项目
+			changePro(){
+				uni.navigateTo({
+					url:'/pages/project/projectChange'
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		.serviceBtn{
+			position: fixed;
+			right: 50rpx;
+			bottom: 300rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			width: 97rpx;
+			height: 97rpx;
+			background-image: url('../../static/project/add.png');
+			background-size: 100%;
+		}
+	}
+	/* 搜索框 */
+	.sousuo{
+		display: flex;
+		align-items: center;
+		margin: 20rpx 0 0 20rpx;
+		width: 710rpx;
+		height: 90rpx;
+		border-radius: 98rpx;
+		background: rgba(255, 255, 255, 1);
+	}
+	.sou-text{
+		font-size: 28rpx;
+		line-height: 40rpx;
+		color: rgba(166, 166, 166, 1);
+		width: 100%;
+	}
+	.sou-text>>>.uni-input-input {
+	    position: relative;
+	    display: block;
+	    height: 100%;
+	    background: none;
+	    color: inherit;
+	    opacity: 1;
+	    font: inherit;
+	    line-height: inherit;
+	    letter-spacing: inherit;
+	    text-align: inherit;
+	    text-indent: inherit;
+	    text-transform: inherit;
+	    text-shadow: inherit;
+	    width: 185px;
+	    height: 35px;
+	    border-radius: 85px;
+	    /* background: rgba(242, 242, 242, 1); */
+	}
+	/* 筛选 */
+	.select2{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		height: 120rpx;
+		background: rgba(255, 255, 255, 1);
+	}
+	.wei-title1{
+		color: rgba(0, 97, 255, 1);
+	}
+	.title1{
+		margin: 40rpx 0 0 48rpx;
+		font-size: 28rpx;
+	}
+	.title2{
+		margin: -41px 0 0 318rpx;
+		font-size: 28rpx;
+	}
+	.title3{
+		margin: -41px 0 0 588rpx;
+		font-size: 28rpx;
+	}
+	.wei-line1{
+		margin: 31rpx 0 0 15rpx;
+		width: 79rpx;
+		height: 8rpx;
+		border-radius: 75rpx;
+		background: rgba(0, 97, 255, 1);
+	}
+	/* 数据列表 */
+	.kuang{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		background: rgba(255, 255, 255, 1);
+	}
+	.all-select{
+		display: flex;
+		margin: 31rpx 0 0  19rpx;
+		width: 712rpx;
+		height: 90rpx;
+		border-radius: 7rpx;
+		background: rgba(242, 247, 255, 1);
+		align-items: center;
+	}
+	.all-list{
+		width: 159rpx;
+		height: 71rpx;
+		border-radius: 7rpx;
+		background: rgba(0, 97, 255, 0.1);
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		font-size: 28rpx;
+		line-height: 71rpx;
+		color: rgba(0, 97, 255, 1);
+		text-align: center;
+	}
+	.all-list2{
+		width: 159rpx;
+		height: 71rpx;
+		border-radius: 7rpx;
+		font-size: 28rpx;
+		line-height: 71rpx;
+		color: rgba(56, 56, 56, 1);
+		text-align: center;
+	}
+	/* 数据列表循环 */
+	.list{
+		display: flex;
+		flex-direction: column;
+		margin: 20rpx 0 0 20rpx;
+		width: 709rpx;
+		height: 422rpx;
+		border-radius: 7rpx;
+		background: rgba(245, 248, 255, 1);
+		box-shadow: 0px 4rpx 6rpx  rgba(0, 0, 0, 0.05);
+		font-size: 24rpx;
+		line-height: 35rpx;
+	}
+	.list-proname{
+		margin: 13rpx 0 0 29rpx;
+		font-size: 29rpx;
+		font-weight: 500;
+		display: flex;
+	}
+	.list-kua{
+		margin-left: 5px;
+		width: 59rpx;
+		height: 29rpx;
+		border-radius: 4rpx;
+		border: 2rpx solid rgba(33, 107, 255, 1);
+		font-size: 22rpx;
+		line-height: 29rpx;
+		color: rgba(33, 107, 255, 1);
+		text-align: center;
+	}
+	.bu-shou{
+		width: 87rpx;
+		height: 44rpx;
+		border-radius: 7rpx;
+		font-size: 24rpx;
+		line-height: 44rpx;
+		text-align: center;
+		color: rgba(255, 255, 255, 1);
+	}
+	.yishou{
+		margin: 10rpx 0 0 222rpx;
+		background: rgba(9, 101, 98, 1);
+	}
+	.shou{
+		margin: 10rpx 0 0 222rpx;
+		background: rgba(0, 97, 255, 1);
+	}
+	.jinji{
+		margin: 10rpx 0 0 15rpx;
+		background: rgba(254, 144, 51, 1);
+	}
+	.norl{
+		margin: 10rpx 0 0 15rpx;
+		background: rgba(166, 166, 166, 1);
+		color: rgba(255, 255, 255, 1);
+	}
+	.more{
+		margin: 10rpx 0 0 15rpx;
+		background: rgba(0, 97, 255, 1);
+	}
+	.xiala{
+		z-index: 4;
+		position: relative;
+		width: 87rpx;
+		height: 174rpx;
+		border-radius: 2rpx;
+		background: rgba(255, 255, 255, 1);
+		box-shadow: 0px 4rpx 4rpx  rgba(0, 0, 0, 0.1);
+		color: rgba(0, 0, 0, 1);
+	}
+	.list-dan{
+		margin: 9rpx 0 0 29rpx;
+	}
+	>>>.uni-progress-bar {
+	    border-radius: 9rpx;  
+	}  
+	>>>.uni-progress-inner-bar {  
+	    border-radius: 9rpx;  
+	}
+	>>>.uni-progress-info {
+		margin-top: 0;
+		margin-bottom: 0;
+		min-width: 2em;
+		margin-left: 15px;
+		font-size: 24rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	>>>uni-progress {
+	    // width: 42%;
+		// margin: 25rpx 0 0 144rpx;
+	}
+</style>

+ 341 - 0
pages/project/projectChange.vue

@@ -0,0 +1,341 @@
+<template>
+	<view class="content">
+		<view class="param">
+			<view class="all-text">创建项目需求</view>
+			<view class="proName">项目标题</view>
+			<input class="proINname" placeholder="请输入项目标题"/>
+			<view class="proMiao">项目描述</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目描述"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">附件</view>
+			<view class="upload-img proINname">
+				<uni-file-picker limit="3" 
+					@select='selectUpload' 
+					:auto-upload='false' 
+					file-extname='pdf,docx,xlsx,' 
+					file-mediatype="all" 
+					@success='uploadSuccess'	
+					@fail='uploadFail' 
+					:list-styles='listStyles' 
+					v-model='fileList'
+				></uni-file-picker>
+			</view>
+			<span style="margin: 20rpx 0 0 20rpx;">支持pdf/word/xlsx格式,单个文件大小不超过5MB</span>
+			<view class="proMiao">主办单位</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择主办单位"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">协办单位</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择协办单位"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			
+			<view class="proMiao">项目级别</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目级别"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">关联流程</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择关联流程"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao">项目来源</view>
+			<uni-data-select class="proINname"
+				v-model="value" placeholder="请选择项目来源"
+				:localdata="range"
+				@change="change"
+			></uni-data-select>
+			<view class="proMiao jindu">完成进度</view>
+			<view class="weidu">维度权重40%</view>
+			<view class="addRen" @click="addRen">新增任务</view>
+			<!-- 任务列表 -->
+			<view style="width: 100%;margin: 50rpx 0 0 20rpx;">
+				<view class="renwu" v-for="(item,index) in renwu" :key="index">
+					<view>{{item.name}}</view>
+					<view>{{item.time}}</view>
+					<view style="display: flex;">
+						<view class="renwu-bu bianji">编辑</view>
+						<view class="renwu-bu delete">删除</view>
+					</view>
+				</view>
+			</view>
+			
+			<view style="width: 100%;margin: 50rpx 0 0 20rpx;">
+				<view class="renwu" v-for="(item,index) in renwu" :key="index">
+					<view>{{item.name}}</view>
+					<view>{{item.time}}</view>
+					<view>30%</view>
+					<view>100</view>
+					<view style="color: rgba(0, 97, 255, 1);">更新进度</view>
+				</view>
+			</view>
+			<view class="proMiao jindu">进度详情</view>
+			<!-- 进度 -->
+			<view style="margin: 33rpx 0 0 19rpx;">
+				<view v-for="(item,index) in 5" :key="index" class="jindu-kuang">
+					<view style="display: flex;">
+						<view class="jindu-circle"></view>
+						<view>
+							<view style="width: 600rpx;display: flex;justify-content: space-between;margin-top: 20rpx;">
+								<view style="display: flex;">
+									<view style="margin-left: 28rpx;">周提</view>
+									<view class="jindu-type lixiang">已立项</view>
+								</view>
+								<!-- <view class="jindu-type cuiban">已催办</view>
+								<view class="jindu-type tijiao">已提交</view> -->
+								<view>2024-06-10 12:00:00</view>
+							</view>
+							<view style="margin:7rpx 0 0 28rpx;">任务指派:xxx,xxx,xxx</view>
+							<view style="margin:7rpx 0 0 28rpx;">任务指派:xxx,xxx,xxx</view>
+							<view style="margin:7rpx 0 0 28rpx;">任务指派:xxx,xxx,xxx</view>
+							<view style="margin:7rpx 0 0 28rpx;">任务指派:xxx,xxx,xxx</view>
+						</view>
+					</view>
+					<view class="jindu-line" v-if="index<4"></view>
+					
+				</view>
+			</view>
+			<view class="project">
+				<view class="butt queren">保存</view>
+			</view>
+			<view style="width: 100%;height: 60rpx;"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				value: 1,
+				  range: [
+					{ value: 0, text: "篮球" },
+					{ value: 1, text: "足球" },
+					{ value: 2, text: "游泳" },
+				  ],
+				fileList: [],
+				listStyles: {
+					"borderStyle": {
+						"width": "0", // 边框宽度
+					},
+					"border": false, // 是否显示边框
+					"dividline": false
+				},
+				renwu:[
+					{name:'xxx任务',time:'2024-04-11'},
+					{name:'xxx任务',time:'2024-04-11'},
+					{name:'xxx任务',time:'2024-04-11'},
+				],
+			}
+		},
+		onLoad() {
+	
+		},
+		methods: {
+			 change(e) {
+			  console.log("e:", e);
+			},
+			//新增任务
+			addRen(){
+				uni.navigateTo({
+					url:'/pages/project/addRenwu'
+				})
+			},
+			// 上传附件
+			uploadSuccess(e) {
+			console.log('上传成功', e)
+			},
+			uploadFail(e) {
+				console.log('上传失败:', e)
+			},
+			selectUpload(e) {
+				console.log('上传:', e)
+			
+				uni.uploadFile({
+					url: 'url', //仅为示例,非真实的接口地址
+					filePath: e.tempFilePaths[0],
+					name: 'file',
+					// formData: {
+					// 	'file': ''
+					// },
+					success: (uploadFileRes) => {
+						console.log(uploadFileRes.data);
+					},
+					fail: (err) => {
+						console.log(err);
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	.param{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		background-color: rgba(255, 255, 255, 1);
+		font-size: 28rpx;
+		font-weight: 400;
+		line-height: 41rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	.all-text{
+		margin: 22rpx 0 0 20rpx;
+		font-size: 32rpx;
+		font-weight: 500;
+		color: rgba(0, 0, 0, 1);
+	}
+	.proName{
+		margin: 23rpx 0 0 20rpx;
+	}
+	.proINname{
+		margin: 25rpx 0 0 21rpx;
+		width: 709rpx;
+		height: 90rpx;
+		border-radius: 13rpx;
+		background: rgba(245, 248, 252, 1);
+		border: 1rpx solid rgba(229, 229, 229, 1);
+		font-size: 28rpx;
+		line-height: 90rpx;
+		color: rgba(179, 179, 179, 1);
+	}
+	.proMiao{
+		margin: 31rpx 0 0 20rpx;
+	}
+	/* 上传附件 */
+	.upload-img{
+		width: 200rpx;
+		height: 80rpx;
+		opacity: 1;
+		border-radius: 13rpx;
+		background: rgba(0, 97, 255, 0.1);
+		border: 1rpx solid rgba(0, 97, 255, 1);
+	}
+	.jindu{
+		font-size: 32rpx;
+		font-weight: 500;
+		line-height: 46rpx;
+	}
+	/* 进度 */
+	.jindu-kuang{
+		font-size: 24rpx;
+		line-height: 35rpx;
+		color: rgba(128, 128, 128, 1);
+	}
+	.jindu-circle{
+		margin-top: 10rpx;
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+		opacity: 1;
+		background-color:rgba(204, 204, 204, 1);
+	}
+	.jindu-type{
+		width: 103rpx;
+		height: 40rpx;
+		border-radius: 71rpx;
+		font-size: 24rpx;
+		line-height: 40rpx;
+		color: rgba(255, 255, 255, 1);
+		text-align: center;
+		margin: 0 0 0 20rpx;
+	}
+	.lixiang{
+		background: linear-gradient(90deg, rgba(54, 111, 255, 1) 0%, rgba(93, 160, 252, 1) 100%);
+	}
+	.cuiban{
+		background: linear-gradient(90deg, rgba(57, 167, 139, 1) 0%, rgba(126, 224, 203, 1) 100%);
+	}
+	.tijiao{
+		background: linear-gradient(90deg, rgba(255, 112, 69, 1) 0%, rgba(247, 161, 114, 1) 100%);
+	}
+	.jindu-line{
+		width: 4rpx;
+		height: 100rpx;
+		background-color: rgba(166, 166, 166, 1);
+	}
+	.weidu{
+		margin: 7rpx 0 0 20rpx;
+		font-size: 24rpx;
+		line-height: 35rpx;
+		color: rgba(128, 128, 128, 1);
+	}
+	.addRen{
+		margin: -80rpx 0 0 581rpx;
+		width: 149rpx;
+		height: 59rpx;
+		border-radius: 92rpx;
+		background: rgba(0, 97, 255, 1);
+		font-size: 28rpx;
+		line-height: 59rpx;
+		color: rgba(255, 255, 255, 1);
+		text-align: center;
+	}
+	/* 任务列表 */
+	.renwu{
+		display: flex;
+		margin-top: 6rpx;
+		width: 709rpx;
+		height: 90rpx;
+		border-radius: 13rpx;
+		background: rgba(245, 248, 252, 1);
+		border: 1rpx solid rgba(229, 229, 229, 1);
+		font-size: 28rpx;
+		justify-content: space-evenly;
+		align-items: center;
+	}
+	.renwu-bu{
+		width: 105rpx;
+		height: 50rpx;
+		border-radius: 110rpx;
+		line-height:50rpx;
+		text-align: center;
+	}
+	.bianji{
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		color: rgba(0, 97, 255, 1);
+	}
+	.delete{
+		margin-left: 20rpx;
+		border: 1rpx solid rgba(212, 48, 48, 1);
+		color: rgba(212, 48, 48, 1);
+	}
+	.project{
+		display: flex;
+		margin: 61rpx 0 0 0;
+		justify-content: space-evenly;
+	}
+	.butt{
+		width: 330rpx;
+		height: 90rpx;
+		border-radius: 117rpx;
+		font-size: 32rpx;
+		line-height: 90rpx;
+		text-align: center;
+	}
+	.quxiao{
+		border: 1rpx solid rgba(0, 97, 255, 1);
+		color: rgba(0, 97, 255, 1);
+	}
+	.queren{
+		background: rgba(0, 97, 255, 1);
+		color: rgba(255, 255, 255, 1);
+	}
+</style>

+ 73 - 0
pages/project/renwuX.vue

@@ -0,0 +1,73 @@
+<template>
+	<view class="content">
+		<view class="param">
+			<view class="proMiao mingchen">xxxx任务</view>
+			<view class="proMiao title">任务详情</view>
+			<view class="prolittle">生死狙击睡觉睡觉睡觉睡觉睡觉死狙击睡觉睡觉睡觉睡觉睡觉死狙击睡觉睡觉睡觉睡觉睡觉死狙击睡觉睡觉睡觉睡觉睡觉死狙击睡觉睡觉睡觉睡觉睡觉</view>
+			<view class="proMiao title">评分上限</view>
+			<view class="prolittle">80</view>
+			<view class="proMiao title">权重</view>
+			<view class="prolittle">80</view>
+			<view class="proMiao title">提醒时间</view>
+			<view class="prolittle">2024-01-22 14:22:22</view>
+			<view class="proMiao title">预警时间</view>
+			<view class="prolittle">2024-01-22 14:22:22</view>
+			<view class="proMiao title">任务周期</view>
+			<view class="prolittle">2024-01-22 14:22:22  -  2024-09-11 14:22:22</view>
+			<view class="proMiao title">完成说明</view>
+			<view class="prolittle">这里是完成说明</view>
+			<view class="proMiao title">附件</view>
+			<view class="prolittle" style="color: rgba(54, 111, 255, 1);">附件1</view>
+			<view class="proMiao title">系统</view>
+			<view class="prolittle">100</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+			}
+		},
+		onLoad() {
+	
+		},
+		methods: {
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+	.param{
+		margin: 20rpx 0 0 0;
+		width: 100%;
+		background-color: rgba(255, 255, 255, 1);
+		font-size: 28rpx;
+		font-weight: 400;
+		line-height: 41rpx;
+		color: rgba(0, 0, 0, 1);
+	}
+	.proMiao{
+		margin: 31rpx 0 0 20rpx;
+		width: 710rpx;
+	}
+	.prolittle{
+		margin: 5rpx 0 0 20rpx;
+		width: 710rpx;
+	}
+	.mingchen{
+		font-size: 32rpx;
+		font-weight: 500;
+		line-height: 46rpx;
+	}
+	.title{
+		color: rgba(102, 102, 102, 1);
+	}
+</style>

+ 20 - 0
static/css/iconfont.css

@@ -0,0 +1,20 @@
+/* 在线链接服务仅供平台体验和调试使用,平台不承诺服务的稳定性,企业客户需下载字体包自行发布使用并做好备份。 */
+@font-face {
+  font-family: 'iconfont';  /* Project id 4726830 */
+  src: url('https://at.alicdn.com/t/c/font_4726830_93dkzgvjc9e.woff2?t=1730168077124') format('woff2'),
+       url('https://at.alicdn.com/t/c/font_4726830_93dkzgvjc9e.woff?t=1730168077124') format('woff'),
+       url('https://at.alicdn.com/t/c/font_4726830_93dkzgvjc9e.ttf?t=1730168077124') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 24px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-sousuo:before {
+  content: "\e622";
+}
+

BIN
static/img/shouye.png


BIN
static/img/shouye_select.png


BIN
static/logo.png


BIN
static/project/add.png


BIN
static/project/project.png


BIN
static/project/project_select.png


BIN
static/project/upload.png


BIN
static/project/warn.png


+ 10 - 0
uni.promisify.adaptor.js

@@ -0,0 +1,10 @@
+uni.addInterceptor({
+  returnValue (res) {
+    if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+      return res;
+    }
+    return new Promise((resolve, reject) => {
+      res.then((res) => res[0] ? reject(res[0]) : resolve(res[1]));
+    });
+  },
+});

+ 76 - 0
uni.scss

@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$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:16px;
+
+/* 图片尺寸 */
+$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;

+ 39 - 0
uni_modules/uni-data-select/changelog.md

@@ -0,0 +1,39 @@
+## 1.0.8(2024-03-28)
+- 修复 在vue2下:style动态绑定导致编译失败的bug
+## 1.0.7(2024-01-20)
+- 修复 长文本回显超过容器的bug,超过容器部分显示省略号
+## 1.0.6(2023-04-12)
+- 修复 微信小程序点击时会改变背景颜色的 bug
+## 1.0.5(2023-02-03)
+- 修复 禁用时会显示清空按钮
+## 1.0.4(2023-02-02)
+- 优化 查询条件短期内多次变更只查询最后一次变更后的结果
+- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue
+## 1.0.3(2023-01-16)
+- 修复 不关联服务空间报错的问题
+## 1.0.2(2023-01-14)
+- 新增  属性 `format` 可用于格式化显示选项内容
+## 1.0.1(2022-12-06)
+- 修复  当where变化时,数据不会自动更新的问题
+## 0.1.9(2022-09-05)
+- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
+## 0.1.8(2022-08-29)
+- 修复 点击的位置不准确
+## 0.1.7(2022-08-12)
+- 新增 支持 disabled 属性
+## 0.1.6(2022-07-06)
+- 修复 pc端宽度异常的bug
+## 0.1.5
+- 修复 pc端宽度异常的bug
+## 0.1.4(2022-07-05)
+- 优化 显示样式
+## 0.1.3(2022-06-02)
+- 修复 localdata 赋值不生效的 bug
+- 新增 支持  uni.scss 修改颜色
+- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
+## 0.1.2(2022-05-08)
+- 修复 当 value 为 0 时选择不生效的 bug
+## 0.1.1(2022-05-07)
+- 新增 记住上次的选项(仅 collection 存在时有效)
+## 0.1.0(2022-04-22)
+- 初始化

+ 562 - 0
uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue

@@ -0,0 +1,562 @@
+<template>
+	<view class="uni-stat__select">
+		<span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span>
+		<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
+			<view class="uni-select" :class="{'uni-select--disabled':disabled}">
+				<view class="uni-select__input-box" @click="toggleSelector">
+					<view v-if="current" class="uni-select__input-text">{{textShow}}</view>
+					<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
+					<view v-if="current && clear && !disabled" @click.stop="clearVal">
+						<uni-icons type="clear" color="#c0c4cc" size="24" />
+					</view>
+					<view v-else>
+						<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
+					</view>
+				</view>
+				<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
+				<view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector">
+					<view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view>
+					<scroll-view scroll-y="true" class="uni-select__selector-scroll">
+						<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0">
+							<text>{{emptyTips}}</text>
+						</view>
+						<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
+							@click="change(item)">
+							<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
+						</view>
+					</scroll-view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * DataChecklist 数据选择器
+	 * @description 通过数据渲染的下拉框组件
+	 * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
+	 * @property {String} value 默认值
+	 * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
+	 * @property {Boolean} clear 是否可以清空已选项
+	 * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
+	 * @property {String} label 左侧标题
+	 * @property {String} placeholder 输入框的提示文字
+	 * @property {Boolean} disabled 是否禁用
+	 * @property {String} placement 弹出位置
+	 * 	@value top   		顶部弹出
+	 * 	@value bottom		底部弹出(default)
+	 * @event {Function} change  选中发生变化触发
+	 */
+
+	export default {
+		name: "uni-data-select",
+		mixins: [uniCloud.mixinDatacom || {}],
+		props: {
+			localdata: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Number],
+				default: ''
+			},
+			label: {
+				type: String,
+				default: ''
+			},
+			placeholder: {
+				type: String,
+				default: '请选择'
+			},
+			emptyTips: {
+				type: String,
+				default: '无选项'
+			},
+			clear: {
+				type: Boolean,
+				default: true
+			},
+			defItem: {
+				type: Number,
+				default: 0
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			// 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
+			format: {
+				type: String,
+				default: ''
+			},
+			placement: {
+				type: String,
+				default: 'bottom'
+			}
+		},
+		data() {
+			return {
+				showSelector: false,
+				current: '',
+				mixinDatacomResData: [],
+				apps: [],
+				channels: [],
+				cacheKey: "uni-data-select-lastSelectedValue",
+			};
+		},
+		created() {
+			this.debounceGet = this.debounce(() => {
+				this.query();
+			}, 300);
+			if (this.collection && !this.localdata.length) {
+				this.debounceGet();
+			}
+		},
+		computed: {
+			typePlaceholder() {
+				const text = {
+					'opendb-stat-app-versions': '版本',
+					'opendb-app-channels': '渠道',
+					'opendb-app-list': '应用'
+				}
+				const common = this.placeholder
+				const placeholder = text[this.collection]
+				return placeholder ?
+					common + placeholder :
+					common
+			},
+			valueCom() {
+				// #ifdef VUE3
+				return this.modelValue;
+				// #endif
+				// #ifndef VUE3
+				return this.value;
+				// #endif
+			},
+			textShow() {
+				// 长文本显示
+				let text = this.current;
+				if (text.length > 10) {
+					return text.slice(0, 25) + '...';
+				}
+				return text;
+			},
+			getOffsetByPlacement() {
+				switch (this.placement) {
+					case 'top':
+						return "bottom:calc(100% + 12px);";
+					case 'bottom':
+						return "top:calc(100% + 12px);";
+				}
+			}
+		},
+
+		watch: {
+			localdata: {
+				immediate: true,
+				handler(val, old) {
+					if (Array.isArray(val) && old !== val) {
+						this.mixinDatacomResData = val
+					}
+				}
+			},
+			valueCom(val, old) {
+				this.initDefVal()
+			},
+			mixinDatacomResData: {
+				immediate: true,
+				handler(val) {
+					if (val.length) {
+						this.initDefVal()
+					}
+				}
+			},
+
+		},
+		methods: {
+			debounce(fn, time = 100) {
+				let timer = null
+				return function(...args) {
+					if (timer) clearTimeout(timer)
+					timer = setTimeout(() => {
+						fn.apply(this, args)
+					}, time)
+				}
+			},
+			// 执行数据库查询
+			query() {
+				this.mixinDatacomEasyGet();
+			},
+			// 监听查询条件变更事件
+			onMixinDatacomPropsChange() {
+				if (this.collection) {
+					this.debounceGet();
+				}
+			},
+			initDefVal() {
+				let defValue = ''
+				if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
+					defValue = this.valueCom
+				} else {
+					let strogeValue
+					if (this.collection) {
+						strogeValue = this.getCache()
+					}
+					if (strogeValue || strogeValue === 0) {
+						defValue = strogeValue
+					} else {
+						let defItem = ''
+						if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
+							defItem = this.mixinDatacomResData[this.defItem - 1].value
+						}
+						defValue = defItem
+					}
+					if (defValue || defValue === 0) {
+						this.emit(defValue)
+					}
+				}
+				const def = this.mixinDatacomResData.find(item => item.value === defValue)
+				this.current = def ? this.formatItemName(def) : ''
+			},
+
+			/**
+			 * @param {[String, Number]} value
+			 * 判断用户给的 value 是否同时为禁用状态
+			 */
+			isDisabled(value) {
+				let isDisabled = false;
+
+				this.mixinDatacomResData.forEach(item => {
+					if (item.value === value) {
+						isDisabled = item.disable
+					}
+				})
+
+				return isDisabled;
+			},
+
+			clearVal() {
+				this.emit('')
+				if (this.collection) {
+					this.removeCache()
+				}
+			},
+			change(item) {
+				if (!item.disable) {
+					this.showSelector = false
+					this.current = this.formatItemName(item)
+					this.emit(item.value)
+				}
+			},
+			emit(val) {
+				this.$emit('input', val)
+				this.$emit('update:modelValue', val)
+				this.$emit('change', val)
+				if (this.collection) {
+					this.setCache(val);
+				}
+			},
+			toggleSelector() {
+				if (this.disabled) {
+					return
+				}
+
+				this.showSelector = !this.showSelector
+			},
+			formatItemName(item) {
+				let {
+					text,
+					value,
+					channel_code
+				} = item
+				channel_code = channel_code ? `(${channel_code})` : ''
+
+				if (this.format) {
+					// 格式化输出
+					let str = "";
+					str = this.format;
+					for (let key in item) {
+						str = str.replace(new RegExp(`{${key}}`, "g"), item[key]);
+					}
+					return str;
+				} else {
+					return this.collection.indexOf('app-list') > 0 ?
+						`${text}(${value})` :
+						(
+							text ?
+							text :
+							`未命名${channel_code}`
+						)
+				}
+			},
+			// 获取当前加载的数据
+			getLoadData() {
+				return this.mixinDatacomResData;
+			},
+			// 获取当前缓存key
+			getCurrentCacheKey() {
+				return this.collection;
+			},
+			// 获取缓存
+			getCache(name = this.getCurrentCacheKey()) {
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				return cacheData[name];
+			},
+			// 设置缓存
+			setCache(value, name = this.getCurrentCacheKey()) {
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				cacheData[name] = value;
+				uni.setStorageSync(this.cacheKey, cacheData);
+			},
+			// 删除缓存
+			removeCache(name = this.getCurrentCacheKey()) {
+				let cacheData = uni.getStorageSync(this.cacheKey) || {};
+				delete cacheData[name];
+				uni.setStorageSync(this.cacheKey, cacheData);
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-base-color: #6a6a6a !default;
+	$uni-main-color: #333 !default;
+	$uni-secondary-color: #909399 !default;
+	$uni-border-3: #e5e5e5;
+
+	/* #ifndef APP-NVUE */
+	@media screen and (max-width: 500px) {
+		.hide-on-phone {
+			display: none;
+		}
+	}
+
+	/* #endif */
+	.uni-stat__select {
+		display: flex;
+		align-items: center;
+		// padding: 15px;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+		width: 100%;
+		flex: 1;
+		box-sizing: border-box;
+	}
+
+	.uni-stat-box {
+		width: 100%;
+		flex: 1;
+	}
+
+	.uni-stat__actived {
+		width: 100%;
+		flex: 1;
+		// outline: 1px solid #2979ff;
+	}
+
+	.uni-label-text {
+		font-size: 14px;
+		font-weight: bold;
+		color: $uni-base-color;
+		margin: auto 0;
+		margin-right: 5px;
+	}
+
+	.uni-select {
+		font-size: 14px;
+		border: 0px solid $uni-border-3;
+		box-sizing: border-box;
+		border-radius: 4px;
+		padding: 0 5px;
+		padding-left: 10px;
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		user-select: none;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		border-bottom: solid 0px $uni-border-3;
+		width: 100%;
+		flex: 1;
+		height: 35px;
+
+		&--disabled {
+			background-color: #f5f7fa;
+			cursor: not-allowed;
+		}
+	}
+
+	.uni-select__label {
+		font-size: 16px;
+		// line-height: 22px;
+		height: 35px;
+		padding-right: 10px;
+		color: $uni-secondary-color;
+	}
+
+	.uni-select__input-box {
+		height: 35px;
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex: 1;
+		flex-direction: row;
+		align-items: center;
+	}
+
+	.uni-select__input {
+		flex: 1;
+		font-size: 14px;
+		height: 22px;
+		line-height: 22px;
+	}
+
+	.uni-select__input-plac {
+		font-size: 14px;
+		color: $uni-secondary-color;
+	}
+
+	.uni-select__selector {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		/* #endif */
+		position: absolute;
+		left: 0;
+		width: 100%;
+		background-color: #FFFFFF;
+		border: 1px solid #EBEEF5;
+		border-radius: 6px;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+		z-index: 3;
+		padding: 4px 0;
+	}
+
+	.uni-select__selector-scroll {
+		/* #ifndef APP-NVUE */
+		max-height: 200px;
+		box-sizing: border-box;
+		/* #endif */
+	}
+
+	/* #ifdef H5 */
+	@media (min-width: 768px) {
+		.uni-select__selector-scroll {
+			max-height: 600px;
+		}
+	}
+
+	/* #endif */
+
+	.uni-select__selector-empty,
+	.uni-select__selector-item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		cursor: pointer;
+		/* #endif */
+		line-height: 35px;
+		font-size: 14px;
+		text-align: center;
+		/* border-bottom: solid 1px $uni-border-3; */
+		padding: 0px 10px;
+	}
+
+	.uni-select__selector-item:hover {
+		background-color: #f9f9f9;
+	}
+
+	.uni-select__selector-empty:last-child,
+	.uni-select__selector-item:last-child {
+		/* #ifndef APP-NVUE */
+		border-bottom: none;
+		/* #endif */
+	}
+
+	.uni-select__selector__disabled {
+		opacity: 0.4;
+		cursor: default;
+	}
+
+	/* picker 弹出层通用的指示小三角 */
+	.uni-popper__arrow_bottom,
+	.uni-popper__arrow_bottom::after,
+	.uni-popper__arrow_top,
+	.uni-popper__arrow_top::after,
+	{
+	position: absolute;
+	display: block;
+	width: 0;
+	height: 0;
+	border-color: transparent;
+	border-style: solid;
+	border-width: 6px;
+	}
+
+	.uni-popper__arrow_bottom {
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
+		top: -6px;
+		left: 10%;
+		margin-right: 3px;
+		border-top-width: 0;
+		border-bottom-color: #EBEEF5;
+	}
+
+	.uni-popper__arrow_bottom::after {
+		content: " ";
+		top: 1px;
+		margin-left: -6px;
+		border-top-width: 0;
+		border-bottom-color: #fff;
+	}
+
+	.uni-popper__arrow_top {
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
+		bottom: -6px;
+		left: 10%;
+		margin-right: 3px;
+		border-bottom-width: 0;
+		border-top-color: #EBEEF5;
+	}
+
+	.uni-popper__arrow_top::after {
+		content: " ";
+		bottom: 1px;
+		margin-left: -6px;
+		border-bottom-width: 0;
+		border-top-color: #fff;
+	}
+
+
+	.uni-select__input-text {
+		// width: 280px;
+		width: 100%;
+		color: $uni-main-color;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		-o-text-overflow: ellipsis;
+		overflow: hidden;
+	}
+
+	.uni-select__input-placeholder {
+		color: $uni-base-color;
+		font-size: 12px;
+	}
+
+	.uni-select--mask {
+		position: fixed;
+		top: 0;
+		bottom: 0;
+		right: 0;
+		left: 0;
+		z-index: 2;
+	}
+</style>

+ 86 - 0
uni_modules/uni-data-select/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "uni-data-select",
+  "displayName": "uni-data-select 下拉框选择器",
+  "version": "1.0.8",
+  "description": "通过数据驱动的下拉框选择器",
+  "keywords": [
+    "uni-ui",
+    "select",
+    "uni-data-select",
+    "下拉框",
+    "下拉选"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.1.1"
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-load-more"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "u",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+        "QQ": "u",
+        "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 8 - 0
uni_modules/uni-data-select/readme.md

@@ -0,0 +1,8 @@
+## DataSelect 下拉框选择器
+> **组件名:uni-data-select**
+> 代码块: `uDataSelect`
+
+当选项过多时,使用下拉菜单展示并选择内容
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 168 - 0
uni_modules/uni-datetime-picker/changelog.md

@@ -0,0 +1,168 @@
+## 2.2.38(2024-10-15)
+- 修复 微信小程序中的getSystemInfo警告
+## 2.2.37(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 2.2.36(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 2.2.35(2024-09-21)
+- 修复 没有选中日期时点击确定直接报错的Bug [详情](https://ask.dcloud.net.cn/question/198168)
+## 2.2.34(2024-04-24)
+- 新增 日期点击事件,在点击日期时会触发该事件。
+## 2.2.33(2024-04-15)
+- 修复 抖音小程序事件传递失效bug
+## 2.2.32(2024-02-20)
+- 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844)
+## 2.2.31(2024-02-20)
+- 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841)
+## 2.2.30(2024-01-31)
+- 修复 隐藏“秒”时,在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788)
+## 2.2.29(2024-01-20)
+- 新增 show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694)
+## 2.2.28(2024-01-18)
+- 去除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815)
+## 2.2.27(2024-01-10)
+- 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815)
+## 2.2.26(2024-01-08)
+- 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834)
+## 2.2.25(2023-10-18)
+- 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
+## 2.2.24(2023-06-02)
+- 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
+- 优化 当前月可以选择上月、下月的日期的Bug
+## 2.2.23(2023-05-02)
+- 修复 部分情况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737)
+- 修复 部分平台及设备第一次点击无法显示弹框的Bug
+- 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979)
+## 2.2.22(2023-03-30)
+- 修复 日历 picker 修改年月后,自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937)
+- 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979)
+## 2.2.21(2023-02-20)
+- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
+## 2.2.20(2023-02-17)
+- 优化 值为空依然选中当天问题
+- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
+- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
+- 优化 字节小程序日期时间范围选择,底部日期换行的Bug
+## 2.2.19(2023-02-09)
+- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
+## 2.2.18(2023-02-08)
+- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
+- 优化 PC端输入日期格式错误时返回当前日期时间
+- 优化 PC端输入日期时间超出 start、end 限制的Bug
+- 优化 移动端日期时间范围用法时间展示不完整问题
+## 2.2.17(2023-02-04)
+- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
+- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
+## 2.2.16(2023-02-02)
+- 修复 字节小程序报错的Bug
+## 2.2.15(2023-02-02)
+- 修复 某些情况切换月份错误的Bug
+## 2.2.14(2023-01-30)
+- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
+## 2.2.13(2023-01-10)
+- 修复 多次加载组件造成内存占用的Bug
+## 2.2.12(2022-12-01)
+- 修复 vue3 下 i18n 国际化初始值不正确的Bug
+## 2.2.11(2022-09-19)
+- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
+## 2.2.10(2022-09-19)
+- 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
+## 2.2.9(2022-09-16)
+- 可以使用 uni-scss 控制主题色
+## 2.2.8(2022-09-08)
+- 修复 close事件无效的Bug
+## 2.2.7(2022-09-05)
+- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
+## 2.2.6(2022-06-30)
+- 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致
+## 2.2.5(2022-06-24)
+- 修复 日历顶部年月及底部确认未国际化的Bug
+## 2.2.4(2022-03-31)
+- 修复 Vue3 下动态赋值,单选类型未响应的Bug
+## 2.2.3(2022-03-28)
+- 修复 Vue3 下动态赋值未响应的Bug
+## 2.2.2(2021-12-10)
+- 修复 clear-icon 属性在小程序平台不生效的Bug
+## 2.2.1(2021-12-10)
+- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug
+## 2.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+## 2.1.5(2021-11-09)
+- 新增 提供组件设计资源,组件样式调整
+## 2.1.4(2021-09-10)
+- 修复 hide-second 在移动端的Bug
+- 修复 单选赋默认值时,赋值日期未高亮的Bug
+- 修复 赋默认值时,移动端未正确显示时间的Bug
+## 2.1.3(2021-09-09)
+- 新增 hide-second 属性,支持只使用时分,隐藏秒
+## 2.1.2(2021-09-03)
+- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
+- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
+- 优化 调整字号大小,美化日历界面
+- 修复 因国际化导致的 placeholder 失效的Bug
+## 2.1.1(2021-08-24)
+- 新增 支持国际化
+- 优化 范围选择器在 pc 端过宽的问题
+## 2.1.0(2021-08-09)
+- 新增 适配 vue3
+## 2.0.19(2021-08-09)
+- 新增 支持作为 uni-forms 子组件相关功能
+- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
+## 2.0.18(2021-08-05)
+- 修复 type 属性动态赋值无效的Bug
+- 修复 ‘确认’按钮被 tabbar 遮盖 bug
+- 修复 组件未赋值时范围选左、右日历相同的Bug
+## 2.0.17(2021-08-04)
+- 修复 范围选未正确显示当前值的Bug
+- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
+## 2.0.16(2021-07-21)
+- 新增 return-type 属性支持返回 date 日期对象
+## 2.0.15(2021-07-14)
+- 修复 单选日期类型,初始赋值后不在当前日历的Bug
+- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
+- 优化 移动端移除显示框的清空按钮,无实际用途
+## 2.0.14(2021-07-14)
+- 修复 组件赋值为空,界面未更新的Bug
+- 修复 start 和 end 不能动态赋值的Bug
+- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug
+## 2.0.13(2021-07-08)
+- 修复 范围选择不能动态赋值的Bug
+## 2.0.12(2021-07-08)
+- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
+## 2.0.11(2021-07-08)
+- 优化 弹出层在超出视窗边缘定位不准确的问题
+## 2.0.10(2021-07-08)
+- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug
+- 优化 弹出层在超出视窗边缘被遮盖的问题
+## 2.0.9(2021-07-07)
+- 新增 maskClick 事件
+- 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px
+- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
+## 2.0.8(2021-07-07)
+- 新增 日期时间显示框支持插槽
+## 2.0.7(2021-07-01)
+- 优化 添加 uni-icons 依赖
+## 2.0.6(2021-05-22)
+- 修复 图标在小程序上不显示的Bug
+- 优化 重命名引用组件,避免潜在组件命名冲突
+## 2.0.5(2021-05-20)
+- 优化 代码目录扁平化
+## 2.0.4(2021-05-12)
+- 新增 组件示例地址
+## 2.0.3(2021-05-10)
+- 修复 ios 下不识别 '-' 日期格式的Bug
+- 优化 pc 下弹出层添加边框和阴影
+## 2.0.2(2021-05-08)
+- 修复 在 admin 中获取弹出层定位错误的bug
+## 2.0.1(2021-05-08)
+- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
+## 2.0.0(2021-04-30)
+- 支持日历形式的日期+时间的范围选择
+ > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
+## 1.0.6(2021-03-18)
+- 新增 hide-second 属性,时间支持仅选择时、分
+- 修复 选择跟显示的日期不一样的Bug
+- 修复 chang事件触发2次的Bug
+- 修复 分、秒 end 范围错误的Bug
+- 优化 更好的 nvue 适配

+ 177 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue

@@ -0,0 +1,177 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable':weeks.disable,
+		'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
+		'uni-calendar-item--multiple': weeks.multiple,
+		'uni-calendar-item--after-checked-x':weeks.afterMultiple,
+		}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
+		<view class="uni-calendar-item__weeks-box-item" :class="{
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
+				'uni-calendar-item--checked-range-text': checkHover,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">
+			<text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
+		</view>
+		<view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			checkHover: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			},
+			handleMousemove(weeks) {
+				this.$emit('handleMouse', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" >
+	$uni-primary: #007aff !default;
+
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		margin: 1px 0;
+		position: relative;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		font-size: 14px;
+		// font-family: Lato-Bold, Lato;
+		font-weight: bold;
+		color: darken($color: $uni-primary, $amount: 40%);
+	}
+
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 40px;
+		height: 40px;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+
+	.uni-calendar-item__weeks-box-circle {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		background-color: #dd524d;
+
+	}
+
+	.uni-calendar-item__weeks-box .uni-calendar-item--disable {
+		cursor: default;
+	}
+
+	.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
+		color: #D1D1D1;
+	}
+
+	.uni-calendar-item--today {
+		position: absolute;
+		top: 10px;
+		right: 17%;
+		background-color: #dd524d;
+		width:6px;
+		height: 6px;
+		border-radius: 50%;
+	}
+
+	.uni-calendar-item--extra {
+		color: #dd524d;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item__weeks-box .uni-calendar-item--checked {
+		background-color: $uni-primary;
+		border-radius: 50%;
+		box-sizing: border-box;
+		border: 3px solid #fff;
+	}
+
+	.uni-calendar-item--checked .uni-calendar-item--checked-text {
+		color: #fff;
+	}
+
+	.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
+		color: #333;
+	}
+
+	.uni-calendar-item--multiple {
+		background-color:  #F6F7FC;
+		// color: #fff;
+	}
+
+	.uni-calendar-item--multiple .uni-calendar-item--before-checked,
+	.uni-calendar-item--multiple .uni-calendar-item--after-checked {
+		background-color: $uni-primary;
+		border-radius: 50%;
+		box-sizing: border-box;
+		border: 3px solid #F6F7FC;
+	}
+
+	.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
+	.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
+		color: #fff;
+	}
+
+	.uni-calendar-item--before-checked-x {
+		border-top-left-radius: 50px;
+		border-bottom-left-radius: 50px;
+		box-sizing: border-box;
+		background-color: #F6F7FC;
+	}
+
+	.uni-calendar-item--after-checked-x {
+		border-top-right-radius: 50px;
+		border-bottom-right-radius: 50px;
+		background-color: #F6F7FC;
+	}
+</style>

+ 947 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue

@@ -0,0 +1,947 @@
+<template>
+	<view class="uni-calendar" @mouseleave="leaveCale">
+
+		<view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
+			@click="maskClick"></view>
+
+		<view v-if="insert || show" class="uni-calendar__content"
+			:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
+			<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
+
+				<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
+					<text
+						class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text>
+				</picker>
+
+				<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+
+				<view v-if="!insert" class="dialog-close" @click="maskClick">
+					<view class="dialog-close-plus" data-id="close"></view>
+					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
+				</view>
+			</view>
+			<view class="uni-calendar__box">
+
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+
+				<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{MONText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
+					</view>
+				</view>
+
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
+							:checkHover="range" @change="choiceDate" @handleMouse="handleMouse">
+						</calendar-item>
+					</view>
+				</view>
+			</view>
+
+			<view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top"
+				style="padding: 0 80px;">
+				<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
+				<time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time"
+					:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
+				</time-picker>
+			</view>
+
+			<view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top">
+				<view class="uni-date-changed--time-start">
+					<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
+					</view>
+					<time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false"
+						:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
+					</time-picker>
+				</view>
+				<view style="line-height: 50px;">
+					<uni-icons type="arrowthinright" color="#999"></uni-icons>
+				</view>
+				<view class="uni-date-changed--time-end">
+					<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
+					<time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false"
+						:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
+					</time-picker>
+				</view>
+			</view>
+
+			<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
+				<view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		Calendar,
+		getDate,
+		getTime
+	} from './util.js';
+	import calendarItem from './calendar-item.vue'
+	import timePicker from './time-picker.vue'
+
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(i18nMessages)
+
+	/**
+	 * Calendar 日历
+	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+	 * @property {String} date 自定义当前时间,默认为今天
+	 * @property {String} startDate 日期选择范围-开始日期
+	 * @property {String} endDate 日期选择范围-结束日期
+	 * @property {Boolean} range 范围选择
+	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
+	 * 	@value true 弹窗模式
+	 * 	@value false 插入模式
+	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+	 * @property {Boolean} showMonth 是否选择月份为背景
+	 * @property {[String} defaultValue 选择器打开时默认显示的时间
+	 * @event {Function} change 日期改变,`insert :ture` 时生效
+	 * @event {Function} confirm 确认选择`insert :false` 时生效
+	 * @event {Function} monthSwitch 切换月份时触发
+	 * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+	 */
+	export default {
+		components: {
+			calendarItem,
+			timePicker
+		},
+
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		props: {
+			date: {
+				type: String,
+				default: ''
+			},
+			defTime: {
+				type: [String, Object],
+				default: ''
+			},
+			selectableTimes: {
+				type: [Object],
+				default () {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			startDate: {
+				type: String,
+				default: ''
+			},
+			endDate: {
+				type: String,
+				default: ''
+			},
+			startPlaceholder: {
+				type: String,
+				default: ''
+			},
+			endPlaceholder: {
+				type: String,
+				default: ''
+			},
+			range: {
+				type: Boolean,
+				default: false
+			},
+			hasTime: {
+				type: Boolean,
+				default: false
+			},
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			showMonth: {
+				type: Boolean,
+				default: true
+			},
+			clearDate: {
+				type: Boolean,
+				default: true
+			},
+			checkHover: {
+				type: Boolean,
+				default: true
+			},
+			hideSecond: {
+				type: [Boolean],
+				default: false
+			},
+			pleStatus: {
+				type: Object,
+				default () {
+					return {
+						before: '',
+						after: '',
+						data: [],
+						fulldate: ''
+					}
+				}
+			},
+			defaultValue: {
+				type: [String, Object, Array],
+				default: ''
+			}
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: {},
+				aniMaskShow: false,
+				firstEnter: true,
+				time: '',
+				timeRange: {
+					startTime: '',
+					endTime: ''
+				},
+				tempSingleDate: '',
+				tempRange: {
+					before: '',
+					after: ''
+				}
+			}
+		},
+		watch: {
+			date: {
+				immediate: true,
+				handler(newVal) {
+					if (!this.range) {
+						this.tempSingleDate = newVal
+						setTimeout(() => {
+							this.init(newVal)
+						}, 100)
+					}
+				}
+			},
+			defTime: {
+				immediate: true,
+				handler(newVal) {
+					if (!this.range) {
+						this.time = newVal
+					} else {
+						this.timeRange.startTime = newVal.start
+						this.timeRange.endTime = newVal.end
+					}
+				}
+			},
+			startDate(val) {
+				// 字节小程序 watch 早于 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setStartDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			endDate(val) {
+				// 字节小程序 watch 早于 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setEndDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			selected(newVal) {
+				// 字节小程序 watch 早于 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			},
+			pleStatus: {
+				immediate: true,
+				handler(newVal) {
+					const {
+						before,
+						after,
+						fulldate,
+						which
+					} = newVal
+					this.tempRange.before = before
+					this.tempRange.after = after
+					setTimeout(() => {
+						if (fulldate) {
+							this.cale.setHoverMultiple(fulldate)
+							if (before && after) {
+								this.cale.lastHover = true
+								if (this.rangeWithinMonth(after, before)) return
+								this.setDate(before)
+							} else {
+								this.cale.setMultiple(fulldate)
+								this.setDate(this.nowDate.fullDate)
+								this.calendar.fullDate = ''
+								this.cale.lastHover = false
+							}
+						} else {
+							// 字节小程序 watch 早于 created
+							if (!this.cale) {
+								return
+							}
+
+							this.cale.setDefaultMultiple(before, after)
+							if (which === 'left' && before) {
+								this.setDate(before)
+								this.weeks = this.cale.weeks
+							} else if (after) {
+								this.setDate(after)
+								this.weeks = this.cale.weeks
+							}
+							this.cale.lastHover = true
+						}
+					}, 16)
+				}
+			}
+		},
+		computed: {
+			timepickerStartTime() {
+				const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
+				return activeDate === this.startDate ? this.selectableTimes.start : ''
+			},
+			timepickerEndTime() {
+				const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
+				return activeDate === this.endDate ? this.selectableTimes.end : ''
+			},
+			/**
+			 * for i18n
+			 */
+			selectDateText() {
+				return t("uni-datetime-picker.selectDate")
+			},
+			startDateText() {
+				return this.startPlaceholder || t("uni-datetime-picker.startDate")
+			},
+			endDateText() {
+				return this.endPlaceholder || t("uni-datetime-picker.endDate")
+			},
+			okText() {
+				return t("uni-datetime-picker.ok")
+			},
+			yearText() {
+				return t("uni-datetime-picker.year")
+			},
+			monthText() {
+				return t("uni-datetime-picker.month")
+			},
+			MONText() {
+				return t("uni-calender.MON")
+			},
+			TUEText() {
+				return t("uni-calender.TUE")
+			},
+			WEDText() {
+				return t("uni-calender.WED")
+			},
+			THUText() {
+				return t("uni-calender.THU")
+			},
+			FRIText() {
+				return t("uni-calender.FRI")
+			},
+			SATText() {
+				return t("uni-calender.SAT")
+			},
+			SUNText() {
+				return t("uni-calender.SUN")
+			},
+			confirmText() {
+				return t("uni-calender.confirm")
+			},
+		},
+		created() {
+			// 获取日历方法实例
+			this.cale = new Calendar({
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			// 选中某一天
+			this.init(this.date)
+		},
+		methods: {
+			leaveCale() {
+				this.firstEnter = true
+			},
+			handleMouse(weeks) {
+				if (weeks.disable) return
+				if (this.cale.lastHover) return
+				let {
+					before,
+					after
+				} = this.cale.multipleStatus
+				if (!before) return
+				this.calendar = weeks
+				// 设置范围选
+				this.cale.setHoverMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				// hover时,进入一个日历,更新另一个
+				if (this.firstEnter) {
+					this.$emit('firstEnterCale', this.cale.multipleStatus)
+					this.firstEnter = false
+				}
+			},
+			rangeWithinMonth(A, B) {
+				const [yearA, monthA] = A.split('-')
+				const [yearB, monthB] = B.split('-')
+				return yearA === yearB && monthA === monthB
+			},
+			// 蒙版点击事件
+			maskClick() {
+				this.close()
+				this.$emit('maskClose')
+			},
+
+			clearCalender() {
+				if (this.range) {
+					this.timeRange.startTime = ''
+					this.timeRange.endTime = ''
+					this.tempRange.before = ''
+					this.tempRange.after = ''
+					this.cale.multipleStatus.before = ''
+					this.cale.multipleStatus.after = ''
+					this.cale.multipleStatus.data = []
+					this.cale.lastHover = false
+				} else {
+					this.time = ''
+					this.tempSingleDate = ''
+				}
+				this.calendar.fullDate = ''
+				this.setDate(new Date())
+			},
+
+			bindDateChange(e) {
+				const value = e.detail.value + '-1'
+				this.setDate(value)
+			},
+			/**
+			 * 初始化日期显示
+			 * @param {Object} date
+			 */
+			init(date) {
+				// 字节小程序 watch 早于 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setDate(date || new Date())
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+				this.calendar = {
+					...this.nowDate
+				}
+				if (!date) {
+					// 优化date为空默认不选中今天
+					this.calendar.fullDate = ''
+					if (this.defaultValue && !this.range) {
+						// 暂时只支持移动端非范围选择
+						const defaultDate = new Date(this.defaultValue)
+						const fullDate = getDate(defaultDate)
+						const year = defaultDate.getFullYear()
+						const month = defaultDate.getMonth() + 1
+						const date = defaultDate.getDate()
+						const day = defaultDate.getDay()
+						this.calendar = {
+								fullDate,
+								year,
+								month,
+								date,
+								day
+							},
+							this.tempSingleDate = fullDate
+						this.time = getTime(defaultDate, this.hideSecond)
+					}
+				}
+			},
+			/**
+			 * 打开日历弹窗
+			 */
+			open() {
+				// 弹窗模式并且清理数据
+				if (this.clearDate && !this.insert) {
+					this.cale.cleanMultipleStatus()
+					this.init(this.date)
+				}
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.aniMaskShow = true
+					}, 50)
+				})
+			},
+			/**
+			 * 关闭日历弹窗
+			 */
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+						this.$emit('close')
+					}, 300)
+				})
+			},
+			/**
+			 * 确认按钮
+			 */
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			/**
+			 * 变化触发
+			 */
+			change(isSingleChange) {
+				if (!this.insert && !isSingleChange) return
+				this.setEmit('change')
+			},
+			/**
+			 * 选择月份触发
+			 */
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			/**
+			 * 派发事件
+			 * @param {Object} name
+			 */
+			setEmit(name) {
+				if (!this.range) {
+					if (!this.calendar.fullDate) {
+						this.calendar = this.cale.getInfo(new Date())
+						this.tempSingleDate = this.calendar.fullDate
+					}
+					if (this.hasTime && !this.time) {
+						this.time = getTime(new Date(), this.hideSecond)
+					}
+				}
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					time: this.time,
+					timeRange: this.timeRange,
+					fulldate: fullDate,
+					extraInfo: extraInfo || {}
+				})
+			},
+			/**
+			 * 选择天触发
+			 * @param {Object} weeks
+			 */
+			choiceDate(weeks) {
+				if (weeks.disable) return
+				this.calendar = weeks
+				this.calendar.userChecked = true
+				// 设置多选
+				this.cale.setMultiple(this.calendar.fullDate, true)
+				this.weeks = this.cale.weeks
+				this.tempSingleDate = this.calendar.fullDate
+				const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
+				const afterDate = new Date(this.cale.multipleStatus.after).getTime()
+				if (beforeDate > afterDate && afterDate) {
+					this.tempRange.before = this.cale.multipleStatus.after
+					this.tempRange.after = this.cale.multipleStatus.before
+				} else {
+					this.tempRange.before = this.cale.multipleStatus.before
+					this.tempRange.after = this.cale.multipleStatus.after
+				}
+				this.change(true)
+			},
+			changeMonth(type) {
+				let newDate
+				if (type === 'pre') {
+					newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
+				} else if (type === 'next') {
+					newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
+				}
+
+				this.setDate(newDate)
+				this.monthSwitch()
+			},
+			/**
+			 * 设置日期
+			 * @param {Object} date
+			 */
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: rgba(0, 0, 0, 0.4);
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		bottom: calc(var(--window-bottom));
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__content-mobile {
+		border-top-left-radius: 10px;
+		border-top-right-radius: 10px;
+		box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+	}
+
+	.uni-calendar__header-mobile {
+		padding: 10px;
+		padding-bottom: 0;
+	}
+
+	.uni-calendar--fixed-top {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+		border-top-color: rgba(0, 0, 0, 0.4);
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: #fff;
+		background-color: #f1f1f1;
+	}
+
+	.uni-calendar__header-text {
+		text-align: center;
+		width: 100px;
+		font-size: 15px;
+		color: #666;
+	}
+
+	.uni-calendar__button-text {
+		text-align: center;
+		width: 100px;
+		font-size: 14px;
+		color: $uni-primary;
+		/* #ifndef APP-NVUE */
+		letter-spacing: 3px;
+		/* #endif */
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 9px;
+		height: 9px;
+		border-left-color: #808080;
+		border-left-style: solid;
+		border-left-width: 1px;
+		border-top-color: #555555;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 40px;
+		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar__weeks-day-text {
+		font-size: 12px;
+		color: #B2B2B2;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+		// padding: 0 10px;
+		padding-bottom: 7px;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: #999;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+
+	.uni-date-changed {
+		padding: 0 10px;
+		// line-height: 50px;
+		text-align: center;
+		color: #333;
+		border-top-color: #DCDCDC;
+		;
+		border-top-style: solid;
+		border-top-width: 1px;
+		flex: 1;
+	}
+
+	.uni-date-btn--ok {
+		padding: 20px 15px;
+	}
+
+	.uni-date-changed--time-start {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+	}
+
+	.uni-date-changed--time-end {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+	}
+
+	.uni-date-changed--time-date {
+		color: #999;
+		line-height: 50px;
+		/* #ifdef MP-TOUTIAO */
+		font-size: 16px;
+		/* #endif */
+		margin-right: 5px;
+		// opacity: 0.6;
+	}
+
+	.time-picker-style {
+		// width: 62px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center
+	}
+
+	.mr-10 {
+		margin-right: 10px;
+	}
+
+	.dialog-close {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		padding: 0 25px;
+		margin-top: 10px;
+	}
+
+	.dialog-close-plus {
+		width: 16px;
+		height: 2px;
+		background-color: #737987;
+		border-radius: 2px;
+		transform: rotate(45deg);
+	}
+
+	.dialog-close-rotate {
+		position: absolute;
+		transform: rotate(-45deg);
+	}
+
+	.uni-datetime-picker--btn {
+		border-radius: 100px;
+		height: 40px;
+		line-height: 40px;
+		background-color: $uni-primary;
+		color: #fff;
+		font-size: 16px;
+		letter-spacing: 2px;
+	}
+
+	/* #ifndef APP-NVUE */
+	.uni-datetime-picker--btn:active {
+		opacity: 0.7;
+	}
+
+	/* #endif */
+</style>

+ 22 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json

@@ -0,0 +1,22 @@
+{
+	"uni-datetime-picker.selectDate": "select date",
+	"uni-datetime-picker.selectTime": "select time",
+	"uni-datetime-picker.selectDateTime": "select date and time",
+	"uni-datetime-picker.startDate": "start date",
+	"uni-datetime-picker.endDate": "end date",
+	"uni-datetime-picker.startTime": "start time",
+	"uni-datetime-picker.endTime": "end time",
+	"uni-datetime-picker.ok": "ok",
+	"uni-datetime-picker.clear": "clear",
+	"uni-datetime-picker.cancel": "cancel",
+	"uni-datetime-picker.year": "-",
+	"uni-datetime-picker.month": "",
+	"uni-calender.MON": "MON",
+	"uni-calender.TUE": "TUE",
+	"uni-calender.WED": "WED",
+	"uni-calender.THU": "THU",
+	"uni-calender.FRI": "FRI",
+	"uni-calender.SAT": "SAT",
+	"uni-calender.SUN": "SUN",
+	"uni-calender.confirm": "confirm"
+}

+ 8 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 22 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json

@@ -0,0 +1,22 @@
+{
+	"uni-datetime-picker.selectDate": "选择日期",
+	"uni-datetime-picker.selectTime": "选择时间",
+	"uni-datetime-picker.selectDateTime": "选择日期时间",
+	"uni-datetime-picker.startDate": "开始日期",
+	"uni-datetime-picker.endDate": "结束日期",
+	"uni-datetime-picker.startTime": "开始时间",
+	"uni-datetime-picker.endTime": "结束时间",
+	"uni-datetime-picker.ok": "确定",
+	"uni-datetime-picker.clear": "清除",
+	"uni-datetime-picker.cancel": "取消",
+	"uni-datetime-picker.year": "年",
+	"uni-datetime-picker.month": "月",
+	"uni-calender.SUN": "日",
+	"uni-calender.MON": "一",
+	"uni-calender.TUE": "二",
+	"uni-calender.WED": "三",
+	"uni-calender.THU": "四",
+	"uni-calender.FRI": "五",
+	"uni-calender.SAT": "六",
+	"uni-calender.confirm": "确认"
+}

+ 22 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json

@@ -0,0 +1,22 @@
+{
+  "uni-datetime-picker.selectDate": "選擇日期",
+  "uni-datetime-picker.selectTime": "選擇時間",
+  "uni-datetime-picker.selectDateTime": "選擇日期時間",
+  "uni-datetime-picker.startDate": "開始日期",
+  "uni-datetime-picker.endDate": "結束日期",
+  "uni-datetime-picker.startTime": "開始时间",
+  "uni-datetime-picker.endTime": "結束时间",
+  "uni-datetime-picker.ok": "確定",
+  "uni-datetime-picker.clear": "清除",
+  "uni-datetime-picker.cancel": "取消",
+  "uni-datetime-picker.year": "年",
+  "uni-datetime-picker.month": "月",
+  "uni-calender.SUN": "日",
+  "uni-calender.MON": "一",
+  "uni-calender.TUE": "二",
+  "uni-calender.WED": "三",
+  "uni-calender.THU": "四",
+  "uni-calender.FRI": "五",
+  "uni-calender.SAT": "六",
+  "uni-calender.confirm": "確認"
+}

+ 940 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue

@@ -0,0 +1,940 @@
+<template>
+	<view class="uni-datetime-picker">
+		<view @click="initTimePicker">
+			<slot>
+				<view class="uni-datetime-picker-timebox-pointer"
+					:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
+					<text class="uni-datetime-picker-text">{{time}}</text>
+					<view v-if="!time" class="uni-datetime-picker-time">
+						<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
+					</view>
+				</view>
+			</slot>
+		</view>
+		<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
+		<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
+			:style="fixNvueBug">
+			<view class="uni-title">
+				<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
+			</view>
+			<view v-if="dateShow" class="uni-datetime-picker__container-box">
+				<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
+					@change="bindDateChange">
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+				</picker-view>
+				<!-- 兼容 nvue 不支持伪类 -->
+				<text class="uni-datetime-picker-sign sign-left">-</text>
+				<text class="uni-datetime-picker-sign sign-right">-</text>
+			</view>
+			<view v-if="timeShow" class="uni-datetime-picker__container-box">
+				<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
+					:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column v-if="!hideSecond">
+						<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+				</picker-view>
+				<!-- 兼容 nvue 不支持伪类 -->
+				<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
+				<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
+			</view>
+			<view class="uni-datetime-picker-btn">
+				<view @click="clearTime">
+					<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
+				</view>
+				<view class="uni-datetime-picker-btn-group">
+					<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
+						<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
+					</view>
+					<view @click="setTime">
+						<text class="uni-datetime-picker-btn-text">{{okText}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(i18nMessages)
+	import {
+		fixIosDateFormat
+	} from './util'
+
+	/**
+	 * DatetimePicker 时间选择器
+	 * @description 可以同时选择日期和时间的选择器
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
+	 * @property {String} type = [datetime | date | time] 显示模式
+	 * @property {Boolean} multiple = [true|false] 是否多选
+	 * @property {String|Number} value 默认值
+	 * @property {String|Number} start 起始日期或时间
+	 * @property {String|Number} end 起始日期或时间
+	 * @property {String} return-type = [timestamp | string]
+	 * @event {Function} change  选中发生变化触发
+	 */
+
+	export default {
+		name: 'UniDatetimePicker',
+		data() {
+			return {
+				indicatorStyle: `height: 50px;`,
+				visible: false,
+				fixNvueBug: {},
+				dateShow: true,
+				timeShow: true,
+				title: '日期和时间',
+				// 输入框当前时间
+				time: '',
+				// 当前的年月日时分秒
+				year: 1920,
+				month: 0,
+				day: 0,
+				hour: 0,
+				minute: 0,
+				second: 0,
+				// 起始时间
+				startYear: 1920,
+				startMonth: 1,
+				startDay: 1,
+				startHour: 0,
+				startMinute: 0,
+				startSecond: 0,
+				// 结束时间
+				endYear: 2120,
+				endMonth: 12,
+				endDay: 31,
+				endHour: 23,
+				endMinute: 59,
+				endSecond: 59,
+			}
+		},
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		props: {
+			type: {
+				type: String,
+				default: 'datetime'
+			},
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Number],
+				default: ''
+			},
+			start: {
+				type: [Number, String],
+				default: ''
+			},
+			end: {
+				type: [Number, String],
+				default: ''
+			},
+			returnType: {
+				type: String,
+				default: 'string'
+			},
+			disabled: {
+				type: [Boolean, String],
+				default: false
+			},
+			border: {
+				type: [Boolean, String],
+				default: true
+			},
+			hideSecond: {
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		watch: {
+			// #ifndef VUE3
+			value: {
+				handler(newVal) {
+					if (newVal) {
+						this.parseValue(fixIosDateFormat(newVal))
+						this.initTime(false)
+					} else {
+						this.time = ''
+						this.parseValue(Date.now())
+					}
+				},
+				immediate: true
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				handler(newVal) {
+					if (newVal) {
+						this.parseValue(fixIosDateFormat(newVal))
+						this.initTime(false)
+					} else {
+						this.time = ''
+						this.parseValue(Date.now())
+					}
+				},
+				immediate: true
+			},
+			// #endif
+			type: {
+				handler(newValue) {
+					if (newValue === 'date') {
+						this.dateShow = true
+						this.timeShow = false
+						this.title = '日期'
+					} else if (newValue === 'time') {
+						this.dateShow = false
+						this.timeShow = true
+						this.title = '时间'
+					} else {
+						this.dateShow = true
+						this.timeShow = true
+						this.title = '日期和时间'
+					}
+				},
+				immediate: true
+			},
+			start: {
+				handler(newVal) {
+					this.parseDatetimeRange(fixIosDateFormat(newVal), 'start')
+				},
+				immediate: true
+			},
+			end: {
+				handler(newVal) {
+					this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
+				},
+				immediate: true
+			},
+
+			// 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
+			months(newVal) {
+				this.checkValue('month', this.month, newVal)
+			},
+			days(newVal) {
+				this.checkValue('day', this.day, newVal)
+			},
+			hours(newVal) {
+				this.checkValue('hour', this.hour, newVal)
+			},
+			minutes(newVal) {
+				this.checkValue('minute', this.minute, newVal)
+			},
+			seconds(newVal) {
+				this.checkValue('second', this.second, newVal)
+			}
+		},
+		computed: {
+			// 当前年、月、日、时、分、秒选择范围
+			years() {
+				return this.getCurrentRange('year')
+			},
+
+			months() {
+				return this.getCurrentRange('month')
+			},
+
+			days() {
+				return this.getCurrentRange('day')
+			},
+
+			hours() {
+				return this.getCurrentRange('hour')
+			},
+
+			minutes() {
+				return this.getCurrentRange('minute')
+			},
+
+			seconds() {
+				return this.getCurrentRange('second')
+			},
+
+			// picker 当前值数组
+			ymd() {
+				return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
+			},
+			hms() {
+				return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
+			},
+
+			// 当前 date 是 start
+			currentDateIsStart() {
+				return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
+			},
+
+			// 当前 date 是 end
+			currentDateIsEnd() {
+				return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
+			},
+
+			// 当前年、月、日、时、分、秒的最小值和最大值
+			minYear() {
+				return this.startYear
+			},
+			maxYear() {
+				return this.endYear
+			},
+			minMonth() {
+				if (this.year === this.startYear) {
+					return this.startMonth
+				} else {
+					return 1
+				}
+			},
+			maxMonth() {
+				if (this.year === this.endYear) {
+					return this.endMonth
+				} else {
+					return 12
+				}
+			},
+			minDay() {
+				if (this.year === this.startYear && this.month === this.startMonth) {
+					return this.startDay
+				} else {
+					return 1
+				}
+			},
+			maxDay() {
+				if (this.year === this.endYear && this.month === this.endMonth) {
+					return this.endDay
+				} else {
+					return this.daysInMonth(this.year, this.month)
+				}
+			},
+			minHour() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart) {
+						return this.startHour
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					return this.startHour
+				}
+			},
+			maxHour() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd) {
+						return this.endHour
+					} else {
+						return 23
+					}
+				}
+				if (this.type === 'time') {
+					return this.endHour
+				}
+			},
+			minMinute() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart && this.hour === this.startHour) {
+						return this.startMinute
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.startHour) {
+						return this.startMinute
+					} else {
+						return 0
+					}
+				}
+			},
+			maxMinute() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd && this.hour === this.endHour) {
+						return this.endMinute
+					} else {
+						return 59
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.endHour) {
+						return this.endMinute
+					} else {
+						return 59
+					}
+				}
+			},
+			minSecond() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
+						return this.startSecond
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.startHour && this.minute === this.startMinute) {
+						return this.startSecond
+					} else {
+						return 0
+					}
+				}
+			},
+			maxSecond() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
+						return this.endSecond
+					} else {
+						return 59
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.endHour && this.minute === this.endMinute) {
+						return this.endSecond
+					} else {
+						return 59
+					}
+				}
+			},
+
+			/**
+			 * for i18n
+			 */
+			selectTimeText() {
+				return t("uni-datetime-picker.selectTime")
+			},
+			okText() {
+				return t("uni-datetime-picker.ok")
+			},
+			clearText() {
+				return t("uni-datetime-picker.clear")
+			},
+			cancelText() {
+				return t("uni-datetime-picker.cancel")
+			}
+		},
+
+		mounted() {
+			// #ifdef APP-NVUE
+			const res = uni.getSystemInfoSync();
+			this.fixNvueBug = {
+				top: res.windowHeight / 2,
+				left: res.windowWidth / 2
+			}
+			// #endif
+		},
+
+		methods: {
+			/**
+			 * @param {Object} item
+			 * 小于 10 在前面加个 0
+			 */
+
+			lessThanTen(item) {
+				return item < 10 ? '0' + item : item
+			},
+
+			/**
+			 * 解析时分秒字符串,例如:00:00:00
+			 * @param {String} timeString
+			 */
+			parseTimeType(timeString) {
+				if (timeString) {
+					let timeArr = timeString.split(':')
+					this.hour = Number(timeArr[0])
+					this.minute = Number(timeArr[1])
+					this.second = Number(timeArr[2])
+				}
+			},
+
+			/**
+			 * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
+			 * @param {String | Number} datetime
+			 */
+			initPickerValue(datetime) {
+				let defaultValue = null
+				if (datetime) {
+					defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
+				} else {
+					defaultValue = Date.now()
+					defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
+				}
+				this.parseValue(defaultValue)
+			},
+
+			/**
+			 * 初始值规则:
+			 * - 用户设置初始值 value
+			 * 	- 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
+			 * 	- 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
+			 * 	- 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
+			 * 	- 无起始终止时间,则初始值为 value
+			 * - 无初始值 value,则初始值为当前本地时间 Date.now()
+			 * @param {Object} value
+			 * @param {Object} dateBase
+			 */
+			compareValueWithStartAndEnd(value, start, end) {
+				let winner = null
+				value = this.superTimeStamp(value)
+				start = this.superTimeStamp(start)
+				end = this.superTimeStamp(end)
+
+				if (start && end) {
+					if (value < start) {
+						winner = new Date(start)
+					} else if (value > end) {
+						winner = new Date(end)
+					} else {
+						winner = new Date(value)
+					}
+				} else if (start && !end) {
+					winner = start <= value ? new Date(value) : new Date(start)
+				} else if (!start && end) {
+					winner = value <= end ? new Date(value) : new Date(end)
+				} else {
+					winner = new Date(value)
+				}
+
+				return winner
+			},
+
+			/**
+			 * 转换为可比较的时间戳,接受日期、时分秒、时间戳
+			 * @param {Object} value
+			 */
+			superTimeStamp(value) {
+				let dateBase = ''
+				if (this.type === 'time' && value && typeof value === 'string') {
+					const now = new Date()
+					const year = now.getFullYear()
+					const month = now.getMonth() + 1
+					const day = now.getDate()
+					dateBase = year + '/' + month + '/' + day + ' '
+				}
+				if (Number(value)) {
+					value = parseInt(value)
+					dateBase = 0
+				}
+				return this.createTimeStamp(dateBase + value)
+			},
+
+			/**
+			 * 解析默认值 value,字符串、时间戳
+			 * @param {Object} defaultTime
+			 */
+			parseValue(value) {
+				if (!value) {
+					return
+				}
+				if (this.type === 'time' && typeof value === "string") {
+					this.parseTimeType(value)
+				} else {
+					let defaultDate = null
+					defaultDate = new Date(value)
+					if (this.type !== 'time') {
+						this.year = defaultDate.getFullYear()
+						this.month = defaultDate.getMonth() + 1
+						this.day = defaultDate.getDate()
+					}
+					if (this.type !== 'date') {
+						this.hour = defaultDate.getHours()
+						this.minute = defaultDate.getMinutes()
+						this.second = defaultDate.getSeconds()
+					}
+				}
+				if (this.hideSecond) {
+					this.second = 0
+				}
+			},
+
+			/**
+			 * 解析可选择时间范围 start、end,年月日字符串、时间戳
+			 * @param {Object} defaultTime
+			 */
+			parseDatetimeRange(point, pointType) {
+				// 时间为空,则重置为初始值
+				if (!point) {
+					if (pointType === 'start') {
+						this.startYear = 1920
+						this.startMonth = 1
+						this.startDay = 1
+						this.startHour = 0
+						this.startMinute = 0
+						this.startSecond = 0
+					}
+					if (pointType === 'end') {
+						this.endYear = 2120
+						this.endMonth = 12
+						this.endDay = 31
+						this.endHour = 23
+						this.endMinute = 59
+						this.endSecond = 59
+					}
+					return
+				}
+				if (this.type === 'time') {
+					const pointArr = point.split(':')
+					this[pointType + 'Hour'] = Number(pointArr[0])
+					this[pointType + 'Minute'] = Number(pointArr[1])
+					this[pointType + 'Second'] = Number(pointArr[2])
+				} else {
+					if (!point) {
+						pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
+						return
+					}
+					if (Number(point)) {
+						point = parseInt(point)
+					}
+					// datetime 的 end 没有时分秒, 则不限制
+					const hasTime = /[0-9]:[0-9]/
+					if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
+							point)) {
+						point = point + ' 23:59:59'
+					}
+					const pointDate = new Date(point)
+					this[pointType + 'Year'] = pointDate.getFullYear()
+					this[pointType + 'Month'] = pointDate.getMonth() + 1
+					this[pointType + 'Day'] = pointDate.getDate()
+					if (this.type === 'datetime') {
+						this[pointType + 'Hour'] = pointDate.getHours()
+						this[pointType + 'Minute'] = pointDate.getMinutes()
+						this[pointType + 'Second'] = pointDate.getSeconds()
+					}
+				}
+			},
+
+			// 获取 年、月、日、时、分、秒 当前可选范围
+			getCurrentRange(value) {
+				const range = []
+				for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
+					range.push(i)
+				}
+				return range
+			},
+
+			// 字符串首字母大写
+			capitalize(str) {
+				return str.charAt(0).toUpperCase() + str.slice(1)
+			},
+
+			// 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
+			checkValue(name, value, values) {
+				if (values.indexOf(value) === -1) {
+					this[name] = values[0]
+				}
+			},
+
+			// 每个月的实际天数
+			daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
+				return new Date(year, month, 0).getDate();
+			},
+
+			/**
+			 * 生成时间戳
+			 * @param {Object} time
+			 */
+			createTimeStamp(time) {
+				if (!time) return
+				if (typeof time === "number") {
+					return time
+				} else {
+					time = time.replace(/-/g, '/')
+					if (this.type === 'date') {
+						time = time + ' ' + '00:00:00'
+					}
+					return Date.parse(time)
+				}
+			},
+
+			/**
+			 * 生成日期或时间的字符串
+			 */
+			createDomSting() {
+				const yymmdd = this.year +
+					'-' +
+					this.lessThanTen(this.month) +
+					'-' +
+					this.lessThanTen(this.day)
+
+				let hhmmss = this.lessThanTen(this.hour) +
+					':' +
+					this.lessThanTen(this.minute)
+
+				if (!this.hideSecond) {
+					hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
+				}
+
+				if (this.type === 'date') {
+					return yymmdd
+				} else if (this.type === 'time') {
+					return hhmmss
+				} else {
+					return yymmdd + ' ' + hhmmss
+				}
+			},
+
+			/**
+			 * 初始化返回值,并抛出 change 事件
+			 */
+			initTime(emit = true) {
+				this.time = this.createDomSting()
+				if (!emit) return
+				if (this.returnType === 'timestamp' && this.type !== 'time') {
+					this.$emit('change', this.createTimeStamp(this.time))
+					this.$emit('input', this.createTimeStamp(this.time))
+					this.$emit('update:modelValue', this.createTimeStamp(this.time))
+				} else {
+					this.$emit('change', this.time)
+					this.$emit('input', this.time)
+					this.$emit('update:modelValue', this.time)
+				}
+			},
+
+			/**
+			 * 用户选择日期或时间更新 data
+			 * @param {Object} e
+			 */
+			bindDateChange(e) {
+				const val = e.detail.value
+				this.year = this.years[val[0]]
+				this.month = this.months[val[1]]
+				this.day = this.days[val[2]]
+			},
+			bindTimeChange(e) {
+				const val = e.detail.value
+				this.hour = this.hours[val[0]]
+				this.minute = this.minutes[val[1]]
+				this.second = this.seconds[val[2]]
+			},
+
+			/**
+			 * 初始化弹出层
+			 */
+			initTimePicker() {
+				if (this.disabled) return
+				const value = fixIosDateFormat(this.time)
+				this.initPickerValue(value)
+				this.visible = !this.visible
+			},
+
+			/**
+			 * 触发或关闭弹框
+			 */
+			tiggerTimePicker(e) {
+				this.visible = !this.visible
+			},
+
+			/**
+			 * 用户点击“清空”按钮,清空当前值
+			 */
+			clearTime() {
+				this.time = ''
+				this.$emit('change', this.time)
+				this.$emit('input', this.time)
+				this.$emit('update:modelValue', this.time)
+				this.tiggerTimePicker()
+			},
+
+			/**
+			 * 用户点击“确定”按钮
+			 */
+			setTime() {
+				this.initTime()
+				this.tiggerTimePicker()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+
+	.uni-datetime-picker {
+		/* #ifndef APP-NVUE */
+		/* width: 100%; */
+		/* #endif */
+	}
+
+	.uni-datetime-picker-view {
+		height: 130px;
+		width: 270px;
+		/* #ifndef APP-NVUE */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-item {
+		height: 50px;
+		line-height: 50px;
+		text-align: center;
+		font-size: 14px;
+	}
+
+	.uni-datetime-picker-btn {
+		margin-top: 60px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		cursor: pointer;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+	}
+
+	.uni-datetime-picker-btn-text {
+		font-size: 14px;
+		color: $uni-primary;
+	}
+
+	.uni-datetime-picker-btn-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-datetime-picker-cancel {
+		margin-right: 30px;
+	}
+
+	.uni-datetime-picker-mask {
+		position: fixed;
+		bottom: 0px;
+		top: 0px;
+		left: 0px;
+		right: 0px;
+		background-color: rgba(0, 0, 0, 0.4);
+		transition-duration: 0.3s;
+		z-index: 998;
+	}
+
+	.uni-datetime-picker-popup {
+		border-radius: 8px;
+		padding: 30px;
+		width: 270px;
+		/* #ifdef APP-NVUE */
+		height: 500px;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		width: 330px;
+		/* #endif */
+		background-color: #fff;
+		position: fixed;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		transition-duration: 0.3s;
+		z-index: 999;
+	}
+
+	.fix-nvue-height {
+		/* #ifdef APP-NVUE */
+		height: 330px;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-time {
+		color: grey;
+	}
+
+	.uni-datetime-picker-column {
+		height: 50px;
+	}
+
+	.uni-datetime-picker-timebox {
+
+		border: 1px solid #E5E5E5;
+		border-radius: 5px;
+		padding: 7px 10px;
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-timebox-pointer {
+		/* #ifndef APP-NVUE */
+		cursor: pointer;
+		/* #endif */
+	}
+
+
+	.uni-datetime-picker-disabled {
+		opacity: 0.4;
+		/* #ifdef H5 */
+		cursor: not-allowed !important;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-text {
+		font-size: 14px;
+		line-height: 50px
+	}
+
+	.uni-datetime-picker-sign {
+		position: absolute;
+		top: 53px;
+		/* 减掉 10px 的元素高度,兼容nvue */
+		color: #999;
+		/* #ifdef APP-NVUE */
+		font-size: 16px;
+		/* #endif */
+	}
+
+	.sign-left {
+		left: 86px;
+	}
+
+	.sign-right {
+		right: 86px;
+	}
+
+	.sign-center {
+		left: 135px;
+	}
+
+	.uni-datetime-picker__container-box {
+		position: relative;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-top: 40px;
+	}
+
+	.time-hide-second {
+		width: 180px;
+	}
+</style>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1066 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue


+ 421 - 0
uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js

@@ -0,0 +1,421 @@
+class Calendar {
+	constructor({
+		selected,
+		startDate,
+		endDate,
+		range,
+	} = {}) {
+		// 当前日期
+		this.date = this.getDateObj(new Date()) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 起始时间
+		this.startDate = startDate
+		// 终止时间
+		this.endDate = endDate
+		// 是否范围选择
+		this.range = range
+		// 多选状态
+		this.cleanMultipleStatus()
+		// 每周日期
+		this.weeks = {}
+		this.lastHover = false
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		const selectDate = this.getDateObj(date)
+		this.getWeeks(selectDate.fullDate)
+	}
+
+	/**
+	 * 清理多选状态
+	 */
+	cleanMultipleStatus() {
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+	}
+
+	setStartDate(startDate) {
+		this.startDate = startDate
+	}
+
+	setEndDate(endDate) {
+		this.endDate = endDate
+	}
+
+	getPreMonthObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		const oldMonth = date.getMonth()
+		date.setMonth(oldMonth - 1)
+		const newMonth = date.getMonth()
+		if (oldMonth !== 0 && newMonth - oldMonth === 0) {
+			date.setMonth(newMonth - 1)
+		}
+		return this.getDateObj(date)
+	}
+	getNextMonthObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		const oldMonth = date.getMonth()
+		date.setMonth(oldMonth + 1)
+		const newMonth = date.getMonth()
+		if (newMonth - oldMonth > 1) {
+			date.setMonth(newMonth - 1)
+		}
+		return this.getDateObj(date)
+	}
+
+	/**
+	 * 获取指定格式Date对象
+	 */
+	getDateObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		return {
+			fullDate: getDate(date),
+			year: date.getFullYear(),
+			month: addZero(date.getMonth() + 1),
+			date: addZero(date.getDate()),
+			day: date.getDay()
+		}
+	}
+
+	/**
+	 * 获取上一个月日期集合
+	 */
+	getPreMonthDays(amount, dateObj) {
+		const result = []
+		for (let i = amount - 1; i >= 0; i--) {
+			const month = dateObj.month - 1
+			result.push({
+				date: new Date(dateObj.year, month, -i).getDate(),
+				month,
+				disable: true
+			})
+		}
+		return result
+	}
+	/**
+	 * 获取本月日期集合
+	 */
+	getCurrentMonthDays(amount, dateObj) {
+		const result = []
+		const fullDate = this.date.fullDate
+		for (let i = 1; i <= amount; i++) {
+			const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
+			const isToday = fullDate === currentDate
+			// 获取打点信息
+			const info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(currentDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				disableBefore = dateCompare(this.startDate, currentDate)
+			}
+
+			if (this.endDate) {
+				disableAfter = dateCompare(currentDate, this.endDate)
+			}
+
+			let multiples = this.multipleStatus.data
+			let multiplesStatus = -1
+			if (this.range && multiples) {
+				multiplesStatus = multiples.findIndex((item) => {
+					return this.dateEqual(item, currentDate)
+				})
+			}
+			const checked = multiplesStatus !== -1
+
+			result.push({
+				fullDate: currentDate,
+				year: dateObj.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
+				afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
+				month: dateObj.month,
+				disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(
+					currentDate, this.endDate)),
+				isToday,
+				userChecked: false,
+				extraInfo: info
+			})
+		}
+		return result
+	}
+	/**
+	 * 获取下一个月日期集合
+	 */
+	_getNextMonthDays(amount, dateObj) {
+		const result = []
+		const month = dateObj.month + 1
+		for (let i = 1; i <= amount; i++) {
+			result.push({
+				date: i,
+				month,
+				disable: true
+			})
+		}
+		return result
+	}
+
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
+		return res ? res : this.getDateObj(date)
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		before = new Date(fixIosDateFormat(before))
+		after = new Date(fixIosDateFormat(after))
+		return before.valueOf() === after.valueOf()
+	}
+
+	/**
+	 *  比较真实起始日期
+	 */
+
+	isLogicBefore(currentDate, before, after) {
+		let logicBefore = before
+		if (before && after) {
+			logicBefore = dateCompare(before, after) ? before : after
+		}
+		return this.dateEqual(logicBefore, currentDate)
+	}
+
+	isLogicAfter(currentDate, before, after) {
+		let logicAfter = after
+		if (before && after) {
+			logicAfter = dateCompare(before, after) ? after : before
+		}
+		return this.dateEqual(logicAfter, currentDate)
+	}
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		if (!this.range) return
+
+		let {
+			before,
+			after
+		} = this.multipleStatus
+		if (before && after) {
+			if (!this.lastHover) {
+				this.lastHover = true
+				return
+			}
+			this.multipleStatus.before = fullDate
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+			this.multipleStatus.fulldate = ''
+			this.lastHover = false
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+				this.multipleStatus.after = undefined;
+				this.lastHover = false
+			} else {
+				this.multipleStatus.after = fullDate
+				if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
+						.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
+						.before);
+				}
+				this.lastHover = true
+			}
+		}
+		this.getWeeks(fullDate)
+	}
+
+	/**
+	 *  鼠标 hover 更新多选状态
+	 */
+	setHoverMultiple(fullDate) {
+		//抖音小程序点击会触发hover事件,需要避免一下
+		// #ifndef MP-TOUTIAO
+		if (!this.range || this.lastHover) return
+		const {
+			before
+		} = this.multipleStatus
+
+		if (!before) {
+			this.multipleStatus.before = fullDate
+		} else {
+			this.multipleStatus.after = fullDate
+			if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+			} else {
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+			}
+		}
+		this.getWeeks(fullDate)
+		// #endif
+
+	}
+
+	/**
+	 * 更新默认值多选状态
+	 */
+	setDefaultMultiple(before, after) {
+		this.multipleStatus.before = before
+		this.multipleStatus.after = after
+		if (before && after) {
+			if (dateCompare(before, after)) {
+				this.multipleStatus.data = this.geDateAll(before, after);
+				this.getWeeks(after)
+			} else {
+				this.multipleStatus.data = this.geDateAll(after, before);
+				this.getWeeks(before)
+			}
+		}
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	getWeeks(dateData) {
+		const {
+			year,
+			month,
+		} = this.getDateObj(dateData)
+
+		const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
+		const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
+
+		const currentMonthDayAmount = new Date(year, month, 0).getDate()
+		const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
+
+		const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
+		const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
+
+		const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
+
+		const weeks = new Array(6)
+		for (let i = 0; i < calendarDays.length; i++) {
+			const index = Math.floor(i / 7)
+			if (!weeks[index]) {
+				weeks[index] = new Array(7)
+			}
+			weeks[index][i % 7] = calendarDays[i]
+		}
+
+		this.calendar = calendarDays
+		this.weeks = weeks
+	}
+}
+
+function getDateTime(date, hideSecond) {
+	return `${getDate(date)} ${getTime(date, hideSecond)}`
+}
+
+function getDate(date) {
+	date = fixIosDateFormat(date)
+	date = new Date(date)
+	const year = date.getFullYear()
+	const month = date.getMonth() + 1
+	const day = date.getDate()
+	return `${year}-${addZero(month)}-${addZero(day)}`
+}
+
+function getTime(date, hideSecond) {
+	date = fixIosDateFormat(date)
+	date = new Date(date)
+	const hour = date.getHours()
+	const minute = date.getMinutes()
+	const second = date.getSeconds()
+	return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
+}
+
+function addZero(num) {
+	if (num < 10) {
+		num = `0${num}`
+	}
+	return num
+}
+
+function getDefaultSecond(hideSecond) {
+	return hideSecond ? '00:00' : '00:00:00'
+}
+
+function dateCompare(startDate, endDate) {
+	startDate = new Date(fixIosDateFormat(startDate))
+	endDate = new Date(fixIosDateFormat(endDate))
+	return startDate <= endDate
+}
+
+function checkDate(date) {
+	const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
+	return date.match(dateReg)
+}
+//ios低版本15及以下,无法匹配 没有 ’秒‘ 时的情况,所以需要在末尾 秒 加上 问号
+const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/;
+
+function fixIosDateFormat(value) {
+	if (typeof value === 'string' && dateTimeReg.test(value)) {
+		value = value.replace(/-/g, '/')
+	}
+	return value
+}
+
+export {
+	Calendar,
+	getDateTime,
+	getDate,
+	getTime,
+	addZero,
+	getDefaultSecond,
+	dateCompare,
+	checkDate,
+	fixIosDateFormat
+}

+ 88 - 0
uni_modules/uni-datetime-picker/package.json

@@ -0,0 +1,88 @@
+{
+  "id": "uni-datetime-picker",
+  "displayName": "uni-datetime-picker 日期选择器",
+  "version": "2.2.38",
+  "description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
+  "keywords": [
+    "uni-datetime-picker",
+    "uni-ui",
+    "uniui",
+    "日期时间选择器",
+    "日期时间"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uni-scss",
+			"uni-icons"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 21 - 0
uni_modules/uni-datetime-picker/readme.md

@@ -0,0 +1,21 @@
+
+
+> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
+
+## DatetimePicker 时间选择器
+
+> **组件名:uni-datetime-picker**
+> 代码块: `uDatetimePicker`
+
+
+该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
+
+若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
+
+**_点击 picker 默认值规则:_**
+
+- 若设置初始值 value, 会显示在 picker 显示框中
+- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 81 - 0
uni_modules/uni-file-picker/changelog.md

@@ -0,0 +1,81 @@
+## 1.0.11(2024-07-19)
+- 修复 vue3 使用value报错的bug
+## 1.0.10(2024-07-09)
+- 优化 vue3兼容性
+## 1.0.9(2024-07-09)
+- 修复 value 属性不兼容vue3的bug
+## 1.0.8(2024-03-20)
+- 补充 删除文件时返回文件下标
+## 1.0.7(2024-02-21)
+- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图
+## 1.0.6(2024-01-06)
+- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia
+## 1.0.5(2024-01-03)
+- 新增 上传文件至云存储携带本地文件名称
+## 1.0.4(2023-03-29)
+- 修复 手动上传删除一个文件后不能再上传的bug
+## 1.0.3(2022-12-19)
+- 新增 sourceType 属性, 可以自定义图片和视频选择的来源
+## 1.0.2(2022-07-04)
+- 修复 在uni-forms下样式不生效的bug
+## 1.0.1(2021-11-23)
+- 修复 参数为对象的情况下,url在某些情况显示错误的bug
+## 1.0.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
+## 0.2.16(2021-11-08)
+- 修复 传入空对象 ,显示错误的Bug
+## 0.2.15(2021-08-30)
+- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug
+## 0.2.14(2021-08-23)
+- 新增 参数中返回 fileID 字段
+## 0.2.13(2021-08-23)
+- 修复 腾讯云传入fileID 不能回显的bug
+- 修复 选择图片后,不能放大的问题
+## 0.2.12(2021-08-17)
+- 修复 由于 0.2.11 版本引起的不能回显图片的Bug
+## 0.2.11(2021-08-16)
+- 新增 clearFiles(index) 方法,可以手动删除指定文件
+- 修复 v-model 值设为 null 报错的Bug
+## 0.2.10(2021-08-13)
+- 修复 return-type="object" 时,无法删除文件的Bug
+## 0.2.9(2021-08-03)
+- 修复 auto-upload 属性失效的Bug
+## 0.2.8(2021-07-31)
+- 修复 fileExtname属性不指定值报错的Bug
+## 0.2.7(2021-07-31)
+- 修复 在某种场景下图片不回显的Bug
+## 0.2.6(2021-07-30)
+- 修复 return-type为object下,返回值不正确的Bug
+## 0.2.5(2021-07-30)
+- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题
+## 0.2.3(2021-07-28)
+- 优化 调整示例代码
+## 0.2.2(2021-07-27)
+- 修复 vue3 下赋值错误的Bug
+- 优化 h5平台下上传文件导致页面卡死的问题
+## 0.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 0.1.1(2021-07-02)
+- 修复 sourceType 缺少默认值导致 ios 无法选择文件
+## 0.1.0(2021-06-30)
+- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改
+## 0.0.11(2021-06-30)
+- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题
+## 0.0.10(2021-06-29)
+- 优化 文件上传后进度条消失时机
+## 0.0.9(2021-06-29)
+- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug
+## 0.0.8(2021-06-15)
+- 修复 删除文件时无法触发 v-model 的Bug
+## 0.0.7(2021-05-12)
+- 新增 组件示例地址
+## 0.0.6(2021-04-09)
+- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug
+## 0.0.5(2021-04-09)
+- 优化 更新组件示例
+## 0.0.4(2021-04-09)
+- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔
+## 0.0.3(2021-02-05)
+- 调整为uni_modules目录规范
+- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug

+ 287 - 0
uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js

@@ -0,0 +1,287 @@
+'use strict';
+
+const ERR_MSG_OK = 'chooseAndUploadFile:ok';
+const ERR_MSG_FAIL = 'chooseAndUploadFile:fail';
+
+function chooseImage(opts) {
+	const {
+		count,
+		sizeType = ['original', 'compressed'],
+		sourceType,
+		extension
+	} = opts
+	return new Promise((resolve, reject) => {
+		// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
+		// #ifdef MP-WEIXIN
+		uni.chooseMedia({
+			count,
+			sizeType,
+			sourceType,
+			mediaType: ['image'],
+			extension,
+			success(res) {
+				res.tempFiles.forEach(item => {
+					item.path = item.tempFilePath;
+				})
+				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+				});
+			},
+		})
+		// #endif
+		// #ifndef MP-WEIXIN
+		uni.chooseImage({
+			count,
+			sizeType,
+			sourceType,
+			extension,
+			success(res) {
+				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+		// #endif
+
+	});
+}
+
+function chooseVideo(opts) {
+	const {
+		count,
+		camera,
+		compressed,
+		maxDuration,
+		sourceType,
+		extension
+	} = opts;
+	return new Promise((resolve, reject) => {
+		// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
+		// #ifdef MP-WEIXIN
+		uni.chooseMedia({
+			count,
+			compressed,
+			maxDuration,
+			sourceType,
+			extension,
+			mediaType: ['video'],
+			success(res) {
+				const {
+					tempFiles,
+				} = res;
+				resolve(normalizeChooseAndUploadFileRes({
+					errMsg: 'chooseVideo:ok',
+					tempFiles: tempFiles.map(item => {
+						return {
+							name: item.name || '',
+							path: item.tempFilePath,
+							thumbTempFilePath: item.thumbTempFilePath,
+							size:item.size,
+							type: (res.tempFile && res.tempFile.type) || '',
+							width:item.width,
+							height:item.height,
+							duration:item.duration,
+							fileType: 'video',
+							cloudPath: '',
+						}
+					}),
+				}, 'video'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
+				});
+			},
+		})
+		// #endif
+		// #ifndef MP-WEIXIN
+		uni.chooseVideo({
+			camera,
+			compressed,
+			maxDuration,
+			sourceType,
+			extension,
+			success(res) {
+				const {
+					tempFilePath,
+					duration,
+					size,
+					height,
+					width
+				} = res;
+				resolve(normalizeChooseAndUploadFileRes({
+					errMsg: 'chooseVideo:ok',
+					tempFilePaths: [tempFilePath],
+					tempFiles: [{
+						name: (res.tempFile && res.tempFile.name) || '',
+						path: tempFilePath,
+						size,
+						type: (res.tempFile && res.tempFile.type) || '',
+						width,
+						height,
+						duration,
+						fileType: 'video',
+						cloudPath: '',
+					}, ],
+				}, 'video'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+		// #endif
+	});
+}
+
+function chooseAll(opts) {
+	const {
+		count,
+		extension
+	} = opts;
+	return new Promise((resolve, reject) => {
+		let chooseFile = uni.chooseFile;
+		if (typeof wx !== 'undefined' &&
+			typeof wx.chooseMessageFile === 'function') {
+			chooseFile = wx.chooseMessageFile;
+		}
+		if (typeof chooseFile !== 'function') {
+			return reject({
+				errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。',
+			});
+		}
+		chooseFile({
+			type: 'all',
+			count,
+			extension,
+			success(res) {
+				resolve(normalizeChooseAndUploadFileRes(res));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+	});
+}
+
+function normalizeChooseAndUploadFileRes(res, fileType) {
+	res.tempFiles.forEach((item, index) => {
+		if (!item.name) {
+			item.name = item.path.substring(item.path.lastIndexOf('/') + 1);
+		}
+		if (fileType) {
+			item.fileType = fileType;
+		}
+		item.cloudPath =
+			Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.'));
+	});
+	if (!res.tempFilePaths) {
+		res.tempFilePaths = res.tempFiles.map((file) => file.path);
+	}
+	return res;
+}
+
+function uploadCloudFiles(files, max = 5, onUploadProgress) {
+	files = JSON.parse(JSON.stringify(files))
+	const len = files.length
+	let count = 0
+	let self = this
+	return new Promise(resolve => {
+		while (count < max) {
+			next()
+		}
+
+		function next() {
+			let cur = count++
+			if (cur >= len) {
+				!files.find(item => !item.url && !item.errMsg) && resolve(files)
+				return
+			}
+			const fileItem = files[cur]
+			const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
+			fileItem.url = ''
+			delete fileItem.errMsg
+
+			uniCloud
+				.uploadFile({
+					filePath: fileItem.path,
+					cloudPath: fileItem.cloudPath,
+					fileType: fileItem.fileType,
+					onUploadProgress: res => {
+						res.index = index
+						onUploadProgress && onUploadProgress(res)
+					}
+				})
+				.then(res => {
+					fileItem.url = res.fileID
+					fileItem.index = index
+					if (cur < len) {
+						next()
+					}
+				})
+				.catch(res => {
+					fileItem.errMsg = res.errMsg || res.message
+					fileItem.index = index
+					if (cur < len) {
+						next()
+					}
+				})
+		}
+	})
+}
+
+
+
+
+
+function uploadFiles(choosePromise, {
+	onChooseFile,
+	onUploadProgress
+}) {
+	return choosePromise
+		.then((res) => {
+			if (onChooseFile) {
+				const customChooseRes = onChooseFile(res);
+				if (typeof customChooseRes !== 'undefined') {
+					return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ?
+						res : chooseRes);
+				}
+			}
+			return res;
+		})
+		.then((res) => {
+			if (res === false) {
+				return {
+					errMsg: ERR_MSG_OK,
+					tempFilePaths: [],
+					tempFiles: [],
+				};
+			}
+			return res
+		})
+}
+
+function chooseAndUploadFile(opts = {
+	type: 'all'
+}) {
+	if (opts.type === 'image') {
+		return uploadFiles(chooseImage(opts), opts);
+	} else if (opts.type === 'video') {
+		return uploadFiles(chooseVideo(opts), opts);
+	}
+	return uploadFiles(chooseAll(opts), opts);
+}
+
+export {
+	chooseAndUploadFile,
+	uploadCloudFiles
+};

+ 672 - 0
uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue

@@ -0,0 +1,672 @@
+<template>
+	<view class="uni-file-picker">
+		<view v-if="title" class="uni-file-picker__header">
+			<text class="file-title">{{ title }}</text>
+			<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
+		</view>
+		<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly"
+			:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview"
+			:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
+			<slot>
+				<view class="is-add">
+					<view class="icon-add"></view>
+					<view class="icon-add rotate"></view>
+				</view>
+			</slot>
+		</upload-image>
+		<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly"
+			:list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon"
+			@uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
+			<slot>
+				<image src="../../../../static/project/upload.png" style="width: 43rpx;height: 43rpx;"></image>
+				<view type="primary" size="mini">上传附件</view>
+				<!-- <button type="primary" size="mini">选择文件</button> -->
+			</slot>
+		</upload-file>
+	</view>
+</template>
+
+<script>
+	import {
+		chooseAndUploadFile,
+		uploadCloudFiles
+	} from './choose-and-upload-file.js'
+	import {
+		get_file_ext,
+		get_extname,
+		get_files_and_is_max,
+		get_file_info,
+		get_file_data
+	} from './utils.js'
+	import uploadImage from './upload-image.vue'
+	import uploadFile from './upload-file.vue'
+	let fileInput = null
+	/**
+	 * FilePicker 文件选择上传
+	 * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
+	 * @property {Object|Array}	value	组件数据,通常用来回显 ,类型由return-type属性决定
+	 * @property {Boolean}	disabled = [true|false]	组件禁用
+	 * 	@value true 	禁用
+	 * 	@value false 	取消禁用
+	 * @property {Boolean}	readonly = [true|false]	组件只读,不可选择,不显示进度,不显示删除按钮
+	 * 	@value true 	只读
+	 * 	@value false 	取消只读
+	 * @property {String}	return-type = [array|object]	限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
+	 * 	@value array	规定 value 属性的类型为数组
+	 * 	@value object	规定 value 属性的类型为对象
+	 * @property {Boolean}	disable-preview = [true|false]	禁用图片预览,仅 mode:grid 时生效
+	 * 	@value true 	禁用图片预览
+	 * 	@value false 	取消禁用图片预览
+	 * @property {Boolean}	del-icon = [true|false]	是否显示删除按钮
+	 * 	@value true 	显示删除按钮
+	 * 	@value false 	不显示删除按钮
+	 * @property {Boolean}	auto-upload = [true|false]	是否自动上传,值为true则只触发@select,可自行上传
+	 * 	@value true 	自动上传
+	 * 	@value false 	取消自动上传
+	 * @property {Number|String}	limit	最大选择个数 ,h5 会自动忽略多选的部分
+	 * @property {String}	title	组件标题,右侧显示上传计数
+	 * @property {String}	mode = [list|grid]	选择文件后的文件列表样式
+	 * 	@value list 	列表显示
+	 * 	@value grid 	宫格显示
+	 * @property {String}	file-mediatype = [image|video|all]	选择文件类型
+	 * 	@value image	只选择图片
+	 * 	@value video	只选择视频
+	 * 	@value all		选择所有文件
+	 * @property {Array}	file-extname	选择文件后缀,根据 file-mediatype 属性而不同
+	 * @property {Object}	list-style	mode:list 时的样式
+	 * @property {Object}	image-styles	选择文件后缀,根据 file-mediatype 属性而不同
+	 * @event {Function} select 	选择文件后触发
+	 * @event {Function} progress 文件上传时触发
+	 * @event {Function} success 	上传成功触发
+	 * @event {Function} fail 		上传失败触发
+	 * @event {Function} delete 	文件从列表移除时触发
+	 */
+	export default {
+		name: 'uniFilePicker',
+		components: {
+			uploadImage,
+			uploadFile
+		},
+		options: {
+			virtualHost: true
+		},
+		emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
+		props: {
+			modelValue: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			value: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			disablePreview: {
+				type: Boolean,
+				default: false
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			// 自动上传
+			autoUpload: {
+				type: Boolean,
+				default: true
+			},
+			// 最大选择个数 ,h5只能限制单选或是多选
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			// 列表样式 grid | list | list-card
+			mode: {
+				type: String,
+				default: 'grid'
+			},
+			// 选择文件类型  image/video/all
+			fileMediatype: {
+				type: String,
+				default: 'image'
+			},
+			// 文件类型筛选
+			fileExtname: {
+				type: [Array, String],
+				default () {
+					return []
+				}
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			listStyles: {
+				type: Object,
+				default () {
+					return {
+						// 是否显示边框
+						border: true,
+						// 是否显示分隔线
+						dividline: true,
+						// 线条样式
+						borderStyle: {}
+					}
+				}
+			},
+			imageStyles: {
+				type: Object,
+				default () {
+					return {
+						width: 'auto',
+						height: 'auto'
+					}
+				}
+			},
+			readonly: {
+				type: Boolean,
+				default: false
+			},
+			returnType: {
+				type: String,
+				default: 'array'
+			},
+			sizeType: {
+				type: Array,
+				default () {
+					return ['original', 'compressed']
+				}
+			},
+			sourceType: {
+				type: Array,
+				default () {
+					return  ['album', 'camera']
+				}
+			},
+			provider: {
+				type: String,
+				default: '' // 默认上传到 unicloud 内置存储 extStorage 扩展存储
+			}
+		},
+		data() {
+			return {
+				files: [],
+				localValue: []
+			}
+		},
+		watch: {
+			value: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+			modelValue: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+		},
+		computed: {
+			filesList() {
+				let files = []
+				this.files.forEach(v => {
+					files.push(v)
+				})
+				return files
+			},
+			showType() {
+				if (this.fileMediatype === 'image') {
+					return this.mode
+				}
+				return 'list'
+			},
+			limitLength() {
+				if (this.returnType === 'object') {
+					return 1
+				}
+				if (!this.limit) {
+					return 1
+				}
+				if (this.limit >= 9) {
+					return 9
+				}
+				return this.limit
+			}
+		},
+		created() {
+			// TODO 兼容不开通服务空间的情况
+			if (!(uniCloud.config && uniCloud.config.provider)) {
+				this.noSpace = true
+				uniCloud.chooseAndUploadFile = chooseAndUploadFile
+			}
+			this.form = this.getForm('uniForms')
+			this.formItem = this.getForm('uniFormsItem')
+			if (this.form && this.formItem) {
+				if (this.formItem.name) {
+					this.rename = this.formItem.name
+					this.form.inputChildrens.push(this)
+				}
+			}
+		},
+		methods: {
+			/**
+			 * 公开用户使用,清空文件
+			 * @param {Object} index
+			 */
+			clearFiles(index) {
+				if (index !== 0 && !index) {
+					this.files = []
+					this.$nextTick(() => {
+						this.setEmit()
+					})
+				} else {
+					this.files.splice(index, 1)
+				}
+				this.$nextTick(() => {
+					this.setEmit()
+				})
+			},
+			/**
+			 * 公开用户使用,继续上传
+			 */
+			upload() {
+				let files = []
+				this.files.forEach((v, index) => {
+					if (v.status === 'ready' || v.status === 'error') {
+						files.push(Object.assign({}, v))
+					}
+				})
+				return this.uploadFiles(files)
+			},
+			async setValue(newVal, oldVal) {
+				const newData =  async (v) => {
+					const reg = /cloud:\/\/([\w.]+\/?)\S*/
+					let url = ''
+					if(v.fileID){
+						url = v.fileID
+					}else{
+						url = v.url
+					}
+					if (reg.test(url)) {
+						v.fileID = url
+						v.url = await this.getTempFileURL(url)
+					}
+					if(v.url) v.path = v.url
+					return v
+				}
+				if (this.returnType === 'object') {
+					if (newVal) {
+						await newData(newVal)
+					} else {
+						newVal = {}
+					}
+				} else {
+					if (!newVal) newVal = []
+					for(let i =0 ;i < newVal.length ;i++){
+						let v = newVal[i]
+						await newData(v)
+					}
+				}
+				this.localValue = newVal
+				if (this.form && this.formItem &&!this.is_reset) {
+					this.is_reset = false
+					this.formItem.setValue(this.localValue)
+				}
+				let filesData = Object.keys(newVal).length > 0 ? newVal : [];
+				this.files = [].concat(filesData)
+			},
+
+			/**
+			 * 选择文件
+			 */
+			choose() {
+				if (this.disabled) return
+				if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
+					'array') {
+					uni.showToast({
+						title: `您最多选择 ${this.limitLength} 个文件`,
+						icon: 'none'
+					})
+					return
+				}
+				this.chooseFiles()
+			},
+
+			/**
+			 * 选择文件并上传
+			 */
+			chooseFiles() {
+				const _extname = get_extname(this.fileExtname)
+				// 获取后缀
+				uniCloud
+					.chooseAndUploadFile({
+						type: this.fileMediatype,
+						compressed: false,
+						sizeType: this.sizeType,
+						sourceType: this.sourceType,
+						// TODO 如果为空,video 有问题
+						extension: _extname.length > 0 ? _extname : undefined,
+						count: this.limitLength - this.files.length, //默认9
+						onChooseFile: this.chooseFileCallback,
+						onUploadProgress: progressEvent => {
+							this.setProgress(progressEvent, progressEvent.index)
+						}
+					})
+					.then(result => {
+						this.setSuccessAndError(result.tempFiles)
+					})
+					.catch(err => {
+						console.log('选择失败', err)
+					})
+			},
+
+			/**
+			 * 选择文件回调
+			 * @param {Object} res
+			 */
+			async chooseFileCallback(res) {
+				const _extname = get_extname(this.fileExtname)
+				const is_one = (Number(this.limitLength) === 1 &&
+						this.disablePreview &&
+						!this.disabled) ||
+					this.returnType === 'object'
+				// 如果这有一个文件 ,需要清空本地缓存数据
+				if (is_one) {
+					this.files = []
+				}
+
+				let {
+					filePaths,
+					files
+				} = get_files_and_is_max(res, _extname)
+				if (!(_extname && _extname.length > 0)) {
+					filePaths = res.tempFilePaths
+					files = res.tempFiles
+				}
+
+				let currentData = []
+				for (let i = 0; i < files.length; i++) {
+					if (this.limitLength - this.files.length <= 0) break
+					files[i].uuid = Date.now()
+					let filedata = await get_file_data(files[i], this.fileMediatype)
+					filedata.progress = 0
+					filedata.status = 'ready'
+					this.files.push(filedata)
+					currentData.push({
+						...filedata,
+						file: files[i]
+					})
+				}
+				this.$emit('select', {
+					tempFiles: currentData,
+					tempFilePaths: filePaths
+				})
+				res.tempFiles = files
+				// 停止自动上传
+				if (!this.autoUpload || this.noSpace) {
+					res.tempFiles = []
+				}
+				res.tempFiles.forEach((fileItem, index) => {
+					this.provider && (fileItem.provider = this.provider);
+					const fileNameSplit = fileItem.name.split('.')
+					const ext = fileNameSplit.pop()
+					const fileName = fileNameSplit.join('.').replace(/[\s\/\?<>\\:\*\|":]/g, '_')
+					fileItem.cloudPath = fileName + '_' + Date.now() + '_' + index + '.' + ext
+				})
+			},
+
+			/**
+			 * 批传
+			 * @param {Object} e
+			 */
+			uploadFiles(files) {
+				files = [].concat(files)
+				return uploadCloudFiles.call(this, files, 5, res => {
+						this.setProgress(res, res.index, true)
+					})
+					.then(result => {
+						this.setSuccessAndError(result)
+						return result;
+					})
+					.catch(err => {
+						console.log(err)
+					})
+			},
+
+			/**
+			 * 成功或失败
+			 */
+			async setSuccessAndError(res, fn) {
+				let successData = []
+				let errorData = []
+				let tempFilePath = []
+				let errorTempFilePath = []
+				for (let i = 0; i < res.length; i++) {
+					const item = res[i]
+					const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
+
+					if (index === -1 || !this.files) break
+					if (item.errMsg === 'request:fail') {
+						this.files[index].url = item.path
+						this.files[index].status = 'error'
+						this.files[index].errMsg = item.errMsg
+						// this.files[index].progress = -1
+						errorData.push(this.files[index])
+						errorTempFilePath.push(this.files[index].url)
+					} else {
+						this.files[index].errMsg = ''
+						this.files[index].fileID = item.url
+						const reg = /cloud:\/\/([\w.]+\/?)\S*/
+						if (reg.test(item.url)) {
+							this.files[index].url = await this.getTempFileURL(item.url)
+						}else{
+							this.files[index].url = item.url
+						}
+
+						this.files[index].status = 'success'
+						this.files[index].progress += 1
+						successData.push(this.files[index])
+						tempFilePath.push(this.files[index].fileID)
+					}
+				}
+
+				if (successData.length > 0) {
+					this.setEmit()
+					// 状态改变返回
+					this.$emit('success', {
+						tempFiles: this.backObject(successData),
+						tempFilePaths: tempFilePath
+					})
+				}
+
+				if (errorData.length > 0) {
+					this.$emit('fail', {
+						tempFiles: this.backObject(errorData),
+						tempFilePaths: errorTempFilePath
+					})
+				}
+			},
+
+			/**
+			 * 获取进度
+			 * @param {Object} progressEvent
+			 * @param {Object} index
+			 * @param {Object} type
+			 */
+			setProgress(progressEvent, index, type) {
+				const fileLenth = this.files.length
+				const percentNum = (index / fileLenth) * 100
+				const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
+				let idx = index
+				if (!type) {
+					idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
+				}
+				if (idx === -1 || !this.files[idx]) return
+				// fix by mehaotian 100 就会消失,-1 是为了让进度条消失
+				this.files[idx].progress = percentCompleted - 1
+				// 上传中
+				this.$emit('progress', {
+					index: idx,
+					progress: parseInt(percentCompleted),
+					tempFile: this.files[idx]
+				})
+			},
+
+			/**
+			 * 删除文件
+			 * @param {Object} index
+			 */
+			delFile(index) {
+				this.$emit('delete', {
+					index,
+					tempFile: this.files[index],
+					tempFilePath: this.files[index].url
+				})
+				this.files.splice(index, 1)
+				this.$nextTick(() => {
+					this.setEmit()
+				})
+			},
+
+			/**
+			 * 获取文件名和后缀
+			 * @param {Object} name
+			 */
+			getFileExt(name) {
+				const last_len = name.lastIndexOf('.')
+				const len = name.length
+				return {
+					name: name.substring(0, last_len),
+					ext: name.substring(last_len + 1, len)
+				}
+			},
+
+			/**
+			 * 处理返回事件
+			 */
+			setEmit() {
+				let data = []
+				if (this.returnType === 'object') {
+					data = this.backObject(this.files)[0]
+					this.localValue = data?data:null
+				} else {
+					data = this.backObject(this.files)
+					if (!this.localValue) {
+						this.localValue = []
+					}
+					this.localValue = [...data]
+				}
+				// #ifdef VUE3
+				this.$emit('update:modelValue', this.localValue)
+				// #endif
+				// #ifndef VUE3
+				this.$emit('input', this.localValue)
+				// #endif
+			},
+
+			/**
+			 * 处理返回参数
+			 * @param {Object} files
+			 */
+			backObject(files) {
+				let newFilesData = []
+				files.forEach(v => {
+					newFilesData.push({
+						extname: v.extname,
+						fileType: v.fileType,
+						image: v.image,
+						name: v.name,
+						path: v.path,
+						size: v.size,
+						fileID:v.fileID,
+						url: v.url,
+						// 修改删除一个文件后不能再上传的bug, #694
+            uuid: v.uuid,
+            status: v.status,
+            cloudPath: v.cloudPath
+					})
+				})
+				return newFilesData
+			},
+			async getTempFileURL(fileList) {
+				fileList = {
+					fileList: [].concat(fileList)
+				}
+				const urls = await uniCloud.getTempFileURL(fileList)
+				return urls.fileList[0].tempFileURL || ''
+			},
+			/**
+			 * 获取父元素实例
+			 */
+			getForm(name = 'uniForms') {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== name) {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			}
+		}
+	}
+</script>
+
+<style>
+	.uni-file-picker {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		overflow: hidden;
+		width: 100%;
+		/* #endif */
+		flex: 1;
+	}
+
+	.uni-file-picker__header {
+		padding-top: 5px;
+		padding-bottom: 10px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: space-between;
+	}
+
+	.file-title {
+		font-size: 14px;
+		color: #333;
+	}
+
+	.file-count {
+		font-size: 14px;
+		color: #999;
+	}
+
+	.is-add {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+	}
+
+	.icon-add {
+		width: 50px;
+		height: 5px;
+		background-color: #f1f1f1;
+		border-radius: 2px;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+</style>

+ 332 - 0
uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue

@@ -0,0 +1,332 @@
+<template>
+	<view class="uni-file-picker__files">
+		<view v-if="!readonly" class="files-button" @click="choose">
+			<slot></slot>
+		</view>
+		<!-- :class="{'is-text-box':showType === 'list'}" -->
+		<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle">
+			<!-- ,'is-list-card':showType === 'list-card' -->
+
+			<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{
+				'files-border':index !== 0 && styles.dividline}"
+			 :style="index !== 0 && styles.dividline &&borderLineStyle">
+				<view class="uni-file-picker__item">
+					<!-- :class="{'is-text-image':showType === 'list'}" -->
+					<!-- 	<view class="files__image is-text-image">
+						<image class="header-image" :src="item.logo" mode="aspectFit"></image>
+					</view> -->
+					<view class="files__name">{{item.name}}</view>
+					<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)">
+						<view class="icon-del icon-files"></view>
+						<view class="icon-del rotate"></view>
+					</view>
+				</view>
+				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
+					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
+					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
+				</view>
+				<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
+					点击重试
+				</view>
+			</view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uploadFile",
+		emits:['uploadFiles','choose','delFile'],
+		props: {
+			filesList: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			showType: {
+				type: String,
+				default: ''
+			},
+			listStyles: {
+				type: Object,
+				default () {
+					return {
+						// 是否显示边框
+						border: true,
+						// 是否显示分隔线
+						dividline: true,
+						// 线条样式
+						borderStyle: {}
+					}
+				}
+			},
+			readonly:{
+				type:Boolean,
+				default:false
+			}
+		},
+		computed: {
+			list() {
+				let files = []
+				this.filesList.forEach(v => {
+					files.push(v)
+				})
+				return files
+			},
+			styles() {
+				let styles = {
+					border: true,
+					dividline: true,
+					'border-style': {}
+				}
+				return Object.assign(styles, this.listStyles)
+			},
+			borderStyle() {
+				let {
+					borderStyle,
+					border
+				} = this.styles
+				let obj = {}
+				if (!border) {
+					obj.border = 'none'
+				} else {
+					let width = (borderStyle && borderStyle.width) || 1
+					width = this.value2px(width)
+					let radius = (borderStyle && borderStyle.radius) || 5
+					radius = this.value2px(radius)
+					obj = {
+						'border-width': width,
+						'border-style': (borderStyle && borderStyle.style) || 'solid',
+						'border-color': (borderStyle && borderStyle.color) || '#eee',
+						'border-radius': radius
+					}
+				}
+				let classles = ''
+				for (let i in obj) {
+					classles += `${i}:${obj[i]};`
+				}
+				return classles
+			},
+			borderLineStyle() {
+				let obj = {}
+				let {
+					borderStyle
+				} = this.styles
+				if (borderStyle && borderStyle.color) {
+					obj['border-color'] = borderStyle.color
+				}
+				if (borderStyle && borderStyle.width) {
+					let width = borderStyle && borderStyle.width || 1
+					let style = borderStyle && borderStyle.style || 0
+					if (typeof width === 'number') {
+						width += 'px'
+					} else {
+						width = width.indexOf('px') ? width : width + 'px'
+					}
+					obj['border-width'] = width
+
+					if (typeof style === 'number') {
+						style += 'px'
+					} else {
+						style = style.indexOf('px') ? style : style + 'px'
+					}
+					obj['border-top-style'] = style
+				}
+				let classles = ''
+				for (let i in obj) {
+					classles += `${i}:${obj[i]};`
+				}
+				return classles
+			}
+		},
+
+		methods: {
+			uploadFiles(item, index) {
+				this.$emit("uploadFiles", {
+					item,
+					index
+				})
+			},
+			choose() {
+				this.$emit("choose")
+			},
+			delFile(index) {
+				this.$emit('delFile', index)
+			},
+			value2px(value) {
+				if (typeof value === 'number') {
+					value += 'px'
+				} else {
+					value = value.indexOf('px') !== -1 ? value : value + 'px'
+				}
+				return value
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-file-picker__files {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: flex-start;
+	}
+
+	.files-button {
+		// border: 1px red solid;
+		display: flex;
+		font-size: 28rpx;
+		line-height: 41rpx;
+		color: rgba(0, 97, 255, 1);
+		line-height: 40px;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.uni-file-picker__lists {
+		position: relative;
+		margin-top: 5px;
+		overflow: hidden;
+	}
+
+	.file-picker__mask {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		color: #fff;
+		font-size: 14px;
+		background-color: rgba(0, 0, 0, 0.4);
+	}
+
+	.uni-file-picker__lists-box {
+		position: relative;
+	}
+
+	.uni-file-picker__item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		padding: 8px 10px;
+		padding-right: 5px;
+		padding-left: 10px;
+	}
+
+	.files-border {
+		border-top: 1px #eee solid;
+	}
+
+	.files__name {
+		flex: 1;
+		font-size: 14px;
+		color: #666;
+		margin-right: 25px;
+		/* #ifndef APP-NVUE */
+		word-break: break-all;
+		word-wrap: break-word;
+		/* #endif */
+	}
+
+	.icon-files {
+		/* #ifndef APP-NVUE */
+		position: static;
+		background-color: initial;
+		/* #endif */
+	}
+
+	// .icon-files .icon-del {
+	// 	background-color: #333;
+	// 	width: 12px;
+	// 	height: 1px;
+	// }
+
+
+	.is-list-card {
+		border: 1px #eee solid;
+		margin-bottom: 5px;
+		border-radius: 5px;
+		box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1);
+		padding: 5px;
+	}
+
+	.files__image {
+		width: 40px;
+		height: 40px;
+		margin-right: 10px;
+	}
+
+	.header-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.is-text-box {
+		border: 1px #eee solid;
+		border-radius: 5px;
+	}
+
+	.is-text-image {
+		width: 25px;
+		height: 25px;
+		margin-left: 5px;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+
+	.icon-del-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		margin: auto 0;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		top: 0px;
+		bottom: 0;
+		right: 5px;
+		height: 26px;
+		width: 26px;
+		// border-radius: 50%;
+		// background-color: rgba(0, 0, 0, 0.5);
+		z-index: 2;
+		transform: rotate(-45deg);
+	}
+
+	.icon-del {
+		width: 15px;
+		height: 1px;
+		background-color: #333;
+		// border-radius: 1px;
+	}
+
+	/* #ifdef H5 */
+	@media all and (min-width: 768px) {
+		.uni-file-picker__files {
+			max-width: 375px;
+		}
+	}
+
+	/* #endif */
+</style>

+ 292 - 0
uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue

@@ -0,0 +1,292 @@
+<template>
+	<view class="uni-file-picker__container">
+		<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
+			<view class="file-picker__box-content" :style="borderStyle">
+				<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
+				<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
+					<view class="icon-del"></view>
+					<view class="icon-del rotate"></view>
+				</view>
+				<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
+					<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
+					 :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
+				</view>
+				<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
+					点击重试
+				</view>
+			</view>
+		</view>
+		<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
+			<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
+				<slot>
+					<view class="icon-add"></view>
+					<view class="icon-add rotate"></view>
+				</slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uploadImage",
+		emits:['uploadFiles','choose','delFile'],
+		props: {
+			filesList: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			disabled:{
+				type: Boolean,
+				default: false
+			},
+			disablePreview: {
+				type: Boolean,
+				default: false
+			},
+			limit: {
+				type: [Number, String],
+				default: 9
+			},
+			imageStyles: {
+				type: Object,
+				default () {
+					return {
+						width: 'auto',
+						height: 'auto',
+						border: {}
+					}
+				}
+			},
+			delIcon: {
+				type: Boolean,
+				default: true
+			},
+			readonly:{
+				type:Boolean,
+				default:false
+			}
+		},
+		computed: {
+			styles() {
+				let styles = {
+					width: 'auto',
+					height: 'auto',
+					border: {}
+				}
+				return Object.assign(styles, this.imageStyles)
+			},
+			boxStyle() {
+				const {
+					width = 'auto',
+						height = 'auto'
+				} = this.styles
+				let obj = {}
+				if (height === 'auto') {
+					if (width !== 'auto') {
+						obj.height = this.value2px(width)
+						obj['padding-top'] = 0
+					} else {
+						obj.height = 0
+					}
+				} else {
+					obj.height = this.value2px(height)
+					obj['padding-top'] = 0
+				}
+
+				if (width === 'auto') {
+					if (height !== 'auto') {
+						obj.width = this.value2px(height)
+					} else {
+						obj.width = '33.3%'
+					}
+				} else {
+					obj.width = this.value2px(width)
+				}
+
+				let classles = ''
+				for(let i in obj){
+					classles+= `${i}:${obj[i]};`
+				}
+				return classles
+			},
+			borderStyle() {
+				let {
+					border
+				} = this.styles
+				let obj = {}
+				const widthDefaultValue = 1
+				const radiusDefaultValue = 3
+				if (typeof border === 'boolean') {
+					obj.border = border ? '1px #eee solid' : 'none'
+				} else {
+					let width = (border && border.width) || widthDefaultValue
+					width = this.value2px(width)
+					let radius = (border && border.radius) || radiusDefaultValue
+					radius = this.value2px(radius)
+					obj = {
+						'border-width': width,
+						'border-style': (border && border.style) || 'solid',
+						'border-color': (border && border.color) || '#eee',
+						'border-radius': radius
+					}
+				}
+				let classles = ''
+				for(let i in obj){
+					classles+= `${i}:${obj[i]};`
+				}
+				return classles
+			}
+		},
+		methods: {
+			uploadFiles(item, index) {
+				this.$emit("uploadFiles", item)
+			},
+			choose() {
+				this.$emit("choose")
+			},
+			delFile(index) {
+				this.$emit('delFile', index)
+			},
+			prviewImage(img, index) {
+				let urls = []
+				if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
+					this.$emit("choose")
+				}
+				if(this.disablePreview) return
+				this.filesList.forEach(i => {
+					urls.push(i.url)
+				})
+
+				uni.previewImage({
+					urls: urls,
+					current: index
+				});
+			},
+			value2px(value) {
+				if (typeof value === 'number') {
+					value += 'px'
+				} else {
+					if (value.indexOf('%') === -1) {
+						value = value.indexOf('px') !== -1 ? value : value + 'px'
+					}
+				}
+				return value
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-file-picker__container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		box-sizing: border-box;
+		/* #endif */
+		flex-wrap: wrap;
+		margin: -5px;
+	}
+
+	.file-picker__box {
+		position: relative;
+		// flex: 0 0 33.3%;
+		width: 33.3%;
+		height: 0;
+		padding-top: 33.33%;
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		/* #endif */
+	}
+
+	.file-picker__box-content {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		margin: 5px;
+		border: 1px #eee solid;
+		border-radius: 5px;
+		overflow: hidden;
+	}
+
+	.file-picker__progress {
+		position: absolute;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		/* border: 1px red solid; */
+		z-index: 2;
+	}
+
+	.file-picker__progress-item {
+		width: 100%;
+	}
+
+	.file-picker__mask {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		color: #fff;
+		font-size: 12px;
+		background-color: rgba(0, 0, 0, 0.4);
+	}
+
+	.file-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.is-add {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+	}
+
+	.icon-add {
+		width: 50px;
+		height: 5px;
+		background-color: #f1f1f1;
+		border-radius: 2px;
+	}
+
+	.rotate {
+		position: absolute;
+		transform: rotate(90deg);
+	}
+
+	.icon-del-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		top: 3px;
+		right: 3px;
+		height: 26px;
+		width: 26px;
+		border-radius: 50%;
+		background-color: rgba(0, 0, 0, 0.5);
+		z-index: 2;
+		transform: rotate(-45deg);
+	}
+
+	.icon-del {
+		width: 15px;
+		height: 2px;
+		background-color: #fff;
+		border-radius: 2px;
+	}
+</style>

+ 110 - 0
uni_modules/uni-file-picker/components/uni-file-picker/utils.js

@@ -0,0 +1,110 @@
+/**
+ * 获取文件名和后缀
+ * @param {String} name
+ */
+export const get_file_ext = (name) => {
+	const last_len = name.lastIndexOf('.')
+	const len = name.length
+	return {
+		name: name.substring(0, last_len),
+		ext: name.substring(last_len + 1, len)
+	}
+}
+
+/**
+ * 获取扩展名
+ * @param {Array} fileExtname
+ */
+export const get_extname = (fileExtname) => {
+	if (!Array.isArray(fileExtname)) {
+		let extname = fileExtname.replace(/(\[|\])/g, '')
+		return extname.split(',')
+	} else {
+		return fileExtname
+	}
+	return []
+}
+
+/**
+ * 获取文件和检测是否可选
+ */
+export const get_files_and_is_max = (res, _extname) => {
+	let filePaths = []
+	let files = []
+	if(!_extname || _extname.length === 0){
+		return {
+			filePaths,
+			files
+		}
+	}
+	res.tempFiles.forEach(v => {
+		let fileFullName = get_file_ext(v.name)
+		const extname = fileFullName.ext.toLowerCase()
+		if (_extname.indexOf(extname) !== -1) {
+			files.push(v)
+			filePaths.push(v.path)
+		}
+	})
+	if (files.length !== res.tempFiles.length) {
+		uni.showToast({
+			title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`,
+			icon: 'none',
+			duration: 5000
+		})
+	}
+
+	return {
+		filePaths,
+		files
+	}
+}
+
+
+/**
+ * 获取图片信息
+ * @param {Object} filepath
+ */
+export const get_file_info = (filepath) => {
+	return new Promise((resolve, reject) => {
+		uni.getImageInfo({
+			src: filepath,
+			success(res) {
+				resolve(res)
+			},
+			fail(err) {
+				reject(err)
+			}
+		})
+	})
+}
+/**
+ * 获取封装数据
+ */
+export const get_file_data = async (files, type = 'image') => {
+	// 最终需要上传数据库的数据
+	let fileFullName = get_file_ext(files.name)
+	const extname = fileFullName.ext.toLowerCase()
+	let filedata = {
+		name: files.name,
+		uuid: files.uuid,
+		extname: extname || '',
+		cloudPath: files.cloudPath,
+		fileType: files.fileType,
+		thumbTempFilePath: files.thumbTempFilePath,
+		url: files.path || files.path,
+		size: files.size, //单位是字节
+		image: {},
+		path: files.path,
+		video: {}
+	}
+	if (type === 'image') {
+		const imageinfo = await get_file_info(files.path)
+		delete filedata.video
+		filedata.image.width = imageinfo.width
+		filedata.image.height = imageinfo.height
+		filedata.image.location = imageinfo.path
+	} else {
+		delete filedata.image
+	}
+	return filedata
+}

+ 84 - 0
uni_modules/uni-file-picker/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "uni-file-picker",
+  "displayName": "uni-file-picker 文件选择上传",
+  "version": "1.0.11",
+  "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "图片上传",
+    "文件上传"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 11 - 0
uni_modules/uni-file-picker/readme.md

@@ -0,0 +1,11 @@
+
+## FilePicker 文件选择上传
+
+> **组件名:uni-file-picker**
+>  代码块: `uFilePicker`
+
+
+文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 42 - 0
uni_modules/uni-icons/changelog.md

@@ -0,0 +1,42 @@
+## 2.0.10(2024-06-07)
+- 优化 uni-app x 中,size 属性的类型
+## 2.0.9(2024-01-12)
+fix: 修复图标大小默认值错误的问题
+## 2.0.8(2023-12-14)
+- 修复 项目未使用 ts 情况下,打包报错的bug
+## 2.0.7(2023-12-14)
+- 修复 size 属性为 string 时,不加单位导致尺寸异常的bug
+## 2.0.6(2023-12-11)
+- 优化 兼容老版本icon类型,如 top ,bottom 等
+## 2.0.5(2023-12-11)
+- 优化 兼容老版本icon类型,如 top ,bottom 等
+## 2.0.4(2023-12-06)
+- 优化 uni-app x 下示例项目图标排序
+## 2.0.3(2023-12-06)
+- 修复 nvue下引入组件报错的bug
+## 2.0.2(2023-12-05)
+-优化 size 属性支持单位
+## 2.0.1(2023-12-05)
+- 新增 uni-app x 支持定义图标
+## 1.3.5(2022-01-24)
+- 优化 size 属性可以传入不带单位的字符串数值
+## 1.3.4(2022-01-24)
+- 优化 size 支持其他单位
+## 1.3.3(2022-01-17)
+- 修复 nvue 有些图标不显示的bug,兼容老版本图标
+## 1.3.2(2021-12-01)
+- 优化 示例可复制图标名称
+## 1.3.1(2021-11-23)
+- 优化 兼容旧组件 type 值
+## 1.3.0(2021-11-19)
+- 新增 更多图标
+- 优化 自定义图标使用方式
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons)
+## 1.1.7(2021-11-08)
+## 1.2.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.5(2021-05-12)
+- 新增 组件示例地址
+## 1.1.4(2021-02-05)
+- 调整为uni_modules目录规范

+ 91 - 0
uni_modules/uni-icons/components/uni-icons/uni-icons.uvue

@@ -0,0 +1,91 @@
+<template>
+  <text class="uni-icons" :style="styleObj">
+    <slot>{{unicode}}</slot>
+  </text>
+</template>
+
+<script>
+  import { fontData, IconsDataItem } from './uniicons_file'
+
+  /**
+   * Icons 图标
+   * @description 用于展示 icon 图标
+   * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+   * @property {Number,String} size 图标大小
+   * @property {String} type 图标图案,参考示例
+   * @property {String} color 图标颜色
+   * @property {String} customPrefix 自定义图标
+   * @event {Function} click 点击 Icon 触发事件
+   */
+  export default {
+    name: "uni-icons",
+    props: {
+      type: {
+        type: String,
+        default: ''
+      },
+      color: {
+        type: String,
+        default: '#333333'
+      },
+      size: {
+        type: [Number, String],
+        default: 16
+      },
+      fontFamily: {
+        type: String,
+        default: ''
+      }
+    },
+    data() {
+      return {};
+    },
+    computed: {
+      unicode() : string {
+        let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
+        if (codes !== null) {
+          return codes.unicode
+        }
+        return ''
+      },
+      iconSize() : string {
+        const size = this.size
+        if (typeof size == 'string') {
+          const reg = /^[0-9]*$/g
+          return reg.test(size as string) ? '' + size + 'px' : '' + size;
+          // return '' + this.size
+        }
+        return this.getFontSize(size as number)
+      },
+      styleObj() : UTSJSONObject {
+        if (this.fontFamily !== '') {
+          return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
+        }
+        return { color: this.color, fontSize: this.iconSize }
+      }
+    },
+    created() { },
+    methods: {
+      /**
+       * 字体大小
+       */
+      getFontSize(size : number) : string {
+        return size + 'px';
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  @font-face {
+    font-family: UniIconsFontFamily;
+    src: url('./uniicons.ttf');
+  }
+
+  .uni-icons {
+    font-family: UniIconsFontFamily;
+    font-size: 18px;
+    font-style: normal;
+    color: #333;
+  }
+</style>

+ 110 - 0
uni_modules/uni-icons/components/uni-icons/uni-icons.vue

@@ -0,0 +1,110 @@
+<template>
+	<!-- #ifdef APP-NVUE -->
+	<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
+	<!-- #endif -->
+	<!-- #ifndef APP-NVUE -->
+	<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
+		<slot></slot>
+	</text>
+	<!-- #endif -->
+</template>
+
+<script>
+	import { fontData } from './uniicons_file_vue.js';
+
+	const getVal = (val) => {
+		const reg = /^[0-9]*$/g
+		return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
+	}
+
+	// #ifdef APP-NVUE
+	var domModule = weex.requireModule('dom');
+	import iconUrl from './uniicons.ttf'
+	domModule.addRule('fontFace', {
+		'fontFamily': "uniicons",
+		'src': "url('" + iconUrl + "')"
+	});
+	// #endif
+
+	/**
+	 * Icons 图标
+	 * @description 用于展示 icons 图标
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 图标大小
+	 * @property {String} type 图标图案,参考示例
+	 * @property {String} color 图标颜色
+	 * @property {String} customPrefix 自定义图标
+	 * @event {Function} click 点击 Icon 触发事件
+	 */
+	export default {
+		name: 'UniIcons',
+		emits: ['click'],
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
+				type: [Number, String],
+				default: 16
+			},
+			customPrefix: {
+				type: String,
+				default: ''
+			},
+			fontFamily: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				icons: fontData
+			}
+		},
+		computed: {
+			unicode() {
+				let code = this.icons.find(v => v.font_class === this.type)
+				if (code) {
+					return code.unicode
+				}
+				return ''
+			},
+			iconSize() {
+				return getVal(this.size)
+			},
+			styleObj() {
+				if (this.fontFamily !== '') {
+					return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
+				}
+				return `color: ${this.color}; font-size: ${this.iconSize};`
+			}
+		},
+		methods: {
+			_onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	/* #ifndef APP-NVUE */
+	@import './uniicons.css';
+
+	@font-face {
+		font-family: uniicons;
+		src: url('./uniicons.ttf');
+	}
+
+	/* #endif */
+	.uni-icons {
+		font-family: uniicons;
+		text-decoration: none;
+		text-align: center;
+	}
+</style>

+ 664 - 0
uni_modules/uni-icons/components/uni-icons/uniicons.css

@@ -0,0 +1,664 @@
+
+.uniui-cart-filled:before {
+  content: "\e6d0";
+}
+
+.uniui-gift-filled:before {
+  content: "\e6c4";
+}
+
+.uniui-color:before {
+  content: "\e6cf";
+}
+
+.uniui-wallet:before {
+  content: "\e6b1";
+}
+
+.uniui-settings-filled:before {
+  content: "\e6ce";
+}
+
+.uniui-auth-filled:before {
+  content: "\e6cc";
+}
+
+.uniui-shop-filled:before {
+  content: "\e6cd";
+}
+
+.uniui-staff-filled:before {
+  content: "\e6cb";
+}
+
+.uniui-vip-filled:before {
+  content: "\e6c6";
+}
+
+.uniui-plus-filled:before {
+  content: "\e6c7";
+}
+
+.uniui-folder-add-filled:before {
+  content: "\e6c8";
+}
+
+.uniui-color-filled:before {
+  content: "\e6c9";
+}
+
+.uniui-tune-filled:before {
+  content: "\e6ca";
+}
+
+.uniui-calendar-filled:before {
+  content: "\e6c0";
+}
+
+.uniui-notification-filled:before {
+  content: "\e6c1";
+}
+
+.uniui-wallet-filled:before {
+  content: "\e6c2";
+}
+
+.uniui-medal-filled:before {
+  content: "\e6c3";
+}
+
+.uniui-fire-filled:before {
+  content: "\e6c5";
+}
+
+.uniui-refreshempty:before {
+  content: "\e6bf";
+}
+
+.uniui-location-filled:before {
+  content: "\e6af";
+}
+
+.uniui-person-filled:before {
+  content: "\e69d";
+}
+
+.uniui-personadd-filled:before {
+  content: "\e698";
+}
+
+.uniui-arrowthinleft:before {
+  content: "\e6d2";
+}
+
+.uniui-arrowthinup:before {
+  content: "\e6d3";
+}
+
+.uniui-arrowthindown:before {
+  content: "\e6d4";
+}
+
+.uniui-back:before {
+  content: "\e6b9";
+}
+
+.uniui-forward:before {
+  content: "\e6ba";
+}
+
+.uniui-arrow-right:before {
+  content: "\e6bb";
+}
+
+.uniui-arrow-left:before {
+  content: "\e6bc";
+}
+
+.uniui-arrow-up:before {
+  content: "\e6bd";
+}
+
+.uniui-arrow-down:before {
+  content: "\e6be";
+}
+
+.uniui-arrowthinright:before {
+  content: "\e6d1";
+}
+
+.uniui-down:before {
+  content: "\e6b8";
+}
+
+.uniui-bottom:before {
+  content: "\e6b8";
+}
+
+.uniui-arrowright:before {
+  content: "\e6d5";
+}
+
+.uniui-right:before {
+  content: "\e6b5";
+}
+
+.uniui-up:before {
+  content: "\e6b6";
+}
+
+.uniui-top:before {
+  content: "\e6b6";
+}
+
+.uniui-left:before {
+  content: "\e6b7";
+}
+
+.uniui-arrowup:before {
+  content: "\e6d6";
+}
+
+.uniui-eye:before {
+  content: "\e651";
+}
+
+.uniui-eye-filled:before {
+  content: "\e66a";
+}
+
+.uniui-eye-slash:before {
+  content: "\e6b3";
+}
+
+.uniui-eye-slash-filled:before {
+  content: "\e6b4";
+}
+
+.uniui-info-filled:before {
+  content: "\e649";
+}
+
+.uniui-reload:before {
+  content: "\e6b2";
+}
+
+.uniui-micoff-filled:before {
+  content: "\e6b0";
+}
+
+.uniui-map-pin-ellipse:before {
+  content: "\e6ac";
+}
+
+.uniui-map-pin:before {
+  content: "\e6ad";
+}
+
+.uniui-location:before {
+  content: "\e6ae";
+}
+
+.uniui-starhalf:before {
+  content: "\e683";
+}
+
+.uniui-star:before {
+  content: "\e688";
+}
+
+.uniui-star-filled:before {
+  content: "\e68f";
+}
+
+.uniui-calendar:before {
+  content: "\e6a0";
+}
+
+.uniui-fire:before {
+  content: "\e6a1";
+}
+
+.uniui-medal:before {
+  content: "\e6a2";
+}
+
+.uniui-font:before {
+  content: "\e6a3";
+}
+
+.uniui-gift:before {
+  content: "\e6a4";
+}
+
+.uniui-link:before {
+  content: "\e6a5";
+}
+
+.uniui-notification:before {
+  content: "\e6a6";
+}
+
+.uniui-staff:before {
+  content: "\e6a7";
+}
+
+.uniui-vip:before {
+  content: "\e6a8";
+}
+
+.uniui-folder-add:before {
+  content: "\e6a9";
+}
+
+.uniui-tune:before {
+  content: "\e6aa";
+}
+
+.uniui-auth:before {
+  content: "\e6ab";
+}
+
+.uniui-person:before {
+  content: "\e699";
+}
+
+.uniui-email-filled:before {
+  content: "\e69a";
+}
+
+.uniui-phone-filled:before {
+  content: "\e69b";
+}
+
+.uniui-phone:before {
+  content: "\e69c";
+}
+
+.uniui-email:before {
+  content: "\e69e";
+}
+
+.uniui-personadd:before {
+  content: "\e69f";
+}
+
+.uniui-chatboxes-filled:before {
+  content: "\e692";
+}
+
+.uniui-contact:before {
+  content: "\e693";
+}
+
+.uniui-chatbubble-filled:before {
+  content: "\e694";
+}
+
+.uniui-contact-filled:before {
+  content: "\e695";
+}
+
+.uniui-chatboxes:before {
+  content: "\e696";
+}
+
+.uniui-chatbubble:before {
+  content: "\e697";
+}
+
+.uniui-upload-filled:before {
+  content: "\e68e";
+}
+
+.uniui-upload:before {
+  content: "\e690";
+}
+
+.uniui-weixin:before {
+  content: "\e691";
+}
+
+.uniui-compose:before {
+  content: "\e67f";
+}
+
+.uniui-qq:before {
+  content: "\e680";
+}
+
+.uniui-download-filled:before {
+  content: "\e681";
+}
+
+.uniui-pyq:before {
+  content: "\e682";
+}
+
+.uniui-sound:before {
+  content: "\e684";
+}
+
+.uniui-trash-filled:before {
+  content: "\e685";
+}
+
+.uniui-sound-filled:before {
+  content: "\e686";
+}
+
+.uniui-trash:before {
+  content: "\e687";
+}
+
+.uniui-videocam-filled:before {
+  content: "\e689";
+}
+
+.uniui-spinner-cycle:before {
+  content: "\e68a";
+}
+
+.uniui-weibo:before {
+  content: "\e68b";
+}
+
+.uniui-videocam:before {
+  content: "\e68c";
+}
+
+.uniui-download:before {
+  content: "\e68d";
+}
+
+.uniui-help:before {
+  content: "\e679";
+}
+
+.uniui-navigate-filled:before {
+  content: "\e67a";
+}
+
+.uniui-plusempty:before {
+  content: "\e67b";
+}
+
+.uniui-smallcircle:before {
+  content: "\e67c";
+}
+
+.uniui-minus-filled:before {
+  content: "\e67d";
+}
+
+.uniui-micoff:before {
+  content: "\e67e";
+}
+
+.uniui-closeempty:before {
+  content: "\e66c";
+}
+
+.uniui-clear:before {
+  content: "\e66d";
+}
+
+.uniui-navigate:before {
+  content: "\e66e";
+}
+
+.uniui-minus:before {
+  content: "\e66f";
+}
+
+.uniui-image:before {
+  content: "\e670";
+}
+
+.uniui-mic:before {
+  content: "\e671";
+}
+
+.uniui-paperplane:before {
+  content: "\e672";
+}
+
+.uniui-close:before {
+  content: "\e673";
+}
+
+.uniui-help-filled:before {
+  content: "\e674";
+}
+
+.uniui-paperplane-filled:before {
+  content: "\e675";
+}
+
+.uniui-plus:before {
+  content: "\e676";
+}
+
+.uniui-mic-filled:before {
+  content: "\e677";
+}
+
+.uniui-image-filled:before {
+  content: "\e678";
+}
+
+.uniui-locked-filled:before {
+  content: "\e668";
+}
+
+.uniui-info:before {
+  content: "\e669";
+}
+
+.uniui-locked:before {
+  content: "\e66b";
+}
+
+.uniui-camera-filled:before {
+  content: "\e658";
+}
+
+.uniui-chat-filled:before {
+  content: "\e659";
+}
+
+.uniui-camera:before {
+  content: "\e65a";
+}
+
+.uniui-circle:before {
+  content: "\e65b";
+}
+
+.uniui-checkmarkempty:before {
+  content: "\e65c";
+}
+
+.uniui-chat:before {
+  content: "\e65d";
+}
+
+.uniui-circle-filled:before {
+  content: "\e65e";
+}
+
+.uniui-flag:before {
+  content: "\e65f";
+}
+
+.uniui-flag-filled:before {
+  content: "\e660";
+}
+
+.uniui-gear-filled:before {
+  content: "\e661";
+}
+
+.uniui-home:before {
+  content: "\e662";
+}
+
+.uniui-home-filled:before {
+  content: "\e663";
+}
+
+.uniui-gear:before {
+  content: "\e664";
+}
+
+.uniui-smallcircle-filled:before {
+  content: "\e665";
+}
+
+.uniui-map-filled:before {
+  content: "\e666";
+}
+
+.uniui-map:before {
+  content: "\e667";
+}
+
+.uniui-refresh-filled:before {
+  content: "\e656";
+}
+
+.uniui-refresh:before {
+  content: "\e657";
+}
+
+.uniui-cloud-upload:before {
+  content: "\e645";
+}
+
+.uniui-cloud-download-filled:before {
+  content: "\e646";
+}
+
+.uniui-cloud-download:before {
+  content: "\e647";
+}
+
+.uniui-cloud-upload-filled:before {
+  content: "\e648";
+}
+
+.uniui-redo:before {
+  content: "\e64a";
+}
+
+.uniui-images-filled:before {
+  content: "\e64b";
+}
+
+.uniui-undo-filled:before {
+  content: "\e64c";
+}
+
+.uniui-more:before {
+  content: "\e64d";
+}
+
+.uniui-more-filled:before {
+  content: "\e64e";
+}
+
+.uniui-undo:before {
+  content: "\e64f";
+}
+
+.uniui-images:before {
+  content: "\e650";
+}
+
+.uniui-paperclip:before {
+  content: "\e652";
+}
+
+.uniui-settings:before {
+  content: "\e653";
+}
+
+.uniui-search:before {
+  content: "\e654";
+}
+
+.uniui-redo-filled:before {
+  content: "\e655";
+}
+
+.uniui-list:before {
+  content: "\e644";
+}
+
+.uniui-mail-open-filled:before {
+  content: "\e63a";
+}
+
+.uniui-hand-down-filled:before {
+  content: "\e63c";
+}
+
+.uniui-hand-down:before {
+  content: "\e63d";
+}
+
+.uniui-hand-up-filled:before {
+  content: "\e63e";
+}
+
+.uniui-hand-up:before {
+  content: "\e63f";
+}
+
+.uniui-heart-filled:before {
+  content: "\e641";
+}
+
+.uniui-mail-open:before {
+  content: "\e643";
+}
+
+.uniui-heart:before {
+  content: "\e639";
+}
+
+.uniui-loop:before {
+  content: "\e633";
+}
+
+.uniui-pulldown:before {
+  content: "\e632";
+}
+
+.uniui-scan:before {
+  content: "\e62a";
+}
+
+.uniui-bars:before {
+  content: "\e627";
+}
+
+.uniui-checkbox:before {
+  content: "\e62b";
+}
+
+.uniui-checkbox-filled:before {
+  content: "\e62c";
+}
+
+.uniui-shop:before {
+  content: "\e62f";
+}
+
+.uniui-headphones:before {
+  content: "\e630";
+}
+
+.uniui-cart:before {
+  content: "\e631";
+}

BIN
uni_modules/uni-icons/components/uni-icons/uniicons.ttf


+ 664 - 0
uni_modules/uni-icons/components/uni-icons/uniicons_file.ts

@@ -0,0 +1,664 @@
+
+export type IconsData = {
+	id : string
+	name : string
+	font_family : string
+	css_prefix_text : string
+	description : string
+	glyphs : Array<IconsDataItem>
+}
+
+export type IconsDataItem = {
+	font_class : string
+	unicode : string
+}
+
+
+export const fontData = [
+  {
+    "font_class": "arrow-down",
+    "unicode": "\ue6be"
+  },
+  {
+    "font_class": "arrow-left",
+    "unicode": "\ue6bc"
+  },
+  {
+    "font_class": "arrow-right",
+    "unicode": "\ue6bb"
+  },
+  {
+    "font_class": "arrow-up",
+    "unicode": "\ue6bd"
+  },
+  {
+    "font_class": "auth",
+    "unicode": "\ue6ab"
+  },
+  {
+    "font_class": "auth-filled",
+    "unicode": "\ue6cc"
+  },
+  {
+    "font_class": "back",
+    "unicode": "\ue6b9"
+  },
+  {
+    "font_class": "bars",
+    "unicode": "\ue627"
+  },
+  {
+    "font_class": "calendar",
+    "unicode": "\ue6a0"
+  },
+  {
+    "font_class": "calendar-filled",
+    "unicode": "\ue6c0"
+  },
+  {
+    "font_class": "camera",
+    "unicode": "\ue65a"
+  },
+  {
+    "font_class": "camera-filled",
+    "unicode": "\ue658"
+  },
+  {
+    "font_class": "cart",
+    "unicode": "\ue631"
+  },
+  {
+    "font_class": "cart-filled",
+    "unicode": "\ue6d0"
+  },
+  {
+    "font_class": "chat",
+    "unicode": "\ue65d"
+  },
+  {
+    "font_class": "chat-filled",
+    "unicode": "\ue659"
+  },
+  {
+    "font_class": "chatboxes",
+    "unicode": "\ue696"
+  },
+  {
+    "font_class": "chatboxes-filled",
+    "unicode": "\ue692"
+  },
+  {
+    "font_class": "chatbubble",
+    "unicode": "\ue697"
+  },
+  {
+    "font_class": "chatbubble-filled",
+    "unicode": "\ue694"
+  },
+  {
+    "font_class": "checkbox",
+    "unicode": "\ue62b"
+  },
+  {
+    "font_class": "checkbox-filled",
+    "unicode": "\ue62c"
+  },
+  {
+    "font_class": "checkmarkempty",
+    "unicode": "\ue65c"
+  },
+  {
+    "font_class": "circle",
+    "unicode": "\ue65b"
+  },
+  {
+    "font_class": "circle-filled",
+    "unicode": "\ue65e"
+  },
+  {
+    "font_class": "clear",
+    "unicode": "\ue66d"
+  },
+  {
+    "font_class": "close",
+    "unicode": "\ue673"
+  },
+  {
+    "font_class": "closeempty",
+    "unicode": "\ue66c"
+  },
+  {
+    "font_class": "cloud-download",
+    "unicode": "\ue647"
+  },
+  {
+    "font_class": "cloud-download-filled",
+    "unicode": "\ue646"
+  },
+  {
+    "font_class": "cloud-upload",
+    "unicode": "\ue645"
+  },
+  {
+    "font_class": "cloud-upload-filled",
+    "unicode": "\ue648"
+  },
+  {
+    "font_class": "color",
+    "unicode": "\ue6cf"
+  },
+  {
+    "font_class": "color-filled",
+    "unicode": "\ue6c9"
+  },
+  {
+    "font_class": "compose",
+    "unicode": "\ue67f"
+  },
+  {
+    "font_class": "contact",
+    "unicode": "\ue693"
+  },
+  {
+    "font_class": "contact-filled",
+    "unicode": "\ue695"
+  },
+  {
+    "font_class": "down",
+    "unicode": "\ue6b8"
+  },
+	{
+	  "font_class": "bottom",
+	  "unicode": "\ue6b8"
+	},
+  {
+    "font_class": "download",
+    "unicode": "\ue68d"
+  },
+  {
+    "font_class": "download-filled",
+    "unicode": "\ue681"
+  },
+  {
+    "font_class": "email",
+    "unicode": "\ue69e"
+  },
+  {
+    "font_class": "email-filled",
+    "unicode": "\ue69a"
+  },
+  {
+    "font_class": "eye",
+    "unicode": "\ue651"
+  },
+  {
+    "font_class": "eye-filled",
+    "unicode": "\ue66a"
+  },
+  {
+    "font_class": "eye-slash",
+    "unicode": "\ue6b3"
+  },
+  {
+    "font_class": "eye-slash-filled",
+    "unicode": "\ue6b4"
+  },
+  {
+    "font_class": "fire",
+    "unicode": "\ue6a1"
+  },
+  {
+    "font_class": "fire-filled",
+    "unicode": "\ue6c5"
+  },
+  {
+    "font_class": "flag",
+    "unicode": "\ue65f"
+  },
+  {
+    "font_class": "flag-filled",
+    "unicode": "\ue660"
+  },
+  {
+    "font_class": "folder-add",
+    "unicode": "\ue6a9"
+  },
+  {
+    "font_class": "folder-add-filled",
+    "unicode": "\ue6c8"
+  },
+  {
+    "font_class": "font",
+    "unicode": "\ue6a3"
+  },
+  {
+    "font_class": "forward",
+    "unicode": "\ue6ba"
+  },
+  {
+    "font_class": "gear",
+    "unicode": "\ue664"
+  },
+  {
+    "font_class": "gear-filled",
+    "unicode": "\ue661"
+  },
+  {
+    "font_class": "gift",
+    "unicode": "\ue6a4"
+  },
+  {
+    "font_class": "gift-filled",
+    "unicode": "\ue6c4"
+  },
+  {
+    "font_class": "hand-down",
+    "unicode": "\ue63d"
+  },
+  {
+    "font_class": "hand-down-filled",
+    "unicode": "\ue63c"
+  },
+  {
+    "font_class": "hand-up",
+    "unicode": "\ue63f"
+  },
+  {
+    "font_class": "hand-up-filled",
+    "unicode": "\ue63e"
+  },
+  {
+    "font_class": "headphones",
+    "unicode": "\ue630"
+  },
+  {
+    "font_class": "heart",
+    "unicode": "\ue639"
+  },
+  {
+    "font_class": "heart-filled",
+    "unicode": "\ue641"
+  },
+  {
+    "font_class": "help",
+    "unicode": "\ue679"
+  },
+  {
+    "font_class": "help-filled",
+    "unicode": "\ue674"
+  },
+  {
+    "font_class": "home",
+    "unicode": "\ue662"
+  },
+  {
+    "font_class": "home-filled",
+    "unicode": "\ue663"
+  },
+  {
+    "font_class": "image",
+    "unicode": "\ue670"
+  },
+  {
+    "font_class": "image-filled",
+    "unicode": "\ue678"
+  },
+  {
+    "font_class": "images",
+    "unicode": "\ue650"
+  },
+  {
+    "font_class": "images-filled",
+    "unicode": "\ue64b"
+  },
+  {
+    "font_class": "info",
+    "unicode": "\ue669"
+  },
+  {
+    "font_class": "info-filled",
+    "unicode": "\ue649"
+  },
+  {
+    "font_class": "left",
+    "unicode": "\ue6b7"
+  },
+  {
+    "font_class": "link",
+    "unicode": "\ue6a5"
+  },
+  {
+    "font_class": "list",
+    "unicode": "\ue644"
+  },
+  {
+    "font_class": "location",
+    "unicode": "\ue6ae"
+  },
+  {
+    "font_class": "location-filled",
+    "unicode": "\ue6af"
+  },
+  {
+    "font_class": "locked",
+    "unicode": "\ue66b"
+  },
+  {
+    "font_class": "locked-filled",
+    "unicode": "\ue668"
+  },
+  {
+    "font_class": "loop",
+    "unicode": "\ue633"
+  },
+  {
+    "font_class": "mail-open",
+    "unicode": "\ue643"
+  },
+  {
+    "font_class": "mail-open-filled",
+    "unicode": "\ue63a"
+  },
+  {
+    "font_class": "map",
+    "unicode": "\ue667"
+  },
+  {
+    "font_class": "map-filled",
+    "unicode": "\ue666"
+  },
+  {
+    "font_class": "map-pin",
+    "unicode": "\ue6ad"
+  },
+  {
+    "font_class": "map-pin-ellipse",
+    "unicode": "\ue6ac"
+  },
+  {
+    "font_class": "medal",
+    "unicode": "\ue6a2"
+  },
+  {
+    "font_class": "medal-filled",
+    "unicode": "\ue6c3"
+  },
+  {
+    "font_class": "mic",
+    "unicode": "\ue671"
+  },
+  {
+    "font_class": "mic-filled",
+    "unicode": "\ue677"
+  },
+  {
+    "font_class": "micoff",
+    "unicode": "\ue67e"
+  },
+  {
+    "font_class": "micoff-filled",
+    "unicode": "\ue6b0"
+  },
+  {
+    "font_class": "minus",
+    "unicode": "\ue66f"
+  },
+  {
+    "font_class": "minus-filled",
+    "unicode": "\ue67d"
+  },
+  {
+    "font_class": "more",
+    "unicode": "\ue64d"
+  },
+  {
+    "font_class": "more-filled",
+    "unicode": "\ue64e"
+  },
+  {
+    "font_class": "navigate",
+    "unicode": "\ue66e"
+  },
+  {
+    "font_class": "navigate-filled",
+    "unicode": "\ue67a"
+  },
+  {
+    "font_class": "notification",
+    "unicode": "\ue6a6"
+  },
+  {
+    "font_class": "notification-filled",
+    "unicode": "\ue6c1"
+  },
+  {
+    "font_class": "paperclip",
+    "unicode": "\ue652"
+  },
+  {
+    "font_class": "paperplane",
+    "unicode": "\ue672"
+  },
+  {
+    "font_class": "paperplane-filled",
+    "unicode": "\ue675"
+  },
+  {
+    "font_class": "person",
+    "unicode": "\ue699"
+  },
+  {
+    "font_class": "person-filled",
+    "unicode": "\ue69d"
+  },
+  {
+    "font_class": "personadd",
+    "unicode": "\ue69f"
+  },
+  {
+    "font_class": "personadd-filled",
+    "unicode": "\ue698"
+  },
+  {
+    "font_class": "personadd-filled-copy",
+    "unicode": "\ue6d1"
+  },
+  {
+    "font_class": "phone",
+    "unicode": "\ue69c"
+  },
+  {
+    "font_class": "phone-filled",
+    "unicode": "\ue69b"
+  },
+  {
+    "font_class": "plus",
+    "unicode": "\ue676"
+  },
+  {
+    "font_class": "plus-filled",
+    "unicode": "\ue6c7"
+  },
+  {
+    "font_class": "plusempty",
+    "unicode": "\ue67b"
+  },
+  {
+    "font_class": "pulldown",
+    "unicode": "\ue632"
+  },
+  {
+    "font_class": "pyq",
+    "unicode": "\ue682"
+  },
+  {
+    "font_class": "qq",
+    "unicode": "\ue680"
+  },
+  {
+    "font_class": "redo",
+    "unicode": "\ue64a"
+  },
+  {
+    "font_class": "redo-filled",
+    "unicode": "\ue655"
+  },
+  {
+    "font_class": "refresh",
+    "unicode": "\ue657"
+  },
+  {
+    "font_class": "refresh-filled",
+    "unicode": "\ue656"
+  },
+  {
+    "font_class": "refreshempty",
+    "unicode": "\ue6bf"
+  },
+  {
+    "font_class": "reload",
+    "unicode": "\ue6b2"
+  },
+  {
+    "font_class": "right",
+    "unicode": "\ue6b5"
+  },
+  {
+    "font_class": "scan",
+    "unicode": "\ue62a"
+  },
+  {
+    "font_class": "search",
+    "unicode": "\ue654"
+  },
+  {
+    "font_class": "settings",
+    "unicode": "\ue653"
+  },
+  {
+    "font_class": "settings-filled",
+    "unicode": "\ue6ce"
+  },
+  {
+    "font_class": "shop",
+    "unicode": "\ue62f"
+  },
+  {
+    "font_class": "shop-filled",
+    "unicode": "\ue6cd"
+  },
+  {
+    "font_class": "smallcircle",
+    "unicode": "\ue67c"
+  },
+  {
+    "font_class": "smallcircle-filled",
+    "unicode": "\ue665"
+  },
+  {
+    "font_class": "sound",
+    "unicode": "\ue684"
+  },
+  {
+    "font_class": "sound-filled",
+    "unicode": "\ue686"
+  },
+  {
+    "font_class": "spinner-cycle",
+    "unicode": "\ue68a"
+  },
+  {
+    "font_class": "staff",
+    "unicode": "\ue6a7"
+  },
+  {
+    "font_class": "staff-filled",
+    "unicode": "\ue6cb"
+  },
+  {
+    "font_class": "star",
+    "unicode": "\ue688"
+  },
+  {
+    "font_class": "star-filled",
+    "unicode": "\ue68f"
+  },
+  {
+    "font_class": "starhalf",
+    "unicode": "\ue683"
+  },
+  {
+    "font_class": "trash",
+    "unicode": "\ue687"
+  },
+  {
+    "font_class": "trash-filled",
+    "unicode": "\ue685"
+  },
+  {
+    "font_class": "tune",
+    "unicode": "\ue6aa"
+  },
+  {
+    "font_class": "tune-filled",
+    "unicode": "\ue6ca"
+  },
+  {
+    "font_class": "undo",
+    "unicode": "\ue64f"
+  },
+  {
+    "font_class": "undo-filled",
+    "unicode": "\ue64c"
+  },
+  {
+    "font_class": "up",
+    "unicode": "\ue6b6"
+  },
+	{
+	  "font_class": "top",
+	  "unicode": "\ue6b6"
+	},
+  {
+    "font_class": "upload",
+    "unicode": "\ue690"
+  },
+  {
+    "font_class": "upload-filled",
+    "unicode": "\ue68e"
+  },
+  {
+    "font_class": "videocam",
+    "unicode": "\ue68c"
+  },
+  {
+    "font_class": "videocam-filled",
+    "unicode": "\ue689"
+  },
+  {
+    "font_class": "vip",
+    "unicode": "\ue6a8"
+  },
+  {
+    "font_class": "vip-filled",
+    "unicode": "\ue6c6"
+  },
+  {
+    "font_class": "wallet",
+    "unicode": "\ue6b1"
+  },
+  {
+    "font_class": "wallet-filled",
+    "unicode": "\ue6c2"
+  },
+  {
+    "font_class": "weibo",
+    "unicode": "\ue68b"
+  },
+  {
+    "font_class": "weixin",
+    "unicode": "\ue691"
+  }
+] as IconsDataItem[]
+
+// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

+ 649 - 0
uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js

@@ -0,0 +1,649 @@
+
+export const fontData = [
+  {
+    "font_class": "arrow-down",
+    "unicode": "\ue6be"
+  },
+  {
+    "font_class": "arrow-left",
+    "unicode": "\ue6bc"
+  },
+  {
+    "font_class": "arrow-right",
+    "unicode": "\ue6bb"
+  },
+  {
+    "font_class": "arrow-up",
+    "unicode": "\ue6bd"
+  },
+  {
+    "font_class": "auth",
+    "unicode": "\ue6ab"
+  },
+  {
+    "font_class": "auth-filled",
+    "unicode": "\ue6cc"
+  },
+  {
+    "font_class": "back",
+    "unicode": "\ue6b9"
+  },
+  {
+    "font_class": "bars",
+    "unicode": "\ue627"
+  },
+  {
+    "font_class": "calendar",
+    "unicode": "\ue6a0"
+  },
+  {
+    "font_class": "calendar-filled",
+    "unicode": "\ue6c0"
+  },
+  {
+    "font_class": "camera",
+    "unicode": "\ue65a"
+  },
+  {
+    "font_class": "camera-filled",
+    "unicode": "\ue658"
+  },
+  {
+    "font_class": "cart",
+    "unicode": "\ue631"
+  },
+  {
+    "font_class": "cart-filled",
+    "unicode": "\ue6d0"
+  },
+  {
+    "font_class": "chat",
+    "unicode": "\ue65d"
+  },
+  {
+    "font_class": "chat-filled",
+    "unicode": "\ue659"
+  },
+  {
+    "font_class": "chatboxes",
+    "unicode": "\ue696"
+  },
+  {
+    "font_class": "chatboxes-filled",
+    "unicode": "\ue692"
+  },
+  {
+    "font_class": "chatbubble",
+    "unicode": "\ue697"
+  },
+  {
+    "font_class": "chatbubble-filled",
+    "unicode": "\ue694"
+  },
+  {
+    "font_class": "checkbox",
+    "unicode": "\ue62b"
+  },
+  {
+    "font_class": "checkbox-filled",
+    "unicode": "\ue62c"
+  },
+  {
+    "font_class": "checkmarkempty",
+    "unicode": "\ue65c"
+  },
+  {
+    "font_class": "circle",
+    "unicode": "\ue65b"
+  },
+  {
+    "font_class": "circle-filled",
+    "unicode": "\ue65e"
+  },
+  {
+    "font_class": "clear",
+    "unicode": "\ue66d"
+  },
+  {
+    "font_class": "close",
+    "unicode": "\ue673"
+  },
+  {
+    "font_class": "closeempty",
+    "unicode": "\ue66c"
+  },
+  {
+    "font_class": "cloud-download",
+    "unicode": "\ue647"
+  },
+  {
+    "font_class": "cloud-download-filled",
+    "unicode": "\ue646"
+  },
+  {
+    "font_class": "cloud-upload",
+    "unicode": "\ue645"
+  },
+  {
+    "font_class": "cloud-upload-filled",
+    "unicode": "\ue648"
+  },
+  {
+    "font_class": "color",
+    "unicode": "\ue6cf"
+  },
+  {
+    "font_class": "color-filled",
+    "unicode": "\ue6c9"
+  },
+  {
+    "font_class": "compose",
+    "unicode": "\ue67f"
+  },
+  {
+    "font_class": "contact",
+    "unicode": "\ue693"
+  },
+  {
+    "font_class": "contact-filled",
+    "unicode": "\ue695"
+  },
+  {
+    "font_class": "down",
+    "unicode": "\ue6b8"
+  },
+	{
+	  "font_class": "bottom",
+	  "unicode": "\ue6b8"
+	},
+  {
+    "font_class": "download",
+    "unicode": "\ue68d"
+  },
+  {
+    "font_class": "download-filled",
+    "unicode": "\ue681"
+  },
+  {
+    "font_class": "email",
+    "unicode": "\ue69e"
+  },
+  {
+    "font_class": "email-filled",
+    "unicode": "\ue69a"
+  },
+  {
+    "font_class": "eye",
+    "unicode": "\ue651"
+  },
+  {
+    "font_class": "eye-filled",
+    "unicode": "\ue66a"
+  },
+  {
+    "font_class": "eye-slash",
+    "unicode": "\ue6b3"
+  },
+  {
+    "font_class": "eye-slash-filled",
+    "unicode": "\ue6b4"
+  },
+  {
+    "font_class": "fire",
+    "unicode": "\ue6a1"
+  },
+  {
+    "font_class": "fire-filled",
+    "unicode": "\ue6c5"
+  },
+  {
+    "font_class": "flag",
+    "unicode": "\ue65f"
+  },
+  {
+    "font_class": "flag-filled",
+    "unicode": "\ue660"
+  },
+  {
+    "font_class": "folder-add",
+    "unicode": "\ue6a9"
+  },
+  {
+    "font_class": "folder-add-filled",
+    "unicode": "\ue6c8"
+  },
+  {
+    "font_class": "font",
+    "unicode": "\ue6a3"
+  },
+  {
+    "font_class": "forward",
+    "unicode": "\ue6ba"
+  },
+  {
+    "font_class": "gear",
+    "unicode": "\ue664"
+  },
+  {
+    "font_class": "gear-filled",
+    "unicode": "\ue661"
+  },
+  {
+    "font_class": "gift",
+    "unicode": "\ue6a4"
+  },
+  {
+    "font_class": "gift-filled",
+    "unicode": "\ue6c4"
+  },
+  {
+    "font_class": "hand-down",
+    "unicode": "\ue63d"
+  },
+  {
+    "font_class": "hand-down-filled",
+    "unicode": "\ue63c"
+  },
+  {
+    "font_class": "hand-up",
+    "unicode": "\ue63f"
+  },
+  {
+    "font_class": "hand-up-filled",
+    "unicode": "\ue63e"
+  },
+  {
+    "font_class": "headphones",
+    "unicode": "\ue630"
+  },
+  {
+    "font_class": "heart",
+    "unicode": "\ue639"
+  },
+  {
+    "font_class": "heart-filled",
+    "unicode": "\ue641"
+  },
+  {
+    "font_class": "help",
+    "unicode": "\ue679"
+  },
+  {
+    "font_class": "help-filled",
+    "unicode": "\ue674"
+  },
+  {
+    "font_class": "home",
+    "unicode": "\ue662"
+  },
+  {
+    "font_class": "home-filled",
+    "unicode": "\ue663"
+  },
+  {
+    "font_class": "image",
+    "unicode": "\ue670"
+  },
+  {
+    "font_class": "image-filled",
+    "unicode": "\ue678"
+  },
+  {
+    "font_class": "images",
+    "unicode": "\ue650"
+  },
+  {
+    "font_class": "images-filled",
+    "unicode": "\ue64b"
+  },
+  {
+    "font_class": "info",
+    "unicode": "\ue669"
+  },
+  {
+    "font_class": "info-filled",
+    "unicode": "\ue649"
+  },
+  {
+    "font_class": "left",
+    "unicode": "\ue6b7"
+  },
+  {
+    "font_class": "link",
+    "unicode": "\ue6a5"
+  },
+  {
+    "font_class": "list",
+    "unicode": "\ue644"
+  },
+  {
+    "font_class": "location",
+    "unicode": "\ue6ae"
+  },
+  {
+    "font_class": "location-filled",
+    "unicode": "\ue6af"
+  },
+  {
+    "font_class": "locked",
+    "unicode": "\ue66b"
+  },
+  {
+    "font_class": "locked-filled",
+    "unicode": "\ue668"
+  },
+  {
+    "font_class": "loop",
+    "unicode": "\ue633"
+  },
+  {
+    "font_class": "mail-open",
+    "unicode": "\ue643"
+  },
+  {
+    "font_class": "mail-open-filled",
+    "unicode": "\ue63a"
+  },
+  {
+    "font_class": "map",
+    "unicode": "\ue667"
+  },
+  {
+    "font_class": "map-filled",
+    "unicode": "\ue666"
+  },
+  {
+    "font_class": "map-pin",
+    "unicode": "\ue6ad"
+  },
+  {
+    "font_class": "map-pin-ellipse",
+    "unicode": "\ue6ac"
+  },
+  {
+    "font_class": "medal",
+    "unicode": "\ue6a2"
+  },
+  {
+    "font_class": "medal-filled",
+    "unicode": "\ue6c3"
+  },
+  {
+    "font_class": "mic",
+    "unicode": "\ue671"
+  },
+  {
+    "font_class": "mic-filled",
+    "unicode": "\ue677"
+  },
+  {
+    "font_class": "micoff",
+    "unicode": "\ue67e"
+  },
+  {
+    "font_class": "micoff-filled",
+    "unicode": "\ue6b0"
+  },
+  {
+    "font_class": "minus",
+    "unicode": "\ue66f"
+  },
+  {
+    "font_class": "minus-filled",
+    "unicode": "\ue67d"
+  },
+  {
+    "font_class": "more",
+    "unicode": "\ue64d"
+  },
+  {
+    "font_class": "more-filled",
+    "unicode": "\ue64e"
+  },
+  {
+    "font_class": "navigate",
+    "unicode": "\ue66e"
+  },
+  {
+    "font_class": "navigate-filled",
+    "unicode": "\ue67a"
+  },
+  {
+    "font_class": "notification",
+    "unicode": "\ue6a6"
+  },
+  {
+    "font_class": "notification-filled",
+    "unicode": "\ue6c1"
+  },
+  {
+    "font_class": "paperclip",
+    "unicode": "\ue652"
+  },
+  {
+    "font_class": "paperplane",
+    "unicode": "\ue672"
+  },
+  {
+    "font_class": "paperplane-filled",
+    "unicode": "\ue675"
+  },
+  {
+    "font_class": "person",
+    "unicode": "\ue699"
+  },
+  {
+    "font_class": "person-filled",
+    "unicode": "\ue69d"
+  },
+  {
+    "font_class": "personadd",
+    "unicode": "\ue69f"
+  },
+  {
+    "font_class": "personadd-filled",
+    "unicode": "\ue698"
+  },
+  {
+    "font_class": "personadd-filled-copy",
+    "unicode": "\ue6d1"
+  },
+  {
+    "font_class": "phone",
+    "unicode": "\ue69c"
+  },
+  {
+    "font_class": "phone-filled",
+    "unicode": "\ue69b"
+  },
+  {
+    "font_class": "plus",
+    "unicode": "\ue676"
+  },
+  {
+    "font_class": "plus-filled",
+    "unicode": "\ue6c7"
+  },
+  {
+    "font_class": "plusempty",
+    "unicode": "\ue67b"
+  },
+  {
+    "font_class": "pulldown",
+    "unicode": "\ue632"
+  },
+  {
+    "font_class": "pyq",
+    "unicode": "\ue682"
+  },
+  {
+    "font_class": "qq",
+    "unicode": "\ue680"
+  },
+  {
+    "font_class": "redo",
+    "unicode": "\ue64a"
+  },
+  {
+    "font_class": "redo-filled",
+    "unicode": "\ue655"
+  },
+  {
+    "font_class": "refresh",
+    "unicode": "\ue657"
+  },
+  {
+    "font_class": "refresh-filled",
+    "unicode": "\ue656"
+  },
+  {
+    "font_class": "refreshempty",
+    "unicode": "\ue6bf"
+  },
+  {
+    "font_class": "reload",
+    "unicode": "\ue6b2"
+  },
+  {
+    "font_class": "right",
+    "unicode": "\ue6b5"
+  },
+  {
+    "font_class": "scan",
+    "unicode": "\ue62a"
+  },
+  {
+    "font_class": "search",
+    "unicode": "\ue654"
+  },
+  {
+    "font_class": "settings",
+    "unicode": "\ue653"
+  },
+  {
+    "font_class": "settings-filled",
+    "unicode": "\ue6ce"
+  },
+  {
+    "font_class": "shop",
+    "unicode": "\ue62f"
+  },
+  {
+    "font_class": "shop-filled",
+    "unicode": "\ue6cd"
+  },
+  {
+    "font_class": "smallcircle",
+    "unicode": "\ue67c"
+  },
+  {
+    "font_class": "smallcircle-filled",
+    "unicode": "\ue665"
+  },
+  {
+    "font_class": "sound",
+    "unicode": "\ue684"
+  },
+  {
+    "font_class": "sound-filled",
+    "unicode": "\ue686"
+  },
+  {
+    "font_class": "spinner-cycle",
+    "unicode": "\ue68a"
+  },
+  {
+    "font_class": "staff",
+    "unicode": "\ue6a7"
+  },
+  {
+    "font_class": "staff-filled",
+    "unicode": "\ue6cb"
+  },
+  {
+    "font_class": "star",
+    "unicode": "\ue688"
+  },
+  {
+    "font_class": "star-filled",
+    "unicode": "\ue68f"
+  },
+  {
+    "font_class": "starhalf",
+    "unicode": "\ue683"
+  },
+  {
+    "font_class": "trash",
+    "unicode": "\ue687"
+  },
+  {
+    "font_class": "trash-filled",
+    "unicode": "\ue685"
+  },
+  {
+    "font_class": "tune",
+    "unicode": "\ue6aa"
+  },
+  {
+    "font_class": "tune-filled",
+    "unicode": "\ue6ca"
+  },
+  {
+    "font_class": "undo",
+    "unicode": "\ue64f"
+  },
+  {
+    "font_class": "undo-filled",
+    "unicode": "\ue64c"
+  },
+  {
+    "font_class": "up",
+    "unicode": "\ue6b6"
+  },
+	{
+	  "font_class": "top",
+	  "unicode": "\ue6b6"
+	},
+  {
+    "font_class": "upload",
+    "unicode": "\ue690"
+  },
+  {
+    "font_class": "upload-filled",
+    "unicode": "\ue68e"
+  },
+  {
+    "font_class": "videocam",
+    "unicode": "\ue68c"
+  },
+  {
+    "font_class": "videocam-filled",
+    "unicode": "\ue689"
+  },
+  {
+    "font_class": "vip",
+    "unicode": "\ue6a8"
+  },
+  {
+    "font_class": "vip-filled",
+    "unicode": "\ue6c6"
+  },
+  {
+    "font_class": "wallet",
+    "unicode": "\ue6b1"
+  },
+  {
+    "font_class": "wallet-filled",
+    "unicode": "\ue6c2"
+  },
+  {
+    "font_class": "weibo",
+    "unicode": "\ue68b"
+  },
+  {
+    "font_class": "weixin",
+    "unicode": "\ue691"
+  }
+]
+
+// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)

+ 89 - 0
uni_modules/uni-icons/package.json

@@ -0,0 +1,89 @@
+{
+  "id": "uni-icons",
+  "displayName": "uni-icons 图标",
+  "version": "2.0.10",
+  "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "icon",
+    "图标"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.2.14"
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y",
+          "app-uvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+					"钉钉": "y",
+					"快手": "y",
+					"飞书": "y",
+					"京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 8 - 0
uni_modules/uni-icons/readme.md

@@ -0,0 +1,8 @@
+## Icons 图标
+> **组件名:uni-icons**
+> 代码块: `uIcons`
+
+用于展示 icons 图标 。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 25 - 0
uni_modules/uni-load-more/changelog.md

@@ -0,0 +1,25 @@
+## 1.3.6(2024-10-15)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.3.5(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.3.4(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.3.3(2022-01-20)
+- 新增 showText属性 ,是否显示文本
+## 1.3.2(2022-01-19)
+- 修复 nvue 平台下不显示文本的bug
+## 1.3.1(2022-01-19)
+- 修复 微信小程序平台样式选择器报警告的问题
+## 1.3.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more)
+## 1.2.1(2021-08-24)
+- 新增 支持国际化
+## 1.2.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.8(2021-05-12)
+- 新增 组件示例地址
+## 1.1.7(2021-03-30)
+- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug
+## 1.1.6(2021-02-05)
+- 调整为uni_modules目录规范

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/en.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "Pull up to show more",
+	"uni-load-more.contentrefresh": "loading...",
+	"uni-load-more.contentnomore": "No more data"
+}

+ 8 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "上拉显示更多",
+	"uni-load-more.contentrefresh": "正在加载...",
+	"uni-load-more.contentnomore": "没有更多数据了"
+}

+ 5 - 0
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json

@@ -0,0 +1,5 @@
+{
+	"uni-load-more.contentdown": "上拉顯示更多",
+	"uni-load-more.contentrefresh": "正在加載...",
+	"uni-load-more.contentnomore": "沒有更多數據了"
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 404 - 0
uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue


+ 84 - 0
uni_modules/uni-load-more/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "uni-load-more",
+  "displayName": "uni-load-more 加载更多",
+  "version": "1.3.6",
+  "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "加载更多",
+    "load-more"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 14 - 0
uni_modules/uni-load-more/readme.md

@@ -0,0 +1,14 @@
+
+
+### LoadMore 加载更多
+> **组件名:uni-load-more**
+> 代码块: `uLoadMore`
+
+
+用于列表中,做滚动加载使用,展示 loading 的各种状态。
+
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+

+ 92 - 0
uni_modules/uni-popup/changelog.md

@@ -0,0 +1,92 @@
+## 1.9.5(2024-10-15)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.9.4(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.9.3(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.9.2(2024-09-21)
+- 修复 uni-popup在android上的重复点击弹出位置不正确的bug
+## 1.9.1(2024-04-02)
+- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
+## 1.9.0(2024-03-28)
+- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
+## 1.8.9(2024-03-20)
+- 修复 uni-popup-dialog 数据输入时修正为双向绑定
+## 1.8.8(2024-02-20)
+- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
+## 1.8.7(2024-02-02)
+- 新增 uni-popup-dialog 新增属性focus:input模式下,是否自动自动聚焦
+## 1.8.6(2024-01-30)
+- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
+## 1.8.5(2024-01-26)
+- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
+## 1.8.4(2023-11-15)
+- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
+## 1.8.3(2023-04-17)
+- 修复 uni-popup 重复打开时的 bug
+## 1.8.2(2023-02-02)
+- uni-popup-dialog 组件新增 inputType 属性
+## 1.8.1(2022-12-01)
+- 修复 nvue 下 v-show 报错
+## 1.8.0(2022-11-29)
+- 优化 主题样式
+## 1.7.9(2022-04-02)
+- 修复 弹出层内部无法滚动的bug
+## 1.7.8(2022-03-28)
+- 修复 小程序中高度错误的bug
+## 1.7.7(2022-03-17)
+- 修复 快速调用open出现问题的Bug
+## 1.7.6(2022-02-14)
+- 修复 safeArea 属性不能设置为false的bug
+## 1.7.5(2022-01-19)
+- 修复 isMaskClick 失效的bug
+## 1.7.4(2022-01-19)
+- 新增 cancelText \ confirmText 属性 ,可自定义文本
+- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
+- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
+## 1.7.3(2022-01-13)
+- 修复 设置 safeArea 属性不生效的bug
+## 1.7.2(2021-11-26)
+- 优化 组件示例
+## 1.7.1(2021-11-26)
+- 修复 vuedoc 文字错误
+## 1.7.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
+## 1.6.2(2021-08-24)
+- 新增 支持国际化
+## 1.6.1(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.6.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.5.0(2021-06-23)
+- 新增 mask-click 遮罩层点击事件
+## 1.4.5(2021-06-22)
+- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.4(2021-06-18)
+- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
+## 1.4.3(2021-06-08)
+- 修复 错误的 watch 字段
+- 修复 safeArea 属性不生效的问题
+- 修复 点击内容,再点击遮罩无法关闭的Bug
+## 1.4.2(2021-05-12)
+- 新增 组件示例地址
+## 1.4.1(2021-04-29)
+- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
+## 1.4.0 (2021-04-29)
+- 新增 type 属性的 left\right 值,支持左右弹出
+- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
+- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
+- 新增 safeArea 属性,是否适配底部安全区
+- 修复 App\h5\微信小程序底部安全区占位不对的Bug
+- 修复 App 端弹出等待的Bug
+- 优化 提升低配设备性能,优化动画卡顿问题
+- 优化 更简单的组件自定义方式
+## 1.2.9(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+## 1.2.8(2021-02-05)
+- 调整为uni_modules目录规范
+## 1.2.7(2021-02-05)
+- 调整为uni_modules目录规范
+- 新增 支持 PC 端
+- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

+ 45 - 0
uni_modules/uni-popup/components/uni-popup-dialog/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    this.$once('hook:beforeDestroy', () => {
+      document.removeEventListener('keyup', listener)
+    })
+  },
+	render: () => {}
+}
+// #endif

+ 316 - 0
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue

@@ -0,0 +1,316 @@
+<template>
+	<view class="uni-popup-dialog">
+		<view class="uni-dialog-title">
+			<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
+		</view>
+		<view v-if="mode === 'base'" class="uni-dialog-content">
+			<slot>
+				<text class="uni-dialog-content-text">{{content}}</text>
+			</slot>
+		</view>
+		<view v-else class="uni-dialog-content">
+			<slot>
+				<input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
+					:placeholder="placeholderText" :focus="focus">
+			</slot>
+		</view>
+		<view class="uni-dialog-button-group">
+			<view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
+				<text class="uni-dialog-button-text">{{closeText}}</text>
+			</view>
+			<view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from '../uni-popup/i18n/index.js'
+	const {
+		t
+	} = initVueI18n(messages)
+	/**
+	 * PopUp 弹出层-对话框样式
+	 * @description 弹出层-对话框样式
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} value input 模式下的默认值
+	 * @property {String} placeholder input 模式下输入提示
+	 * @property {Boolean} focus input模式下是否自动聚焦,默认为true
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} mode = [base|input] 模式、
+	 * 	@value base 基础对话框
+	 * 	@value input 可输入对话框
+	 * @showClose {Boolean} 是否显示关闭按钮
+	 * @property {String} content 对话框内容
+	 * @property {Boolean} beforeClose 是否拦截取消事件
+	 * @property {Number} maxlength 输入
+	 * @event {Function} confirm 点击确认按钮触发
+	 * @event {Function} close 点击取消按钮触发
+	 */
+
+	export default {
+		name: "uniPopupDialog",
+		mixins: [popup],
+		emits: ['confirm', 'close', 'update:modelValue', 'input'],
+		props: {
+			inputType: {
+				type: String,
+				default: 'text'
+			},
+			showClose: {
+				type: Boolean,
+				default: true
+			},
+			// #ifdef VUE2
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				type: [Number, String],
+				default: ''
+			},
+			// #endif
+
+
+			placeholder: {
+				type: [String, Number],
+				default: ''
+			},
+			type: {
+				type: String,
+				default: 'error'
+			},
+			mode: {
+				type: String,
+				default: 'base'
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			content: {
+				type: String,
+				default: ''
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			},
+			cancelText: {
+				type: String,
+				default: ''
+			},
+			confirmText: {
+				type: String,
+				default: ''
+			},
+			maxlength: {
+				type: Number,
+				default: -1,
+			},
+			focus: {
+				type: Boolean,
+				default: true,
+			}
+		},
+		data() {
+			return {
+				dialogType: 'error',
+				val: ""
+			}
+		},
+		computed: {
+			okText() {
+				return this.confirmText || t("uni-popup.ok")
+			},
+			closeText() {
+				return this.cancelText || t("uni-popup.cancel")
+			},
+			placeholderText() {
+				return this.placeholder || t("uni-popup.placeholder")
+			},
+			titleText() {
+				return this.title || t("uni-popup.title")
+			}
+		},
+		watch: {
+			type(val) {
+				this.dialogType = val
+			},
+			mode(val) {
+				if (val === 'input') {
+					this.dialogType = 'info'
+				}
+			},
+			value(val) {
+				if (this.maxlength != -1 && this.mode === 'input') {
+					this.val = val.slice(0, this.maxlength);
+				} else {
+					this.val = val
+				}
+			},
+			val(val) {
+				// #ifdef VUE2
+				// TODO 兼容 vue2
+				this.$emit('input', val);
+				// #endif
+				// #ifdef VUE3
+				// TODO 兼容 vue3
+				this.$emit('update:modelValue', val);
+				// #endif
+			}
+		},
+		created() {
+			// 对话框遮罩不可点击
+			this.popup.disableMask()
+			// this.popup.closeMask()
+			if (this.mode === 'input') {
+				this.dialogType = 'info'
+				this.val = this.value;
+				// #ifdef VUE3
+				this.val = this.modelValue;
+				// #endif
+			} else {
+				this.dialogType = this.type
+			}
+		},
+		methods: {
+			/**
+			 * 点击确认按钮
+			 */
+			onOk() {
+				if (this.mode === 'input') {
+					this.$emit('confirm', this.val)
+				} else {
+					this.$emit('confirm')
+				}
+				if (this.beforeClose) return
+				this.popup.close()
+			},
+			/**
+			 * 点击取消按钮
+			 */
+			closeDialog() {
+				this.$emit('close')
+				if (this.beforeClose) return
+				this.popup.close()
+			},
+			close() {
+				this.popup.close()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.uni-popup-dialog {
+		width: 300px;
+		border-radius: 11px;
+		background-color: #fff;
+	}
+
+	.uni-dialog-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 25px;
+	}
+
+	.uni-dialog-title-text {
+		font-size: 16px;
+		font-weight: 500;
+	}
+
+	.uni-dialog-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 20px;
+	}
+
+	.uni-dialog-content-text {
+		font-size: 14px;
+		color: #6C6C6C;
+	}
+
+	.uni-dialog-button-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-top-color: #f5f5f5;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-dialog-button {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+
+		flex: 1;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+	}
+
+	.uni-border-left {
+		border-left-color: #f0f0f0;
+		border-left-style: solid;
+		border-left-width: 1px;
+	}
+
+	.uni-dialog-button-text {
+		font-size: 16px;
+		color: #333;
+	}
+
+	.uni-button-color {
+		color: #007aff;
+	}
+
+	.uni-dialog-input {
+		flex: 1;
+		font-size: 14px;
+		border: 1px #eee solid;
+		height: 40px;
+		padding: 0 10px;
+		border-radius: 5px;
+		color: #555;
+	}
+
+	.uni-popup__success {
+		color: #4cd964;
+	}
+
+	.uni-popup__warn {
+		color: #f0ad4e;
+	}
+
+	.uni-popup__error {
+		color: #dd524d;
+	}
+
+	.uni-popup__info {
+		color: #909399;
+	}
+</style>

+ 143 - 0
uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue

@@ -0,0 +1,143 @@
+<template>
+	<view class="uni-popup-message">
+		<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
+			<slot>
+				<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	/**
+	 * PopUp 弹出层-消息提示
+	 * @description 弹出层-消息提示
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} message 消息提示文字
+	 * @property {String} duration 显示时间,设置为 0 则不会自动关闭
+	 */
+
+	export default {
+		name: 'uniPopupMessage',
+		mixins:[popup],
+		props: {
+			/**
+			 * 主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'success'
+			},
+			/**
+			 * 消息文字
+			 */
+			message: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 显示时间,设置为 0 则不会自动关闭
+			 */
+			duration: {
+				type: Number,
+				default: 3000
+			},
+			maskShow:{
+				type:Boolean,
+				default:false
+			}
+		},
+		data() {
+			return {}
+		},
+		created() {
+			this.popup.maskShow = this.maskShow
+			this.popup.messageChild = this
+		},
+		methods: {
+			timerClose(){
+				if(this.duration === 0) return
+				clearTimeout(this.timer) 
+				this.timer = setTimeout(()=>{
+					this.popup.close()
+				},this.duration)
+			}
+		}
+	}
+</script>
+<style lang="scss" >
+	.uni-popup-message {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+	}
+
+	.uni-popup-message__box {
+		background-color: #e1f3d8;
+		padding: 10px 15px;
+		border-color: #eee;
+		border-style: solid;
+		border-width: 1px;
+		flex: 1;
+	}
+
+	@media screen and (min-width: 500px) {
+		.fixforpc-width {
+			margin-top: 20px;
+			border-radius: 4px;
+			flex: none;
+			min-width: 380px;
+			/* #ifndef APP-NVUE */
+			max-width: 50%;
+			/* #endif */
+			/* #ifdef APP-NVUE */
+			max-width: 500px;
+			/* #endif */
+		}
+	}
+
+	.uni-popup-message-text {
+		font-size: 14px;
+		padding: 0;
+	}
+
+	.uni-popup__success {
+		background-color: #e1f3d8;
+	}
+
+	.uni-popup__success-text {
+		color: #67C23A;
+	}
+
+	.uni-popup__warn {
+		background-color: #faecd8;
+	}
+
+	.uni-popup__warn-text {
+		color: #E6A23C;
+	}
+
+	.uni-popup__error {
+		background-color: #fde2e2;
+	}
+
+	.uni-popup__error-text {
+		color: #F56C6C;
+	}
+
+	.uni-popup__info {
+		background-color: #F2F6FC;
+	}
+
+	.uni-popup__info-text {
+		color: #909399;
+	}
+</style>

+ 187 - 0
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue

@@ -0,0 +1,187 @@
+<template>
+	<view class="uni-popup-share">
+		<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
+		<view class="uni-share-content">
+			<view class="uni-share-content-box">
+				<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
+					<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
+					<text class="uni-share-text">{{item.text}}</text>
+				</view>
+
+			</view>
+		</view>
+		<view class="uni-share-button-box">
+			<button class="uni-share-button" @click="close">{{cancelText}}</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import popup from '../uni-popup/popup.js'
+	import {
+	initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from '../uni-popup/i18n/index.js'
+	const {	t	} = initVueI18n(messages)
+	export default {
+		name: 'UniPopupShare',
+		mixins:[popup],
+		emits:['select'],
+		props: {
+			title: {
+				type: String,
+				default: ''
+			},
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				bottomData: [{
+						text: '微信',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
+						name: 'wx'
+					},
+					{
+						text: '支付宝',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
+						name: 'ali'
+					},
+					{
+						text: 'QQ',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
+						name: 'qq'
+					},
+					{
+						text: '新浪',
+						icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
+						name: 'sina'
+					},
+					// {
+					// 	text: '百度',
+					// 	icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
+					// 	name: 'copy'
+					// },
+					// {
+					// 	text: '其他',
+					// 	icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
+					// 	name: 'more'
+					// }
+				]
+			}
+		},
+		created() {},
+		computed: {
+			cancelText() {
+				return t("uni-popup.cancel")
+			},
+		shareTitleText() {
+				return this.title || t("uni-popup.shareTitle")
+			}
+		},
+		methods: {
+			/**
+			 * 选择内容
+			 */
+			select(item, index) {
+				this.$emit('select', {
+					item,
+					index
+				})
+				this.close()
+
+			},
+			/**
+			 * 关闭窗口
+			 */
+			close() {
+				if(this.beforeClose) return
+				this.popup.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" >
+	.uni-popup-share {
+		background-color: #fff;
+		border-top-left-radius: 11px;
+		border-top-right-radius: 11px;
+	}
+	.uni-share-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+	}
+	.uni-share-title-text {
+		font-size: 14px;
+		color: #666;
+	}
+	.uni-share-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 10px;
+	}
+
+	.uni-share-content-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: wrap;
+		width: 360px;
+	}
+
+	.uni-share-content-item {
+		width: 90px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		padding: 10px 0;
+		align-items: center;
+	}
+
+	.uni-share-content-item:active {
+		background-color: #f5f5f5;
+	}
+
+	.uni-share-image {
+		width: 30px;
+		height: 30px;
+	}
+
+	.uni-share-text {
+		margin-top: 10px;
+		font-size: 14px;
+		color: #3B4144;
+	}
+
+	.uni-share-button-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: 10px 15px;
+	}
+
+	.uni-share-button {
+		flex: 1;
+		border-radius: 50px;
+		color: #666;
+		font-size: 16px;
+	}
+
+	.uni-share-button::after {
+		border-radius: 50px;
+	}
+</style>

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/en.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "cancel",
+	"uni-popup.ok": "ok",
+	"uni-popup.placeholder": "pleace enter",
+	"uni-popup.title": "Hint",
+	"uni-popup.shareTitle": "Share to"
+}

+ 8 - 0
uni_modules/uni-popup/components/uni-popup/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "确定",
+	"uni-popup.placeholder": "请输入",
+		"uni-popup.title": "提示",
+		"uni-popup.shareTitle": "分享到"
+}

+ 7 - 0
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json

@@ -0,0 +1,7 @@
+{
+	"uni-popup.cancel": "取消",
+	"uni-popup.ok": "確定",
+	"uni-popup.placeholder": "請輸入",
+	"uni-popup.title": "提示",
+	"uni-popup.shareTitle": "分享到"
+}

+ 45 - 0
uni_modules/uni-popup/components/uni-popup/keypress.js

@@ -0,0 +1,45 @@
+// #ifdef H5
+export default {
+  name: 'Keypress',
+  props: {
+    disable: {
+      type: Boolean,
+      default: false
+    }
+  },
+  mounted () {
+    const keyNames = {
+      esc: ['Esc', 'Escape'],
+      tab: 'Tab',
+      enter: 'Enter',
+      space: [' ', 'Spacebar'],
+      up: ['Up', 'ArrowUp'],
+      left: ['Left', 'ArrowLeft'],
+      right: ['Right', 'ArrowRight'],
+      down: ['Down', 'ArrowDown'],
+      delete: ['Backspace', 'Delete', 'Del']
+    }
+    const listener = ($event) => {
+      if (this.disable) {
+        return
+      }
+      const keyName = Object.keys(keyNames).find(key => {
+        const keyName = $event.key
+        const value = keyNames[key]
+        return value === keyName || (Array.isArray(value) && value.includes(keyName))
+      })
+      if (keyName) {
+        // 避免和其他按键事件冲突
+        setTimeout(() => {
+          this.$emit(keyName, {})
+        }, 0)
+      }
+    }
+    document.addEventListener('keyup', listener)
+    // this.$once('hook:beforeDestroy', () => {
+    //   document.removeEventListener('keyup', listener)
+    // })
+  },
+	render: () => {}
+}
+// #endif

+ 26 - 0
uni_modules/uni-popup/components/uni-popup/popup.js

@@ -0,0 +1,26 @@
+
+export default {
+	data() {
+		return {
+			
+		}
+	},
+	created(){
+		this.popup = this.getParent()
+	},
+	methods:{
+		/**
+		 * 获取父元素实例
+		 */
+		getParent(name = 'uniPopup') {
+			let parent = this.$parent;
+			let parentName = parent.$options.name;
+			while (parentName !== name) {
+				parent = parent.$parent;
+				if (!parent) return false
+				parentName = parent.$options.name;
+			}
+			return parent;
+		},
+	}
+}

+ 90 - 0
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue

@@ -0,0 +1,90 @@
+<template>
+  <view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
+    <view @click.stop>
+      <slot></slot>
+    </view>
+  </view>
+</template>
+
+<script>
+  type CloseCallBack = ()=> void;
+  let closeCallBack:CloseCallBack = () :void => {};
+  export default {
+    emits:["close","clickMask"],
+    data() {
+      return {
+        isShow:false,
+        isOpen:false
+      }
+    },
+    props: {
+      maskClick: {
+        type: Boolean,
+        default: true
+      },
+    },
+    watch: {
+      // 设置show = true 时,如果没有 open 需要设置为 open
+      isShow:{
+        handler(isShow) {
+          // console.log("isShow",isShow)
+          if(isShow && this.isOpen == false){
+            this.isOpen = true
+          }
+        },
+        immediate:true
+      },
+      // 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
+      isOpen:{
+        handler(isOpen) {
+          // console.log("isOpen",isOpen)
+          if(isOpen && this.isShow == false){
+            this.isShow = true
+          }
+        },
+        immediate:true
+      }
+    },
+    methods:{
+      open(){
+        // ...funs : CloseCallBack[]
+        // if(funs.length > 0){
+        //   closeCallBack = funs[0]
+        // }
+        this.isOpen = true;
+      },
+      clickMask(){
+        if(this.maskClick == true){
+          this.$emit('clickMask')
+          this.close()
+        }
+      },
+      close(): void{
+        this.isOpen = false;
+        this.$emit('close')
+        closeCallBack()
+      },
+      hiden(){
+        this.isShow = false
+      },
+      show(){
+        this.isShow = true
+      }
+    }
+  }
+</script>
+
+<style>
+.popup-root {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 750rpx;
+  height: 100%;
+  flex: 1;
+  background-color: rgba(0, 0, 0, 0.3);
+  justify-content: center;
+  align-items: center;
+  z-index: 99;
+}
+</style>

+ 518 - 0
uni_modules/uni-popup/components/uni-popup/uni-popup.vue

@@ -0,0 +1,518 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
+		<view @touchstart="touchstart">
+			<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
+				:duration="duration" :show="showTrans" @click="onTap" />
+			<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
+				:show="showTrans" @click="onTap">
+				<view class="uni-popup__wrapper" :style="getStyles" :class="[popupstyle]" @click="clear">
+					<slot />
+				</view>
+			</uni-transition>
+		</view>
+		<!-- #ifdef H5 -->
+		<keypress v-if="maskShow" @esc="onTap" />
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	// #ifdef H5
+	import keypress from './keypress.js'
+	// #endif
+
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * 	@value left		左侧弹出
+	 * 	@value right  右侧弹出
+	 * 	@value message 消息提示
+	 * 	@value dialog 对话框
+	 * 	@value share 底部分享示例
+	 * @property {Boolean} animation = [true|false] 是否开启动画
+	 * @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
+	 * @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
+	 * @property {String}  backgroundColor 主窗口背景色
+	 * @property {String}  maskBackgroundColor 蒙版颜色
+	 * @property {String}  borderRadius 设置圆角(左上、右上、右下和左下) 示例:"10px 10px 10px 10px"
+	 * @property {Boolean} safeArea		   是否适配底部安全区
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 * @event {Function} maskClick 点击遮罩触发
+	 */
+
+	export default {
+		name: 'uniPopup',
+		components: {
+			// #ifdef H5
+			keypress
+			// #endif
+		},
+		emits: ['change', 'maskClick'],
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			// message: 消息提示 ; dialog : 对话框
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			isMaskClick: {
+				type: Boolean,
+				default: null
+			},
+			// TODO 2 个版本后废弃属性 ,使用 isMaskClick
+			maskClick: {
+				type: Boolean,
+				default: null
+			},
+			backgroundColor: {
+				type: String,
+				default: 'none'
+			},
+			safeArea: {
+				type: Boolean,
+				default: true
+			},
+			maskBackgroundColor: {
+				type: String,
+				default: 'rgba(0, 0, 0, 0.4)'
+			},
+			borderRadius:{
+				type: String,
+			}
+		},
+
+		watch: {
+			/**
+			 * 监听type类型
+			 */
+			type: {
+				handler: function(type) {
+					if (!this.config[type]) return
+					this[this.config[type]](true)
+				},
+				immediate: true
+			},
+			isDesktop: {
+				handler: function(newVal) {
+					if (!this.config[newVal]) return
+					this[this.config[this.type]](true)
+				},
+				immediate: true
+			},
+			/**
+			 * 监听遮罩是否可点击
+			 * @param {Object} val
+			 */
+			maskClick: {
+				handler: function(val) {
+					this.mkclick = val
+				},
+				immediate: true
+			},
+			isMaskClick: {
+				handler: function(val) {
+					this.mkclick = val
+				},
+				immediate: true
+			},
+			// H5 下禁止底部滚动
+			showPopup(show) {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
+				// #endif
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				popupWidth: 0,
+				popupHeight: 0,
+				config: {
+					top: 'top',
+					bottom: 'bottom',
+					center: 'center',
+					left: 'left',
+					right: 'right',
+					message: 'top',
+					dialog: 'center',
+					share: 'bottom'
+				},
+				maskClass: {
+					position: 'fixed',
+					bottom: 0,
+					top: 0,
+					left: 0,
+					right: 0,
+					backgroundColor: 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					backgroundColor: 'transparent',
+					borderRadius: this.borderRadius || "0",
+					position: 'fixed',
+					left: 0,
+					right: 0
+				},
+				maskShow: true,
+				mkclick: true,
+				popupstyle: 'top'
+			}
+		},
+		computed: {
+			getStyles() {
+				let res = { backgroundColor: this.bg };
+				if (this.borderRadius || "0") {
+					res = Object.assign(res, { borderRadius: this.borderRadius })
+				}
+				return res;
+			},
+			isDesktop() {
+				return this.popupWidth >= 500 && this.popupHeight >= 500
+			},
+			bg() {
+				if (this.backgroundColor === '' || this.backgroundColor === 'none') {
+					return 'transparent'
+				}
+				return this.backgroundColor
+			}
+		},
+		mounted() {
+			const fixSize = () => {
+				// #ifdef MP-WEIXIN
+				const {
+					windowWidth,
+					windowHeight,
+					windowTop,
+					safeArea,
+					screenHeight,
+					safeAreaInsets
+				} = uni.getWindowInfo()
+				// #endif
+				// #ifndef MP-WEIXIN
+				const {
+					windowWidth,
+					windowHeight,
+					windowTop,
+					safeArea,
+					screenHeight,
+					safeAreaInsets
+				} = uni.getSystemInfoSync()
+				// #endif
+				this.popupWidth = windowWidth
+				this.popupHeight = windowHeight + (windowTop || 0)
+				// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
+				if (safeArea && this.safeArea) {
+					// #ifdef MP-WEIXIN
+					this.safeAreaInsets = screenHeight - safeArea.bottom
+					// #endif
+					// #ifndef MP-WEIXIN
+					this.safeAreaInsets = safeAreaInsets.bottom
+					// #endif
+				} else {
+					this.safeAreaInsets = 0
+				}
+			}
+			fixSize()
+			// #ifdef H5
+			// window.addEventListener('resize', fixSize)
+			// this.$once('hook:beforeDestroy', () => {
+			// 	window.removeEventListener('resize', fixSize)
+			// })
+			// #endif
+		},
+		// #ifndef VUE3
+		// TODO vue2
+		destroyed() {
+			this.setH5Visible()
+		},
+		// #endif
+		// #ifdef VUE3
+		// TODO vue3
+		unmounted() {
+			this.setH5Visible()
+		},
+		// #endif
+		activated() {
+   	  this.setH5Visible(!this.showPopup);
+    },
+    deactivated() {
+      this.setH5Visible(true);
+    },
+		created() {
+			// this.mkclick =  this.isMaskClick || this.maskClick
+			if (this.isMaskClick === null && this.maskClick === null) {
+				this.mkclick = true
+			} else {
+				this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
+			}
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+			// TODO 处理 message 组件生命周期异常的问题
+			this.messageChild = null
+			// TODO 解决头条冒泡的问题
+			this.clearPropagation = false
+			this.maskClass.backgroundColor = this.maskBackgroundColor
+		},
+		methods: {
+			setH5Visible(visible = true) {
+				// #ifdef H5
+				// fix by mehaotian 处理 h5 滚动穿透的问题
+				document.getElementsByTagName('body')[0].style.overflow =  visible ? "visible" : "hidden";
+				// #endif
+			},
+			/**
+			 * 公用方法,不显示遮罩层
+			 */
+			closeMask() {
+				this.maskShow = false
+			},
+			/**
+			 * 公用方法,遮罩层禁止点击
+			 */
+			disableMask() {
+				this.mkclick = false
+			},
+			// TODO nvue 取消冒泡
+			clear(e) {
+				// #ifndef APP-NVUE
+				e.stopPropagation()
+				// #endif
+				this.clearPropagation = true
+			},
+
+			open(direction) {
+				// fix by mehaotian 处理快速打开关闭的情况
+				if (this.showPopup) {
+					return
+				}
+				let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
+				if (!(direction && innerType.indexOf(direction) !== -1)) {
+					direction = this.type
+				}
+				if (!this.config[direction]) {
+					console.error('缺少类型:', direction)
+					return
+				}
+				this[this.config[direction]]()
+				this.$emit('change', {
+					show: true,
+					type: direction
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$emit('change', {
+					show: false,
+					type: this.type
+				})
+				clearTimeout(this.timer)
+				// // 自定义关闭事件
+				// this.customOpen && this.customClose()
+				this.timer = setTimeout(() => {
+					this.showPopup = false
+				}, 300)
+			},
+			// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
+			touchstart() {
+				this.clearPropagation = false
+			},
+
+			onTap() {
+				if (this.clearPropagation) {
+					// fix by mehaotian 兼容 nvue
+					this.clearPropagation = false
+					return
+				}
+				this.$emit('maskClick')
+				if (!this.mkclick) return
+				this.close()
+			},
+			/**
+			 * 顶部弹出样式处理
+			 */
+			top(type) {
+				this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
+				this.ani = ['slide-top']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					right: 0,
+					backgroundColor: this.bg,
+					borderRadius:this.borderRadius || "0"
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPopup = true
+				this.showTrans = true
+				this.$nextTick(() => {
+					this.showPoptrans()
+					if (this.messageChild && this.type === 'message') {
+						this.messageChild.timerClose()
+					}
+				})
+			},
+			/**
+			 * 底部弹出样式处理
+			 */
+			bottom(type) {
+				this.popupstyle = 'bottom'
+				this.ani = ['slide-bottom']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					right: 0,
+					bottom: 0,
+					paddingBottom: this.safeAreaInsets + 'px',
+					backgroundColor: this.bg,
+					borderRadius:this.borderRadius || "0",
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPoptrans()
+			},
+			/**
+			 * 中间弹出样式处理
+			 */
+			center(type) {
+				this.popupstyle = 'center'
+				//微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
+				// #ifdef MP-WEIXIN
+					this.ani = ['fade']
+				// #endif
+				// #ifndef MP-WEIXIN
+					this.ani = ['zoom-out', 'fade']
+				// #endif
+				this.transClass = {
+					position: 'fixed',
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column',
+					/* #endif */
+					bottom: 0,
+					left: 0,
+					right: 0,
+					top: 0,
+					justifyContent: 'center',
+					alignItems: 'center',
+					borderRadius:this.borderRadius || "0"
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPoptrans()
+			},
+			left(type) {
+				this.popupstyle = 'left'
+				this.ani = ['slide-left']
+				this.transClass = {
+					position: 'fixed',
+					left: 0,
+					bottom: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					borderRadius:this.borderRadius || "0",
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPoptrans()
+			},
+			right(type) {
+				this.popupstyle = 'right'
+				this.ani = ['slide-right']
+				this.transClass = {
+					position: 'fixed',
+					bottom: 0,
+					right: 0,
+					top: 0,
+					backgroundColor: this.bg,
+					borderRadius:this.borderRadius || "0",
+					/* #ifndef APP-NVUE */
+					display: 'flex',
+					flexDirection: 'column'
+					/* #endif */
+				}
+				// TODO 兼容 type 属性 ,后续会废弃
+				if (type) return
+				this.showPoptrans()
+			},
+			showPoptrans(){
+				this.$nextTick(()=>{
+					this.showPopup = true
+					this.showTrans = true
+				})
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.uni-popup {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+
+		/* #endif */
+		&.top,
+		&.left,
+		&.right {
+			/* #ifdef H5 */
+			top: var(--window-top);
+			/* #endif */
+			/* #ifndef H5 */
+			top: 0;
+			/* #endif */
+		}
+
+		.uni-popup__wrapper {
+			/* #ifndef APP-NVUE */
+			display: block;
+			/* #endif */
+			position: relative;
+
+			/* iphonex 等安全区设置,底部安全区适配 */
+			/* #ifndef APP-NVUE */
+			// padding-bottom: constant(safe-area-inset-bottom);
+			// padding-bottom: env(safe-area-inset-bottom);
+			/* #endif */
+			&.left,
+			&.right {
+				/* #ifdef H5 */
+				padding-top: var(--window-top);
+				/* #endif */
+				/* #ifndef H5 */
+				padding-top: 0;
+				/* #endif */
+				flex: 1;
+			}
+		}
+	}
+
+	.fixforpc-z-index {
+		/* #ifndef APP-NVUE */
+		z-index: 999;
+		/* #endif */
+	}
+
+	.fixforpc-top {
+		top: 0;
+	}
+</style>

+ 88 - 0
uni_modules/uni-popup/package.json

@@ -0,0 +1,88 @@
+{
+	"id": "uni-popup",
+	"displayName": "uni-popup 弹出层",
+	"version": "1.9.5",
+	"description": " Popup 组件,提供常用的弹层",
+	"keywords": [
+        "uni-ui",
+        "弹出层",
+        "弹窗",
+        "popup",
+        "弹框"
+    ],
+	"repository": "https://github.com/dcloudio/uni-ui",
+	"engines": {
+		"HBuilderX": ""
+	},
+	"directories": {
+		"example": "../../temps/example_temps"
+	},
+    "dcloudext": {
+        "sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": ""
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "无",
+			"permissions": "无"
+		},
+        "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+        "type": "component-vue"
+	},
+	"uni_modules": {
+		"dependencies": [
+			"uni-scss",
+			"uni-transition"
+		],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+                "aliyun": "y",
+                "alipay": "n"
+			},
+			"client": {
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+                },
+                "Vue": {
+                    "vue2": "y",
+                    "vue3": "y"
+                }
+			}
+		}
+	}
+}

+ 17 - 0
uni_modules/uni-popup/readme.md

@@ -0,0 +1,17 @@
+
+
+## Popup 弹出层
+> **组件名:uni-popup**
+> 代码块: `uPopup`
+> 关联组件:`uni-transition`
+
+
+弹出层组件,在应用中弹出一个消息提示窗口、提示框等
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+
+
+
+

+ 8 - 0
uni_modules/uni-scss/changelog.md

@@ -0,0 +1,8 @@
+## 1.0.3(2022-01-21)
+- 优化 组件示例
+## 1.0.2(2021-11-22)
+- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
+## 1.0.1(2021-11-22)
+- 修复 vue3中scss语法兼容问题
+## 1.0.0(2021-11-18)
+- init

+ 1 - 0
uni_modules/uni-scss/index.scss

@@ -0,0 +1 @@
+@import './styles/index.scss';

+ 82 - 0
uni_modules/uni-scss/package.json

@@ -0,0 +1,82 @@
+{
+  "id": "uni-scss",
+  "displayName": "uni-scss 辅助样式",
+  "version": "1.0.3",
+  "description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。",
+  "keywords": [
+    "uni-scss",
+    "uni-ui",
+    "辅助样式"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "category": [
+        "JS SDK",
+        "通用 SDK"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "u"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "n",
+          "联盟": "n"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 4 - 0
uni_modules/uni-scss/readme.md

@@ -0,0 +1,4 @@
+`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 7 - 0
uni_modules/uni-scss/styles/index.scss

@@ -0,0 +1,7 @@
+@import './setting/_variables.scss';
+@import './setting/_border.scss';
+@import './setting/_color.scss';
+@import './setting/_space.scss';
+@import './setting/_radius.scss';
+@import './setting/_text.scss';
+@import './setting/_styles.scss';

+ 3 - 0
uni_modules/uni-scss/styles/setting/_border.scss

@@ -0,0 +1,3 @@
+.uni-border {
+	border: 1px $uni-border-1 solid;
+}

+ 66 - 0
uni_modules/uni-scss/styles/setting/_color.scss

@@ -0,0 +1,66 @@
+
+// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐
+// @mixin get-styles($k,$c) {
+// 	@if $k == size or $k == weight{
+// 		font-#{$k}:#{$c}
+// 	}@else{
+// 		#{$k}:#{$c}
+// 	}
+// }
+$uni-ui-color:(
+	// 主色
+	primary: $uni-primary,
+	primary-disable: $uni-primary-disable,
+	primary-light: $uni-primary-light,
+	// 辅助色
+	success: $uni-success,
+	success-disable: $uni-success-disable,
+	success-light: $uni-success-light,
+	warning: $uni-warning,
+	warning-disable: $uni-warning-disable,
+	warning-light: $uni-warning-light,
+	error: $uni-error,
+	error-disable: $uni-error-disable,
+	error-light: $uni-error-light,
+	info: $uni-info,
+	info-disable: $uni-info-disable,
+	info-light: $uni-info-light,
+	// 中性色
+	main-color: $uni-main-color,
+	base-color: $uni-base-color,
+	secondary-color: $uni-secondary-color,
+	extra-color: $uni-extra-color,
+	// 背景色
+	bg-color: $uni-bg-color,
+	// 边框颜色
+	border-1: $uni-border-1,
+	border-2: $uni-border-2,
+	border-3: $uni-border-3,
+	border-4: $uni-border-4,
+	// 黑色
+	black:$uni-black,
+	// 白色
+	white:$uni-white,
+	// 透明
+	transparent:$uni-transparent
+) !default;
+@each $key, $child in $uni-ui-color {
+	.uni-#{"" + $key} {
+		color: $child;
+	}
+	.uni-#{"" + $key}-bg {
+		background-color: $child;
+	}
+}
+.uni-shadow-sm {
+	box-shadow: $uni-shadow-sm;
+}
+.uni-shadow-base {
+	box-shadow: $uni-shadow-base;
+}
+.uni-shadow-lg {
+	box-shadow: $uni-shadow-lg;
+}
+.uni-mask {
+	background-color:$uni-mask;
+}

+ 55 - 0
uni_modules/uni-scss/styles/setting/_radius.scss

@@ -0,0 +1,55 @@
+@mixin radius($r,$d:null ,$important: false){
+  $radius-value:map-get($uni-radius, $r) if($important, !important, null);
+  // Key exists within the $uni-radius variable
+  @if (map-has-key($uni-radius, $r) and  $d){
+		@if $d == t {
+				border-top-left-radius:$radius-value;
+				border-top-right-radius:$radius-value;
+		}@else if $d == r {
+				border-top-right-radius:$radius-value;
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == b {
+				border-bottom-left-radius:$radius-value;
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == l {
+				border-top-left-radius:$radius-value;
+				border-bottom-left-radius:$radius-value;
+		}@else if $d == tl {
+				border-top-left-radius:$radius-value;
+		}@else if $d == tr {
+				border-top-right-radius:$radius-value;
+		}@else if $d == br {
+				border-bottom-right-radius:$radius-value;
+		}@else if $d == bl {
+				border-bottom-left-radius:$radius-value;
+		}
+  }@else{
+		border-radius:$radius-value;
+  }
+}
+
+@each $key, $child in $uni-radius {
+	@if($key){
+		.uni-radius-#{"" + $key} {
+				@include radius($key)
+		}
+	}@else{
+		.uni-radius {
+				@include radius($key)
+		}
+	}
+}
+
+@each $direction in t, r, b, l,tl, tr, br, bl {
+	@each $key, $child in $uni-radius {
+		@if($key){
+			.uni-radius-#{"" + $direction}-#{"" + $key} {
+				@include radius($key,$direction,false)
+			}
+		}@else{
+			.uni-radius-#{$direction} {
+				@include radius($key,$direction,false)
+			}
+		}
+	}
+}

+ 56 - 0
uni_modules/uni-scss/styles/setting/_space.scss

@@ -0,0 +1,56 @@
+
+@mixin fn($space,$direction,$size,$n) {
+	@if $n {
+		#{$space}-#{$direction}: #{$size*$uni-space-root}px
+	} @else {
+		 #{$space}-#{$direction}: #{-$size*$uni-space-root}px
+	}
+}
+@mixin get-styles($direction,$i,$space,$n){
+	@if $direction == t {
+		@include fn($space, top,$i,$n);
+	} 
+	@if $direction == r {
+		@include fn($space, right,$i,$n);
+	} 
+	@if $direction == b {
+		@include fn($space, bottom,$i,$n);
+	} 
+	@if $direction == l {
+	 @include fn($space, left,$i,$n);
+	} 
+	@if $direction == x {
+		@include fn($space, left,$i,$n);
+		@include fn($space, right,$i,$n);
+	} 
+	@if $direction == y {
+		@include fn($space, top,$i,$n);
+		@include fn($space, bottom,$i,$n);
+	} 
+	@if $direction == a {
+		@if $n {
+			#{$space}:#{$i*$uni-space-root}px;
+		} @else {
+			#{$space}:#{-$i*$uni-space-root}px;
+		}
+	} 
+}
+
+@each $orientation in m,p {
+	$space: margin;
+	@if $orientation == m {
+		$space: margin;
+	} @else {
+		$space: padding;
+	}
+	@for $i from 0 through 16 {
+		@each $direction in t, r, b, l, x, y, a {
+			.uni-#{$orientation}#{$direction}-#{$i} { 
+				@include  get-styles($direction,$i,$space,true);
+			} 
+			.uni-#{$orientation}#{$direction}-n#{$i} { 
+				@include  get-styles($direction,$i,$space,false);
+			}
+		}
+	}
+}

+ 167 - 0
uni_modules/uni-scss/styles/setting/_styles.scss

@@ -0,0 +1,167 @@
+/* #ifndef APP-NVUE */
+
+$-color-white:#fff;
+$-color-black:#000;
+@mixin base-style($color) {
+	color: #fff;
+	background-color: $color;
+	border-color: mix($-color-black, $color, 8%);
+	&:not([hover-class]):active {
+		background: mix($-color-black, $color, 10%);
+		border-color: mix($-color-black, $color, 20%);
+		color: $-color-white;
+		outline: none;
+	}
+}
+@mixin is-color($color) {
+	@include base-style($color);
+	&[loading] {
+		@include base-style($color);
+		&::before {
+			margin-right:5px;
+		}
+	}
+	&[disabled] {
+	  &,
+		&[loading],
+	  &:not([hover-class]):active {
+	    color: $-color-white;
+			border-color: mix(darken($color,10%), $-color-white);
+	    background-color: mix($color, $-color-white);
+	  }
+	}
+
+}
+@mixin base-plain-style($color) {
+	color:$color;
+	background-color: mix($-color-white, $color, 90%);
+	border-color: mix($-color-white, $color, 70%);
+	&:not([hover-class]):active {
+	  background: mix($-color-white, $color, 80%);
+	  color: $color;
+	  outline: none;
+		border-color: mix($-color-white, $color, 50%);
+	}
+}
+@mixin is-plain($color){
+	&[plain] {
+		@include base-plain-style($color);
+		&[loading] {
+			@include base-plain-style($color);
+			&::before {
+				margin-right:5px;
+			}
+		}
+		&[disabled] {
+		  &,
+		  &:active {
+		    color: mix($-color-white, $color, 40%);
+		    background-color: mix($-color-white, $color, 90%);
+				border-color: mix($-color-white, $color, 80%);
+		  }
+		}
+	}
+}
+
+
+.uni-btn {
+	margin: 5px;
+	color: #393939;
+	border:1px solid #ccc;
+	font-size: 16px;
+	font-weight: 200;
+	background-color: #F9F9F9;
+	// TODO 暂时处理边框隐藏一边的问题
+	overflow: visible;
+	&::after{
+		border: none;
+	}
+
+	&:not([type]),&[type=default] {
+		color: #999;
+		&[loading] {
+			background: none;
+			&::before {
+				margin-right:5px;
+			}
+		}
+
+
+
+		&[disabled]{
+			color: mix($-color-white, #999, 60%);
+		  &,
+			&[loading],
+		  &:active {
+				color: mix($-color-white, #999, 60%);
+		    background-color: mix($-color-white,$-color-black , 98%);
+				border-color: mix($-color-white,  #999, 85%);
+		  }
+		}
+
+		&[plain] {
+			color: #999;
+			background: none;
+			border-color: $uni-border-1;
+			&:not([hover-class]):active {
+				background: none;
+			  color: mix($-color-white, $-color-black, 80%);
+				border-color: mix($-color-white, $-color-black, 90%);
+			  outline: none;
+			}
+			&[disabled]{
+			  &,
+				&[loading],
+			  &:active {
+			    background: none;
+					color: mix($-color-white, #999, 60%);
+					border-color: mix($-color-white,  #999, 85%);
+			  }
+			}
+		}
+	}
+
+	&:not([hover-class]):active {
+	  color: mix($-color-white, $-color-black, 50%);
+	}
+
+	&[size=mini] {
+		font-size: 16px;
+		font-weight: 200;
+		border-radius: 8px;
+	}
+
+
+
+	&.uni-btn-small {
+		font-size: 14px;
+	}
+	&.uni-btn-mini {
+		font-size: 12px;
+	}
+
+	&.uni-btn-radius {
+		border-radius: 999px;
+	}
+	&[type=primary] {
+		@include is-color($uni-primary);
+		@include is-plain($uni-primary)
+	}
+	&[type=success] {
+		@include is-color($uni-success);
+		@include is-plain($uni-success)
+	}
+	&[type=error] {
+		@include is-color($uni-error);
+		@include is-plain($uni-error)
+	}
+	&[type=warning] {
+		@include is-color($uni-warning);
+		@include is-plain($uni-warning)
+	}
+	&[type=info] {
+		@include is-color($uni-info);
+		@include is-plain($uni-info)
+	}
+}
+/* #endif */

+ 24 - 0
uni_modules/uni-scss/styles/setting/_text.scss

@@ -0,0 +1,24 @@
+@mixin get-styles($k,$c) {
+	@if $k == size or $k == weight{
+		font-#{$k}:#{$c}
+	}@else{
+		#{$k}:#{$c}
+	}
+}
+
+@each $key, $child in $uni-headings {
+	/* #ifndef APP-NVUE */
+	.uni-#{$key} {
+		@each $k, $c in $child {
+			@include get-styles($k,$c)
+		}
+	}
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	.container .uni-#{$key} {
+		@each $k, $c in $child {
+			@include get-styles($k,$c)
+		}
+	}
+	/* #endif */
+}

+ 146 - 0
uni_modules/uni-scss/styles/setting/_variables.scss

@@ -0,0 +1,146 @@
+// @use "sass:math";
+@import  '../tools/functions.scss';
+// 间距基础倍数
+$uni-space-root: 2 !default;
+// 边框半径默认值
+$uni-radius-root:5px !default;
+$uni-radius: () !default;
+// 边框半径断点
+$uni-radius: map-deep-merge(
+  (
+    0: 0,
+		// TODO 当前版本暂时不支持 sm 属性
+    // 'sm': math.div($uni-radius-root, 2),
+    null: $uni-radius-root,
+    'lg': $uni-radius-root * 2,
+    'xl': $uni-radius-root * 6,
+    'pill': 9999px,
+    'circle': 50%
+  ),
+  $uni-radius
+);
+// 字体家族
+$body-font-family: 'Roboto', sans-serif !default;
+// 文本
+$heading-font-family: $body-font-family !default;
+$uni-headings: () !default;
+$letterSpacing: -0.01562em;
+$uni-headings: map-deep-merge(
+  (
+    'h1': (
+      size: 32px,
+			weight: 300,
+			line-height: 50px,
+			// letter-spacing:-0.01562em
+    ),
+    'h2': (
+      size: 28px,
+      weight: 300,
+      line-height: 40px,
+      // letter-spacing: -0.00833em
+    ),
+    'h3': (
+      size: 24px,
+      weight: 400,
+      line-height: 32px,
+      // letter-spacing: normal
+    ),
+    'h4': (
+      size: 20px,
+      weight: 400,
+      line-height: 30px,
+      // letter-spacing: 0.00735em
+    ),
+    'h5': (
+      size: 16px,
+      weight: 400,
+      line-height: 24px,
+      // letter-spacing: normal
+    ),
+    'h6': (
+      size: 14px,
+      weight: 500,
+      line-height: 18px,
+      // letter-spacing: 0.0125em
+    ),
+    'subtitle': (
+      size: 12px,
+      weight: 400,
+      line-height: 20px,
+      // letter-spacing: 0.00937em
+    ),
+    'body': (
+      font-size: 14px,
+			font-weight: 400,
+			line-height: 22px,
+			// letter-spacing: 0.03125em
+    ),
+    'caption': (
+      'size': 12px,
+      'weight': 400,
+      'line-height': 20px,
+      // 'letter-spacing': 0.03333em,
+      // 'text-transform': false
+    )
+  ),
+  $uni-headings
+);
+
+
+
+// 主色
+$uni-primary: #2979ff !default;
+$uni-primary-disable:lighten($uni-primary,20%) !default;
+$uni-primary-light: lighten($uni-primary,25%) !default;
+
+// 辅助色
+// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
+$uni-success: #18bc37 !default;
+$uni-success-disable:lighten($uni-success,20%) !default;
+$uni-success-light: lighten($uni-success,25%) !default;
+
+$uni-warning: #f3a73f !default;
+$uni-warning-disable:lighten($uni-warning,20%) !default;
+$uni-warning-light: lighten($uni-warning,25%) !default;
+
+$uni-error: #e43d33 !default;
+$uni-error-disable:lighten($uni-error,20%) !default;
+$uni-error-light: lighten($uni-error,25%) !default;
+
+$uni-info: #8f939c !default;
+$uni-info-disable:lighten($uni-info,20%) !default;
+$uni-info-light: lighten($uni-info,25%) !default;
+
+// 中性色
+// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
+$uni-main-color: #3a3a3a !default; 			// 主要文字
+$uni-base-color: #6a6a6a !default;			// 常规文字
+$uni-secondary-color: #909399 !default;	// 次要文字
+$uni-extra-color: #c7c7c7 !default;			// 辅助说明
+
+// 边框颜色
+$uni-border-1: #F0F0F0 !default;
+$uni-border-2: #EDEDED !default;
+$uni-border-3: #DCDCDC !default;
+$uni-border-4: #B9B9B9 !default;
+
+// 常规色
+$uni-black: #000000 !default;
+$uni-white: #ffffff !default;
+$uni-transparent: rgba($color: #000000, $alpha: 0) !default;
+
+// 背景色
+$uni-bg-color: #f7f7f7 !default;
+
+/* 水平间距 */
+$uni-spacing-sm: 8px !default;
+$uni-spacing-base: 15px !default;
+$uni-spacing-lg: 30px !default;
+
+// 阴影
+$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default;
+$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
+$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default;
+
+// 蒙版
+$uni-mask: rgba($color: #000000, $alpha: 0.4) !default;

+ 19 - 0
uni_modules/uni-scss/styles/tools/functions.scss

@@ -0,0 +1,19 @@
+// 合并 map
+@function map-deep-merge($parent-map, $child-map){
+	$result: $parent-map;
+	@each $key, $child in $child-map {
+		$parent-has-key: map-has-key($result, $key);
+		$parent-value: map-get($result, $key);
+		$parent-type: type-of($parent-value);
+		$child-type: type-of($child);
+		$parent-is-map: $parent-type == map;
+		$child-is-map: $child-type == map;
+			
+		@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){
+			$result: map-merge($result, ( $key: $child ));
+		}@else {
+			$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) ));
+		}
+	}
+	@return $result;
+};

+ 31 - 0
uni_modules/uni-scss/theme.scss

@@ -0,0 +1,31 @@
+// 间距基础倍数
+$uni-space-root: 2;
+// 边框半径默认值
+$uni-radius-root:5px;
+// 主色
+$uni-primary: #2979ff;
+// 辅助色
+$uni-success: #4cd964;
+// 警告色
+$uni-warning: #f0ad4e;
+// 错误色
+$uni-error: #dd524d;
+// 描述色
+$uni-info: #909399;
+// 中性色
+$uni-main-color: #303133;
+$uni-base-color: #606266;
+$uni-secondary-color: #909399;
+$uni-extra-color: #C0C4CC;
+// 背景色
+$uni-bg-color: #f5f5f5;
+// 边框颜色
+$uni-border-1: #DCDFE6;
+$uni-border-2: #E4E7ED;
+$uni-border-3: #EBEEF5;
+$uni-border-4: #F2F6FC;
+
+// 常规色
+$uni-black: #000000;
+$uni-white: #ffffff;
+$uni-transparent: rgba($color: #000000, $alpha: 0);

+ 62 - 0
uni_modules/uni-scss/variables.scss

@@ -0,0 +1,62 @@
+@import './styles/setting/_variables.scss';
+// 间距基础倍数
+$uni-space-root: 2;
+// 边框半径默认值
+$uni-radius-root:5px;
+
+// 主色
+$uni-primary: #2979ff;
+$uni-primary-disable:mix(#fff,$uni-primary,50%);
+$uni-primary-light: mix(#fff,$uni-primary,80%);
+
+// 辅助色
+// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
+$uni-success: #18bc37;
+$uni-success-disable:mix(#fff,$uni-success,50%);
+$uni-success-light: mix(#fff,$uni-success,80%);
+
+$uni-warning: #f3a73f;
+$uni-warning-disable:mix(#fff,$uni-warning,50%);
+$uni-warning-light: mix(#fff,$uni-warning,80%);
+
+$uni-error: #e43d33;
+$uni-error-disable:mix(#fff,$uni-error,50%);
+$uni-error-light: mix(#fff,$uni-error,80%);
+
+$uni-info: #8f939c;
+$uni-info-disable:mix(#fff,$uni-info,50%);
+$uni-info-light: mix(#fff,$uni-info,80%);
+
+// 中性色
+// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
+$uni-main-color: #3a3a3a; 			// 主要文字
+$uni-base-color: #6a6a6a;			// 常规文字
+$uni-secondary-color: #909399;	// 次要文字
+$uni-extra-color: #c7c7c7;			// 辅助说明
+
+// 边框颜色
+$uni-border-1: #F0F0F0;
+$uni-border-2: #EDEDED;
+$uni-border-3: #DCDCDC;
+$uni-border-4: #B9B9B9;
+
+// 常规色
+$uni-black: #000000;
+$uni-white: #ffffff;
+$uni-transparent: rgba($color: #000000, $alpha: 0);
+
+// 背景色
+$uni-bg-color: #f7f7f7;
+
+/* 水平间距 */
+$uni-spacing-sm: 8px;
+$uni-spacing-base: 15px;
+$uni-spacing-lg: 30px;
+
+// 阴影
+$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5);
+$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2);
+$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
+
+// 蒙版
+$uni-mask: rgba($color: #000000, $alpha: 0.4);

+ 33 - 0
uni_modules/uni-table/changelog.md

@@ -0,0 +1,33 @@
+## 1.2.8(2024-10-15)
+- 修复 运行到抖音小程序上出现的问题
+## 1.2.7(2024-10-15)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.2.4(2023-12-19)
+- 修复 uni-tr只有一列时minWidth计算错误,列变化实时计算更新
+## 1.2.3(2023-03-28)
+- 修复 在vue3模式下可能会出现错误的问题
+## 1.2.2(2022-11-29)
+- 优化 主题样式
+## 1.2.1(2022-06-06)
+- 修复 微信小程序存在无使用组件的问题
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table)
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-07-08)
+- 新增 uni-th 支持 date 日期筛选范围
+## 1.0.6(2021-07-05)
+- 新增 uni-th 支持 range 筛选范围
+## 1.0.5(2021-06-28)
+- 新增 uni-th 筛选功能
+## 1.0.4(2021-05-12)
+- 新增 示例地址
+- 修复 示例项目缺少组件的Bug
+## 1.0.3(2021-04-16)
+- 新增 sortable 属性,是否开启单列排序
+- 优化 表格多选逻辑
+## 1.0.2(2021-03-22)
+- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制
+## 1.0.1(2021-02-05)
+- 调整为uni_modules目录规范

+ 462 - 0
uni_modules/uni-table/components/uni-table/uni-table.vue

@@ -0,0 +1,462 @@
+<template>
+	<view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }">
+		<!-- #ifdef H5 -->
+		 <!-- :style="{ 'min-width': minWidth + 'px' }" -->
+		<table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }">
+			<slot></slot>
+			<tr v-if="noData" class="uni-table-loading">
+				<td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td>
+			</tr>
+			<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
+		</table>
+		<!-- #endif -->
+		<!-- #ifndef H5 -->
+		 <!-- :style="{ 'min-width': minWidth + 'px' }" -->
+		<view class="uni-table" :class="{ 'table--stripe': stripe }">
+			<slot></slot>
+			<view v-if="noData" class="uni-table-loading">
+				<view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view>
+			</view>
+			<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+/**
+ * Table 表格
+ * @description 用于展示多条结构类似的数据
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+ * @property {Boolean} 	border 				是否带有纵向边框
+ * @property {Boolean} 	stripe 				是否显示斑马线
+ * @property {Boolean} 	type 					是否开启多选
+ * @property {String} 	emptyText 			空数据时显示的文本内容
+ * @property {Boolean} 	loading 			显示加载中
+ * @event {Function} 	selection-change 	开启多选时,当选择项发生变化时会触发该事件
+ */
+export default {
+	name: 'uniTable',
+	options: {
+		// #ifdef MP-TOUTIAO
+		virtualHost: false,
+		// #endif
+		// #ifndef MP-TOUTIAO
+		virtualHost: true
+		// #endif
+	},
+	emits:['selection-change'],
+	props: {
+		data: {
+			type: Array,
+			default() {
+				return []
+			}
+		},
+		// 是否有竖线
+		border: {
+			type: Boolean,
+			default: false
+		},
+		// 是否显示斑马线
+		stripe: {
+			type: Boolean,
+			default: false
+		},
+		// 多选
+		type: {
+			type: String,
+			default: ''
+		},
+		// 没有更多数据
+		emptyText: {
+			type: String,
+			default: '没有更多数据'
+		},
+		loading: {
+			type: Boolean,
+			default: false
+		},
+		rowKey: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			noData: true,
+			minWidth: 0,
+			multiTableHeads: []
+		}
+	},
+	watch: {
+		loading(val) {},
+		data(newVal) {
+			let theadChildren = this.theadChildren
+			let rowspan = 1
+			if (this.theadChildren) {
+				rowspan = this.theadChildren.rowspan
+			}
+
+			// this.trChildren.length - rowspan
+			this.noData = false
+			// this.noData = newVal.length === 0
+		}
+	},
+	created() {
+		// 定义tr的实例数组
+		this.trChildren = []
+		this.thChildren = []
+		this.theadChildren = null
+		this.backData = []
+		this.backIndexData = []
+	},
+
+	methods: {
+		isNodata() {
+			let theadChildren = this.theadChildren
+			let rowspan = 1
+			if (this.theadChildren) {
+				rowspan = this.theadChildren.rowspan
+			}
+			this.noData = this.trChildren.length - rowspan <= 0
+		},
+		/**
+		 * 选中所有
+		 */
+		selectionAll() {
+			let startIndex = 1
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			} else {
+				startIndex = theadChildren.rowspan - 1
+			}
+			let isHaveData = this.data && this.data.length > 0
+			theadChildren.checked = true
+			theadChildren.indeterminate = false
+			this.trChildren.forEach((item, index) => {
+				if (!item.disabled) {
+					item.checked = true
+					if (isHaveData && item.keyValue) {
+						const row = this.data.find(v => v[this.rowKey] === item.keyValue)
+						if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) {
+							this.backData.push(row)
+						}
+					}
+					if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) {
+						this.backIndexData.push(index - startIndex)
+					}
+				}
+			})
+			// this.backData = JSON.parse(JSON.stringify(this.data))
+			this.$emit('selection-change', {
+				detail: {
+					value: this.backData,
+					index: this.backIndexData
+				}
+			})
+		},
+		/**
+		 * 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
+		 */
+		toggleRowSelection(row, selected) {
+			// if (!this.theadChildren) return
+			row = [].concat(row)
+
+			this.trChildren.forEach((item, index) => {
+				// if (item.keyValue) {
+
+				const select = row.findIndex(v => {
+					//
+					if (typeof v === 'number') {
+						return v === index - 1
+					} else {
+						return v[this.rowKey] === item.keyValue
+					}
+				})
+				let ischeck = item.checked
+				if (select !== -1) {
+					if (typeof selected === 'boolean') {
+						item.checked = selected
+					} else {
+						item.checked = !item.checked
+					}
+					if (ischeck !== item.checked) {
+						this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true)
+					}
+				}
+				// }
+			})
+			this.$emit('selection-change', {
+				detail: {
+					value: this.backData,
+					index:this.backIndexData
+				}
+			})
+		},
+
+		/**
+		 * 用于多选表格,清空用户的选择
+		 */
+		clearSelection() {
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			}
+			// if (!this.theadChildren) return
+			theadChildren.checked = false
+			theadChildren.indeterminate = false
+			this.trChildren.forEach(item => {
+				// if (item.keyValue) {
+					item.checked = false
+				// }
+			})
+			this.backData = []
+			this.backIndexData = []
+			this.$emit('selection-change', {
+				detail: {
+					value: [],
+					index: []
+				}
+			})
+		},
+		/**
+		 * 用于多选表格,切换所有行的选中状态
+		 */
+		toggleAllSelection() {
+			let list = []
+			let startIndex = 1
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			} else {
+				startIndex = theadChildren.rowspan - 1
+			}
+			this.trChildren.forEach((item, index) => {
+				if (!item.disabled) {
+					if (index > (startIndex - 1) ) {
+						list.push(index-startIndex)
+					}
+				}
+			})
+			this.toggleRowSelection(list)
+		},
+
+		/**
+		 * 选中\取消选中
+		 * @param {Object} child
+		 * @param {Object} check
+		 * @param {Object} rowValue
+		 */
+		check(child, check, keyValue, emit) {
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			}
+
+
+
+			let childDomIndex = this.trChildren.findIndex((item, index) => child === item)
+			if(childDomIndex < 0){
+				childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1
+			}
+			const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length
+			if (childDomIndex === 0) {
+				check ? this.selectionAll() : this.clearSelection()
+				return
+			}
+
+			if (check) {
+				if (keyValue) {
+					this.backData.push(child)
+				}
+				this.backIndexData.push(childDomIndex - 1)
+			} else {
+				const index = this.backData.findIndex(v => v[this.rowKey] === keyValue)
+				const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1)
+				if (keyValue) {
+					this.backData.splice(index, 1)
+				}
+				this.backIndexData.splice(idx, 1)
+			}
+
+			const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled)
+			if (!domCheckAll) {
+				theadChildren.indeterminate = false
+				theadChildren.checked = true
+			} else {
+				theadChildren.indeterminate = true
+				theadChildren.checked = false
+			}
+
+			if (this.backIndexData.length === 0) {
+				theadChildren.indeterminate = false
+			}
+
+			if (!emit) {
+				this.$emit('selection-change', {
+					detail: {
+						value: this.backData,
+						index: this.backIndexData
+					}
+				})
+			}
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+
+.uni-table-scroll {
+	width: 100%;
+	/* #ifndef APP-NVUE */
+	overflow-x: auto;
+	/* #endif */
+}
+
+.uni-table {
+	position: relative;
+	width: 100%;
+	border-radius: 5px;
+	// box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);
+	background-color: #fff;
+	/* #ifndef APP-NVUE */
+	box-sizing: border-box;
+	display: table;
+	overflow-x: auto;
+	::v-deep .uni-table-tr:nth-child(n + 2) {
+		&:hover {
+			background-color: #f5f7fa;
+		}
+	}
+	::v-deep .uni-table-thead {
+		.uni-table-tr {
+			// background-color: #f5f7fa;
+			&:hover {
+				background-color:#fafafa;
+			}
+		}
+	}
+	/* #endif */
+}
+
+.table--border {
+	border: 1px $border-color solid;
+	border-right: none;
+}
+
+.border-none {
+	/* #ifndef APP-NVUE */
+	border-bottom: none;
+	/* #endif */
+}
+
+.table--stripe {
+	/* #ifndef APP-NVUE */
+	::v-deep .uni-table-tr:nth-child(2n + 3) {
+		background-color: #fafafa;
+	}
+	/* #endif */
+}
+
+/* 表格加载、无数据样式 */
+.uni-table-loading {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: table-row;
+	/* #endif */
+	height: 50px;
+	line-height: 50px;
+	overflow: hidden;
+	box-sizing: border-box;
+}
+.empty-border {
+	border-right: 1px $border-color solid;
+}
+.uni-table-text {
+	position: absolute;
+	right: 0;
+	left: 0;
+	text-align: center;
+	font-size: 14px;
+	color: #999;
+}
+
+.uni-table-mask {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background-color: rgba(255, 255, 255, 0.8);
+	z-index: 99;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	margin: auto;
+	transition: all 0.5s;
+	/* #endif */
+	justify-content: center;
+	align-items: center;
+}
+
+.uni-table--loader {
+	width: 30px;
+	height: 30px;
+	border: 2px solid #aaa;
+	// border-bottom-color: transparent;
+	border-radius: 50%;
+	/* #ifndef APP-NVUE */
+	animation: 2s uni-table--loader linear infinite;
+	/* #endif */
+	position: relative;
+}
+
+@keyframes uni-table--loader {
+	0% {
+		transform: rotate(360deg);
+	}
+
+	10% {
+		border-left-color: transparent;
+	}
+
+	20% {
+		border-bottom-color: transparent;
+	}
+
+	30% {
+		border-right-color: transparent;
+	}
+
+	40% {
+		border-top-color: transparent;
+	}
+
+	50% {
+		transform: rotate(0deg);
+	}
+
+	60% {
+		border-top-color: transparent;
+	}
+
+	70% {
+		border-left-color: transparent;
+	}
+
+	80% {
+		border-bottom-color: transparent;
+	}
+
+	90% {
+		border-right-color: transparent;
+	}
+
+	100% {
+		transform: rotate(-360deg);
+	}
+}
+</style>

+ 34 - 0
uni_modules/uni-table/components/uni-tbody/uni-tbody.vue

@@ -0,0 +1,34 @@
+<template>
+	<!-- #ifdef H5 -->
+	<tbody>
+		<slot></slot>
+	</tbody>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view><slot></slot></view>
+	<!-- #endif -->
+</template>
+
+<script>
+export default {
+	name: 'uniBody',
+	options: {
+		// #ifdef MP-TOUTIAO
+		virtualHost: false,
+		// #endif
+		// #ifndef MP-TOUTIAO
+		virtualHost: true
+		// #endif
+	},
+	data() {
+		return {
+
+		}
+	},
+	created() {},
+	methods: {}
+}
+</script>
+
+<style>
+</style>

+ 95 - 0
uni_modules/uni-table/components/uni-td/uni-td.vue

@@ -0,0 +1,95 @@
+<template>
+	<!-- #ifdef H5 -->
+	<td class="uni-table-td" :rowspan="rowspan" :colspan="colspan" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
+		<slot></slot>
+	</td>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<!-- :class="{'table--border':border}"  -->
+	<view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
+		<slot></slot>
+	</view>
+	<!-- #endif -->
+
+</template>
+
+<script>
+	/**
+	 * Td 单元格
+	 * @description 表格中的标准单元格组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+	 * @property {Number} 	align = [left|center|right]	单元格对齐方式
+	 */
+	export default {
+		name: 'uniTd',
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		props: {
+			width: {
+				type: [String, Number],
+				default: ''
+			},
+			align: {
+				type: String,
+				default: 'left'
+			},
+			rowspan: {
+				type: [Number,String],
+				default: 1
+			},
+			colspan: {
+					type: [Number,String],
+				default: 1
+			}
+		},
+		data() {
+			return {
+				border: false
+			};
+		},
+		created() {
+			this.root = this.getTable()
+			this.border = this.root.border
+		},
+		methods: {
+			/**
+			 * 获取父元素实例
+			 */
+			getTable() {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== 'uniTable') {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	$border-color:#EBEEF5;
+
+	.uni-table-td {
+		display: table-cell;
+		padding: 8px 10px;
+		font-size: 14px;
+		border-bottom: 1px $border-color solid;
+		font-weight: 400;
+		color: #606266;
+		line-height: 23px;
+		box-sizing: border-box;
+	}
+
+	.table--border {
+		border-right: 1px $border-color solid;
+	}
+</style>

+ 511 - 0
uni_modules/uni-table/components/uni-th/filter-dropdown.vue

@@ -0,0 +1,511 @@
+<template>
+	<view class="uni-filter-dropdown">
+		<view class="dropdown-btn" @click="onDropdown">
+			<view class="icon-select" :class="{active: canReset}" v-if="isSelect || isRange"></view>
+			<view class="icon-search" :class="{active: canReset}" v-if="isSearch">
+				<view class="icon-search-0"></view>
+				<view class="icon-search-1"></view>
+			</view>
+			<view class="icon-calendar" :class="{active: canReset}" v-if="isDate">
+				<view class="icon-calendar-0"></view>
+				<view class="icon-calendar-1"></view>
+			</view>
+		</view>
+		<view class="uni-dropdown-cover" v-if="isOpened" @click="handleClose"></view>
+		<view class="dropdown-popup dropdown-popup-right" v-if="isOpened" @click.stop>
+			<!-- select-->
+			<view v-if="isSelect" class="list">
+				<label class="flex-r a-i-c list-item" v-for="(item,index) in dataList" :key="index"
+					@click="onItemClick($event, index)">
+					<check-box class="check" :checked="item.checked" />
+					<view class="checklist-content">
+						<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
+					</view>
+				</label>
+			</view>
+			<view v-if="isSelect" class="flex-r opera-area">
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSelectReset">
+					{{resource.reset}}</view>
+				<view class="flex-f btn btn-submit" @click="handleSelectSubmit">{{resource.submit}}</view>
+			</view>
+			<!-- search -->
+			<view v-if="isSearch" class="search-area">
+				<input class="search-input" v-model="filterValue" />
+			</view>
+			<view v-if="isSearch" class="flex-r opera-area">
+				<view class="flex-f btn btn-submit" @click="handleSearchSubmit">{{resource.search}}</view>
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSearchReset">
+					{{resource.reset}}</view>
+			</view>
+			<!-- range -->
+			<view v-if="isRange">
+				<view class="input-label">{{resource.gt}}</view>
+				<input class="input" v-model="gtValue" />
+				<view class="input-label">{{resource.lt}}</view>
+				<input class="input" v-model="ltValue" />
+			</view>
+			<view v-if="isRange" class="flex-r opera-area">
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleRangeReset">
+					{{resource.reset}}</view>
+				<view class="flex-f btn btn-submit" @click="handleRangeSubmit">{{resource.submit}}</view>
+			</view>
+			<!-- date -->
+			<view v-if="isDate">
+				<uni-datetime-picker ref="datetimepicker" :value="dateRange" type="datetimerange" return-type="timestamp" @change="datetimechange" @maskClick="timepickerclose">
+					<view></view>
+				</uni-datetime-picker>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import checkBox from '../uni-tr/table-checkbox.vue'
+
+	const resource = {
+		"reset": "重置",
+		"search": "搜索",
+		"submit": "确定",
+		"filter": "筛选",
+		"gt": "大于等于",
+		"lt": "小于等于",
+		"date": "日期范围"
+	}
+
+	const DropdownType = {
+		Select: "select",
+		Search: "search",
+		Range: "range",
+		Date: "date",
+		Timestamp: "timestamp"
+	}
+
+	export default {
+		name: 'FilterDropdown',
+		emits:['change'],
+		components: {
+			checkBox
+		},
+		options: {
+			virtualHost: true
+		},
+		props: {
+			filterType: {
+				type: String,
+				default: DropdownType.Select
+			},
+			filterData: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			mode: {
+				type: String,
+				default: 'default'
+			},
+			map: {
+				type: Object,
+				default () {
+					return {
+						text: 'text',
+						value: 'value'
+					}
+				}
+			},
+			filterDefaultValue: {
+				type: [Array,String],
+				default () {
+					return ""
+				}
+			}
+		},
+		computed: {
+			canReset() {
+				if (this.isSearch) {
+					return this.filterValue.length > 0
+				}
+				if (this.isSelect) {
+					return this.checkedValues.length > 0
+				}
+				if (this.isRange) {
+					return (this.gtValue.length > 0 && this.ltValue.length > 0)
+				}
+				if (this.isDate) {
+					return this.dateSelect.length > 0
+				}
+				return false
+			},
+			isSelect() {
+				return this.filterType === DropdownType.Select
+			},
+			isSearch() {
+				return this.filterType === DropdownType.Search
+			},
+			isRange() {
+				return this.filterType === DropdownType.Range
+			},
+			isDate() {
+				return (this.filterType === DropdownType.Date || this.filterType === DropdownType.Timestamp)
+			}
+		},
+		watch: {
+			filterData(newVal) {
+				this._copyFilters()
+			},
+			indeterminate(newVal) {
+				this.isIndeterminate = newVal
+			}
+		},
+		data() {
+			return {
+				resource,
+				enabled: true,
+				isOpened: false,
+				dataList: [],
+				filterValue: this.filterDefaultValue,
+				checkedValues: [],
+				gtValue: '',
+				ltValue: '',
+				dateRange: [],
+				dateSelect: []
+			};
+		},
+		created() {
+			this._copyFilters()
+		},
+		methods: {
+			_copyFilters() {
+				let dl = JSON.parse(JSON.stringify(this.filterData))
+				for (let i = 0; i < dl.length; i++) {
+					if (dl[i].checked === undefined) {
+						dl[i].checked = false
+					}
+				}
+				this.dataList = dl
+			},
+			openPopup() {
+				this.isOpened = true
+				if (this.isDate) {
+					this.$nextTick(() => {
+						if (!this.dateRange.length) {
+							this.resetDate()
+						}
+						this.$refs.datetimepicker.show()
+					})
+				}
+			},
+			closePopup() {
+				this.isOpened = false
+			},
+			handleClose(e) {
+				this.closePopup()
+			},
+			resetDate() {
+				let date = new Date()
+				let dateText = date.toISOString().split('T')[0]
+				this.dateRange = [dateText + ' 0:00:00', dateText + ' 23:59:59']
+			},
+			onDropdown(e) {
+				this.openPopup()
+			},
+			onItemClick(e, index) {
+				let items = this.dataList
+				let listItem = items[index]
+				if (listItem.checked === undefined) {
+					items[index].checked = true
+				} else {
+					items[index].checked = !listItem.checked
+				}
+
+				let checkvalues = []
+				for (let i = 0; i < items.length; i++) {
+					const item = items[i]
+					if (item.checked) {
+						checkvalues.push(item.value)
+					}
+				}
+				this.checkedValues = checkvalues
+			},
+			datetimechange(e) {
+				this.closePopup()
+				this.dateRange = e
+				this.dateSelect = e
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: e
+				})
+			},
+			timepickerclose(e) {
+				this.closePopup()
+			},
+			handleSelectSubmit() {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: this.checkedValues
+				})
+			},
+			handleSelectReset() {
+				if (!this.canReset) {
+					return;
+				}
+				var items = this.dataList
+				for (let i = 0; i < items.length; i++) {
+					let item = items[i]
+					this.$set(item, 'checked', false)
+				}
+				this.checkedValues = []
+				this.handleSelectSubmit()
+			},
+			handleSearchSubmit() {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: this.filterValue
+				})
+			},
+			handleSearchReset() {
+				if (!this.canReset) {
+					return;
+				}
+				this.filterValue = ''
+				this.handleSearchSubmit()
+			},
+			handleRangeSubmit(isReset) {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: isReset === true ? [] : [parseInt(this.gtValue), parseInt(this.ltValue)]
+				})
+			},
+			handleRangeReset() {
+				if (!this.canReset) {
+					return;
+				}
+				this.gtValue = ''
+				this.ltValue = ''
+				this.handleRangeSubmit(true)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #1890ff !default;
+	
+	.flex-r {
+		display: flex;
+		flex-direction: row;
+	}
+
+	.flex-f {
+		flex: 1;
+	}
+
+	.a-i-c {
+		align-items: center;
+	}
+
+	.j-c-c {
+		justify-content: center;
+	}
+
+	.icon-select {
+		width: 14px;
+		height: 16px;
+		border: solid 6px transparent;
+		border-top: solid 6px #ddd;
+		border-bottom: none;
+		background-color: #ddd;
+		background-clip: content-box;
+		box-sizing: border-box;
+	}
+
+	.icon-select.active {
+		background-color: $uni-primary;
+		border-top-color: $uni-primary;
+	}
+
+	.icon-search {
+		width: 12px;
+		height: 16px;
+		position: relative;
+	}
+
+	.icon-search-0 {
+		border: 2px solid #ddd;
+		border-radius: 8px;
+		width: 7px;
+		height: 7px;
+	}
+
+	.icon-search-1 {
+		position: absolute;
+		top: 8px;
+		right: 0;
+		width: 1px;
+		height: 7px;
+		background-color: #ddd;
+		transform: rotate(-45deg);
+	}
+
+	.icon-search.active .icon-search-0 {
+		border-color: $uni-primary;
+	}
+
+	.icon-search.active .icon-search-1 {
+		background-color: $uni-primary;
+	}
+
+	.icon-calendar {
+		color: #ddd;
+		width: 14px;
+		height: 16px;
+	}
+
+	.icon-calendar-0 {
+		height: 4px;
+		margin-top: 3px;
+		margin-bottom: 1px;
+		background-color: #ddd;
+		border-radius: 2px 2px 1px 1px;
+		position: relative;
+	}
+	.icon-calendar-0:before, .icon-calendar-0:after {
+		content: '';
+		position: absolute;
+		top: -3px;
+		width: 4px;
+		height: 3px;
+		border-radius: 1px;
+		background-color: #ddd;
+	}
+	.icon-calendar-0:before {
+		left: 2px;
+	}
+	.icon-calendar-0:after {
+		right: 2px;
+	}
+
+	.icon-calendar-1 {
+		height: 9px;
+		background-color: #ddd;
+		border-radius: 1px 1px 2px 2px;
+	}
+
+	.icon-calendar.active {
+		color: $uni-primary;
+	}
+
+	.icon-calendar.active .icon-calendar-0,
+	.icon-calendar.active .icon-calendar-1,
+	.icon-calendar.active .icon-calendar-0:before,
+	.icon-calendar.active .icon-calendar-0:after {
+		background-color: $uni-primary;
+	}
+
+	.uni-filter-dropdown {
+		position: relative;
+		font-weight: normal;
+	}
+
+	.dropdown-popup {
+		position: absolute;
+		top: 100%;
+		background-color: #fff;
+		box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
+		min-width: 150px;
+		z-index: 1000;
+	}
+
+	.dropdown-popup-left {
+		left: 0;
+	}
+
+	.dropdown-popup-right {
+		right: 0;
+	}
+
+	.uni-dropdown-cover {
+		position: fixed;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		background-color: transparent;
+		z-index: 100;
+	}
+
+	.list {
+		margin-top: 5px;
+		margin-bottom: 5px;
+	}
+
+	.list-item {
+		padding: 5px 10px;
+		text-align: left;
+	}
+
+	.list-item:hover {
+		background-color: #f0f0f0;
+	}
+
+	.check {
+		margin-right: 5px;
+	}
+
+	.search-area {
+		padding: 10px;
+	}
+
+	.search-input {
+		font-size: 12px;
+		border: 1px solid #f0f0f0;
+		border-radius: 3px;
+		padding: 2px 5px;
+		min-width: 150px;
+		text-align: left;
+	}
+
+	.input-label {
+		margin: 10px 10px 5px 10px;
+		text-align: left;
+	}
+
+	.input {
+		font-size: 12px;
+		border: 1px solid #f0f0f0;
+		border-radius: 3px;
+		margin: 10px;
+		padding: 2px 5px;
+		min-width: 150px;
+		text-align: left;
+	}
+
+	.opera-area {
+		cursor: default;
+		border-top: 1px solid #ddd;
+		padding: 5px;
+	}
+
+	.opera-area .btn {
+		font-size: 12px;
+		border-radius: 3px;
+		margin: 5px;
+		padding: 4px 4px;
+	}
+
+	.btn-default {
+		border: 1px solid #ddd;
+	}
+
+	.btn-default.disable {
+		border-color: transparent;
+	}
+
+	.btn-submit {
+		background-color: $uni-primary;
+		color: #ffffff;
+	}
+</style>

+ 295 - 0
uni_modules/uni-table/components/uni-th/uni-th.vue

@@ -0,0 +1,295 @@
+<template>
+	<!-- #ifdef H5 -->
+	<th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }">
+		<view class="uni-table-th-row">
+			<view class="uni-table-th-content" :style="{ 'justify-content': contentAlign }" @click="sort">
+				<slot></slot>
+				<view v-if="sortable" class="arrow-box">
+					<text class="arrow up" :class="{ active: ascending }" @click.stop="ascendingFn"></text>
+					<text class="arrow down" :class="{ active: descending }" @click.stop="descendingFn"></text>
+				</view>
+			</view>
+			<dropdown v-if="filterType || filterData.length" :filterDefaultValue="filterDefaultValue" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown>
+		</view>
+	</th>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"><slot></slot></view>
+	<!-- #endif -->
+</template>
+
+<script>
+	// #ifdef H5
+	import dropdown from './filter-dropdown.vue'
+	// #endif
+/**
+ * Th 表头
+ * @description 表格内的表头单元格组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+ * @property {Number | String} 	width 	单元格宽度(支持纯数字、携带单位px或rpx)
+ * @property {Boolean} 	sortable 					是否启用排序
+ * @property {Number} 	align = [left|center|right]	单元格对齐方式
+ * @value left   	单元格文字左侧对齐
+ * @value center	单元格文字居中
+ * @value right		单元格文字右侧对齐
+ * @property {Array}	filterData 筛选数据
+ * @property {String}	filterType	[search|select] 筛选类型
+ * @value search	关键字搜素
+ * @value select	条件选择
+ * @event {Function} sort-change 排序触发事件
+ */
+export default {
+	name: 'uniTh',
+	options: {
+		// #ifdef MP-TOUTIAO
+		virtualHost: false,
+		// #endif
+		// #ifndef MP-TOUTIAO
+		virtualHost: true
+		// #endif
+	},
+	components: {
+		// #ifdef H5
+		dropdown
+		// #endif
+	},
+	emits:['sort-change','filter-change'],
+	props: {
+		width: {
+			type: [String, Number],
+			default: ''
+		},
+		align: {
+			type: String,
+			default: 'left'
+		},
+		rowspan: {
+			type: [Number, String],
+			default: 1
+		},
+		colspan: {
+			type: [Number, String],
+			default: 1
+		},
+		sortable: {
+			type: Boolean,
+			default: false
+		},
+		filterType: {
+			type: String,
+			default: ""
+		},
+		filterData: {
+			type: Array,
+			default () {
+				return []
+			}
+		},
+		filterDefaultValue: {
+			type: [Array,String],
+			default () {
+				return ""
+			}
+		}
+	},
+	data() {
+		return {
+			border: false,
+			ascending: false,
+			descending: false
+		}
+	},
+	computed: {
+		// 根据props中的width属性 自动匹配当前th的宽度(px)
+		customWidth(){
+			if(typeof this.width === 'number'){
+				return this.width
+			} else if(typeof this.width === 'string') {
+				let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g)
+				let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g)
+				let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g)
+				if (this.width.match(regexHaveUnitPx) !== null) { // 携带了 px
+					return this.width.replace('px', '')
+				} else if (this.width.match(regexHaveUnitRpx) !== null) { // 携带了 rpx
+					let numberRpx = Number(this.width.replace('rpx', ''))
+					// #ifdef MP-WEIXIN
+					let widthCoe = uni.getWindowInfo().screenWidth / 750
+					// #endif
+					// #ifndef MP-WEIXIN
+					let widthCoe = uni.getSystemInfoSync().screenWidth / 750
+					// #endif
+					return Math.round(numberRpx * widthCoe)
+				} else if (this.width.match(regexHaveNotUnit) !== null) { // 未携带 rpx或px 的纯数字 String
+					return this.width
+				} else { // 不符合格式
+					return ''
+				}
+			} else {
+				return ''
+			}
+		},
+		contentAlign() {
+			let align = 'left'
+			switch (this.align) {
+				case 'left':
+					align = 'flex-start'
+					break
+				case 'center':
+					align = 'center'
+					break
+				case 'right':
+					align = 'flex-end'
+					break
+			}
+			return align
+		}
+	},
+	created() {
+		this.root = this.getTable('uniTable')
+		this.rootTr = this.getTable('uniTr')
+		this.rootTr.minWidthUpdate(this.customWidth ? this.customWidth : 140)
+		this.border = this.root.border
+		this.root.thChildren.push(this)
+	},
+	methods: {
+		sort() {
+			if (!this.sortable) return
+			this.clearOther()
+			if (!this.ascending && !this.descending) {
+				this.ascending = true
+				this.$emit('sort-change', { order: 'ascending' })
+				return
+			}
+			if (this.ascending && !this.descending) {
+				this.ascending = false
+				this.descending = true
+				this.$emit('sort-change', { order: 'descending' })
+				return
+			}
+
+			if (!this.ascending && this.descending) {
+				this.ascending = false
+				this.descending = false
+				this.$emit('sort-change', { order: null })
+			}
+		},
+		ascendingFn() {
+			this.clearOther()
+			this.ascending = !this.ascending
+			this.descending = false
+			this.$emit('sort-change', { order: this.ascending ? 'ascending' : null })
+		},
+		descendingFn() {
+			this.clearOther()
+			this.descending = !this.descending
+			this.ascending = false
+			this.$emit('sort-change', { order: this.descending ? 'descending' : null })
+		},
+		clearOther() {
+			this.root.thChildren.map(item => {
+				if (item !== this) {
+					item.ascending = false
+					item.descending = false
+				}
+				return item
+			})
+		},
+		ondropdown(e) {
+			this.$emit("filter-change", e)
+		},
+		/**
+		 * 获取父元素实例
+		 */
+		getTable(name) {
+			let parent = this.$parent
+			let parentName = parent.$options.name
+			while (parentName !== name) {
+				parent = parent.$parent
+				if (!parent) return false
+				parentName = parent.$options.name
+			}
+			return parent
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+$uni-primary: #007aff !default;
+
+.uni-table-th {
+	padding: 12px 10px;
+	/* #ifndef APP-NVUE */
+	display: table-cell;
+	box-sizing: border-box;
+	/* #endif */
+	font-size: 14px;
+	font-weight: bold;
+	color: #909399;
+	border-bottom: 1px $border-color solid;
+}
+
+.uni-table-th-row {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+}
+
+.table--border {
+	border-right: 1px $border-color solid;
+}
+.uni-table-th-content {
+	display: flex;
+	align-items: center;
+	flex: 1;
+}
+.arrow-box {
+}
+.arrow {
+	display: block;
+	position: relative;
+	width: 10px;
+	height: 8px;
+	// border: 1px red solid;
+	left: 5px;
+	overflow: hidden;
+	cursor: pointer;
+}
+.down {
+	top: 3px;
+	::after {
+		content: '';
+		width: 8px;
+		height: 8px;
+		position: absolute;
+		left: 2px;
+		top: -5px;
+		transform: rotate(45deg);
+		background-color: #ccc;
+	}
+	&.active {
+		::after {
+			background-color: $uni-primary;
+		}
+	}
+}
+.up {
+	::after {
+		content: '';
+		width: 8px;
+		height: 8px;
+		position: absolute;
+		left: 2px;
+		top: 5px;
+		transform: rotate(45deg);
+		background-color: #ccc;
+	}
+	&.active {
+		::after {
+			background-color: $uni-primary;
+		}
+	}
+}
+</style>

+ 0 - 0
uni_modules/uni-table/components/uni-thead/uni-thead.vue


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov