Commit 53bcba4f8383ae82d5b60aa7bea16474607b24bd

Authored by 吉鹏
1 parent 01b15d67e9
Exists in master

文件分包,首页添加骨架屏幕

package-lock.json
... ... @@ -9254,6 +9254,11 @@
9254 9254 "minipass": "^3.0.0"
9255 9255 }
9256 9256 },
  9257 + "miniprogram-skeleton": {
  9258 + "version": "1.0.3",
  9259 + "resolved": "https://registry.npmjs.org/miniprogram-skeleton/-/miniprogram-skeleton-1.0.3.tgz",
  9260 + "integrity": "sha512-oDt4rPP1gRYqlNuMcN+LQ0HDcEbBssOnp/weyZQsGV6+bDM++ClIX3AVU4LQBu3CF3XZ6n5qQIJJXt70e0Igsg=="
  9261 + },
9257 9262 "mississippi": {
9258 9263 "version": "3.0.0",
9259 9264 "resolved": "http://registry.npm.taobao.org/mississippi/download/mississippi-3.0.0.tgz",
... ...
... ... @@ -42,6 +42,7 @@
42 42 "@dcloudio/uni-stat": "^2.0.0-26920200424005",
43 43 "core-js": "^3.6.4",
44 44 "flyio": "^0.6.2",
  45 + "miniprogram-skeleton": "^1.0.3",
45 46 "regenerator-runtime": "^0.12.1",
46 47 "sass-resources-loader": "^2.0.3",
47 48 "vue": "^2.6.11",
... ...
src/components/EasyLoadimage/EasyLoadimage.vue
... ... @@ -57,7 +57,8 @@ export default{
57 57 loadImg:false,
58 58 showImg:false,
59 59 isLoadError:false,
60   - showTransition:false
  60 + showTransition:false,
  61 + defaultImg: '/static/easy-loadimage/loading.gif'
61 62 }
62 63 },
63 64 methods:{
... ... @@ -99,9 +100,13 @@ export default{
99 100 </script>
100 101  
101 102 <style scoped>
  103 + .easy-loadimage{
  104 + min-height: 160rpx;
  105 + }
102 106 /* 官方优化图片tips */
103 107 image{
104 108 will-change: transform
  109 +
105 110 }
106 111 /* 渐变过渡效果处理 */
107 112 image.origin-img{
... ...
src/components/quick-skeleton/quick-skeleton.vue
... ... @@ -0,0 +1,171 @@
  1 +<template>
  2 +<view v-show="show" :style="{width: systemInfo.width + 'px', height: systemInfo.height + 'px', backgroundColor: bgcolor, position: 'absolute', left: 0, top: 0, zIndex: 9998, overflow: 'hidden'}">
  3 + <view v-for="(item,rect_idx) in skeletonRectLists" :key="rect_idx + 'rect'" :class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']" :style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view>
  4 + <view v-for="(item,circle_idx) in skeletonCircleLists" :key="circle_idx + 'circle'" :class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''" :style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', borderRadius: item.width + 'px', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view>
  5 +
  6 + <view class="spinbox" v-if="loading == 'spin'">
  7 + <view class="spin"></view>
  8 + </view>
  9 +</view>
  10 +</template>
  11 +
  12 +<script>
  13 + export default {
  14 + name: "skeleton",
  15 + props: {
  16 + bgcolor: {
  17 + type: String,
  18 + value: '#FFF'
  19 + },
  20 + selector: {
  21 + type: String,
  22 + value: 'skeleton'
  23 + },
  24 + loading: {
  25 + type: String,
  26 + value: 'spin'
  27 + },
  28 + show: {
  29 + type: Boolean,
  30 + value: false
  31 + }
  32 + },
  33 + data() {
  34 + return {
  35 + loadingAni: ['spin', 'chiaroscuro'],
  36 + systemInfo: {},
  37 + skeletonRectLists: [],
  38 + skeletonCircleLists: []
  39 + }
  40 + },
  41 + watch: {
  42 + show() {
  43 + this.attachedAction();
  44 + this.readyAction();
  45 + }
  46 + },
  47 + methods: {
  48 + attachedAction: function(){
  49 + //默认的首屏宽高,防止内容闪现
  50 + const systemInfo = uni.getSystemInfoSync();
  51 + this.systemInfo = {
  52 + width: systemInfo.windowWidth,
  53 + height: systemInfo.windowHeight
  54 + };
  55 + console.log(this.systemInfo)
  56 + this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin';
  57 + },
  58 + readyAction: function(){
  59 + const that = this;
  60 + //绘制背景
  61 + uni.createSelectorQuery().selectAll(`.${this.selector}`).boundingClientRect().exec(function(res){
  62 +
  63 + that.systemInfo.height = res[0][0].height + res[0][0].top;
  64 + });
  65 +
  66 + //绘制矩形
  67 + this.rectHandle();
  68 +
  69 + //绘制圆形
  70 + this.radiusHandle();
  71 + },
  72 + rectHandle: function(){
  73 + const that = this;
  74 +
  75 + //绘制不带样式的节点
  76 + uni.createSelectorQuery().selectAll(`.${this.selector}-rect`).boundingClientRect().exec(function(res){
  77 + that.skeletonRectLists = res[0];
  78 + });
  79 +
  80 + },
  81 + radiusHandle(){
  82 + const that = this;
  83 +
  84 + uni.createSelectorQuery().selectAll(`.${this.selector}-radius`).boundingClientRect().exec(function(res){
  85 + that.skeletonCircleLists = res[0];
  86 + });
  87 + }
  88 + }
  89 + }
  90 +</script>
  91 +
  92 +<style>
  93 +.spinbox{
  94 + position: fixed;
  95 + display: flex;
  96 + justify-content: center;
  97 + align-items: center;
  98 + height: 100%;
  99 + width: 100%;
  100 + z-index: 9999
  101 +}
  102 +.spin {
  103 + display: inline-block;
  104 + width: 64rpx;
  105 + height: 64rpx;
  106 +}
  107 +.spin:after {
  108 + content: " ";
  109 + display: block;
  110 + width: 46rpx;
  111 + height: 46rpx;
  112 + margin: 1rpx;
  113 + border-radius: 50%;
  114 + border: 5rpx solid #409eff;
  115 + border-color: #409eff transparent #409eff transparent;
  116 + animation: spin 1.2s linear infinite;
  117 +}
  118 +@keyframes spin {
  119 + 0% {
  120 + transform: rotate(0deg);
  121 + }
  122 + 100% {
  123 + transform: rotate(360deg);
  124 + }
  125 +}
  126 +
  127 +.chiaroscuro{
  128 + width: 100%;
  129 + height: 100%;
  130 + background: rgb(194, 207, 214);
  131 + animation-duration: 2s;
  132 + animation-name: blink;
  133 + animation-iteration-count: infinite;
  134 +}
  135 +
  136 +@keyframes blink {
  137 + 0% {
  138 + opacity: .4;
  139 + }
  140 + 50% {
  141 + opacity: 1;
  142 + }
  143 + 100% {
  144 + opacity: .4;
  145 + }
  146 +}
  147 +
  148 +@keyframes flush {
  149 + 0% {
  150 + left: -100%;
  151 + }
  152 + 50% {
  153 + left: 0;
  154 + }
  155 + 100% {
  156 + left: 100%;
  157 + }
  158 +}
  159 +.shine {
  160 + animation: flush 2s linear infinite;
  161 + position: absolute;
  162 + top: 0;
  163 + bottom: 0;
  164 + width: 100%;
  165 + background: linear-gradient(to left,
  166 + rgba(255, 255, 255, 0) 0%,
  167 + rgba(255, 255, 255, .85) 50%,
  168 + rgba(255, 255, 255, 0) 100%
  169 + )
  170 +}
  171 +</style>
... ...
... ... @@ -60,42 +60,42 @@
60 60 "navigationBarTitleText": "产品详情"
61 61 }
62 62 },
63   - {
64   - "path": "pages/refundProgress/refundProgress",
65   - "style": {
66   - "navigationBarTitleText": "申请退款"
67   - }
68   - },
69   - {
70   - "path": "pages/address/addAddress",
71   - "style": {
72   - "navigationBarTitleText": "新增地址"
73   - }
74   - },
75   - {
76   - "path": "pages/address/addressList",
77   - "style": {
78   - "navigationBarTitleText": "地址管理"
79   - }
80   - },
  63 + // {
  64 + // "path": "pages/refundProgress/refundProgress",
  65 + // "style": {
  66 + // "navigationBarTitleText": "申请退款"
  67 + // }
  68 + // },
  69 + // {
  70 + // "path": "pages/address/addAddress",
  71 + // "style": {
  72 + // "navigationBarTitleText": "新增地址"
  73 + // }
  74 + // },
  75 + // {
  76 + // "path": "pages/address/addressList",
  77 + // "style": {
  78 + // "navigationBarTitleText": "地址管理"
  79 + // }
  80 + // },
81 81 {
82 82 "path": "pages/confirmOrder/confirmOrder",
83 83 "style": {
84 84 "navigationBarTitleText": "确认订单"
85 85 }
86 86 },
87   - {
88   - "path": "pages/refundment/refundWays",
89   - "style": {
90   - "navigationBarTitleText": "退款方式"
91   - }
92   - },
93   - {
94   - "path": "pages/refundment/refundment",
95   - "style": {
96   - "navigationBarTitleText": "申请退款"
97   - }
98   - },
  87 + // {
  88 + // "path": "pages/refundment/refundWays",
  89 + // "style": {
  90 + // "navigationBarTitleText": "退款方式"
  91 + // }
  92 + // },
  93 + // {
  94 + // "path": "pages/refundment/refundment",
  95 + // "style": {
  96 + // "navigationBarTitleText": "申请退款"
  97 + // }
  98 + // },
99 99 {
100 100 "path": "pages/predelivery/predelivery",
101 101 "style": {
... ... @@ -126,7 +126,25 @@
126 126 "navigationBarTitleText": "验光数据"
127 127 }
128 128 }
129   - ],
  129 + ],
  130 + "subpackages": [
  131 + {
  132 + "root": "pages/refundment",
  133 + "pages": [
  134 + "pages/refundment/refundWays",
  135 + "pages/refundment/refundment",
  136 + "pages/refundProgress/refundProgress"
  137 + ]
  138 + },
  139 + {
  140 + "root": "pages/address",
  141 + "name": "pack2",
  142 + "pages": [
  143 + "pages/address/addAddress",
  144 + "pages/address/addressList"
  145 + ]
  146 + }
  147 + ],
