查询ios的固件,提示降级
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

623 lines
16 KiB

<template>
<view class="chart-container">
<text class="title">价格走势图</text>
<!-- 悬浮筛选按钮 -->
<view class="floating-filter-btn" @click="showFilter = true">
<text>筛选</text>
</view>
<!-- 筛选面板 -->
<view class="filter-panel" v-if="showFilter">
<view class="filter-header">
<text class="filter-title">选择关注的区服和阵营</text>
<view class="close-btn" @click="showFilter = false">关闭</view>
</view>
<view class="filter-content">
<checkbox-group @change="handleCheckboxChange">
<view class="server-group" v-for="server in allServers" :key="server.id">
<view class="server-title">{{ server.name }}</view>
<view class="faction-items">
<view class="filter-item" v-for="faction in server.factions" :key="faction.id">
<checkbox :value="faction.id" :checked="selectedFactions.includes(faction.id)">
{{ faction.name }}
</checkbox>
</view>
</view>
</view>
</checkbox-group>
</view>
<view class="filter-footer">
<view class="cancel-btn" @click="showFilter = false">取消</view>
<view class="save-btn" @click="saveFilter">保存</view>
</view>
</view>
<view class="chart-header">
<!-- 时间筛选 -->
<view class="time-filter">
<view class="filter-item" :class="{ active: timeFilter === 'day' }" @click="timeFilter = 'day'">
<text>天</text>
</view>
<view class="filter-item" :class="{ active: timeFilter === 'hour' }" @click="timeFilter = 'hour'">
<text>时</text>
</view>
<view class="filter-item" :class="{ active: timeFilter === 'week' }" @click="timeFilter = 'week'">
<text>周</text>
</view>
</view>
</view>
<view class="chart-content">
<!-- 价格走势图区域 -->
<!-- 铁血1区 -->
<view v-if="isServerSelected('tx1')">
<view class="chart-item" v-if="isFactionSelected('tx1-alliance')">
<text class="chart-title">铁血1区 - 联盟</text>
<canvas canvas-id="tx1AllianceChart" class="chart-canvas"></canvas>
</view>
<view class="chart-item" v-if="isFactionSelected('tx1-horde')">
<text class="chart-title">铁血1区 - 部落</text>
<canvas canvas-id="tx1HordeChart" class="chart-canvas"></canvas>
</view>
</view>
<!-- 铁血2区 -->
<view v-if="isServerSelected('tx2')">
<view class="chart-item" v-if="isFactionSelected('tx2-alliance')">
<text class="chart-title">铁血2区 - 联盟</text>
<canvas canvas-id="tx2AllianceChart" class="chart-canvas"></canvas>
</view>
<view class="chart-item" v-if="isFactionSelected('tx2-horde')">
<text class="chart-title">铁血2区 - 部落</text>
<canvas canvas-id="tx2HordeChart" class="chart-canvas"></canvas>
</view>
</view>
<!-- 时光1区 -->
<view v-if="isServerSelected('sg1')">
<view class="chart-item" v-if="isFactionSelected('sg1-alliance')">
<text class="chart-title">时光1区 - 联盟</text>
<canvas canvas-id="sg1AllianceChart" class="chart-canvas"></canvas>
</view>
<view class="chart-item" v-if="isFactionSelected('sg1-horde')">
<text class="chart-title">时光1区 - 部落</text>
<canvas canvas-id="sg1HordeChart" class="chart-canvas"></canvas>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
timeFilter: 'day', // day, hour, week
showFilter: false,
// 修改数据结构,支持服务器和阵营的两级筛选
allServers: [
{
id: 'tx1',
name: '铁血1区',
factions: [
{ id: 'tx1-alliance', name: '联盟', serverId: 'tx1' },
{ id: 'tx1-horde', name: '部落', serverId: 'tx1' }
]
},
{
id: 'tx2',
name: '铁血2区',
factions: [
{ id: 'tx2-alliance', name: '联盟', serverId: 'tx2' },
{ id: 'tx2-horde', name: '部落', serverId: 'tx2' }
]
},
{
id: 'sg1',
name: '时光1区',
factions: [
{ id: 'sg1-alliance', name: '联盟', serverId: 'sg1' },
{ id: 'sg1-horde', name: '部落', serverId: 'sg1' }
]
}
],
selectedFactions: [],
// 模拟价格历史数据(元/1000金)
chartData: {
tx1Alliance: [],
tx1Horde: [],
tx2Alliance: [],
tx2Horde: [],
sg1Alliance: [],
sg1Horde: []
}
};
},
onLoad() {
// 页面加载时从缓存中读取用户选择的阵营
this.loadSelectedFactions();
// 加载走势图数据
this.generateMockData();
// 初始绘制图表
// 注意:需要等待DOM渲染完成后才能获取canvas尺寸
},
onReady() {
// 确保DOM渲染完成后绘制图表
this.$nextTick(() => {
this.drawCharts();
});
},
watch: {
// 监听时间筛选变化,重新绘制图表
timeFilter: function() {
this.generateMockData();
this.$nextTick(() => {
this.drawCharts();
});
}
},
methods: {
// 加载用户选择的阵营
loadSelectedFactions() {
const selected = uni.getStorageSync('selectedFactions');
if (selected && selected.length > 0) {
this.selectedFactions = selected;
} else {
// 默认全部选中
this.selectedFactions = [];
this.allServers.forEach(server => {
server.factions.forEach(faction => {
this.selectedFactions.push(faction.id);
});
});
// 保存到缓存
uni.setStorageSync('selectedFactions', this.selectedFactions);
}
},
// 处理checkbox变化
handleCheckboxChange(e) {
this.selectedFactions = e.detail.value;
},
// 保存筛选结果
saveFilter() {
// 确保至少选择一个阵营
if (this.selectedFactions.length === 0) {
uni.showToast({
title: '请至少选择一个阵营',
icon: 'none'
});
return;
}
// 保存到缓存
uni.setStorageSync('selectedFactions', this.selectedFactions);
uni.showToast({
title: '保存成功',
icon: 'success'
});
this.showFilter = false;
// 重新绘制图表
this.drawCharts();
},
// 检查阵营是否被选中
isFactionSelected(factionId) {
return this.selectedFactions.includes(factionId);
},
// 检查服务器是否有任何阵营被选中
isServerSelected(serverId) {
return this.allServers.some(server =>
server.id === serverId &&
server.factions.some(faction =>
this.selectedFactions.includes(faction.id)
)
);
},
// 生成模拟数据
generateMockData() {
const basePrice = 10;
const fluctuation = 2;
let dataCount = 24; // 默认按小时
let timeStep = 1; // 默认按小时
if (this.timeFilter === 'day') {
dataCount = 30; // 30天
timeStep = 24; // 按天
} else if (this.timeFilter === 'hour') {
dataCount = 24; // 24小时
timeStep = 1; // 按小时
} else if (this.timeFilter === 'week') {
dataCount = 12; // 12周
timeStep = 168; // 按周
}
// 生成所有区服的模拟数据
this.chartData = {
tx1Alliance: this.generatePriceData(basePrice + 1, fluctuation, dataCount, timeStep),
tx1Horde: this.generatePriceData(basePrice + 0.5, fluctuation, dataCount, timeStep),
tx2Alliance: this.generatePriceData(basePrice + 2, fluctuation, dataCount, timeStep),
tx2Horde: this.generatePriceData(basePrice + 1.5, fluctuation, dataCount, timeStep),
sg1Alliance: this.generatePriceData(basePrice + 3, fluctuation, dataCount, timeStep),
sg1Horde: this.generatePriceData(basePrice + 2.5, fluctuation, dataCount, timeStep)
};
console.log('Generated mock data:', this.chartData);
},
// 生成单个区服的价格数据
generatePriceData(base, fluctuation, count, timeStep) {
const data = [];
const now = new Date();
for (let i = count - 1; i >= 0; i--) {
const time = new Date(now.getTime() - i * timeStep * 60 * 60 * 1000);
// 生成有波动的价格
const price = base + (Math.random() - 0.5) * fluctuation;
data.push({
time: time,
price: parseFloat(price.toFixed(2))
});
}
return data;
},
// 绘制所有图表
drawCharts() {
// 只绘制用户选择的阵营的图表
if (this.isFactionSelected('tx1-alliance')) {
this.drawChart('tx1AllianceChart', this.chartData.tx1Alliance);
}
if (this.isFactionSelected('tx1-horde')) {
this.drawChart('tx1HordeChart', this.chartData.tx1Horde);
}
if (this.isFactionSelected('tx2-alliance')) {
this.drawChart('tx2AllianceChart', this.chartData.tx2Alliance);
}
if (this.isFactionSelected('tx2-horde')) {
this.drawChart('tx2HordeChart', this.chartData.tx2Horde);
}
if (this.isFactionSelected('sg1-alliance')) {
this.drawChart('sg1AllianceChart', this.chartData.sg1Alliance);
}
if (this.isFactionSelected('sg1-horde')) {
this.drawChart('sg1HordeChart', this.chartData.sg1Horde);
}
},
// 绘制单个图表
drawChart(canvasId, data) {
if (!data || data.length === 0) return;
const ctx = uni.createCanvasContext(canvasId, this);
// 使用像素单位而不是rpx
const canvasWidth = 350; // 画布宽度(像素)
const canvasHeight = 150; // 画布高度(像素)
const padding = 25; // 边距(像素)
// 计算图表区域
const chartWidth = canvasWidth - 2 * padding;
const chartHeight = canvasHeight - 2 * padding;
// 计算价格范围
const prices = data.map(item => item.price);
const minPrice = Math.min(...prices) - 0.5;
const maxPrice = Math.max(...prices) + 0.5;
const priceRange = maxPrice - minPrice;
// 计算时间范围
const times = data.map(item => item.time);
const minTime = times[0];
const maxTime = times[times.length - 1];
const timeRange = maxTime - minTime;
// 绘制坐标轴
this.drawAxes(ctx, padding, chartWidth, chartHeight, minPrice, maxPrice, data);
// 绘制价格曲线
this.drawPriceLine(ctx, padding, chartWidth, chartHeight, data, minPrice, maxPrice, minTime, maxTime);
// 绘制价格点
this.drawPricePoints(ctx, padding, chartWidth, chartHeight, data, minPrice, maxPrice, minTime, maxTime);
// 绘制完成
ctx.draw();
},
// 绘制坐标轴
drawAxes(ctx, padding, chartWidth, chartHeight, minPrice, maxPrice, data) {
ctx.setStrokeStyle('#ccc');
ctx.setLineWidth(1);
// X轴
ctx.beginPath();
ctx.moveTo(padding, padding + chartHeight);
ctx.lineTo(padding + chartWidth, padding + chartHeight);
ctx.stroke();
// Y轴
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, padding + chartHeight);
ctx.stroke();
// 绘制Y轴刻度和标签
const yTicks = 5;
for (let i = 0; i <= yTicks; i++) {
const y = padding + (chartHeight / yTicks) * i;
const price = maxPrice - (maxPrice - minPrice) * (i / yTicks);
// 绘制刻度线
ctx.beginPath();
ctx.moveTo(padding - 3, y);
ctx.lineTo(padding, y);
ctx.stroke();
// 绘制价格标签
ctx.setFontSize(10);
ctx.setFillStyle('#666');
ctx.setTextAlign('right');
ctx.fillText(price.toFixed(1), padding - 5, y + 3);
}
// 绘制X轴刻度和标签
const xTicks = this.timeFilter === 'day' ? 6 : 5;
for (let i = 0; i <= xTicks; i++) {
const x = padding + (chartWidth / xTicks) * i;
const index = Math.round((data.length - 1) * (i / xTicks));
const time = data[index].time;
// 绘制刻度线
ctx.beginPath();
ctx.moveTo(x, padding + chartHeight);
ctx.lineTo(x, padding + chartHeight + 3);
ctx.stroke();
// 格式化时间标签
let timeLabel = '';
if (this.timeFilter === 'day') {
timeLabel = time.getDate() + '日';
} else if (this.timeFilter === 'hour') {
timeLabel = time.getHours() + '时';
} else if (this.timeFilter === 'week') {
timeLabel = '第' + Math.ceil((time.getDate() + time.getDay()) / 7) + '周';
}
// 绘制时间标签
ctx.setFontSize(10);
ctx.setFillStyle('#666');
ctx.setTextAlign('center');
ctx.fillText(timeLabel, x, padding + chartHeight + 15);
}
// 绘制单位标签
ctx.setFontSize(12);
ctx.setFillStyle('#333');
ctx.setTextAlign('center');
ctx.fillText('时间', padding + chartWidth / 2, padding + chartHeight + 30);
ctx.setTextAlign('right');
ctx.fillText('价格 (元/1000金)', padding - 20, padding + chartHeight / 2);
},
// 绘制价格曲线
drawPriceLine(ctx, padding, chartWidth, chartHeight, data, minPrice, maxPrice, minTime, maxTime) {
ctx.setStrokeStyle('#1989fa');
ctx.setLineWidth(2);
ctx.beginPath();
data.forEach((item, index) => {
// 计算坐标
const x = padding + (chartWidth * (item.time - minTime)) / (maxTime - minTime);
const y = padding + chartHeight - (chartHeight * (item.price - minPrice)) / (maxPrice - minPrice);
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
},
// 绘制价格点
drawPricePoints(ctx, padding, chartWidth, chartHeight, data, minPrice, maxPrice, minTime, maxTime) {
ctx.setFillStyle('#1989fa');
data.forEach(item => {
// 计算坐标
const x = padding + (chartWidth * (item.time - minTime)) / (maxTime - minTime);
const y = padding + chartHeight - (chartHeight * (item.price - minPrice)) / (maxPrice - minPrice);
// 绘制圆点
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.fill();
});
}
}
};
</script>
<style scoped>
.chart-container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20rpx;
}
.chart-header {
text-align: center;
padding: 20rpx 0;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 20rpx;
display: block;
}
/* 悬浮筛选按钮样式 */
.floating-filter-btn {
position: fixed;
right: 30rpx;
top: 30rpx;
width: 80rpx;
height: 80rpx;
background-color: #1989fa;
color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 24rpx;
box-shadow: 0 4rpx 10rpx rgba(25, 137, 250, 0.3);
z-index: 998;
}
/* 筛选面板样式 */
.filter-panel {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
flex-direction: column;
}
.filter-header {
background-color: #fff;
padding: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #eee;
}
.filter-title {
font-size: 32rpx;
font-weight: bold;
}
.close-btn {
background-color: #f0f0f0;
color: #333;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 20rpx;
}
.filter-content {
background-color: #fff;
flex: 1;
padding: 20rpx;
overflow-y: auto;
}
.server-group {
margin-bottom: 30rpx;
}
.server-title {
font-weight: bold;
font-size: 30rpx;
margin-bottom: 15rpx;
padding-left: 20rpx;
}
.faction-items {
padding-left: 40rpx;
}
.filter-item {
margin-bottom: 15rpx;
font-size: 28rpx;
}
.filter-footer {
background-color: #fff;
padding: 20rpx;
display: flex;
justify-content: space-around;
border-top: 1rpx solid #eee;
}
.cancel-btn {
background-color: #f0f0f0;
color: #333;
font-size: 24rpx;
padding: 10rpx 40rpx;
border-radius: 20rpx;
}
.save-btn {
background-color: #1989fa;
color: #fff;
font-size: 24rpx;
padding: 10rpx 40rpx;
border-radius: 20rpx;
}
/* 时间筛选样式 */
.time-filter {
display: flex;
justify-content: center;
margin: 20rpx 0;
gap: 20rpx;
}
.filter-item {
padding: 10rpx 30rpx;
border-radius: 20rpx;
background-color: #f5f5f5;
font-size: 28rpx;
color: #666;
}
.filter-item.active {
background-color: #1989fa;
color: #fff;
}
.chart-content {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.chart-item {
background-color: #fff;
border-radius: 10rpx;
padding: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.chart-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
/* 图表画布样式 */
.chart-canvas {
width: 700rpx;
height: 300rpx;
background-color: #fafafa;
border-radius: 8rpx;
}
</style>