2 Commits

Author SHA1 Message Date
  前端-胡立永 1e303eeb3c Merge branch 'master' of http://175.178.51.79:3000/hly/building-materials-shop-uniapp-241014 2 days ago
  前端-胡立永 95566eb3e6 feat(订单): 添加快捷订单列表功能及相关优化 2 days ago
18 changed files with 2360 additions and 129 deletions
Split View
  1. +1
    -0
      App.vue
  2. +6
    -0
      api/api.js
  3. +5
    -2
      api/http.js
  4. +10
    -1
      api/model/index.js
  5. +1055
    -96
      apijson.json
  6. +43
    -7
      components/QuickOrderEntry.vue
  7. +213
    -0
      components/selectCityPopup.vue
  8. +1
    -1
      mixins/configList.js
  9. +6
    -0
      pages.json
  10. +14
    -6
      pages/index/category.vue
  11. +18
    -3
      pages/index/center.vue
  12. +21
    -2
      pages/index/index.vue
  13. +239
    -8
      pages_order/mine/purse.vue
  14. +32
    -1
      pages_order/order/fastCreateOrder.vue
  15. +665
    -0
      pages_order/order/fastOrderList.vue
  16. +15
    -1
      pages_order/product/productList.vue
  17. BIN
      static/image/home/1.png
  18. +16
    -1
      store/store.js

+ 1
- 0
App.vue View File

