<template> <view class="bing-progress" :style="{width:bpWidth,height:bpHeight,borderRadius:borderRadius, backgroundColor:backgroundColor,flexDirection:direction!='vertical'?'row':'column'}"> <!-- 进度 --> <!-- #ifdef APP-NVUE --> <div class="bp-bar_max" :style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor, flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft,borderRadius:barBorderRadius}"> <div class="bp-bar_sub_active" :style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor, top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></div> <div class="bp-bar_active" :style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor, top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></div> </div> <!-- #endif --> <!-- #ifndef APP-NVUE --> <view class="bp-bar_max" :style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor,borderRadius:barBorderRadius, flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft}"> <view class="bp-bar_sub_active" :style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor, top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view> <view class="bp-bar_active" :style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor, top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view> </view> <!-- #endif --> <movable-area id="bp-marea" class="bp-marea" @touchmove.stop.prevent="touchmove" @touchstart.stop.prevent="touchstart" @touchcancel="touchend" @touchend="touchend" :style="{width:mareaWidth,height:mareaHeight,left:mareaLeft}"> <!-- 拖柄 --> <movable-view id="bp-mview" class="bp-mview" :direction="direction=='vertical'?'vertical':'horizontal'" :animation="false" :disabled="true" :x="handleX" :y="handleY" friction="10" damping="100" :style="{width:mhandleWidth,height:mhandleHeight,backgroundColor:handleColor, borderRadius:handleBorderRadius,fontSize:infoFontSize,top:mhandleTop}"> <view id="bp-handle" class="bp-handle" :style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}"> <image class="bp-handle-img" :src="handleImgUrl" v-if="handleImgUrl" :style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}"></image> <!-- 进度值 --> <text class="bp-handle-text" v-if="handleImgUrl=='' && infoAlign=='handle' && showInfo" :style="{fontSize:infoFontSize,color:infoColor,width:mhandleWidth,height:textHeight,borderRadius:'20px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text> <!-- 挂件 --> <!-- #ifndef APP-NVUE --> <!-- 图片挂件 --> <image v-if="widgetPos=='top' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius, bottom: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image> <image v-if="widgetPos=='right' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,left: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image> <image v-if="widgetPos=='bottom' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,top: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image> <image v-if="widgetPos=='left' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,right: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image> <!-- 自定义元素挂件 --> <view v-if="widgetPos=='top' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,bottom: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"> <slot/> </view> <view v-if="widgetPos=='right' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,left: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"> <slot/> </view> <view v-if="widgetPos=='bottom' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,top: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"> <slot/> </view> <view v-if="widgetPos=='left' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,right: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"> <slot/> </view> <!-- #endif --> </view> </movable-view> </movable-area> <!-- 进度值 --> <text class="bp-value" v-if="showValueState() || (infoAlign=='center'&&direction!='vertical' && showInfo)" :style="{color:infoColor,fontSize:infoFontSize,left:valueLeft,width:valueWidth()+'px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text> </view> </template> <script> /** * 进度条,副进度条 */ export default { created() { /** * 获取系统屏幕信息,用于后续单位换算 */ const systemInfo = uni.getSystemInfoSync() this.px2rpx = 750 / systemInfo.screenWidth this.screenWidth = systemInfo.screenWidth this.screenHeight = systemInfo.screenHeight }, mounted() { // #ifndef APP-NVUE /** * 非NVUE movable-area 滑动事件获取到的位置是相对于文档的,获取组件位置,用于计算滑块位置 */ this.updateRect() // #endif this.mmax = this.valueFormat(this.max,false) this.percent = Math.abs((this.valueFormat(this.value) - this.min) / (this.mmax - this.min)) this.subPercent = Math.abs((this.valueFormat(this.subValue,true) - this.min) / (this.mmax - this.min)) if(this.reverse) { if(this.direction!='vertical') { this.handleX = (1 - this.percent) * this.barMaxLength } else { this.handleY = this.percent * this.barMaxLength } } else { if(this.direction!='vertical') { this.handleX = this.percent * this.barMaxLength } else { this.handleY = (1 - this.percent) * this.barMaxLength } } if(this.bpname=='test') { console.log(this.mainInfo) } }, /** * sub表示副进度条属性 */ props: { // 组件名字 bpname: { type: String, default: '' }, width: { type: String, default: '300px' }, strokeWidth: { type: String, default: '30px' }, backgroundColor: { type: String, default: 'rgba(0,0,0,0)' }, noActiveColor: { type: String, default: "#00ffff" }, activeColor: { type: String, default: "#0000ff" }, subActiveColor: { type: String, default: "#ffaaaa" }, handleColor: { type: String, default: "#ffff00" }, infoColor: { type: String, default: "#000000" }, // 整个进度条的外边界圆角半径 borderRadius: { type: String, default: '5px' }, // 进度条内部滑轨圆角半径 barBorderRadius: { type: String, default: '5px' }, // active and subActive 是否显示圆角 NVUE默认true,其他默认false // #ifdef APP-NVUE isActiveCircular: { type: Boolean, default: true }, // #endif // #ifndef APP-NVUE isActiveCircular: { type: Boolean, default: false }, // #endif handleWidth: { type: String, default: '50px' }, handleHeight: { type: String, default: '40px' }, handleBorderRadius: { type: String, default: '5px' }, handleImgUrl: { type: String, default: '' }, disabled: { type: Boolean, default: false }, direction: { type: String, default: 'horizontal' }, infoEndText: { type: String, default: "" }, infoFontSize: { type: String, default: '18px' }, showInfo: { type: Boolean, default: true }, // 进度值显示value还是subValue infoContent: { type: String, default: 'value' }, // 进度值显示位置 left, right, center, handle infoAlign: { type: String, default: 'right' }, max: { type: Number, default: 100 }, min: { type: Number, default: 0 }, value: { type: Number, default: 0 }, subValue: { type: Number, default: 0 }, step: { type: Number, default: 1 }, // 副进度条步长 subStep: { type: Number, default: 1 }, // true连续滑动,false步进,即以step的间隔变化 continuous: { type: Boolean, default: true }, // 副进度条continuous subContinuous: { type: Boolean, default: true }, reverse: { type: Boolean, default: false }, // 挂件位置 top, right, bottom, left widgetPos: { type: String, default: "top" }, widgetHeight: { type: [String,Number], default: '40px' }, widgetWidth: { type: [String,Number], default: '50px' }, widgetBorderRadius: { type: [String,Number], default: '5px' }, // 挂件不透明度 0完全透明 1不透明 widgetOpacity: { type: [String,Number], default: 1 }, // 挂件距离组件的偏移量,正数原理组件,负数靠近组件 widgetOffset: { type: [String,Number], default: '0px' }, // 挂件图片 widgetUrl: { type: String, default: '' }, // 挂件旋转角度 widgetAngle: { type: [String,Number], default: 0 } }, data() { return { handleX: 50, handleY: 0, px2rpx: 1, percent: 0, // 0-1 subPercent: 0, // 0-1 mainInfo: { left: 0, top: 0, bottom: 0, right: 0 }, touchState: false, screenHeight: 0, screenWidth: 0, msubValue: 0, moveable: true, lastTouchTime: 0, mmax: 100 } }, watch: { /** * @param {Object} newValue * @param {Object} oldValue */ value(newValue, oldValue) { if(!this.touchState) { newValue = this.valueSetBoundary(newValue) this.percent = Math.abs((newValue - this.min) / (this.mmax - this.min)) } }, showValue(newValue, oldValue) { // 步进 if(!this.continuous) { let percent if(this.reverse) { if(this.direction!='vertical') { percent = Math.abs(1 - (newValue - this.min) / (this.mmax - this.min)) this.handleX = percent * this.barMaxLength } else { percent = Math.abs((newValue - this.min) / (this.mmax - this.min)) this.handleY = percent * this.barMaxLength } } else { if(this.direction!='vertical') { percent = Math.abs((newValue - this.min) / (this.mmax - this.min)) this.handleX = percent * this.barMaxLength } else { percent = (1 - Math.abs((newValue - this.min) / (this.mmax - this.min))) this.handleY = percent * this.barMaxLength } } } this.$emit("change", {bpname: this.bpname,type: 'change',value:this.showValue,subValue:this.msubValue}) this.$emit("valuechange", {bpname: this.bpname,type: 'valuechange',value:this.showValue,subValue:this.msubValue}) }, percent(newValue, oldValue) { // 连续 if(this.continuous) { if(this.reverse) { if(this.direction!='vertical') { this.handleX = (1 - newValue) * this.barMaxLength } else { this.handleY = newValue * this.barMaxLength } } else { if(this.direction!='vertical') { this.handleX = newValue * this.barMaxLength } else { this.handleY = (1 - newValue) * this.barMaxLength } } } }, subValue(newValue, oldValue) { newValue = this.valueSetBoundary(newValue) if(this.subContinuous) { this.msubValue = newValue } else { this.msubValue = this.valueFormat(newValue, true) } this.subPercent = Math.abs((newValue - this.min) / (this.mmax - this.min)) this.$emit("change", {bpname: this.bpname,type: 'change',value:this.showValue,subValue:this.msubValue}) this.$emit("subvaluechange", {bpname: this.bpname,type: 'subvaluechange',value:this.showValue,subValue:this.msubValue}) }, max(newValue,oldValue) { this.mmax = this.valueFormat(newValue,false) } }, computed: { bpWidth() { if(this.direction=="vertical") { return this.maxHeight()[2] } return this.sizeDeal(this.width)[2] }, bpHeight() { if(this.direction=="vertical") { return this.sizeDeal(this.width)[2] } return this.maxHeight()[2] }, mareaWidth() { if(this.direction=="vertical") { return this.maxHeight()[2] } let width = this.sizeDeal(this.width)[0] return (width - this.textWidth()) + 'px' }, mareaHeight() { if(this.direction=="vertical") { let width = this.sizeDeal(this.width)[0] return (width - this.textWidth()) + 'px' } return this.maxHeight()[2] }, mareaLeft() { if(this.showValueState()) { if(this.infoAlign == 'left') { return this.textWidth() + 'px' } } return 0 }, barMaxHeight() { if(this.direction=="vertical") { let width = this.sizeDeal(this.width)[0] let handleWidth = this.sizeDeal(this.handleWidth) return (width - this.textWidth() - handleWidth[0]) + 'px' } return this.sizeDeal(this.strokeWidth)[2] }, barMaxWidth() { if(this.direction=="vertical") { return this.sizeDeal(this.strokeWidth)[2] } let width = this.sizeDeal(this.width)[0] let handleWidth = this.sizeDeal(this.handleWidth) return (width - this.textWidth() - handleWidth[0]) + 'px' }, barMaxLeft() { if(this.showValueState()) { if(this.infoAlign == 'left') { return this.textWidth() + this.sizeDeal(this.handleWidth)[0] / 2 + 'px' } } if(this.direction != 'vertical') { return this.sizeDeal(this.handleWidth)[0] / 2 + 'px' } // vertical return (this.maxHeight()[0] - this.sizeDeal(this.strokeWidth)[0]) / 2 + 'px' }, activeRight() { if(this.reverse) { return 0 } return 'unset' }, activeLeft() { if(this.reverse) { return 'unset' } return 0 }, activeTop() { if(this.reverse) { return 0 } return 'unset' }, activeBottom() { if(this.reverse) { return 'unset' } return 0 }, barActiveWidth() { if(this.direction=="vertical") { return this.sizeDeal(this.strokeWidth)[2] } let percent if(this.continuous) { percent = this.percent } else { percent = Math.abs((this.showValue - this.min) / (this.mmax - this.min)) } return this.barMaxLength * percent + 'px' }, barActiveHeight() { if(this.direction=="vertical") { let percent if(this.continuous) { percent = this.percent } else { percent = Math.abs((this.showValue - this.min) / (this.mmax - this.min)) } return this.barMaxLength * percent + 'px' } return this.sizeDeal(this.strokeWidth)[2] }, subActiveTop() { if(this.reverse) { return 0 } return 'unset' }, subActiveBottom() { if(this.reverse) { return 'unset' } return 0 }, subActiveRight() { if(this.reverse) { return 0 } return 'unset' }, subActiveLeft() { if(this.reverse) { return 'unset' } return 0 }, barSubActiveWidth() { if(this.direction == "vertical") { return this.sizeDeal(this.strokeWidth)[2] } if(this.subContinuous) { return this.barMaxLength * this.subPercent + 'px' } else { return this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.mmax - this.min)) + 'px' } }, barSubActiveHeight() { if(this.direction == "vertical") { if(this.subContinuous) { return this.barMaxLength * this.subPercent + 'px' } else { this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.mmax - this.min)) + 'px' } } return this.sizeDeal(this.strokeWidth)[2] }, mhandleWidth() { if(this.direction == "vertical") { return this.sizeDeal(this.handleHeight)[2] } return this.sizeDeal(this.handleWidth)[2] }, mhandleHeight() { if(this.direction == "vertical") { return this.sizeDeal(this.handleWidth)[2] } return this.sizeDeal(this.handleHeight)[2] }, mhandleTop() { if(this.direction == 'vertical') { return 0 } else { // 拖柄垂直居中 let handle = this.sizeDeal(this.handleHeight)[0] let top = this.maxHeight()[0] / 2 - handle / 2 + 'px' return top } }, showValue() { return this.valueFormat(this.percent * (this.mmax - this.min) + this.min) }, textHeight() { let infoSize = this.sizeDeal(this.infoFontSize) return infoSize[0]*1.2 + infoSize[1] }, valueLeft() { if(this.infoAlign=='left') { return 0 } else if(this.infoAlign == 'center') { let width = this.sizeDeal(this.width) return width[0]/2 - this.valueWidth()/2 + 'px' } else if(this.infoAlign=='right'){ let width = this.sizeDeal(this.width) return width[0] - this.textWidth() + 'px' } return 0 }, barMaxLength() { let width = this.sizeDeal(this.width)[0] let handleWidth = this.sizeDeal(this.handleWidth) return width - this.textWidth() - handleWidth[0] }, mwidgetWidth() { return this.sizeDeal(this.widgetWidth)[2]; }, mwidgetHeight() { return this.sizeDeal(this.widgetHeight)[2]; }, moffset() { let off = this.sizeDeal(this.widgetOffset); // console.log(off) switch(this.widgetPos) { case 'top': return this.sizeDeal(this.mhandleHeight)[0] + off[0] + 'px' case 'right': return this.sizeDeal(this.mhandleWidth)[0] + off[0] + 'px' case 'bottom': return this.sizeDeal(this.mhandleHeight)[0] + off[0] + 'px' case 'left': return this.sizeDeal(this.mhandleWidth)[0] + off[0] + 'px' } return 0 }, mwidgetBorderRadius() { return this.sizeDeal(this.widgetBorderRadius)[2]; }, mwidgetAngle() { return "rotate("+Number(this.widgetAngle)+"deg)" } }, methods: { prevent(e) { console.log(1) }, updateRect() { // #ifndef APP-NVUE /** * 非NVUE movable-area 滑动事件获取到的位置是相对于文档的,获取组件位置,用于计算滑块位置 */ let query = uni.createSelectorQuery().in(this) query.select('.bing-progress').boundingClientRect(data => { this.mainInfo.top = data.top this.mainInfo.left = data.left this.mainInfo.bottom = data.bottom this.mainInfo.right = data.right }).exec() // #endif }, touchstart(e) { if(!this.disabled) { // #ifdef APP-NVUE e.stopPropagation() e.target.attr.preventGesture = true if(this.direction == 'vertical' && e.target.attr.id != 'bp-mview' && (e.timestamp - this.lastTouchTime > 100)) { this.moveable = false } this.lastTouchTime = e.timestamp // #endif // #ifndef APP-NVUE /** * 防止组件在文档流中的位置被修改,导致组件进度值异常 */ this.updateRect() // #endif // 阻止组件信息异常情况下的进度值修改 if(this.mainInfo.top > this.screenHeight) { this.$emit("dragstart", {bpname: this.bpname,type: 'dragstart',value:this.showValue,subValue:this.msubValue}) return } this.touchState = true let detail = e.changedTouches[0] this.handleMove(detail) this.$emit("dragstart", {bpname: this.bpname,type: 'dragstart',value:this.showValue,subValue:this.msubValue}) } }, touchmove(e) { if(!this.disabled) { let detail = e.changedTouches[0] this.handleMove(detail) this.$emit("dragging", {bpname: this.bpname,type: 'dragging',value:this.showValue,subValue:this.msubValue}) } }, touchend(e) { if(!this.disabled) { // #ifdef APP-NVUE if(!this.moveable) { this.moveable = true return } // #endif let detail = e.changedTouches[0] this.handleMove(detail) this.touchState = false this.$emit("dragend", {bpname: this.bpname,type: 'dragend',value:this.showValue,subValue:this.msubValue}) } }, handleMove(detail) { let width = this.sizeDeal(this.width)[0] let handleWidth = this.sizeDeal(this.handleWidth) let percent if(this.direction!='vertical') { if(this.infoAlign=='left') { // #ifndef APP-NVUE percent = (detail.pageX - this.mainInfo.left - this.textWidth() - handleWidth[0]/2)/ this.barMaxLength // #endif // #ifdef APP-NVUE percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength // #endif } else { // #ifndef APP-NVUE percent = (detail.pageX - this.mainInfo.left - handleWidth[0]/2)/ this.barMaxLength // #endif // #ifdef APP-NVUE percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength // #endif } } else { // #ifdef APP-NVUE percent = 1 - (detail.pageY - handleWidth[0]/2- 1) / this.barMaxLength // #endif // #ifndef APP-NVUE percent = 1 - (detail.clientY - this.mainInfo.top - handleWidth[0]/2)/ this.barMaxLength // #endif } percent = percent > 0 ? percent : 0 percent = percent < 1 ? percent : 1 if(this.reverse) { this.percent = 1 - percent } else { this.percent = percent } }, showValueState() { if(this.direction != 'vertical' && this.showInfo && (this.infoAlign=='left' || this.infoAlign=='right')) { return true } return false }, valueSetBoundary(value) { // 控制value在合法范围内 if(this.mmax > this.min) { value = value < this.mmax ? value : this.mmax value = value > this.min ? value : this.min } else { value = value > this.mmax ? value : this.mmax value = value < this.min ? value : this.min } return value }, /** * @param {Object} v * @param {Object} isSub 是否是副副进度条 */ valueFormat (v,isSub){ // set step v = this.valueSetBoundary(v) let stepInfo = this.stepInfo(isSub) v = Number(v - this.min).toFixed(stepInfo[1]) let step = stepInfo[0] * 10 ** stepInfo[1] let valueE = v * 10 ** stepInfo[1] let remainder = valueE % step let remainderInt = Math.floor(remainder) // 对余数四舍五入0-1 let sub = Math.round(remainder / step) let value = (Math.floor(valueE) - remainderInt + sub*step) / (10 ** stepInfo[1]) value = Number((value + this.min).toFixed(stepInfo[1])) return value }, /** * @param {Object} v * @param {Object} isSub 是否是副副进度条 */ stepInfo(isSub) { // return step, decimal位数 let step if(isSub) { step = Number(this.subStep) } else { step = Number(this.step) } if (step <= 0 || !step){ return [1, 0] } else{ let steps = step.toString().split('.') if (steps.length == 1){ return [step,0] } else { return [step,steps[1].length] } } }, textWidth() { if(this.showValueState()) { let numWidth = this.mmax.toString().length> this.min.toString().length? this.mmax.toString().length: this.min.toString().length let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0] return Number(textWidth.toFixed(2)) } return 0 }, valueWidth() { let numWidth = this.mmax.toString().length> this.min.toString().length? this.mmax.toString().length: this.min.toString().length let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0] return Number(textWidth.toFixed(2)) }, maxHeight() { let h = [] if (this.direction!='vertical'){ // direction 为 vertical 时不显示info let subt = this.infoEndText.match(/[^\x00-\xff]/g) if (subt){ h.push(this.sizeDeal(this.infoFontSize)[0] * 1.1) } else{ h.push(this.sizeDeal(this.infoFontSize)[0]) } } h.push(this.sizeDeal(this.strokeWidth)[0]) h.push(this.sizeDeal(this.handleHeight)[0]) h.sort(function(a, b) { return b - a }) // 降序 return [h[0], 'px', h[0] + 'px'] }, sizeDeal(size) { // 分离字体大小和单位,rpx 转 px let s = Number.isNaN(parseFloat(size)) ? 0 : parseFloat(size) let u = size.toString().replace(/[0-9\.]/g, '') if (u == 'rpx') { s /= this.px2rpx u = 'px' }else if (u == 'vw') { u = 'px' s = s / 100 * this.screenWidth } else if(u == 'vh') { u = 'px' s = s / 100 * this.screenHeight } else{ u = 'px' } return [s, u, s + u] }, } } </script> <style scoped> @import "bing-progress.css" </style>