This commit is contained in:
WindowBird 2025-08-21 18:12:21 +08:00
parent 5746c31584
commit 024ecb2827
7 changed files with 1380 additions and 10 deletions

View File

@ -52,3 +52,27 @@ export function createLeaseOrder(orderData) {
loadingText: '创建订单中...',
})
}
/**
* 设备续费API
* @param {Object} data 续费参数
* @param {string} data.suitId 套餐ID
* @param {string} data.appId 应用ID
* @param {string} data.payAmount 支付金额
* @param {string} data.channelId 渠道ID
* @param {string} data.devId 设备ID
* @returns {Promise} API响应
*/
export function renewDevice(data) {
return request({
url: '/app/order/renew',
method: 'POST',
data: {
suitId: data.suitId,
appId: data.appId,
payAmount: data.payAmount,
channelId: data.channelId,
devId: data.devId,
},
})
}

216
api/order/README.md Normal file
View File

@ -0,0 +1,216 @@
# 订单API使用说明
## 概述
订单API提供了完整的订单管理功能包括设备续费、订单创建、查询、取消和支付等功能。
## API接口列表
### 1. 设备续费 (renewDevice)
**接口地址:** `POST /app/order/renew`
**功能描述:** 为指定设备进行续费操作
**请求参数:**
```javascript
{
"suitId": "3", // 套餐ID
"appId": "1", // 应用ID
"payAmount": "365", // 支付金额
"channelId": "2", // 渠道ID
"devId": "1" // 设备ID
}
```
**使用示例:**
```javascript
import { renewDevice } from '@/api/order/order.js'
// 设备续费
const renewData = {
suitId: '3',
appId: '1',
payAmount: '365',
channelId: '2',
devId: '1'
}
try {
const response = await renewDevice(renewData)
console.log('续费成功:', response)
} catch (error) {
console.error('续费失败:', error)
}
```
### 2. 创建订单 (createOrder)
**接口地址:** `POST /app/order/create`
**功能描述:** 创建新的订单
**请求参数:**
```javascript
{
"deviceType": "设备类型",
"period": "租赁周期",
"amount": "订单金额",
// 其他订单相关参数...
}
```
### 3. 获取订单列表 (getOrderList)
**接口地址:** `GET /app/order/list`
**功能描述:** 获取订单列表,支持分页和状态筛选
**请求参数:**
```javascript
{
"page": 1, // 页码
"size": 10, // 每页数量
"status": "" // 订单状态(可选)
}
```
### 4. 获取订单详情 (getOrderDetail)
**接口地址:** `GET /app/order/detail/{orderId}`
**功能描述:** 获取指定订单的详细信息
**路径参数:**
- `orderId`: 订单ID
### 5. 取消订单 (cancelOrder)
**接口地址:** `POST /app/order/cancel/{orderId}`
**功能描述:** 取消指定订单
**路径参数:**
- `orderId`: 订单ID
### 6. 支付订单 (payOrder)
**接口地址:** `POST /app/order/pay`
**功能描述:** 为指定订单进行支付
**请求参数:**
```javascript
{
"orderId": "订单ID",
"payMethod": "支付方式"
}
```
## 完整使用示例
```javascript
import {
renewDevice,
createOrder,
getOrderList,
getOrderDetail,
cancelOrder,
payOrder
} from '@/api/order/order.js'
export default {
methods: {
// 设备续费
async handleRenew() {
try {
const renewData = {
suitId: '3',
appId: '1',
payAmount: '365',
channelId: '2',
devId: '1'
}
const response = await renewDevice(renewData)
console.log('续费成功:', response)
uni.showToast({
title: '续费成功',
icon: 'success'
})
} catch (error) {
console.error('续费失败:', error)
uni.showToast({
title: error.message || '续费失败',
icon: 'error'
})
}
},
// 创建订单
async handleCreateOrder() {
try {
const orderData = {
deviceType: '设备类型',
period: '租赁周期',
amount: '订单金额'
}
const response = await createOrder(orderData)
console.log('创建订单成功:', response)
} catch (error) {
console.error('创建订单失败:', error)
}
},
// 获取订单列表
async handleGetOrderList() {
try {
const params = {
page: 1,
size: 10,
status: ''
}
const response = await getOrderList(params)
console.log('订单列表:', response)
} catch (error) {
console.error('获取订单列表失败:', error)
}
}
}
}
```
## 错误处理
所有API接口都使用统一的错误处理机制
```javascript
try {
const response = await apiFunction(params)
// 处理成功响应
} catch (error) {
// 处理错误
console.error('API调用失败:', error)
uni.showToast({
title: error.message || '操作失败',
icon: 'error'
})
}
```
## 注意事项
1. 所有API调用都需要确保网络连接正常
2. 请求参数需要按照接口文档要求进行传递
3. 建议在调用API前进行参数验证
4. 错误处理应该包含用户友好的提示信息
5. 敏感操作(如支付)建议增加二次确认
## 更新日志
- **v1.0.0** - 初始版本,包含基础的订单管理功能
- 设备续费接口
- 订单创建、查询、取消、支付功能