@ -4,6 +4,7 @@
},
onShow: function() {
this.$store.commit('initConfig')
this.$store.commit('getCityList')
if(uni.getStorageSync('token')){
this.$store.commit('getQrCode')
}


+ 6
- 0
api/api.js View File

@ -1,5 +1,6 @@
import http from './http.js'
import utils from '../utils/utils.js'
import store from '../store/store.js'
let limit = {}
@ -48,7 +49,12 @@ export function api(key, data, callback, loadingTitle) {
//必须登录
if (req.auth) {
if (!uni.getStorageSync('token')) {
store.state.userInfo = {}
store.state.riceInfo = {}
uni.removeStorageSync('token')
utils.toLogin()
console.error('需要登录', req.url)
return Promise.reject()
}


+ 5
- 2
api/http.js View File

@ -35,8 +35,11 @@ function http(uri, data, callback, method = 'GET', showLoading, title) {
if(res.statusCode == 401 ||
res.data.message == '操作失败,token非法无效!' ||
res.data.message == '操作失败,用户不存在!'){
store.commit('logout')
console.error('登录过期');
store.state.userInfo = {}
store.state.riceInfo = {}
uni.removeStorageSync('token')
utils.toLogin()
}


+ 10
- 1
api/model/index.js View File

@ -147,7 +147,16 @@ const api = {
method: 'POST',
limit : 1000,
},
// 获取城市
getCity: {
url: '/index/getCity',
method: 'GET',
},
// 获取快捷订单列表
getAddOrderPageBean: {
url: '/index/getAddOrderPageBean',
method: 'GET',
},
}
export default api

+ 1055
- 96
apijson.json
File diff suppressed because it is too large
View File


+ 43
- 7
components/QuickOrderEntry.vue View File

@ -1,8 +1,9 @@
<template>
<view class="quick-order-container"
:style="{bottom}"
@click="handleClick">
<view class="new-message" v-if="innerHasNewMessage">
@click="handleClick"
@longpress="handleLongPress">
<view class="new-message" v-if="innerHasNewMessage" @click.stop="goToOrderList">
你有新的快捷下单信息
</view>
<view class="quick-order">
@ -10,6 +11,7 @@
{{ innerMessageCount }}
</view>
<image :src="imageUrl" mode=""></image>
<view class="long-press-hint">长按查看更多</view>
</view>
</view>
</template>
@ -59,17 +61,37 @@ export default {
this.$emit('order-info', this.orderInfo);
}
if(this.orderInfo.length > 0){
this.navigateTo('/pages_order/order/firmOrder');
return
}
//
if (this.targetUrl) {
this.navigateTo(this.targetUrl);
}
},
// -
handleLongPress() {
//
uni.vibrateShort();
//
uni.showActionSheet({
itemList: ['快捷下单', '我的快捷订单'],
success: (res) => {
if (res.tapIndex === 0) {
//
this.navigateTo(this.targetUrl);
} else if (res.tapIndex === 1) {
//
this.navigateTo('/pages_order/order/fastOrderList');
}
}
});
},
//
goToOrderList() {
this.navigateTo('/pages_order/order/fastOrderList');
},
//
getQuickOrderInfo() {
@ -198,6 +220,20 @@ export default {
top: 10rpx;
left: 25rpx;
}
.long-press-hint {
position: absolute;
bottom: -30rpx;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 12rpx;
white-space: nowrap;
opacity: 0.8;
}
}
}


+ 213
- 0
components/selectCityPopup.vue View File

@ -0,0 +1,213 @@
<template>
<view class="">
<view class="address" @click="openPopup">
<uv-icon
name="map"
size="30rpx"
:color="color"
></uv-icon>
<!-- <image src="../../static/image/home/1.png" mode=""></image> -->
<view class="eare">
<text :style="{color}">{{ selectCity.name || '请选择城市' }}</text>
</view>
<view class="down-arrow" :style="{'border-top': `15rpx solid ${color}`}"></view>
</view>
<uv-popup ref="popup" :round="30" :customStyle="{height: '60vh'}">
<view class="content">
<view class="popup-header">
<text class="popup-title">选择城市</text>
<uv-icon name="close" size="40rpx" color="#666" @click="closePopup"></uv-icon>
</view>
<scroll-view scroll-y class="city-list">
<view
v-for="(city, index) in cityList"
:key="index"
class="city-item"
:class="{ active: selectCity.id === city.id }"
@click="selectCityHandler(city)"
>
<view class="city-name">{{ city.name }}</view>
<uv-icon
v-if="selectCity.id === city.id"
name="checkmark"
size="32rpx"
color="#DC2828"
></uv-icon>
</view>
</scroll-view>
</view>
</uv-popup>
</view>
</template>
<script>
import { mapState } from 'vuex'
/**
* 城市选择弹窗组件
*
* 使用方法
* 1. 在页面中引入组件
* <selectCityPopup :color="#333" @cityChange="onCityChange"></selectCityPopup>
*
* 2. 在页面的methods中监听城市变化
* methods: {
* onCityChange(city) {
* console.log('城市切换到:', city);
* this.refreshData(); //
* },
* refreshData() {
* //
* }
* }
*
* 3. 也可以通过全局事件监听城市变化
* mounted() {
* uni.$on('cityChanged', (city) => {
* this.refreshData();
* });
* },
* beforeDestroy() {
* uni.$off('cityChanged');
* }
*/
export default {
name: "selectCityPopup",
props : {
color : {
default : '#fff'
}
},
data() {
return {
};
},
computed : {
...mapState(['cityList', 'selectCity']),
},
methods: {
//
openPopup() {
//
if (!this.cityList || this.cityList.length === 0) {
this.$store.commit('getCityList');
}
this.$refs.popup.open('bottom');
},
//
closePopup() {
this.$refs.popup.close();
},
//
selectCityHandler(city) {
//
this.$store.commit('setCity', city);
//
this.closePopup();
//
this.$emit('cityChange', city);
// 线
uni.$emit('cityChanged', city);
//
uni.showToast({
title: `已切换到${city.name}`,
icon: 'none',
duration: 1500
});
}
},
}
</script>
<style scoped lang="scss">
.address {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
cursor: pointer;
image {
height: 20rpx;
width: 20rpx;
}
.eare {
color: $uni-bg-color;
}
.down-arrow {
content: "";
display: block;
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
border-top: 15rpx solid $uni-bg-color;
}
}
.content {
height: 100%;
display: flex;
flex-direction: column;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 40rpx;
border-bottom: 1px solid #f0f0f0;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.city-list {
flex: 1;
padding: 20rpx 0;
.city-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 40rpx;
border-bottom: 1px solid #f5f5f5;
transition: background-color 0.2s;
&:active {
background-color: #f8f8f8;
}
&.active {
background-color: rgba($uni-color, 0.08);
.city-name {
color: $uni-color;
font-weight: 500;
}
}
.city-name {
font-size: 30rpx;
color: #333;
}
}
}
}
</style>

+ 1
- 1
mixins/configList.js View File

