modify:新增小程序
This commit is contained in:
188
Sale/pages/index/CLAUDE.md
Normal file
188
Sale/pages/index/CLAUDE.md
Normal file
@@ -0,0 +1,188 @@
|
||||
[根目录](../../CLAUDE.md) > [pages](../) > **index**
|
||||
|
||||
---
|
||||
|
||||
# pages/index - 主页模块
|
||||
|
||||
> 最后更新:2026-01-06 15:26:54
|
||||
|
||||
---
|
||||
|
||||
## 变更记录 (Changelog)
|
||||
|
||||
### 2026-01-06
|
||||
- 初始化模块文档
|
||||
- 识别为用户信息展示模板页面,需改造为价格查询功能
|
||||
|
||||
---
|
||||
|
||||
## 模块职责
|
||||
|
||||
当前状态:**微信小程序模板主页**,展示用户头像与昵称信息。
|
||||
|
||||
**目标职责**(需实现):
|
||||
- 作为钢材价格查询的入口页面
|
||||
- 提供搜索条件选择(地区、材质、规格、日期)
|
||||
- 展示价格查询结果列表
|
||||
- 跳转到价格趋势详情页面
|
||||
|
||||
---
|
||||
|
||||
## 入口与启动
|
||||
|
||||
### 页面路径
|
||||
- **注册路径**:`pages/index/index`(在 `app.json` 中注册)
|
||||
- **物理路径**:`pages/index/index.js`
|
||||
- **访问方式**:小程序启动时的首页
|
||||
|
||||
### 生命周期
|
||||
```javascript
|
||||
Page({
|
||||
onLoad() { }, // 页面加载
|
||||
onReady() { }, // 页面初次渲染完成
|
||||
onShow() { } // 页面显示
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 对外接口
|
||||
|
||||
### 页面跳转接口
|
||||
- **跳转到日志页**:`bindViewTap()` 方法
|
||||
```javascript
|
||||
wx.navigateTo({
|
||||
url: '../logs/logs'
|
||||
})
|
||||
```
|
||||
|
||||
### 用户信息接口
|
||||
- **获取用户头像**:`onChooseAvatar(e)`
|
||||
- **输入昵称**:`onInputChange(e)`
|
||||
- **获取用户资料**:`getUserProfile(e)`(已废弃,推荐使用 `chooseAvatar`)
|
||||
|
||||
---
|
||||
|
||||
## 关键依赖与配置
|
||||
|
||||
### 依赖文件
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `index.js` | 页面逻辑 |
|
||||
| `index.wxml` | 页面结构 |
|
||||
| `index.wxss` | 页面样式 |
|
||||
| `index.json` | 页面配置(当前为空) |
|
||||
|
||||
### 外部依赖
|
||||
- 微信小程序基础库 2.10.4+
|
||||
- 无外部 npm 包依赖
|
||||
|
||||
### 配置文件
|
||||
```json
|
||||
// index.json(当前为空对象)
|
||||
{}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据模型
|
||||
|
||||
### 当前数据结构
|
||||
```javascript
|
||||
data: {
|
||||
motto: 'Hello World',
|
||||
userInfo: {
|
||||
avatarUrl: 'https://mmbiz.qpic.cn/...',
|
||||
nickName: ''
|
||||
},
|
||||
hasUserInfo: false,
|
||||
canIUseGetUserProfile: wx.canIUse('getUserProfile'),
|
||||
canIUseNicknameComp: wx.canIUse('input.type.nickname')
|
||||
}
|
||||
```
|
||||
|
||||
### 建议的数据结构(改造后)
|
||||
```javascript
|
||||
data: {
|
||||
regions: ['昆明', '玉溪', '大理', '楚雄'], // 地区列表
|
||||
materials: ['HPB300', 'HRB400', 'HRB500E'], // 材质列表
|
||||
selectedRegion: '', // 选中的地区
|
||||
selectedMaterial: '', // 选中的材质
|
||||
selectedDate: '', // 选中的日期
|
||||
priceList: [], // 查询结果
|
||||
loading: false, // 加载状态
|
||||
errorMessage: '' // 错误信息
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 测试与质量
|
||||
|
||||
### 测试覆盖
|
||||
- **手动测试**:可在微信开发者工具中测试用户头像/昵称功能
|
||||
- **单元测试**:暂无
|
||||
- **快照测试**:暂无
|
||||
|
||||
### 已知问题
|
||||
1. 当前页面为模板代码,未实现实际业务功能
|
||||
2. 缺少价格查询相关逻辑
|
||||
3. 缺少 API 调用封装
|
||||
|
||||
---
|
||||
|
||||
## 常见问题 (FAQ)
|
||||
|
||||
### Q: 如何改造为价格查询页面?
|
||||
A: 建议步骤:
|
||||
1. 删除用户信息相关代码
|
||||
2. 添加搜索表单(地区、材质、日期选择器)
|
||||
3. 实现查询按钮点击事件
|
||||
4. 调用 `/api/prices/search` 接口
|
||||
5. 展示查询结果列表
|
||||
|
||||
### Q: 如何调用后端 API?
|
||||
A: 使用 `wx.request()`,参考示例:
|
||||
```javascript
|
||||
wx.request({
|
||||
url: 'http://localhost:3000/api/prices/search',
|
||||
data: {
|
||||
region: this.data.selectedRegion,
|
||||
material: this.data.selectedMaterial,
|
||||
date: this.data.selectedDate
|
||||
},
|
||||
success: (res) => {
|
||||
this.setData({
|
||||
priceList: res.data.data
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关文件清单
|
||||
|
||||
```
|
||||
pages/index/
|
||||
├── index.js # 页面逻辑(50 行)
|
||||
├── index.wxml # 页面结构(28 行)
|
||||
├── index.wxss # 页面样式
|
||||
├── index.json # 页面配置
|
||||
└── CLAUDE.md # 本文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步建议
|
||||
|
||||
1. **重构 UI**:设计简洁的价格查询界面
|
||||
2. **封装 API**:在 `utils` 中创建 `api.js` 封装请求方法
|
||||
3. **添加图表**:集成 ECharts 或使用 Canvas 绘制价格趋势图
|
||||
4. **优化体验**:添加加载动画、空状态提示、错误处理
|
||||
|
||||
---
|
||||
|
||||
**模块状态**:待开发
|
||||
**优先级**:高
|
||||
**预估工作量**:4-6 小时
|
||||
413
Sale/pages/index/index.js
Normal file
413
Sale/pages/index/index.js
Normal file
@@ -0,0 +1,413 @@
|
||||
// pages/index/index.js
|
||||
const api = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 地区选项 (对象格式)
|
||||
regions: [
|
||||
{ label: '昆明', value: '昆明' },
|
||||
{ label: '玉溪', value: '玉溪' },
|
||||
{ label: '楚雄', value: '楚雄' },
|
||||
{ label: '大理', value: '大理' },
|
||||
{ label: '曲靖', value: '曲靖' },
|
||||
{ label: '红河', value: '红河' },
|
||||
{ label: '文山', value: '文山' },
|
||||
{ label: '重庆', value: '重庆' },
|
||||
{ label: '成都', value: '成都' },
|
||||
{ label: '广州', value: '广州' },
|
||||
{ label: '南宁', value: '南宁' }
|
||||
],
|
||||
// 材质选项 (对象格式)
|
||||
materials: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: 'HPB300', value: 'HPB300' },
|
||||
{ label: 'HRB400', value: 'HRB400' },
|
||||
{ label: 'HRB400E', value: 'HRB400E' },
|
||||
{ label: 'HRB500', value: 'HRB500' },
|
||||
{ label: 'HRB500E', value: 'HRB500E' },
|
||||
{ label: 'HRB600', value: 'HRB600' },
|
||||
{ label: 'CRB550', value: 'CRB550' },
|
||||
{ label: 'Q235', value: 'Q235' },
|
||||
{ label: 'Q345', value: 'Q345' },
|
||||
{ label: 'Q355', value: 'Q355' }
|
||||
],
|
||||
// 品名选项 (对象格式)
|
||||
partsnames: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '高线', value: '高线' },
|
||||
{ label: '螺纹钢', value: '螺纹钢' },
|
||||
{ label: '盘螺', value: '盘螺' },
|
||||
{ label: '工字钢', value: '工字钢' },
|
||||
{ label: '槽钢', value: '槽钢' },
|
||||
{ label: '角钢', value: '角钢' },
|
||||
{ label: 'H型钢', value: 'H型钢' },
|
||||
{ label: '钢板', value: '钢板' },
|
||||
{ label: '卷板', value: '卷板' },
|
||||
{ label: '中厚板', value: '中厚板' }
|
||||
],
|
||||
// 选中的值
|
||||
selectedRegion: '',
|
||||
selectedMaterial: '',
|
||||
selectedPartsname: '',
|
||||
// 显示的文本
|
||||
regionText: '请选择地区',
|
||||
materialText: '请选择材质 (可选)',
|
||||
partsnameText: '全部',
|
||||
// 选中的日期
|
||||
selectedDate: '',
|
||||
// 今天日期
|
||||
today: '',
|
||||
// 加载状态
|
||||
loading: false,
|
||||
// 是否已搜索
|
||||
searched: false,
|
||||
// 查询结果
|
||||
priceList: [],
|
||||
total: 0,
|
||||
// 统计信息
|
||||
stats: null,
|
||||
// Picker 显示状态
|
||||
regionPickerVisible: false,
|
||||
materialPickerVisible: false,
|
||||
partsnamePickerVisible: false,
|
||||
datePickerVisible: false,
|
||||
// Picker value (数组形式)
|
||||
regionPickerValue: [],
|
||||
materialPickerValue: [],
|
||||
partsnamePickerValue: []
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 设置今天日期
|
||||
const today = this.formatDate(new Date())
|
||||
this.setData({ today })
|
||||
|
||||
// 测试 API 连接
|
||||
this.testApiConnection()
|
||||
},
|
||||
|
||||
/**
|
||||
* 测试 API 连接
|
||||
*/
|
||||
async testApiConnection() {
|
||||
try {
|
||||
const res = await api.checkHealth()
|
||||
console.log('API 连接成功:', res)
|
||||
} catch (error) {
|
||||
console.error('API 连接失败:', error)
|
||||
api.showError('API 服务连接失败,请确保后端服务已启动')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示地区选择器
|
||||
*/
|
||||
showRegionPicker() {
|
||||
this.setData({ regionPickerVisible: true })
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示材质选择器
|
||||
*/
|
||||
showMaterialPicker() {
|
||||
this.setData({ materialPickerVisible: true })
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示品名选择器
|
||||
*/
|
||||
showPartsnamePicker() {
|
||||
this.setData({ partsnamePickerVisible: true })
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示日期选择器
|
||||
*/
|
||||
showDatePicker() {
|
||||
this.setData({ datePickerVisible: true })
|
||||
},
|
||||
|
||||
/**
|
||||
* Picker 选择改变
|
||||
*/
|
||||
onPickerChange(e) {
|
||||
const { key } = e.currentTarget.dataset
|
||||
const { value } = e.detail
|
||||
|
||||
console.log('Picker change:', { key, value })
|
||||
|
||||
// 根据 key 设置对应的文本和值
|
||||
if (key === 'region') {
|
||||
const region = this.data.regions.find(item => item.value === value[0])
|
||||
this.setData({
|
||||
regionPickerVisible: false,
|
||||
regionPickerValue: value,
|
||||
selectedRegion: value[0] || '',
|
||||
regionText: region ? region.label : '请选择地区'
|
||||
})
|
||||
} else if (key === 'material') {
|
||||
const material = this.data.materials.find(item => item.value === value[0])
|
||||
this.setData({
|
||||
materialPickerVisible: false,
|
||||
materialPickerValue: value,
|
||||
selectedMaterial: value[0] || '',
|
||||
materialText: material ? material.label : '请选择材质 (可选)'
|
||||
})
|
||||
} else if (key === 'partsname') {
|
||||
const partsname = this.data.partsnames.find(item => item.value === value[0])
|
||||
this.setData({
|
||||
partsnamePickerVisible: false,
|
||||
partsnamePickerValue: value,
|
||||
selectedPartsname: value[0] || '',
|
||||
partsnameText: partsname ? partsname.label : '全部'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Picker 取消选择
|
||||
*/
|
||||
onPickerCancel(e) {
|
||||
const { key } = e.currentTarget.dataset
|
||||
console.log('Picker cancel:', key)
|
||||
|
||||
if (key === 'region') {
|
||||
this.setData({ regionPickerVisible: false })
|
||||
} else if (key === 'material') {
|
||||
this.setData({ materialPickerVisible: false })
|
||||
} else if (key === 'partsname') {
|
||||
this.setData({ partsnamePickerVisible: false })
|
||||
} else if (key === 'date') {
|
||||
this.setData({ datePickerVisible: false })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 日期选择确认
|
||||
*/
|
||||
onDateConfirm(e) {
|
||||
const { value } = e.detail
|
||||
this.setData({
|
||||
selectedDate: value,
|
||||
datePickerVisible: false
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 日期选择取消
|
||||
*/
|
||||
onDatePickerCancel() {
|
||||
this.setData({
|
||||
datePickerVisible: false
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询价格
|
||||
*/
|
||||
async onSearch() {
|
||||
const {
|
||||
selectedRegion,
|
||||
selectedMaterial,
|
||||
selectedPartsname,
|
||||
selectedDate
|
||||
} = this.data
|
||||
|
||||
// 验证必填项
|
||||
if (!selectedRegion) {
|
||||
api.showError('请选择地区')
|
||||
return
|
||||
}
|
||||
|
||||
// 开始加载
|
||||
this.setData({
|
||||
loading: true,
|
||||
searched: false
|
||||
})
|
||||
|
||||
try {
|
||||
// 构建搜索参数
|
||||
const searchParams = {
|
||||
region: selectedRegion,
|
||||
pageSize: 100
|
||||
}
|
||||
|
||||
// 添加可选参数
|
||||
if (selectedMaterial) searchParams.material = selectedMaterial
|
||||
if (selectedPartsname) searchParams.partsname = selectedPartsname
|
||||
if (selectedDate) searchParams.startDate = selectedDate
|
||||
|
||||
// 如果选择了日期,设置结束日期
|
||||
if (selectedDate) {
|
||||
searchParams.endDate = selectedDate
|
||||
}
|
||||
|
||||
console.log('查询参数:', searchParams)
|
||||
|
||||
// 调用搜索接口
|
||||
const searchResult = await api.searchPrices(searchParams)
|
||||
|
||||
console.log('查询结果:', searchResult)
|
||||
|
||||
// 获取统计数据
|
||||
const statsParams = {
|
||||
region: selectedRegion,
|
||||
material: selectedMaterial
|
||||
}
|
||||
if (selectedDate) {
|
||||
statsParams.startDate = selectedDate
|
||||
statsParams.endDate = selectedDate
|
||||
} else {
|
||||
statsParams.days = 30
|
||||
}
|
||||
|
||||
const statsResult = await api.getPriceStats(statsParams)
|
||||
console.log('==================== 统计结果 ====================')
|
||||
console.log('完整响应:', statsResult)
|
||||
console.log('success:', statsResult.success)
|
||||
console.log('data:', statsResult.data)
|
||||
console.log('data 类型:', typeof statsResult.data)
|
||||
console.log('data 字段:', Object.keys(statsResult.data || {}))
|
||||
console.log('JSON 数据:', JSON.stringify(statsResult.data, null, 2))
|
||||
console.log('====================================================')
|
||||
|
||||
// 更新数据
|
||||
const priceList = searchResult.data || []
|
||||
const total = searchResult.total || searchResult.pagination?.total || priceList.length || 0
|
||||
|
||||
// 格式化日期字段
|
||||
const formattedList = priceList.map(item => {
|
||||
let dateStr = ''
|
||||
if (item.price_date) {
|
||||
const date = new Date(item.price_date)
|
||||
dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
price_date_str: dateStr
|
||||
}
|
||||
})
|
||||
|
||||
this.setData({
|
||||
priceList: formattedList,
|
||||
total,
|
||||
stats: statsResult.data || null,
|
||||
searched: true,
|
||||
loading: false
|
||||
})
|
||||
|
||||
// 显示结果提示
|
||||
if (searchResult.data && searchResult.data.length > 0) {
|
||||
api.showSuccess(`查询成功,共找到 ${searchResult.data.length} 条数据`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error)
|
||||
this.setData({
|
||||
loading: false,
|
||||
searched: true,
|
||||
priceList: [],
|
||||
total: 0,
|
||||
stats: null
|
||||
})
|
||||
// API 错误已在 request.js 中处理
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
onReset() {
|
||||
this.setData({
|
||||
selectedRegion: '',
|
||||
selectedMaterial: '',
|
||||
selectedPartsname: '',
|
||||
regionText: '请选择地区',
|
||||
materialText: '请选择材质 (可选)',
|
||||
partsnameText: '全部',
|
||||
regionPickerValue: [],
|
||||
materialPickerValue: [],
|
||||
partsnamePickerValue: [],
|
||||
selectedDate: '',
|
||||
searched: false,
|
||||
priceList: [],
|
||||
total: 0,
|
||||
stats: null
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 查看价格详情
|
||||
*/
|
||||
onPriceDetail(e) {
|
||||
const item = e.currentTarget.dataset.item
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return '-'
|
||||
const date = new Date(dateStr)
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 构建详情信息
|
||||
let detail = `地区:${item.price_region || '-'}\n`
|
||||
detail += `品名:${item.partsname_name || '-'}\n`
|
||||
detail += `材质:${item.goods_material || '-'}\n`
|
||||
if (item.goods_spec) {
|
||||
detail += `规格:${item.goods_spec}\n`
|
||||
}
|
||||
const price = item.hang_price || item.make_price || '-'
|
||||
detail += `价格:¥${price}\n`
|
||||
detail += `日期:${formatDate(item.price_date)}\n`
|
||||
if (item.price_source) {
|
||||
detail += `来源:${item.price_source}\n`
|
||||
}
|
||||
if (item.productarea_name) {
|
||||
detail += `产地:${item.productarea_name}\n`
|
||||
}
|
||||
detail += `单位:元/吨`
|
||||
|
||||
wx.showModal({
|
||||
title: '价格详情',
|
||||
content: detail,
|
||||
showCancel: false,
|
||||
confirmText: '关闭'
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化日期为 YYYY-MM-DD
|
||||
*/
|
||||
formatDate(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
},
|
||||
|
||||
/**
|
||||
* TabBar 切换
|
||||
*/
|
||||
onTabChange(e) {
|
||||
const value = e.detail.value
|
||||
console.log('TabBar 切换:', value, '类型:', typeof value)
|
||||
|
||||
// value 可能是字符串或数字,统一处理
|
||||
const tabIndex = parseInt(value)
|
||||
|
||||
if (tabIndex === 0) {
|
||||
// 当前页,不做处理
|
||||
console.log('已在当前页,不跳转')
|
||||
return
|
||||
} else if (tabIndex === 1) {
|
||||
// 跳转到价格趋势页
|
||||
console.log('跳转到价格趋势页')
|
||||
wx.navigateTo({
|
||||
url: '/pages/trend/trend'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
4
Sale/pages/index/index.json
Normal file
4
Sale/pages/index/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
}
|
||||
}
|
||||
215
Sale/pages/index/index.wxml
Normal file
215
Sale/pages/index/index.wxml
Normal file
@@ -0,0 +1,215 @@
|
||||
<!--pages/index/index.wxml-->
|
||||
<view class="container">
|
||||
<!-- 搜索表单区域 -->
|
||||
<view class="search-section">
|
||||
<view class="section-title">价格查询</view>
|
||||
|
||||
<!-- 地区选择 -->
|
||||
<view class="form-item">
|
||||
<t-cell-group>
|
||||
<t-cell title="地区" note="{{regionText}}" arrow hover bindtap="showRegionPicker" required />
|
||||
</t-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 材质选择 -->
|
||||
<view class="form-item">
|
||||
<t-cell-group>
|
||||
<t-cell title="材质" note="{{materialText}}" arrow hover bindtap="showMaterialPicker" />
|
||||
</t-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 品名选择 -->
|
||||
<view class="form-item">
|
||||
<t-cell-group>
|
||||
<t-cell title="品名" note="{{partsnameText}}" arrow hover bindtap="showPartsnamePicker" />
|
||||
</t-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 日期选择 -->
|
||||
<view class="form-item">
|
||||
<t-cell-group>
|
||||
<t-cell title="日期" note="{{selectedDate || '请选择日期 (可选)'}}" hover bindtap="showDatePicker" />
|
||||
</t-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 查询按钮 -->
|
||||
<view class="btn-group">
|
||||
<t-button
|
||||
theme="primary"
|
||||
size="large"
|
||||
bindtap="onSearch"
|
||||
loading="{{loading}}"
|
||||
disabled="{{loading}}"
|
||||
block>
|
||||
查询价格
|
||||
</t-button>
|
||||
<t-button
|
||||
theme="default"
|
||||
size="large"
|
||||
variant="outline"
|
||||
bindtap="onReset"
|
||||
disabled="{{loading}}"
|
||||
block>
|
||||
重置
|
||||
</t-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<t-loading theme="circular" loading="{{loading}}" text="加载中..." wx:if="{{loading}}"></t-loading>
|
||||
|
||||
<!-- 查询结果区域 -->
|
||||
<view class="result-section" wx:if="{{searched && !loading}}">
|
||||
<!-- 统计信息卡片 -->
|
||||
<view
|
||||
wx:if="{{stats && stats.count > 0}}"
|
||||
class="stats-card">
|
||||
<view class="stats-title">价格统计</view>
|
||||
<view class="stats-grid">
|
||||
<view class="stats-item">
|
||||
<view class="stats-label">数据量</view>
|
||||
<view class="stats-value">{{stats.count}} 条</view>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<view class="stats-label">平均价</view>
|
||||
<view class="stats-value avg">¥{{stats.avgPrice}}</view>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<view class="stats-label">最低价</view>
|
||||
<view class="stats-value min">¥{{stats.minPrice}}</view>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<view class="stats-label">最高价</view>
|
||||
<view class="stats-value max">¥{{stats.maxPrice}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<t-divider></t-divider>
|
||||
<view class="stats-trend" wx:if="{{stats.trend}}">
|
||||
<text>价格趋势:</text>
|
||||
<t-tag
|
||||
theme="{{stats.trend === 'up' ? 'danger' : stats.trend === 'down' ? 'success' : 'default'}}"
|
||||
variant="light"
|
||||
size="small">
|
||||
{{stats.trend === 'up' ? '↑ 上涨' : stats.trend === 'down' ? '↓ 下跌' : '→ 平稳'}}
|
||||
</t-tag>
|
||||
<text class="trend-rate" wx:if="{{stats.changeRate}}">({{stats.changeRate}})</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格列表 -->
|
||||
<view class="price-list">
|
||||
<view class="list-header">
|
||||
<text wx:if="{{priceList.length > 0}}">共找到 {{total}} 条结果</text>
|
||||
<text wx:else>暂无数据</text>
|
||||
</view>
|
||||
|
||||
<!-- 价格卡片 -->
|
||||
<view
|
||||
class="price-card"
|
||||
wx:for="{{priceList}}"
|
||||
wx:key="id"
|
||||
bindtap="onPriceDetail"
|
||||
data-item="{{item}}">
|
||||
<view class="price-main">
|
||||
<view class="price-info">
|
||||
<text class="price-region">{{item.price_region}}</text>
|
||||
<text class="price-separator">·</text>
|
||||
<text class="price-material">{{item.goods_material}}</text>
|
||||
<text class="price-spec" wx:if="{{item.goods_spec}}">({{item.goods_spec}})</text>
|
||||
</view>
|
||||
<view class="price-value">¥{{item.hang_price || item.make_price}}</view>
|
||||
</view>
|
||||
<view class="price-sub">
|
||||
<text class="price-date">{{item.price_date_str || item.price_date}}</text>
|
||||
<t-tag wx:if="{{item.price_source}}" theme="primary" variant="light" size="small">{{item.price_source}}</t-tag>
|
||||
<t-tag wx:if="{{item.productarea_name}}" theme="success" variant="light" size="small">{{item.productarea_name}}</t-tag>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<t-empty
|
||||
wx:if="{{priceList.length === 0}}"
|
||||
icon="search"
|
||||
description="未找到相关价格数据"
|
||||
tips="请尝试调整查询条件">
|
||||
</t-empty>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 初始提示 -->
|
||||
<view class="welcome-section" wx:if="{{!searched && !loading}}">
|
||||
<view class="welcome-card">
|
||||
<view class="welcome-icon">📊</view>
|
||||
<view class="welcome-title">钢材价格查询</view>
|
||||
<view class="welcome-desc">请选择地区和材质查询钢材价格信息</view>
|
||||
</view>
|
||||
<view class="features">
|
||||
<view class="feature-item">
|
||||
<view class="feature-icon">📊</view>
|
||||
<view class="feature-text">实时价格</view>
|
||||
</view>
|
||||
<view class="feature-item">
|
||||
<view class="feature-icon">📈</view>
|
||||
<view class="feature-text">趋势分析</view>
|
||||
</view>
|
||||
<view class="feature-item">
|
||||
<view class="feature-icon">🔍</view>
|
||||
<view class="feature-text">多维筛选</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Picker 选择器 -->
|
||||
<t-picker
|
||||
visible="{{regionPickerVisible}}"
|
||||
value="{{regionPickerValue}}"
|
||||
data-key="region"
|
||||
title="选择地区"
|
||||
cancelBtn="取消"
|
||||
confirmBtn="确认"
|
||||
usingCustomNavbar
|
||||
bindchange="onPickerChange"
|
||||
bindcancel="onPickerCancel">
|
||||
<t-picker-item options="{{regions}}"></t-picker-item>
|
||||
</t-picker>
|
||||
|
||||
<t-picker
|
||||
visible="{{materialPickerVisible}}"
|
||||
value="{{materialPickerValue}}"
|
||||
data-key="material"
|
||||
title="选择材质"
|
||||
cancelBtn="取消"
|
||||
confirmBtn="确认"
|
||||
usingCustomNavbar
|
||||
bindchange="onPickerChange"
|
||||
bindcancel="onPickerCancel">
|
||||
<t-picker-item options="{{materials}}"></t-picker-item>
|
||||
</t-picker>
|
||||
|
||||
<t-picker
|
||||
visible="{{partsnamePickerVisible}}"
|
||||
value="{{partsnamePickerValue}}"
|
||||
data-key="partsname"
|
||||
title="选择品名"
|
||||
cancelBtn="取消"
|
||||
confirmBtn="确认"
|
||||
usingCustomNavbar
|
||||
bindchange="onPickerChange"
|
||||
bindcancel="onPickerCancel">
|
||||
<t-picker-item options="{{partsnames}}"></t-picker-item>
|
||||
</t-picker>
|
||||
|
||||
<t-date-time-picker
|
||||
visible="{{datePickerVisible}}"
|
||||
value="{{selectedDate}}"
|
||||
mode="date"
|
||||
end="{{today}}"
|
||||
bind:confirm="onDateConfirm"
|
||||
bind:cancel="onDatePickerCancel" />
|
||||
|
||||
<!-- TDesign TabBar -->
|
||||
<t-tab-bar value="0" 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>
|
||||
275
Sale/pages/index/index.wxss
Normal file
275
Sale/pages/index/index.wxss
Normal file
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 钢材价格查询页面样式
|
||||
* 使用 TDesign 组件库 + 自定义样式
|
||||
*/
|
||||
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ========== 搜索表单区域 ========== */
|
||||
.search-section {
|
||||
padding: 40rpx 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 32rpx;
|
||||
padding-left: 16rpx;
|
||||
border-left: 6rpx solid #0052D9;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
/* ========== 加载状态 ========== */
|
||||
t-loading {
|
||||
margin: 40rpx 0;
|
||||
}
|
||||
|
||||
/* ========== 查询结果区域 ========== */
|
||||
.result-section {
|
||||
padding: 0 30rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
flex: 0 0 calc(100% - 6rpx);
|
||||
background: #fafafa;
|
||||
padding: 20rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
min-height: 120rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 24rpx;
|
||||
color: #8c8c8c;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #1a1a1a;
|
||||
word-break: keep-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stats-value.avg {
|
||||
color: #0052D9;
|
||||
}
|
||||
|
||||
.stats-value.min {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.stats-value.max {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stats-trend {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
font-size: 26rpx;
|
||||
color: #595959;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.trend-rate {
|
||||
color: #8c8c8c;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 价格列表 */
|
||||
.price-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
font-size: 26rpx;
|
||||
color: #8c8c8c;
|
||||
margin-bottom: 16rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.price-card {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border: 1rpx solid #f0f0f0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.price-card:active {
|
||||
background: #fafafa;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.price-main {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.price-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.price-region {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #1a1a1a;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.price-separator {
|
||||
color: #d9d9d9;
|
||||
margin: 0 8rpx;
|
||||
}
|
||||
|
||||
.price-material {
|
||||
font-size: 30rpx;
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.price-spec {
|
||||
font-size: 26rpx;
|
||||
color: #8c8c8c;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.price-sub {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.price-date {
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
/* ========== 欢迎区域 ========== */
|
||||
.welcome-section {
|
||||
padding: 60rpx 30rpx;
|
||||
}
|
||||
|
||||
.welcome-card {
|
||||
background: linear-gradient(135deg, #0052D9 0%, #003C9E 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 82, 217, 0.25);
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.features {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 56rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
font-size: 26rpx;
|
||||
color: #595959;
|
||||
}
|
||||
Reference in New Issue
Block a user