tui-skeleton.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <template>
  2. <view class="tui-skeleton-cmomon tui-skeleton-box" :style="{width: winWidth+'px', height:winHeight+'px', backgroundColor:backgroundColor}">
  3. <view class="tui-skeleton-cmomon" v-for="(item,index) in skeletonElements" :key="index" :style="{width: item.width+'px', height:item.height+'px', left: item.left+'px', top: item.top+'px',backgroundColor: skeletonBgColor,borderRadius:getRadius(item.skeletonType,borderRadius)}"></view>
  4. <view class="tui-loading" :class="[getLoadingType(loadingType)]" v-if="isLoading"></view>
  5. </view>
  6. </template>
  7. <script>
  8. // +----------------------------------------------------------------------
  9. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  10. // +----------------------------------------------------------------------
  11. // | Copyright (c) 2016~2025 https://www.crmeb.com All rights reserved.
  12. // +----------------------------------------------------------------------
  13. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  14. // +----------------------------------------------------------------------
  15. // | Author: CRMEB Team <admin@crmeb.com>
  16. // +----------------------------------------------------------------------
  17. export default {
  18. name: "tuiSkeleton",
  19. props: {
  20. //选择器(外层容器)
  21. selector: {
  22. type: String,
  23. default: "tui-skeleton"
  24. },
  25. //外层容器背景颜色
  26. backgroundColor: {
  27. type: String,
  28. default: "#fff"
  29. },
  30. //骨架元素背景颜色
  31. skeletonBgColor: {
  32. type: String,
  33. default: "#e9e9e9"
  34. },
  35. //骨架元素类型:矩形,圆形,带圆角矩形["rect","circular","fillet"]
  36. //默认所有,根据页面情况进行传值
  37. //页面对应元素class为:tui-skeleton-rect,tui-skeleton-circular,tui-skeleton-fillet
  38. //如果传入的值不在下列数组中,则为自定义class值,默认按矩形渲染
  39. skeletonType: {
  40. type: Array,
  41. default () {
  42. return ["rect", "circular", "fillet"]
  43. }
  44. },
  45. //圆角值,skeletonType=fillet时生效
  46. borderRadius: {
  47. type: String,
  48. default: "16rpx"
  49. },
  50. //骨架屏预生成数据:提前生成好的数据,当传入该属性值时,则不会再次查找子节点信息
  51. preloadData: {
  52. type: Array,
  53. default () {
  54. return []
  55. }
  56. },
  57. //是否需要loading
  58. isLoading: {
  59. type: Boolean,
  60. default: false
  61. },
  62. //loading类型[1-10]
  63. loadingType: {
  64. type: Number,
  65. default: 1
  66. }
  67. },
  68. created() {
  69. const res = uni.getSystemInfoSync();
  70. this.winWidth = res.windowWidth;
  71. this.winHeight = res.windowHeight;
  72. //如果有预生成数据,则直接使用
  73. this.isPreload(true)
  74. },
  75. mounted() {
  76. this.$nextTick(() => {
  77. this.nodesRef(`.${this.selector}`).then((res) => {
  78. if(res && res[0]){
  79. this.winHeight = res[0].height + Math.abs(res[0].top)
  80. }
  81. });
  82. !this.isPreload() && this.selectorQuery()
  83. })
  84. },
  85. data() {
  86. return {
  87. winWidth: 375,
  88. winHeight: 800,
  89. skeletonElements: []
  90. };
  91. },
  92. methods: {
  93. getLoadingType: function(type) {
  94. let value = 1
  95. if (type && type > 0 && type < 11) {
  96. value = type
  97. }
  98. return 'tui-loading-' + value
  99. },
  100. getRadius: function(type, val) {
  101. let radius = "0"
  102. if (type == "circular") {
  103. radius = "50%"
  104. } else if (type == "fillet") {
  105. radius = val
  106. }
  107. return radius;
  108. },
  109. isPreload(init) {
  110. let preloadData = this.preloadData || []
  111. if (preloadData.length) {
  112. init && (this.skeletonElements = preloadData)
  113. return true
  114. }
  115. return false
  116. },
  117. async selectorQuery() {
  118. let skeletonType = this.skeletonType || []
  119. let nodes = []
  120. for (let item of skeletonType) {
  121. let className = '';
  122. // #ifndef MP-WEIXIN
  123. className = `.${item}`;
  124. if (~'rect_circular_fillet'.indexOf(item)) {
  125. className = `.${this.selector}-${item}`;
  126. }
  127. // #endif
  128. // #ifdef MP-WEIXIN
  129. className = `.${this.selector} >>> .${item}`;
  130. if (~'rect_circular_fillet'.indexOf(item)) {
  131. className = `.${this.selector} >>> .${this.selector}-${item}`;
  132. }
  133. // #endif
  134. await this.nodesRef(className).then((res) => {
  135. res.map(d => {
  136. d.skeletonType = item
  137. })
  138. nodes = nodes.concat(res)
  139. })
  140. }
  141. this.skeletonElements = nodes
  142. },
  143. async nodesRef(className) {
  144. return await new Promise((resolve, reject) => {
  145. uni.createSelectorQuery().selectAll(className).boundingClientRect((res) => {
  146. if (res) {
  147. resolve(res);
  148. } else {
  149. reject(res)
  150. }
  151. }).exec();
  152. })
  153. }
  154. }
  155. }
  156. </script>
  157. <style scoped>
  158. .tui-skeleton-cmomon {
  159. position: absolute;
  160. z-index: 99999;
  161. }
  162. .tui-skeleton-box {
  163. left: 0;
  164. top: 0;
  165. }
  166. .tui-loading {
  167. display: inline-block;
  168. vertical-align: middle;
  169. width: 40rpx;
  170. height: 40rpx;
  171. background: 0 0;
  172. border-radius: 50%;
  173. border: 2px solid;
  174. animation: tui-rotate 0.7s linear infinite;
  175. position: fixed;
  176. z-index: 999999;
  177. left: 50%;
  178. top: 50%;
  179. margin-left: -20rpx;
  180. margin-top: -20rpx;
  181. }
  182. .tui-loading-1 {
  183. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #5677fc;
  184. }
  185. .tui-loading-2 {
  186. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e;
  187. }
  188. .tui-loading-3 {
  189. border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) #fff;
  190. }
  191. .tui-loading-4 {
  192. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #35b06a;
  193. }
  194. .tui-loading-5 {
  195. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #fc872d;
  196. }
  197. .tui-loading-6 {
  198. border-color: #e5e5e5 #e5e5e5 #e5e5e5 #eb0909;
  199. }
  200. .tui-loading-7 {
  201. border-color: #5677fc transparent #5677fc transparent;
  202. }
  203. .tui-loading-8 {
  204. border-color: #35b06a transparent #35b06a transparent;
  205. }
  206. .tui-loading-9 {
  207. border-color: #fc872d transparent #fc872d transparent;
  208. }
  209. .tui-loading-10 {
  210. border-color: #eb0909 transparent #eb0909 transparent;
  211. }
  212. @-webkit-keyframes tui-rotate {
  213. 0% {
  214. transform: rotate(0);
  215. }
  216. 100% {
  217. transform: rotate(360deg);
  218. }
  219. }
  220. @keyframes tui-rotate {
  221. 0% {
  222. transform: rotate(0);
  223. }
  224. 100% {
  225. transform: rotate(360deg);
  226. }
  227. }
  228. </style>