Xiao_123 vor 8 Monaten
Ursprung
Commit
f9223fac54

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+.DS_Store
+node_modules
+/dist
+/mtk/url.js
+unpackage
+ 
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+ 
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 20 - 3
App.vue

@@ -1,7 +1,6 @@
 <script>
 	export default {
 		onLaunch: function() {
-			console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
 			console.log('App Launch')
 		},
 		onShow: function() {
@@ -17,10 +16,10 @@
 	/*每个页面公共css */
 	@import '@/uni_modules/uni-scss/index.scss';
 	/* #ifndef APP-NVUE */
-	@import '@/static/customicons.css';
+	@import '@/static/iconfont.css';
 	// 设置整个项目的背景色
 	page {
-		background-color: #f5f5f5;
+		background-color: #ffffff;
 	}
 
 	/* #endif */
@@ -29,4 +28,22 @@
 		color: #333;
 		padding: 10px;
 	}
+
+		/* 隐藏scroll-view的滚动条 */
+		::-webkit-scrollbar {
+    display: none;
+    width: 0 !important;
+    height: 0 !important;
+    -webkit-appearance: none;
+    background: transparent;
+    color: transparent;
+  }
+	uni-scroll-view .uni-scroll-view::-webkit-scrollbar {
+    display: none;
+    width: 0 !important;
+    height: 0 !important;
+    -webkit-appearance: none;
+    background: transparent;
+    color: transparent;
+  }
 </style>

+ 15 - 0
api/auth.js

@@ -0,0 +1,15 @@
+import request from "@/utils/request"
+
+// 刷新令牌
+export const refreshToken = async (refreshToken) => {
+  return await request.post({
+    url: '/menduner/system/auth/refresh-token',
+    params: {
+      refreshToken
+    },
+    custom: {
+      showLoading: false,
+      showError: false
+    }
+  })
+}

+ 13 - 0
api/user.js

@@ -0,0 +1,13 @@
+import request from "@/utils/request"
+
+// 获取用户基本信息
+export const getUserInfo = async (params) => {
+  return await request.get({
+    url: '/member/user/get',
+    params,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}

+ 88 - 0
components/Tabbar/index.vue

@@ -0,0 +1,88 @@
+<template>
+	<view class="myTabbar">
+		<view class="tabbar"
+			v-for="(item, index) in list"
+			:key="index"
+			@tap="navigatorTo(index, item.url)"
+			style="text-align:center"
+		>
+			<view class="item">
+				<uni-icons  :type="index == activeIndex ? item.activeIconUrl : item.iconUrl" size="26" :color="index == activeIndex ?'#00897B':'#000'" custom-prefix="iconfont"></uni-icons>
+			</view>
+			<view :class="index == activeIndex ? 'text-active':'text'">{{item.text}}</view>
+		</view>
+	</view>
+</template>
+
+<script setup name="MyTabbar">
+import { ref } from 'vue'
+
+const activeIndex = ref(0)
+const list = ref([
+  {
+    activeIconUrl: 'icon-shouye-',
+    iconUrl: 'icon-shouye',
+    text: '首页',
+    url: '/pages/index/index'
+  },
+  {
+    activeIconUrl: 'icon-zhiwei',
+    iconUrl: 'icon-zhiweisvg',
+    text: '职位',
+    url: '/pages/index/position'
+  },
+  {
+    activeIconUrl: 'icon-icon-myself-1',
+    iconUrl: 'icon-wode',
+    text: '我的',
+    url: '/pages/index/my'
+  }
+])
+
+const navigatorTo = (curIndex, url) => {
+	activeIndex.value = curIndex
+	uni.reLaunch({
+    url
+  })
+}
+</script>
+
+<style lang="scss">
+.myTabbar {
+	z-index: 999;
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	display: flex;
+	justify-content: space-around;
+	width: 100%;
+	height: 100rpx;
+	background-color: #fff;
+	border-top: 1px solid #f1f1f1;
+ 
+	.tabbar {
+		display: flex;
+		flex-direction: column;
+		justify-content: space-around;
+		align-items: center;
+		width: 20%;
+		height: 100%;
+ 
+		.item {
+			width: 50rpx;
+			height: 50rpx;
+			text-align: center;
+			margin-top: 10rpx;
+		}
+ 
+		.text {
+			color: #000;
+			font-size: 22rpx;
+		}
+		.text-active{
+			color: #00897B;
+			font-size: 22rpx;
+		}
+	}
+}
+</style>

+ 1 - 1
manifest.json

@@ -50,7 +50,7 @@
     /* 快应用特有相关 */
     "mp-weixin" : {
         /* 小程序特有相关 */
-        "appid" : "",
+        "appid" : "wx5dd538ccc752b03a",
         "setting" : {
             "urlCheck" : false
         },

+ 146 - 0
package-lock.json

@@ -0,0 +1,146 @@
+{
+  "name": "门墩招聘小程序",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "lodash-es": "^4.17.21",
+        "luch-request": "^3.1.1",
+        "pinia": "^2.2.2",
+        "pinia-plugin-persist-uni": "^1.3.1"
+      }
+    },
+    "node_modules/@dcloudio/types": {
+      "version": "2.6.12",
+      "resolved": "https://registry.npmmirror.com/@dcloudio/types/-/types-2.6.12.tgz",
+      "integrity": "sha512-mrCMwcINy1IFjU9VUqLeWBkj404yWs5paLDttBcA+eqUjanuUQbBcTVPqlrGgkyzLXDcV2oDDZRSNxNpXi4kMQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
+    "node_modules/luch-request": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/luch-request/-/luch-request-3.1.1.tgz",
+      "integrity": "sha512-p7+mlcEtgRcd0OfXC4XZbyiwSr1XgCeqNT7LlVUjnk7InYl/8d5Rk7BUqAYNA2WRafI1wRIUQWRWZRpeUwWR0w==",
+      "license": "MIT",
+      "dependencies": {
+        "@dcloudio/types": "^2.0.16"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.2.tgz",
+      "integrity": "sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia-plugin-persist-uni": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/pinia-plugin-persist-uni/-/pinia-plugin-persist-uni-1.3.1.tgz",
+      "integrity": "sha512-E7HEj6Hy3ZZKXCDG7yKWQ7Dg99Ibtei2qssY6w8wlqVBl3X54xSydiju8kO7IYPHw8heYAPjPBAF8TGCTdvRGQ==",
+      "dependencies": {
+        "vue-demi": "^0.12.1"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0",
+        "pinia": "^2.0.0",
+        "vue": "^2.0.0 || >=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia-plugin-persist-uni/node_modules/vue-demi": {
+      "version": "0.12.5",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz",
+      "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "2.6.14",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-2.6.14.tgz",
+      "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
+      "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 8 - 0
package.json

@@ -0,0 +1,8 @@
+{
+  "dependencies": {
+    "lodash-es": "^4.17.21",
+    "luch-request": "^3.1.1",
+    "pinia": "^2.2.2",
+    "pinia-plugin-persist-uni": "^1.3.1"
+  }
+}

+ 53 - 11
pages.json

@@ -1,17 +1,59 @@
 {
-	"pages": [{
-		"path": "pages/index/index",
-		"style": {
-			"navigationBarTitleText": "uni-app"
+	"pages": [
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "门墩儿招聘"
+			}
+		},
+		{
+			"path": "pages/index/position",
+			"style": {
+				"navigationBarTitleText": "职位"
+			}
+		},
+		{
+			"path": "pages/index/my",
+			"style": {
+				"navigationBarTitleText": "我的"
+			}
 		}
-	}],
+	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
-		"navigationBarTitleText": "uni-app",
-		"navigationBarBackgroundColor": "#F8F8F8",
-		"backgroundColor": "#F8F8F8",
-		"app-plus": {
-			"background": "#efeff4"
-		}
+		"navigationBarTitleText": "门墩儿招聘",
+		"navigationBarBackgroundColor": "#ffffff",
+		"backgroundColor": "#ffffff"
+	},
+	"tabBar": {
+		"color": "#000",
+		"selectedColor": "#00897B",
+		"borderStyle": "black",
+		"backgroundColor": "#ffffff",
+		"height": "50px",
+		"fontSize": "30rpx",
+		"iconWidth": "24px",
+		"spacing": "3px",
+		"iconfontSrc":"/static/iconfont.ttf",
+		"list": [
+			{
+				"pagePath": "pages/index/index",
+				"text": "首页",
+				"iconPath": "/static/img/home.png",
+				"selectedIconPath": "/static/img/home-fill.png"
+			},
+			{
+				"pagePath": "pages/index/position",
+				"text": "职位",
+				"iconPath": "/static/img/position.png",
+				"selectedIconPath": "/static/img/position-fill.png"
+			},
+			{
+				"pagePath": "pages/index/my",
+				"text": "我的",
+				"iconPath": "/static/img/my.png",
+				"selectedIconPath": "/static/img/my-fill.png"
+			}
+		]
 	}
 }

+ 6 - 22
pages/index/index.vue

@@ -1,29 +1,13 @@
 <template>
-	<view class="container">
-		
-		<view class="intro">本项目已包含uni ui组件,无需import和注册,可直接使用。在代码区键入字母u,即可通过代码助手列出所有可用组件。光标置于组件名称处按F1,即可查看组件文档。</view>
-		<text class="intro">详见:</text>
-		<uni-link :href="href" :text="href"></uni-link>
+	<view>
+		index
+		<!-- <MyLayout>home</MyLayout> -->
 	</view>
 </template>
 
-<script>
-	export default {
-		data() {
-			return {
-				href: 'https://uniapp.dcloud.io/component/README?id=uniui'
-			}
-		},
-		methods: {
-
-		}
-	}
+<script setup>
+	// import MyLayout from '@/components/Layout'
 </script>
 
-<style>
-	.container {
-		padding: 20px;
-		font-size: 14px;
-		line-height: 24px;
-	}
+<style lang="scss">
 </style>

+ 10 - 0
pages/index/my.vue

@@ -0,0 +1,10 @@
+<template>
+  <view>my</view>
+</template>
+
+<script setup>
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 10 - 0
pages/index/position.vue

@@ -0,0 +1,10 @@
+<template>
+  <view>position</view>
+</template>
+
+<script setup>
+</script>
+
+<style scoped lang="scss">
+
+</style>

BIN
static/c1.png


BIN
static/c2.png


BIN
static/c3.png


BIN
static/c4.png


BIN
static/c5.png


BIN
static/c6.png


BIN
static/c7.png


BIN
static/c8.png


BIN
static/c9.png


+ 0 - 20
static/customicons.css

@@ -1,20 +0,0 @@
-@font-face {
-  font-family: "customicons"; /* Project id 2878519 */
-  src:url('/static/customicons.ttf') format('truetype');
-}
-
-.customicons {
-  font-family: "customicons" !important;
-}
-
-.youxi:before {
-  content: "\e60e";
-}
-
-.wenjian:before {
-  content: "\e60f";
-}
-
-.zhuanfa:before {
-  content: "\e610";
-}

BIN
static/customicons.ttf


+ 69 - 0
static/iconfont.css

@@ -0,0 +1,69 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4679810 */
+  src: url('/static/iconfont.ttf') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-wode:before {
+  content: "\e60f";
+}
+
+.icon-xiaoxi:before {
+  content: "\e62c";
+}
+
+.icon-sousuo:before {
+  content: "\e653";
+}
+
+.icon-jianli:before {
+  content: "\e6b2";
+}
+
+.icon-jianli1:before {
+  content: "\e608";
+}
+
+.icon-xiaoxi1:before {
+  content: "\e614";
+}
+
+.icon-qiye:before {
+  content: "\e91d";
+}
+
+.icon-sousuo1:before {
+  content: "\f8b0";
+}
+
+.icon-qiye1:before {
+  content: "\e640";
+}
+
+.icon-zhiwei:before {
+  content: "\e60a";
+}
+
+.icon-zhiweisvg:before {
+  content: "\eac0";
+}
+
+.icon-icon-myself-1:before {
+  content: "\e601";
+}
+
+.icon-shouye-:before {
+  content: "\e629";
+}
+
+.icon-shouye:before {
+  content: "\e6fa";
+}
+

BIN
static/iconfont.ttf


BIN
static/img/home-fill.png


BIN
static/img/home.png


BIN
static/img/my-fill.png


BIN
static/img/my.png


BIN
static/img/position-fill.png


BIN
static/img/position.png


BIN
static/logo.png


BIN
static/uni.png


+ 20 - 0
store/index.js

@@ -0,0 +1,20 @@
+import { createPinia } from 'pinia';
+import piniaPersist from 'pinia-plugin-persist-uni';
+
+// 自动注入所有pinia模块
+const files = import.meta.glob('./*.js', { eager: true });
+const modules = {};
+Object.keys(files).forEach((key) => {
+  modules[key.replace(/(.*\/)*([^.]+).*/gi, '$2')] = files[key].default;
+});
+
+export const setupPinia = (app) => {
+  const pinia = createPinia();
+  pinia.use(piniaPersist);
+
+  app.use(pinia);
+};
+
+export default (name) => {
+  return modules[name]();
+};

+ 106 - 0
store/user.js

@@ -0,0 +1,106 @@
+import { defineStore } from 'pinia';
+import { clone, cloneDeep } from 'lodash-es';
+import { getUserInfo } from '@/api/user';
+
+// 默认用户信息
+const defaultUserInfo = {
+  avatar: '', // 头像
+  nickname: '', // 昵称
+  gender: 0, // 性别
+  mobile: '', // 手机号
+  point: 0, // 积分
+};
+
+// 默认账户信息
+const defaultAccountInfo = {
+  accessToken: '',
+  expiresTime: '',
+  openid: '',
+  refreshToken: '',
+  userId: ''
+}
+
+const user = defineStore({
+  id: 'user',
+  state: () => ({
+    userInfo: {}, // 用户信息
+    isLogin: !!uni.getStorageSync('token'), // 登录状态
+    lastUpdateTime: 0, // 上次更新时间
+    accountInfo: cloneDeep(defaultAccountInfo), // 账号信息
+  }),
+
+  actions: {
+    // 获取用户信息
+    async getInfo() {
+      const { code, data } = await getUserInfo({ userId: this.accountInfo.userId });
+      if (code !== 0) {
+        return;
+      }
+      this.userInfo = data;
+      return Promise.resolve(data);
+    },
+    // 设置 token
+    setToken(token = '', refreshToken = '') {
+      if (token === '') {
+        this.isLogin = false;
+        uni.removeStorageSync('token');
+        uni.removeStorageSync('refresh-token');
+      } else {
+        this.isLogin = true;
+        uni.setStorageSync('token', token);
+        uni.setStorageSync('refresh-token', refreshToken);
+        this.loginAfter();
+      }
+      return this.isLogin;
+    },
+
+    // 更新用户相关信息 (手动限流,5 秒之内不刷新)
+    async updateUserData() {
+      if (!this.isLogin) {
+        this.resetUserData();
+        return;
+      }
+      // 防抖,5 秒之内不刷新
+      const nowTime = new Date().getTime();
+      if (this.lastUpdateTime + 5000 > nowTime) {
+        return;
+      }
+      this.lastUpdateTime = nowTime;
+
+      // 获取最新信息
+      // await this.getInfo();
+      this.getWallet();
+      return this.userInfo;
+    },
+
+    // 重置用户默认数据
+    resetUserData() {
+      // 清空 token
+      this.setToken();
+      // 清空用户相关的缓存
+      this.userInfo = clone(defaultUserInfo);
+      this.accountInfo = cloneDeep(defaultAccountInfo);
+    },
+
+    // 登录后,加载各种信息
+    async loginAfter() {
+      await this.updateUserData();
+    },
+
+    // 登出系统
+    async logout() {
+      this.resetUserData();
+      return !this.isLogin;
+    },
+  },
+  persist: {
+    enabled: true,
+    strategies: [
+      {
+        key: 'user-store',
+      },
+    ],
+  },
+});
+
+export default user;

+ 142 - 156
uni_modules/uni-list/readme.md

@@ -1,9 +1,9 @@
 ## List 列表
+>
 > **组件名:uni-list**
 > 代码块: `uList`、`uListItem`
 > 关联组件:`uni-list-item`、`uni-badge`、`uni-icons`、`uni-list-chat`、`uni-list-ad`
 
-
 List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。
 
 在vue页面里,它默认使用页面级滚动。在app-nvue页面里,它默认使用原生list组件滚动。这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。
@@ -20,7 +20,6 @@ uni-list-item有很多风格,uni-list-item组件通过内置的属性,满足
 
 uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29)
 
-
 ### 安装方式
 
 本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
@@ -29,6 +28,7 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 
 > **注意事项**
 > 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
+>
 > - 组件需要依赖 `sass` 插件 ,请自行手动安装
 > - 组件内部依赖 `'uni-icons'` 、`uni-badge` 组件
 > - `uni-list` 和 `uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item`
@@ -39,19 +39,18 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 > - 如果需要修改 `switch`、`badge` 样式,请使用插槽自定义
 > - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli`
 > - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
- 
 
-### 基本用法 
+### 基本用法
 
 - 设置 `title` 属性,可以显示列表标题
 - 设置 `disabled` 属性,可以禁用当前项
 
 ```html
 <uni-list>
-	<uni-list-item  title="列表文字" ></uni-list-item>
-	<uni-list-item :disabled="true" title="列表禁用状态" ></uni-list-item>
+ <uni-list-item  title="列表文字" ></uni-list-item>
+ <uni-list-item :disabled="true" title="列表禁用状态" ></uni-list-item>
 </uni-list>
-			 
+    
 ```
 
 ### 多行内容显示
@@ -60,8 +59,8 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 
 ```html
 <uni-list>
-	<uni-list-item title="列表文字" note="列表描述信息"></uni-list-item>
-	<uni-list-item :disabled="true" title="列表文字" note="列表禁用状态"></uni-list-item>
+ <uni-list-item title="列表文字" note="列表描述信息"></uni-list-item>
+ <uni-list-item :disabled="true" title="列表文字" note="列表禁用状态"></uni-list-item>
 </uni-list>
 
 ```
@@ -73,8 +72,8 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 
 ```html
 <uni-list>
-	<uni-list-item  title="列表右侧显示角标" :show-badge="true" badge-text="12" ></uni-list-item>
-	<uni-list-item title="列表右侧显示 switch"  :show-switch="true"  @switchChange="switchChange" ></uni-list-item>
+ <uni-list-item  title="列表右侧显示角标" :show-badge="true" badge-text="12" ></uni-list-item>
+ <uni-list-item title="列表右侧显示 switch"  :show-switch="true"  @switchChange="switchChange" ></uni-list-item>
 </uni-list>
 
 ```
@@ -86,13 +85,14 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 
 ```html
  <uni-list>
- 	<uni-list-item title="列表左侧带略缩图" note="列表描述信息" thumb="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png"
- 	 thumb-size="lg" rightText="右侧文字"></uni-list-item>
- 	<uni-list-item :show-extra-icon="true" :extra-icon="extraIcon1" title="列表左侧带扩展图标" ></uni-list-item>
+  <uni-list-item title="列表左侧带略缩图" note="列表描述信息" thumb="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png"
+   thumb-size="lg" rightText="右侧文字"></uni-list-item>
+  <uni-list-item :show-extra-icon="true" :extra-icon="extraIcon1" title="列表左侧带扩展图标" ></uni-list-item>
 </uni-list>
 ```
 
 ### 开启点击反馈和右侧箭头
+
 - 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件
 - 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头
 - 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo`
@@ -100,15 +100,15 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 ```html
 
 <uni-list>
-	<uni-list-item title="开启点击反馈" clickable  @click="onClick" ></uni-list-item>
-	<uni-list-item title="默认 navigateTo 方式跳转页面" link to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
-	<uni-list-item title="reLaunch 方式跳转页面" link="reLaunch" to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
+ <uni-list-item title="开启点击反馈" clickable  @click="onClick" ></uni-list-item>
+ <uni-list-item title="默认 navigateTo 方式跳转页面" link to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
+ <uni-list-item title="reLaunch 方式跳转页面" link="reLaunch" to="/pages/vue/index/index" @click="onClick($event,1)" ></uni-list-item>
 </uni-list>
 
 ```
 
-
 ### 聊天列表示例
+
 - 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件
 - 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo`
 - 设置 `to` 属性,可以跳转页面
@@ -119,26 +119,26 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 ```html
 
 <uni-list>
-	<uni-list :border="true">
-		<!-- 显示圆形头像 -->
-		<uni-list-chat :avatar-circle="true" title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" ></uni-list-chat>
-		<!-- 右侧带角标 -->
-		<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-text="12" :badge-style="{backgroundColor:'#FF80AB'}"></uni-list-chat>
-		<!-- 头像显示圆点 -->
-		<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
-		<!-- 头像显示角标 -->
-		<uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="99"></uni-list-chat>
-		<!-- 显示多头像 -->
-		<uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
-		<!-- 自定义右侧内容 -->
-		<uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot">
-			<view class="chat-custom-right">
-				<text class="chat-custom-text">刚刚</text>
-				<!-- 需要使用 uni-icons 请自行引入 -->
-				<uni-icons type="star-filled" color="#999" size="18"></uni-icons>
-			</view>
-		</uni-list-chat>
-	</uni-list>
+ <uni-list :border="true">
+  <!-- 显示圆形头像 -->
+  <uni-list-chat :avatar-circle="true" title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" ></uni-list-chat>
+  <!-- 右侧带角标 -->
+  <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-text="12" :badge-style="{backgroundColor:'#FF80AB'}"></uni-list-chat>
+  <!-- 头像显示圆点 -->
+  <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
+  <!-- 头像显示角标 -->
+  <uni-list-chat title="uni-app" avatar="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="99"></uni-list-chat>
+  <!-- 显示多头像 -->
+  <uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot"></uni-list-chat>
+  <!-- 自定义右侧内容 -->
+  <uni-list-chat title="uni-app" :avatar-list="avatarList" note="您收到一条新的消息" time="2020-02-02 20:20" badge-positon="left" badge-text="dot">
+   <view class="chat-custom-right">
+    <text class="chat-custom-text">刚刚</text>
+    <!-- 需要使用 uni-icons 请自行引入 -->
+    <uni-icons type="star-filled" color="#999" size="18"></uni-icons>
+   </view>
+  </uni-list-chat>
+ </uni-list>
 </uni-list>
 
 ```
@@ -146,38 +146,37 @@ uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-l
 ```javascript
 
 export default {
-	components: {},
-	data() {
-		return {
-			avatarList: [{
-				url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
-			}, {
-				url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
-			}, {
-				url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
-			}]
-		}
-	}
+ components: {},
+ data() {
+  return {
+   avatarList: [{
+    url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
+   }, {
+    url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
+   }, {
+    url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png'
+   }]
+  }
+ }
 }
 
 ```
 
-
 ```css
 
 .chat-custom-right {
-	flex: 1;
-	/* #ifndef APP-NVUE */
-	display: flex;
-	/* #endif */
-	flex-direction: column;
-	justify-content: space-between;
-	align-items: flex-end;
+ flex: 1;
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: flex-end;
 }
 
 .chat-custom-text {
-	font-size: 12px;
-	color: #999;
+ font-size: 12px;
+ color: #999;
 }
 
 ```
@@ -186,140 +185,127 @@ export default {
 
 ### List Props
 
-属性名			|类型		|默认值		|	说明																									
-:-:				|:-:		|:-:		|	:-:	
-border			|Boolean	|true		|	是否显示边框
-
+属性名   |类型  |默认值  | 说明                         
+:-:    |:-:  |:-:  | :-: 
+border   |Boolean |true  | 是否显示边框
 
 ### ListItem Props
 
-属性名			|类型		|默认值		|	说明																					
-:-:				|:-:		|:-:		|	:-:	
-title			|String		|-			|	标题
-note			|String		|-			|	描述
-ellipsis		|Number		|0			|	title 是否溢出隐藏,可选值,0:默认;  1:显示一行;	2:显示两行;【nvue 暂不支持】
-thumb			|String		|-			|	左侧缩略图,若thumb有值,则不会显示扩展图标
-thumbSize		|String 	|medium 	|	略缩图尺寸,可选值,lg:大图;  medium:一般;	sm:小图;
-showBadge		|Boolean	|false		|	是否显示数字角标	
-badgeText		|String		|-			|	数字角标内容
-badgeType		|String		|-			|	数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21)	
+属性名   |类型  |默认值  | 说明                     
+:-:    |:-:  |:-:  | :-: 
+title   |String  |-   | 标题
+note   |String  |-   | 描述
+ellipsis  |Number  |0   | title 是否溢出隐藏,可选值,0:默认;  1:显示一行; 2:显示两行;【nvue 暂不支持】
+thumb   |String  |-   | 左侧缩略图,若thumb有值,则不会显示扩展图标
+thumbSize  |String  |medium  | 略缩图尺寸,可选值,lg:大图;  medium:一般; sm:小图;
+showBadge  |Boolean |false  | 是否显示数字角标 
+badgeText  |String  |-   | 数字角标内容
+badgeType  |String  |-   | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) 
 badgeStyle  |Object   |-      | 数字角标样式,使用uni-badge的custom-style参数
-rightText		|String		|-			|	右侧文字内容
-disabled		|Boolean	|false		|	是否禁用	
-showArrow 		|Boolean	|true		|	是否显示箭头图标			
-link			|String 	|navigateTo	|	新页面跳转方式,可选值见下表
-to				|String		|-			|	新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功			
-clickable		|Boolean	|false		|	是否开启点击反馈
-showSwitch	    |Boolean	|false		|	是否显示Switch																			
-switchChecked	|Boolean	|false		|	Switch是否被选中																			
-showExtraIcon   |Boolean	|false		|	左侧是否显示扩展图标																		
-extraIcon		|Object		|-			|	扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28)	
-direction		| String	|row		|	排版方向,可选值,row:水平排列;  column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制
-
+rightText  |String  |-   | 右侧文字内容
+disabled  |Boolean |false  | 是否禁用 
+showArrow   |Boolean |true  | 是否显示箭头图标   
+link   |String  |navigateTo | 新页面跳转方式,可选值见下表
+to    |String  |-   | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功   
+clickable  |Boolean |false  | 是否开启点击反馈
+showSwitch     |Boolean |false  | 是否显示Switch                   
+switchChecked |Boolean |false  | Switch是否被选中                   
+showExtraIcon   |Boolean |false  | 左侧是否显示扩展图标                  
+extraIcon  |Object  |-   | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28) 
+direction  | String |row  | 排版方向,可选值,row:水平排列;  column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制
 
 #### Link Options
 
-属性名				|	说明
-:-:					|	:-:
-navigateTo 	| 	同 uni.navigateTo()
-redirectTo 	|	同 uni.reLaunch()
-reLaunch		|	同 uni.reLaunch()
-switchTab  	|	同 uni.switchTab()
+属性名    | 说明
+:-:     | :-:
+navigateTo  |  同 uni.navigateTo()
+redirectTo  | 同 uni.reLaunch()
+reLaunch  | 同 uni.reLaunch()
+switchTab   | 同 uni.switchTab()
 
 ### ListItem Events
 
-事件称名			|说明									|返回参数			
-:-:				|:-:									|:-:				
-click			|点击 uniListItem 触发事件,需开启点击反馈	|-					
-switchChange	|点击切换 Switch 时触发,需显示 switch		|e={value:checked}	
-
-
+事件称名   |说明         |返回参数   
+:-:    |:-:         |:-:    
+click   |点击 uniListItem 触发事件,需开启点击反馈 |-     
+switchChange |点击切换 Switch 时触发,需显示 switch  |e={value:checked} 
 
 ### ListItem Slots
 
-名称	 	|	说明					
-:-:		|	:-:						
-header	|	左/上内容插槽,可完全自定义默认显示
-body	|	中间内容插槽,可完全自定义中间内容				
-footer	|	右/下内容插槽,可完全自定义右侧内容		
-
+名称   | 说明     
+:-:  | :-:      
+header | 左/上内容插槽,可完全自定义默认显示
+body | 中间内容插槽,可完全自定义中间内容    
+footer | 右/下内容插槽,可完全自定义右侧内容  
 
 > **通过插槽扩展**
 > 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现
-> 如果	`uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。
+> 如果 `uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。
 > uni-list-item提供了3个可扩展的插槽:`header`、`body`、`footer`
+>
 > - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分
 > - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分
 > 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。
 
-	
 **示例**
 
 ```html
 <uni-list>
-	<uni-list-item title="自定义右侧插槽" note="列表描述信息" link>
-		<template slot="header">
-			<image class="slot-image" src="/static/logo.png" mode="widthFix"></image>
-		</template>
-	</uni-list-item>
-	<uni-list-item>
-		<!-- 自定义 header -->
-		<view slot="header" class="slot-box"><image class="slot-image" src="/static/logo.png" mode="widthFix"></image></view>
-		<!-- 自定义 body -->
-		<text slot="body" class="slot-box slot-text">自定义插槽</text>
-		<!-- 自定义 footer-->
-		<template slot="footer">
-			<image class="slot-image" src="/static/logo.png" mode="widthFix"></image>
-		</template>
-	</uni-list-item>
+ <uni-list-item title="自定义右侧插槽" note="列表描述信息" link>
+  <template slot="header">
+   <!-- <image class="slot-image" src="/static/logo.png" mode="widthFix"></image> -->
+  </template>
+ </uni-list-item>
+ <uni-list-item>
+  <!-- 自定义 header -->
+  <!-- <view slot="header" class="slot-box"><image class="slot-image" src="/static/logo.png" mode="widthFix"></image></view> -->
+  <!-- 自定义 body -->
+  <text slot="body" class="slot-box slot-text">自定义插槽</text>
+  <!-- 自定义 footer-->
+  <template slot="footer">
+   <!-- <image class="slot-image" src="/static/logo.png" mode="widthFix"></image> -->
+  </template>
+ </uni-list-item>
 </uni-list>
 ```
 
-
-
-
-
 ### ListItemChat Props
 
-属性名			|类型		|默认值		|	说明																		
-:-:				|:-:		|:-:		|	:-:	
-title 			|String		|-			|	标题
-note 			|String		|-			|	描述
-clickable		|Boolean	|false		|	是否开启点击反馈
-badgeText		|String		|-			|	数字角标内容,设置为 `dot` 将显示圆点
-badgePositon 	|String		|right		|	角标位置
-link			|String 	|navigateTo	|	是否展示右侧箭头并开启点击反馈,可选值见下表
-clickable		|Boolean	|false		|	是否开启点击反馈
-to				|String		|-			|	跳转页面地址,如填写此属性,click 会返回页面是否跳转成功	
-time			|String 	|-			|	右侧时间显示
-avatarCircle 	|Boolean 	|false		|	是否显示圆形头像
-avatar			|String 	|-			|	头像地址,avatarCircle 不填时生效
-avatarList 		|Array	 	|-			|	头像组,格式为 [{url:''}]
+属性名   |类型  |默认值  | 说明                  
+:-:    |:-:  |:-:  | :-: 
+title    |String  |-   | 标题
+note    |String  |-   | 描述
+clickable  |Boolean |false  | 是否开启点击反馈
+badgeText  |String  |-   | 数字角标内容,设置为 `dot` 将显示圆点
+badgePositon  |String  |right  | 角标位置
+link   |String  |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表
+clickable  |Boolean |false  | 是否开启点击反馈
+to    |String  |-   | 跳转页面地址,如填写此属性,click 会返回页面是否跳转成功 
+time   |String  |-   | 右侧时间显示
+avatarCircle  |Boolean  |false  | 是否显示圆形头像
+avatar   |String  |-   | 头像地址,avatarCircle 不填时生效
+avatarList   |Array   |-   | 头像组,格式为 [{url:''}]
 
 #### Link Options
 
-属性名		|	说明
-:-:			|	:-:
-navigateTo 	| 	同 uni.navigateTo()
-redirectTo 	|	同 uni.reLaunch()
-reLaunch	|	同 uni.reLaunch()
-switchTab  	|	同 uni.switchTab()
+属性名  | 说明
+:-:   | :-:
+navigateTo  |  同 uni.navigateTo()
+redirectTo  | 同 uni.reLaunch()
+reLaunch | 同 uni.reLaunch()
+switchTab   | 同 uni.switchTab()
 
 ### ListItemChat Slots
 
-名称	 	|	说明					
-:-		|	:-						
-default	|	自定义列表右侧内容(包括时间和角标显示)
+名称   | 说明     
+:-  | :-      
+default | 自定义列表右侧内容(包括时间和角标显示)
 
 ### ListItemChat Events
-事件称名			|	说明						|	返回参数			
-:-:				|	:-:						|	:-:	
-@click			|	点击 uniListChat 触发事件	|	{data:{}}	,如有 to 属性,会返回页面跳转信息	
-
-
-
-
 
+事件称名   | 说明      | 返回参数   
+:-:    | :-:      | :-: 
+@click   | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息 
 
 ## 基于uni-list扩展的页面模板
 
@@ -343,4 +329,4 @@ default	|	自定义列表右侧内容(包括时间和角标显示)
 
 ## 组件示例
 
-点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list)
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list)

+ 4 - 4
uni_modules/uni-scss/theme.scss

@@ -3,13 +3,13 @@ $uni-space-root: 2;
 // 边框半径默认值
 $uni-radius-root:5px;
 // 主色
-$uni-primary: #2979ff;
+$uni-primary: #00897B;
 // 辅助色
-$uni-success: #4cd964;
+$uni-success: #4CAF50;
 // 警告色
-$uni-warning: #f0ad4e;
+$uni-warning: #FB8C00;
 // 错误色
-$uni-error: #dd524d;
+$uni-error: #ff5252;
 // 描述色
 $uni-info: #909399;
 // 中性色

+ 106 - 0
utils/index.js

@@ -0,0 +1,106 @@
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export function deepMerge(target = {}, source = {}) {
+  target = deepClone(target);
+  if (typeof target !== 'object' || typeof source !== 'object') return false;
+  for (const prop in source) {
+    if (!source.hasOwnProperty(prop)) continue;
+    if (prop in target) {
+      if (typeof target[prop] !== 'object') {
+        target[prop] = source[prop];
+      } else if (typeof source[prop] !== 'object') {
+        target[prop] = source[prop];
+      } else if (target[prop].concat && source[prop].concat) {
+        target[prop] = target[prop].concat(source[prop]);
+      } else {
+        target[prop] = deepMerge(target[prop], source[prop]);
+      }
+    } else {
+      target[prop] = source[prop];
+    }
+  }
+  return target;
+}
+
+/**
+ * @description 去除空格
+ * @param String str 需要去除空格的字符串
+ * @param String pos both(左右)|left|right|all 默认both
+ */
+function trim(str, pos = 'both') {
+  str = String(str);
+  if (pos == 'both') {
+    return str.replace(/^\s+|\s+$/g, '');
+  }
+  if (pos == 'left') {
+    return str.replace(/^\s*/, '');
+  }
+  if (pos == 'right') {
+    return str.replace(/(\s*$)/g, '');
+  }
+  if (pos == 'all') {
+    return str.replace(/\s+/g, '');
+  }
+  return str;
+}
+
+/**
+ * @description 样式转换
+ * 对象转字符串,或者字符串转对象
+ * @param {object | string} customStyle 需要转换的目标
+ * @param {String} target 转换的目的,object-转为对象,string-转为字符串
+ * @returns {object|string}
+ */
+export function addStyle(customStyle, target = 'object') {
+  // 字符串转字符串,对象转对象情形,直接返回
+  if (
+    test.empty(customStyle) ||
+    (typeof customStyle === 'object' && target === 'object') ||
+    (target === 'string' && typeof customStyle === 'string')
+  ) {
+    return customStyle;
+  }
+  // 字符串转对象
+  if (target === 'object') {
+    // 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
+    customStyle = trim(customStyle);
+    // 根据";"将字符串转为数组形式
+    const styleArray = customStyle.split(';');
+    const style = {};
+    // 历遍数组,拼接成对象
+    for (let i = 0; i < styleArray.length; i++) {
+      // 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
+      if (styleArray[i]) {
+        const item = styleArray[i].split(':');
+        style[trim(item[0])] = trim(item[1]);
+      }
+    }
+    return style;
+  }
+  // 这里为对象转字符串形式
+  let string = '';
+  for (const i in customStyle) {
+    // 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
+    const key = i.replace(/([A-Z])/g, '-$1').toLowerCase();
+    string += `${key}:${customStyle[i]};`;
+  }
+  // 去除两端空格
+  return trim(string);
+}
+
+/**
+ * @description 进行延时,以达到可以简写代码的目的
+ * @param {number} value 堵塞时间 单位ms 毫秒
+ * @returns {Promise} 返回promise
+ */
+export function sleep(value = 30) {
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve();
+    }, value);
+  });
+}

+ 302 - 0
utils/request.js

@@ -0,0 +1,302 @@
+/**
+ * Shopro-request
+ * @description api模块管理,loading配置,请求拦截,错误处理
+ */
+
+import Request from 'luch-request';
+// import { showAuthModal } from '@/sheep/hooks/useModal';
+import { refreshToken } from '@/api/auth';
+import { user } from '@/store/user'
+
+const baseUrl = 'https://menduner.citupro.com:2443'
+const tenantId = '155'
+const options = {
+	// 显示操作成功消息 默认不显示
+	showSuccess: false,
+	// 成功提醒 默认使用后端返回值
+	successMsg: '',
+	// 显示失败消息 默认显示
+	showError: true,
+	// 失败提醒 默认使用后端返回信息
+	errorMsg: '',
+	// 显示请求时loading模态框 默认显示
+	showLoading: true,
+	// loading提醒文字
+	loadingMsg: '加载中',
+	// 需要授权才能请求 默认放开
+	auth: false,
+	// ...
+};
+
+// Loading全局实例
+let LoadingInstance = {
+	target: null,
+	count: 0,
+};
+
+/**
+ * 关闭loading
+ */
+function closeLoading() {
+	if (LoadingInstance.count > 0) LoadingInstance.count--;
+	if (LoadingInstance.count === 0) uni.hideLoading();
+}
+
+/**
+ * @description 请求基础配置 可直接使用访问自定义请求
+ */
+const http = new Request({
+	baseURL: baseUrl + '/app-api',
+	timeout: 8000,
+	method: 'GET',
+	header: {
+		Accept: 'text/json',
+		'Content-Type': 'application/json;charset=UTF-8',
+		platform: 'WechatMiniProgram',
+	},
+	// #ifdef APP-PLUS
+	sslVerify: false,
+	// #endif
+	// #ifdef H5
+	// 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)
+	withCredentials: false,
+	// #endif
+	custom: options,
+});
+
+/**
+ * @description 请求拦截器
+ */
+http.interceptors.request.use(
+	(config) => {
+    const useUserStore = user()
+    // 自定义处理【auth 授权】:必须登录的接口,则跳出 AuthModal 登录弹窗
+		if (config.custom.auth && !useUserStore.isLogin) {
+		// 	showAuthModal();
+    console.log('必须登录========')
+			return Promise.reject();
+		}
+
+    // 自定义处理【loading 加载中】:如果需要显示 loading,则显示 loading
+		if (config.custom.showLoading) {
+			LoadingInstance.count++;
+			LoadingInstance.count === 1 &&
+				uni.showLoading({
+					title: config.custom.loadingMsg,
+					mask: true,
+					fail: () => {
+						uni.hideLoading();
+					},
+				});
+		}
+
+    // 增加 token 令牌、terminal 终端、tenant 租户的请求头
+		const token = getAccessToken();
+		if (token) {
+			config.header['Authorization'] = token;
+		}
+		config.header['terminal'] = 'mp-weixin'
+
+    config.header['Accept'] = '*/*';
+    config.header['tenant-id'] = tenantId;
+		return config;
+	},
+	(error) => {
+		return Promise.reject(error);
+	},
+);
+
+/**
+ * @description 响应拦截器
+ */
+http.interceptors.response.use(
+	(response) => {
+		// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
+		if (response.config.url.indexOf('/system/auth/') >= 0 && response.data?.data?.accessToken) {
+			const userStore = user()
+			userStore.setToken(response.data.data.accessToken, response.data.data.refreshToken);
+		}
+
+    // 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loading
+		response.config.custom.showLoading && closeLoading();
+
+    // 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示
+		if (response.data.code !== 0) {
+      // 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌
+      if (response.data.code === 401) {
+        return refreshToken(response.config);
+      }
+
+      // 错误提示
+			if (response.config.custom.showError) {
+				uni.showToast({
+					title: response.data.msg || '服务器开小差啦,请稍后再试~',
+					icon: 'none',
+					mask: true,
+				});
+      }
+		}
+
+		// 自定义处理【showSuccess 成功提示】:如果需要显示成功提示,则显示成功提示
+		if (response.config.custom.showSuccess
+      && response.config.custom.successMsg !== ''
+      &&  response.data.code === 0) {
+      uni.showToast({
+				title: response.config.custom.successMsg,
+				icon: 'none',
+			});
+		}
+
+    // 返回结果:包括 code + data + msg
+		return Promise.resolve(response.data);
+	},
+	(error) => {
+		const userStore = user()
+		const isLogin = userStore.isLogin;
+		let errorMessage = '网络请求出错';
+		if (error !== undefined) {
+			switch (error.statusCode) {
+				case 400:
+					errorMessage = '请求错误';
+					break;
+				case 401:
+          errorMessage = isLogin ? '您的登陆已过期' : '请先登录';
+          // 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorized
+          break;
+				case 403:
+					errorMessage = '拒绝访问';
+					break;
+				case 404:
+					errorMessage = '请求出错';
+					break;
+				case 408:
+					errorMessage = '请求超时';
+					break;
+				case 429:
+					errorMessage = '请求频繁, 请稍后再访问';
+					break;
+				case 500:
+					errorMessage = '服务器开小差啦,请稍后再试~';
+					break;
+				case 501:
+					errorMessage = '服务未实现';
+					break;
+				case 502:
+					errorMessage = '网络错误';
+					break;
+				case 503:
+					errorMessage = '服务不可用';
+					break;
+				case 504:
+					errorMessage = '网络超时';
+					break;
+				case 505:
+					errorMessage = 'HTTP 版本不受支持';
+					break;
+			}
+			if (error.errMsg.includes('timeout')) errorMessage = '请求超时';
+			// #ifdef H5
+			if (error.errMsg.includes('Network'))
+				errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接';
+			// #endif
+		}
+
+		if (error && error.config) {
+			if (error.config.custom.showError === false) {
+				uni.showToast({
+					title: error.data?.msg || errorMessage,
+					icon: 'none',
+					mask: true,
+				});
+			}
+			error.config.custom.showLoading && closeLoading();
+		}
+
+		return false;
+	},
+);
+
+// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
+let requestList = [] // 请求队列
+let isRefreshToken = false // 是否正在刷新中
+const refreshToken = async (config) => {
+  // 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)
+  if (config.url.indexOf('/member/auth/refresh-token') >= 0) {
+    return Promise.reject('error')
+  }
+
+  // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+  if (!isRefreshToken) {
+    isRefreshToken = true
+    // 1. 如果获取不到刷新令牌,则只能执行登出操作
+    const refreshToken = getRefreshToken()
+    if (!refreshToken) {
+      return handleAuthorized()
+    }
+    // 2. 进行刷新访问令牌
+    try {
+      const refreshTokenResult = await refreshToken(refreshToken);
+      if (refreshTokenResult.code !== 0) {
+        // 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑
+        // noinspection ExceptionCaughtLocallyJS
+        throw new Error('刷新令牌失败');
+      }
+      // 2.1 刷新成功,则回放队列的请求 + 当前请求
+      config.header.Authorization = 'Bearer ' + getAccessToken()
+      requestList.forEach((cb) => {
+        cb()
+      })
+      requestList = []
+      return request(config)
+    } catch (e) {
+      // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
+      // 2.2 刷新失败,只回放队列的请求
+      requestList.forEach((cb) => {
+        cb()
+      })
+      // 提示是否要登出。即不回放当前请求!不然会形成递归
+      return handleAuthorized()
+    } finally {
+      requestList = []
+      isRefreshToken = false
+    }
+  } else {
+    // 添加到队列,等待刷新获取到新的令牌
+    return new Promise((resolve) => {
+      requestList.push(() => {
+        config.header.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+        resolve(request(config))
+      })
+    })
+  }
+}
+
+/**
+ * 处理 401 未登录的错误
+ */
+const handleAuthorized = () => {
+  const userStore = user()
+  userStore.logout(true);
+  // showAuthModal();
+  // 登录超时
+  return Promise.reject({
+    code: 401,
+    msg: userStore.isLogin ? '您的登陆已过期' : '请先登录'
+  })
+}
+
+/** 获得访问令牌 */
+const getAccessToken = () => {
+  return uni.getStorageSync('token');
+}
+
+/** 获得刷新令牌 */
+const getRefreshToken = () => {
+  return uni.getStorageSync('refresh-token');
+}
+
+const request = (config) => {
+	return http.middleware(config);
+};
+
+export default request;