130 148 "globalStyle": {
131 149 "navigationBarTextStyle": "black",
132 150 "navigationBarTitleText": "uni-app",
... ... @@ -162,6 +180,11 @@
162 180 "condition": {
163 181 "current": 0,
164 182 "list": [
  183 + // {
  184 + // "name": "首页",
  185 + // "path": "pages/test/index",
  186 + // "query": ""
  187 + // },
165 188 {
166 189 "name": "首页",
167 190 "path": "pages/index/index",
... ...
src/pages/index/index.vue
1   -<template>
2   - <view class="content">
  1 +<template>
  2 + <view class="container">
  3 + <view class="content skeleton" v-show="showContent">
3 4 <view class="header">
4 5 <!-- 搜索-->
5   - <view class="searchBar">
  6 + <view class="searchBar skeleton-rect">
6 7 <icon
7 8 class="searchIcon"
8 9 type="search"
... ... @@ -73,7 +74,7 @@
73 74 </Uni-drawer>
74 75  
75 76 <!-- 筛选菜单-->
76   - <view class="content-wrap">
  77 + <view class="content-wrap skeleton-rect">
77 78 <view>
78 79 <HMfilterDropdown
79 80 :filterData="categoryList"
... ... @@ -93,7 +94,7 @@
93 94 <view class="goods-list">
94 95 <view class="product-list">
95 96 <view
96   - class="product"
  97 + class="product skeleton-rect"
97 98 v-for="(goods) in goodsList"
98 99 :key="goods.id"
99 100 >
... ... @@ -105,27 +106,33 @@
105 106 </view>
106 107 </view>
107 108 <view class="loading-text">
108   - <text v-if="isLoading==true">{{loadingText}}</text>
109   - <text v-else>{{loadedText}}</text>
  109 + <text>{{loadedText}}</text>
  110 + <!-- <text v-else>{{loadedText}}</text> -->
110 111 </view>
111 112 </view>
112 113 <!-- </scroll-view> -->
113 114 </view>
114   - </view>
115   - </view>
  115 + </view>
  116 + </view>
  117 + <!--引用组件-->
  118 + <skeleton :show="showSkeleton" ref="skeleton" loading="chiaroscuro" selector="skeleton" bgcolor="#FFF"></skeleton>
  119 + </view>
116 120 </template>
117 121  
118 122 <script>
119 123 import UniDrawer from "@/components/UniDrawer/UniDrawer.vue";
120 124 import Card from "@/components/CommodityCard/CommodityCard.vue";
121   -import HMfilterDropdown from "@/components/HMFilterDropdown/HMFilterDropdown.vue";
  125 +import HMfilterDropdown from "@/components/HMFilterDropdown/HMFilterDropdown.vue";
  126 +import skeleton from "@/components/quick-skeleton/quick-skeleton.vue";
122 127 import store from "@/store";
  128 +
123 129  
124 130 export default {
125 131 components: {
126 132 UniDrawer: UniDrawer,
127 133 HMfilterDropdown: HMfilterDropdown,
128   - Card: Card
  134 + Card: Card,
  135 + skeleton: skeleton
129 136 },
130 137 data() {
131 138 return {
... ... @@ -137,14 +144,15 @@ export default {
137 144 filterDropdownValue: [],
138 145 filterData: [],
139 146 searchText: "",
140   - scrollTop: 0,
141   - viewHeight: uni.getSystemInfoSync().windowHeight
  147 + scrollTop: 0,
  148 + showContent: true,
  149 + viewHeight: uni.getSystemInfoSync().windowHeight,
  150 + showSkeleton: false //骨架屏显示隐藏
142 151 };
143 152 },
144 153 onPageScroll({ scrollTop }) {
145 154 // 传入scrollTop值并触发所有easy-loadimage组件下的滚动监听事件
146 155 this.scrollTop = scrollTop;
147   - console.log("pagescroll====>", this.viewHeight);
148 156 },
149 157 computed: {
150 158 goodsList() {
... ... @@ -160,13 +168,20 @@ export default {
160 168 outData(value) {
161 169 return JSON.stringify(value);
162 170 }
  171 + },
  172 + /**
  173 + * 页面载入完成后调用子组件的方法生成预加载效果
  174 + */
  175 + onReady:function(){
  176 + this.getInitData()
163 177 },
164   - onLoad() {
165   - store.dispatch("index/category");
166   - // this.getList();
167   - store.dispatch("index/list");
168   - },
169   - methods: {
  178 + methods: {
  179 + async getInitData (){
  180 + this.showSkeleton = true;
  181 + await Promise.all([store.dispatch("index/category"), store.dispatch("index/list")])
  182 + this.showSkeleton = false;
  183 + this.showContent = true
  184 + },
170 185 showDrawer(e) {
171 186 this.$refs[e].open();
172 187 },
... ... @@ -240,7 +255,7 @@ export default {
240 255 </script>
241 256  
242 257 <style lang="scss">
243   -.content {
  258 +.content,.container {
244 259 display: flex;
245 260 flex-direction: column;
246 261 align-items: center;
... ... @@ -324,7 +339,8 @@ export default {
324 339 justify-content: space-between;
325 340 flex-wrap: wrap;
326 341 .product {
327   - width: 48%;
  342 + width: 48%;
  343 + min-height: 120rpx;
328 344 margin: 0 0 20rpx 0;
329 345 background: #ffffff;
330 346 border: 1px solid #f2f2f2;
... ...
src/pages/test/index.vue
... ... @@ -0,0 +1,96 @@
  1 +<template>
  2 + <view class="controller">
  3 + <view class="container skeleton">
  4 + <view class="userinfo">
  5 + <block>
  6 + <!--skeleton-radius 绘制圆形-->
  7 + <image class="userinfo-avatar skeleton-radius" :src="userInfo.avatarUrl" mode="cover"></image>
  8 + <!--skeleton-rect 绘制矩形-->
  9 + <text class="userinfo-nickname skeleton-rect">{{userInfo.nickName}}</text>
  10 + </block>
  11 + </view>
  12 + <view style="margin: 20px 0">
  13 + <view v-for="(item,index) in lists" :key="index" class="lists">
  14 + <text class="skeleton-rect">{{item}}</text>
  15 + </view>
  16 + </view>
  17 +
  18 + <view class="usermotto">
  19 + <text class="user-motto skeleton-rect">{{motto}}</text>
  20 + </view>
  21 + </view>
  22 + <!--引用组件-->
  23 + <skeleton :show="showSkeleton" ref="skeleton" loading="chiaroscuro" selector="skeleton" bgcolor="#FFF"></skeleton>
  24 + </view>
  25 +</template>
  26 +
  27 +<script>
  28 + //引入骨架屏组件(以我本地地址为例,具体地址由自身引用位置决定)
  29 + import skeleton from "@/components/quick-skeleton/quick-skeleton.vue";
  30 + export default {
  31 + data() {
  32 + return {
  33 + motto: 'Hello World',
  34 + userInfo: {
  35 + avatarUrl: 'https://wx.qlogo.cn/mmopen/vi_32/s4RzXCAQsVNliaJXtHBvdpAkeRwnK7Jhiaf9mzuVqEhZza3zSYM7tJ1xZCQE9SCoOR8qjVEjDKltw1SQnxyicWq6A/132',
  36 + nickName: 'jayzou'
  37 + },
  38 + lists: [
  39 + '第1行数据',
  40 + '第2行数据',
  41 + '第3行数据',
  42 + '第4行数据',
  43 + '第5行数据',
  44 + '第6行数据'
  45 + ],
  46 + showSkeleton: false //骨架屏显示隐藏
  47 + }
  48 + },
  49 + components: {
  50 + skeleton
  51 + },
  52 + onLoad: function () {
  53 + },
  54 + /**
  55 + * 页面载入完成后调用子组件的方法生成预加载效果
  56 + */
  57 + onReady:function(){
  58 + const that = this;
  59 + that.showSkeleton = true;
  60 +
  61 + setTimeout(() => {
  62 + that.showSkeleton = false;
  63 + }, 2000);
  64 + }
  65 + }
  66 +</script>
  67 +
  68 +<style>
  69 +.container {
  70 + padding: 20upx 60upx;
  71 +}
  72 +/**index.wxss**/
  73 +.userinfo {
  74 + display: flex;
  75 + flex-direction: column;
  76 + align-items: center;
  77 +}
  78 +.userinfo-avatar {
  79 + width: 128rpx;
  80 + height: 128rpx;
  81 + margin: 20rpx;
  82 + border-radius: 50%;
  83 +}
  84 +.userinfo-nickname {
  85 + color: #aaa;
  86 +}
  87 +.usermotto {
  88 + margin-top: 200px;
  89 +}
  90 +.lists{
  91 + margin: 10px 0;
  92 +}
  93 +.list{
  94 + margin-right: 10px;
  95 +}
  96 +</style>
0 97 \ No newline at end of file
... ...
src/static/easy-loadimage/default.jpeg

2.14 KB

src/store/modules/index.js
... ... @@ -7,11 +7,21 @@
7 7 search,
8 8 } = urlAlias;
9 9  
  10 +let initData = {"name":"志平防蓝光-防辐射电脑网课眼镜","id":"9","imgurl":"",
  11 + "price":0,"trade_num":"102","rsSon":{"pic":"9_FDB33D.jpg","model_pic":null,"in_price":"6000","sku_value":"80_83",
  12 + "discount":"45","kc":"0","sku_name":"1.56非球面防蓝光_黑透+蓝纹","Max_Price":158,"Min_Price":99}}
  13 +var initArr = [1,2,3,4,5,6,7,8]
  14 +let list = []
  15 +initArr.forEach(function (value) {
  16 + list.push(initData)
  17 +});
  18 +
  19 +//初始化数据是为了显示默认骨架
10 20 const state = {
11 21 categoryList: [],
12   - list: []
  22 + list: list
13 23 };
14   -
  24 +
15 25 const mutations = {
16 26 LIST: (state, list) => {
17 27 state.list = list;
... ... @@ -25,10 +35,9 @@
25 35 category({
26 36 commit
27 37 }, param) {
28   - request({
  38 + return new Promise((resolve) => request({
29 39 url: category,
30 40 success: (res) => {
31   - console.log('category', res);
32 41 let data = res.data.data;
33 42 for (let i = 0; i <= data.length; i++) {
34 43 if (data[i] && data[i].type !== 'filter') {
... ... @@ -40,35 +49,34 @@
40 49 name: "全部",
41 50 value: "all",
42 51 isNoPull: true,
43   -
44 52 });
45 53 commit('CATEGORY', data);
  54 + resolve(data)
  55 + console.log('3333')
46 56 },
47 57 fail: (res) => {
48 58 console.log("fail status === > ", res);
49   - },
50   - complete: (res) => {
51   - console.log("complete status === > ", res);
52   - },
53   - })
  59 + }
  60 + }))
  61 +
54 62 },
55 63 list({
56 64 commit
57 65 }, param) {
58   -
59   - request({
  66 + return new Promise((resolve) => request({
60 67 url: shopList,
61 68 success: (res) => {
62 69 commit('LIST', res.data.data)
  70 + resolve(res.data)
  71 + console.log('3333')
63 72 },
64 73 fail: (res) => {
65 74 console.log("fail status === > ", res);
66 75 },
67 76 complete: (res) => {
68   - console.log("complete status === > ", res);
69 77 state.isLoading = false;
70 78 },
71   - })
  79 + }))
72 80 },
73 81 search({
74 82 commit
... ... @@ -76,9 +84,8 @@
76 84 params,
77 85 keyword
78 86 }) {
79   - const uid = uni.getStorageSync('uid');
80   - console.log("params", params, keyword);
81   - request({
  87 + const uid = uni.getStorageSync('uid');
  88 + return new Promise((resolve) => request({
82 89 url: search,
83 90 data: {
84 91 params: JSON.stringify(params),
... ... @@ -88,14 +95,13 @@
88 95 },
89 96 success: (res) => {
90 97 commit('LIST', res.data.data);
  98 + resolve(res.data)
91 99 },
92 100 fail: (res) => {
93 101 console.log("fail status === > ", res);
94   - },
95   - complete: (res) => {
96   - console.log("complete status === > ", res);
97   - },
98   - })
  102 + }
  103 + }))
  104 +
99 105 },
100 106 };
101 107  
... ...