Commit 53012a16c9f186ff9dab22422ba4344917601b9f
1 parent
cf70629b
首页 优化
Showing
3 changed files
with
128 additions
and
75 deletions
components/uni-charts/uni-charts.vue
| @@ -38,7 +38,11 @@ export default { | @@ -38,7 +38,11 @@ export default { | ||
| 38 | option: { | 38 | option: { |
| 39 | type: Object, | 39 | type: Object, |
| 40 | default () { | 40 | default () { |
| 41 | - return {} | 41 | + return { |
| 42 | + yAxis: { | ||
| 43 | + tickCount: 5 // 默认5个刻度,可通过option自定义,无上限 | ||
| 44 | + } | ||
| 45 | + } | ||
| 42 | } | 46 | } |
| 43 | }, | 47 | }, |
| 44 | width: { | 48 | width: { |
| @@ -88,7 +92,41 @@ export default { | @@ -88,7 +92,41 @@ export default { | ||
| 88 | this.drawLine() | 92 | this.drawLine() |
| 89 | } | 93 | } |
| 90 | }, | 94 | }, |
| 91 | - // 折线图绘制(移除平滑过渡,改为纯直线) | 95 | + // 核心:自适应计算Y轴整数刻度(无任何数值限制) |
| 96 | + calculateYAxisTicks (minVal, maxVal, tickCount = 5) { | ||
| 97 | + // 1. 确保是整数,且最大值>最小值(无数值范围限制) | ||
| 98 | + minVal = parseInt(minVal, 10) | ||
| 99 | + maxVal = parseInt(maxVal, 10) | ||
| 100 | + | ||
| 101 | + // 边界处理:极值相等时自动扩展范围(避免刻度重复) | ||
| 102 | + if (maxVal <= minVal) { | ||
| 103 | + maxVal = minVal + tickCount // 扩展范围,保证刻度不重复 | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + // 2. 动态计算整数步长(完全自适应,无固定值限制) | ||
| 107 | + const totalRange = maxVal - minVal | ||
| 108 | + let step = Math.ceil(totalRange / (tickCount - 1)) // 关键:按刻度数算步长 | ||
| 109 | + step = step < 1 ? 1 : step // 步长至少为1,保证整数刻度 | ||
| 110 | + | ||
| 111 | + // 3. 生成连续不重复的整数刻度(无数值上限) | ||
| 112 | + const ticks = [] | ||
| 113 | + for (let i = 0; i < tickCount; i++) { | ||
| 114 | + ticks.push(minVal + i * step) | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + // 4. 确保最大值能覆盖数据(无限制) | ||
| 118 | + if (ticks[ticks.length - 1] < maxVal) { | ||
| 119 | + ticks.push(maxVal) | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + return { | ||
| 123 | + ticks: ticks, // 最终刻度数组(纯整数、不重复、无限制) | ||
| 124 | + min: minVal, | ||
| 125 | + max: Math.max(...ticks), | ||
| 126 | + step: step | ||
| 127 | + } | ||
| 128 | + }, | ||
| 129 | + // 折线图绘制(无限制版) | ||
| 92 | drawLine () { | 130 | drawLine () { |
| 93 | if (!this.data.length || !this.categories.length) return; | 131 | if (!this.data.length || !this.categories.length) return; |
| 94 | 132 | ||
| @@ -106,7 +144,7 @@ export default { | @@ -106,7 +144,7 @@ export default { | ||
| 106 | // 清空画布 | 144 | // 清空画布 |
| 107 | ctx.clearRect(0, 0, width, height) | 145 | ctx.clearRect(0, 0, width, height) |
| 108 | 146 | ||
| 109 | - // 调整Grid布局,彻底解决重叠 | 147 | + // Grid布局(自适应,无固定值) |
| 110 | const gridTop = grid.top ? (typeof grid.top === 'string' ? parseFloat(grid.top) / 100 * height : grid.top) : 60 | 148 | const gridTop = grid.top ? (typeof grid.top === 'string' ? parseFloat(grid.top) / 100 * height : grid.top) : 60 |
| 111 | const gridLeft = grid.left ? (typeof grid.left === 'string' ? parseFloat(grid.left) / 100 * width : grid.left) : 70 | 149 | const gridLeft = grid.left ? (typeof grid.left === 'string' ? parseFloat(grid.left) / 100 * width : grid.left) : 70 |
| 112 | const gridRight = grid.right ? (typeof grid.right === 'string' ? parseFloat(grid.right) / 100 * width : grid.right) : 20 | 150 | const gridRight = grid.right ? (typeof grid.right === 'string' ? parseFloat(grid.right) / 100 * width : grid.right) : 20 |
| @@ -115,61 +153,58 @@ export default { | @@ -115,61 +153,58 @@ export default { | ||
| 115 | const drawWidth = width - gridLeft - gridRight | 153 | const drawWidth = width - gridLeft - gridRight |
| 116 | const drawHeight = height - gridTop - gridBottom | 154 | const drawHeight = height - gridTop - gridBottom |
| 117 | 155 | ||
| 118 | - // Y轴最大值自适应(无小数点) | 156 | + // 收集所有Y轴数据(无数值限制) |
| 119 | let allValues = [] | 157 | let allValues = [] |
| 120 | this.data.forEach(series => { | 158 | this.data.forEach(series => { |
| 121 | allValues = allValues.concat(series.data) | 159 | allValues = allValues.concat(series.data) |
| 122 | }) | 160 | }) |
| 123 | if (allValues.length === 0) return; | 161 | if (allValues.length === 0) return; |
| 124 | 162 | ||
| 125 | - // 动态计算Y轴极值(保留10%顶部余量,转为整数) | 163 | + // 基础极值(完全按数据来,无任何限制) |
| 126 | const rawMaxVal = Math.max(...allValues) | 164 | const rawMaxVal = Math.max(...allValues) |
| 127 | const rawMinVal = Math.min(...allValues) | 165 | const rawMinVal = Math.min(...allValues) |
| 128 | - const maxVal = yAxis.max || Math.ceil(rawMaxVal * 1.1) // 向上取整,保证能容纳最大值 | ||
| 129 | - const minVal = yAxis.min || (rawMinVal < 0 ? Math.floor(rawMinVal * 1.1) : 0) // 向下取整(负数),默认0 | ||
| 130 | - const valRange = maxVal - minVal || 1 | ||
| 131 | - | ||
| 132 | - // 计算X轴每个点的宽度 | 166 | + // 用户可通过yAxis.min/max自定义极值,否则用数据极值(无限制) |
| 167 | + const maxVal = yAxis.max !== undefined ? yAxis.max : rawMaxVal | ||
| 168 | + const minVal = yAxis.min !== undefined ? yAxis.min : rawMinVal | ||
| 169 | + // 刻度数量自定义(默认5个,可传任意数,无上限) | ||
| 170 | + const tickCount = yAxis.tickCount || 5 | ||
| 171 | + | ||
| 172 | + // 核心:计算无限制、不重复的整数刻度 | ||
| 173 | + const yAxisConfig = this.calculateYAxisTicks(minVal, maxVal, tickCount) | ||
| 174 | + const { ticks: yTicks, min: alignedMinVal, max: alignedMaxVal } = yAxisConfig | ||
| 175 | + const valRange = alignedMaxVal - alignedMinVal || 1 | ||
| 176 | + | ||
| 177 | + // X轴步长(自适应) | ||
| 133 | const xStep = drawWidth / (this.categories.length - 1 || 1) | 178 | const xStep = drawWidth / (this.categories.length - 1 || 1) |
| 134 | 179 | ||
| 135 | - // X轴标签防拥挤 | 180 | + // X轴标签防拥挤(自适应,无限制) |
| 136 | const minLabelWidth = 30 | 181 | const minLabelWidth = 30 |
| 137 | const maxShowLabels = Math.floor(drawWidth / minLabelWidth) | 182 | const maxShowLabels = Math.floor(drawWidth / minLabelWidth) |
| 138 | const labelInterval = maxShowLabels < this.categories.length | 183 | const labelInterval = maxShowLabels < this.categories.length |
| 139 | ? Math.ceil(this.categories.length / maxShowLabels) | 184 | ? Math.ceil(this.categories.length / maxShowLabels) |
| 140 | : 1 | 185 | : 1 |
| 141 | 186 | ||
| 142 | - // 绘制网格线 + Y轴整数刻度 | 187 | + // 绘制网格线 + Y轴整数刻度(无数值限制) |
| 143 | ctx.setStrokeStyle(yAxis.splitLine?.lineStyle?.color || '#f5f5f7') | 188 | ctx.setStrokeStyle(yAxis.splitLine?.lineStyle?.color || '#f5f5f7') |
| 144 | ctx.setLineWidth(1) | 189 | ctx.setLineWidth(1) |
| 145 | - const yTickCount = 5 | ||
| 146 | - const yTickStep = valRange / yTickCount | ||
| 147 | 190 | ||
| 148 | - for (let i = 0; i <= yTickCount; i++) { | ||
| 149 | - const y = gridTop + drawHeight - (i * drawHeight / yTickCount) | 191 | + yTicks.forEach((tickVal, i) => { |
| 192 | + const y = gridTop + drawHeight - (i * drawHeight / (yTicks.length - 1)) | ||
| 150 | // 绘制网格线 | 193 | // 绘制网格线 |
| 151 | ctx.beginPath() | 194 | ctx.beginPath() |
| 152 | ctx.moveTo(gridLeft, y) | 195 | ctx.moveTo(gridLeft, y) |
| 153 | ctx.lineTo(width - gridRight, y) | 196 | ctx.lineTo(width - gridRight, y) |
| 154 | ctx.stroke() | 197 | ctx.stroke() |
| 155 | 198 | ||
| 156 | - // Y轴显示整数(无小数点) | 199 | + // Y轴显示纯整数(无小数点、无任何数值限制) |
| 157 | ctx.setFillStyle(yAxis.axisLabel?.color || '#666') | 200 | ctx.setFillStyle(yAxis.axisLabel?.color || '#666') |
| 158 | ctx.setFontSize(yAxis.axisLabel?.fontSize || 12) | 201 | ctx.setFontSize(yAxis.axisLabel?.fontSize || 12) |
| 159 | - const val = minVal + (i * yTickStep) | ||
| 160 | - // 转为整数(四舍五入),彻底去掉小数点 | ||
| 161 | - const intVal = Math.round(val) | ||
| 162 | - let valText = intVal.toString() | ||
| 163 | - // 长数字处理(仍为整数格式) | ||
| 164 | - if (valText.length > 6) { | ||
| 165 | - valText = intVal.toLocaleString() // 用千分位显示长整数,如 1234567 → 1,234,567 | ||
| 166 | - } | ||
| 167 | - // 文字右对齐,避免折叠 | 202 | + const valText = tickVal.toString() // 纯整数,直接转字符串 |
| 168 | const textWidth = ctx.measureText(valText).width | 203 | const textWidth = ctx.measureText(valText).width |
| 169 | ctx.fillText(valText, gridLeft - 10 - textWidth, y + 5) | 204 | ctx.fillText(valText, gridLeft - 10 - textWidth, y + 5) |
| 170 | - } | 205 | + }) |
| 171 | 206 | ||
| 172 | - // 绘制X轴标签 | 207 | + // 绘制X轴标签(自适应) |
| 173 | ctx.setFillStyle(xAxis.axisLabel?.color || '#666') | 208 | ctx.setFillStyle(xAxis.axisLabel?.color || '#666') |
| 174 | ctx.setFontSize(xAxis.axisLabel?.fontSize || 12) | 209 | ctx.setFontSize(xAxis.axisLabel?.fontSize || 12) |
| 175 | this.categories.forEach((text, index) => { | 210 | this.categories.forEach((text, index) => { |
| @@ -180,7 +215,6 @@ export default { | @@ -180,7 +215,6 @@ export default { | ||
| 180 | labelText = text.slice(0, 6) + '...' | 215 | labelText = text.slice(0, 6) + '...' |
| 181 | } | 216 | } |
| 182 | const textLines = labelText.split('\n') | 217 | const textLines = labelText.split('\n') |
| 183 | - | ||
| 184 | textLines.forEach((line, lineIdx) => { | 218 | textLines.forEach((line, lineIdx) => { |
| 185 | const textWidth = ctx.measureText(line).width | 219 | const textWidth = ctx.measureText(line).width |
| 186 | ctx.fillText(line, x - textWidth / 2, height - gridBottom + 20 + (lineIdx * 12)) | 220 | ctx.fillText(line, x - textWidth / 2, height - gridBottom + 20 + (lineIdx * 12)) |
| @@ -188,29 +222,45 @@ export default { | @@ -188,29 +222,45 @@ export default { | ||
| 188 | } | 222 | } |
| 189 | }) | 223 | }) |
| 190 | 224 | ||
| 191 | - // 优化图例间距 | 225 | + // 绘制图例(核心修复:颜色块和文字垂直居中对齐) |
| 192 | if (legend.show) { | 226 | if (legend.show) { |
| 193 | - ctx.setFontSize(legend.textStyle?.fontSize || 12) | 227 | + // 获取图例文字大小(统一基准) |
| 228 | + const legendFontSize = legend.textStyle?.fontSize || 12 | ||
| 229 | + ctx.setFontSize(legendFontSize) | ||
| 230 | + // 定义颜色块尺寸(和文字高度匹配) | ||
| 231 | + const legendBlockSize = legendFontSize * 0.8 // 颜色块大小 = 文字大小的80%,视觉更协调 | ||
| 232 | + const legendBlockMargin = 5 // 颜色块和文字的间距 | ||
| 194 | let legendX = gridLeft + 10 | 233 | let legendX = gridLeft + 10 |
| 234 | + // 计算垂直居中基准线 | ||
| 235 | + const legendYBase = gridTop - 30 | ||
| 236 | + | ||
| 195 | this.data.forEach((series, idx) => { | 237 | this.data.forEach((series, idx) => { |
| 196 | let seriesName = series.name || `系列${idx + 1}` | 238 | let seriesName = series.name || `系列${idx + 1}` |
| 197 | if (seriesName.length > 8) { | 239 | if (seriesName.length > 8) { |
| 198 | seriesName = seriesName.slice(0, 8) + '...' | 240 | seriesName = seriesName.slice(0, 8) + '...' |
| 199 | } | 241 | } |
| 200 | const textWidth = ctx.measureText(seriesName).width | 242 | const textWidth = ctx.measureText(seriesName).width |
| 201 | - const legendItemWidth = textWidth + 25 | ||
| 202 | 243 | ||
| 203 | - const legendY = gridTop - 30 | 244 | + // 核心:计算颜色块和文字的垂直居中位置 |
| 245 | + // 颜色块Y坐标 = 基准线 - 颜色块高度/2(居中) | ||
| 246 | + const legendBlockY = legendYBase - legendBlockSize / 2 | ||
| 247 | + // 文字Y坐标 = 基准线 + 文字高度/4(canvas文字居中的黄金比例) | ||
| 248 | + const legendTextY = legendYBase + legendFontSize / 4 | ||
| 249 | + | ||
| 250 | + // 绘制颜色块(居中对齐) | ||
| 204 | ctx.setFillStyle(series.color || color[idx % color.length]) | 251 | ctx.setFillStyle(series.color || color[idx % color.length]) |
| 205 | - ctx.fillRect(legendX, legendY, 10, 10) | 252 | + ctx.fillRect(legendX, legendBlockY, legendBlockSize, legendBlockSize) |
| 253 | + | ||
| 254 | + // 绘制文字(和颜色块垂直居中) | ||
| 206 | ctx.setFillStyle('#666') | 255 | ctx.setFillStyle('#666') |
| 207 | - ctx.fillText(seriesName, legendX + 15, legendY + 8) | 256 | + ctx.fillText(seriesName, legendX + legendBlockSize + legendBlockMargin, legendTextY) |
| 208 | 257 | ||
| 209 | - legendX += Math.max(80, legendItemWidth + 10) | 258 | + // 更新下一个图例的X坐标(包含间距) |
| 259 | + legendX += legendBlockSize + legendBlockMargin + textWidth + 10 | ||
| 210 | }) | 260 | }) |
| 211 | } | 261 | } |
| 212 | 262 | ||
| 213 | - // ========== 核心修改:移除平滑曲线,改为纯直线连接 ========== | 263 | + // 绘制折线(纯直线,无数值限制) |
| 214 | this.data.forEach((series, seriesIdx) => { | 264 | this.data.forEach((series, seriesIdx) => { |
| 215 | const seriesColor = series.color || color[seriesIdx % color.length] | 265 | const seriesColor = series.color || color[seriesIdx % color.length] |
| 216 | 266 | ||
| @@ -220,12 +270,12 @@ export default { | @@ -220,12 +270,12 @@ export default { | ||
| 220 | 270 | ||
| 221 | series.data.forEach((value, index) => { | 271 | series.data.forEach((value, index) => { |
| 222 | const x = gridLeft + index * xStep | 272 | const x = gridLeft + index * xStep |
| 223 | - const y = gridTop + drawHeight - ((value - minVal) / valRange) * drawHeight | 273 | + // 自适应计算Y坐标(无数值限制) |
| 274 | + const y = gridTop + drawHeight - ((value - alignedMinVal) / valRange) * drawHeight | ||
| 224 | 275 | ||
| 225 | if (index === 0) { | 276 | if (index === 0) { |
| 226 | ctx.moveTo(x, y) | 277 | ctx.moveTo(x, y) |
| 227 | } else { | 278 | } else { |
| 228 | - // 直接直线连接,移除所有贝塞尔曲线平滑逻辑 | ||
| 229 | ctx.lineTo(x, y) | 279 | ctx.lineTo(x, y) |
| 230 | } | 280 | } |
| 231 | }) | 281 | }) |
| @@ -235,7 +285,7 @@ export default { | @@ -235,7 +285,7 @@ export default { | ||
| 235 | 285 | ||
| 236 | ctx.draw() | 286 | ctx.draw() |
| 237 | }, | 287 | }, |
| 238 | - // K线图绘制(同步修改Y轴为整数,K线本身无平滑逻辑,保持原样) | 288 | + // K线图绘制(同步无限制逻辑 + 图例对齐修复) |
| 239 | drawKline () { | 289 | drawKline () { |
| 240 | const ctx = uni.createCanvasContext('uni-charts', this) | 290 | const ctx = uni.createCanvasContext('uni-charts', this) |
| 241 | this.ctx = ctx | 291 | this.ctx = ctx |
| @@ -244,7 +294,7 @@ export default { | @@ -244,7 +294,7 @@ export default { | ||
| 244 | 294 | ||
| 245 | ctx.clearRect(0, 0, width, height) | 295 | ctx.clearRect(0, 0, width, height) |
| 246 | 296 | ||
| 247 | - // 调整Grid布局 | 297 | + // Grid布局(自适应) |
| 248 | const gridTop = grid.top ? (typeof grid.top === 'string' ? parseFloat(grid.top) / 100 * height : grid.top) : 50 | 298 | const gridTop = grid.top ? (typeof grid.top === 'string' ? parseFloat(grid.top) / 100 * height : grid.top) : 50 |
| 249 | const gridLeft = grid.left ? (typeof grid.left === 'string' ? parseFloat(grid.left) / 100 * width : grid.left) : 70 | 299 | const gridLeft = grid.left ? (typeof grid.left === 'string' ? parseFloat(grid.left) / 100 * width : grid.left) : 70 |
| 250 | const gridRight = grid.right ? (typeof grid.right === 'string' ? parseFloat(grid.right) / 100 * width : grid.right) : 20 | 300 | const gridRight = grid.right ? (typeof grid.right === 'string' ? parseFloat(grid.right) / 100 * width : grid.right) : 20 |
| @@ -253,44 +303,47 @@ export default { | @@ -253,44 +303,47 @@ export default { | ||
| 253 | const drawWidth = width - gridLeft - gridRight | 303 | const drawWidth = width - gridLeft - gridRight |
| 254 | const drawHeight = height - gridTop - gridBottom | 304 | const drawHeight = height - gridTop - gridBottom |
| 255 | 305 | ||
| 256 | - // Y轴自适应极值(整数) | 306 | + // 收集所有Y轴数据(无限制) |
| 257 | let allValues = [] | 307 | let allValues = [] |
| 258 | this.data.forEach(item => { | 308 | this.data.forEach(item => { |
| 259 | allValues = allValues.concat([item.open, item.high, item.low, item.close]) | 309 | allValues = allValues.concat([item.open, item.high, item.low, item.close]) |
| 260 | }) | 310 | }) |
| 261 | if (allValues.length === 0) return; | 311 | if (allValues.length === 0) return; |
| 262 | 312 | ||
| 313 | + // 基础极值(无限制) | ||
| 263 | const rawMaxVal = Math.max(...allValues) | 314 | const rawMaxVal = Math.max(...allValues) |
| 264 | const rawMinVal = Math.min(...allValues) | 315 | const rawMinVal = Math.min(...allValues) |
| 265 | - const maxVal = yAxis.max || Math.ceil(rawMaxVal * 1.1) | ||
| 266 | - const minVal = yAxis.min || (rawMinVal < 0 ? Math.floor(rawMinVal * 1.1) : 0) | ||
| 267 | - const valRange = maxVal - minVal || 1 | 316 | + const maxVal = yAxis.max !== undefined ? yAxis.max : rawMaxVal |
| 317 | + const minVal = yAxis.min !== undefined ? yAxis.min : rawMinVal | ||
| 318 | + const tickCount = yAxis.tickCount || 5 | ||
| 319 | + | ||
| 320 | + // 核心:无限制整数刻度计算 | ||
| 321 | + const yAxisConfig = this.calculateYAxisTicks(minVal, maxVal, tickCount) | ||
| 322 | + const { ticks: yTicks, min: alignedMinVal, max: alignedMaxVal } = yAxisConfig | ||
| 323 | + const valRange = alignedMaxVal - alignedMinVal || 1 | ||
| 268 | 324 | ||
| 269 | const xStep = drawWidth / (this.data.length || 1) | 325 | const xStep = drawWidth / (this.data.length || 1) |
| 270 | 326 | ||
| 271 | - // 绘制网格线和Y轴整数刻度 | 327 | + // 绘制网格线和Y轴刻度(无限制) |
| 272 | ctx.setStrokeStyle('#f5f5f7') | 328 | ctx.setStrokeStyle('#f5f5f7') |
| 273 | ctx.setLineWidth(1) | 329 | ctx.setLineWidth(1) |
| 274 | - const yTickCount = 5 | ||
| 275 | - for (let i = 0; i <= yTickCount; i++) { | ||
| 276 | - const y = gridTop + drawHeight - (i * drawHeight / yTickCount) | 330 | + |
| 331 | + yTicks.forEach((tickVal, i) => { | ||
| 332 | + const y = gridTop + drawHeight - (i * drawHeight / (yTicks.length - 1)) | ||
| 277 | ctx.beginPath() | 333 | ctx.beginPath() |
| 278 | ctx.moveTo(gridLeft, y) | 334 | ctx.moveTo(gridLeft, y) |
| 279 | ctx.lineTo(width - gridRight, y) | 335 | ctx.lineTo(width - gridRight, y) |
| 280 | ctx.stroke() | 336 | ctx.stroke() |
| 281 | 337 | ||
| 282 | - // Y轴显示整数 | 338 | + // 纯整数显示(无限制) |
| 283 | ctx.setFillStyle(yAxis.axisLabel?.color || '#666') | 339 | ctx.setFillStyle(yAxis.axisLabel?.color || '#666') |
| 284 | ctx.setFontSize(yAxis.axisLabel?.fontSize || 12) | 340 | ctx.setFontSize(yAxis.axisLabel?.fontSize || 12) |
| 285 | - const val = minVal + (i * valRange / yTickCount) | ||
| 286 | - const intVal = Math.round(val) | ||
| 287 | - let valText = intVal.toString() | ||
| 288 | - if (valText.length > 6) valText = intVal.toLocaleString() | 341 | + const valText = tickVal.toString() |
| 289 | const textWidth = ctx.measureText(valText).width | 342 | const textWidth = ctx.measureText(valText).width |
| 290 | ctx.fillText(valText, gridLeft - 10 - textWidth, y + 5) | 343 | ctx.fillText(valText, gridLeft - 10 - textWidth, y + 5) |
| 291 | - } | 344 | + }) |
| 292 | 345 | ||
| 293 | - // X轴标签(防拥挤) | 346 | + // X轴标签(自适应) |
| 294 | ctx.setFillStyle(xAxis.axisLabel?.color || '#666') | 347 | ctx.setFillStyle(xAxis.axisLabel?.color || '#666') |
| 295 | ctx.setFontSize(xAxis.axisLabel?.fontSize || 12) | 348 | ctx.setFontSize(xAxis.axisLabel?.fontSize || 12) |
| 296 | const minLabelWidth = 30 | 349 | const minLabelWidth = 30 |
| @@ -308,16 +361,17 @@ export default { | @@ -308,16 +361,17 @@ export default { | ||
| 308 | } | 361 | } |
| 309 | }) | 362 | }) |
| 310 | 363 | ||
| 311 | - // 绘制K线(K线本身就是直线,无平滑逻辑) | 364 | + // 绘制K线(无数值限制) |
| 312 | this.data.forEach((item, index) => { | 365 | this.data.forEach((item, index) => { |
| 313 | const { open, high, low, close } = item | 366 | const { open, high, low, close } = item |
| 314 | const x = gridLeft + index * xStep + xStep / 2 | 367 | const x = gridLeft + index * xStep + xStep / 2 |
| 315 | - const yOpen = gridTop + drawHeight - ((open - minVal) / valRange) * drawHeight | ||
| 316 | - const yClose = gridTop + drawHeight - ((close - minVal) / valRange) * drawHeight | ||
| 317 | - const yHigh = gridTop + drawHeight - ((high - minVal) / valRange) * drawHeight | ||
| 318 | - const yLow = gridTop + drawHeight - ((low - minVal) / valRange) * drawHeight | 368 | + // 自适应坐标计算 |
| 369 | + const yOpen = gridTop + drawHeight - ((open - alignedMinVal) / valRange) * drawHeight | ||
| 370 | + const yClose = gridTop + drawHeight - ((close - alignedMinVal) / valRange) * drawHeight | ||
| 371 | + const yHigh = gridTop + drawHeight - ((high - alignedMinVal) / valRange) * drawHeight | ||
| 372 | + const yLow = gridTop + drawHeight - ((low - alignedMinVal) / valRange) * drawHeight | ||
| 319 | 373 | ||
| 320 | - // 绘制高低线(直线) | 374 | + // 绘制高低线 |
| 321 | ctx.setStrokeStyle(close >= open ? color[0] : color[1]) | 375 | ctx.setStrokeStyle(close >= open ? color[0] : color[1]) |
| 322 | ctx.setLineWidth(1) | 376 | ctx.setLineWidth(1) |
| 323 | ctx.beginPath() | 377 | ctx.beginPath() |
| @@ -347,7 +401,7 @@ export default { | @@ -347,7 +401,7 @@ export default { | ||
| 347 | this.touchInfo.y = e.changedTouches[0].y | 401 | this.touchInfo.y = e.changedTouches[0].y |
| 348 | }, | 402 | }, |
| 349 | touchEnd (e) { | 403 | touchEnd (e) { |
| 350 | - // 可扩展点击交互逻辑 | 404 | + // 可扩展交互逻辑,无限制 |
| 351 | } | 405 | } |
| 352 | } | 406 | } |
| 353 | } | 407 | } |
pages/index/index.vue
| @@ -10,8 +10,8 @@ | @@ -10,8 +10,8 @@ | ||
| 10 | </view> | 10 | </view> |
| 11 | <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover"> | 11 | <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover"> |
| 12 | <up-icon name="chat" color="#fff" size="24"/> | 12 | <up-icon name="chat" color="#fff" size="24"/> |
| 13 | - <view class="msg-badge" v-if="msgCount > 0"> | ||
| 14 | - <up-badge type="error" max="999" :value="msgCount"></up-badge> | 13 | + <view class="msg-badge" > |
| 14 | + <up-badge type="error" showZero="true" max="999" :value="msgCount"></up-badge> | ||
| 15 | </view> | 15 | </view> |
| 16 | </view> | 16 | </view> |
| 17 | </view> | 17 | </view> |
| @@ -55,11 +55,10 @@ | @@ -55,11 +55,10 @@ | ||
| 55 | active-color="#0A86F4" | 55 | active-color="#0A86F4" |
| 56 | inactive-color="#666" | 56 | inactive-color="#666" |
| 57 | font-size="14px" | 57 | font-size="14px" |
| 58 | - line-width="40" | ||
| 59 | - line-height="3" | 58 | + lineWidth="60" |
| 60 | @change="handleTabChange" | 59 | @change="handleTabChange" |
| 61 | class="task-tab-container" | 60 | class="task-tab-container" |
| 62 | - scrollable="false" | 61 | + :scrollable="false" |
| 63 | ></up-tabs> | 62 | ></up-tabs> |
| 64 | 63 | ||
| 65 | <!-- z-paging分页列表 --> | 64 | <!-- z-paging分页列表 --> |
| @@ -150,9 +149,9 @@ const klineChartData = ref([ | @@ -150,9 +149,9 @@ const klineChartData = ref([ | ||
| 150 | const klineOption = ref({ | 149 | const klineOption = ref({ |
| 151 | grid: { | 150 | grid: { |
| 152 | top: '25%', | 151 | top: '25%', |
| 153 | - left: '15%', | 152 | + left: '12%', |
| 154 | right: '8%', | 153 | right: '8%', |
| 155 | - bottom: '20%' | 154 | + bottom: '16%' |
| 156 | }, | 155 | }, |
| 157 | xAxis: { | 156 | xAxis: { |
| 158 | axisLabel: {fontSize: 12, color: '#666'} | 157 | axisLabel: {fontSize: 12, color: '#666'} |
| @@ -372,7 +371,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | @@ -372,7 +371,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | ||
| 372 | .user-info-bar { | 371 | .user-info-bar { |
| 373 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 372 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 374 | background-size: 100% 100%; | 373 | background-size: 100% 100%; |
| 375 | - padding: 100px $spacing-lg 135px; | 374 | + padding: 90px $spacing-lg 135px; |
| 376 | display: flex; | 375 | display: flex; |
| 377 | justify-content: space-between; | 376 | justify-content: space-between; |
| 378 | align-items: center; | 377 | align-items: center; |
| @@ -393,8 +392,8 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | @@ -393,8 +392,8 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | ||
| 393 | .msg-icon { | 392 | .msg-icon { |
| 394 | position: relative; | 393 | position: relative; |
| 395 | padding: 5px; | 394 | padding: 5px; |
| 395 | + top:10px; | ||
| 396 | border-radius: 50%; | 396 | border-radius: 50%; |
| 397 | - transition: background-color 0.2s; | ||
| 398 | 397 | ||
| 399 | &--hover { | 398 | &--hover { |
| 400 | background-color: rgba(255, 255, 255, 0.2); | 399 | background-color: rgba(255, 255, 255, 0.2); |
| @@ -413,7 +412,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | @@ -413,7 +412,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | ||
| 413 | border-radius: $border-radius $border-radius 0 0; | 412 | border-radius: $border-radius $border-radius 0 0; |
| 414 | background-color: $bg-color; | 413 | background-color: $bg-color; |
| 415 | position: relative; | 414 | position: relative; |
| 416 | - z-index: 2; | 415 | + z-index: 999; |
| 417 | overflow: hidden; | 416 | overflow: hidden; |
| 418 | padding-bottom: $spacing-lg; | 417 | padding-bottom: $spacing-lg; |
| 419 | } | 418 | } |
pages/workbench/index.vue
| @@ -166,7 +166,7 @@ const handleMenuClick = (item: MenuItem) => { | @@ -166,7 +166,7 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 166 | top: 0; | 166 | top: 0; |
| 167 | left: 0; | 167 | left: 0; |
| 168 | width: 100%; | 168 | width: 100%; |
| 169 | - padding: 90px 15px 135px; | 169 | + padding: 80px 15px 125px; |
| 170 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 170 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 171 | background-size: 100% 100%; | 171 | background-size: 100% 100%; |
| 172 | z-index: 1; | 172 | z-index: 1; |
| @@ -191,7 +191,7 @@ const handleMenuClick = (item: MenuItem) => { | @@ -191,7 +191,7 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 191 | .content-wrap { | 191 | .content-wrap { |
| 192 | position: relative; | 192 | position: relative; |
| 193 | z-index: 2; | 193 | z-index: 2; |
| 194 | - padding: 150px 0 0; | 194 | + padding: 135px 0 0; |
| 195 | display: flex; | 195 | display: flex; |
| 196 | flex-direction: column; | 196 | flex-direction: column; |
| 197 | gap: 10px; | 197 | gap: 10px; |