|
@@ -0,0 +1,318 @@
|
|
|
+<template>
|
|
|
+ <view class="cmd-circle">
|
|
|
+ <canvas :canvas-id="cid" :style="calCircleStyle"></canvas>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ /**
|
|
|
+ * 进度圈组件
|
|
|
+ * @description 用圈显示一个操作完成的百分比时,为用户显示该操作的当前进度和状态。
|
|
|
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=259
|
|
|
+ * @property {String} cid 画布编号 - 默认defaultCanvas
|
|
|
+ * @property {String} type 进度圈类型 - 圆圈形:circle、仪表盘:dashboard,默认圆圈形:circle
|
|
|
+ * @property {Number} percent 进度圈百分比值 - 显示范围0-100 ,可能数比较大就需要自己转成百分比的值
|
|
|
+ * @property {Boolean} show-info 进度圈进度状态信息 - 显示进度数值或状态图标,默认true
|
|
|
+ * @property {String} font-color 进度圈文字信息颜色
|
|
|
+ * @property {String} font-size 进度圈文字信息大小 - 默认:14
|
|
|
+ * @property {String} status 进度圈状态 - 正常:normal、完成:success、失败:exception,默认正常:normal
|
|
|
+ * @property {Number} stroke-width 进度圈线条宽度 - 建议在条线的宽度范围:1-50,与进度条显示宽度有关,默认:6
|
|
|
+ * @property {String} stroke-color 进度圈的颜色 - 设置后status状态无效
|
|
|
+ * @property {String} stroke-background 进度圈的底圈颜色 - 默认:#eeeeee
|
|
|
+ * @property {String} stroke-shape 进度圈两端的形状 - 圆:round、方直角:square,默认圆:round
|
|
|
+ * @property {Number} width 进度圈布宽度 - 默认80
|
|
|
+ * @property {String} gap-degree 进度圈形缺口角度 - 可取值 0 ~ 360,仅支持类型:circle
|
|
|
+ * @property {String} gap-position 进度圈形缺口位置 - 可取值'top', 'bottom', 'left', 'right',仅支持类型:circle
|
|
|
+ * @example <cmd-circle id="circle1" type="circle" :percent="75"></cmd-circle>
|
|
|
+ */
|
|
|
+ export default {
|
|
|
+ name: "cmd-circle",
|
|
|
+
|
|
|
+ props: {
|
|
|
+ // 画布编号 默认defaultCanvas
|
|
|
+ cid: {
|
|
|
+ type: String,
|
|
|
+ default: "defaultCanvas"
|
|
|
+ },
|
|
|
+ // 圈类型默认:circle,可选 circle dashboard
|
|
|
+ type: {
|
|
|
+ type: String,
|
|
|
+ validator: val => {
|
|
|
+ return ['circle', 'dashboard'].includes(val);
|
|
|
+ },
|
|
|
+ default: 'circle'
|
|
|
+ },
|
|
|
+ // 圈进度百分比值
|
|
|
+ percent: {
|
|
|
+ type: Number,
|
|
|
+ validator: val => {
|
|
|
+ return val >= 0 && val <= 100;
|
|
|
+ },
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ // 圈是否显示进度数值或状态图标
|
|
|
+ showInfo: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ // 圈文字信息颜色
|
|
|
+ fontColor: {
|
|
|
+ type: String,
|
|
|
+ default: "#595959"
|
|
|
+ },
|
|
|
+ // 圈文字信息大小 默认14
|
|
|
+ fontSize: {
|
|
|
+ type: Number,
|
|
|
+ default: 14
|
|
|
+ },
|
|
|
+ // 圈进度状态,可选:normal success exception
|
|
|
+ status: {
|
|
|
+ type: String,
|
|
|
+ validator: val => {
|
|
|
+ return ['normal', 'success', 'exception'].includes(val);
|
|
|
+ },
|
|
|
+ default: 'normal'
|
|
|
+ },
|
|
|
+ // 圈线条宽度1-50,与width有关
|
|
|
+ strokeWidth: {
|
|
|
+ type: Number,
|
|
|
+ default: 6
|
|
|
+ },
|
|
|
+ // 圈的颜色,设置后status状态无效
|
|
|
+ strokeColor: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ // 圈的底圈颜色 默认:#eeeeee
|
|
|
+ strokeBackground: {
|
|
|
+ type: String,
|
|
|
+ default: '#eeeeee'
|
|
|
+ },
|
|
|
+ // 圈两端的形状 可选:'round', 'square'
|
|
|
+ strokeShape: {
|
|
|
+ type: String,
|
|
|
+ validator: val => {
|
|
|
+ return ['round', 'square'].includes(val);
|
|
|
+ },
|
|
|
+ default: 'round'
|
|
|
+ },
|
|
|
+ // 圈画布宽度
|
|
|
+ width: {
|
|
|
+ type: Number,
|
|
|
+ default: 80
|
|
|
+ },
|
|
|
+ // 圈缺口角度,可取值 0 ~ 360,仅支持类型:circle
|
|
|
+ gapDegree: {
|
|
|
+ type: Number,
|
|
|
+ validator: val => {
|
|
|
+ return val >= 0 && val <= 360;
|
|
|
+ },
|
|
|
+ default: 360
|
|
|
+ },
|
|
|
+ // 圈缺口开始位置,可取值'top', 'bottom', 'left', 'right',仅支持类型:circle
|
|
|
+ gapPosition: {
|
|
|
+ type: String,
|
|
|
+ validator: val => {
|
|
|
+ return ['top', 'bottom', 'left', 'right'].includes(val);
|
|
|
+ },
|
|
|
+ default: 'top'
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ // 画布实例
|
|
|
+ ctx: {},
|
|
|
+ // 圈半径
|
|
|
+ width2px: ""
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ // 计算设置圈样式
|
|
|
+ calCircleStyle() {
|
|
|
+ return `width: ${this.width}px;
|
|
|
+ height: ${this.width}px;`
|
|
|
+ },
|
|
|
+ // 计算圈状态
|
|
|
+ calStatus() {
|
|
|
+ let status = {}
|
|
|
+ switch (this.status) {
|
|
|
+ case 'normal':
|
|
|
+ status = {
|
|
|
+ color: "#1890ff",
|
|
|
+ value: 1
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ case 'success':
|
|
|
+ status = {
|
|
|
+ color: "#52c41a",
|
|
|
+ value: 2
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ case 'exception':
|
|
|
+ status = {
|
|
|
+ color: "#f5222d",
|
|
|
+ value: 3
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return status
|
|
|
+ },
|
|
|
+ // 计算圈缺口角度
|
|
|
+ calGapDegree() {
|
|
|
+ return this.gapDegree <= 0 ? 360 : this.gapDegree
|
|
|
+ },
|
|
|
+ // 计算圈缺口位置
|
|
|
+ calGapPosition() {
|
|
|
+ let gapPosition = 0
|
|
|
+ switch (this.gapPosition) {
|
|
|
+ case 'bottom':
|
|
|
+ gapPosition = 90;
|
|
|
+ break;
|
|
|
+ case 'left':
|
|
|
+ gapPosition = 180;
|
|
|
+ break;
|
|
|
+ case 'top':
|
|
|
+ gapPosition = 270;
|
|
|
+ break;
|
|
|
+ case 'right':
|
|
|
+ gapPosition = 360;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return gapPosition
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ watch: {
|
|
|
+ // 监听百分比值改变
|
|
|
+ percent(val) {
|
|
|
+ this.drawStroke(val);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ mounted() {
|
|
|
+ // 创建画布实例
|
|
|
+ this.ctx = uni.createCanvasContext(this.cid, this)
|
|
|
+ // upx转px 圈半径大小
|
|
|
+ this.width2px = uni.upx2px(this.width)
|
|
|
+ // 绘制初始
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.drawStroke(this.percent)
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ // 绘制圈
|
|
|
+ drawStroke(percent) {
|
|
|
+ percent = percent >= 100 ? 100 : percent < 0 ? 0 : percent
|
|
|
+ // 圈条进度色
|
|
|
+ let color = this.strokeColor || this.calStatus.color
|
|
|
+ // 是否圈中心显示信息
|
|
|
+ if (this.showInfo) {
|
|
|
+ switch (this.calStatus.value) {
|
|
|
+ case 1:
|
|
|
+ if (percent >= 100) {
|
|
|
+ // 设置打勾
|
|
|
+ this.drawSuccess()
|
|
|
+ percent = 100
|
|
|
+ color = "#52c41a"
|
|
|
+ } else {
|
|
|
+ // 设置字体
|
|
|
+ this.drawText(percent)
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ // 设置打勾
|
|
|
+ this.drawSuccess()
|
|
|
+ percent = 100
|
|
|
+ color = "#52c41a"
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ // 设置打叉
|
|
|
+ this.drawException()
|
|
|
+ percent = 0
|
|
|
+ color = "#f5222d"
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 缺口
|
|
|
+ let gapPosition = this.calGapPosition
|
|
|
+ let gapDegree = this.calGapDegree
|
|
|
+ // 仪表固定
|
|
|
+ if (this.type === "dashboard") {
|
|
|
+ gapPosition = 135
|
|
|
+ gapDegree = 270
|
|
|
+ }
|
|
|
+ // 圈型条宽
|
|
|
+ this.ctx.setLineCap(this.strokeShape)
|
|
|
+ this.ctx.setLineWidth(this.strokeWidth)
|
|
|
+ // 位置原点
|
|
|
+ this.ctx.translate(this.width2px, this.width2px)
|
|
|
+ // 缺口方向
|
|
|
+ this.ctx.rotate(gapPosition * Math.PI / 180)
|
|
|
+ // 圈底
|
|
|
+ this.ctx.beginPath()
|
|
|
+ this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, gapDegree * Math.PI / 180)
|
|
|
+ this.ctx.setStrokeStyle(this.strokeBackground)
|
|
|
+ this.ctx.stroke()
|
|
|
+ // 圈进度
|
|
|
+ this.ctx.beginPath()
|
|
|
+ this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, percent * gapDegree * Math.PI / 18000)
|
|
|
+ this.ctx.setStrokeStyle(color)
|
|
|
+ this.ctx.stroke()
|
|
|
+ // 绘制
|
|
|
+ this.ctx.draw()
|
|
|
+ },
|
|
|
+ // 绘制文字格式
|
|
|
+ drawText(percent) {
|
|
|
+ this.ctx.beginPath()
|
|
|
+ this.ctx.setFontSize(this.fontSize)
|
|
|
+ this.ctx.setFillStyle(this.fontColor)
|
|
|
+ this.ctx.setTextAlign('center')
|
|
|
+ this.ctx.fillText(`${percent}%`, this.width2px, this.width2px + this.fontSize / 2)
|
|
|
+ this.ctx.stroke()
|
|
|
+ },
|
|
|
+ // 绘制成功打勾
|
|
|
+ drawSuccess() {
|
|
|
+ let x = this.width2px - this.fontSize / 2
|
|
|
+ let y = this.width2px + this.fontSize / 2
|
|
|
+ this.ctx.beginPath()
|
|
|
+ this.ctx.setLineCap('round')
|
|
|
+ this.ctx.setLineWidth(this.fontSize / 4)
|
|
|
+ this.ctx.moveTo(this.width2px, y)
|
|
|
+ this.ctx.lineTo(y, x)
|
|
|
+ this.ctx.moveTo(this.width2px, y)
|
|
|
+ this.ctx.lineTo(x, this.width2px)
|
|
|
+ this.ctx.setStrokeStyle("#52c41a")
|
|
|
+ this.ctx.stroke()
|
|
|
+ },
|
|
|
+ // 绘制异常打叉
|
|
|
+ drawException() {
|
|
|
+ let x = this.width2px - this.fontSize / 2
|
|
|
+ let y = this.width2px + this.fontSize / 2
|
|
|
+ this.ctx.beginPath()
|
|
|
+ this.ctx.setLineCap('round')
|
|
|
+ this.ctx.setLineWidth(this.fontSize / 4)
|
|
|
+ this.ctx.moveTo(x, x)
|
|
|
+ this.ctx.lineTo(y, y)
|
|
|
+ this.ctx.moveTo(y, x)
|
|
|
+ this.ctx.lineTo(x, y)
|
|
|
+ this.ctx.setStrokeStyle("#f5222d")
|
|
|
+ this.ctx.stroke()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+ .cmd-circle {
|
|
|
+ display: inline-block;
|
|
|
+ box-sizing: border-box;
|
|
|
+ list-style: none;
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+</style>
|