modify:新增小程序

This commit is contained in:
ECRZ
2026-01-06 18:00:43 +08:00
parent 498fa0e915
commit da4a055c1c
47 changed files with 7321 additions and 61 deletions

270
Sale/pages/trend/trend.js Normal file
View File

@@ -0,0 +1,270 @@
// pages/trend/trend.js
const api = require('../../utils/request')
const echarts = require('../../components/ec-canvas/echarts')
let chart = null
Page({
data: {
// 地区选项
regions: [
'全部', '昆明', '玉溪', '楚雄', '大理', '曲靖', '红河', '文山',
'重庆', '成都', '广州', '南宁'
],
// 材质选项
materials: [
'全部', 'HPB300', 'HRB400', 'HRB400E', 'HRB500', 'HRB500E',
'HRB600', 'CRB550', 'Q235', 'Q345', 'Q355'
],
// 时间范围选项
dayRanges: [
{ label: '最近 7 天', value: 7 },
{ label: '最近 15 天', value: 15 },
{ label: '最近 30 天', value: 30 },
{ label: '最近 60 天', value: 60 },
{ label: '最近 90 天', value: 90 }
],
// 选中的索引
selectedRegionIndex: 0,
selectedMaterialIndex: 0,
selectedDayIndex: 2,
// 加载和搜索状态
loading: false,
searched: false,
hasData: false,
// 图表实例
ec: {
onInit: null
},
// 趋势数据
trendData: null,
// 统计数据
startPrice: '-',
endPrice: '-',
priceChange: '-'
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 初始化图表
this.setData({
ec: {
onInit: this.initChart.bind(this)
}
})
},
/**
* 初始化图表
*/
initChart(canvas, width, height, res) {
console.log('initChart 被调用', {
hasTrendData: !!this.data.trendData,
dates: this.data.trendData?.dates,
prices: this.data.trendData?.prices
})
if (!this.data.trendData || !this.data.trendData.dates || this.data.trendData.dates.length === 0) {
console.log('没有趋势数据,跳过图表初始化')
return null
}
// 创建图表实例
const chartInstance = echarts.init(canvas)
const option = {
xAxis: {
type: 'category',
data: this.data.trendData.dates
},
yAxis: {
type: 'value'
},
series: [{
data: this.data.trendData.prices,
type: 'line',
smooth: true,
areaStyle: {}
}]
}
chartInstance.setOption(option)
console.log('图表初始化完成')
return chartInstance
},
/**
* 地区选择改变
*/
onRegionChange(e) {
const index = parseInt(e.detail.value)
this.setData({
selectedRegionIndex: index
})
},
/**
* 材质选择改变
*/
onMaterialChange(e) {
const index = parseInt(e.detail.value)
this.setData({
selectedMaterialIndex: index
})
},
/**
* 时间范围选择改变
*/
onDayRangeChange(e) {
const index = parseInt(e.detail.value)
this.setData({
selectedDayIndex: index
})
},
/**
* 查询趋势
*/
async onQuery() {
const {
selectedRegionIndex,
selectedMaterialIndex,
selectedDayIndex,
regions,
materials,
dayRanges
} = this.data
const region = selectedRegionIndex === 0 ? '' : regions[selectedRegionIndex]
const material = selectedMaterialIndex === 0 ? '' : materials[selectedMaterialIndex]
const days = dayRanges[selectedDayIndex].value
// 开始加载
this.setData({
loading: true,
searched: false,
hasData: false
})
try {
// 获取趋势数据
const trendResult = await api.getPriceTrend({
region,
material,
days
})
console.log('趋势数据:', trendResult)
const trendData = trendResult.data || []
if (trendData.length === 0) {
this.setData({
loading: false,
searched: true,
hasData: false
})
api.showError('暂无趋势数据')
return
}
// 处理数据
const dates = []
const prices = []
trendData.forEach(item => {
const date = new Date(item.date)
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`
dates.push(dateStr)
prices.push(item.avgPrice || item.avg_price || 0)
})
// 计算统计数据
const startPrice = prices[0] || 0
const endPrice = prices[prices.length - 1] || 0
const priceChange = endPrice - startPrice
console.log('准备更新数据和图表', { dates, prices, startPrice, endPrice })
this.setData({
trendData: {
dates,
prices
},
startPrice,
endPrice,
priceChange,
loading: false,
searched: true,
hasData: true
}, () => {
// setData 回调中重新初始化图表
console.log('setData 完成,准备重新初始化图表')
if (chart) {
chart.clear()
// 重新调用 initChart
const canvas = chart.canvas
if (canvas) {
chart = this.initChart(canvas, canvas.width, canvas.height)
}
}
})
} catch (error) {
console.error('查询趋势失败:', error)
this.setData({
loading: false,
searched: true,
hasData: false
})
}
},
/**
* 重置
*/
onReset() {
this.setData({
selectedRegionIndex: 0,
selectedMaterialIndex: 0,
selectedDayIndex: 2,
searched: false,
hasData: false,
trendData: null,
startPrice: '-',
endPrice: '-',
priceChange: '-'
})
if (chart) {
chart.clear()
}
},
/**
* TabBar 切换
*/
onTabChange(e) {
const value = e.detail.value
console.log('TabBar 切换:', value, '类型:', typeof value)
// value 可能是字符串或数字,统一处理
const tabIndex = parseInt(value)
if (tabIndex === 1) {
// 当前页,不做处理
console.log('已在当前页,不跳转')
return
} else if (tabIndex === 0) {
// 跳转到价格查询页
console.log('跳转到价格查询页')
wx.navigateTo({
url: '/pages/index/index'
})
}
}
})

View File

@@ -0,0 +1,6 @@
{
"usingComponents": {
"ec-canvas": "../../components/ec-canvas/ec-canvas"
},
"navigationBarTitleText": "价格趋势"
}

127
Sale/pages/trend/trend.wxml Normal file
View File

@@ -0,0 +1,127 @@
<!--pages/trend/trend.wxml-->
<view class="container">
<!-- 筛选条件区域 -->
<view class="filter-section">
<view class="section-title">趋势分析</view>
<!-- 地区选择 -->
<view class="form-item">
<view class="form-label">地区</view>
<picker
class="form-picker"
mode="selector"
range="{{regions}}"
value="{{selectedRegionIndex}}"
bindchange="onRegionChange">
<view class="picker-text {{selectedRegionIndex === -1 ? 'placeholder' : ''}}">
{{selectedRegionIndex === -1 ? '全部地区' : regions[selectedRegionIndex]}}
</view>
</picker>
</view>
<!-- 材质选择 -->
<view class="form-item">
<view class="form-label">材质</view>
<picker
class="form-picker"
mode="selector"
range="{{materials}}"
value="{{selectedMaterialIndex}}"
bindchange="onMaterialChange">
<view class="picker-text {{selectedMaterialIndex === -1 ? 'placeholder' : ''}}">
{{selectedMaterialIndex === -1 ? '全部材质' : materials[selectedMaterialIndex]}}
</view>
</picker>
</view>
<!-- 时间范围选择 -->
<view class="form-item">
<view class="form-label">时间范围</view>
<picker
class="form-picker"
mode="selector"
range="{{dayRanges}}"
range-key="{{'label'}}"
value="{{selectedDayIndex}}"
bindchange="onDayRangeChange">
<view class="picker-text {{selectedDayIndex === -1 ? 'placeholder' : ''}}">
{{dayRanges[selectedDayIndex].label}}
</view>
</picker>
</view>
<!-- 查询按钮 -->
<view class="btn-group">
<button
class="btn-primary"
bindtap="onQuery"
loading="{{loading}}"
disabled="{{loading}}">
查询趋势
</button>
<button
class="btn-secondary"
bindtap="onReset"
disabled="{{loading}}">
重置
</button>
</view>
</view>
<!-- 图表展示区域 -->
<view class="chart-section" wx:if="{{hasData}}">
<view class="chart-card">
<view class="chart-title">价格走势图</view>
<view class="chart-container">
<ec-canvas id="mychart-dom-line" canvas-id="mychart-line" ec="{{ ec }}"></ec-canvas>
</view>
</view>
<!-- 数据统计卡片 -->
<view class="stats-summary">
<view class="stat-item">
<view class="stat-label">起始价格</view>
<view class="stat-value">¥{{startPrice}}</view>
</view>
<view class="stat-item">
<view class="stat-label">最新价格</view>
<view class="stat-value {{priceChange >= 0 ? 'up' : 'down'}}">
¥{{endPrice}}
</view>
</view>
<view class="stat-item">
<view class="stat-label">价格变动</view>
<view class="stat-value {{priceChange >= 0 ? 'up' : 'down'}}">
{{priceChange >= 0 ? '+' : ''}}{{priceChange}}
</view>
</view>
</view>
</view>
<!-- 初始提示 -->
<view class="welcome-section" wx:if="{{!hasData && !loading}}">
<view class="welcome-card">
<view class="welcome-icon">📈</view>
<view class="welcome-title">价格趋势分析</view>
<view class="welcome-desc">选择地区和材质查看价格走势</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading-section" wx:if="{{loading}}">
<view class="loading-text">正在加载趋势数据...</view>
</view>
<!-- 空状态 -->
<view class="empty-section" wx:if="{{!hasData && searched && !loading}}">
<view class="empty-icon">📊</view>
<view class="empty-text">暂无趋势数据</view>
<view class="empty-hint">请尝试调整查询条件</view>
</view>
<!-- TDesign TabBar -->
<t-tab-bar value="1" theme="normal" bindchange="onTabChange">
<t-tab-bar-item value="0" icon="search" label="价格查询" />
<t-tab-bar-item value="1" icon="chart-line" label="价格趋势" />
</t-tab-bar>
</view>

222
Sale/pages/trend/trend.wxss Normal file
View File

@@ -0,0 +1,222 @@
/**
* 价格趋势页面样式
*/
page {
background-color: #f5f5f5;
height: 100%;
}
.container {
min-height: 100vh;
padding-bottom: 120rpx;
}
/* ========== 筛选条件区域 ========== */
.filter-section {
background: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
border-radius: 0 0 24rpx 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.section-title {
font-size: 36rpx;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 32rpx;
padding-left: 16rpx;
border-left: 6rpx solid #1890ff;
}
.form-item {
margin-bottom: 32rpx;
}
.form-label {
font-size: 28rpx;
color: #595959;
margin-bottom: 16rpx;
font-weight: 500;
}
.form-picker {
background: #f5f5f5;
border-radius: 12rpx;
padding: 24rpx 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.picker-text {
font-size: 30rpx;
color: #1a1a1a;
}
.picker-text.placeholder {
color: #bfbfbf;
}
.btn-group {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
}
.btn-primary,
.btn-secondary {
flex: 1;
height: 88rpx;
line-height: 88rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
border: none;
}
.btn-primary {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: #fff;
}
.btn-primary[disabled] {
background: #d9d9d9;
color: #bfbfbf;
}
.btn-secondary {
background: #fff;
color: #595959;
border: 2rpx solid #d9d9d9;
}
.btn-secondary[disabled] {
border-color: #f0f0f0;
color: #bfbfbf;
}
/* ========== 图表区域 ========== */
.chart-section {
padding: 0 30rpx;
}
.chart-card {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.chart-title {
font-size: 32rpx;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 24rpx;
}
.chart-container {
width: 100%;
height: 500rpx;
}
ec-canvas {
width: 100%;
height: 100%;
}
/* ========== 统计摘要 ========== */
.stats-summary {
display: flex;
justify-content: space-between;
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-label {
font-size: 24rpx;
color: #8c8c8c;
margin-bottom: 12rpx;
}
.stat-value {
font-size: 32rpx;
font-weight: bold;
color: #1a1a1a;
}
.stat-value.up {
color: #ff4d4f;
}
.stat-value.down {
color: #52c41a;
}
/* ========== 欢迎/空状态 ========== */
.welcome-section,
.empty-section {
padding: 120rpx 30rpx;
text-align: center;
}
.welcome-card {
background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
border-radius: 24rpx;
padding: 60rpx 40rpx;
box-shadow: 0 8rpx 24rpx rgba(82, 196, 26, 0.25);
}
.welcome-icon {
font-size: 120rpx;
margin-bottom: 24rpx;
}
.welcome-title {
font-size: 40rpx;
font-weight: bold;
color: #fff;
margin-bottom: 16rpx;
}
.welcome-desc {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.9);
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 24rpx;
opacity: 0.5;
}
.empty-text {
font-size: 30rpx;
color: #595959;
margin-bottom: 12rpx;
}
.empty-hint {
font-size: 26rpx;
color: #8c8c8c;
}
/* ========== 加载状态 ========== */
.loading-section {
padding: 120rpx 30rpx;
text-align: center;
}
.loading-text {
font-size: 28rpx;
color: #8c8c8c;
}