Browse Source

no message

xiaoxin 3 years ago
parent
commit
29d69f002e
100 changed files with 10321 additions and 399 deletions
  1. 6 1
      main.js
  2. 5 0
      node_modules/.package-lock.json
  3. 11 0
      package-lock.json
  4. 1 0
      package.json
  5. 46 0
      pages.json
  6. 9 5
      pages/index/index.vue
  7. 21 0
      pagesRepairs/addressBook/addressBook.vue
  8. 160 0
      pagesRepairs/box/box.vue
  9. 45 0
      pagesRepairs/components/floatButton.vue
  10. 296 0
      pagesRepairs/components/recording.vue
  11. 402 0
      pagesRepairs/components/recording2.vue
  12. 41 0
      pagesRepairs/components/tabbar.vue
  13. 87 0
      pagesRepairs/evaluate/evaluate.vue
  14. 125 0
      pagesRepairs/home/home.vue
  15. 18 0
      pagesRepairs/index/index.vue
  16. 26 0
      pagesRepairs/management/management.vue
  17. 113 0
      pagesRepairs/message/message.vue
  18. 583 0
      pagesRepairs/myRepairs/myRepairs.vue
  19. 341 0
      pagesRepairs/order/order.vue
  20. 21 0
      pagesRepairs/pending/pending.vue
  21. 215 0
      pagesRepairs/repairDetails/repairDetails.vue
  22. 437 0
      pagesRepairs/repairs/repairs.vue
  23. 138 0
      pagesRepairs/selectArea/selectArea.vue
  24. 111 0
      pagesRepairs/selectGoods/selectGoods.vue
  25. 27 0
      pagesRepairs/util/api.js
  26. 97 0
      pagesRepairs/util/imageCompress.js
  27. 27 0
      pagesRepairs/waitAllot/waitAllot.vue
  28. BIN
      static/images/repairsImg/eye.png
  29. BIN
      static/images/repairsImg/message.png
  30. BIN
      static/images/repairsImg/msg.png
  31. BIN
      static/images/repairsImg/myRepairs-active.png
  32. BIN
      static/images/repairsImg/myRepairs.png
  33. BIN
      static/images/repairsImg/order.png
  34. BIN
      static/images/repairsImg/phone.png
  35. BIN
      static/images/repairsImg/repairs-active.png
  36. BIN
      static/images/repairsImg/repairs.png
  37. BIN
      static/images/repairsImg/right.png
  38. BIN
      static/images/repairsImg/voice.png
  39. 74 0
      uni_modules/qiun-data-charts/changelog.md
  40. 37 26
      uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue
  41. 13 8
      uni_modules/qiun-data-charts/js_sdk/u-charts/config-ucharts.js
  42. 727 318
      uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.js
  43. 1 1
      uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.min.js
  44. 5 8
      uni_modules/qiun-data-charts/package.json
  45. 14 32
      uni_modules/qiun-data-charts/readme.md
  46. 67 0
      uni_modules/uni-file-picker/changelog.md
  47. 224 0
      uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js
  48. 667 0
      uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue
  49. 325 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue
  50. 292 0
      uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue
  51. 109 0
      uni_modules/uni-file-picker/components/uni-file-picker/utils.js
  52. 83 0
      uni_modules/uni-file-picker/package.json
  53. 11 0
      uni_modules/uni-file-picker/readme.md
  54. 18 0
      uni_modules/uni-notice-bar/changelog.md
  55. 449 0
      uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue
  56. 87 0
      uni_modules/uni-notice-bar/package.json
  57. 13 0
      uni_modules/uni-notice-bar/readme.md
  58. 25 0
      uni_modules/uni-rate/changelog.md
  59. 361 0
      uni_modules/uni-rate/components/uni-rate/uni-rate.vue
  60. 88 0
      uni_modules/uni-rate/package.json
  61. 12 0
      uni_modules/uni-rate/readme.md
  62. 7 0
      uni_modules/uv-badge/changelog.md
  63. 73 0
      uni_modules/uv-badge/components/uv-badge/props.js
  64. 176 0
      uni_modules/uv-badge/components/uv-badge/uv-badge.vue
  65. 87 0
      uni_modules/uv-badge/package.json
  66. 11 0
      uni_modules/uv-badge/readme.md
  67. 15 0
      uni_modules/uv-icon/changelog.md
  68. 157 0
      uni_modules/uv-icon/components/uv-icon/icons.js
  69. 90 0
      uni_modules/uv-icon/components/uv-icon/props.js
  70. 663 0
      uni_modules/uv-icon/components/uv-icon/uniicons.css
  71. 220 0
      uni_modules/uv-icon/components/uv-icon/uv-icon.vue
  72. BIN
      uni_modules/uv-icon/components/uv-icon/uvicons.ttf
  73. 83 0
      uni_modules/uv-icon/package.json
  74. 11 0
      uni_modules/uv-icon/readme.md
  75. 5 0
      uni_modules/uv-link/changelog.md
  76. 40 0
      uni_modules/uv-link/components/uv-link/props.js
  77. 83 0
      uni_modules/uv-link/components/uv-link/uv-link.vue
  78. 87 0
      uni_modules/uv-link/package.json
  79. 11 0
      uni_modules/uv-link/readme.md
  80. 7 0
      uni_modules/uv-safe-bottom/changelog.md
  81. 67 0
      uni_modules/uv-safe-bottom/components/uv-safe-bottom/uv-safe-bottom.vue
  82. 87 0
      uni_modules/uv-safe-bottom/package.json
  83. 11 0
      uni_modules/uv-safe-bottom/readme.md
  84. 10 0
      uni_modules/uv-steps/changelog.md
  85. 25 0
      uni_modules/uv-steps/components/uv-steps-item/props.js
  86. 347 0
      uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue
  87. 40 0
      uni_modules/uv-steps/components/uv-steps/props.js
  88. 83 0
      uni_modules/uv-steps/components/uv-steps/uv-steps.vue
  89. 89 0
      uni_modules/uv-steps/package.json
  90. 11 0
      uni_modules/uv-steps/readme.md
  91. 11 0
      uni_modules/uv-tabbar/changelog.md
  92. 40 0
      uni_modules/uv-tabbar/components/uv-tabbar-item/props.js
  93. 146 0
      uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue
  94. 50 0
      uni_modules/uv-tabbar/components/uv-tabbar/props.js
  95. 146 0
      uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue
  96. 90 0
      uni_modules/uv-tabbar/package.json
  97. 11 0
      uni_modules/uv-tabbar/readme.md
  98. 7 0
      uni_modules/uv-text/changelog.md
  99. 113 0
      uni_modules/uv-text/components/uv-text/props.js
  100. 0 0
      uni_modules/uv-text/components/uv-text/uv-text.vue

+ 6 - 1
main.js

@@ -12,6 +12,10 @@ import {
 	myRequest_clockIn
 } from "./pagesClockIn/util/api"
 
+import {
+	myRequest_repairs
+} from "./pagesRepairs/util/api.js"
+
 import dropDown from '@/uni_modules/zwx-dropDown/components/zwx-dropDown/zwx-dropDown'
 
 Vue.component("dropDown", dropDown)
@@ -23,6 +27,7 @@ Vue.prototype.$getOrderId = getOrderId
 Vue.prototype.$store = store
 
 Vue.prototype.$myRequest_clockIn = myRequest_clockIn
+Vue.prototype.$myRequest_repairs = myRequest_repairs
 
 Vue.config.productionTip = false
 
@@ -31,4 +36,4 @@ App.mpType = 'app'
 const app = new Vue({
 	...App
 })
-app.$mount()
+app.$mount()

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

@@ -3,6 +3,11 @@
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
+    "node_modules/@qiun/vue-ucharts": {
+      "version": "2.5.0-20230101",
+      "resolved": "https://registry.npmmirror.com/@qiun/vue-ucharts/-/vue-ucharts-2.5.0-20230101.tgz",
+      "integrity": "sha512-HZ4q6CBjzheAmocr//jY2nV3wO2Xu+2CX6NjwUD+uM1Jo/ewlnNIL2TiRRqtwMBhyaIoJh4nCzmnoeT7kUvlxA=="
+    },
     "node_modules/crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.1.1.tgz",

+ 11 - 0
package-lock.json