@ -13,7 +13,7 @@ export default {
}
},
computed: {
...mapState(['configList', 'userInfo', 'riceInfo']),
...mapState(['configList', 'userInfo', 'riceInfo', 'selectCity']),
currentPagePath() {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];


+ 6
- 0
pages.json View File

@ -99,6 +99,12 @@
{
"path": "order/createOrder"
},
{
"path": "order/fastOrderList",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "mine/individualTeam",
"style": {


+ 14
- 6
pages/index/category.vue View File

@ -22,6 +22,8 @@
:chain="chain"
:list="category[current].children"
keyName="title"
:key="category[current].id"
:current="currentChildren"
:barItemBadgeStyle="{right:'20px',top:'12px'}"
@change="change">
<uv-vtabs-item>
@ -66,10 +68,12 @@
<script>
import tabber from '@/components/base/tabbar.vue'
import productItem from '@/components/productItem.vue'
import selectCityPopup from '@/components/selectCityPopup.vue'
export default {
components: {
tabber,
productItem,
selectCityPopup,
},
data() {
return {
@ -82,10 +86,10 @@
}
},
computed: {
categoryList(){
selectCategory(){
if(!this.category[this.current] || !this.category[this.current].children){
return []
return {}
}
return this.category[this.current].children[this.currentChildren]
@ -101,6 +105,9 @@
clickTabs({index}){
this.current = index
this.currentChildren = 0
this.$nextTick(() => {
this.getProductList()
})
},
//
getCategory() {
@ -109,7 +116,7 @@
this.category = res.result
if(this.category.length > 0) {
this.$nextTick(() => {
this.getProductList(this.categoryList.id)
this.getProductList()
})
}
}
@ -118,9 +125,10 @@
//
getProductList(shopIconId) {
this.$api('getProductList', {
shopClassId: shopIconId,
shopClassId: this.selectCategory.id,
pageNo: 1,
pageSize: 99999
pageSize: 99999,
// cityId : this.selectCity.id,
}, res => {
if(res.code == 200) {
this.list = res.result.records
@ -131,7 +139,7 @@
this.currentChildren = index;
if(this.category[index]) {
this.$nextTick(() => {
this.getProductList(this.categoryList.id)
this.getProductList()
})
}
}


+ 18
- 3
pages/index/center.vue View File

@ -87,9 +87,9 @@
<image class="image" src="/static/image/center/8.png" mode=""></image>
<text class="grid-text">我的分享</text>
</uv-grid-item>
<uv-grid-item @click="$utils.navigateTo('/pages_order/order/fastCreateOrder')">
<image class="image" src="/static/image/center/7.png" mode=""></image>
<uv-grid-item @click="$utils.navigateTo('/pages_order/order/fastOrderList')">
<image class="image" src="/static/image/center/12.png" mode=""></image>
<text class="grid-text">快捷订单</text>
</uv-grid-item>
@ -166,6 +166,21 @@
}
},
methods: {
//
showQuickOrderMenu() {
uni.showActionSheet({
itemList: ['快捷下单', '我的快捷订单'],
success: (res) => {
if (res.tapIndex === 0) {
//
this.$utils.navigateTo('/pages_order/order/fastCreateOrder');
} else if (res.tapIndex === 1) {
//
this.$utils.navigateTo('/pages_order/order/fastOrderList');
}
}
});
}
}
}
</script>


+ 21
- 2
pages/index/index.vue View File

@ -13,10 +13,13 @@
<!-- <view class="address">
<image src="../../static/image/home/1.png" mode=""></image>
<view class="eare">
<text>深圳</text>
<text>{{ selectCity.name }}</text>
</view>
<view class="down-arrow"></view>
</view> -->
<selectCityPopup @cityChange="getData"/>
<view class="search">
<uv-search bgColor="#ffffff"
placeholder="搜索材料"
@ -78,24 +81,35 @@ import PrivacyAgreementPoup from '@/components/config/PrivacyAgreementPoup.vue'
import tabber from '@/components/base/tabbar.vue'
import productItem from '@/components/productItem.vue'
import mixinsList from '@/mixins/list.js'
import selectCityPopup from '@/components/selectCityPopup.vue'
export default {
mixins: [mixinsList],
components: {
tabber,
PrivacyAgreementPoup,
productItem,
selectCityPopup,
},
data() {
return {
keyword: '',
bannerList : [],
menuList: [],
mixinsListApi: 'getProductList',
mixinsListApi: '',
hasNewOrderMessage: false,
orderMessageCount: 0
}
},
computed: {
},
onLoad() {
if(this.selectCity.id){
this.mixinsListApi = 'getProductList'
}
uni.$on('initCity', () => {
this.mixinsListApi = 'getProductList'
this.getData()
})
},
onShow() {
this.getBanner()
@ -113,6 +127,11 @@ export default {
}
})
},
beforeGetData(){
return {
cityId : this.selectCity.id
}
},
//
getMenu() {
this.$api('getIcon', res => {


+ 239
- 8
pages_order/mine/purse.vue View File

@ -13,9 +13,39 @@
<view class="from-body">
<view>我要提现</view>
<view class="from-line">
<input placeholder="请输入提现金额" />
<input
placeholder="请输入提现金额"
v-model="withdrawAmount"
type="digit"
@input="onAmountInput"
/>
</view>
<view class="from-line">
<view class="tips" v-if="userInfo.price">
可提现金额¥{{userInfo.price}}
</view>
<!-- 快捷金额选择 -->
<view class="quick-amounts" v-if="userInfo.price && parseFloat(userInfo.price) > 0">
<view class="quick-label">快捷选择</view>
<view class="amount-buttons">
<view
class="amount-btn"
v-for="amount in quickAmounts"
:key="amount"
:class="{'disabled': amount > parseFloat(userInfo.price)}"
@click="selectQuickAmount(amount)"
>
¥{{amount}}
</view>
<view
class="amount-btn all"
@click="selectAllAmount"
>
全部
</view>
</view>
</view>
<!-- <view class="from-line">
<input placeholder="请输入姓名" />
</view>
<view class="from-line">
@ -23,7 +53,7 @@
</view>
<view class="from-line">
<input placeholder="请输入银行卡卡号" />
</view>
</view> -->
<view class="mt56">提现说明</view>
<view style="line-height: 45rpx; font-size: 24rpx;color: #666666;" v-html="notice">
</view>
@ -33,12 +63,18 @@
<view class="b-fiexd">
<view class="button-submit">提交</view>
<view
class="button-submit"
:class="{'disabled': isSubmitting || !withdrawAmount}"
@click="submitWithdraw"
>
{{isSubmitting ? '提交中...' : '提交'}}
</view>
</view>
<quick-order-entry
<!-- <quick-order-entry
ref="quickOrderEntry"
/>
/> -->
</view>
</template>
@ -51,14 +87,154 @@
},
data() {
return {
notice : ''
notice: '',
withdrawAmount: '', //
isSubmitting: false, //
quickAmounts: [10, 50, 100, 200, 500], //
}
},
computed: {
userInfo() {
return this.$store.state.userInfo || {};
}
},
onLoad() {
this.$store.commit('getUserInfo');
},
methods: {
//
selectQuickAmount(amount) {
if (amount > parseFloat(this.userInfo.price)) return;
this.withdrawAmount = amount.toString();
},
//
selectAllAmount() {
if (this.userInfo.price) {
this.withdrawAmount = parseFloat(this.userInfo.price).toString();
}
},
//
onAmountInput(e) {
let value = e.detail.value;
//
value = value.replace(/[^\d.]/g, '');
//
const pointIndex = value.indexOf('.');
if (pointIndex !== -1) {
value = value.substring(0, pointIndex + 1) + value.substring(pointIndex + 1).replace(/\./g, '');
}
// 2
if (pointIndex !== -1 && value.length > pointIndex + 3) {
value = value.substring(0, pointIndex + 3);
}
this.withdrawAmount = value;
},
//
validateAmount() {
if (!this.withdrawAmount) {
uni.showToast({
title: '请输入提现金额',
icon: 'none'
});
return false;
}
const amount = parseFloat(this.withdrawAmount);
if (isNaN(amount) || amount <= 0) {
uni.showToast({
title: '请输入正确的提现金额',
icon: 'none'
});
return false;
}
//
if (this.userInfo.price && amount > parseFloat(this.userInfo.price)) {
uni.showToast({
title: '提现金额不能超过可用余额',
icon: 'none'
});
return false;
}
// 1
if (amount < 1) {
uni.showToast({
title: '最小提现金额为1元',
icon: 'none'
});
return false;
}
return true;
},
//
submitWithdraw() {
if (this.isSubmitting) return;
//
if (!this.validateAmount()) return;
//
uni.showModal({
title: '确认提现',
content: `确定要提现 ¥${this.withdrawAmount} 元吗?`,
success: (res) => {
if (res.confirm) {
this.doWithdraw();
}
}
});
},
//
doWithdraw() {
this.isSubmitting = true;
const params = {
money: parseFloat(this.withdrawAmount)
};
this.$api('openMoney', params, res => {
this.isSubmitting = false;
if (res.code === 200) {
uni.showToast({
title: '提现申请提交成功',
icon: 'success',
duration: 2000,
success: () => {
//
this.withdrawAmount = '';
//
this.$store.commit('getUserInfo');
//
setTimeout(() => {
uni.navigateBack();
}, 2000);
}
});
} else {
uni.showModal({
title: '提现失败',
content: res.message || '提现申请提交失败,请重试',
showCancel: false
});
}
}, err => {
this.isSubmitting = false;
console.error('提现请求失败', err);
uni.showModal({
title: '网络错误',
content: '网络连接失败,请检查网络后重试',
showCancel: false
});
});
}
}
}
</script>
@ -97,6 +273,55 @@
text-align: left;
color: #333;
}
.tips {
margin-top: 20rpx;
font-size: 24rpx;
color: #999;
font-weight: 400;
}
.quick-amounts {
margin-top: 30rpx;
.quick-label {
font-size: 26rpx;
color: #666;
margin-bottom: 20rpx;
font-weight: 400;
}
.amount-buttons {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.amount-btn {
padding: 16rpx 32rpx;
background: #f5f5f5;
border-radius: 30rpx;
font-size: 26rpx;
color: #333;
text-align: center;
border: 1rpx solid transparent;
transition: all 0.3s;
&:active {
transform: scale(0.95);
}
&.disabled {
opacity: 0.5;
color: #999;
}
&.all {
background: $uni-color;
color: #fff;
}
}
}
}
}
.button-submit {
@ -113,6 +338,12 @@
font-weight: 400;
text-align: center;
color: #ffffff;
transition: opacity 0.3s;
&.disabled {
opacity: 0.6;
background: #ccc;
}
}
}

+ 32
- 1
pages_order/order/fastCreateOrder.vue View File

@ -20,6 +20,14 @@
</view>
</view>
<!-- 查看快捷订单列表 -->
<view class="order-list-btn">
<view class="list-button" @click="$utils.navigateTo('/pages_order/order/fastOrderList')">
<uv-icon name="list" size="40rpx" color="#D03F25"></uv-icon>
<text>我的快捷订单</text>
</view>
</view>
<kefu/>
</view>
@ -37,7 +45,7 @@
<style scoped lang="scss">
.select-order{
margin-top: 400rpx;
margin-top: 300rpx;
width: 100%;
height: 400rpx;
.picture-order{
@ -71,4 +79,27 @@
}
}
}
.order-list-btn{
margin-top: 50rpx;
width: 100%;
display: flex;
justify-content: center;
.list-button{
border: 1rpx solid #D03F25;
color: #D03F25;
background-color: #ffffff;
width: 85%;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 100rpx;
gap: 20rpx;
text {
font-size: 28rpx;
}
}
}
</style>

+ 665
- 0
pages_order/order/fastOrderList.vue View File

@ -0,0 +1,665 @@
<template>
<view class="fast-order-list">
<navbar
title="快捷订单"
leftClick
@leftClick="$utils.navigateBack"
/>
<!-- 状态筛选tabs -->
<uv-tabs
:list="statusTabs"
:activeStyle="{ color: '#FD5100', fontWeight: 600 }"
lineColor="#FD5100"
lineHeight="8rpx"
lineWidth="50rpx"
:scrollable="false"
@click="clickTabs">
</uv-tabs>
<!-- 订单列表 -->
<view class="order-list">
<view
class="order-item"
v-for="(order, index) in orderList"
:key="order.id"
@click="viewOrderDetail(order)"
>
<!-- 订单头部信息 -->
<view class="order-header">
<view class="order-info">
<text class="order-number">订单号: {{order.orderNumber || order.id}}</text>
<text class="order-time">{{formatTime(order.createTime)}}</text>
</view>
<view class="order-status" :class="{
'pending': order.state === '0',
'approved': order.state === '1',
'cancelled': order.state === '2'
}">
<text>{{getStatusText(order.state)}}</text>
</view>
</view>
<!-- 订单内容 -->
<view class="order-content">
<!-- 下单方式标识 -->
<view class="order-type">
<view class="type-icon" :class="{
'photo-type': order.type === '0',
'voice-type': order.type === '2',
'normal-type': order.type !== '0' && order.type !== '2'
}">
<uv-icon :name="getTypeIcon(order.type)" size="40rpx" color="#ffffff"></uv-icon>
</view>
<view class="type-info">
<text class="type-name">{{getTypeName(order.type)}}</text>
<text class="type-desc">{{getTypeDesc(order.type)}}</text>
</view>
</view>
<!-- 订单详情 -->
<view class="order-detail">
<!-- 拍照订单显示图片 -->
<view class="photo-content" v-if="order.type === '0' && order.imageUrl">
<image :src="order.imageUrl" mode="aspectFill" class="order-image"></image>
<text class="image-desc">通过图片识别下单</text>
</view>
<!-- 语音订单显示语音信息 -->
<view class="voice-content" v-else-if="order.type === '2' && order.voiceUrl">
<view class="voice-item">
<view class="voice-icon">
<uv-icon name="mic" size="30rpx" color="#D03F25"></uv-icon>
</view>
<text class="voice-desc">通过语音识别下单</text>
<view class="voice-play" @click.stop="playVoice(order.voiceUrl)">
<uv-icon name="play-circle" size="40rpx" color="#D03F25"></uv-icon>
</view>
</view>
</view>
<!-- 订单备注信息 -->
<view class="order-remark" v-if="order.remark">
<text class="remark-label">备注:</text>
<text class="remark-content">{{order.remark}}</text>
</view>
</view>
</view>
<!-- 订单底部操作 -->
<view class="order-footer">
<view class="order-price" v-if="order.totalPrice">
<text class="price-label">订单金额:</text>
<text class="price-value">¥{{order.totalPrice}}</text>
</view>
<view class="order-actions">
<view
class="action-btn cancel"
v-if="order.state === '0'"
@click.stop="cancelOrder(order)"
>
<text>取消订单</text>
</view>
<!-- <view
class="action-btn detail"
@click.stop="viewOrderDetail(order)"
>
<text>查看详情</text>
</view> -->
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="!orderList.length && !loading">
<text class="empty-text">暂无{{currentTabName}}订单</text>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="orderList.length && hasMore">
<text>{{loading ? '加载中...' : '上拉加载更多'}}</text>
</view>
<!-- 没有更多了 -->
<view class="no-more" v-if="orderList.length && !hasMore">
<text>没有更多订单了</text>
</view>
</view>
<kefu/>
</view>
</template>
<script>
export default {
computed: {
currentTabName() {
return this.statusTabs[this.currentTab] ? this.statusTabs[this.currentTab].name : '';
}
},
data() {
return {
currentTab: 0, // tab
statusTabs: [
{ name: '全部', state: '' },
{ name: '待审核', state: '0' },
{ name: '已审核', state: '1' },
{ name: '已取消', state: '2' }
],
orderList: [], //
pageNo: 1, //
pageSize: 10, //
hasMore: true, //
loading: false, //
innerAudioContext: null, //
};
},
onLoad() {
//
this.innerAudioContext = uni.createInnerAudioContext();
this.innerAudioContext.onError((err) => {
console.error('音频播放错误', err);
uni.showToast({
title: '音频播放失败',
icon: 'none'
});
});
//
this.loadOrderList();
},
onUnload() {
//
if (this.innerAudioContext) {
this.innerAudioContext.destroy();
}
},
onReachBottom() {
//
if (this.hasMore && !this.loading) {
this.loadMore();
}
},
onPullDownRefresh() {
//
this.refreshList();
},
methods: {
// tab - 使uv-tabs
clickTabs({ index }) {
if (this.currentTab === index) return;
this.currentTab = index;
this.refreshList();
},
//
refreshList() {
this.pageNo = 1;
this.hasMore = true;
this.orderList = [];
this.loadOrderList();
},
//
loadMore() {
if (!this.hasMore || this.loading) return;
this.pageNo++;
this.loadOrderList();
},
//
loadOrderList() {
if (this.loading) return;
this.loading = true;
const params = {
pageNo: this.pageNo,
pageSize: this.pageSize,
state: this.statusTabs[this.currentTab].state
};
this.$api('getAddOrderPageBean', params, res => {
this.loading = false;
uni.stopPullDownRefresh();
if (res.code === 200 && res.result) {
const newList = res.result.records || [];
if (this.pageNo === 1) {
this.orderList = newList;
} else {
this.orderList = [...this.orderList, ...newList];
}
//
this.hasMore = newList.length >= this.pageSize;
} else {
uni.showToast({
title: res.message || '加载失败',
icon: 'none'
});
}
}, err => {
this.loading = false;
uni.stopPullDownRefresh();
console.error('加载订单列表失败', err);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
});
},
//
getStatusText(state) {
const statusMap = {
'0': '待审核',
'1': '已审核',
'2': '已取消'
};
return statusMap[state] || '未知状态';
},
//
getTypeName(type) {
const typeMap = {
'0': '拍照下单',
'2': '语音下单'
};
return typeMap[type] || '普通下单';
},
//
getTypeDesc(type) {
const descMap = {
'0': '通过拍照识别商品',
'2': '通过语音识别商品'
};
return descMap[type] || '快捷下单方式';
},
//
getTypeIcon(type) {
const iconMap = {
'0': 'camera-fill',
'2': 'mic'
};
return iconMap[type] || 'list';
},
//
playVoice(voiceUrl) {
if (!voiceUrl) {
uni.showToast({
title: '语音文件不存在',
icon: 'none'
});
return;
}
//
this.innerAudioContext.src = voiceUrl;
this.innerAudioContext.play();
uni.showToast({
title: '正在播放语音',
icon: 'none'
});
},
//
cancelOrder(order) {
uni.showModal({
title: '确认取消',
content: '确定要取消这个快捷订单吗?',
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '取消中...'
});
this.$api('cancelOrderFast', {
orderId: order.id
}, res => {
uni.hideLoading();
if (res.code === 200) {
uni.showToast({
title: '取消成功',
icon: 'success'
});
//
this.refreshList();
} else {
uni.showToast({
title: res.message || '取消失败',
icon: 'none'
});
}
}, err => {
uni.hideLoading();
console.error('取消订单失败', err);
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
});
});
}
}
});
},
//
viewOrderDetail(order) {
//
if (order.state === '1') {
//
this.$utils.navigateTo(`/pages_order/order/firmOrder?orderId=${order.id}`);
} else {
//
uni.showModal({
title: '订单详情',
content: `订单号: ${order.orderNumber || order.id}\n状态: ${this.getStatusText(order.state)}\n下单方式: ${this.getTypeName(order.type)}\n创建时间: ${this.formatTime(order.createTime)}`,
showCancel: false
});
}
},
//
formatTime(time) {
if (!time) return '';
const date = new Date(time);
const now = new Date();
const diff = now.getTime() - date.getTime();
//
if (diff < 60 * 1000) {
return '刚刚';
}
//
if (diff < 60 * 60 * 1000) {
return Math.floor(diff / (60 * 1000)) + '分钟前';
}
//
if (diff < 24 * 60 * 60 * 1000) {
return Math.floor(diff / (60 * 60 * 1000)) + '小时前';
}
//
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
//
if (year === now.getFullYear()) {
return `${month}-${day} ${hour}:${minute}`;
} else {
return `${year}-${month}-${day} ${hour}:${minute}`;
}
}
}
}
</script>
<style scoped lang="scss">
.fast-order-list {
min-height: 100vh;
background-color: #f6f6f6;
.order-list {
padding: 20rpx;
.order-item {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.order-info {
flex: 1;
.order-number {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
.order-time {
font-size: 24rpx;
color: #999;
}
}
.order-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
&.pending {
background-color: rgba(255, 193, 7, 0.1);
color: #ffc107;
}
&.approved {
background-color: rgba(40, 167, 69, 0.1);
color: #28a745;
}
&.cancelled {
background-color: rgba(220, 53, 69, 0.1);
color: #dc3545;
}
}
}
.order-content {
padding: 30rpx;
.order-type {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.type-icon {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
&.photo-type {
background-color: #D03F25;
}
&.voice-type {
background-color: #17a2b8;
}
&.normal-type {
background-color: #6c757d;
}
}
.type-info {
flex: 1;
.type-name {
display: block;
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 6rpx;
}
.type-desc {
font-size: 24rpx;
color: #999;
}
}
}
.order-detail {
.photo-content {
display: flex;
align-items: center;
.order-image {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
background-color: #f9f9f9;
}
.image-desc {
font-size: 26rpx;
color: #666;
}
}
.voice-content {
.voice-item {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
.voice-icon {
width: 60rpx;
height: 60rpx;
background-color: rgba(208, 63, 37, 0.1);
border-radius: 30rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.voice-desc {
flex: 1;
font-size: 26rpx;
color: #666;
}
.voice-play {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.order-remark {
margin-top: 20rpx;
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
.remark-label {
font-size: 26rpx;
color: #999;
margin-right: 10rpx;
}
.remark-content {
font-size: 26rpx;
color: #666;
}
}
}
}
.order-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-top: 1rpx solid #f0f0f0;
.order-price {
.price-label {
font-size: 26rpx;
color: #999;
margin-right: 10rpx;
}
.price-value {
font-size: 30rpx;
font-weight: 600;
color: #D03F25;
}
}
.order-actions {
display: flex;
gap: 20rpx;
.action-btn {
padding: 12rpx 30rpx;
border-radius: 30rpx;
font-size: 26rpx;
border: 1rpx solid;
&.cancel {
border-color: #dc3545;
color: #dc3545;
background-color: rgba(220, 53, 69, 0.05);
}
&.detail {
border-color: #D03F25;
color: #D03F25;
background-color: rgba(208, 63, 37, 0.05);
}
}
}
}
}
.empty-state {
text-align: center;
padding: 100rpx 0;
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
.load-more,
.no-more {
text-align: center;
padding: 30rpx 0;
font-size: 26rpx;
color: #999;
}
}
}
</style>

+ 15
- 1
pages_order/product/productList.vue View File

@ -2,7 +2,14 @@
<view class="page">
<navbar :title="title" leftClick @leftClick="$utils.navigateBack" />
<view class="search" v-if="search">
<!-- v-if="search" -->
<view class="search">
<selectCityPopup
color="#000"
@cityChange="getData"/>
<uv-search bgColor="#ffffff"
placeholder="搜索材料"
inputAlign="left"
@ -29,10 +36,12 @@
<script>
import productItem from '@/components/productItem.vue'
import mixinsList from '@/mixins/list.js'
import selectCityPopup from '@/components/selectCityPopup.vue'
export default {
mixins: [mixinsList],
components : {
productItem,
selectCityPopup,
},
data() {
return {
@ -54,6 +63,11 @@
}
},
methods: {
beforeGetData(){
return {
cityId : this.selectCity.id
}
},
handleSearch(value){
this.queryParams.title = this.keyword
this.getData()


BIN
static/image/home/1.png View File

Before After
Width: 8  |  Height: 11  |  Size: 257 B

+ 16
- 1
store/store.js View File

@ -15,8 +15,11 @@ const store = new Vuex.Store({
category: [], //分类信息
payOrderProduct: [], //支付订单中的商品
promotionUrl : '',//分享二维码
cityList : [],
selectCity : {},
},
getters: {
},
getters: {},
mutations: {
// 初始化配置
initConfig(state) {
@ -188,6 +191,18 @@ const store = new Vuex.Store({
setPromotionUrl(state, data){
state.promotionUrl = data
},
setCity(state, data){
state.selectCity = data
},
getCityList(state){
api('getCity', res => {
if (res.code == 200) {
state.cityList = res.result
state.selectCity = res.result[0]
uni.$emit('initCity', res.result[0])
}
})
},
},
actions: {},
})


Loading…
Cancel
Save