View File

@ -0,0 +1,425 @@
<template>
<view v-if="visible" class="renew-modal-overlay" @click="handleOverlayClick">
<view class="renew-modal" @click.stop>
<!-- 弹窗头部 -->
<view class="modal-header">
<text class="modal-title">设备续费</text>
<view class="close-btn" @click="handleClose">
<text class="close-icon">×</text>
</view>
</view>
<!-- 设备信息 -->
<view class="device-info" v-if="device">
<view class="device-item">
<image class="device-image" :src="device.image" mode="aspectFill" />
<view class="device-details">
<text class="device-name">{{ device.name }}</text>
<text class="device-status" :class="device.status">
{{ getStatusText(device.status) }}
</text>
<text class="device-time" v-if="device.endTime">
到期时间: {{ formatTime(device.endTime) }}
</text>
</view>
</view>
</view>
<!-- 套餐列表 -->
<view class="package-section">
<text class="section-title">选择续费套餐</text>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<text class="loading-text">加载中...</text>
</view>
<!-- 套餐列表 -->
<view v-else-if="packageList.length > 0" class="package-list">
<view
v-for="(item, index) in packageList"
:key="index"
class="package-item"
:class="{ active: selectedPackage && selectedPackage.id === item.id }"
@click="handleSelectPackage(item)"
>
<view class="package-info">
<text class="package-name">{{ item.name || `套餐${index + 1}` }}</text>
<text class="package-desc">{{ item.description || item.period || '暂无描述' }}</text>
</view>
<view class="package-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ item.price || item.amount || '0.00' }}</text>
</view>
<view class="select-icon" v-if="selectedPackage && selectedPackage.id === item.id">
<text class="check-icon"></text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-state">
<text class="empty-text">暂无可用套餐</text>
</view>
</view>
<!-- 底部按钮 -->
<view class="modal-footer">
<button class="cancel-btn" @click="handleClose">取消</button>
<button class="confirm-btn" :disabled="!selectedPackage" @click="handleConfirm">
确认续费
</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'RenewModal',
props: {
visible: {
type: Boolean,
default: false,
},
device: {
type: Object,
default: null,
},
packageList: {
type: Array,
default: () => [],
},
loading: {
type: Boolean,
default: false,
},
},
data() {
return {
selectedPackage: null,
}
},
watch: {
visible(newVal) {
if (!newVal) {
//
this.selectedPackage = null
}
},
},
methods: {
//
handleOverlayClick() {
this.handleClose()
},
//
handleClose() {
this.$emit('close')
},
//
handleSelectPackage(combo) {
this.selectedPackage = combo
this.$emit('select-package', combo)
},
//
handleConfirm() {
if (!this.selectedPackage) {
uni.showToast({
title: '请选择套餐',
icon: 'none',
})
return
}
this.$emit('confirm-renew', this.selectedPackage)
},
//
getStatusText(status) {
const statusMap = {
available: '可租用',
rented: '已出租',
maintenance: '维护中',
scrapped: '报废',
normal: '正常',
}
return statusMap[status] || '未知状态'
},
//
formatTime(timeStr) {
if (!timeStr) return ''
const date = new Date(timeStr)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
},
},
}
</script>
<style lang="scss" scoped>
.renew-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.renew-modal {
width: 90%;
max-width: 600rpx;
max-height: 80vh;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.close-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: #f5f5f5;
}
.close-icon {
font-size: 40rpx;
color: #999;
line-height: 1;
}
.device-info {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.device-item {
display: flex;
align-items: center;
}
.device-image {
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
margin-right: 20rpx;
}
.device-details {
flex: 1;
display: flex;
flex-direction: column;
}
.device-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.device-status {
font-size: 24rpx;
margin-bottom: 8rpx;
&.available {
color: #52c41a;
}
&.rented {
color: #1890ff;
}
&.maintenance {
color: #faad14;
}
&.scrapped {
color: #ff4d4f;
}
&.normal {
color: #52c41a;
}
}
.device-time {
font-size: 24rpx;
color: #666;
}
.package-section {
flex: 1;
padding: 30rpx;
overflow-y: auto;
}
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 60rpx 0;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
.package-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.package-item {
display: flex;
align-items: center;
padding: 24rpx;
border: 2rpx solid #f0f0f0;
border-radius: 12rpx;
position: relative;
transition: all 0.3s ease;
&.active {
border-color: #1890ff;
background-color: #f6ffed;
}
&:active {
transform: scale(0.98);
}
}
.package-info {
flex: 1;
display: flex;
flex-direction: column;
}
.package-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.package-desc {
font-size: 24rpx;
color: #666;
}
.package-price {
display: flex;
align-items: baseline;
margin-right: 20rpx;
}
.price-symbol {
font-size: 24rpx;
color: #ff4d4f;
margin-right: 4rpx;
}
.price-value {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
.select-icon {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #1890ff;
display: flex;
align-items: center;
justify-content: center;
}
.check-icon {
font-size: 24rpx;
color: #fff;
line-height: 1;
}
.empty-state {
display: flex;
align-items: center;
justify-content: center;
padding: 60rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.modal-footer {
display: flex;
gap: 20rpx;
padding: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.cancel-btn,
.confirm-btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
.cancel-btn {
background-color: #f5f5f5;
color: #666;
}
.confirm-btn {
background-color: #1890ff;
color: #fff;
&:disabled {
background-color: #d9d9d9;
color: #999;
}
}
</style>

View File

@ -2,7 +2,7 @@
export const DEV_CONFIG = {
// 临时token用于开发测试
TEMP_TOKEN:
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZjMDRiYWQ0LWVjMGQtNDJiMi05NGJkLTQxZGVhMmNmZGE3OCJ9.YyzEJIfPuy2ZrmKRuoWWWHArGxpY9U5kmAM1CFHHrOietHfjWN3rsK0WNxOWTkIDvRiWyAqkTrNwDtWP3ClyQA',
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImRhNmJhNmU0LTc5MmEtNGQ0ZC1iZWE5LTdiYjY2MTllODNjYiJ9.wx0jcmrYQ0FXVEFFBEwhRXtvkJmv5DTQhBJt7mg0udgp8jHbNueZlUIeAIN39HFKjOcA6PupdPOFb9NSaIpT1g',
// 是否使用临时token
USE_TEMP_TOKEN: true,

252
examples/order-api-usage.js Normal file
View File

@ -0,0 +1,252 @@
import {
renewDevice,
createLeaseOrder,
getOrderList,
getOrderDetail,
cancelOrder,
confirmReceive,
applyRefund
} from '@/api/order/order.js'
/**
* 设备续费示例
*/
export async function renewDeviceExample() {
try {
const renewData = {
suitId: "3", // 套餐ID
appId: "1", // 应用ID
payAmount: "365", // 支付金额
channelId: "2", // 渠道ID
devId: "1" // 设备ID
}
const response = await renewDevice(renewData)
if (response.code === 200) {
console.log('续费成功:', response.data)
uni.showToast({
title: '续费成功',
icon: 'success'
})
} else {
throw new Error(response.message || '续费失败')
}
} catch (error) {
console.error('续费失败:', error)
uni.showToast({
title: error.message || '续费失败',
icon: 'error'
})
}
}
/**
* 创建租赁订单示例
*/
export async function createLeaseOrderExample(formData) {
try {
const orderData = {
name: formData.name,
phone: formData.phone,
address: formData.address,
detailAddress: formData.detailAddress,
equipmentId: formData.equipmentId,
periodId: formData.periodId,
amount: formData.amount
}
const response = await createLeaseOrder(orderData)
if (response.code === 200) {
console.log('订单创建成功:', response.data)
uni.showToast({
title: '订单创建成功',
icon: 'success'
})
return response.data
} else {
throw new Error(response.message || '订单创建失败')
}
} catch (error) {
console.error('订单创建失败:', error)
uni.showToast({
title: error.message || '订单创建失败',
icon: 'error'
})
throw error
}
}
/**
* 获取订单列表示例
*/
export async function getOrderListExample() {
try {
const params = {
page: 1,
size: 10,
status: 'all' // 可选: pending, paid, shipped, completed, cancelled
}
const response = await getOrderList(params)
if (response.code === 200) {
console.log('订单列表:', response.data)
return response.data
} else {
throw new Error(response.message || '获取订单列表失败')
}
} catch (error) {
console.error('获取订单列表失败:', error)
uni.showToast({
title: error.message || '获取订单列表失败',
icon: 'error'
})
throw error
}
}
/**
* 获取订单详情示例
*/
export async function getOrderDetailExample(orderId) {
try {
const response = await getOrderDetail(orderId)
if (response.code === 200) {
console.log('订单详情:', response.data)
return response.data
} else {
throw new Error(response.message || '获取订单详情失败')
}
} catch (error) {
console.error('获取订单详情失败:', error)
uni.showToast({
title: error.message || '获取订单详情失败',
icon: 'error'
})
throw error
}
}
/**
* 取消订单示例
*/
export async function cancelOrderExample(orderId) {
try {
const response = await cancelOrder(orderId)
if (response.code === 200) {
console.log('订单取消成功:', response.data)
uni.showToast({
title: '订单取消成功',
icon: 'success'
})
return response.data
} else {
throw new Error(response.message || '订单取消失败')
}
} catch (error) {
console.error('订单取消失败:', error)
uni.showToast({
title: error.message || '订单取消失败',
icon: 'error'
})
throw error
}
}
/**
* 确认收货示例
*/
export async function confirmReceiveExample(orderId) {
try {
const response = await confirmReceive(orderId)
if (response.code === 200) {
console.log('确认收货成功:', response.data)
uni.showToast({
title: '确认收货成功',
icon: 'success'
})
return response.data
} else {
throw new Error(response.message || '确认收货失败')
}
} catch (error) {
console.error('确认收货失败:', error)
uni.showToast({
title: error.message || '确认收货失败',
icon: 'error'
})
throw error
}
}
/**
* 申请退款示例
*/
export async function applyRefundExample(orderId, reason, amount) {
try {
const refundData = {
orderId: orderId,
reason: reason,
amount: amount
}
const response = await applyRefund(refundData)
if (response.code === 200) {
console.log('退款申请成功:', response.data)
uni.showToast({
title: '退款申请成功',
icon: 'success'
})
return response.data
} else {
throw new Error(response.message || '退款申请失败')
}
} catch (error) {
console.error('退款申请失败:', error)
uni.showToast({
title: error.message || '退款申请失败',
icon: 'error'
})
throw error
}
}
/**
* 在租赁页面中使用API的示例
*/
export function useInLeasePage() {
// 在租赁页面的支付按钮点击事件中调用
async function handlePayment() {
try {
// 1. 创建租赁订单
const orderResult = await createLeaseOrderExample({
name: '张三',
phone: '13800138000',
address: '北京市朝阳区',
detailAddress: '某某小区1号楼101室',
equipmentId: '1',
periodId: '3',
amount: '365.00'
})
// 2. 如果订单创建成功,可以进行支付
if (orderResult) {
console.log('订单创建成功,订单号:', orderResult.orderId)
// 这里可以调用支付接口
}
} catch (error) {
console.error('支付流程失败:', error)
}
}
return {
handlePayment
}
}

View File

@ -0,0 +1,320 @@
<template>
<view class="order-api-demo">
<view class="header">
<text class="title">订单API使用示例</text>
</view>
<!-- 设备续费示例 -->
<view class="section">
<text class="section-title">设备续费</text>
<view class="form-item">
<text class="label">套餐ID:</text>
<input v-model="renewData.suitId" placeholder="请输入套餐ID" />
</view>
<view class="form-item">
<text class="label">应用ID:</text>
<input v-model="renewData.appId" placeholder="请输入应用ID" />
</view>
<view class="form-item">
<text class="label">支付金额:</text>
<input v-model="renewData.payAmount" placeholder="请输入支付金额" />
</view>
<view class="form-item">
<text class="label">渠道ID:</text>
<input v-model="renewData.channelId" placeholder="请输入渠道ID" />
</view>
<view class="form-item">
<text class="label">设备ID:</text>
<input v-model="renewData.devId" placeholder="请输入设备ID" />
</view>
<button @click="handleRenew" class="btn">设备续费</button>
</view>
<!-- 创建订单示例 -->
<view class="section">
<text class="section-title">创建订单</text>
<view class="form-item">
<text class="label">设备类型:</text>
<input v-model="orderData.deviceType" placeholder="请输入设备类型" />
</view>
<view class="form-item">
<text class="label">租赁周期:</text>
<input v-model="orderData.period" placeholder="请输入租赁周期" />
</view>
<view class="form-item">
<text class="label">金额:</text>
<input v-model="orderData.amount" placeholder="请输入金额" />
</view>
<button @click="handleCreateOrder" class="btn">创建订单</button>
</view>
<!-- 获取订单列表示例 -->
<view class="section">
<text class="section-title">获取订单列表</text>
<view class="form-item">
<text class="label">页码:</text>
<input v-model="listParams.page" placeholder="请输入页码" />
</view>
<view class="form-item">
<text class="label">每页数量:</text>
<input v-model="listParams.size" placeholder="请输入每页数量" />
</view>
<button @click="handleGetOrderList" class="btn">获取订单列表</button>
</view>
<!-- 结果显示 -->
<view class="result-section" v-if="result">
<text class="result-title">API返回结果:</text>
<text class="result-content">{{ JSON.stringify(result, null, 2) }}</text>
</view>
</view>
</template>
<script>
import {
renewDevice,
createOrder,
getOrderList,
getOrderDetail,
cancelOrder,
payOrder
} from '@/api/order/order.js'
export default {
name: 'OrderApiDemo',
data() {
return {
//
renewData: {
suitId: '3',
appId: '1',
payAmount: '365',
channelId: '2',
devId: '1'
},
//
orderData: {
deviceType: '',
period: '',
amount: ''
},
//
listParams: {
page: 1,
size: 10,
status: ''
},
// API
result: null
}
},
methods: {
//
async handleRenew() {
try {
console.log('🔍 发送续费请求:', this.renewData)
const response = await renewDevice(this.renewData)
this.result = response
console.log('✅ 续费成功:', response)
uni.showToast({
title: '续费成功',
icon: 'success'
})
} catch (error) {
console.error('❌ 续费失败:', error)
this.result = { error: error.message }
uni.showToast({
title: error.message || '续费失败',
icon: 'error'
})
}
},
//
async handleCreateOrder() {
try {
console.log('🔍 发送创建订单请求:', this.orderData)
const response = await createOrder(this.orderData)
this.result = response
console.log('✅ 创建订单成功:', response)
uni.showToast({
title: '创建订单成功',
icon: 'success'
})
} catch (error) {
console.error('❌ 创建订单失败:', error)
this.result = { error: error.message }
uni.showToast({
title: error.message || '创建订单失败',
icon: 'error'
})
}
},
//
async handleGetOrderList() {
try {
console.log('🔍 发送获取订单列表请求:', this.listParams)
const response = await getOrderList(this.listParams)
this.result = response
console.log('✅ 获取订单列表成功:', response)
uni.showToast({
title: '获取订单列表成功',
icon: 'success'
})
} catch (error) {
console.error('❌ 获取订单列表失败:', error)
this.result = { error: error.message }
uni.showToast({
title: error.message || '获取订单列表失败',
icon: 'error'
})
}
},
//
async getOrderDetail(orderId) {
try {
const response = await getOrderDetail(orderId)
console.log('✅ 获取订单详情成功:', response)
return response
} catch (error) {
console.error('❌ 获取订单详情失败:', error)
throw error
}
},
//
async cancelOrder(orderId) {
try {
const response = await cancelOrder(orderId)
console.log('✅ 取消订单成功:', response)
return response
} catch (error) {
console.error('❌ 取消订单失败:', error)
throw error
}
},
//
async payOrder(orderId, payMethod) {
try {
const response = await payOrder({
orderId,
payMethod
})
console.log('✅ 支付订单成功:', response)
return response
} catch (error) {
console.error('❌ 支付订单失败:', error)
throw error
}
}
}
}
</script>
<style lang="scss" scoped>
.order-api-demo {
padding: 30rpx;
background: #f5f5f5;
min-height: 100vh;
}
.header {
text-align: center;
margin-bottom: 40rpx;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
}
.section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
display: block;
}
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.label {
width: 160rpx;
font-size: 28rpx;
color: #666;
}
input {
flex: 1;
height: 70rpx;
padding: 0 20rpx;
border: 2rpx solid #e0e0e0;
border-radius: 10rpx;
font-size: 28rpx;
&:focus {
border-color: #007aff;
}
}
}
.btn {
width: 100%;
height: 80rpx;
background: #007aff;
color: #fff;
border: none;
border-radius: 10rpx;
font-size: 30rpx;
font-weight: bold;
margin-top: 20rpx;
&:active {
background: #0056cc;
}
}
.result-section {
background: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-top: 30rpx;
.result-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.result-content {
font-size: 24rpx;
color: #666;
line-height: 1.6;
word-break: break-all;
white-space: pre-wrap;
}
}
</style>

View File

@ -30,6 +30,17 @@
@equipment-click="onEquipmentClick"
/>
<!-- 续费弹窗 -->
<renew-modal
:device="selectedDevice"
:loading="packageLoading"
:package-list="packageList"
:visible="showRenewModal"
@close="closeRenewModal"
@select-package="onSelectPackage"
@confirm-renew="onConfirmRenew"
/>
<!-- 底部导航已由系统tabBar处理 -->
</view>
</template>
@ -39,15 +50,18 @@ import commonEnum from '../../enum/commonEnum'
import AnnouncementBar from '../../components/announcement-bar/announcement-bar.vue'
import BannerSwiper from '../../components/banner-swiper/banner-swiper.vue'
import EquipmentList from '../../components/equipment-list/equipment-list.vue'
import RenewModal from '../../components/renew-modal/renew-modal.vue'
import { getNewAnnouncement } from '../../api/article/article.js'
import { getBannerList } from '../../api/banner/banner.js'
import { getDeviceList } from '../../api/device/device.js'
import { renewDevice, getPeriodPackages } from '../../api/lease/lease.js'
export default {
components: {
AnnouncementBar,
BannerSwiper,
EquipmentList,
RenewModal,
},
data() {
return {
@ -72,6 +86,13 @@ export default {
//
equipmentList: [],
//
showRenewModal: false,
selectedDevice: null,
packageList: [],
packageLoading: false,
selectedPackage: null,
}
},
@ -170,6 +191,9 @@ export default {
isOnline: isOnline,
powerStatus: device.powerStatus,
iotExpireTime: device.iotExpireTime,
// API
typeId: device.typeId || device.deviceTypeId,
originalData: device,
}
})
@ -187,6 +211,7 @@ export default {
endTime: '2026-07-25 13:23:59',
image: commonEnum.TEMP2,
isOnline: true,
typeId: '1', // ID
},
{
id: 'default2',
@ -196,6 +221,7 @@ export default {
endTime: '2026-07-25 13:23:59',
image: commonEnum.TEMP3,
isOnline: true,
typeId: '2', // ID
},
]
}
@ -214,7 +240,7 @@ export default {
if (this.currentAnnouncement) {
//
uni.navigateTo({
url:'/pages/announcementList/announcementList'
url: '/pages/announcementList/announcementList',
})
} else {
uni.showToast({
@ -242,17 +268,124 @@ export default {
//
onEquipmentClick(equipment) {
//
uni.navigateTo({
url: `/pages/device-detail/device-detail?id=${equipment.id}&name=${encodeURIComponent(equipment.name)}`,
})
},
//
onRenew(equipment) {
//
uni.navigateTo({
url: `/pages/renew/renew?id=${equipment.id}&name=${encodeURIComponent(equipment.name)}&endTime=${encodeURIComponent(equipment.endTime)}`,
})
async onRenew(equipment) {
try {
this.selectedDevice = equipment
this.packageLoading = true
// ID
await this.fetchPackageList(equipment.id)
this.showRenewModal = true
} catch (error) {
console.error('打开续费弹窗失败:', error)
uni.showToast({
title: '打开续费弹窗失败',
icon: 'error',
})
}
},
//
async fetchPackageList(deviceId) {
try {
// IDID
const device = this.equipmentList.find(d => d.id === deviceId)
if (!device) {
throw new Error('设备不存在')
}
// 使ID
const typeId = device.typeId || device.originalData?.typeId || device.originalData?.deviceTypeId || '1'
console.log('设备类型ID:', typeId)
const response = await getPeriodPackages(typeId)
if (response.code === 200) {
this.packageList = response.data || []
console.log('套餐列表:', this.packageList)
} else {
throw new Error(response.message || '获取套餐列表失败')
}
} catch (error) {
console.error('获取套餐列表失败:', error)
uni.showToast({
title: error.message || '获取套餐列表失败',
icon: 'error',
})
} finally {
this.packageLoading = false
}
},
//
closeRenewModal() {
this.showRenewModal = false
this.selectedDevice = null
this.selectedPackage = null
this.packageList = []
},
//
onSelectPackage(combo) {
this.selectedPackage = combo
console.log('选择的套餐:', combo)
},
//
async onConfirmRenew() {
if (!this.selectedPackage) {
uni.showToast({
title: '请选择套餐',
icon: 'none',
})
return
}
try {
uni.showLoading({
title: '续费中...',
})
const renewData = {
suitId: this.selectedPackage.id,
appId: '1', // ID
payAmount: this.selectedPackage.price || this.selectedPackage.amount,
channelId: '2', // ID
devId: this.selectedDevice.id,
}
console.log('续费数据:', renewData)
const response = await renewDevice(renewData)
uni.hideLoading()
if (response.code === 200) {
uni.showToast({
title: '续费成功',
icon: 'success',
})
//
this.closeRenewModal()
//
this.fetchDeviceList()
} else {
throw new Error(response.message || '续费失败')
}
} catch (error) {
uni.hideLoading()
console.error('续费失败:', error)
uni.showToast({
title: error.message || '续费失败',
icon: 'error',
})
}
},
//