@@ -5,10 +5,16 @@
   "packages": {
     "": {
       "dependencies": {
+        "@qiun/vue-ucharts": "^2.5.0-20230101",
         "crypto-js": "^4.1.1",
         "wxmp-rsa": "^2.1.0"
       }
     },
+    "node_modules/@qiun/vue-ucharts": {
+      "version": "2.5.0-20230101",
+      "resolved": "https://registry.npmmirror.com/@qiun/vue-ucharts/-/vue-ucharts-2.5.0-20230101.tgz",
+      "integrity": "sha512-HZ4q6CBjzheAmocr//jY2nV3wO2Xu+2CX6NjwUD+uM1Jo/ewlnNIL2TiRRqtwMBhyaIoJh4nCzmnoeT7kUvlxA=="
+    },
     "node_modules/crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.1.1.tgz",
@@ -21,6 +27,11 @@
     }
   },
   "dependencies": {
+    "@qiun/vue-ucharts": {
+      "version": "2.5.0-20230101",
+      "resolved": "https://registry.npmmirror.com/@qiun/vue-ucharts/-/vue-ucharts-2.5.0-20230101.tgz",
+      "integrity": "sha512-HZ4q6CBjzheAmocr//jY2nV3wO2Xu+2CX6NjwUD+uM1Jo/ewlnNIL2TiRRqtwMBhyaIoJh4nCzmnoeT7kUvlxA=="
+    },
     "crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.1.1.tgz",

+ 1 - 0
package.json

@@ -1,5 +1,6 @@
 {
   "dependencies": {
+    "@qiun/vue-ucharts": "^2.5.0-20230101",
     "crypto-js": "^4.1.1",
     "wxmp-rsa": "^2.1.0"
   }

+ 46 - 0
pages.json

@@ -136,6 +136,52 @@
 			}]
 		},
 		{
+			"root": "pagesRepairs",
+			"pages": [{
+				"path": "index/index",
+				"style": {
+					"navigationBarTitleText": "授权中",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "box/box",
+				"style": {
+					"navigationBarTitleText": "首页",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "message/message",
+				"style": {
+					"navigationBarTitleText": "消息中心",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "selectArea/selectArea",
+				"style": {
+					"navigationBarTitleText": "区域选择",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "selectGoods/selectGoods",
+				"style": {
+					"navigationBarTitleText": "物品选择",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "evaluate/evaluate",
+				"style": {
+					"navigationBarTitleText": "评价",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "repairDetails/repairDetails",
+				"style": {
+					"navigationBarTitleText": "报修详情",
+					"enablePullDownRefresh": false
+				}
+			}]
+		},
+		{
 			"root": "pagesClockIn",
 			"pages": [{
 					"path": "home/home",

+ 9 - 5
pages/index/index.vue

@@ -15,6 +15,10 @@
 					<image src="../../static/images/air.png" mode=""></image>
 					<text>共享空调</text>
 				</navigator>
+				<navigator :url="'/pagesRepairs/index/index'" open-type="navigate" class="menu_item">
+					<image src="../../static/images/money.png" mode=""></image>
+					<text>报修</text>
+				</navigator>
 				<!-- <navigator :url="'/pagesBus/bus/bus'" open-type="navigate" class="menu_item">
 					<image src="../../static/images/bus.png" mode=""></image>
 					<text>校车预约</text>
@@ -198,7 +202,7 @@ export default {
 					content: '授权:请先领取校园卡、并激活!',
 					// cancelText: '取消',
 					confirmText: '领取',
-					success: res1 => {
+					success: (res1) => {
 						if (res1.confirm) {
 							uni.navigateTo({
 								url: '../qr_code/qr_code'
@@ -249,7 +253,7 @@ export default {
 		/**
 		 * 授权登陆成功回调
 		 */
-		login_success_callback: function({ detail }) {
+		login_success_callback: function ({ detail }) {
 			const { wxcode = '' } = detail
 
 			this.validation_failed = false
@@ -335,7 +339,7 @@ export default {
 		 */
 		getCode() {
 			uni.login({
-				success: res => {
+				success: (res) => {
 					// console.log('reshui', res);
 					if (res.code) {
 						// 检查用户是否存在第三方库
@@ -376,7 +380,7 @@ export default {
 					icon: 'success',
 					title: '授权成功',
 					duration: 800,
-					success: res => {
+					success: (res) => {
 						if (this.from != 0 || this.from != '0') {
 							uni.navigateBack({
 								delta: 1
@@ -388,7 +392,7 @@ export default {
 				uni.showToast({
 					icon: 'none',
 					title: '授权失败:' + res.data.msg,
-					success: res => {}
+					success: (res) => {}
 				})
 			}
 		}

+ 21 - 0
pagesRepairs/addressBook/addressBook.vue

@@ -0,0 +1,21 @@
+<template>
+	<view class="container">
+		<view class="">通讯录</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	background-color: salmon;
+}
+</style>

+ 160 - 0
pagesRepairs/box/box.vue

@@ -0,0 +1,160 @@
+<template>
+	<view class="container">
+		<!-- 首页 -->
+		<Home v-if="show === 'home'" />
+		<!-- 工单管理 -->
+		<Management v-if="show === 'management'" />
+		<!-- 接单池 -->
+		<Order v-if="show === 'order'" />
+		<!-- 通讯录 -->
+		<AddressBook v-if="show === 'addressBook'" />
+		<!-- 待分配单 -->
+		<WaitAllot v-if="show === 'waitAllot'" />
+		<!-- 待处理池 -->
+		<Pending v-if="show === 'pending'" />
+		<!-- 报修 -->
+		<Repairs v-if="show === 'repairs'" />
+		<!-- 我的报修 -->
+		<MyRepairs v-if="show === 'myRepairs'" />
+		<!-- 底部导航栏 -->
+		<Tabbar :list="list" @changeRouter="handleChangeRouter" />
+	</view>
+</template>
+
+<script>
+import Home from '../home/home.vue'
+import Management from '../management/management.vue'
+import Order from '../order/order.vue'
+import AddressBook from '../addressBook/addressBook.vue'
+import WaitAllot from '../waitAllot/waitAllot.vue'
+import Pending from '../pending/pending.vue'
+import Repairs from '../repairs/repairs.vue'
+import MyRepairs from '../myRepairs/myRepairs.vue'
+import Tabbar from '../components/tabbar.vue'
+
+export default {
+	components: {
+		Home,
+		Management,
+		Order,
+		AddressBook,
+		WaitAllot,
+		Pending,
+		Repairs,
+		MyRepairs,
+		Tabbar
+	},
+	mounted() {
+		this.list = this.userList
+		this.show = this.list[0].show
+		uni.setNavigationBarTitle({
+			title: this.list[0].text
+		})
+	},
+	data() {
+		return {
+			list: [],
+			show: 'home',
+			// 用户路由
+			userList: [
+				{
+					text: '报修',
+					imgUrl: '../../static/images/repairsImg/repairs.png',
+					imgUrlActive: '../../static/images/repairsImg/repairs-active.png',
+					show: 'repairs'
+				},
+				{
+					text: '我的报修',
+					imgUrl: '../../static/images/repairsImg/myRepairs.png',
+					imgUrlActive: '../../static/images/repairsImg/myRepairs-active.png',
+					show: 'myRepairs'
+				}
+			],
+			// 师傅路由
+			workerList: [
+				{
+					text: '首页',
+					icon: 'home',
+					show: 'home'
+				},
+				{
+					text: '工单管理',
+					icon: 'list-dot',
+					show: 'management'
+				},
+				{
+					text: '待分配单',
+					icon: 'clock',
+					show: 'waitAllot'
+				},
+				{
+					text: '接单池',
+					icon: 'order',
+					show: 'order'
+				}
+			],
+			// 后勤路由
+			logisticsList: [
+				{
+					text: '首页',
+					icon: 'home',
+					show: 'home'
+				},
+				{
+					text: '工单管理',
+					icon: 'list-dot',
+					show: 'management'
+				},
+				{
+					text: '待处理池',
+					icon: 'warning',
+					show: 'pending'
+				},
+				{
+					text: '通讯录',
+					icon: 'phone',
+					show: 'addressBook'
+				}
+			],
+			// 管理者路由
+			adminList: [
+				{
+					text: '首页',
+					icon: 'home',
+					show: 'home'
+				},
+				{
+					text: '工单管理',
+					icon: 'list-dot',
+					show: 'management'
+				},
+				{
+					text: '待处理池',
+					icon: 'warning',
+					show: 'pending'
+				},
+				{
+					text: '通讯录',
+					icon: 'phone',
+					show: 'addressBook'
+				}
+			]
+		}
+	},
+	methods: {
+		handleChangeRouter(show, text) {
+			this.show = show
+			uni.setNavigationBarTitle({
+				title: text
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+}
+</style>

+ 45 - 0
pagesRepairs/components/floatButton.vue

@@ -0,0 +1,45 @@
+<template>
+	<view>
+		<movable-area class="movable-area">
+			<movable-view class="movable-view" :x="x" :y="y" direction="all" @click="handleGoPage">消息通知</movable-view>
+		</movable-area>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			x: 300, //x坐标
+			y: 400 //y坐标
+		}
+	},
+	methods: {
+		// 点击悬浮按钮回调
+		handleGoPage() {
+			uni.navigateTo({
+				url: '/pagesRepairs/message/message'
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$all_width: 96rpx;
+$all_height: 96rpx;
+
+.movable-area {
+	height: calc(100vh - 100rpx);
+	width: 750rpx;
+	top: 0;
+	position: fixed;
+	pointer-events: none; //此处要加,鼠标事件可以渗透
+	.movable-view {
+		background-color: rgba(000, 000, 000, 0.5);
+		width: $all_width;
+		height: $all_height;
+		pointer-events: auto; //恢复鼠标事件
+	}
+}
+</style>

+ 296 - 0
pagesRepairs/components/recording.vue

@@ -0,0 +1,296 @@
+<template>
+	<view class="record-layer">
+		<view class="record-box">
+			<view class="record-btn-layer" v-if="tempFilePath == ''">
+				<button class="record-btn" :class="longPress == '1' ? 'record-btn-1' : 'record-btn-2'" @longpress="longpressBtn()" @touchend="touchendBtn()">
+					<image src="../../static/image/logo.png" />
+					<text>{{ longPress == '1' ? '按住说话' : '说话中...' }}</text>
+				</button>
+			</view>
+			<view class="record-btn-layer" v-else>
+				<button class="record-btn" @longpress="delShow = true" @click="playBtn()" :class="playStatus == '1' ? 'record-btn-2' : 'record-btn-1'">
+					<image src="../../static/image/logo.png" />
+					<text>{{ playStatus == '1' ? count + 's' : '点击播放' }}</text>
+				</button>
+			</view>
+			<!-- 语音音阶动画 -->
+			<view class="prompt-layer prompt-layer-1" v-if="longPress == '2'">
+				<view class="prompt-loader">
+					<view class="em" v-for="(item, index) in 15" :key="index"></view>
+				</view>
+				<text class="p">{{ '剩余:' + count + 's' }}</text>
+				<text class="span">松手结束录音</text>
+			</view>
+			<!-- 删除 -->
+			<view class="prompt-layer prompt-layer-2" v-if="delShow" @click.stop="delBtn()">
+				<text>删除</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+const recorderManager = uni.getRecorderManager()
+const innerAudioContext = uni.createInnerAudioContext()
+var init // 录制时长计时器
+var timer // 播放 录制倒计时
+export default {
+	data() {
+		return {
+			count: null, // 录制倒计时
+			longPress: '1', // 1显示 按住说话 2显示 说话中
+			delShow: false, // 删除提示框显示隐藏
+			time: 0, //录音时长
+			duration: 60000, //录音最大值ms 60000/1分钟
+			tempFilePath: '', //音频路径
+			playStatus: 0 //录音播放状态 0:未播放 1:正在播放
+		}
+	},
+	methods: {
+		// 倒计时
+		countdown(val) {
+			this.count = Number(val)
+			timer = setInterval(() => {
+				if (this.count > 0) {
+					this.count--
+				} else {
+					this.longPress = '1'
+					clearInterval(timer)
+				}
+			}, 1000)
+		},
+		// 长按录音事件
+		longpressBtn() {
+			this.longPress = '2'
+			this.countdown(60) // 倒计时
+			clearInterval(init) // 清除定时器
+			recorderManager.onStop((res) => {
+				this.tempFilePath = res.tempFilePath
+				this.recordingTimer(this.time)
+			})
+			const options = {
+				duration: this.duration, // 指定录音的时长,单位 ms
+				sampleRate: 16000, // 采样率
+				numberOfChannels: 1, // 录音通道数
+				encodeBitRate: 96000, // 编码码率
+				format: 'mp3', // 音频格式,有效值 aac/mp3
+				frameSize: 10 // 指定帧大小,单位 KB
+			}
+			this.recordingTimer()
+			recorderManager.start(options)
+			// 监听音频开始事件
+			recorderManager.onStart((res) => {
+				console.log(res)
+			})
+		},
+		// 长按松开录音事件
+		touchendBtn() {
+			this.longPress = '1'
+			recorderManager.onStop((res) => {
+				this.tempFilePath = res.tempFilePath
+			})
+			this.recordingTimer(this.time)
+			recorderManager.stop()
+		},
+		recordingTimer(time) {
+			if (time == undefined) {
+				// 将计时器赋值给init
+				init = setInterval(() => {
+					this.time++
+				}, 1000)
+			} else {
+				clearInterval(init)
+			}
+		},
+		// 删除录音
+		delBtn() {
+			this.delShow = false
+			this.time = 0
+			this.tempFilePath = ''
+			this.playStatus = 0
+			innerAudioContext.stop()
+		},
+		// 播放
+		playBtn() {
+			innerAudioContext.src = this.tempFilePath
+			//在ios下静音时播放没有声音,默认为true,改为false就好了。
+			// innerAudioContext.obeyMuteSwitch = false
+			//点击播放
+			if (this.playStatus == 0) {
+				this.playStatus = 1
+				innerAudioContext.play()
+				this.countdown(this.time) // 倒计时
+			} else {
+				this.playStatus = 0
+				innerAudioContext.pause()
+			}
+			// //播放结束
+			innerAudioContext.onEnded(() => {
+				this.playStatus = 0
+				innerAudioContext.stop()
+			})
+		}
+	}
+}
+</script>
+
+<style scoped>
+/* 语音录制开始--------------------------------------------------------------------- */
+.record-layer {
+	margin: auto;
+	width: 50%;
+	padding: 300px 0;
+	box-sizing: border-box;
+}
+.record-box {
+	width: 100%;
+	position: relative;
+}
+.record-btn-layer {
+	width: 100%;
+}
+.record-btn-layer button::after {
+	border: none;
+}
+.record-btn-layer button {
+	font-size: 14px;
+	line-height: 38px;
+	width: 100%;
+	height: 38px;
+	border-radius: 8px;
+	text-align: center;
+	background: #ffd300;
+}
+.record-btn-layer button image {
+	width: 16px;
+	height: 16px;
+	margin-right: 4px;
+	vertical-align: middle;
+}
+.record-btn-layer .record-btn-2 {
+	background: rgba(255, 211, 0, 0.2);
+}
+/* 提示小弹窗 */
+.prompt-layer {
+	border-radius: 8px;
+	background: #ffd300;
+	padding: 8px 16px;
+	box-sizing: border-box;
+	position: absolute;
+	left: 50%;
+	transform: translateX(-50%);
+}
+.prompt-layer::after {
+	content: '';
+	display: block;
+	border: 6px solid rgba(0, 0, 0, 0);
+	border-top-color: rgba(255, 211, 0, 1);
+	position: absolute;
+	bottom: -10px;
+	left: 50%;
+	transform: translateX(-50%);
+}
+.prompt-layer-1 {
+	font-size: 12px;
+	width: 128px;
+	text-align: center;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	top: -80px;
+}
+.prompt-layer-1 .p {
+	color: #000000;
+}
+.prompt-layer-1 .span {
+	color: rgba(0, 0, 0, 0.6);
+}
+.prompt-loader .em {
+}
+/* 语音音阶------------- */
+.prompt-loader {
+	width: 96px;
+	height: 20px;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	margin-bottom: 6px;
+}
+.prompt-loader .em {
+	display: block;
+	background: #333333;
+	width: 1px;
+	height: 10%;
+	margin-right: 2.5px;
+	float: left;
+}
+.prompt-loader .em:last-child {
+	margin-right: 0px;
+}
+.prompt-loader .em:nth-child(1) {
+	animation: load 2.5s 1.4s infinite linear;
+}
+.prompt-loader .em:nth-child(2) {
+	animation: load 2.5s 1.2s infinite linear;
+}
+.prompt-loader .em:nth-child(3) {
+	animation: load 2.5s 1s infinite linear;
+}
+.prompt-loader .em:nth-child(4) {
+	animation: load 2.5s 0.8s infinite linear;
+}
+.prompt-loader .em:nth-child(5) {
+	animation: load 2.5s 0.6s infinite linear;
+}
+.prompt-loader .em:nth-child(6) {
+	animation: load 2.5s 0.4s infinite linear;
+}
+.prompt-loader .em:nth-child(7) {
+	animation: load 2.5s 0.2s infinite linear;
+}
+.prompt-loader .em:nth-child(8) {
+	animation: load 2.5s 0s infinite linear;
+}
+.prompt-loader .em:nth-child(9) {
+	animation: load 2.5s 0.2s infinite linear;
+}
+.prompt-loader .em:nth-child(10) {
+	animation: load 2.5s 0.4s infinite linear;
+}
+.prompt-loader .em:nth-child(11) {
+	animation: load 2.5s 0.6s infinite linear;
+}
+.prompt-loader .em:nth-child(12) {
+	animation: load 2.5s 0.8s infinite linear;
+}
+.prompt-loader .em:nth-child(13) {
+	animation: load 2.5s 1s infinite linear;
+}
+.prompt-loader .em:nth-child(14) {
+	animation: load 2.5s 1.2s infinite linear;
+}
+.prompt-loader .em:nth-child(15) {
+	animation: load 2.5s 1.4s infinite linear;
+}
+@keyframes load {
+	0% {
+		height: 10%;
+	}
+	50% {
+		height: 100%;
+	}
+	100% {
+		height: 10%;
+	}
+}
+/* 语音音阶-------------------- */
+.prompt-layer-2 {
+	top: -40px;
+}
+.prompt-layer-2 .text {
+	color: rgba(0, 0, 0, 1);
+	font-size: 12px;
+}
+/* 语音录制结束---------------------------------------------------------------- */
+</style>

+ 402 - 0
pagesRepairs/components/recording2.vue

@@ -0,0 +1,402 @@
+<template>
+	<view>
+		<view>
+			<view class="record-btn-layer">
+				<button
+					class="record-btn"
+					:class="longPress == '1' ? 'record-btn-1' : 'record-btn-2'"
+					:style="
+						VoiceTitle != '松开手指,取消发送' && longPress != '1'
+							? 'background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);'
+							: 'background-color: rgba(0, 0, 0, .5);color:white'
+					"
+					@longtap="longpressBtn"
+					@touchend="touchendBtn()"
+					@touchmove="handleTouchMove"
+					@touchstart="longpressBtn"
+				>
+					<image src="../../static/image/logo.png" />
+					<text>{{ VoiceText }}</text>
+				</button>
+			</view>
+			<!-- 语音音阶动画 -->
+			<view :class="VoiceTitle != '松开手指,取消发送' ? 'prompt-layer prompt-layer-1' : 'prompt-layer1 prompt-layer-1'" v-if="longPress == '2'">
+				<view class="prompt-loader">
+					<view class="em" v-for="(item, index) in 15" :key="index"></view>
+				</view>
+				<text class="span">{{ VoiceTitle }}</text>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+const recorderManager = uni.getRecorderManager()
+var init // 录制时长计时器
+var timer // 播放 录制倒计时
+export default {
+	data() {
+		return {
+			longPress: '1', // 1显示 按住 说话 2显示 说话中
+			delShow: false, // 删除提示框显示隐藏
+			time: 0, //录音时长
+			duration: 60000, //录音最大值ms 60000/1分钟
+			tempFilePath: '', //音频路径
+			startPoint: {}, //记录长按录音开始点信息,用于后面计算滑动距离。
+			sendLock: true, //发送锁,当为true时上锁,false时解锁发送
+			VoiceTitle: '松手结束录音',
+			recorderManager: uni.getRecorderManager(),
+			VoiceText: '按住 说话',
+			types: ''
+		}
+	},
+	props: ['type'],
+	created: function () {
+		this.types = this.type
+		console.log(this.type) //打印父组件传过来的参数
+	},
+	methods: {
+		// 长按录音事件
+		longpressBtn(e) {
+			this.startPoint = e.touches[0] //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
+			this.longPress = '2'
+			this.VoiceText = '说话中...'
+			recorderManager.onStop((res) => {
+				// console.log(res);
+				this.tempFilePath = res.tempFilePath
+			})
+			const options = {
+				duration: this.duration, // 指定录音的时长,单位 ms
+				sampleRate: 16000, // 采样率
+				numberOfChannels: 1, // 录音通道数
+				encodeBitRate: 96000, // 编码码率
+				format: 'mp3', // 音频格式,有效值 aac/mp3
+				frameSize: 10 // 指定帧大小,单位 KB
+			}
+			this.recorderManager.start(options)
+			// 监听音频开始事件
+			this.sendLock = false //长按时是不上锁的。
+			recorderManager.onStart((res) => {})
+		},
+		// 长按松开录音事件
+		touchendBtn() {
+			this.longPress = '1'
+			this.VoiceText = '按住 说话'
+			this.VoiceTitle = '松手结束录音'
+			recorderManager.onStop((res) => {
+				console.log(this.sendLock)
+				if (this.sendLock) {
+					//上锁不发送
+				} else {
+					//解锁发送,发送网络请求
+					if (res.duration < 1000)
+						wx.showToast({
+							title: '录音时间太短',
+							icon: 'none',
+							duration: 1000
+						})
+					else {
+						this.tempFilePath = res.tempFilePath
+						console.log(this.tempFilePath + '666')
+						// uploadFile({
+						// 	url: '/voice/VoiceControl',
+						// 	src: res.tempFilePath,
+						// }).then(res => {
+						// 	console.log(JSON.parse(res.data));
+						// })
+					}
+				}
+			})
+
+			// this.recordingTimer(this.time)
+			this.recorderManager.stop() //结束录音
+		},
+		// 删除录音
+		handleTouchMove(e) {
+			//touchmove时触发
+			var moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY //移动距离
+			if (Math.abs(moveLenght) > 70) {
+				// wx.showToast({
+				//       title: "松开手指,取消发送",
+				//       icon: "none",
+				//       duration: 60000
+				// });
+				// console.log("松开手指,取消发送");
+				this.VoiceTitle = '松开手指,取消发送'
+				this.VoiceText = '松开手指,取消发送'
+				this.delBtn()
+				this.sendLock = true //触发了上滑取消发送,上锁
+			} else {
+				// wx.showToast({
+				//       title: "正在录音,上划取消发送",
+				//       icon: "none",
+				//       duration: 60000
+				// });
+				this.VoiceTitle = '松手结束录音'
+				this.VoiceText = '松手结束录音'
+				this.sendLock = false //上划距离不足,依然可以发送,不上锁
+			}
+		},
+		delBtn() {
+			this.delShow = false
+			this.time = 0
+			this.tempFilePath = ''
+			// this.VoiceTitle = '松手结束录音'
+		}
+	}
+}
+</script>
+<style lang="scss">
+/* 语音录制开始--------------------------------------------------------------------- */
+.record-layer {
+	width: 91vw;
+	padding: 300px 0;
+	box-sizing: border-box;
+	height: 15vw;
+	position: fixed;
+	margin-left: 4vw;
+	z-index: 10;
+	bottom: 3vh;
+}
+
+.record-layer1 {
+	width: 100vw;
+	padding: 300px 0;
+	box-sizing: border-box;
+	height: 100vh;
+	position: fixed;
+	background-color: rgba(0, 0, 0, 0.6);
+	// padding: 0 4vw;
+	z-index: 10;
+	bottom: 0vh;
+}
+
+.record-box {
+	width: 100%;
+	position: relative;
+}
+.record-box1 {
+	width: 100%;
+	position: relative;
+	bottom: -83vh;
+	height: 17vh;
+}
+.record-btn-layer {
+	width: 100%;
+}
+
+.record-btn-layer button::after {
+	border: none;
+	transition: all 0.1s;
+}
+
+.record-btn-layer button {
+	font-size: 14px;
+	line-height: 50px;
+	width: 100%;
+	height: 50px;
+	border-radius: 8px;
+	text-align: center;
+	background: #ffd300;
+	transition: all 0.1s;
+}
+
+.record-btn-layer button image {
+	width: 16px;
+	height: 16px;
+	margin-right: 4px;
+	vertical-align: middle;
+	transition: all 0.3s;
+}
+.record-btn-layer .record-btn-1 {
+	background-image: linear-gradient(to right, #43e97b 0%, #38f9d7 100%);
+	color: #000000 !important;
+}
+
+.record-btn-layer .record-btn-2 {
+	border-radius: 168rpx 168rpx 0 0;
+	height: 17vh;
+	line-height: 17vh;
+	transition: all 0.3s;
+}
+
+/* 提示小弹窗 */
+.prompt-layer {
+	border-radius: 15px;
+	background: #95eb6c;
+	padding: 8px 16px;
+	box-sizing: border-box;
+	position: absolute;
+	left: 50%;
+	height: 11vh;
+	transform: translateX(-50%);
+	transition: all 0.3s;
+}
+
+.prompt-layer::after {
+	content: '';
+	display: block;
+	border: 12px solid rgba(0, 0, 0, 0);
+	border-radius: 10rpx;
+	border-top-color: #95eb6c;
+	position: absolute;
+	bottom: -46rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	transition: all 0.3s;
+}
+//取消动画
+.prompt-layer1 {
+	border-radius: 15px;
+	background: #fb5353;
+	padding: 8px 16px;
+	box-sizing: border-box;
+	position: absolute;
+	left: 50%;
+	height: 11vh;
+	transform: translateX(-50%);
+	transition: all 0.3s;
+}
+
+.prompt-layer1::after {
+	content: '';
+	display: block;
+	border: 12px solid rgba(0, 0, 0, 0);
+	border-radius: 10rpx;
+	border-top-color: #fb5353;
+	position: absolute;
+	bottom: -46rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	transition: all 0.3s;
+}
+.prompt-layer-1 {
+	font-size: 12px;
+	width: 150px;
+	text-align: center;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	top: -400rpx;
+}
+
+.prompt-layer-1 .p {
+	color: #000000;
+}
+
+.prompt-layer-1 .span {
+	color: rgba(0, 0, 0, 0.6);
+}
+
+.prompt-loader .em {
+}
+
+/* 语音音阶------------- */
+.prompt-loader {
+	width: 96px;
+	height: 20px;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	margin-bottom: 6px;
+}
+
+.prompt-loader .em {
+	display: block;
+	background: #333333;
+	width: 1px;
+	height: 10%;
+	margin-right: 2.5px;
+	float: left;
+}
+
+.prompt-loader .em:last-child {
+	margin-right: 0px;
+}
+
+.prompt-loader .em:nth-child(1) {
+	animation: load 2.5s 1.4s infinite linear;
+}
+
+.prompt-loader .em:nth-child(2) {
+	animation: load 2.5s 1.2s infinite linear;
+}
+
+.prompt-loader .em:nth-child(3) {
+	animation: load 2.5s 1s infinite linear;
+}
+
+.prompt-loader .em:nth-child(4) {
+	animation: load 2.5s 0.8s infinite linear;
+}
+
+.prompt-loader .em:nth-child(5) {
+	animation: load 2.5s 0.6s infinite linear;
+}
+
+.prompt-loader .em:nth-child(6) {
+	animation: load 2.5s 0.4s infinite linear;
+}
+
+.prompt-loader .em:nth-child(7) {
+	animation: load 2.5s 0.2s infinite linear;
+}
+
+.prompt-loader .em:nth-child(8) {
+	animation: load 2.5s 0s infinite linear;
+}
+
+.prompt-loader .em:nth-child(9) {
+	animation: load 2.5s 0.2s infinite linear;
+}
+
+.prompt-loader .em:nth-child(10) {
+	animation: load 2.5s 0.4s infinite linear;
+}
+
+.prompt-loader .em:nth-child(11) {
+	animation: load 2.5s 0.6s infinite linear;
+}
+
+.prompt-loader .em:nth-child(12) {
+	animation: load 2.5s 0.8s infinite linear;
+}
+
+.prompt-loader .em:nth-child(13) {
+	animation: load 2.5s 1s infinite linear;
+}
+
+.prompt-loader .em:nth-child(14) {
+	animation: load 2.5s 1.2s infinite linear;
+}
+
+.prompt-loader .em:nth-child(15) {
+	animation: load 2.5s 1.4s infinite linear;
+}
+
+@keyframes load {
+	0% {
+		height: 10%;
+	}
+
+	50% {
+		height: 100%;
+	}
+
+	100% {
+		height: 10%;
+	}
+}
+
+/* 语音音阶-------------------- */
+.prompt-layer-2 {
+	top: -40px;
+}
+
+.prompt-layer-2 .text {
+	color: rgba(0, 0, 0, 1);
+	font-size: 12px;
+}
+
+/* 语音录制结束---------------------------------------------------------------- */
+</style>

+ 41 - 0
pagesRepairs/components/tabbar.vue

@@ -0,0 +1,41 @@
+<template>
+	<view>
+		<uv-tabbar activeColor="#6FB6B8" :value="currentRouter" @change="change">
+			<uv-tabbar-item v-for="(item, index) in list" :key="index" :text="item.text">
+				<template v-slot:active-icon>
+					<image class="icon" :src="item.imgUrlActive"></image>
+				</template>
+				<template v-slot:inactive-icon>
+					<image class="icon" :src="item.imgUrl"></image>
+				</template>
+			</uv-tabbar-item>
+		</uv-tabbar>
+	</view>
+</template>
+
+<script>
+export default {
+	props: ['list'],
+	data() {
+		return {
+			currentRouter: 0
+		}
+	},
+	created() {},
+	mounted() {},
+	methods: {
+		// 导航栏切换回调
+		change(e) {
+			this.currentRouter = e
+			this.$emit('changeRouter', this.list[e].show, this.list[e].text)
+		}
+	}
+}
+</script>
+
+<style scoped>
+.icon {
+	width: 40rpx;
+	height: 40rpx;
+}
+</style>

+ 87 - 0
pagesRepairs/evaluate/evaluate.vue

@@ -0,0 +1,87 @@
+<template>
+	<view class="container">
+		<!-- 处理时效区域 -->
+		<view class="score">
+			<text>处理时效</text>
+			<uni-rate activeColor="#FF5733" size="30" v-model="value" @change="onChange" />
+		</view>
+
+		<!-- 服务评价区域 -->
+		<view class="evaluate">
+			服务评价
+			<textarea placeholder-style="color:#CCCCCC" placeholder="您宝贵的评价对我们很重要哦"></textarea>
+		</view>
+
+		<!-- 确认提交区域 -->
+		<view class="btn" @click="handleSub">确认提交</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			value: 2
+		}
+	},
+	methods: {
+		onChange(e) {
+			console.log('rate发生改变:' + JSON.stringify(e))
+		},
+		handleSub() {
+			uni.navigateBack(1)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	position: relative;
+	padding-left: 30rpx;
+	width: 100vw;
+	height: 100vh;
+
+	.score {
+		display: flex;
+		align-items: center;
+		height: 172rpx;
+		font-size: 28rpx;
+
+		text {
+			margin-right: 25rpx;
+		}
+	}
+
+	.evaluate {
+		display: flex;
+		font-size: 28rpx;
+
+		textarea {
+			box-sizing: border-box;
+			margin-left: 28rpx;
+			padding: 14rpx 20rpx;
+			width: 550rpx;
+			height: 298rpx;
+			font-size: 28rpx;
+			border-radius: 11rpx;
+			border: 1rpx solid #e6e6e6;
+		}
+	}
+
+	.btn {
+		position: absolute;
+		left: 30rpx;
+		bottom: 65rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 690rpx;
+		height: 100rpx;
+		color: #fff;
+		font-size: 32rpx;
+		border-radius: 12rpx;
+		background-color: #6fb6b8;
+	}
+}
+</style>

+ 125 - 0
pagesRepairs/home/home.vue

@@ -0,0 +1,125 @@
+<template>
+	<view class="container">
+		<view class="">首页</view>
+		<view class="charts">
+			<qiun-data-charts type="bar" :opts="opts" :chartData="chartData" />
+		</view>
+		<view class="table">
+			<uni-table border stripe emptyText="暂无更多数据">
+				<!-- 表头行 -->
+				<uni-tr>
+					<uni-th width="50" align="center">姓名</uni-th>
+					<uni-th width="50" align="center">工种</uni-th>
+					<uni-th width="80" align="center">状态</uni-th>
+					<uni-th width="80" align="center">是否值班</uni-th>
+				</uni-tr>
+				<!-- 表格数据行 -->
+				<uni-tr v-for="(item, index) in tableData" :key="index">
+					<uni-td>{{ item.name }}</uni-td>
+					<uni-td>{{ item.type }}</uni-td>
+					<uni-td>{{ item.status }}</uni-td>
+					<uni-td>{{ item.work }}</uni-td>
+				</uni-tr>
+			</uni-table>
+		</view>
+
+		<view class="more"></view>
+
+		<!-- 悬浮按钮区域 -->
+		<FloatButton />
+	</view>
+</template>
+
+<script>
+import FloatButton from '../components/floatButton.vue'
+
+export default {
+	components: {
+		FloatButton
+	},
+	data() {
+		return {
+			chartData: {
+				categories: ['张三', '李四', '王五', '老六', '刘八', '钱七'],
+				series: [
+					{
+						name: '好评数',
+						data: [35, 36, 31, 33, 13, 34]
+					},
+					{
+						name: '完成数',
+						data: [18, 27, 21, 24, 6, 28]
+					}
+				]
+			},
+			opts: {
+				color: ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'],
+				padding: [15, 30, 0, 5],
+				enableScroll: false,
+				legend: {},
+				xAxis: {
+					boundaryGap: 'justify',
+					disableGrid: false,
+					min: 0,
+					axisLine: false,
+					max: 40
+				},
+				yAxis: {},
+				extra: {
+					bar: {
+						type: 'group',
+						width: 30,
+						meterBorde: 1,
+						meterFillColor: '#FFFFFF',
+						activeBgColor: '#000000',
+						activeBgOpacity: 0.08,
+						linearType: 'custom',
+						barBorderCircle: true,
+						seriesGap: 2,
+						categoryGap: 2
+					}
+				}
+			},
+			tableData: [
+				{
+					name: '张三',
+					type: '电工',
+					status: 0,
+					work: 0
+				},
+				{
+					name: '张三三',
+					type: '泥工',
+					status: 0,
+					work: 0
+				}
+			]
+		}
+	},
+	mounted() {},
+	methods: {}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	background-color: salmon;
+
+	.charts {
+		width: 100%;
+		height: 600rpx;
+	}
+	.table {
+		margin: auto;
+		width: 90%;
+		color: #000;
+	}
+
+	.more {
+		height: 600rpx;
+		background-color: skyblue;
+	}
+}
+</style>

+ 18 - 0
pagesRepairs/index/index.vue

@@ -0,0 +1,18 @@
+<template>
+	<view>授权</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {}
+	},
+	mounted() {
+		uni.reLaunch({
+			url: '/pagesRepairs/box/box'
+		})
+	}
+}
+</script>
+
+<style lang="scss"></style>

+ 26 - 0
pagesRepairs/management/management.vue

@@ -0,0 +1,26 @@
+<template>
+	<view class="container">
+		<view class="">工单管理</view>
+		<Recording />
+	</view>
+</template>
+
+<script>
+import Recording from '../components/recording.vue'
+export default {
+	components: {
+		Recording
+	},
+	data() {
+		return {}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	background-color: salmon;
+}
+</style>

+ 113 - 0
pagesRepairs/message/message.vue

@@ -0,0 +1,113 @@
+<template>
+	<view class="container">
+		<!-- 每一个消息区域 -->
+		<view class="box" v-for="item in imgList" :key="item.id">
+			<view class="box_time">{{ item.time }}</view>
+			<view class="box_info">
+				<view class="info_circle" :class="{ while: item.type === false }"></view>
+				<view class="info_icon">
+					<img src="../../static/images/repairsImg/msg.png" />
+				</view>
+				<view class="info_msg">
+					<view class="msg_title">{{ item.title }}</view>
+					<view class="msg_order">订单号:{{ item.order }}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			imgList: [
+				{
+					id: 1,
+					type: false,
+					title: '订单已完成请尽快确认!',
+					order: '06521649496',
+					time: '2023-07-07  12:25:25'
+				},
+				{
+					id: 2,
+					type: true,
+					title: '请尽快确认!',
+					order: '06521649496',
+					time: '2023-08-07  12:25:25'
+				},
+				{
+					id: 3,
+					type: false,
+					title: '已完成请尽快确认!',
+					order: '06521649496',
+					time: '2023-06-07  12:25:25'
+				}
+			]
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	min-width: 100vw;
+	min-height: 100vh;
+	background-color: #f2f2f2;
+	overflow-y: auto;
+
+	.box {
+		margin-top: 10rpx;
+		height: 194rpx;
+		background-color: #fff;
+
+		.box_time {
+			height: 76rpx;
+			line-height: 76rpx;
+			text-align: center;
+			color: #999999;
+			font-size: 24rpx;
+		}
+
+		.box_info {
+			display: flex;
+			align-items: center;
+			margin-left: 30rpx;
+
+			.info_circle {
+				width: 16rpx;
+				height: 16rpx;
+				border-radius: 50%;
+				background-color: #d43030;
+			}
+
+			.while {
+				background-color: #fff;
+			}
+			.info_icon {
+				margin: 0 8rpx;
+				width: 90rpx;
+				height: 90rpx;
+
+				img {
+					width: 100%;
+					height: 100%;
+				}
+			}
+
+			.info_msg {
+				.msg_title {
+					font-size: 32rpx;
+					font-weight: bold;
+				}
+
+				.msg_order {
+					margin-top: 10rpx;
+					color: #808080;
+					font-size: 24rpx;
+				}
+			}
+		}
+	}
+}
+</style>

+ 583 - 0
pagesRepairs/myRepairs/myRepairs.vue

@@ -0,0 +1,583 @@
+<template>
+	<view class="container">
+		<!-- 顶部分段器区域 -->
+		<view class="control">
+			<view class="control_item" :class="{ active: current === index }" v-for="(item, index) in items" :key="index" @click="onClickItem(index)">{{ item }}</view>
+		</view>
+
+		<!-- 主体内容区域 -->
+		<view class="body">
+			<!-- 每一个记录区域 -->
+			<view class="body_item" v-for="item in list" :key="item.id">
+				<!-- 工单编号 -->
+				<view class="item_title" @click="handleLookDetail">
+					<img src="../../static/images/repairsImg/order.png" />
+					<view class="title_info">工单编号:{{ item.order }}</view>
+					<view class="title_type" v-if="item.overtime">超时未接</view>
+				</view>
+
+				<!-- 报修时间 -->
+				<view class="item_time">
+					<view class="time_msg">
+						报修时间:
+						<text>{{ item.time }}</text>
+					</view>
+					<view class="time_type" v-if="item.type === 1">待接单</view>
+					<view class="time_type color_type" v-if="item.type === 2">维修中</view>
+					<view class="time_type" v-if="item.type === 3">待确认</view>
+					<view class="time_type color_type2" v-if="item.type === 4">已完成</view>
+					<view class="time_type" v-if="item.type === 5">已取消</view>
+				</view>
+
+				<!-- 报修姓名 -->
+				<view class="item_box">
+					<view class="box_key">报修姓名:</view>
+					<view class="box_value">{{ item.name }}</view>
+				</view>
+
+				<!-- 报修电话 -->
+				<view class="item_box">
+					<view class="box_key">报修电话:</view>
+					<view class="box_value phone">
+						{{ item.phone }}
+						<img src="../../static/images/repairsImg/phone.png" />
+					</view>
+				</view>
+
+				<!-- 报修区域 -->
+				<view class="item_box">
+					<view class="box_key">报修区域:</view>
+					<view class="box_value">{{ item.area }}</view>
+				</view>
+
+				<!-- 详细地址 -->
+				<view class="item_box">
+					<view class="box_key">详细地址:</view>
+					<view class="box_value">{{ item.address }}</view>
+				</view>
+
+				<!-- 报修物品 -->
+				<view class="item_box">
+					<view class="box_key">报修物品:</view>
+					<view class="box_value">{{ item.goods }}</view>
+				</view>
+
+				<!-- 故障描述 -->
+				<view class="item_box">
+					<view class="box_key">故障描述:</view>
+					<view class="box_value">{{ item.description }}</view>
+				</view>
+
+				<!-- 上传图片 -->
+				<view class="item_img">
+					<view class="img_key">上传图片:</view>
+					<view class="img_value"></view>
+				</view>
+
+				<!-- 维修师傅 -->
+				<view class="item_time" v-if="item.type === 3 || item.type === 4">
+					<view class="time_msg">
+						维修师傅:
+						<text>{{ item.workerName }}</text>
+					</view>
+				</view>
+
+				<!-- 师傅电话 -->
+				<view class="item_box" v-if="item.type === 3 || item.type === 4">
+					<view class="box_key">师傅电话:</view>
+					<view class="box_value phone">
+						{{ item.workerPhone }}
+						<img src="../../static/images/repairsImg/phone.png" />
+					</view>
+				</view>
+
+				<!-- 维修费用 -->
+				<view class="item_box" v-if="item.type === 3 || item.type === 4">
+					<view class="box_key">维修费用:</view>
+					<view class="box_value2 phone" @click="checkFeeDetail">
+						{{ item.money }}元
+						<img src="../../static/images/repairsImg/eye.png" />
+					</view>
+				</view>
+
+				<!-- 维修费用弹窗 -->
+				<uni-popup :is-mask-click="false" ref="popup_fee">
+					<view class="pop_fee">
+						<view class="fee_title">
+							维修费用
+							<text @click="$refs.popup_fee[0].close()">×</text>
+						</view>
+						<view class="fee_box">
+							耗材:
+							<text>螺丝刀</text>
+						</view>
+						<view class="fee_box">
+							耗材单价:
+							<text>1元</text>
+						</view>
+						<view class="fee_box">
+							耗材数量:
+							<text>2</text>
+						</view>
+						<view class="fee_box">
+							耗材费用:
+							<text>2元</text>
+						</view>
+					</view>
+				</uni-popup>
+
+				<!-- 按钮 -->
+				<view class="item_btn" v-if="item.type !== 5">
+					<view class="btn_box type" v-if="item.type === 1 || item.type === 2">催单</view>
+					<view class="btn_box type" v-if="item.type === 3" @click="handleBackOffice">转后勤</view>
+					<view class="btn_box type" v-if="item.type === 3">支付</view>
+					<view class="btn_box type2" v-if="item.type < 4">撤销</view>
+					<view class="btn_box type" v-if="item.type === 4" @click="handleEvaluate">去评价</view>
+				</view>
+				<view class="item_btn2" v-else></view>
+
+				<!-- 转后勤弹窗 -->
+				<uni-popup ref="popup_logistics" :is-mask-click="false">
+					<view class="pop_logistics">
+						<view class="logistics_title">转后勤</view>
+						<view class="logistics_body">
+							<textarea placeholder-style="color:#CCCCCC" placeholder="请输入您宝贵的留言"></textarea>
+						</view>
+						<view class="logistics_btn">
+							<view class="btn_box cancel" @click="$refs.popup_logistics[0].close()">取消</view>
+							<view class="btn_box confirm" @click="$refs.popup_logistics[0].close()">确定</view>
+						</view>
+					</view>
+				</uni-popup>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			items: ['未完成(2)', '待确认(4)', '已完成(5)'],
+			current: 0,
+			list: [],
+			// type为 1 是 待接单,type为 2 是维修中,
+			// type为 3 是 待确认,type为 4 是已完成,
+			// type为 5 是已取消
+			dataList: [
+				{
+					id: 1,
+					order: '656262219626262',
+					overtime: true,
+					time: '2023.07.05  09:25:26',
+					type: 1,
+					name: '张三',
+					phone: '13659585689',
+					area: '行政楼',
+					address: '3楼309',
+					goods: '电脑',
+					description: '水龙头坏了',
+					img: ''
+				},
+				{
+					id: 2,
+					order: '956262219626262',
+					overtime: false,
+					time: '2023.07.05  09:25:26',
+					type: 2,
+					name: '李四',
+					phone: '13659585689',
+					area: '行政楼',
+					address: '3楼309',
+					goods: '电脑',
+					description: '电脑坏了',
+					img: ''
+				}
+			],
+			dataList2: [
+				{
+					id: 1,
+					order: '656262219626262',
+					overtime: false,
+					time: '2023.07.05  09:25:26',
+					type: 3,
+					name: '张三',
+					phone: '13659585689',
+					area: '行政楼',
+					address: '3楼309',
+					goods: '电脑',
+					description: '水龙头坏了',
+					img: '',
+					workerName: '小李',
+					workerPhone: '13659585689',
+					money: 50
+				}
+			],
+			dataList3: [
+				{
+					id: 1,
+					order: '656262219626262',
+					overtime: false,
+					time: '2023.07.05  09:25:26',
+					type: 4,
+					name: '张三',
+					phone: '13659585689',
+					area: '行政楼',
+					address: '3楼309',
+					goods: '电脑',
+					description: '水龙头坏了',
+					img: '',
+					workerName: '小李',
+					workerPhone: '13659585689',
+					money: 50
+				},
+				{
+					id: 2,
+					order: '656262219626262',
+					overtime: false,
+					time: '2023.07.05  09:25:26',
+					type: 5,
+					name: '张三',
+					phone: '13659585689',
+					area: '行政楼',
+					address: '3楼309',
+					goods: '电脑',
+					description: '水龙头坏了',
+					img: '',
+					workerName: '小李',
+					workerPhone: '13659585689',
+					money: 50
+				}
+			]
+		}
+	},
+	mounted() {
+		this.list = this.dataList
+	},
+	methods: {
+		onClickItem(index) {
+			console.log(index)
+			if (this.current != index) {
+				this.current = index
+			}
+			if (this.current === 0) {
+				this.list = this.dataList
+			} else if (this.current === 1) {
+				this.list = this.dataList2
+			} else {
+				this.list = this.dataList3
+			}
+		},
+		checkFeeDetail() {
+			this.$refs.popup_fee[0].open('center')
+		},
+		handleBackOffice() {
+			this.$refs.popup_logistics[0].open('center')
+		},
+		handleEvaluate() {
+			uni.navigateTo({
+				url: '/pagesRepairs/evaluate/evaluate'
+			})
+		},
+		handleLookDetail() {
+			uni.navigateTo({
+				url: '/pagesRepairs/repairDetails/repairDetails'
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	display: flex;
+	flex-direction: column;
+	box-sizing: border-box;
+	padding: 0 30rpx;
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+
+	.control {
+		display: flex;
+		box-sizing: border-box;
+		margin: 26rpx 0 29rpx;
+		padding: 6rpx;
+		width: 100%;
+		height: 88rpx;
+		border-radius: 18rpx;
+		background-color: #f2f2f2;
+
+		.control_item {
+			flex: 1;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			font-size: 32rpx;
+		}
+
+		.active {
+			border-radius: 18rpx;
+			background-color: #fff;
+		}
+	}
+
+	.body {
+		height: calc(100vh - 190rpx);
+		overflow-y: auto;
+
+		.body_item {
+			margin: 10rpx 10rpx 30rpx;
+			// height: 799rpx;
+			border-radius: 11rpx;
+			box-shadow: 0 0 8rpx #d9d9d9;
+
+			.item_title {
+				display: flex;
+				align-items: center;
+				padding: 0 30rpx;
+				height: 87rpx;
+				border-bottom: 1rpx solid #e6e6e6;
+
+				img {
+					width: 30rpx;
+					height: 30rpx;
+				}
+
+				.title_info {
+					margin: 0 21rpx 0 13rpx;
+					font-size: 28rpx;
+				}
+
+				.title_type {
+					width: 137rpx;
+					height: 47rpx;
+					line-height: 47rpx;
+					text-align: center;
+					border-radius: 136rpx;
+					color: #ff5733;
+					font-size: 24rpx;
+					border: 1rpx solid #ff5733;
+				}
+			}
+
+			.item_time {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				padding: 0 30rpx;
+				height: 79rpx;
+
+				.time_msg {
+					font-size: 28rpx;
+					color: #808080;
+
+					text {
+						color: #000000;
+					}
+				}
+
+				.time_type {
+					font-size: 32rpx;
+					color: #ff5733;
+				}
+
+				.color_type {
+					color: #1e7dfb;
+				}
+				.color_type2 {
+					color: #6fb6b8;
+				}
+			}
+
+			.item_box {
+				display: flex;
+				padding: 0 30rpx;
+				height: 60rpx;
+				font-size: 28rpx;
+
+				.box_key {
+					color: #808080;
+				}
+
+				.box_value {
+					display: flex;
+
+					img {
+						margin-top: 5rpx;
+						margin-left: 10rpx;
+						width: 28rpx;
+						height: 28rpx;
+					}
+				}
+
+				.box_value2 {
+					display: flex;
+
+					img {
+						margin-top: -2rpx;
+						margin-left: 14rpx;
+						width: 46rpx;
+						height: 46rpx;
+					}
+				}
+
+				.phone {
+					color: #6fb6b8;
+				}
+			}
+
+			.item_img {
+				display: flex;
+				align-items: center;
+				padding: 0 30rpx;
+				height: 120rpx;
+				color: #808080;
+				font-size: 28rpx;
+
+				.img_key {
+				}
+
+				.img_value {
+					width: 120rpx;
+					height: 120rpx;
+					border-radius: 14rpx;
+					background-color: salmon;
+				}
+			}
+
+			.item_btn {
+				display: flex;
+				align-items: center;
+				justify-content: flex-end;
+				padding: 0 30rpx;
+				height: 153rpx;
+
+				.btn_box {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					margin-left: 16rpx;
+					width: 174rpx;
+					height: 68rpx;
+					font-size: 28rpx;
+					border-radius: 11rpx;
+				}
+
+				.type {
+					color: #fff;
+					background-color: #6fb6b8;
+				}
+
+				.type2 {
+					color: #6fb6b8;
+					background-color: #fff;
+					border: 1rpx solid #6fb6b8;
+				}
+			}
+
+			.item_btn2 {
+				height: 50rpx;
+			}
+
+			.pop_fee {
+				padding-bottom: 50rpx;
+				border-radius: 19rpx;
+				background-color: #fff;
+
+				.fee_title {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					box-sizing: border-box;
+					padding: 0 31rpx 0 42rpx;
+					width: 690rpx;
+					height: 110rpx;
+					font-size: 32rpx;
+					font-weight: bold;
+					border-radius: 19rpx 19rpx 0 0;
+					border-bottom: 1rpx solid #e6e6e6;
+
+					text {
+						font-size: 45rpx;
+						font-weight: 400;
+						color: #808080;
+					}
+				}
+
+				.fee_box {
+					display: flex;
+					align-items: center;
+					padding-left: 42rpx;
+					height: 80rpx;
+					font-size: 28rpx;
+					color: #808080;
+
+					text {
+						color: #000000;
+					}
+				}
+			}
+
+			.pop_logistics {
+				border-radius: 19rpx;
+				background-color: #fff;
+
+				.logistics_title {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					width: 690rpx;
+					height: 110rpx;
+					font-size: 32rpx;
+					font-weight: bold;
+					border-radius: 19rpx 19rpx 0 0;
+					border-bottom: 1rpx solid #e6e6e6;
+				}
+
+				.logistics_body {
+					height: 440rpx;
+					border-bottom: 1rpx solid #e6e6e6;
+
+					textarea {
+						box-sizing: border-box;
+						margin: 35rpx 40rpx;
+						padding: 20rpx;
+						width: 610rpx;
+						height: 370rpx;
+						font-size: 28rpx;
+						border-radius: 14rpx;
+						border: 1rpx solid #e6e6e6;
+					}
+				}
+
+				.logistics_btn {
+					display: flex;
+					align-items: center;
+					justify-content: space-evenly;
+					height: 121rpx;
+
+					.btn_box {
+						display: flex;
+						justify-content: center;
+						align-items: center;
+						width: 203rpx;
+						height: 72rpx;
+						border-radius: 9rpx;
+						font-size: 32rpx;
+					}
+
+					.cancel {
+						background-color: #e5e5e5;
+						color: #6fb6b8;
+					}
+
+					.confirm {
+						background-color: #6fb6b8;
+						color: #fff;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 341 - 0
pagesRepairs/order/order.vue

@@ -0,0 +1,341 @@
+<template>
+	<view class="container">
+		<view class="">接单池</view>
+		<view class="button" @longpress="longpressBtn" @touchend="touchendBtn" @touchmove="handleTouchMove">
+			{{ longPress }}
+		</view>
+
+		<view class="audio" v-if="tempFilePath" @click="playBtn">
+			<!-- {{ playStatus === 1 ? remainTime + 's' : '点击播放' }} -->
+			点击播放 {{ longTime + 's' }}
+		</view>
+
+		<!-- 语音音阶动画 -->
+		<view class="recording_btn" v-if="longPress === '说话中...'">
+			<view class="loading_btn">
+				<view class="em" v-for="(item, index) in 15" :key="index"></view>
+			</view>
+			<text class="p">{{ '剩余:' + remainTime + 's' }}</text>
+			<text class="span">松手结束录音</text>
+		</view>
+	</view>
+</template>
+
+<script>
+const recorderManager = uni.getRecorderManager()
+const innerAudioContext = uni.createInnerAudioContext()
+export default {
+	data() {
+		return {
+			timer: null,
+			timer_long: null,
+			remainTime: 0,
+			temLongTime: 0,
+			longTime: 0,
+			longPress: '按住说话',
+			duration: 60000,
+			startPoint: {},
+			tempFilePath: '',
+			playStatus: 0, //录音播放状态 0:未播放 1:正在播放
+
+			sendLock: true //发送锁,当为true时上锁,false时解锁发送
+		}
+	},
+	methods: {
+		// 播放按钮回调
+		playBtn() {
+			innerAudioContext.src = this.tempFilePath
+			//在ios下静音时播放没有声音,默认为true,改为false就好了。
+			// innerAudioContext.obeyMuteSwitch = false
+			//点击播放
+			if (this.playStatus === 0) {
+				this.playStatus = 1
+				innerAudioContext.play()
+				// this.countdown(this.longTime)
+			} else {
+				this.playStatus = 0
+				innerAudioContext.pause()
+			}
+			// //播放结束
+			innerAudioContext.onEnded(() => {
+				this.playStatus = 0
+				innerAudioContext.stop()
+			})
+		},
+		// 长按回调
+		longpressBtn(e) {
+			// 先授权麦克风权限
+			uni.getSetting({
+				success: (res) => {
+					console.log(res.authSetting['scope.record'])
+					if (!res.authSetting['scope.record']) {
+						console.log(1)
+						uni.showModal({
+							title: '提示',
+							content: '需要您授权麦克风权限',
+							showCancel: false,
+							success: () => {
+								uni.openSetting({
+									success(settingdata) {
+										if (settingdata.authSetting['scope.record']) {
+											uni.showToast({
+												title: '已获取麦克风使用权限',
+												icon: 'none'
+											})
+										} else {
+											uni.showToast({
+												title: '您取消了授权',
+												icon: 'none'
+											})
+										}
+									}
+								})
+							}
+						})
+					} else {
+						console.log(2)
+						this.longPress = '说话中...'
+						//记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
+						this.startPoint = e.touches[0]
+						// 倒计时
+						this.countdown(60)
+						// 记录录音时长
+						this.recordingTimer()
+						recorderManager.onStop((res) => {
+							this.tempFilePath = res.tempFilePath
+						})
+						const options = {
+							duration: this.duration, // 指定录音的时长,单位 ms
+							sampleRate: 16000, // 采样率
+							numberOfChannels: 1, // 录音通道数
+							encodeBitRate: 96000, // 编码码率
+							format: 'mp3', // 音频格式,有效值 aac/mp3
+							frameSize: 10 // 指定帧大小,单位 KB
+						}
+						recorderManager.start(options)
+						// 监听音频开始事件
+						this.sendLock = false //长按时是不上锁的。
+						recorderManager.onStart((res) => {
+							// console.log(res)
+						})
+					}
+				}
+			})
+		},
+		// 记录录音时长回调
+		recordingTimer() {
+			this.temLongTime = 0
+			this.timer_long = setInterval(() => {
+				this.temLongTime++
+			}, 1000)
+		},
+		// 录音时长倒计时
+		countdown(val) {
+			this.remainTime = Number(val)
+			this.timer = setInterval(() => {
+				if (this.remainTime > 0) {
+					this.remainTime--
+				} else {
+					this.longPress = '按住说话'
+					recorderManager.stop()
+					clearInterval(this.timer)
+				}
+			}, 1000)
+		},
+		// 手指触摸离开回调
+		touchendBtn() {
+			this.longPress = '按住说话'
+			this.longTime = this.temLongTime
+			clearInterval(this.timer)
+			clearInterval(this.timer_long)
+
+			recorderManager.onStop((res) => {
+				console.log(this.sendLock)
+				if (this.sendLock) {
+					//上锁不发送
+				} else {
+					//解锁发送,发送网络请求
+					if (res.duration < 1000)
+						wx.showToast({
+							title: '录音时间太短',
+							icon: 'none',
+							duration: 1000
+						})
+					else {
+						this.tempFilePath = res.tempFilePath
+						console.log(this.tempFilePath + ' 666')
+						// uploadFile({
+						// 	url: '/voice/VoiceControl',
+						// 	src: res.tempFilePath,
+						// }).then(res => {
+						// 	console.log(JSON.parse(res.data));
+						// })
+					}
+				}
+			})
+			recorderManager.stop() //结束录音
+		},
+		//手指移动回调  删除录音
+		handleTouchMove(e) {
+			//touchmove时触发
+			var moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY //移动距离
+			console.log(moveLenght)
+			if (Math.abs(moveLenght) > 70) {
+				this.longPress = '松开手指,取消发送'
+				this.delBtn()
+				this.sendLock = true //触发了上滑取消发送,上锁
+			} else {
+				this.sendLock = false //上划距离不足,依然可以发送,不上锁
+			}
+		},
+		delBtn() {
+			// this.delShow = false
+			// this.time = 0
+			this.tempFilePath = ''
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	position: relative;
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	.button {
+		margin: auto;
+		margin-top: 300rpx;
+		width: 50vw;
+		height: 80rpx;
+		line-height: 80rpx;
+		text-align: center;
+		border-radius: 50rpx;
+		background-color: skyblue;
+	}
+
+	.audio {
+		margin: auto;
+		margin-top: 100rpx;
+		width: 50vw;
+		height: 80rpx;
+		line-height: 80rpx;
+		text-align: center;
+		border-radius: 50rpx;
+		background-color: skyblue;
+	}
+
+	.recording_btn {
+		position: absolute;
+		top: 80rpx;
+		left: 50%;
+		transform: translateX(-50%);
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		padding: 8px 16px;
+		box-sizing: border-box;
+		width: 128px;
+		font-size: 12px;
+		text-align: center;
+		border-radius: 8px;
+		background: #ffd300;
+
+		::after {
+			content: '';
+			display: block;
+			border: 6px solid rgba(0, 0, 0, 0);
+			border-top-color: rgba(255, 211, 0, 1);
+			position: absolute;
+			bottom: -10px;
+			left: 50%;
+			transform: translateX(-50%);
+		}
+
+		.loading_btn {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-bottom: 6px;
+			width: 96px;
+			height: 20px;
+
+			.em {
+				float: left;
+				display: block;
+				margin-right: 2.5px;
+				width: 1px;
+				height: 10%;
+				background: #333333;
+			}
+		}
+
+		.loading_btn .em:last-child {
+			margin-right: 0px;
+		}
+		.loading_btn .em:nth-child(1) {
+			animation: load 2.5s 1.4s infinite linear;
+		}
+		.loading_btn .em:nth-child(2) {
+			animation: load 2.5s 1.2s infinite linear;
+		}
+		.loading_btn .em:nth-child(3) {
+			animation: load 2.5s 1s infinite linear;
+		}
+		.loading_btn .em:nth-child(4) {
+			animation: load 2.5s 0.8s infinite linear;
+		}
+		.loading_btn .em:nth-child(5) {
+			animation: load 2.5s 0.6s infinite linear;
+		}
+		.loading_btn .em:nth-child(6) {
+			animation: load 2.5s 0.4s infinite linear;
+		}
+		.loading_btn .em:nth-child(7) {
+			animation: load 2.5s 0.2s infinite linear;
+		}
+		.loading_btn .em:nth-child(8) {
+			animation: load 2.5s 0s infinite linear;
+		}
+		.loading_btn .em:nth-child(9) {
+			animation: load 2.5s 0.2s infinite linear;
+		}
+		.loading_btn .em:nth-child(10) {
+			animation: load 2.5s 0.4s infinite linear;
+		}
+		.loading_btn .em:nth-child(11) {
+			animation: load 2.5s 0.6s infinite linear;
+		}
+		.loading_btn .em:nth-child(12) {
+			animation: load 2.5s 0.8s infinite linear;
+		}
+		.loading_btn .em:nth-child(13) {
+			animation: load 2.5s 1s infinite linear;
+		}
+		.loading_btn .em:nth-child(14) {
+			animation: load 2.5s 1.2s infinite linear;
+		}
+		.loading_btn .em:nth-child(15) {
+			animation: load 2.5s 1.4s infinite linear;
+		}
+		@keyframes load {
+			0% {
+				height: 10%;
+			}
+			50% {
+				height: 100%;
+			}
+			100% {
+				height: 10%;
+			}
+		}
+
+		.p {
+			color: #000000;
+		}
+		.span {
+			color: rgba(0, 0, 0, 0.6);
+		}
+	}
+}
+</style>

+ 21 - 0
pagesRepairs/pending/pending.vue

@@ -0,0 +1,21 @@
+<template>
+	<view class="container">
+		<view class="">待处理池</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	background-color: salmon;
+}
+</style>

+ 215 - 0
pagesRepairs/repairDetails/repairDetails.vue

@@ -0,0 +1,215 @@
+<template>
+	<view class="container">
+		<view class="title">工单信息</view>
+		<view class="box">
+			<view class="box_key">工单编号:</view>
+			<view class="box_value">656262219626262</view>
+		</view>
+		<view class="box_time">
+			<view class="time_msg">
+				报修时间:
+				<text>2023.07.05 09:25:26</text>
+			</view>
+			<!-- v-if="item.type === 1" -->
+			<view class="time_type">待接单</view>
+			<!-- <view class="time_type color_type" v-if="item.type === 2">维修中</view>
+			<view class="time_type" v-if="item.type === 3">待确认</view>
+			<view class="time_type color_type2" v-if="item.type === 4">已完成</view>
+			<view class="time_type" v-if="item.type === 5">已取消</view> -->
+		</view>
+
+		<view class="box">
+			<view class="box_key">报修姓名:</view>
+			<view class="box_value">张三</view>
+		</view>
+		<view class="box">
+			<view class="box_key">报修电话:</view>
+			<view class="box_value phone">
+				13659585689
+				<img src="../../static/images/repairsImg/phone.png" />
+			</view>
+		</view>
+		<view class="box">
+			<view class="box_key">报修区域:</view>
+			<view class="box_value">行政楼</view>
+		</view>
+		<view class="box">
+			<view class="box_key">详细地址:</view>
+			<view class="box_value">3楼309</view>
+		</view>
+		<view class="box">
+			<view class="box_key">报修物品:</view>
+			<view class="box_value">电脑</view>
+		</view>
+		<view class="box">
+			<view class="box_key">故障描述:</view>
+			<view class="box_value">水龙头坏了</view>
+		</view>
+
+		<view class="box_img">
+			<view class="img_key">上传图片:</view>
+			<view class="img_value"></view>
+		</view>
+
+		<view class="title top">报修跟踪</view>
+
+		<!-- 步骤条区域 -->
+		<!-- activeIcon="checkmark-circle-fill" -->
+		<uv-steps activeColor="#6FB6B8" direction="column" current="3">
+			<uv-steps-item :customStyle="customStyle" v-for="item in stepsList" :key="item.id">
+				<template v-slot:title>
+					<view class="steps_title" :class="{ active: item.type }">{{ item.title }}</view>
+				</template>
+				<template v-slot:desc>
+					<view class="steps_desc" v-for="(element, index) in item.desc" :key="index">{{ element }}</view>
+				</template>
+			</uv-steps-item>
+		</uv-steps>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			customStyle: {
+				marginBottom: '5px'
+			},
+			stepsList: [
+				{
+					id: 1,
+					title: '提交订单',
+					type: true,
+					desc: ['2023-07-05 08:25:25']
+				},
+				{
+					id: 2,
+					title: '已接单',
+					type: true,
+					desc: ['2023-07-05 08:25:25']
+				},
+				{
+					id: 3,
+					title: '待确认',
+					type: true,
+					desc: ['2023-07-05 08:25:25', '张发财转后勤:太贵了', '系统管理员改价:耗材为螺丝刀,价格50元', '系统管理员改价:耗材为螺丝刀,价格50元']
+				},
+				{
+					id: 4,
+					title: '待确认',
+					type: false,
+					desc: ['2023-07-05 08:25:25']
+				},
+				{
+					id: 5,
+					title: '已完成',
+					type: false,
+					desc: ['2023-07-05 08:25:25']
+				}
+			]
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	padding: 0 30rpx 50rpx;
+
+	.title {
+		height: 90rpx;
+		line-height: 90rpx;
+		font-size: 32rpx;
+		font-weight: bold;
+	}
+
+	.top {
+		margin-top: 20rpx;
+	}
+
+	.box {
+		display: flex;
+		height: 60rpx;
+		font-size: 28rpx;
+
+		.box_key {
+			color: #808080;
+		}
+
+		.box_value {
+			display: flex;
+
+			img {
+				margin-top: 5rpx;
+				margin-left: 10rpx;
+				width: 28rpx;
+				height: 28rpx;
+			}
+		}
+
+		.phone {
+			color: #6fb6b8;
+		}
+	}
+
+	.box_time {
+		display: flex;
+		height: 60rpx;
+
+		.time_msg {
+			font-size: 28rpx;
+			color: #808080;
+
+			text {
+				color: #000000;
+			}
+		}
+
+		.time_type {
+			margin-left: 123rpx;
+			margin-top: -5rpx;
+			font-size: 32rpx;
+			color: #ff5733;
+		}
+
+		.color_type {
+			color: #1e7dfb;
+		}
+		.color_type2 {
+			color: #6fb6b8;
+		}
+	}
+
+	.box_img {
+		display: flex;
+		align-items: center;
+		height: 120rpx;
+		color: #808080;
+		font-size: 28rpx;
+
+		.img_key {
+		}
+
+		.img_value {
+			width: 120rpx;
+			height: 120rpx;
+			border-radius: 14rpx;
+			background-color: salmon;
+		}
+	}
+
+	.steps_title {
+		color: #969799;
+		font-size: 28rpx;
+	}
+
+	.active {
+		color: #6fb6b8;
+	}
+
+	.steps_desc {
+		line-height: 45rpx;
+		font-size: 24rpx;
+	}
+}
+</style>

+ 437 - 0
pagesRepairs/repairs/repairs.vue

@@ -0,0 +1,437 @@
+<template>
+	<view class="container">
+		<!-- 公告区域 -->
+		<view class="notice">
+			<uni-notice-bar show-icon single speed="50" :scrollable="true" background-color="#fff" color="#000" text="今日14:00系统更新请不要进行报修!" />
+		</view>
+		<view class="gap"></view>
+
+		<!-- 报修区域 -->
+		<view class="box_item">
+			<view class="item_title">
+				报修区域
+				<text>*</text>
+			</view>
+			<view class="item_info" @click="handleSelectArea">请选择报修区域</view>
+			<view class="item_icon" @click="handleSelectArea">
+				<img src="../../static/images/repairsImg/right.png" />
+			</view>
+		</view>
+
+		<!-- 详细地址区域 -->
+		<view class="box_item">
+			<view class="item_title">
+				详细地址
+				<text>*</text>
+			</view>
+			<view class="item_info">
+				<view class="item_input">
+					<input placeholder-style="color:#CCCCCC" type="text" placeholder="请输入详细地址" />
+				</view>
+			</view>
+		</view>
+
+		<!-- 报修物品区域 -->
+		<view class="box_item">
+			<view class="item_title">
+				报修物品
+				<text>*</text>
+			</view>
+			<view class="item_info" @click="handleSelectGoods">请选择报修物品</view>
+			<view class="item_icon" @click="handleSelectGoods">
+				<img src="../../static/images/repairsImg/right.png" />
+			</view>
+		</view>
+
+		<!-- 故障描述 -->
+		<view class="box_item2">
+			<view class="item2_title">
+				故障描述
+				<text>*</text>
+			</view>
+			<view class="item2_info">
+				<view class="item2_textarea">
+					<textarea placeholder-style="color:#CCCCCC" placeholder="请输入故障描述"></textarea>
+				</view>
+			</view>
+		</view>
+
+		<!-- 报修录音区域 -->
+		<view class="box_item">
+			<view class="item_title">报修录音</view>
+			<view class="item_info">
+				<view class="info_icon">
+					<img src="../../static/images/repairsImg/voice.png" />
+				</view>
+				<view class="info_tips">点击录音</view>
+			</view>
+		</view>
+
+		<!-- 上传照片区域 -->
+		<view class="box_item3">
+			<view class="item3_title">
+				上传照片
+				<text>(可上传三张,请上传局部图和区域图)</text>
+			</view>
+
+			<uni-file-picker
+				ref="filePicker"
+				limit="3"
+				v-model="imgList"
+				fileMediatype="image"
+				:image-styles="imageStyles"
+				mode="grid"
+				@select="select"
+				@delete="handleDelete"
+			></uni-file-picker>
+		</view>
+
+		<!-- 报修姓名区域 -->
+		<view class="box_item">
+			<view class="item_title">
+				报修姓名
+				<text>*</text>
+			</view>
+			<view class="item_info">
+				<view class="item_input">
+					<input placeholder-style="color:#CCCCCC" type="text" placeholder="请输入报修姓名" />
+				</view>
+			</view>
+		</view>
+
+		<!-- 联系电话区域 -->
+		<view class="box_item">
+			<view class="item_title">
+				联系电话
+				<text>*</text>
+			</view>
+			<view class="item_info">
+				<view class="item_input">
+					<input placeholder-style="color:#CCCCCC" type="text" placeholder="请输入联系电话" />
+				</view>
+			</view>
+		</view>
+
+		<!-- 底部区域 -->
+		<view class="foot">
+			<view class="foot_btn">确认提交</view>
+			<view class="foot_phone">紧急电话 5661626</view>
+			<view class="foot_phone">服务监督电话 5661626</view>
+		</view>
+
+		<!-- 消息通知按钮区域 -->
+		<view class="fixed" @tap="handleClickFixed">
+			<view class="fixed_box">
+				<img src="../../static/images/repairsImg/message.png" />
+				消息通知
+			</view>
+		</view>
+
+		<!-- 用于图片压缩的canvas画布 -->
+		<canvas
+			:style="{
+				width: cw + 'px',
+				height: cw + 'px',
+				position: 'absolute',
+				zIndex: -1,
+				left: '-10000rpx',
+				top: '-10000rpx'
+			}"
+			canvas-id="zipCanvas"
+		></canvas>
+		<!--画布结束-->
+	</view>
+</template>
+
+<script>
+// 图片压缩方法
+import getLessLimitSizeImage from '../util/imageCompress.js'
+
+export default {
+	data() {
+		return {
+			// 显示的图片数据
+			imgList: [],
+			// 上传的图片数据
+			subImgList: [],
+			// 图片上传框的样式
+			imageStyles: {
+				width: 60,
+				height: 60,
+				border: {
+					color: '#ccc',
+					width: 1,
+					style: 'dashed',
+					radius: '9px'
+				}
+			},
+			//画板边长默认是屏幕宽度,正方形画布
+			cw: uni.getSystemInfoSync().windowWidth
+		}
+	},
+	methods: {
+		// 选择图片回调
+		select(e) {
+			console.log(e)
+			e.tempFiles.forEach((item) => {
+				//这里的id和页面中写的html代码的canvas的id要一致
+				let canvasId = 'zipCanvas'
+				//原图的路径
+				let imagePath = item.path
+				//大小限制1024kb
+				let limitSize = 1024
+				//初始绘画区域是画布自身的宽度也就是屏幕宽度
+				let drawWidth = uni.getSystemInfoSync().windowWidth
+
+				getLessLimitSizeImage(canvasId, imagePath, limitSize, drawWidth, (resPath) => {
+					uni.showLoading({
+						title: '上传中'
+					})
+					uni.uploadFile({
+						url: `https://jiangxih3cpartner.com/reporting/file/fileUpDown`,
+						filePath: resPath,
+						name: 'file',
+						success: (uploadFileRes) => {
+							subImgList.value.push(JSON.parse(uploadFileRes.data).data.join())
+							imgList.value.push({
+								url: item.path,
+								name: ''
+							})
+							uni.hideLoading()
+						},
+						fail: () => {
+							uni.showToast({
+								title: '上传失败',
+								icon: 'error'
+							})
+						}
+					})
+				})
+			})
+		},
+		handleDelete(e) {
+			// console.log(e);
+			const num = imgList.value.findIndex((v) => v.url === e.tempFilePath)
+			subImgList.value.splice(num, 1)
+			imgList.value.splice(num, 1)
+		},
+
+		// 点击报修区域回调
+		handleSelectArea() {
+			uni.navigateTo({
+				url: '/pagesRepairs/selectArea/selectArea'
+			})
+		},
+		// 点击报修物品回调
+		handleSelectGoods() {
+			uni.navigateTo({
+				url: '/pagesRepairs/selectGoods/selectGoods'
+			})
+		},
+		// 点击消息通知按钮回调
+		handleClickFixed() {
+			uni.navigateTo({
+				url: '/pagesRepairs/message/message'
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+	overflow-y: auto;
+
+	.notice {
+		height: 90rpx;
+	}
+
+	.gap {
+		height: 12rpx;
+		background-color: #f2f2f2;
+	}
+
+	.box_item {
+		display: flex;
+		align-items: center;
+		margin: 0 30rpx;
+		height: 105rpx;
+		font-size: 28rpx;
+		border-bottom: 1rpx solid #e6e6e6;
+
+		.item_title {
+			width: 190rpx;
+			font-weight: bold;
+
+			text {
+				margin-left: 5rpx;
+				color: #d43030;
+			}
+		}
+
+		.item_info {
+			flex: 1;
+			display: flex;
+			padding: 0 10rpx;
+			color: #cccccc;
+
+			.item_input {
+				width: 100%;
+				height: 105rpx;
+
+				input {
+					height: 105rpx;
+					line-height: 105rpx;
+					color: #000;
+				}
+			}
+
+			.info_icon {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				width: 79rpx;
+				height: 37rpx;
+				border-radius: 33rpx;
+				border: 1rpx solid #e6e6e6;
+
+				img {
+					width: 26rpx;
+					height: 26rpx;
+				}
+			}
+
+			.info_tips {
+				margin-left: 33rpx;
+				font-size: 28rpx;
+				color: #cccccc;
+			}
+		}
+
+		.item_icon {
+			width: 38rpx;
+			height: 38rpx;
+
+			img {
+				width: 100%;
+				height: 100%;
+			}
+		}
+	}
+
+	.box_item2 {
+		display: flex;
+		align-items: center;
+		margin: 0 30rpx;
+		height: 237rpx;
+		font-size: 28rpx;
+		border-bottom: 1rpx solid #e6e6e6;
+
+		.item2_title {
+			width: 190rpx;
+			font-weight: bold;
+
+			text {
+				margin-left: 5rpx;
+				color: #d43030;
+			}
+		}
+
+		.item2_info {
+			flex: 1;
+			padding: 0 10rpx;
+
+			.item2_textarea {
+				display: flex;
+				align-items: center;
+				width: 100%;
+				height: 237rpx;
+
+				textarea {
+					box-sizing: border-box;
+					padding: 20rpx;
+					width: 500rpx;
+					height: 176rpx;
+					border: 1rpx solid #e6e6e6;
+					border-radius: 6rpx;
+				}
+			}
+		}
+	}
+
+	.box_item3 {
+		margin: 0 30rpx;
+		height: 246rpx;
+		border-bottom: 1rpx solid #e6e6e6;
+
+		.item3_title {
+			height: 95rpx;
+			line-height: 95rpx;
+			font-size: 28rpx;
+			font-weight: bold;
+
+			text {
+				font-size: 24rpx;
+				color: #808080;
+				font-weight: 400;
+			}
+		}
+	}
+
+	.foot {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		margin: 0 30rpx;
+		height: 407rpx;
+
+		.foot_btn {
+			margin-top: 167rpx;
+			width: 100%;
+			height: 100rpx;
+			line-height: 100rpx;
+			text-align: center;
+			color: #fff;
+			font-size: 32rpx;
+			border-radius: 12rpx;
+			background-color: #6fb6b8;
+		}
+
+		.foot_phone {
+			margin-top: 30rpx;
+			color: #b3b3b3;
+			font-size: 24rpx;
+		}
+	}
+
+	.fixed {
+		z-index: 2;
+		position: fixed;
+		right: 0;
+		bottom: 382rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 211rpx;
+		height: 90rpx;
+		border-radius: 150rpx 0 0 150rpx;
+		border: 1rpx solid #d9d9d9;
+		background-color: #fff;
+		box-shadow: 0 0 7rpx #d9d9d9;
+
+		.fixed_box {
+			display: flex;
+			align-items: center;
+			font-size: 28rpx;
+
+			img {
+				margin-right: 10rpx;
+				width: 38rpx;
+				height: 38rpx;
+			}
+		}
+	}
+}
+</style>

+ 138 - 0
pagesRepairs/selectArea/selectArea.vue

@@ -0,0 +1,138 @@
+<template>
+	<view class="container">
+		<!-- 顶部选择校区区域 -->
+		<view class="top">
+			<uni-segmented-control :current="current" :values="items" @clickItem="onClickItem" styleType="text" activeColor="#6FB6B8"></uni-segmented-control>
+		</view>
+
+		<view class="gap"></view>
+
+		<!-- 主体内容区域 -->
+		<view class="body">
+			<!-- 左边区域 -->
+			<view class="body_left">
+				<view class="body_left_item" :class="{ active: active_left === index }" v-for="(item, index) in leftList" :key="index" @click="handleChange(index)">
+					{{ item }}
+				</view>
+			</view>
+
+			<!-- 右边区域 -->
+			<view class="body_right">
+				<view class="body_right_item" :class="{ active: active_right === index }" v-for="(item, index) in rightList" :key="index" @click="handleChange2(index)">
+					{{ item }}
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			items: ['黄家湖校区', '墨轩湖校区'],
+			current: 0,
+			leftList: ['教学楼', '行政楼', '综合楼', '科技楼', '学生宿舍', '其他'],
+			rightList: ['1栋', '2栋', '3栋', '4栋', '5栋', '6栋', '7栋', '8栋', '体育场', '篮球场', '其他'],
+			active_left: 0,
+			active_right: 0
+		}
+	},
+	methods: {
+		onClickItem(e) {
+			if (this.current != e.currentIndex) {
+				this.current = e.currentIndex
+				console.log(this.current)
+			}
+		},
+		handleChange(index) {
+			this.active_left = index
+		},
+		handleChange2(index) {
+			this.active_right = index
+			uni.navigateBack(1)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: 100vh;
+
+	.top {
+		height: 100rpx;
+	}
+
+	.gap {
+		height: 10rpx;
+		background-color: #f2f2f2;
+	}
+
+	.body {
+		display: flex;
+		height: calc(100vh - 110rpx);
+
+		.body_left {
+			box-sizing: border-box;
+			padding: 15rpx;
+			width: 198rpx;
+			border-right: 1rpx solid #ccc;
+			overflow-y: auto;
+
+			.body_left_item {
+				width: 100%;
+				height: 80rpx;
+				line-height: 80rpx;
+				text-align: center;
+				font-size: 28rpx;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			.active {
+				color: #6fb6b8;
+			}
+		}
+
+		.body_right {
+			box-sizing: border-box;
+			padding: 30rpx 40rpx;
+			// display: grid;
+			// grid-template-columns: repeat(4, 1fr);
+			// grid-auto-rows: 50rpx;
+			// gap: 30rpx 28rpx;
+			width: 552rpx;
+			overflow-y: auto;
+
+			.body_right_item {
+				float: left;
+				box-sizing: border-box;
+				margin: 0 12rpx 28rpx 0;
+				padding: 10rpx 30rpx;
+				height: 50rpx;
+				line-height: 30rpx;
+				text-align: center;
+				color: #808080;
+				font-size: 28rpx;
+				border-radius: 53rpx;
+				background-color: #e6e6e6;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			.active {
+				color: #fff;
+				background-color: #6fb6b8;
+			}
+		}
+	}
+}
+
+::v-deep .segmented-control {
+	height: 100rpx;
+}
+</style>

+ 111 - 0
pagesRepairs/selectGoods/selectGoods.vue

@@ -0,0 +1,111 @@
+<template>
+	<view class="container">
+		<view class="gap"></view>
+
+		<view class="body">
+			<!-- 左边区域 -->
+			<view class="left">
+				<view class="left_item" :class="{ active: active_left === index }" v-for="(item, index) in leftList" :key="index" @click="handleChange(index)">
+					{{ item }}
+				</view>
+			</view>
+
+			<!-- 右边区域 -->
+			<view class="right">
+				<view class="right_item" :class="{ active: active_right === index }" v-for="(item, index) in rightList" :key="index" @click="handleChange2(index)">
+					{{ item }}
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			active_left: 0,
+			active_right: 0,
+			leftList: ['其他', '水电'],
+			rightList: ['下水道', '安全指示牌', '消费应急灯', '排水管道', '吊顶', '风扇', '没电']
+		}
+	},
+	methods: {
+		handleChange(index) {
+			this.active_left = index
+		},
+		handleChange2(index) {
+			this.active_right = index
+			uni.navigateBack(1)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: 100vh;
+
+	.gap {
+		height: 10rpx;
+		background-color: #f2f2f2;
+	}
+
+	.body {
+		display: flex;
+		height: calc(100vh - 10rpx);
+		.left {
+			box-sizing: border-box;
+			padding: 15rpx;
+			width: 198rpx;
+			border-right: 1rpx solid #ccc;
+			overflow-y: auto;
+
+			.left_item {
+				width: 100%;
+				height: 80rpx;
+				line-height: 80rpx;
+				text-align: center;
+				font-size: 28rpx;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			.active {
+				color: #6fb6b8;
+			}
+		}
+
+		.right {
+			box-sizing: border-box;
+			padding: 30rpx 40rpx;
+			width: 552rpx;
+			overflow-y: auto;
+
+			.right_item {
+				float: left;
+				box-sizing: border-box;
+				padding: 10rpx 30rpx;
+				margin: 0 32rpx 37rpx 0;
+				height: 50rpx;
+				line-height: 30rpx;
+				text-align: center;
+				color: #808080;
+				font-size: 28rpx;
+				border-radius: 53rpx;
+				background-color: #e6e6e6;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+
+			.active {
+				color: #fff;
+				background-color: #6fb6b8;
+			}
+		}
+	}
+}
+</style>

+ 27 - 0
pagesRepairs/util/api.js

@@ -0,0 +1,27 @@
+const BASE_URL = "https://jiangxih3cpartner.com/reporting"
+export const myRequest_repairs = (options) => {
+	uni.showLoading({
+		title: "加载中",
+		mask: true,
+	});
+	return new Promise((resolve, reject) => {
+		uni.request({
+			url: BASE_URL + options.url,
+			method: options.method || "GET",
+			data: options.data || {},
+			success: (res) => {
+				uni.hideLoading();
+				resolve(res.data)
+			},
+			fail: (err) => {
+				uni.hideLoading();
+				uni.showToast({
+					title: "请求数据失败",
+					icon: "none",
+					mask: true
+				})
+				reject(err)
+			}
+		})
+	})
+}

+ 97 - 0
pagesRepairs/util/imageCompress.js

@@ -0,0 +1,97 @@
+ //通过canvas将图片压缩至指定大小
+
+ //判断图片大小是否满足需求,limitSize的单位是kb
+ function imageSizeIsLessLimitSize(imagePath, limitSize, lessCallback, moreCallback) {
+ 	//获取文件信息
+ 	wx.getFileSystemManager().getFileInfo({
+ 		filePath: imagePath,
+ 		success: (res) => {
+ 			// console.log("压缩前图片大小", res.size / 1024, 'kb');
+ 			//如果图片太大了走moreCallback
+ 			if (res.size > 1024 * limitSize) {
+ 				moreCallback()
+ 			}
+ 			//图片满足要求了走lessCallback
+ 			else {
+ 				lessCallback()
+ 			}
+ 		}
+ 	})
+ }
+
+ //将图片画在画布上并获取画好之后的图片的路径
+ function getCanvasImage(canvasId, imagePath, imageW, imageH, getImgSuccess) {
+ 	//创建画布内容
+ 	const ctx = wx.createCanvasContext(canvasId);
+ 	//图片画上去,imageW和imageH是画上去的尺寸,图像和画布间隔都是0
+ 	ctx.drawImage(imagePath, 0, 0, imageW, imageH);
+ 	//这里一定要加定时器,给足够的时间去画(所以每次递归最少要耗时200ms,多次递归很耗时!)
+ 	ctx.draw(false, setTimeout(function() {
+ 		//把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径
+ 		wx.canvasToTempFilePath({
+ 			canvasId: canvasId,
+ 			x: 0,
+ 			y: 0,
+ 			width: imageW,
+ 			height: imageH,
+ 			quality: 1, //最高质量
+ 			success: (res) => {
+ 				//将取出的图片路径通过回调函数返回
+ 				getImgSuccess(res.tempFilePath);
+ 			}
+ 		})
+ 	}, 200));
+ }
+
+ //主函数,默认限制大小1024kb即1mb,drawWidth是绘画区域的大小
+ //初始值传入为画布自身的边长(我们这是一个正方形的画布)
+ function getLessLimitSizeImage(canvasId, imagePath, limitSize = 1024, drawWidth, callback) {
+ 	//判断图片尺寸是否满足要求
+ 	imageSizeIsLessLimitSize(imagePath, limitSize,
+ 		(lessRes) => {
+ 			//满足要求走callback,将压缩后的文件路径返回
+ 			callback(imagePath);
+ 		},
+ 		(moreRes) => {
+ 			//不满足要求需要压缩的时候
+ 			wx.getImageInfo({
+ 				src: imagePath,
+ 				success: (imageInfo) => {
+ 					let maxSide = Math.max(imageInfo.width, imageInfo.height);
+ 					let windowW = drawWidth;
+ 					let scale = 1;
+ 					/*
+ 					这里的目的是当绘画区域缩小的比图片自身尺寸还要小的时候
+ 					取图片长宽的最大值,然后和当前绘画区域计算出需要放缩的比例
+ 					然后再画经过放缩后的尺寸,保证画出的一定是一个完整的图片。由于每次递归绘画区域都会缩小,
+ 					所以不用担心scale永远都是1绘画尺寸永远不变的情况,只要不满足压缩后体积的要求
+ 					就会缩小绘画区域,早晚会有绘画区域小于图片尺寸的情况发生
+ 					*/
+ 					if (maxSide > windowW) {
+ 						scale = windowW / maxSide;
+ 					}
+ 					//trunc是去掉小数
+ 					let imageW = Math.trunc(imageInfo.width * scale);
+ 					let imageH = Math.trunc(imageInfo.height * scale);
+ 					// console.log('调用压缩', imageW, imageH);
+ 					//图片在规定绘画区域上画并获取新的图片的path
+ 					getCanvasImage(canvasId, imagePath, imageW, imageH,
+ 						(pressImgPath) => {
+ 							/*
+ 							再去检查是否满足要求,始终缩小绘画区域,让图片适配绘画区域
+ 							这里乘以0.95是必须的,如果不缩小绘画区域,会出现尺寸比绘画区域小,
+ 							而体积比要求压缩体积大的情况出现,就会无穷递归下去,因为scale的值永远是1
+ 							但0.95不是固定的,你可以根据需要自己改,0到1之间,越小则绘画区域缩小的越快
+ 							但不建议取得太小,绘画区域缩小的太快,压出来的将总是很糊的
+ 							*/
+ 							getLessLimitSizeImage(canvasId, pressImgPath, limitSize, drawWidth *
+ 								0.95, callback);
+ 						}
+ 					)
+ 				}
+ 			})
+ 		}
+ 	)
+ }
+
+ export default getLessLimitSizeImage

+ 27 - 0
pagesRepairs/waitAllot/waitAllot.vue

@@ -0,0 +1,27 @@
+<template>
+	<view class="container">
+		<view class="">待分配单</view>
+		<Recording2 />
+	</view>
+</template>
+
+<script>
+import Recording2 from '../components/recording2.vue'
+export default {
+	components: {
+		Recording2
+	},
+	data() {
+		return {}
+	},
+	mounted() {},
+	methods: {}
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+	width: 100vw;
+	height: calc(100vh - 102rpx);
+}
+</style>

BIN
static/images/repairsImg/eye.png


BIN
static/images/repairsImg/message.png


BIN
static/images/repairsImg/msg.png


BIN
static/images/repairsImg/myRepairs-active.png


BIN
static/images/repairsImg/myRepairs.png


BIN
static/images/repairsImg/order.png


BIN
static/images/repairsImg/phone.png


BIN
static/images/repairsImg/repairs-active.png


BIN
static/images/repairsImg/repairs.png


BIN
static/images/repairsImg/right.png


BIN
static/images/repairsImg/voice.png


+ 74 - 0
uni_modules/qiun-data-charts/changelog.md

@@ -1,3 +1,77 @@
+## 2.5.0-20230101(2023-01-01)
+- 秋云图表组件 修改条件编译顺序,确保uniapp的cli方式的项目依赖不完整时可以正常显示
+- 秋云图表组件 恢复props属性directory的使用,以修复vue3项目中,开启echarts后,echarts目录识别错误的bug
+- uCharts.js 修复区域图、混合图只有一个数据时图表显示不正确的bug
+- uCharts.js 修复折线图、区域图中时间轴类别图表tooltip指示点显示不正确的bug
+- uCharts.js 修复x轴使用labelCount时,并且boundaryGap = 'justify' 并且关闭Y轴显示的时候,最后一个坐标值不显示的bug
+- uCharts.js 修复折线图只有一组数据时 ios16 渲染颜色不正确的bug
+- uCharts.js 修复玫瑰图半径显示不正确的bug
+- uCharts.js 柱状图、山峰图增加正负图功能,y轴网格如果需要显示0轴则由 min max 及 splitNumber 确定,后续版本优化自动显示0轴
+- uCharts.js 柱状图column增加 opts.extra.column.labelPosition,数据标签位置,有效值为 outside外部, insideTop内顶部, center内中间, bottom内底部
+- uCharts.js 雷达图radar增加 opts.extra.radar.labelShow,否显示各项标识文案是,默认true
+- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.boxPadding,提示窗边框填充距离,默认3px
+- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.fontSize,提示窗字体大小配置,默认13px
+- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.lineHeight,提示窗文字行高,默认20px
+- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.legendShow,是否显示左侧图例,默认true
+- uCharts.js 提示窗tooltip增加 opts.extra.tooltip.legendShape,图例形状,图例标识样式,有效值为 auto自动跟随图例, diamond◆, circle●, triangle▲, square■, rect▬, line-
+- uCharts.js 标记线markLine增加 opts.extra.markLine.labelFontSize,字体大小配置,默认13px
+- uCharts.js 标记线markLine增加 opts.extra.markLine.labelPadding,标签边框内填充距离,默认6px
+- uCharts.js 折线图line增加 opts.extra.line.linearType,渐变色类型,可选值 none关闭渐变色,custom 自定义渐变色。使用自定义渐变色时请赋值serie.linearColor作为颜色值
+- uCharts.js 折线图line增加 serie.linearColor,渐变色数组,格式为2维数组[起始位置,颜色值],例如[[0,'#0EE2F8'],[0.3,'#2BDCA8'],[0.6,'#1890FF'],[1,'#9A60B4']]
+- uCharts.js 折线图line增加 opts.extra.line.onShadow,是否开启折线阴影,开启后请赋值serie.setShadow阴影设置
+- uCharts.js 折线图line增加 serie.setShadow,阴影配置,格式为4位数组:[offsetX,offsetY,blur,color]
+- uCharts.js 折线图line增加 opts.extra.line.animation,动画效果方向,可选值为vertical 垂直动画效果,horizontal 水平动画效果
+- uCharts.js X轴xAxis增加 opts.xAxis.lineHeight,X轴字体行高,默认20px
+- uCharts.js X轴xAxis增加 opts.xAxis.marginTop,X轴文字距离轴线的距离,默认0px
+- uCharts.js X轴xAxis增加 opts.xAxis.title,当前X轴标题
+- uCharts.js X轴xAxis增加 opts.xAxis.titleFontSize,标题字体大小,默认13px
+- uCharts.js X轴xAxis增加 opts.xAxis.titleOffsetY,标题纵向偏移距离,负数为向上偏移,正数向下偏移
+- uCharts.js X轴xAxis增加 opts.xAxis.titleOffsetX,标题横向偏移距离,负数为向左偏移,正数向右偏移
+- uCharts.js X轴xAxis增加 opts.xAxis.titleFontColor,标题字体颜色,默认#666666
+
+## 报错TypeError: Cannot read properties of undefined (reading 'length')
+- 如果是uni-modules版本组件,请先登录HBuilderX账号;
+- 在HBuilderX中的manifest.json,点击重新获取uniapp的appid,或者删除appid重新粘贴,重新运行;
+- 如果是cli项目请使用码云上的非uniCloud版本组件;
+- 或者添加uniCloud的依赖;
+- 或者使用原生uCharts;
+## 2.4.5-20221130(2022-11-30)
+- uCharts.js 优化tooltip当文字很多变为左侧显示时,如果画布仍显显示不下,提示框错位置变为以左侧0位置起画
+- uCharts.js 折线图修复特殊情况下只有单点数据,并改变线宽后点变为圆形的bug
+- uCharts.js 修复Y轴disabled启用后无效并报错的bug
+- uCharts.js 修复仪表盘起始结束角度特殊情况下显示不正确的bug
+- uCharts.js 雷达图新增参数 opts.extra.radar.radius , 自定义雷达图半径
+- uCharts.js 折线图、区域图增加tooltip指示点,opts.extra.line.activeType/opts.extra.area.activeType,可选值"none"不启用激活指示点,"hollow"空心点模式,"solid"实心点模式
+## 2.4.4-20221102(2022-11-02)
+- 秋云图表组件 修复使用echarts时reload、reshow无法调用重新渲染的bug,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/40)
+- 秋云图表组件 修复使用echarts时,初始化时宽高不正确的bug,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/42)
+- 秋云图表组件 修复uniapp的h5使用history模式时,无法加载echarts的bug
+- 秋云图表组件 小程序端@complete、@scrollLeft、@scrollRight、@getTouchStart、@getTouchMove、@getTouchEnd事件增加opts参数传出,方便一些特殊需求的交互获取数据。
+
+- uCharts.js 修复calTooltipYAxisData方法内formatter格式化方法未与y轴方法同步的问题,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/43)
+- uCharts.js 地图新增参数opts.series[i].fillOpacity,以透明度方式来设置颜色过度效果,[详见码云PR](https://gitee.com/uCharts/uCharts/pulls/38)
+- uCharts.js 地图新增参数opts.extra.map.active,是否启用点击激活变色
+- uCharts.js 地图新增参数opts.extra.map.activeTextColor,是否启用点击激活变色
+- uCharts.js 地图新增渲染完成事件renderComplete
+- uCharts.js 漏斗图修复当部分数据相同时tooltip提示窗点击错误的bug
+- uCharts.js 漏斗图新增参数series.data[i].centerText 居中标签文案
+- uCharts.js 漏斗图新增参数series.data[i].centerTextSize 居中标签文案字体大小,默认opts.fontSize
+- uCharts.js 漏斗图新增参数series.data[i].centerTextColor 居中标签文案字体颜色,默认#FFFFFF
+- uCharts.js 漏斗图新增参数opts.extra.funnel.minSize 最小值的最小宽度,默认0
+- uCharts.js 进度条新增参数opts.extra.arcbar.direction,动画方向,可选值为cw顺时针、ccw逆时针
+- uCharts.js 混合图新增参数opts.extra.mix.line.width,折线的宽度,默认2
+- uCharts.js 修复tooltip开启horizentalLine水平横线标注时,图表显示错位的bug
+- uCharts.js 优化tooltip当文字很多变为左侧显示时,如果画布仍显显示不下,提示框错位置变为以左侧0位置起画
+- uCharts.js 修复开启滚动条后X轴文字超出绘图区域后的隐藏逻辑
+- uCharts.js 柱状图、条状图修复堆叠模式不能通过{value,color}赋值单个柱子颜色的问题
+- uCharts.js 气泡图修复不识别series.textSize和series.textColor的bug
+
+## 报错TypeError: Cannot read properties of undefined (reading 'length')
+1. 如果是uni-modules版本组件,请先登录HBuilderX账号;
+2. 在HBuilderX中的manifest.json,点击重新获取uniapp的appid,或者删除appid重新粘贴,重新运行;
+3. 如果是cli项目请使用码云上的非uniCloud版本组件;
+4. 或者添加uniCloud的依赖;
+5. 或者使用原生uCharts;
 ## 2.4.3-20220505(2022-05-05)
 - 秋云图表组件 修复开启canvas2d后将series赋值为空数组显示加载图标时,再次赋值后画布闪动的bug
 - 秋云图表组件 修复升级hbx最新版后ECharts的highlight方法报错的bug

+ 37 - 26
uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue

@@ -97,7 +97,7 @@
     </block>
     <!-- #endif -->
     <!-- 其他小程序通过vue渲染图表 -->
-    <!-- #ifdef MP-360 || MP-BAIDU || MP-QQ || MP-TOUTIAO || MP-WEIXIN || MP-KUAISHOU || MP-LARK || MP-JD -->
+    <!-- #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO || MP-KUAISHOU || MP-LARK || MP-JD || MP-360 -->
     <block v-if="type2d">
       <view v-if="ontouch" @tap="_tap">
         <canvas
@@ -407,7 +407,9 @@ export default {
       cHeight: 250,
       showchart: false,
       echarts: false,
-      echartsResize:false,
+      echartsResize:{
+        state:false
+      },
       uchartsOpts: {},
       echartsOpts: {},
       drawData:{},
@@ -495,7 +497,7 @@ export default {
           return;
         }
         if (_this.echarts) {
-          _this.echartsResize = !_this.echartsResize;
+          _this.echartsResize.state = !_this.echartsResize.state;
         } else {
           _this.resizeHandler();
         }
@@ -590,7 +592,7 @@ export default {
       if (val === true && this.mixinDatacomLoading === false) {
         setTimeout(() => {
           this.mixinDatacomErrorMessage = null;
-          this.echartsResize = !this.echartsResize;
+          this.echartsResize.state = !this.echartsResize.state;
           this.checkData(this.drawData);
         }, 200);
       }
@@ -863,10 +865,10 @@ export default {
     },
     _clearChart() {
       let cid = this.cid
-      if (this.echrts !== true && cfu.option[cid] && cfu.option[cid].context) {
+      if (this.echarts !== true && cfu.option[cid] && cfu.option[cid].context) {
         const ctx = cfu.option[cid].context;
-        if(typeof ctx === "object" && !cfu.option[cid].update){
-          ctx.clearRect(0, 0, this.cWidth, this.cHeight);
+        if(typeof ctx === "object" && !!!cfu.option[cid].update){
+          ctx.clearRect(0, 0, this.cWidth*this.pixel, this.cHeight*this.pixel);
           ctx.draw();
         }
       }
@@ -1036,14 +1038,14 @@ export default {
       this.showchart = true;
       cfu.instance[cid] = new uCharts(cfu.option[cid]);
       cfu.instance[cid].addEventListener('renderComplete', () => {
-        this.emitMsg({name: 'complete', params: {type:"complete", complete: true, id: cid}});
+        this.emitMsg({name: 'complete', params: {type:"complete", complete: true, id: cid, opts: cfu.instance[cid].opts}});
         cfu.instance[cid].delEventListener('renderComplete')
       });
       cfu.instance[cid].addEventListener('scrollLeft', () => {
-        this.emitMsg({name: 'scrollLeft', params: {type:"scrollLeft", scrollLeft: true, id: cid}});
+        this.emitMsg({name: 'scrollLeft', params: {type:"scrollLeft", scrollLeft: true, id: cid, opts: cfu.instance[cid].opts}});
       });
       cfu.instance[cid].addEventListener('scrollRight', () => {
-        this.emitMsg({name: 'scrollRight', params: {type:"scrollRight", scrollRight: true, id: cid}});
+        this.emitMsg({name: 'scrollRight', params: {type:"scrollRight", scrollRight: true, id: cid, opts: cfu.instance[cid].opts}});
       });
     },
     _updataUChart(cid) {
@@ -1160,7 +1162,7 @@ export default {
       if(cfu.option[cid].enableScroll === true && e.touches.length == 1){
         cfu.instance[cid].scrollStart(e);
       }
-      this.emitMsg({name:'getTouchStart', params:{type:"touchStart", event:e.changedTouches[0], id:cid}});
+      this.emitMsg({name:'getTouchStart', params:{type:"touchStart", event:e.changedTouches[0], id:cid, opts: cfu.instance[cid].opts}});
     },
     _touchMove(e) {
       let cid = this.cid
@@ -1178,14 +1180,14 @@ export default {
       if(this.ontouch === true && cfu.option[cid].enableScroll === true && this.onzoom === true && e.changedTouches.length == 2){
         cfu.instance[cid].dobuleZoom(e);
       }
-      this.emitMsg({name: 'getTouchMove', params: {type:"touchMove", event:e.changedTouches[0], id: cid}});
+      this.emitMsg({name: 'getTouchMove', params: {type:"touchMove", event:e.changedTouches[0], id: cid, opts: cfu.instance[cid].opts}});
     },
     _touchEnd(e) {
       let cid = this.cid
       if(cfu.option[cid].enableScroll === true && e.touches.length == 0){
         cfu.instance[cid].scrollEnd(e);
       }
-      this.emitMsg({name:'getTouchEnd', params:{type:"touchEnd", event:e.changedTouches[0], id:cid}});
+      this.emitMsg({name:'getTouchEnd', params:{type:"touchEnd", event:e.changedTouches[0], id:cid, opts: cfu.instance[cid].opts}});
       if(this.ontap === true && cfu.option[cid].enableScroll === false && this.onmovetip === true){
         this._tap(e,true)
       }
@@ -1307,9 +1309,9 @@ export default {
         script.src = './uni_modules/qiun-data-charts/static/app-plus/echarts.min.js'
         // #endif
         // #ifdef H5
-        const { origin, pathname } = window.location
-        const rooturl = origin + pathname
-        script.src = rooturl + 'uni_modules/qiun-data-charts/static/h5/echarts.min.js'
+        const rooturl = window.location.origin
+        const directory = instance.getDataset().directory
+        script.src = rooturl + directory + 'uni_modules/qiun-data-charts/static/h5/echarts.min.js'
         // #endif
         script.onload = this.newEChart
         document.head.appendChild(script)
@@ -1368,7 +1370,16 @@ export default {
         if(cfe.instance[cid]){
           cfe.instance[cid].off('finished')
         }
-      })
+      });
+
+      //修复init初始化实例获取宽高不正确问题
+      if(
+        typeof that[cid].$el.children[0].clientWidth != 'undefined' &&
+          (
+            Math.abs( that[cid].$el.children[0].clientWidth - cfe.instance[cid].getWidth() )>3 ||
+            Math.abs( that[cid].$el.children[0].clientHeight - cfe.instance[cid].getHeight() )>3
+          )
+      ){this.ecresize();}
     },
     tooltipPosition(){
       return (point, params, dom, rect, size) => {
@@ -1420,14 +1431,14 @@ export default {
       let cid = this.rid
       cfu.instance[cid] = new uChartsRD(cfu.option[cid])
       cfu.instance[cid].addEventListener('renderComplete', () => {
-        that[cid].callMethod('emitMsg',{name:"complete",params:{type:"complete",complete:true,id:cid}})
+        that[cid].callMethod('emitMsg',{name:"complete",params:{type:"complete",complete:true,id:cid, opts: cfu.instance[cid].opts}})
         cfu.instance[cid].delEventListener('renderComplete')
       });
       cfu.instance[cid].addEventListener('scrollLeft', () => {
-        that[cid].callMethod('emitMsg',{name:"scrollLeft",params:{type:"scrollLeft",scrollLeft:true,id:cid}})
+        that[cid].callMethod('emitMsg',{name:"scrollLeft",params:{type:"scrollLeft",scrollLeft:true,id:cid, opts: cfu.instance[cid].opts}})
       });
       cfu.instance[cid].addEventListener('scrollRight', () => {
-        that[cid].callMethod('emitMsg',{name:"scrollRight",params:{type:"scrollRight",scrollRight:true,id:cid}})
+        that[cid].callMethod('emitMsg',{name:"scrollRight",params:{type:"scrollRight",scrollRight:true,id:cid, opts: cfu.instance[cid].opts}})
       });
     },
     updataUChart() {
@@ -1514,7 +1525,7 @@ export default {
       if(cfu.option[cid].enableScroll === true && e.touches.length == 1){
         cfu.instance[cid].scrollStart(e);
       }
-      that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"touchStart",event:e.changedTouches[0],id:cid}})
+      that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"touchStart",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
     },
     touchMove(e) {
       let cid = this.rid
@@ -1534,7 +1545,7 @@ export default {
       if(ontouch === true && cfu.option[cid].enableScroll === true && cfu.option[cid].onzoom === true && e.changedTouches.length == 2){
         cfu.instance[cid].dobuleZoom(e);
       }
-      that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"touchMove",event:e.changedTouches[0],id:cid}})
+      that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"touchMove",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
     },
     touchEnd(e) {
       let cid = this.rid
@@ -1543,7 +1554,7 @@ export default {
       if(cfu.option[cid].enableScroll === true && e.touches.length == 0){
         cfu.instance[cid].scrollEnd(e);
       }
-      that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"touchEnd",event:e.changedTouches[0],id:cid}})
+      that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"touchEnd",event:e.changedTouches[0],id:cid, opts: cfu.instance[cid].opts}})
     },
     mouseDown(e) {
       let cid = this.rid
@@ -1556,7 +1567,7 @@ export default {
       e.changedTouches.unshift(tmpe)
       cfu.instance[cid].scrollStart(e)
       cfu.option[cid].mousedown=true;
-      that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"mouseDown",event:tmpe,id:cid}})
+      that[cid].callMethod('emitMsg',{name:"getTouchStart",params:{type:"mouseDown",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
     },
     mouseMove(e) {
       let cid = this.rid
@@ -1570,7 +1581,7 @@ export default {
       e.changedTouches.unshift(tmpe)
       if(cfu.option[cid].mousedown){
         cfu.instance[cid].scroll(e)
-        that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"mouseMove",event:tmpe,id:cid}})
+        that[cid].callMethod('emitMsg',{name:"getTouchMove",params:{type:"mouseMove",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
       }else if(cfu.instance[cid]){
         if(tooltipShow==true){
           this.showTooltip(e,cid)
@@ -1588,7 +1599,7 @@ export default {
       e.changedTouches.unshift(tmpe)
       cfu.instance[cid].scrollEnd(e)
       cfu.option[cid].mousedown=false;
-      that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"mouseUp",event:tmpe,id:cid}})
+      that[cid].callMethod('emitMsg',{name:"getTouchEnd",params:{type:"mouseUp",event:tmpe,id:cid, opts: cfu.instance[cid].opts}})
     },
   }
 }

+ 13 - 8
uni_modules/qiun-data-charts/js_sdk/u-charts/config-ucharts.js

@@ -56,14 +56,14 @@ const cfu = {
   "formatter":{
     "yAxisDemo1":function(val, index, opts){return val+'元'},
     "yAxisDemo2":function(val, index, opts){return val.toFixed(2)},
-    "xAxisDemo1":function(val, index, opts){return val+'';},
+    "xAxisDemo1":function(val, index, opts){return val+'';},
     "xAxisDemo2":function(val, index, opts){return formatDateTime(val,'h:m')},
     "seriesDemo1":function(val, index, series, opts){return val+'元'},
     "tooltipDemo1":function(item, category, index, opts){
       if(index==0){
-      	return ''+item.data+'元'
+      	return '随便用'+item.data+'年'
       }else{
-      	return ''+item.data+'元'
+      	return '其他我没改'+item.data+'天'
       }
     },
     "pieDemo":function(val, index, series, opts){
@@ -251,7 +251,8 @@ const cfu = {
 		"extra": {
 			"line": {
 				"type": "straight",
-				"width": 2
+				"width": 2,
+        "activeType": "hollow"
 			},
 		}
 	},
@@ -278,7 +279,8 @@ const cfu = {
   	"extra": {
   		"line": {
   			"type": "curve",
-  			"width": 2
+  			"width": 2,
+        "activeType": "hollow"
   		},
   	}
   },
@@ -308,7 +310,8 @@ const cfu = {
   			"opacity": 0.2,
   			"addLine": true,
   			"width": 2,
-  			"gradient": true
+  			"gradient": true,
+        "activeType": "hollow"
   		},
   	}
   },
@@ -396,7 +399,8 @@ const cfu = {
 				"opacity": 0.2,
 				"addLine": true,
 				"width": 2,
-				"gradient": false
+				"gradient": false,
+        "activeType": "hollow"
 			},
 		}
 	},
@@ -416,7 +420,8 @@ const cfu = {
 				"gridColor": "#CCCCCC",
 				"gridCount": 3,
 				"opacity": 0.2,
-				"max": 200
+				"max": 200,
+				"labelShow": true
 			},
 		}
 	},

File diff suppressed because it is too large
+ 727 - 318
uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.js


File diff suppressed because it is too large
+ 1 - 1
uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.min.js


+ 5 - 8
uni_modules/qiun-data-charts/package.json

@@ -1,8 +1,8 @@
 {
   "id": "qiun-data-charts",
   "displayName": "秋云 ucharts echarts 高性能跨全端图表组件",
-  "version": "2.4.3-20220505",
-  "description": "uCharts 新增双指缩放、新增山峰图!支持H5及APP用 ucharts echarts 渲染图表,uniapp可视化首选组件",
+  "version": "2.5.0-20230101",
+  "description": "uCharts 新增正负柱状图!支持H5及APP用 ucharts echarts 渲染图表,uniapp可视化首选组件",
   "keywords": [
     "ucharts",
     "echarts",
@@ -14,11 +14,7 @@
   "engines": {
     "HBuilderX": "^3.3.8"
   },
-  "dcloudext": {
-    "category": [
-        "前端组件",
-        "通用组件"
-    ],
+"dcloudext": {
     "sale": {
       "regular": {
         "price": "0.00"
@@ -35,7 +31,8 @@
       "data": "插件不采集任何数据",
       "permissions": "无"
     },
-    "npmurl": "https://www.npmjs.com/~qiun"
+    "npmurl": "https://www.npmjs.com/~qiun",
+    "type": "component-vue"
   },
   "uni_modules": {
     "dependencies": [],

File diff suppressed because it is too large
+ 14 - 32
uni_modules/qiun-data-charts/readme.md


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

@@ -0,0 +1,67 @@
+## 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

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

@@ -0,0 +1,224 @@
+'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) => {
+		uni.chooseImage({
+			count,
+			sizeType,
+			sourceType,
+			extension,
+			success(res) {
+				resolve(normalizeChooseAndUploadFileRes(res, 'image'));
+			},
+			fail(res) {
+				reject({
+					errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL),
+				});
+			},
+		});
+	});
+}
+
+function chooseVideo(opts) {
+	const {
+		camera,
+		compressed,
+		maxDuration,
+		sourceType,
+		extension
+	} = opts;
+	return new Promise((resolve, reject) => {
+		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),
+				});
+			},
+		});
+	});
+}
+
+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
+};

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

@@ -0,0 +1,667 @@
+<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><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: {
+			// #ifdef VUE3
+			modelValue: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			// #endif
+
+			// #ifndef VUE3
+			value: {
+				type: [Array, Object],
+				default () {
+					return []
+				}
+			},
+			// #endif
+
+			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']
+				}
+			}
+		},
+		data() {
+			return {
+				files: [],
+				localValue: []
+			}
+		},
+		watch: {
+			// #ifndef VUE3
+			value: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				handler(newVal, oldVal) {
+					this.setValue(newVal, oldVal)
+				},
+				immediate: true
+			},
+			// #endif
+		},
+		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 = []
+				}
+			},
+
+			/**
+			 * 批传
+			 * @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', {
+					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>

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

@@ -0,0 +1,325 @@
+<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;
+	}
+
+	.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>

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

@@ -0,0 +1,109 @@
+/**
+ * 获取文件名和后缀
+ * @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,
+		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
+}

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

@@ -0,0 +1,83 @@
+{
+  "id": "uni-file-picker",
+  "displayName": "uni-file-picker 文件选择上传",
+  "version": "1.0.4",
+  "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"
+      },
+      "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 

+ 18 - 0
uni_modules/uni-notice-bar/changelog.md

@@ -0,0 +1,18 @@
+## 1.2.1(2022-09-05)
+- 新增 属性 fontSize,可修改文字大小。
+## 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-notice-bar](https://uniapp.dcloud.io/component/uniui/uni-notice-bar)
+## 1.1.1(2021-11-09) 
+- 新增 提供组件设计资源,组件样式调整
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.9(2021-05-12)
+- 新增 组件示例地址
+## 1.0.8(2021-04-21)
+- 优化 添加依赖 uni-icons, 导入后自动下载依赖
+## 1.0.7(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+
+## 1.0.6(2021-02-05)
+- 调整为uni_modules目录规范

+ 449 - 0
uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue

@@ -0,0 +1,449 @@
+<template>
+	<view v-if="show" class="uni-noticebar" :style="{ backgroundColor }" @click="onClick">
+		<uni-icons v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon" type="sound" :color="color" :size="fontSize * 1.5" />
+		<view
+			ref="textBox"
+			class="uni-noticebar__content-wrapper"
+			:class="{
+				'uni-noticebar__content-wrapper--scrollable': scrollable,
+				'uni-noticebar__content-wrapper--single': !scrollable && (single || moreText)
+			}"
+			:style="{ height: scrollable ? fontSize * 1.5 + 'px' : 'auto' }"
+		>
+			<view
+				:id="elIdBox"
+				class="uni-noticebar__content"
+				:class="{
+					'uni-noticebar__content--scrollable': scrollable,
+					'uni-noticebar__content--single': !scrollable && (single || moreText)
+				}"
+			>
+				<text
+					:id="elId"
+					ref="animationEle"
+					class="uni-noticebar__content-text"
+					:class="{
+						'uni-noticebar__content-text--scrollable': scrollable,
+						'uni-noticebar__content-text--single': !scrollable && (single || showGetMore)
+					}"
+					:style="{
+						color: color,
+						fontSize: fontSize + 'px',
+						lineHeight: fontSize * 1.5 + 'px',
+						width: wrapWidth + 'px',
+						animationDuration: animationDuration,
+						'-webkit-animationDuration': animationDuration,
+						animationPlayState: webviewHide ? 'paused' : animationPlayState,
+						'-webkit-animationPlayState': webviewHide ? 'paused' : animationPlayState,
+						animationDelay: animationDelay,
+						'-webkit-animationDelay': animationDelay
+					}"
+				>
+					{{ text }}
+				</text>
+			</view>
+		</view>
+		<view v-if="isShowGetMore" class="uni-noticebar__more uni-cursor-point" @click="clickMore">
+			<text v-if="moreText.length > 0" :style="{ color: moreColor, fontSize: fontSize + 'px' }">{{ moreText }}</text>
+			<uni-icons v-else type="right" :color="moreColor" :size="fontSize * 1.1" />
+		</view>
+		<view class="uni-noticebar-close uni-cursor-point" v-if="isShowClose">
+			<uni-icons type="closeempty" :color="color" :size="fontSize * 1.1" @click="close" />
+		</view>
+	</view>
+</template>
+
+<script>
+// #ifdef APP-NVUE
+const dom = weex.requireModule('dom')
+const animation = weex.requireModule('animation')
+// #endif
+
+/**
+ * NoticeBar 自定义导航栏
+ * @description 通告栏组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=30
+ * @property {Number} speed 文字滚动的速度,默认100px/秒
+ * @property {String} text 显示文字
+ * @property {String} backgroundColor 背景颜色
+ * @property {String} color 文字颜色
+ * @property {String} moreColor 查看更多文字的颜色
+ * @property {String} moreText 设置“查看更多”的文本
+ * @property {Boolean} single = [true|false] 是否单行
+ * @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行
+ * @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标
+ * @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮
+ * @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行
+ * @event {Function} click 点击 NoticeBar 触发事件
+ * @event {Function} close 关闭 NoticeBar 触发事件
+ * @event {Function} getmore 点击”查看更多“时触发事件
+ */
+
+export default {
+	name: 'UniNoticeBar',
+	emits: ['click', 'getmore', 'close'],
+	props: {
+		text: {
+			type: String,
+			default: ''
+		},
+		moreText: {
+			type: String,
+			default: ''
+		},
+		backgroundColor: {
+			type: String,
+			default: '#FFF9EA'
+		},
+		speed: {
+			// 默认1s滚动100px
+			type: Number,
+			default: 100
+		},
+		color: {
+			type: String,
+			default: '#FF9A43'
+		},
+		fontSize: {
+			type: Number,
+			default: 14
+		},
+		moreColor: {
+			type: String,
+			default: '#FF9A43'
+		},
+		single: {
+			// 是否单行
+			type: [Boolean, String],
+			default: false
+		},
+		scrollable: {
+			// 是否滚动,添加后控制单行效果取消
+			type: [Boolean, String],
+			default: false
+		},
+		showIcon: {
+			// 是否显示左侧icon
+			type: [Boolean, String],
+			default: false
+		},
+		showGetMore: {
+			// 是否显示右侧查看更多
+			type: [Boolean, String],
+			default: false
+		},
+		showClose: {
+			// 是否显示左侧关闭按钮
+			type: [Boolean, String],
+			default: false
+		}
+	},
+	data() {
+		const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+		const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+		return {
+			textWidth: 0,
+			boxWidth: 0,
+			wrapWidth: '',
+			webviewHide: false,
+			// #ifdef APP-NVUE
+			stopAnimation: false,
+			// #endif
+			elId: elId,
+			elIdBox: elIdBox,
+			show: true,
+			animationDuration: 'none',
+			animationPlayState: 'paused',
+			animationDelay: '0s'
+		}
+	},
+	computed: {
+		isShowGetMore() {
+			return this.showGetMore === true || this.showGetMore === 'true'
+		},
+		isShowClose() {
+			return (this.showClose === true || this.showClose === 'true') && (this.showGetMore === false || this.showGetMore === 'false')
+		}
+	},
+	mounted() {
+		// #ifdef APP-PLUS
+		var pages = getCurrentPages()
+		var page = pages[pages.length - 1]
+		var currentWebview = page.$getAppWebview()
+		currentWebview.addEventListener('hide', () => {
+			this.webviewHide = true
+		})
+		currentWebview.addEventListener('show', () => {
+			this.webviewHide = false
+		})
+		// #endif
+		this.$nextTick(() => {
+			this.initSize()
+		})
+	},
+	// #ifdef APP-NVUE
+	beforeDestroy() {
+		this.stopAnimation = true
+	},
+	// #endif
+	methods: {
+		initSize() {
+			if (this.scrollable) {
+				// #ifndef APP-NVUE
+				let query = [],
+					boxWidth = 0,
+					textWidth = 0
+				let textQuery = new Promise((resolve, reject) => {
+					uni.createSelectorQuery()
+						// #ifndef MP-ALIPAY
+						.in(this)
+						// #endif
+						.select(`#${this.elId}`)
+						.boundingClientRect()
+						.exec((ret) => {
+							this.textWidth = ret[0].width
+							resolve()
+						})
+				})
+				let boxQuery = new Promise((resolve, reject) => {
+					uni.createSelectorQuery()
+						// #ifndef MP-ALIPAY
+						.in(this)
+						// #endif
+						.select(`#${this.elIdBox}`)
+						.boundingClientRect()
+						.exec((ret) => {
+							this.boxWidth = ret[0].width
+							resolve()
+						})
+				})
+				query.push(textQuery)
+				query.push(boxQuery)
+				Promise.all(query).then(() => {
+					this.animationDuration = `${this.textWidth / this.speed}s`
+					this.animationDelay = `-${this.boxWidth / this.speed}s`
+					setTimeout(() => {
+						this.animationPlayState = 'running'
+					}, 1000)
+				})
+				// #endif
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['animationEle'], (res) => {
+					let winWidth = uni.getSystemInfoSync().windowWidth
+					this.textWidth = res.size.width
+					animation.transition(
+						this.$refs['animationEle'],
+						{
+							styles: {
+								transform: `translateX(-${winWidth}px)`
+							},
+							duration: 0,
+							timingFunction: 'linear',
+							delay: 0
+						},
+						() => {
+							if (!this.stopAnimation) {
+								animation.transition(
+									this.$refs['animationEle'],
+									{
+										styles: {
+											transform: `translateX(-${this.textWidth}px)`
+										},
+										timingFunction: 'linear',
+										duration: ((this.textWidth - winWidth) / this.speed) * 1000,
+										delay: 1000
+									},
+									() => {
+										if (!this.stopAnimation) {
+											this.loopAnimation()
+										}
+									}
+								)
+							}
+						}
+					)
+				})
+				// #endif
+			}
+			// #ifdef APP-NVUE
+			if (!this.scrollable && (this.single || this.moreText)) {
+				dom.getComponentRect(this.$refs['textBox'], (res) => {
+					this.wrapWidth = res.size.width
+				})
+			}
+			// #endif
+		},
+		loopAnimation() {
+			// #ifdef APP-NVUE
+			animation.transition(
+				this.$refs['animationEle'],
+				{
+					styles: {
+						transform: `translateX(0px)`
+					},
+					duration: 0
+				},
+				() => {
+					if (!this.stopAnimation) {
+						animation.transition(
+							this.$refs['animationEle'],
+							{
+								styles: {
+									transform: `translateX(-${this.textWidth}px)`
+								},
+								duration: (this.textWidth / this.speed) * 1000,
+								timingFunction: 'linear',
+								delay: 0
+							},
+							() => {
+								if (!this.stopAnimation) {
+									this.loopAnimation()
+								}
+							}
+						)
+					}
+				}
+			)
+			// #endif
+		},
+		clickMore() {
+			this.$emit('getmore')
+		},
+		close() {
+			this.show = false
+			this.$emit('close')
+		},
+		onClick() {
+			this.$emit('click')
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.uni-noticebar {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	width: 100%;
+	height: 100%;
+	box-sizing: border-box;
+	/* #endif */
+	flex-direction: row;
+	align-items: center;
+	padding: 10px 12px;
+	margin-bottom: 10px;
+}
+
+.uni-cursor-point {
+	/* #ifdef H5 */
+	cursor: pointer;
+	/* #endif */
+}
+
+.uni-noticebar-close {
+	margin-left: 8px;
+	margin-right: 5px;
+}
+
+.uni-noticebar-icon {
+	margin-right: 5px;
+}
+
+.uni-noticebar__content-wrapper {
+	flex: 1;
+	flex-direction: column;
+	overflow: hidden;
+}
+
+.uni-noticebar__content-wrapper--single {
+	/* #ifndef APP-NVUE */
+	line-height: 18px;
+	/* #endif */
+}
+
+.uni-noticebar__content-wrapper--single,
+.uni-noticebar__content-wrapper--scrollable {
+	flex-direction: row;
+}
+
+/* #ifndef APP-NVUE */
+.uni-noticebar__content-wrapper--scrollable {
+	position: relative;
+}
+
+/* #endif */
+
+.uni-noticebar__content--scrollable {
+	/* #ifdef APP-NVUE */
+	flex: 0;
+	/* #endif */
+	/* #ifndef APP-NVUE */
+	flex: 1;
+	display: block;
+	overflow: hidden;
+	/* #endif */
+}
+
+.uni-noticebar__content--single {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex: none;
+	width: 100%;
+	justify-content: center;
+	/* #endif */
+}
+
+.uni-noticebar__content-text {
+	font-size: 14px;
+	line-height: 18px;
+	/* #ifndef APP-NVUE */
+	word-break: break-all;
+	/* #endif */
+}
+
+.uni-noticebar__content-text--single {
+	/* #ifdef APP-NVUE */
+	lines: 1;
+	/* #endif */
+	/* #ifndef APP-NVUE */
+	display: block;
+	width: 100%;
+	white-space: nowrap;
+	/* #endif */
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.uni-noticebar__content-text--scrollable {
+	/* #ifdef APP-NVUE */
+	lines: 1;
+	padding-left: 750rpx;
+	/* #endif */
+	/* #ifndef APP-NVUE */
+	position: absolute;
+	display: block;
+	height: 18px;
+	line-height: 18px;
+	white-space: nowrap;
+	padding-left: 100%;
+	animation: notice 10s 0s linear infinite both;
+	animation-play-state: paused;
+	/* #endif */
+}
+
+.uni-noticebar__more {
+	/* #ifndef APP-NVUE */
+	display: inline-flex;
+	/* #endif */
+	flex-direction: row;
+	flex-wrap: nowrap;
+	align-items: center;
+	padding-left: 5px;
+}
+
+@keyframes notice {
+	100% {
+		transform: translate3d(-100%, 0, 0);
+	}
+}
+</style>

+ 87 - 0
uni_modules/uni-notice-bar/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uni-notice-bar",
+  "displayName": "uni-notice-bar 通告栏",
+  "version": "1.2.1",
+  "description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告",
+  "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",
+			"uni-icons"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "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"
+        }
+      }
+    }
+  }
+}

+ 13 - 0
uni_modules/uni-notice-bar/readme.md

@@ -0,0 +1,13 @@
+
+
+## NoticeBar 通告栏
+> **组件名:uni-notice-bar**
+> 代码块: `uNoticeBar`
+
+
+通告栏组件 。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-notice-bar)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+

+ 25 - 0
uni_modules/uni-rate/changelog.md

@@ -0,0 +1,25 @@
+## 1.3.1(2022-02-25)
+- 修复 条件判断 `NaN` 错误的 bug
+## 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-rate](https://uniapp.dcloud.io/component/uniui/uni-rate)
+## 1.2.2(2021-09-10)
+- 优化 默认值修改为 0 颗星
+## 1.2.1(2021-07-30)
+- 优化 vue3下事件警告的问题
+## 1.2.0(2021-07-13)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.1.2(2021-05-12)
+- 新增 组件示例地址
+## 1.1.1(2021-04-21)
+- 修复 布局变化后 uni-rate  星星计算不准确的 bug
+- 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖
+## 1.1.0(2021-04-16)
+- 修复 uni-rate 属性 margin 值为 string 组件失效的 bug
+
+## 1.0.9(2021-02-05)
+- 优化 组件引用关系,通过uni_modules引用组件
+
+## 1.0.8(2021-02-05)
+- 调整为uni_modules目录规范
+- 支持 pc 端

+ 361 - 0
uni_modules/uni-rate/components/uni-rate/uni-rate.vue

@@ -0,0 +1,361 @@
+<template>
+	<view>
+		<view ref="uni-rate" class="uni-rate">
+			<view class="uni-rate__icon" :class="{'uni-cursor-not-allowed': disabled}"
+				:style="{ 'margin-right': marginNumber + 'px' }" v-for="(star, index) in stars" :key="index"
+				@touchstart.stop="touchstart" @touchmove.stop="touchmove" @mousedown.stop="mousedown"
+				@mousemove.stop="mousemove" @mouseleave="mouseleave">
+				<uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" />
+				<!-- #ifdef APP-NVUE -->
+				<view :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" class="uni-rate__icon-on">
+					<uni-icons style="text-align: left;" :color="disabled?'#ccc':activeColor" :size="size"
+						type="star-filled" />
+				</view>
+				<!-- #endif -->
+				<!-- #ifndef APP-NVUE -->
+				<view :style="{ width: star.activeWitch}" class="uni-rate__icon-on">
+					<uni-icons :color="disabled?disabledColor:activeColor" :size="size" type="star-filled" />
+				</view>
+				<!-- #endif -->
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	const dom = uni.requireNativePlugin('dom');
+	// #endif
+	/**
+	 * Rate 评分
+	 * @description 评分组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=33
+	 * @property {Boolean} 	isFill = [true|false] 		星星的类型,是否为实心类型, 默认为实心
+	 * @property {String} 	color 						未选中状态的星星颜色,默认为 "#ececec"
+	 * @property {String} 	activeColor 				选中状态的星星颜色,默认为 "#ffca3e"
+	 * @property {String} 	disabledColor 				禁用状态的星星颜色,默认为 "#c0c0c0"
+	 * @property {Number} 	size 						星星的大小
+	 * @property {Number} 	value/v-model 				当前评分
+	 * @property {Number} 	max 						最大评分评分数量,目前一分一颗星
+	 * @property {Number} 	margin 						星星的间距,单位 px
+	 * @property {Boolean} 	disabled = [true|false] 	是否为禁用状态,默认为 false
+	 * @property {Boolean} 	readonly = [true|false] 	是否为只读状态,默认为 false
+	 * @property {Boolean} 	allowHalf = [true|false] 	是否实现半星,默认为 false
+	 * @property {Boolean} 	touchable = [true|false] 	是否支持滑动手势,默认为 true
+	 * @event {Function} change 						uniRate 的 value 改变时触发事件,e={value:Number}
+	 */
+
+	export default {
+		name: "UniRate",
+		props: {
+			isFill: {
+				// 星星的类型,是否镂空
+				type: [Boolean, String],
+				default: true
+			},
+			color: {
+				// 星星未选中的颜色
+				type: String,
+				default: "#ececec"
+			},
+			activeColor: {
+				// 星星选中状态颜色
+				type: String,
+				default: "#ffca3e"
+			},
+			disabledColor: {
+				// 星星禁用状态颜色
+				type: String,
+				default: "#c0c0c0"
+			},
+			size: {
+				// 星星的大小
+				type: [Number, String],
+				default: 24
+			},
+			value: {
+				// 当前评分
+				type: [Number, String],
+				default: 0
+			},
+			modelValue: {
+				// 当前评分
+				type: [Number, String],
+				default: 0
+			},
+			max: {
+				// 最大评分
+				type: [Number, String],
+				default: 5
+			},
+			margin: {
+				// 星星的间距
+				type: [Number, String],
+				default: 0
+			},
+			disabled: {
+				// 是否可点击
+				type: [Boolean, String],
+				default: false
+			},
+			readonly: {
+				// 是否只读
+				type: [Boolean, String],
+				default: false
+			},
+			allowHalf: {
+				// 是否显示半星
+				type: [Boolean, String],
+				default: false
+			},
+			touchable: {
+				// 是否支持滑动手势
+				type: [Boolean, String],
+				default: true
+			}
+		},
+		data() {
+			return {
+				valueSync: "",
+				userMouseFristMove: true,
+				userRated: false,
+				userLastRate: 1
+			};
+		},
+		watch: {
+			value(newVal) {
+				this.valueSync = Number(newVal);
+			},
+			modelValue(newVal) {
+				this.valueSync = Number(newVal);
+			},
+		},
+		computed: {
+			stars() {
+				const value = this.valueSync ? this.valueSync : 0;
+				const starList = [];
+				const floorValue = Math.floor(value);
+				const ceilValue = Math.ceil(value);
+				for (let i = 0; i < this.max; i++) {
+					if (floorValue > i) {
+						starList.push({
+							activeWitch: "100%"
+						});
+					} else if (ceilValue - 1 === i) {
+						starList.push({
+							activeWitch: (value - floorValue) * 100 + "%"
+						});
+					} else {
+						starList.push({
+							activeWitch: "0"
+						});
+					}
+				}
+				return starList;
+			},
+
+			marginNumber() {
+				return Number(this.margin)
+			}
+		},
+		created() {
+			this.valueSync = Number(this.value || this.modelValue);
+			this._rateBoxLeft = 0
+			this._oldValue = null
+		},
+		mounted() {
+			setTimeout(() => {
+				this._getSize()
+			}, 100)
+			// #ifdef H5
+			this.PC = this.IsPC()
+			// #endif
+		},
+		methods: {
+			touchstart(e) {
+				// #ifdef H5
+				if (this.IsPC()) return
+				// #endif
+				if (this.readonly || this.disabled) return
+				const {
+					clientX,
+					screenX
+				} = e.changedTouches[0]
+				// TODO 做一下兼容,只有 Nvue 下才有 screenX,其他平台式 clientX
+				this._getRateCount(clientX || screenX)
+			},
+			touchmove(e) {
+				// #ifdef H5
+				if (this.IsPC()) return
+				// #endif
+				if (this.readonly || this.disabled || !this.touchable) return
+				const {
+					clientX,
+					screenX
+				} = e.changedTouches[0]
+				this._getRateCount(clientX || screenX)
+			},
+
+			/**
+			 * 兼容 PC @tian
+			 */
+
+			mousedown(e) {
+				// #ifdef H5
+				if (!this.IsPC()) return
+				if (this.readonly || this.disabled) return
+				const {
+					clientX,
+				} = e
+				this.userLastRate = this.valueSync
+				this._getRateCount(clientX)
+				this.userRated = true
+				// #endif
+			},
+			mousemove(e) {
+				// #ifdef H5
+				if (!this.IsPC()) return
+				if (this.userRated) return
+				if (this.userMouseFristMove) {
+					console.log('---mousemove----', this.valueSync);
+					this.userLastRate = this.valueSync
+					this.userMouseFristMove = false
+				}
+				if (this.readonly || this.disabled || !this.touchable) return
+				const {
+					clientX,
+				} = e
+				this._getRateCount(clientX)
+				// #endif
+			},
+			mouseleave(e) {
+				// #ifdef H5
+				if (!this.IsPC()) return
+				if (this.readonly || this.disabled || !this.touchable) return
+				if (this.userRated) {
+					this.userRated = false
+					return
+				}
+				this.valueSync = this.userLastRate
+				// #endif
+			},
+			// #ifdef H5
+			IsPC() {
+				var userAgentInfo = navigator.userAgent;
+				var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
+				var flag = true;
+				for (let v = 0; v < Agents.length - 1; v++) {
+					if (userAgentInfo.indexOf(Agents[v]) > 0) {
+						flag = false;
+						break;
+					}
+				}
+				return flag;
+			},
+			// #endif
+
+			/**
+			 * 获取星星个数
+			 */
+			_getRateCount(clientX) {
+				this._getSize()
+				const size = Number(this.size)
+				if (isNaN(size)) {
+					return new Error('size 属性只能设置为数字')
+				}
+				const rateMoveRange = clientX - this._rateBoxLeft
+				let index = parseInt(rateMoveRange / (size + this.marginNumber))
+				index = index < 0 ? 0 : index;
+				index = index > this.max ? this.max : index;
+				const range = parseInt(rateMoveRange - (size + this.marginNumber) * index);
+				let value = 0;
+				if (this._oldValue === index && !this.PC) return;
+				this._oldValue = index;
+				if (this.allowHalf) {
+					if (range > (size / 2)) {
+						value = index + 1
+					} else {
+						value = index + 0.5
+					}
+				} else {
+					value = index + 1
+				}
+
+				value = Math.max(0.5, Math.min(value, this.max))
+				this.valueSync = value
+				this._onChange()
+			},
+
+			/**
+			 * 触发动态修改
+			 */
+			_onChange() {
+
+				this.$emit("input", this.valueSync);
+				this.$emit("update:modelValue", this.valueSync);
+				this.$emit("change", {
+					value: this.valueSync
+				});
+			},
+			/**
+			 * 获取星星距离屏幕左侧距离
+			 */
+			_getSize() {
+				// #ifndef APP-NVUE
+				uni.createSelectorQuery()
+					.in(this)
+					.select('.uni-rate')
+					.boundingClientRect()
+					.exec(ret => {
+						if (ret) {
+							this._rateBoxLeft = ret[0].left
+						}
+					})
+				// #endif
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['uni-rate'], (ret) => {
+					const size = ret.size
+					if (size) {
+						this._rateBoxLeft = size.left
+					}
+				})
+				// #endif
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.uni-rate {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		line-height: 1;
+		font-size: 0;
+		flex-direction: row;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-rate__icon {
+		position: relative;
+		line-height: 1;
+		font-size: 0;
+	}
+
+	.uni-rate__icon-on {
+		overflow: hidden;
+		position: absolute;
+		top: 0;
+		left: 0;
+		line-height: 1;
+		text-align: left;
+	}
+
+	.uni-cursor-not-allowed {
+		/* #ifdef H5 */
+		cursor: not-allowed !important;
+		/* #endif */
+	}
+</style>

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

@@ -0,0 +1,88 @@
+{
+  "id": "uni-rate",
+  "displayName": "uni-rate 评分",
+  "version": "1.3.1",
+  "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "评分"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "category": [
+      "前端组件",
+      "通用组件"
+    ],
+    "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": [
+			"uni-scss",
+			"uni-icons"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "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"
+        }
+      }
+    }
+  }
+}

+ 12 - 0
uni_modules/uni-rate/readme.md

@@ -0,0 +1,12 @@
+
+
+## Rate 评分
+> **组件名:uni-rate**
+> 代码块: `uRate`
+> 关联组件:`uni-icons`
+
+
+评分组件,多用于购买商品后,对商品进行评价等场景
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-rate)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 7 - 0
uni_modules/uv-badge/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-04)
+1. 修复type等属性为null的时候不显示徽标的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-badge 徽标数,数字角标

+ 73 - 0
uni_modules/uv-badge/components/uv-badge/props.js

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

+ 176 - 0
uni_modules/uv-badge/components/uv-badge/uv-badge.vue

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

+ 87 - 0
uni_modules/uv-badge/package.json

@@ -0,0 +1,87 @@
+{
+	"id": "uv-badge",
+	"displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
+	"version": "1.0.2",
+	"description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
+	"keywords": [
+        "uv-badge",
+        "uvui",
+        "uv-ui",
+        "徽标数",
+        "数字角标"
+    ],
+	"repository": "",
+	"engines": {
+		"HBuilderX": "^3.1.0"
+	},
+	"dcloudext": {
+		"type": "component-vue",
+		"sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": ""
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "插件不采集任何数据",
+			"permissions": "无"
+		},
+		"npmurl": ""
+	},
+	"uni_modules": {
+		"dependencies": [
+			"uv-ui-tools"
+		],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+	}
+}

+ 11 - 0
uni_modules/uv-badge/readme.md

@@ -0,0 +1,11 @@
+## Badge 徽标数
+
+> **组件名:uv-badge**
+
+该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+
+### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 15 - 0
uni_modules/uv-icon/changelog.md

@@ -0,0 +1,15 @@
+## 1.0.5(2023-07-04)
+1. 更新图标,删除一些不常用的图标
+2. 删除base64,修改成ttf文件引入读取图标
+3. 自定义图标文档说明:https://www.uvui.cn/guide/customIcon.html
+## 1.0.4(2023-07-03)
+1. 修复主题颜色在APP不生效的BUG
+## 1.0.3(2023-05-24)
+1. 将线上ttf字体包替换成base64,避免加载时或者网络差时候显示白色方块
+## 1.0.2(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.1(2023-05-10)
+1. 修复小程序中异常显示
+## 1.0.0(2023-05-04)
+新发版

+ 157 - 0
uni_modules/uv-icon/components/uv-icon/icons.js

@@ -0,0 +1,157 @@
+export default {
+	'uvicon-level': 'e68e',
+	'uvicon-checkbox-mark': 'e659',
+	'uvicon-folder': 'e694',
+	'uvicon-movie': 'e67c',
+	'uvicon-star-fill': 'e61e',
+	'uvicon-star': 'e618',
+	'uvicon-phone-fill': 'e6ac',
+	'uvicon-phone': 'e6ba',
+	'uvicon-apple-fill': 'e635',
+	'uvicon-backspace': 'e64d',
+	'uvicon-attach': 'e640',
+	'uvicon-cut': 'e65e',
+	'uvicon-empty-address': 'e68a',
+	'uvicon-empty-favor': 'e662',
+	'uvicon-reload': 'e627',
+	'uvicon-order': 'e695',
+	'uvicon-server-man': 'e601',
+	'uvicon-search': 'e632',
+	'uvicon-fingerprint': 'e6aa',
+	'uvicon-more-dot-fill': 'e66f',
+	'uvicon-scan': 'e631',
+	'uvicon-map': 'e61d',
+	'uvicon-map-fill': 'e6a8',
+	'uvicon-tags': 'e621',
+	'uvicon-tags-fill': 'e613',
+	'uvicon-eye': 'e664',
+	'uvicon-eye-fill': 'e697',
+	'uvicon-eye-off': 'e69c',
+	'uvicon-eye-off-outline': 'e688',
+	'uvicon-mic': 'e66d',
+	'uvicon-mic-off': 'e691',
+	'uvicon-calendar': 'e65c',
+	'uvicon-trash': 'e623',
+	'uvicon-trash-fill': 'e6ce',
+	'uvicon-play-left': 'e6bf',
+	'uvicon-play-right': 'e6b3',
+	'uvicon-minus': 'e614',
+	'uvicon-plus': 'e625',
+	'uvicon-info-circle': 'e69f',
+	'uvicon-info-circle-fill': 'e6a7',
+	'uvicon-question-circle': 'e622',
+	'uvicon-question-circle-fill': 'e6bc',
+	'uvicon-close': 'e65a',
+	'uvicon-checkmark': 'e64a',
+	'uvicon-checkmark-circle': 'e643',
+	'uvicon-checkmark-circle-fill': 'e668',
+	'uvicon-setting': 'e602',
+	'uvicon-setting-fill': 'e6d0',
+	'uvicon-heart': 'e6a2',
+	'uvicon-heart-fill': 'e68b',
+	'uvicon-camera': 'e642',
+	'uvicon-camera-fill': 'e650',
+	'uvicon-more-circle': 'e69e',
+	'uvicon-more-circle-fill': 'e684',
+	'uvicon-chat': 'e656',
+	'uvicon-chat-fill': 'e63f',
+	'uvicon-bag': 'e647',
+	'uvicon-error-circle': 'e66e',
+	'uvicon-error-circle-fill': 'e655',
+	'uvicon-close-circle': 'e64e',
+	'uvicon-close-circle-fill': 'e666',
+	'uvicon-share': 'e629',
+	'uvicon-share-fill': 'e6bb',
+	'uvicon-share-square': 'e6c4',
+	'uvicon-shopping-cart': 'e6cb',
+	'uvicon-shopping-cart-fill': 'e630',
+	'uvicon-bell': 'e651',
+	'uvicon-bell-fill': 'e604',
+	'uvicon-list': 'e690',
+	'uvicon-list-dot': 'e6a9',
+	'uvicon-zhifubao-circle-fill': 'e617',
+	'uvicon-weixin-circle-fill': 'e6cd',
+	'uvicon-weixin-fill': 'e620',
+	'uvicon-qq-fill': 'e608',
+	'uvicon-qq-circle-fill': 'e6b9',
+	'uvicon-moments-circel-fill': 'e6c2',
+	'uvicon-moments': 'e6a0',
+	'uvicon-facebook-circle-fill': 'e661',
+	'uvicon-facebook': 'e674',
+	'uvicon-car': 'e64f',
+	'uvicon-car-fill': 'e648',
+	'uvicon-warning-fill': 'e6c7',
+	'uvicon-warning': 'e6c1',
+	'uvicon-clock-fill': 'e64b',
+	'uvicon-clock': 'e66c',
+	'uvicon-edit-pen': 'e65d',
+	'uvicon-edit-pen-fill': 'e679',
+	'uvicon-email': 'e673',
+	'uvicon-email-fill': 'e683',
+	'uvicon-minus-circle': 'e6a5',
+	'uvicon-plus-circle': 'e603',
+	'uvicon-plus-circle-fill': 'e611',
+	'uvicon-file-text': 'e687',
+	'uvicon-file-text-fill': 'e67f',
+	'uvicon-pushpin': 'e6d1',
+	'uvicon-pushpin-fill': 'e6b6',
+	'uvicon-grid': 'e68c',
+	'uvicon-grid-fill': 'e698',
+	'uvicon-play-circle': 'e6af',
+	'uvicon-play-circle-fill': 'e62a',
+	'uvicon-pause-circle-fill': 'e60c',
+	'uvicon-pause': 'e61c',
+	'uvicon-pause-circle': 'e696',
+	'uvicon-gift-fill': 'e6b0',
+	'uvicon-gift': 'e680',
+	'uvicon-kefu-ermai': 'e660',
+	'uvicon-server-fill': 'e610',
+	'uvicon-coupon-fill': 'e64c',
+	'uvicon-coupon': 'e65f',
+	'uvicon-integral': 'e693',
+	'uvicon-integral-fill': 'e6b1',
+	'uvicon-home-fill': 'e68e',
+	'uvicon-home': 'e67b',
+	'uvicon-account': 'e63a',
+	'uvicon-account-fill': 'e653',
+	'uvicon-thumb-down-fill': 'e628',
+	'uvicon-thumb-down': 'e60a',
+	'uvicon-thumb-up': 'e612',
+	'uvicon-thumb-up-fill': 'e62c',
+	'uvicon-lock-fill': 'e6a6',
+	'uvicon-lock-open': 'e68d',
+	'uvicon-lock-opened-fill': 'e6a1',
+	'uvicon-lock': 'e69d',
+	'uvicon-red-packet': 'e6c3',
+	'uvicon-photo-fill': 'e6b4',
+	'uvicon-photo': 'e60d',
+	'uvicon-volume-off-fill': 'e6c8',
+	'uvicon-volume-off': 'e6bd',
+	'uvicon-volume-fill': 'e624',
+	'uvicon-volume': 'e605',
+	'uvicon-download': 'e670',
+	'uvicon-arrow-up-fill': 'e636',
+	'uvicon-arrow-down-fill': 'e638',
+	'uvicon-play-left-fill': 'e6ae',
+	'uvicon-play-right-fill': 'e6ad',
+	'uvicon-arrow-downward': 'e634',
+	'uvicon-arrow-leftward': 'e63b',
+	'uvicon-arrow-rightward': 'e644',
+	'uvicon-arrow-upward': 'e641',
+	'uvicon-arrow-down': 'e63e',
+	'uvicon-arrow-right': 'e63c',
+	'uvicon-arrow-left': 'e646',
+	'uvicon-arrow-up': 'e633',
+	'uvicon-skip-back-left': 'e6c5',
+	'uvicon-skip-forward-right': 'e61f',
+	'uvicon-rewind-left': 'e62f',
+	'uvicon-arrow-right-double': 'e639',
+	'uvicon-arrow-left-double': 'e637',
+	'uvicon-empty-data': 'e671',
+	'uvicon-man': 'e675',
+	'uvicon-woman': 'e626',
+	'uvicon-zh': 'e6d3',
+	'uvicon-en': 'e6b8',
+	'uvicon-twitte': 'e607',
+	'uvicon-twitter-circle-fill': 'e6cf'
+}

+ 90 - 0
uni_modules/uv-icon/components/uv-icon/props.js

@@ -0,0 +1,90 @@
+export default {
+	props: {
+		// 图标类名
+		name: {
+			type: String,
+			default: ''
+		},
+		// 图标颜色,可接受主题色
+		color: {
+			type: String,
+			default: '#606266'
+		},
+		// 字体大小,单位px
+		size: {
+			type: [String, Number],
+			default: '16px'
+		},
+		// 是否显示粗体
+		bold: {
+			type: Boolean,
+			default: false
+		},
+		// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
+		index: {
+			type: [String, Number],
+			default: null
+		},
+		// 触摸图标时的类名
+		hoverClass: {
+			type: String,
+			default: ''
+		},
+		// 自定义扩展前缀,方便用户扩展自己的图标库
+		customPrefix: {
+			type: String,
+			default: 'uvicon'
+		},
+		// 图标右边或者下面的文字
+		label: {
+			type: [String, Number],
+			default: ''
+		},
+		// label的位置,只能右边或者下边
+		labelPos: {
+			type: String,
+			default: 'right'
+		},
+		// label的大小
+		labelSize: {
+			type: [String, Number],
+			default: '15px'
+		},
+		// label的颜色
+		labelColor: {
+			type: String,
+			default: '#606266'
+		},
+		// label与图标的距离
+		space: {
+			type: [String, Number],
+			default: '3px'
+		},
+		// 图片的mode
+		imgMode: {
+			type: String,
+			default: ''
+		},
+		// 用于显示图片小图标时,图片的宽度
+		width: {
+			type: [String, Number],
+			default: ''
+		},
+		// 用于显示图片小图标时,图片的高度
+		height: {
+			type: [String, Number],
+			default: ''
+		},
+		// 用于解决某些情况下,让图标垂直居中的用途
+		top: {
+			type: [String, Number],
+			default: 0
+		},
+		// 是否阻止事件传播
+		stop: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.icon
+	}
+}

+ 663 - 0
uni_modules/uv-icon/components/uv-icon/uniicons.css

@@ -0,0 +1,663 @@
+.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-gift-filled:before {
+  content: "\e6c4";
+}
+
+.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-back:before {
+  content: "\e6b9";
+}
+
+.uniui-forward:before {
+  content: "\e6ba";
+}
+
+.uniui-arrow-right:before {
+  content: "\e6bb";
+}
+
+.uniui-arrowthinright:before {
+  content: "\e6bb";
+}
+
+.uniui-arrow-left:before {
+  content: "\e6bc";
+}
+
+.uniui-arrowthinleft:before {
+  content: "\e6bc";
+}
+
+.uniui-arrow-up:before {
+  content: "\e6bd";
+}
+
+.uniui-arrowthinup:before {
+  content: "\e6bd";
+}
+
+.uniui-arrow-down:before {
+  content: "\e6be";
+}
+
+.uniui-arrowthindown:before {
+  content: "\e6be";
+}
+
+.uniui-bottom:before {
+  content: "\e6b8";
+}
+
+.uniui-arrowdown:before {
+  content: "\e6b8";
+}
+
+.uniui-right:before {
+  content: "\e6b5";
+}
+
+.uniui-arrowright:before {
+  content: "\e6b5";
+}
+
+.uniui-top:before {
+  content: "\e6b6";
+}
+
+.uniui-arrowup:before {
+  content: "\e6b6";
+}
+
+.uniui-left:before {
+  content: "\e6b7";
+}
+
+.uniui-arrowleft:before {
+  content: "\e6b7";
+}
+
+.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-cart-filled:before {
+  content: "\e629";
+}
+
+.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";
+}

+ 220 - 0
uni_modules/uv-icon/components/uv-icon/uv-icon.vue

@@ -0,0 +1,220 @@
+<template>
+	<view
+	  class="uv-icon"
+	  @tap="clickHandler"
+	  :class="['uv-icon--' + labelPos]"
+	>
+		<image
+		  class="uv-icon__img"
+		  v-if="isImg"
+		  :src="name"
+		  :mode="imgMode"
+		  :style="[imgStyle, $uv.addStyle(customStyle)]"
+		></image>
+		<text
+		  v-else
+		  class="uv-icon__icon"
+		  :class="uClasses"
+		  :style="[iconStyle, $uv.addStyle(customStyle)]"
+		  :hover-class="hoverClass"
+		>{{icon}}</text>
+		<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
+		<text
+		  v-if="label !== ''" 
+		  class="uv-icon__label"
+		  :style="{
+			color: labelColor,
+			fontSize: $uv.addUnit(labelSize),
+			marginLeft: labelPos == 'right' ? $uv.addUnit(space) : 0,
+			marginTop: labelPos == 'bottom' ? $uv.addUnit(space) : 0,
+			marginRight: labelPos == 'left' ? $uv.addUnit(space) : 0,
+			marginBottom: labelPos == 'top' ? $uv.addUnit(space) : 0
+		}"
+		>{{ label }}</text>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	// #ifdef APP-NVUE
+	// nvue通过weex的dom模块引入字体,相关文档地址如下:
+	// https://weex.apache.org/zh/docs/modules/dom.html#addrule
+	import iconUrl from './uvicons.ttf';
+	const domModule = weex.requireModule('dom')
+	domModule.addRule('fontFace', {
+		'fontFamily': "uvicon-iconfont",
+		'src': "url('" + iconUrl + "')"
+	})
+	// #endif
+	// 引入图标名称,已经对应的unicode
+	import icons from './icons';
+	import props from './props.js';
+	/**
+	 * icon 图标
+	 * @description 基于字体的图标集,包含了大多数常见场景的图标。
+	 * @tutorial https://www.uvui.cn/components/icon.html
+	 * @property {String}			name			图标名称,见示例图标集
+	 * @property {String}			color			图标颜色,可接受主题色 (默认 color['uv-content-color'] )
+	 * @property {String | Number}	size			图标字体大小,单位px (默认 '16px' )
+	 * @property {Boolean}			bold			是否显示粗体 (默认 false )
+	 * @property {String | Number}	index			点击图标的时候传递事件出去的index(用于区分点击了哪一个)
+	 * @property {String}			hoverClass		图标按下去的样式类,用法同uni的view组件的hoverClass参数,详情见官网
+	 * @property {String}			customPrefix	自定义扩展前缀,方便用户扩展自己的图标库 (默认 'uicon' )
+	 * @property {String | Number}	label			图标右侧的label文字
+	 * @property {String}			labelPos		label相对于图标的位置,只能right或bottom (默认 'right' )
+	 * @property {String | Number}	labelSize		label字体大小,单位px (默认 '15px' )
+	 * @property {String}			labelColor		图标右侧的label文字颜色 ( 默认 color['uv-content-color'] )
+	 * @property {String | Number}	space			label与图标的距离,单位px (默认 '3px' )
+	 * @property {String}			imgMode			图片的mode
+	 * @property {String | Number}	width			显示图片小图标时的宽度
+	 * @property {String | Number}	height			显示图片小图标时的高度
+	 * @property {String | Number}	top				图标在垂直方向上的定位 用于解决某些情况下,让图标垂直居中的用途  (默认 0 )
+	 * @property {Boolean}			stop			是否阻止事件传播 (默认 false )
+	 * @property {Object}			customStyle		icon的样式,对象形式
+	 * @event {Function} click 点击图标时触发
+	 * @event {Function} touchstart 事件触摸时触发
+	 * @example <uv-icon name="photo" color="#2979ff" size="28"></uv-icon>
+	 */
+	export default {
+		name: 'uv-icon',
+		emits: ['click'],
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				colorType: [
+					'primary',
+					'success',
+					'info',
+					'error',
+					'warning'
+				]
+			}
+		},
+		computed: {
+			uClasses() {
+				let classes = []
+				classes.push(this.customPrefix)
+				classes.push(this.customPrefix + '-' + this.name)
+				// 主题色,通过类配置
+				if (this.color && this.colorType.includes(this.color)) classes.push('uv-icon__icon--' + this.color)
+				// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
+				// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
+				//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
+				classes = classes.join(' ')
+				//#endif
+				return classes
+			},
+			iconStyle() {
+				let style = {}
+				style = {
+					fontSize: this.$uv.addUnit(this.size),
+					lineHeight: this.$uv.addUnit(this.size),
+					fontWeight: this.bold ? 'bold' : 'normal',
+					// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
+					top: this.$uv.addUnit(this.top)
+				}
+				// 非主题色值时,才当作颜色值
+				if (this.color && !this.colorType.includes(this.color)) style.color = this.color
+				return style
+			},
+			// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
+			isImg() {
+				return this.name.indexOf('/') !== -1
+			},
+			imgStyle() {
+				let style = {}
+				// 如果设置width和height属性,则优先使用,否则使用size属性
+				style.width = this.width ? this.$uv.addUnit(this.width) : this.$uv.addUnit(this.size)
+				style.height = this.height ? this.$uv.addUnit(this.height) : this.$uv.addUnit(this.size)
+				return style
+			},
+			// 通过图标名,查找对应的图标
+			icon() {
+				// 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码
+				const code = icons['uvicon-' + this.name];
+				return code ? unescape(`%u${code}`) : ['uvicon'].indexOf(this.customPrefix) > -1 ? this.name : '';
+			}
+		},
+		methods: {
+			clickHandler(e) {
+				this.$emit('click', this.index)
+				// 是否阻止事件冒泡
+				this.stop && this.preventEvent(e)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	// 变量定义
+	$uv-icon-primary: $uv-primary !default;
+	$uv-icon-success: $uv-success !default;
+	$uv-icon-info: $uv-info !default;
+	$uv-icon-warning: $uv-warning !default;
+	$uv-icon-error: $uv-error !default;
+	$uv-icon-label-line-height: 1 !default;
+	/* #ifndef APP-NVUE */
+	// 非nvue下加载字体
+	@font-face {
+		font-family: 'uvicon-iconfont';
+		src: url('./uvicons.ttf') format('truetype');
+	}
+	/* #endif */
+	.uv-icon {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+		&--left {
+			flex-direction: row-reverse;
+			align-items: center;
+		}
+		&--right {
+			flex-direction: row;
+			align-items: center;
+		}
+		&--top {
+			flex-direction: column-reverse;
+			justify-content: center;
+		}
+		&--bottom {
+			flex-direction: column;
+			justify-content: center;
+		}
+		&__icon {
+			font-family: uvicon-iconfont;
+			position: relative;
+			@include flex;
+			align-items: center;
+			&--primary {
+				color: $uv-icon-primary;
+			}
+			&--success {
+				color: $uv-icon-success;
+			}
+			&--error {
+				color: $uv-icon-error;
+			}
+			&--warning {
+				color: $uv-icon-warning;
+			}
+			&--info {
+				color: $uv-icon-info;
+			}
+		}
+		&__img {
+			/* #ifndef APP-NVUE */
+			height: auto;
+			will-change: transform;
+			/* #endif */
+		}
+		&__label {
+			/* #ifndef APP-NVUE */
+			line-height: $uv-icon-label-line-height;
+			/* #endif */
+		}
+	}
+</style>

BIN
uni_modules/uv-icon/components/uv-icon/uvicons.ttf


+ 83 - 0
uni_modules/uv-icon/package.json

@@ -0,0 +1,83 @@
+{
+  "id": "uv-icon",
+  "displayName": "uv-icon 图标  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.5",
+  "description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
+  "keywords": [
+    "uv-ui,uvui,uv-icon,icon,图标,字体图标"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "插件不采集任何数据",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "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",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 11 - 0
uni_modules/uv-icon/readme.md

@@ -0,0 +1,11 @@
+## uv-icon 图标库
+
+> **组件名:uv-icon**
+
+基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。
+
+### <a href="https://www.uvui.cn/components/icon.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:549833913 或 <a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 5 - 0
uni_modules/uv-link/changelog.md

@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-link 超链接组件

+ 40 - 0
uni_modules/uv-link/components/uv-link/props.js

@@ -0,0 +1,40 @@
+export default {
+	props: {
+		// 文字颜色
+		color: {
+			type: String,
+			default: ''
+		},
+		// 字体大小,单位px
+		fontSize: {
+			type: [String, Number],
+			default: 14
+		},
+		// 是否显示下划线
+		underLine: {
+			type: Boolean,
+			default: false
+		},
+		// 要跳转的链接
+		href: {
+			type: String,
+			default: ''
+		},
+		// 小程序中复制到粘贴板的提示语
+		mpTips: {
+			type: String,
+			default: '链接已复制,请在浏览器打开'
+		},
+		// 下划线颜色
+		lineColor: {
+			type: String,
+			default: ''
+		},
+		// 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色
+		text: {
+			type: String,
+			default: ''
+		},
+		...uni.$uv?.props?.link
+	}
+}

+ 83 - 0
uni_modules/uv-link/components/uv-link/uv-link.vue

@@ -0,0 +1,83 @@
+<template>
+	<text
+	    class="uv-link"
+	    @tap.stop="openLink"
+	    :style="[linkStyle, $uv.addStyle(customStyle)]"
+	>{{text}}</text>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * link 超链接
+	 * @description 该组件为超链接组件,在不同平台有不同表现形式:在APP平台会通过plus环境打开内置浏览器,在小程序中把链接复制到粘贴板,同时提示信息,在H5中通过window.open打开链接。
+	 * @tutorial https://www.uvui.cn/components/link.html
+	 * @property {String}			color		文字颜色 (默认 color['uv-primary'] )
+	 * @property {String | Number}	fontSize	字体大小,单位px (默认 15 )
+	 * @property {Boolean}			underLine	是否显示下划线 (默认 false )
+	 * @property {String}			href		跳转的链接,要带上http(s)
+	 * @property {String}			mpTips		各个小程序平台把链接复制到粘贴板后的提示语(默认“链接已复制,请在浏览器打开”)
+	 * @property {String}			lineColor	下划线颜色,默认同color参数颜色 
+	 * @property {String}			text		超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色 
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <uv-link href="http://www.uvui.cn">蜀道难,难于上青天</uv-link>
+	 */
+	export default {
+		name: "uv-link",
+		emits:['click'],
+		mixins: [mpMixin, mixin, props],
+		computed: {
+			linkStyle() {
+				const style = {
+					color: this.color,
+					fontSize: this.$uv.addUnit(this.fontSize),
+					// line-height设置为比字体大小多2px
+					lineHeight: this.$uv.addUnit(this.$uv.getPx(this.fontSize) + 2),
+					textDecoration: this.underLine ? 'underline' : 'none'
+				}
+				return style
+			}
+		},
+		methods: {
+			openLink() {
+				// #ifdef APP-PLUS
+				plus.runtime.openURL(this.href)
+				// #endif
+				// #ifdef H5
+				window.open(this.href)
+				// #endif
+				// #ifdef MP
+				uni.setClipboardData({
+					data: this.href,
+					success: () => {
+						uni.hideToast();
+						this.$nextTick(() => {
+							toast(this.mpTips);
+						})
+					}
+				});
+				// #endif
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	$uv-link-line-height:1 !default;
+
+	.uv-link {
+		/* #ifndef APP-NVUE */
+		line-height: $uv-link-line-height;
+		/* #endif */
+		@include flex;
+		flex-wrap: wrap;
+		flex: 1;
+		color: $uv-primary;
+	}
+</style>

+ 87 - 0
uni_modules/uv-link/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-link",
+  "displayName": "uv-link 超链接  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.1",
+  "description": "uv-link 该组件为超链接组件",
+  "keywords": [
+    "uv-link",
+    "uvui",
+    "uv-ui",
+    "link",
+    "超链接"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-link/readme.md

@@ -0,0 +1,11 @@
+## Link 超链接
+
+> **组件名:uv-link**
+
+该组件为超链接组件,在不同平台有不同表现形式。
+
+### <a href="https://www.uvui.cn/components/link.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 7 - 0
uni_modules/uv-safe-bottom/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-07-02)
+uv-safe-bottom 修复,在百度程序,抖音小程序不生效的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-safe-bottom 底部安全区组件

+ 67 - 0
uni_modules/uv-safe-bottom/components/uv-safe-bottom/uv-safe-bottom.vue

@@ -0,0 +1,67 @@
+<template>
+	<view
+		class="uv-safe-bottom"
+		:style="[style]"
+		:class="[!isNvue && 'uv-safe-area-inset-bottom']"
+	>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	/**
+	 * SafeBottom 底部安全区
+	 * @description 这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
+	 * @tutorial https://www.uvui.cn/components/safeAreaInset.html
+	 * @property {type}		prop_name
+	 * @property {Object}	customStyle	定义需要用到的外部样式
+	 *
+	 * @event {Function()}
+	 * @example <uv-status-bar></uv-status-bar>
+	 */
+	export default {
+		name: "uv-safe-bottom",
+		mixins: [mpMixin, mixin],
+		data() {
+			return {
+				safeAreaBottomHeight: 0,
+				isNvue: false,
+			};
+		},
+		computed: {
+			style() {
+				const style = {};
+				// #ifdef APP-NVUE || MP-TOUTIAO || MP-BAIDU
+				// nvue下,高度使用js计算填充
+				style.height = this.$uv.addUnit(this.$uv.sys().safeAreaInsets.bottom, 'px');
+				// #endif
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+			},
+		},
+		mounted() {
+			// #ifdef APP-NVUE
+			// 标识为是否nvue
+			this.isNvue = true;
+			// #endif
+		},
+	};
+</script>
+
+<style lang="scss" scoped>
+	.uv-safe-bottom {
+		/* #ifndef APP-NVUE */
+		width: 100%;
+		/* #endif */
+	}
+	/* #ifndef APP-NVUE */
+	// 历遍生成4个方向的底部安全区
+	@each $d in top, right, bottom, left {
+		.uv-safe-area-inset-#{$d} {
+			padding-#{$d}: 0;
+			padding-#{$d}: constant(safe-area-inset-#{$d});  
+			padding-#{$d}: env(safe-area-inset-#{$d});  
+		}
+	}
+	/* #endif */
+</style>

+ 87 - 0
uni_modules/uv-safe-bottom/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-safe-bottom",
+  "displayName": "uv-safe-bottom 底部安全区  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.2",
+  "description": "这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。",
+  "keywords": [
+    "uv-safe-bottom",
+    "uvui",
+    "uv-ui",
+    "bottom",
+    "底部安全区"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-safe-bottom/readme.md

@@ -0,0 +1,11 @@
+## SafeBottom 底部安全区 
+
+> **组件名:uv-safe-bottom**
+
+这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
+
+### <a href="https://www.uvui.cn/guide/safeAreaInset.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 10 - 0
uni_modules/uv-steps/changelog.md

@@ -0,0 +1,10 @@
+## 1.0.3(2023-06-29)
+1. 增加customStyle参数,方便设置样式
+## 1.0.2(2023-06-29)
+1. 新增title自定义
+2. 完善文档说明
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-steps 步骤条

+ 25 - 0
uni_modules/uv-steps/components/uv-steps-item/props.js

@@ -0,0 +1,25 @@
+export default {
+	props: {
+		// 标题
+		title: {
+			type: [String, Number],
+			default: ''
+		},
+		// 描述文本
+		desc: {
+			type: [String, Number],
+			default: ''
+		},
+		// 图标大小
+		iconSize: {
+			type: [String, Number],
+			default: 17
+		},
+		// 当前步骤是否处于失败状态
+		error: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.stepsItem
+	}
+}

+ 347 - 0
uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue

@@ -0,0 +1,347 @@
+<template>
+	<view 
+		class="uv-steps-item" 
+		ref="uv-steps-item" 
+		:class="[`uv-steps-item--${parentData.direction}`]"
+		:style="[$uv.addStyle(customStyle)]"
+		>
+		<view 
+			class="uv-steps-item__line" 
+			v-if="index + 1 < childLength"
+			:class="[`uv-steps-item__line--${parentData.direction}`]" 
+			:style="[lineStyle]">
+		</view>
+		<view 
+			:class="[
+				'uv-steps-item__wrapper',
+				`uv-steps-item__wrapper--${parentData.direction}`, 
+				parentData.dot && `uv-steps-item__wrapper--${parentData.direction}--dot`
+			]">
+			<slot name="icon">
+				<view 
+					class="uv-steps-item__wrapper__dot" 
+					v-if="parentData.dot" 
+					:style="{
+						backgroundColor: statusColor
+					}">
+				</view>
+				<view 
+					class="uv-steps-item__wrapper__icon" 
+					v-else-if="parentData.activeIcon || parentData.inactiveIcon">
+					<uv-icon 
+						:name="index <= parentData.current ? parentData.activeIcon : parentData.inactiveIcon"
+						:size="iconSize"
+						:color="index <= parentData.current ? parentData.activeColor : parentData.inactiveColor">
+					</uv-icon>
+				</view>
+				<view 
+					v-else 
+					:style="{
+						backgroundColor: statusClass === 'process' ? parentData.activeColor : 'transparent',
+						borderColor: statusColor
+					}" 
+					class="uv-steps-item__wrapper__circle">
+					<text 
+						v-if="statusClass === 'process' || statusClass === 'wait'"
+						class="uv-steps-item__wrapper__circle__text" 
+						:style="{
+							color: index == parentData.current ? '#ffffff' : parentData.inactiveColor
+						}">{{ index + 1}}</text>
+					<uv-icon 
+						v-else 
+						:color="statusClass === 'error' ? 'error' : parentData.activeColor" 
+						size="12"
+						:name="statusClass === 'error' ? 'close' : 'checkmark'">
+					</uv-icon>
+				</view>
+			</slot>
+		</view>
+		<view 
+			:class="[
+				'uv-steps-item__content',
+				`uv-steps-item__content--${parentData.direction}`
+			]"
+			:style="[contentStyle]"
+		>
+			<slot name="title">
+				<uv-text
+					:text="title" 
+					:type="parentData.current == index ? 'main' : 'content'" 
+					lineHeight="20px"
+					:size="parentData.current == index ? 14 : 13"
+				></uv-text>
+			</slot>
+			<slot name="desc">
+				<uv-text 
+					:text="desc" 
+					type="tips" 
+					size="12"
+				></uv-text>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	/**
+	 * StepsItem 步骤条的子组件
+	 * @description 本组件需要和uv-steps配合使用
+	 * @tutorial https://www.uvui.cn/components/steps.html
+	 * @property {String}			title			标题文字
+	 * @property {String}			current			描述文本
+	 * @property {String | Number}	iconSize		图标大小  (默认 17 )
+	 * @property {Boolean}			error			当前步骤是否处于失败状态  (默认 false )
+	 * @example <uv-steps current="0"><uv-steps-item title="已出库" desc="10:35" ></uv-steps-item></uv-steps>
+	 */
+	export default {
+		name: 'uv-steps-item',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				index: 0,
+				childLength: 0,
+				showLine: false,
+				size: {
+					height: 0,
+					width: 0
+				},
+				parentData: {
+					direction: 'row',
+					current: 0,
+					activeColor: '',
+					inactiveColor: '',
+					activeIcon: '',
+					inactiveIcon: '',
+					dot: false
+				}
+			}
+		},
+		watch: {
+			'parentData'(newValue, oldValue) {}
+		},
+		created() {
+			this.init()
+		},
+		computed: {
+			lineStyle() {
+				const style = {}
+				if (this.parentData.direction === 'row') {
+					style.width = this.size.width + 'px'
+					style.left = this.size.width / 2 + 'px'
+				} else {
+					style.height = this.size.height + 'px'
+				}
+				style.backgroundColor = this.parent.children?.[this.index + 1]?.error ? '#f56c6c' : this.index < this.parentData.current ? this.parentData.activeColor : this.parentData.inactiveColor
+				return style
+			},
+			statusClass() {
+				const {
+					index,
+					error
+				} = this
+				const {
+					current
+				} = this.parentData
+				if (current == index) {
+					return error === true ? 'error' : 'process'
+				} else if (error) {
+					return 'error'
+				} else if (current > index) {
+					return 'finish'
+				} else {
+					return 'wait'
+				}
+			},
+			statusColor() {
+				let color = ''
+				switch (this.statusClass) {
+					case 'finish':
+						color = this.parentData.activeColor
+						break
+					case 'error':
+						color = '#f56c6c'
+						break
+					case 'process':
+						color = this.parentData.dot ? this.parentData.activeColor : 'transparent'
+						break
+					default:
+						color = this.parentData.inactiveColor
+						break
+				}
+				return color
+			},
+			contentStyle() {
+				const style = {}
+				if (this.parentData.direction === 'column') {
+					style.marginLeft = this.parentData.dot ? '2px' : '6px'
+					style.marginTop = this.parentData.dot ? '0px' : '6px'
+				} else {
+					style.marginTop = this.parentData.dot ? '2px' : '6px'
+					style.marginLeft = this.parentData.dot ? '2px' : '6px'
+				}
+				return style
+			}
+		},
+		mounted() {
+			this.parent && this.parent.updateFromChild()
+			this.$uv.sleep().then(() => {
+				this.getStepsItemRect()
+			})
+		},
+		methods: {
+			init() {
+				// 初始化数据
+				this.updateParentData()
+				if (!this.parent) {
+					return this.$uv.error('uv-steps-item必须要搭配uv-steps组件使用')
+				}
+				this.index = this.parent.children.indexOf(this)
+				this.childLength = this.parent.children.length
+			},
+			updateParentData() {
+				// 此方法在mixin中
+				this.getParentData('uv-steps')
+			},
+			// 父组件数据发生变化
+			updateFromParent() {
+				this.init()
+			},
+			// 获取组件的尺寸,用于设置横线的位置
+			getStepsItemRect() {
+				// #ifndef APP-NVUE
+				this.$uvGetRect('.uv-steps-item').then(size => {
+					this.size = size
+				})
+				// #endif
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['uv-steps-item'], res => {
+					const {
+						size
+					} = res
+					this.size = size
+				})
+				// #endif
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	.uv-steps-item {
+		flex: 1;
+		@include flex;
+
+		&--row {
+			flex-direction: column;
+			align-items: center;
+			position: relative;
+		}
+
+		&--column {
+			position: relative;
+			flex-direction: row;
+			justify-content: flex-start;
+			padding-bottom: 5px;
+		}
+
+		&__wrapper {
+			@include flex;
+			justify-content: center;
+			align-items: center;
+			position: relative;
+			background-color: #fff;
+
+			&--column {
+				width: 20px;
+				height: 32px;
+
+				&--dot {
+					height: 20px;
+					width: 20px;
+				}
+			}
+
+			&--row {
+				width: 32px;
+				height: 20px;
+
+				&--dot {
+					width: 20px;
+					height: 20px;
+				}
+			}
+
+			&__circle {
+				width: 20px;
+				height: 20px;
+				/* #ifndef APP-NVUE */
+				box-sizing: border-box;
+				flex-shrink: 0;
+				/* #endif */
+				border-radius: 100px;
+				border-width: 1px;
+				border-color: $uv-tips-color;
+				border-style: solid;
+				@include flex(row);
+				align-items: center;
+				justify-content: center;
+				transition: background-color 0.3s;
+
+				&__text {
+					color: $uv-tips-color;
+					font-size: 11px;
+					@include flex(row);
+					align-items: center;
+					justify-content: center;
+					text-align: center;
+					line-height: 11px;
+				}
+			}
+
+			&__dot {
+				width: 10px;
+				height: 10px;
+				border-radius: 100px;
+				background-color: $uv-content-color;
+			}
+		}
+
+		&__content {
+			@include flex;
+			flex: 1;
+
+			&--row {
+				flex-direction: column;
+				align-items: center;
+			}
+
+			&--column {
+				flex-direction: column;
+				margin-left: 6px;
+			}
+		}
+
+		&__line {
+			position: absolute;
+			background: $uv-tips-color;
+
+			&--row {
+				top: 10px;
+				height: 1px;
+			}
+
+			&--column {
+				width: 1px;
+				left: 10px;
+			}
+		}
+	}
+</style>

+ 40 - 0
uni_modules/uv-steps/components/uv-steps/props.js

@@ -0,0 +1,40 @@
+export default {
+	props: {
+		// 排列方向
+		direction: {
+			type: String,
+			default: 'row'
+		},
+		// 设置第几个步骤
+		current: {
+			type: [String, Number],
+			default: 0
+		},
+		// 激活状态颜色
+		activeColor: {
+			type: String,
+			default: '#3c9cff'
+		},
+		// 未激活状态颜色
+		inactiveColor: {
+			type: String,
+			default: '#969799'
+		},
+		// 激活状态的图标
+		activeIcon: {
+			type: String,
+			default: ''
+		},
+		// 未激活状态图标
+		inactiveIcon: {
+			type: String,
+			default: ''
+		},
+		// 是否显示点类型
+		dot: {
+			type: Boolean,
+			default: false
+		},
+		...uni.$uv?.props?.steps
+	}
+}

+ 83 - 0
uni_modules/uv-steps/components/uv-steps/uv-steps.vue

@@ -0,0 +1,83 @@
+<template>
+	<view
+	  :class="['uv-steps',`uv-steps--${direction}`]"
+		:style="[$uv.addStyle(customStyle)]"
+	>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import { func } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * Steps 步骤条
+	 * @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
+	 * @tutorial https://www.uvui.cn/components/steps.html
+	 * @property {String}			direction		row-横向,column-竖向 (默认 'row' )
+	 * @property {String | Number}	current			设置当前处于第几步 (默认 0 )
+	 * @property {String}			activeColor		激活状态颜色 (默认 '#3c9cff' )
+	 * @property {String}			inactiveColor	未激活状态颜色 (默认 '#969799' )
+	 * @property {String}			activeIcon		激活状态的图标
+	 * @property {String}			inactiveIcon	未激活状态图标 
+	 * @property {Boolean}			dot				是否显示点类型 (默认 false )
+	 * @example <uv-steps current="0"><uv-steps-item title="已出库" desc="10:35" ></uv-steps-item></uv-steps>
+	 */
+	export default {
+		name: 'uv-steps',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+			}
+		},
+		watch: {
+			children() {
+				this.updateChildData()
+			},
+			parentData() {
+				this.updateChildData()
+			}
+		},
+		computed: {
+			// 监听参数的变化,通过watch中,手动去更新子组件的数据,否则子组件不会自动变化
+			parentData() {
+				return [this.current, this.direction, this.activeColor, this.inactiveColor, this.activeIcon, this.inactiveIcon, this.dot]
+			}
+		},
+		methods: {
+			// 更新子组件的数据
+			updateChildData() {
+				this.children.map(child => {
+					// 先判断子组件是否存在对应的方法
+					func((child || {}).updateFromParent()) && child.updateFromParent()
+				})
+			},
+			// 接受子组件的通知,去修改其他子组件的数据
+			updateFromChild() {
+				this.updateChildData()
+			}
+		},
+		created() {
+			this.children = []
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+	.uv-steps {
+		@include flex;
+
+		&--column {
+			flex-direction: column
+		}
+
+		&--row {
+			flex-direction: row;
+			flex: 1;
+		}
+	}
+</style>

+ 89 - 0
uni_modules/uv-steps/package.json

@@ -0,0 +1,89 @@
+{
+  "id": "uv-steps",
+  "displayName": "uv-steps 步骤条  全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.3",
+  "description": "步骤条组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。",
+  "keywords": [
+    "uv-steps",
+    "uvui",
+    "uv-ui",
+    "steps",
+    "步骤条"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-icon",
+			"uv-text"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-steps/readme.md

@@ -0,0 +1,11 @@
+## Steps 步骤条
+
+> **组件名:uv-steps**
+
+该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
+
+### <a href="https://www.uvui.cn/components/steps.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 11 - 0
uni_modules/uv-tabbar/changelog.md

@@ -0,0 +1,11 @@
+## 1.0.4(2023-06-13)
+1. 底部安全距离依赖添加
+## 1.0.3(2023-06-09)
+1. 增加iconSize参数
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次事件的BUG 
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-tabbar 底部导航栏 

+ 40 - 0
uni_modules/uv-tabbar/components/uv-tabbar-item/props.js

@@ -0,0 +1,40 @@
+export default {
+	props: {
+		// item标签的名称,作为与uv-tabbar的value参数匹配的标识符
+		name: {
+			type: [String, Number, null],
+			default: null
+		},
+		// uv-ui内置图标或者绝对路径的图片
+		icon: {
+			icon: String,
+			default: ''
+		},
+		// 图标大小,默认uv-tabbar的iconSize=20
+		iconSize: {
+			type: [String, Number],
+			default: ''
+		},
+		// 右上角的角标提示信息
+		badge: {
+			type: [String, Number, null],
+			default: null
+		},
+		// 是否显示圆点,将会覆盖badge参数
+		dot: {
+			type: Boolean,
+			default: false
+		},
+		// 描述文本
+		text: {
+			type: String,
+			default: ''
+		},
+		// 控制徽标的位置,对象或者字符串形式,可以设置top和right属性
+		badgeStyle: {
+			type: [Object, String],
+			default: 'top: 6px;right:2px;'
+		},
+		...uni.$uv?.props?.tabbarItem
+	}
+}

+ 146 - 0
uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue

@@ -0,0 +1,146 @@
+<template>
+	<view
+	    class="uv-tabbar-item"
+	    :style="[$uv.addStyle(customStyle)]"
+	    @tap="clickHandler"
+	>
+		<view class="uv-tabbar-item__icon">
+			<uv-icon
+			    v-if="icon"
+			    :name="icon"
+			    :color="isActive? parentData.activeColor : parentData.inactiveColor"
+			    :size="iconSize? iconSize: parentData.iconSize"
+			></uv-icon>
+			<template v-else>
+				<slot
+				    v-if="isActive"
+				    name="active-icon"
+				/>
+				<slot
+				    v-else
+				    name="inactive-icon"
+				/>
+			</template>
+			<uv-badge
+				absolute
+				:offset="[0, dot ? '34rpx' : badge > 9 ? '14rpx' : '20rpx']"
+			    :customStyle="badgeStyle"
+			    :isDot="dot"
+			    :value="badge || (dot ? 1 : null)"
+			    :show="dot || badge > 0"
+			></uv-badge>
+		</view>
+		
+		<slot name="text">
+			<text
+			    class="uv-tabbar-item__text"
+			    :style="{
+					color: isActive? parentData.activeColor : parentData.inactiveColor
+				}"
+			>{{ text }}</text>
+		</slot>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	/**
+	 * TabbarItem 底部导航栏子组件
+	 * @description 此组件提供了自定义tabbar的能力。
+	 * @tutorial https://www.uvui.cn/components/tabbar.html
+	 * @property {String | Number}	name		item标签的名称,作为与uv-tabbar的value参数匹配的标识符
+	 * @property {String}			icon		uvui内置图标或者绝对路径的图片
+	 * @property {String | Number}	badge		右上角的角标提示信息
+	 * @property {Boolean}			dot			是否显示圆点,将会覆盖badge参数(默认 false )
+	 * @property {String}			text		描述文本
+	 * @property {Object | String}	badgeStyle	控制徽标的位置,对象或者字符串形式,可以设置top和right属性(默认 'top: 6px;right:2px;' )
+	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * 
+	 * @example <uv-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><uv-tabbar-item text="首页" icon="home" dot ></uv-tabbar-item></uv-tabbar>
+	 */
+	export default {
+		name: 'uv-tabbar-item',
+		mixins: [mpMixin, mixin, props],
+		emits: ['click','change'],
+		data() {
+			return {
+				isActive: false, // 是否处于激活状态
+				parentData: {
+					value: null,
+					activeColor: '',
+					inactiveColor: '',
+					iconSize: 20
+				}
+			}
+		},
+		created() {
+			this.init()
+		},
+		methods: {
+			init() {
+				// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+				this.updateParentData()
+				if (!this.parent) {
+					this.$uv.error('uv-tabbar-item必须搭配uv-tabbar组件使用')
+				}
+				// 本子组件在uv-tabbar的children数组中的索引
+				const index = this.parent.children.indexOf(this)
+				// 判断本组件的name(如果没有定义name,就用index索引)是否等于父组件的value参数
+				this.isActive = (this.name || index) === this.parentData.value
+			},
+			updateParentData() {
+				// 此方法在mixin中
+				this.getParentData('uv-tabbar')
+			},
+			// 此方法将会被父组件uv-tabbar调用
+			updateFromParent() {
+				// 重新初始化
+				this.init()
+			},
+			clickHandler() {
+				this.$nextTick(() => {
+					const index = this.parent.children.indexOf(this)
+					const name = this.name || index
+					// 点击的item为非激活的item才发出change事件
+					if (name !== this.parent.value) {
+						this.parent.$emit('change', name)
+					}
+					this.$emit('click', name)
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	.uv-tabbar-item {
+		@include flex(column);
+		align-items: center;
+		justify-content: center;
+		flex: 1;
+		
+		&__icon {
+			@include flex;
+			position: relative;
+			width: 150rpx;
+			justify-content: center;
+		}
+
+		&__text {
+			margin-top: 2px;
+			font-size: 12px;
+			color: $uv-content-color;
+		}
+	}
+
+	/* #ifdef MP */
+	// 由于小程序都使用shadow DOM形式实现,需要给影子宿主设置flex: 1才能让其撑开
+	:host {
+		flex: 1
+	}
+	/* #endif */
+</style>

+ 50 - 0
uni_modules/uv-tabbar/components/uv-tabbar/props.js

@@ -0,0 +1,50 @@
+export default {
+	props: {
+		// 当前匹配项的name
+		value: {
+			type: [String, Number, null],
+			default: null
+		},
+		// 是否为iPhoneX留出底部安全距离
+		safeAreaInsetBottom: {
+			type: Boolean,
+			default: true
+		},
+		// 是否显示上方边框
+		border: {
+			type: Boolean,
+			default: true
+		},
+		// 元素层级z-index
+		zIndex: {
+			type: [String, Number],
+			default: 9
+		},
+		// 选中标签的颜色
+		activeColor: {
+			type: String,
+			default: '#1989fa'
+		},
+		// 未选中标签的颜色
+		inactiveColor: {
+			type: String,
+			default: '#7d7e80'
+		},
+		// 是否固定在底部
+		fixed: {
+			type: Boolean,
+			default: true
+		},
+		// fixed定位固定在底部时,是否生成一个等高元素防止塌陷
+		placeholder: {
+			type: Boolean,
+			default: true
+		},
+		// 图标大小
+		iconSize: {
+			type: [String, Number],
+			default: 20
+		},
+		...uni.$uv?.props?.tabbar
+	}
+}

+ 146 - 0
uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue

@@ -0,0 +1,146 @@
+<template>
+	<view class="uv-tabbar">
+		<view
+		    class="uv-tabbar__content"
+		    ref="uv-tabbar__content"
+		    @touchmove.stop.prevent="noop"
+		    :class="[border && 'uv-border-top', fixed && 'uv-tabbar--fixed']"
+		    :style="[tabbarStyle]"
+		>
+			<view class="uv-tabbar__content__item-wrapper">
+				<slot />
+			</view>
+			<uv-safe-bottom v-if="safeAreaInsetBottom"></uv-safe-bottom>
+		</view>
+		<view
+		    class="uv-tabbar__placeholder"
+			v-if="placeholder"
+		    :style="{
+				height: placeholderHeight + 'px',
+			}"
+		></view>
+	</view>
+</template>
+
+<script>
+	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	/**
+	 * Tabbar 底部导航栏
+	 * @description 此组件提供了自定义tabbar的能力。
+	 * @tutorial https://www.uvui.cn/components/tabbar.html
+	 * @property {String | Number}	value				当前匹配项的name
+	 * @property {Boolean}			safeAreaInsetBottom	是否为iPhoneX留出底部安全距离(默认 true )
+	 * @property {Boolean}			border				是否显示上方边框(默认 true )
+	 * @property {String | Number}	zIndex				元素层级z-index(默认 1 )
+	 * @property {String}			activeColor			选中标签的颜色(默认 '#1989fa' )
+	 * @property {String}			inactiveColor		未选中标签的颜色(默认 '#7d7e80' )
+	 * @property {Boolean}			fixed				是否固定在底部(默认 true )
+	 * @property {Boolean}			placeholder			fixed定位固定在底部时,是否生成一个等高元素防止塌陷(默认 true )
+	 * @property {Object}			customStyle			定义需要用到的外部样式
+	 * 
+	 * @example <uv-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><uv-tabbar-item text="首页" icon="home" dot ></uv-tabbar-item></uv-tabbar>
+	 */
+	export default {
+		name: 'uv-tabbar',
+		mixins: [mpMixin, mixin, props],
+		data() {
+			return {
+				placeholderHeight: 0
+			}
+		},
+		computed: {
+			tabbarStyle() {
+				const style = {
+					zIndex: this.zIndex
+				}
+				// 合并来自父组件的customStyle样式
+				return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+			},
+			// 监听多个参数的变化,通过在computed执行对应的操作
+			updateChild() {
+				return [this.value, this.activeColor, this.inactiveColor]
+			},
+			updatePlaceholder() {
+				return [this.fixed, this.placeholder]
+			}
+		},
+		watch: {
+			updateChild() {
+				// 如果updateChildren中的元素发生了变化,则执行子元素初始化操作
+				this.updateChildren()
+			},
+			updatePlaceholder() {
+				// 如果fixed,placeholder等参数发生变化,重新计算占位元素的高度
+				this.setPlaceholderHeight()
+			}
+		},
+		created() {
+			this.children = []
+		},
+		mounted() {
+			this.setPlaceholderHeight()
+		},
+		methods: {
+			updateChildren() {
+				// 如果存在子元素,则执行子元素的updateFromParent进行更新数据
+				this.children.length && this.children.map(child => child.updateFromParent())
+			},
+			// 设置用于防止塌陷元素的高度
+			async setPlaceholderHeight() {
+				if (!this.fixed || !this.placeholder) return
+				// 延时一定时间
+				await this.$uv.sleep(20)
+				// #ifndef APP-NVUE
+				this.$uvGetRect('.uv-tabbar__content').then(({height = 50}) => {
+					// 修复IOS safearea bottom 未填充高度
+					this.placeholderHeight = height
+				})
+				// #endif
+
+				// #ifdef APP-NVUE
+				dom.getComponentRect(this.$refs['uv-tabbar__content'], (res) => {
+					const {
+						size
+					} = res
+					this.placeholderHeight = size.height
+				})
+				// #endif
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$show-border: 1;
+	$show-border-top: 1;
+	@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+	@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+	.uv-tabbar {
+		@include flex(column);
+		flex: 1;
+		justify-content: center;
+		
+		&__content {
+			@include flex(column);
+			background-color: #fff;
+			
+			&__item-wrapper {
+				height: 50px;
+				@include flex(row);
+			}
+		}
+
+		&--fixed {
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			right: 0;
+		}
+	}
+</style>

+ 90 - 0
uni_modules/uv-tabbar/package.json

@@ -0,0 +1,90 @@
+{
+  "id": "uv-tabbar",
+  "displayName": "uv-tabbar 底部导航栏   全面兼容小程序、nvue、vue2、vue3等多端",
+  "version": "1.0.4",
+  "description": "底部导航栏组件提供了自定义tabbar的能力。",
+  "keywords": [
+    "uv-tabbar",
+    "uvui",
+    "uv-ui",
+    "tabbar",
+    "底部导航栏"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools",
+			"uv-icon",
+			"uv-badge",
+			"uv-safe-bottom"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"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",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 11 - 0
uni_modules/uv-tabbar/readme.md

@@ -0,0 +1,11 @@
+## Tabbar 底部导航栏
+
+> **组件名:uv-tabbar**
+
+此组件提供了自定义tabbar的能力。
+
+### <a href="https://www.uvui.cn/components/tabbar.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 7 - 0
uni_modules/uv-text/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-05-24)
+1. 去掉多余的data-index属性,避免警告
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-text 文本组件

+ 113 - 0
uni_modules/uv-text/components/uv-text/props.js

@@ -0,0 +1,113 @@
+export default {
+	props: {
+		// 主题颜色
+		type: {
+			type: String,
+			default: ''
+		},
+		// 是否显示
+		show: {
+			type: Boolean,
+			default: true
+		},
+		// 显示的值
+		text: {
+			type: [String, Number],
+			default: ''
+		},
+		// 前置图标
+		prefixIcon: {
+			type: String,
+			default: ''
+		},
+		// 后置图标
+		suffixIcon: {
+			type: String,
+			default: ''
+		},
+		// 文本处理的匹配模式
+		// text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+		mode: {
+			type: String,
+			default: ''
+		},
+		// mode=link下,配置的链接
+		href: {
+			type: String,
+			default: ''
+		},
+		// 格式化规则
+		format: {
+			type: [String, Function],
+			default: ''
+		},
+		// mode=phone时,点击文本是否拨打电话
+		call: {
+			type: Boolean,
+			default: true
+		},
+		// 小程序的打开方式
+		openType: {
+			type: String,
+			default: ''
+		},
+		// 是否粗体,默认normal
+		bold: {
+			type: Boolean,
+			default: false
+		},
+		// 是否块状
+		block: {
+			type: Boolean,
+			default: false
+		},
+		// 文本显示的行数,如果设置,超出此行数,将会显示省略号
+		lines: {
+			type: [String, Number],
+			default: ''
+		},
+		// 文本颜色
+		color: {
+			type: String,
+			default: '#303133'
+		},
+		// 字体大小
+		size: {
+			type: [String, Number],
+			default: 15
+		},
+		// 图标的样式
+		iconStyle: {
+			type: [Object, String],
+			default: () => ({
+				fontSize: '15px'
+			})
+		},
+		// 文字装饰,下划线,中划线等,可选值 none|underline|line-through
+		decoration: {
+			type: String,
+			default: 'none'
+		},
+		// 外边距,对象、字符串,数值形式均可
+		margin: {
+			type: [Object, String, Number],
+			default: 0
+		},
+		// 文本行高
+		lineHeight: {
+			type: [String, Number],
+			default: ''
+		},
+		// 文本对齐方式,可选值left|center|right
+		align: {
+			type: String,
+			default: 'left'
+		},
+		// 文字换行,可选值break-word|normal|anywhere
+		wordWrap: {
+			type: String,
+			default: 'normal'
+		},
+		...uni.$uv?.props?.text
+	}
+}

+ 0 - 0
uni_modules/uv-text/components/uv-text/uv-text.vue


Some files were not shown because too many files changed in this diff