xiongzhu %!s(int64=3) %!d(string=hai) anos
pai
achega
f4104e4096

+ 2 - 1
package.json

@@ -33,6 +33,7 @@
     "eslint": "^8.22.0",
     "eslint-plugin-vue": "^9.3.0",
     "prettier": "^2.7.1",
-    "vite": "^3.2.4"
+    "vite": "^3.2.4",
+    "vite-plugin-imagemin": "^0.6.1"
   }
 }

BIN=BIN
src/assets/bg_wallet.png


BIN=BIN
src/assets/dingdan_icon_dizhi.png


BIN=BIN
src/assets/dingdan_img_kong.png


BIN=BIN
src/assets/double_arrow_left.png


BIN=BIN
src/assets/double_arrow_right.png


+ 5 - 2
src/components/OrderItem.vue

@@ -195,8 +195,11 @@ export default {
     },
     methods: {
         goDetail() {
-            this.goNext('orderDetail', {
-                id: this.info.id
+            this.$router.push({
+                name: 'orderDetail',
+                query: {
+                    id: this.info.id
+                }
             })
         },
         getTime() {

+ 14 - 1
src/locales/zh.json

@@ -1,6 +1,19 @@
 {
     "login": "登录",
     "order": {
-        "my": "我的订单"
+        "my": "我的订单",
+        "detail": "订单详情"
+    },
+    "balance": {
+        "symbol": "¥",
+        "balance": "账户余额",
+        "record": "交易明细",
+        "chooseAmount": "选择金额",
+        "inputCustomAmount": "请输入自定义金额",
+        "withdraw": "提现",
+        "recharge": "充值"
+    },
+    "user": {
+        "wallet": "我的钱包"
     }
 }

+ 7 - 28
src/main.js

@@ -3,22 +3,8 @@ import { createPinia } from 'pinia'
 import App from './App.vue'
 import router from './router'
 import http from '@/plugins/http'
-import {
-    IonicVue,
-    IonButton,
-    IonPage,
-    IonContent,
-    IonHeader,
-    IonToolbar,
-    IonButtons,
-    IonBackButton,
-    IonList,
-    IonLabel,
-    IonInput,
-    IonItem,
-    IonTitle
-} from '@ionic/vue'
-import { Button, PullRefresh, Sticky, Tabs, Tab, List, Empty, Image, Popup, Stepper } from 'vant'
+import { IonicVue } from '@ionic/vue'
+import * as IonComponents from '@ionic/vue'
 import i18n from './locales'
 import Vant from 'vant'
 import { ConfigProvider } from 'vant'
@@ -57,18 +43,11 @@ app.use(Vant)
 app.use(ConfigProvider)
 
 // ionic components
-app.component('ion-button', IonButton)
-app.component('ion-page', IonPage)
-app.component('ion-content', IonContent)
-app.component('ion-header', IonHeader)
-app.component('ion-toolbar', IonToolbar)
-app.component('ion-buttons', IonButtons)
-app.component('ion-back-button', IonBackButton)
-app.component('ion-list', IonList)
-app.component('ion-label', IonLabel)
-app.component('ion-input', IonInput)
-app.component('ion-item', IonItem)
-app.component('ion-title', IonTitle)
+Object.keys(IonComponents).forEach(key => {
+    if (/^Ion[A-Z]\w+$/.test(key)) {
+        app.component(key, IonComponents[key])
+    }
+})
 
 //dark mode
 useDark()

+ 15 - 0
src/router/index.js

@@ -47,6 +47,21 @@ const router = createRouter({
             path: '/order',
             name: 'order',
             component: () => import('@/views/OrderPage.vue')
+        },
+        {
+            path: '/orderDetail',
+            name: 'orderDetail',
+            component: () => import('@/views/OrderDetailPage.vue')
+        },
+        {
+            path: '/wallet',
+            name: 'wallet',
+            component: () => import('@/views/WalletPage.vue')
+        },
+        {
+            path: '/balanceRecord',
+            name: 'balanceRecord',
+            component: () => import('@/views/BalanceRecordPage.vue')
         }
     ]
 })

+ 61 - 0
src/views/BalanceRecordPage.vue

@@ -0,0 +1,61 @@
+<template>
+    <ion-page>
+        <ion-header>
+            <ion-toolbar>
+                <ion-buttons slot="start">
+                    <ion-back-button default-href="#" @click="$router.back()"></ion-back-button>
+                </ion-buttons>
+                <ion-title>{{ $t('order.my') }}</ion-title>
+            </ion-toolbar>
+        </ion-header>
+        <ion-content>
+            <van-pull-refresh v-model="refreshing" @refresh="refresh">
+                <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="loadmore">
+                    <van-cell
+                        v-for="item in records"
+                        :key="item.id"
+                        :title="item.amount"
+                        :value="item.createdAt.substr(0, 16)"
+                    />
+                    <van-empty v-if="finished && records.length === 0"></van-empty>
+                </van-list>
+            </van-pull-refresh>
+        </ion-content>
+    </ion-page>
+</template>
+<script>
+export default {
+    data() {
+        return {
+            page: 0,
+            refreshing: true,
+            loading: false,
+            finished: false,
+            records: []
+        }
+    },
+    methods: {
+        refresh() {
+            this.loading = true
+            this.page = 0
+            this.getBalance()
+            this.getData()
+        },
+        loadmore() {
+            this.page++
+            this.getData()
+        },
+        getData() {
+            return this.$http.get('/user/balanceRecord', { page: this.page, size: 10 }).then(res => {
+                if (res.number === 0) {
+                    this.records = []
+                }
+                this.records = this.records.concat(res.content)
+                this.refreshing = false
+                this.loading = false
+                this.finished = res.last
+            })
+        }
+    }
+}
+</script>

+ 732 - 0
src/views/OrderDetailPage.vue

@@ -0,0 +1,732 @@
+<template>
+    <ion-page>
+        <ion-header>
+            <ion-toolbar>
+                <ion-buttons slot="start">
+                    <ion-back-button default-href="#" @click="$router.back(-1)"></ion-back-button>
+                </ion-buttons>
+                <ion-title>{{ $t('order.detail') }}</ion-title>
+            </ion-toolbar>
+        </ion-header>
+        <ion-content :fullscreen="true">
+            <div class="top">
+                <div class="status">
+                    {{
+                        orderInfo.locked && orderInfo.status === 'SELLING' ? '暂停出售' : orderStatus[orderInfo.status]
+                    }}
+                </div>
+            </div>
+
+            <div class="addresss" v-if="orderInfo.address">
+                <img src="../assets/dingdan_icon_dizhi.png" alt="" />
+                <div class="addresss-content">
+                    <div>{{ orderInfo.name }} {{ orderInfo.phone }}</div>
+                    <div>{{ orderInfo.address }}</div>
+                </div>
+            </div>
+
+            <div class="product">
+                <div class="product-content">
+                    <van-image class="suk-img" style="min-width: 80px" width="80" height="80" fit="fill" :src="pic" />
+
+                    <div class="order-text">
+                        <div class="van-ellipsis text1">{{ productInfo.name }}</div>
+
+                        <div class="text2" v-if="productInfo.user">当前所有人为:{{ productInfo.user.nickname }}</div>
+
+                        <div class="text3">
+                            <div class="price">¥{{ orderInfo.totalPrice }}</div>
+                            <div class="num">×1</div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="order-info" v-if="orderInfo.address">
+                    <div class="name">配送方式</div>
+                    <div class="val">普通配送</div>
+                </div>
+
+                <!-- <div class="order-info">
+        <div class="name">订单留言</div>
+        <div class="val">麻烦尽快发货,谢谢</div>
+      </div> -->
+            </div>
+
+            <div class="order-detail">
+                <div class="detail-item">
+                    <div class="name">商品金额</div>
+                    <div class="val">¥{{ orderInfo.totalPrice }}</div>
+                </div>
+
+                <div class="detail-item">
+                    <div class="name">运费</div>
+                    <div class="val">包邮</div>
+                </div>
+
+                <div class="detail-item">
+                    <div class="name">实付款</div>
+                    <div class="val price">¥{{ orderInfo.totalPrice }}</div>
+                </div>
+
+                <div class="pay-content">
+                    <div class="detail-item">
+                        <div class="name">收款人</div>
+                        <div class="val">{{ fromUser ? fromUser.aliName : '' }}</div>
+                    </div>
+
+                    <div class="detail-item">
+                        <div class="name">收款支付宝号</div>
+                        <div class="val">{{ fromUser ? fromUser.aliAccount : '' }}</div>
+                    </div>
+                </div>
+
+                <div class="time-content">
+                    <div class="detail-item">
+                        <div class="name">订单编号</div>
+                        <div class="val">{{ orderInfo.id }}</div>
+                    </div>
+                    <div class="detail-item">
+                        <div class="name">创建时间</div>
+                        <div class="val">{{ orderInfo.createdAt }}</div>
+                    </div>
+                    <div class="detail-item" v-if="orderInfo.paidTime">
+                        <div class="name">付款时间</div>
+                        <div class="val">{{ orderInfo.paidTime }}</div>
+                    </div>
+                    <div class="detail-item" v-if="orderInfo.confirmTime">
+                        <div class="name">成交时间</div>
+                        <div class="val">{{ orderInfo.confirmTime }}</div>
+                    </div>
+                    <div class="detail-item" v-if="orderInfo.logisticsType">
+                        <div class="name">物流名称</div>
+                        <div class="val">{{ orderInfo.logisticsType }}</div>
+                    </div>
+                    <div class="detail-item" v-if="orderInfo.logisticsNo">
+                        <div class="name">物流编号</div>
+                        <div class="val">{{ orderInfo.logisticsNo }}</div>
+                    </div>
+                </div>
+            </div>
+
+            <div class="bottom" style="height: 50px">
+                <div>
+                    <div class="order-button" v-if="orderInfo.status == 'NOT_PAID'">
+                        <!-- <van-button color="#FF8F00" round plain @click="confirmPayment">我已付款</van-button> -->
+                        <van-button type="primary" round @click="pay">立即支付</van-button>
+                    </div>
+
+                    <div
+                        class="order-button"
+                        v-else-if="orderInfo.status == 'SOLD_NOT_CONFIRMED' && !orderInfo.delegationId"
+                    >
+                        <!-- <van-button color="#AAACAD" round plain @click="confirmPayment">未收到款</van-button> -->
+                        <!-- <van-button type="primary" round @click="confirmReceipt">确认收款</van-button> -->
+                    </div>
+
+                    <div class="order-button" v-else-if="orderInfo.status == 'CONFIRMED'">
+                        <!-- <van-button color="#AAACAD" round plain @click="applyShip">申请发货</van-button> -->
+                        <van-button type="primary" round @click="show = true" v-if="delegationActive"
+                            >委托代卖
+                        </van-button>
+                        <van-button color="#aaacad" round @click="showTip" v-else>委托代卖 </van-button>
+                    </div>
+
+                    <div class="order-button" v-else-if="orderInfo.status == 'SHIPPED'">
+                        <!-- <van-button color="#AAACAD" round plain @click="confirmPayment">未收到款</van-button> -->
+                        <van-button type="primary" round @click="receive"> 确认收货</van-button>
+                    </div>
+                </div>
+            </div>
+
+            <van-popup v-model="show" class="getsold">
+                <div class="title">委托代卖</div>
+                <div class="sold-list">
+                    <div class="sold-item">
+                        <div class="name">原价</div>
+                        <div class="val">¥{{ orderInfo.totalPrice }}</div>
+                    </div>
+                    <div class="sold-item">
+                        <div class="name">加价</div>
+                        <van-stepper v-model="value" input-width="60px" step="1" integer min="1" :max="maxRiseRate" />
+                    </div>
+                    <div class="sold-item">
+                        <div class="name">卖价</div>
+                        <div class="val bold">¥{{ soldValue }}</div>
+                    </div>
+                </div>
+                <div class="tips">
+                    注:委托平台代卖服务,每次最高可将商品价格提高{{ maxRiseRate }}%,平台会收取{{
+                        serviceValue
+                    }}%的托管服务费用
+                </div>
+
+                <van-button class="button" :disabled="loading" block type="primary" round @click="sale">
+                    支付手续费 ¥{{ serviceCharge }}</van-button
+                >
+            </van-popup>
+        </ion-content>
+    </ion-page>
+</template>
+<script>
+import { mapState } from 'pinia'
+import { useUserStore } from '@/stores/user'
+import { orderStatus } from '../status'
+import { parse } from 'date-fns'
+export default {
+    name: 'orderDetail',
+    data() {
+        return {
+            orderStatus,
+            orderInfo: {},
+            productInfo: {},
+            fromUser: {},
+            show: false,
+            value: 6,
+            platformCommission: 0.02,
+            delegationActive: '',
+            delegationTime: '',
+            maxRiseRate: 6,
+            loading: false,
+            payType: 'balance'
+        }
+    },
+    computed: {
+        ...mapState(useUserStore, ['user']),
+        pic() {
+            if (this.productInfo.pic && this.productInfo.pic.length > 0) {
+                return this.productInfo.pic[0]
+            } else {
+                return null
+            }
+        },
+        soldValue() {
+            var price = Number(this.orderInfo.totalPrice)
+            if (this.value) {
+                var more = this.mul(this.value, price)
+                more = this.mul(more, 0.01)
+                price = this.addNum(more, price)
+            }
+            return price.toFixed(2)
+        },
+        serviceCharge() {
+            let totalPrice = this.orderInfo ? this.orderInfo.totalPrice || 0 : 0
+            return (totalPrice * this.platformCommission).toFixed(2)
+        },
+        serviceValue() {
+            return this.platformCommission * 100
+        }
+    },
+    created() {
+        this.$http
+            .get('/order/get/' + this.$route.query.id)
+            .then(res => {
+                this.orderInfo = res
+                if (res.productId) {
+                    return this.$http.get('/product/get/' + res.productId)
+                }
+            })
+            .then(res => {
+                this.productInfo = res
+                if (this.orderInfo.fromUserId) {
+                    return this.$http.get('/user/get/' + this.orderInfo.fromUserId)
+                }
+            })
+            .then(res => {
+                this.fromUser = res
+            })
+        this.$http.get('/sysConfig/get/platform_commission').then(res => {
+            this.platformCommission = res.value
+        })
+        this.$http.get('/sysConfig/get/max_rise_rate').then(res => {
+            this.maxRiseRate = res.value * 100
+            this.value = this.maxRiseRate
+        })
+        this.$http.get('/sysConfig/get/delegation_time').then(res => {
+            var date = parse(res.value, 'HH:mm', new Date())
+            this.$http.get('/sysConfig/get/sell_time').then(res => {
+                var date2 = parse(res.value, 'HH:mm', new Date())
+                if (new Date().getTime() >= date.getTime() || new Date().getTime() <= date2.getTime()) {
+                    this.delegationActive = true
+                    this.delegationTime = res.value
+                }
+            })
+        })
+        // this.$http.get('/sysConfig/get/pay_type').then(res => {
+        //     if (res.value == '0') {
+        //         if (this.isWeixin) {
+        //             this.payType = 'weixin';
+        //         } else {
+        //             this.payType = 'alipay';
+        //         }
+        //     } else {
+        //         this.payType = 'third';
+        //     }
+        // });
+    },
+    methods: {
+        showTip() {
+            this.$dialog.alert({
+                title: '提示',
+                message: '委托代卖暂未开始,将在今天' + this.delegationTime + '开始'
+            })
+        },
+        getInfo() {
+            this.$http.get('/order/get/' + this.$route.query.id).then(res => {
+                this.orderInfo = res
+            })
+        },
+        addNum(a, b) {
+            var c, d, e
+            try {
+                c = a.toString().split('.')[1].length
+            } catch (f) {
+                c = 0
+            }
+            try {
+                d = b.toString().split('.')[1].length
+            } catch (f) {
+                d = 0
+            }
+            return (e = Math.pow(10, Math.max(c, d))), (this.mul(a, e) + this.mul(b, e)) / e
+        },
+        mul(a, b) {
+            var c = 0,
+                d = a.toString(),
+                e = b.toString()
+            try {
+                c += d.split('.')[1].length
+            } catch (f) {
+                /* empty */
+            }
+            try {
+                c += e.split('.')[1].length
+            } catch (f) {
+                /* empty */
+            }
+            return (Number(d.replace('.', '')) * Number(e.replace('.', ''))) / Math.pow(10, c)
+        },
+        confirmPayment() {
+            this.$toast.loading({
+                mask: false,
+                message: '加载中...',
+                duration: 0,
+                forbidClick: true
+            })
+            this.$http
+                .post('/order/confirmPayment', {
+                    orderId: this.orderInfo.id
+                })
+                .then(res => {
+                    this.$toast.success('确认成功')
+                    setTimeout(() => {
+                        this.getInfo()
+                    }, 1000)
+                })
+                .catch(e => {
+                    return this.$toast(e.error)
+                })
+        },
+        confirmReceipt() {
+            this.$toast.loading({
+                mask: false,
+                message: '加载中...',
+                duration: 0,
+                forbidClick: true
+            })
+            this.$http
+                .post('/order/confirmReceipt', {
+                    orderId: this.orderInfo.id
+                })
+                .then(res => {
+                    this.$toast.success('确认成功')
+                    setTimeout(() => {
+                        this.getInfo()
+                    }, 1000)
+                })
+                .catch(e => {
+                    return this.$toast(e.error)
+                })
+        },
+        sale() {
+            this.show = false
+            if (this.payType === 'alipay') {
+                window.open(
+                    this.$baseUrl +
+                        `/payDelegation/alipay?userId=${this.user.id}&orderId=${this.orderInfo.id}&riseRate=${
+                            this.value / 100
+                        }&returnUrl=${encodeURIComponent(location.href)}`
+                )
+            } else if (this.payType === 'third') {
+                console.log(
+                    this.$baseUrl +
+                        `/payDelegation/third?userId=${this.user.id}&orderId=${this.orderInfo.id}&riseRate=${
+                            this.value / 100
+                        }&&payType=` +
+                        (this.isWeixin ? 'WXGZH' : 'ZFBH5')
+                )
+                window.open(
+                    this.$baseUrl +
+                        `/payDelegation/third?userId=${this.user.id}&orderId=${this.orderInfo.id}&riseRate=${
+                            this.value / 100
+                        }&&payType=` +
+                        (this.isWeixin ? 'WXGZH' : 'ZFBH5')
+                )
+            } else if (this.payType === 'weixin') {
+                this.loading = true
+                this.$toast.loading({
+                    mask: false,
+                    message: '加载中...',
+                    duration: 0,
+                    forbidClick: true
+                })
+                this.$http
+                    .get('/payDelegation/wx', {
+                        userId: this.user.id,
+                        orderId: this.orderInfo.id,
+                        riseRate: this.value / 100
+                    })
+                    .then(res => {
+                        this.$toast.clear()
+                        this.loading = false
+                        wx.chooseWXPay({
+                            appId: res.appId,
+                            timestamp: res.timeStamp,
+                            nonceStr: res.nonceStr,
+                            package: res.packageValue,
+                            signType: res.signType,
+                            paySign: res.paySign,
+                            success(res) {
+                                this.$toast.success('支付成功')
+                            }
+                        })
+                    })
+                    .catch(e => {
+                        this.$toast.clear()
+                        this.loading = false
+                        return this.$toast(e.error)
+                    })
+            } else if (this.payType === 'balance') {
+                this.$toast.loading('支付中')
+                this.$http
+                    .post('/payDelegation/balance', {
+                        userId: this.user.id,
+                        orderId: this.orderInfo.id,
+                        riseRate: this.value / 100
+                    })
+                    .then(res => {
+                        this.$toast.success('支付成功')
+                        this.getInfo()
+                    })
+                    .catch(e => {
+                        this.$toast(e.error)
+                    })
+            }
+        },
+        applyShip() {
+            this.$dialog
+                .confirm({
+                    title: '提示',
+                    message: '确认申请发货?'
+                })
+                .then(() => {
+                    this.$toast.loading({
+                        mask: false,
+                        message: '加载中...',
+                        duration: 0,
+                        forbidClick: true
+                    })
+                    this.$http
+                        .post('/order/applyShip', {
+                            orderId: this.orderInfo.id
+                        })
+                        .then(res => {
+                            this.$toast.success('申请成功')
+                            setTimeout(() => {
+                                this.getInfo()
+                            }, 1000)
+                        })
+                        .catch(e => {
+                            return this.$toast(e.error)
+                        })
+                })
+                .catch(() => {})
+        },
+        receive() {
+            this.$toast.loading({
+                mask: false,
+                message: '加载中...',
+                duration: 0,
+                forbidClick: true
+            })
+            this.$http
+                .post('/order/receive', {
+                    orderId: this.orderInfo.id
+                })
+                .then(res => {
+                    this.$toast.success('确认成功')
+                    setTimeout(() => {
+                        this.getInfo()
+                    }, 1000)
+                })
+                .catch(e => {
+                    return this.$toast(e.error)
+                })
+        },
+        pay() {
+            this.$toast.loading('支付中')
+            this.$http
+                .post('/order/balancePay', { orderId: this.orderInfo.id })
+                .then(res => {
+                    this.$toast.success('支付成功')
+                    setTimeout(() => {
+                        this.getInfo()
+                    }, 1000)
+                })
+                .catch(e => {
+                    this.$toast(e.error)
+                })
+        }
+    }
+}
+</script>
+<style lang="less" scoped>
+.top {
+    height: 72px;
+    background: rgba(255, 143, 0, 1);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .status {
+        box-sizing: border-box;
+        height: 35px;
+        background: rgba(255, 255, 255, 0.3);
+        border-radius: 18px;
+        min-width: 120px;
+        padding: 0 20px;
+        font-size: 17px;
+        font-weight: bold;
+        color: rgba(255, 255, 255, 1);
+        line-height: 35px;
+        text-align: center;
+    }
+}
+.addresss {
+    background: rgba(255, 255, 255, 1);
+    border-radius: 2px;
+    margin: 15px 15px 0;
+    display: flex;
+    padding: 15px;
+    img {
+        width: 22px;
+        height: 22px;
+        margin-top: 5px;
+    }
+
+    .addresss-content {
+        font-size: 14px;
+        color: rgba(0, 0, 0, 1);
+        line-height: 20px;
+        margin-left: 10px;
+    }
+}
+
+.product {
+    background: rgba(255, 255, 255, 1);
+    border-radius: 2px;
+    margin: 15px 15px 0;
+    padding: 15px;
+
+    .product-content {
+        display: flex;
+
+        .order-text {
+            margin-left: 10px;
+            flex-grow: 1;
+            flex-basis: 0;
+            min-width: 0;
+            .text1 {
+                font-size: 14px;
+                color: rgba(0, 0, 0, 1);
+                line-height: 20px;
+                text-overflow: ellipsis;
+                white-space: nowrap;
+                overflow: hidden;
+            }
+
+            .text2 {
+                font-size: 12px;
+                color: rgba(255, 143, 0, 1);
+                line-height: 22px;
+                height: 22px;
+                background: rgba(255, 143, 0, 0.12);
+                border-radius: 2px 100px 100px 100px;
+                padding: 0 8px;
+                margin-top: 5px;
+                display: inline-block;
+            }
+
+            .text3 {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                margin-top: 13px;
+
+                .price {
+                    font-size: 14px;
+                    font-weight: bold;
+                    color: rgba(0, 0, 0, 1);
+                    line-height: 20px;
+                }
+
+                .num {
+                    font-size: 14px;
+                    color: rgba(170, 172, 173, 1);
+                    line-height: 20px;
+                }
+            }
+        }
+    }
+
+    .order-info {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-top: 20px;
+        border-top: 1px solid #f2f4f5;
+        padding-top: 10px;
+
+        .name {
+            font-size: 13px;
+            color: rgba(170, 172, 173, 1);
+            line-height: 18px;
+        }
+
+        .val {
+            font-size: 13px;
+            color: rgba(0, 0, 0, 1);
+            line-height: 18px;
+        }
+    }
+}
+
+.order-detail {
+    background: rgba(255, 255, 255, 1);
+    border-radius: 2px;
+    margin: 15px;
+    padding: 15px;
+
+    .detail-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .name {
+            font-size: 13px;
+            color: rgba(170, 172, 173, 1);
+            line-height: 18px;
+        }
+        .val {
+            font-size: 13px;
+            color: rgba(0, 0, 0, 1);
+            line-height: 18px;
+
+            &.price {
+                color: #ff3b30;
+                font-weight: bold;
+            }
+        }
+        &:not(:last-child) {
+            margin-bottom: 10px;
+        }
+    }
+
+    .time-content {
+        padding-top: 10px;
+        border-top: 1px solid #f2f4f5;
+        .val {
+            color: rgba(102, 102, 102, 1);
+        }
+    }
+
+    .pay-content {
+        padding-top: 10px;
+        border-top: 1px solid #f2f4f5;
+        padding-bottom: 10px;
+    }
+}
+
+.order-button {
+    height: 50px;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding: 0 15px;
+    width: 100%;
+
+    .van-button {
+        width: 110px;
+        height: 39px;
+        line-height: 39px;
+        margin-left: 10px;
+    }
+}
+
+.getsold {
+    width: 300px;
+    height: 405px;
+    background: rgba(255, 255, 255, 1);
+    border-radius: 4px;
+    padding: 15px 20px;
+    box-sizing: border-box;
+    .title {
+        font-size: 16px;
+        font-weight: bold;
+        text-align: center;
+        color: rgba(0, 0, 0, 1);
+        line-height: 22px;
+    }
+
+    .sold-item {
+        display: flex;
+        align-items: center;
+        height: 28px;
+        padding: 22px 0 10px;
+        border-bottom: 1px solid #f2f4f5;
+        .name {
+            font-size: 18px;
+            font-weight: bold;
+            color: rgba(0, 0, 0, 1);
+            line-height: 25px;
+            min-width: 82px;
+        }
+
+        .val {
+            font-size: 14px;
+            font-weight: bold;
+            color: rgba(0, 0, 0, 1);
+            line-height: 20px;
+
+            &.bold {
+                font-size: 16px;
+                color: rgba(255, 59, 48, 1);
+                line-height: 22px;
+            }
+        }
+    }
+    .sold-list {
+        margin-top: 10px;
+    }
+    .tips {
+        font-size: 13px;
+        color: rgba(170, 172, 173, 1);
+        line-height: 18px;
+        margin-top: 20px;
+    }
+
+    .button {
+        width: 200px;
+        display: block;
+        margin: 50px auto 0;
+    }
+}
+</style>

+ 16 - 25
src/views/OrderPage.vue

@@ -1,30 +1,21 @@
 <template>
     <ion-page>
-        <ion-content :fullscreen="true" class="list">
-            <ion-page>
-                <ion-header>
-                    <ion-toolbar>
-                        <ion-buttons slot="start">
-                            <ion-back-button default-href="#" @click="$router.back(-1)"></ion-back-button>
-                        </ion-buttons>
-                        <ion-title>{{ $t('order.my') }}</ion-title>
-                    </ion-toolbar>
-                </ion-header>
-                <ion-content :fullscreen="true" class="ion-content">
-                    <van-tabs v-model:active="status" :line-height="2">
-                        <van-tab v-for="item in statusOptions" :title="item.title" :name="item.value" :key="item.value">
-                        </van-tab>
-                    </van-tabs>
-                    <van-list
-                        v-model:loading="loading"
-                        :finished="finished"
-                        finished-text="没有更多了"
-                        @load="loadmore"
-                    >
-                        <order-item v-for="item in list" :key="item.id" :info="item"></order-item>
-                    </van-list>
-                </ion-content>
-            </ion-page>
+        <ion-header>
+            <ion-toolbar>
+                <ion-buttons slot="start">
+                    <ion-back-button default-href="#" @click="$router.back(-1)"></ion-back-button>
+                </ion-buttons>
+                <ion-title>{{ $t('order.my') }}</ion-title>
+            </ion-toolbar>
+        </ion-header>
+        <ion-content :fullscreen="true">
+            <van-tabs v-model:active="status" :line-height="2">
+                <van-tab v-for="item in statusOptions" :title="item.title" :name="item.value" :key="item.value">
+                </van-tab>
+            </van-tabs>
+            <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="loadmore">
+                <order-item v-for="item in list" :key="item.id" :info="item"></order-item>
+            </van-list>
         </ion-content>
     </ion-page>
 </template>

+ 214 - 0
src/views/WalletPage.vue

@@ -0,0 +1,214 @@
+<template>
+    <ion-page>
+        <ion-header>
+            <ion-toolbar>
+                <ion-buttons slot="start">
+                    <ion-back-button default-href="#" @click="$router.back()"></ion-back-button>
+                </ion-buttons>
+                <ion-title>{{ $t('user.wallet') }}</ion-title>
+            </ion-toolbar>
+        </ion-header>
+        <ion-content :fullscreen="true" class="wallet-page">
+            <div class="head">
+                <div class="balance">
+                    <span class="sym">{{ $t('balance.symbol') }}</span
+                    >{{ balance.balance }}
+                </div>
+                <div class="label">{{ $t('balance.balance') }}</div>
+                <div class="btn-record">
+                    <img src="../assets/double_arrow_left.png" class="arrow" />
+                    <div>{{ $t('balance.record') }}</div>
+                    <img src="../assets/double_arrow_right.png" class="arrow" />
+                </div>
+            </div>
+            <div class="recharge">
+                <div class="title">{{ $t('balance.chooseAmount') }}</div>
+                <div class="choose-amount">
+                    <div
+                        class="amount-item"
+                        v-for="item in amountOptions"
+                        :key="item"
+                        :class="{ active: amount === item }"
+                        @click="amount = item"
+                    >
+                        <span class="sym">{{ $t('balance.symbol') }}</span
+                        >{{ item }}
+                    </div>
+                    <div class="input-wrapper">
+                        <span class="sym">{{ $t('balance.symbol') }}</span>
+                        <ion-input
+                            color="dark"
+                            typeof="number"
+                            :placeholder="$t('balance.inputCustomAmount')"
+                            clear-input
+                            inputmode="decimal"
+                            v-model="customAmount"
+                        ></ion-input>
+                    </div>
+                </div>
+            </div>
+        </ion-content>
+        <ion-footer class="ion-no-border">
+            <ion-toolbar>
+                <div class="bottom-buttons">
+                    <ion-button color="light">{{ $t('balance.withdraw') }}</ion-button>
+                    <ion-button>{{ $t('balance.recharge') }}</ion-button>
+                </div>
+            </ion-toolbar>
+        </ion-footer>
+    </ion-page>
+</template>
+<script>
+export default {
+    data() {
+        return {
+            balance: {
+                balance: 0
+            },
+            showRecharge: false,
+            rechargeAmount: 999,
+            amountOptions: [100, 200, 300, 600, 800, 1000],
+            amount: 100,
+            customAmount: null
+        }
+    },
+    created() {
+        this.getBalance()
+    },
+    methods: {
+        getBalance() {
+            this.$http.get('/user/balance').then(res => {
+                this.balance = res
+            })
+        },
+        confirmRecharge() {
+            this.showRecharge = false
+            this.$toast.loading('充值')
+            this.$http.post('/user/recharge', { amount: this.rechargeAmount }).then(res => {
+                this.$toast.success('充值成功')
+                this.getBalance()
+            })
+        }
+    }
+}
+</script>
+<style lang="less" scoped>
+.wallet-page {
+    background-color: white;
+    display: flex;
+    flex-direction: column;
+    .head {
+        background: url('../assets/bg_wallet.png') no-repeat center center;
+        background-size: cover;
+        margin: 16px 16px 0 16px;
+        height: 138px;
+        padding-top: 28px;
+        position: relative;
+        .balance {
+            font-size: 30px;
+            font-weight: bold;
+            color: var(--ion-color-light);
+            margin-left: 20px;
+            .f();
+            align-items: baseline;
+            .sym {
+                font-size: 16px;
+                font-weight: bold;
+                margin-right: 4px;
+            }
+        }
+        .label {
+            font-size: 14px;
+            color: rgba(var(--ion-color-light-rgb), 0.6);
+            margin-left: 20px;
+            margin-top: 4px;
+        }
+        .btn-record {
+            position: absolute;
+            left: 0;
+            bottom: 0;
+            right: 0;
+            .f();
+            justify-content: center;
+            line-height: 40px;
+            font-size: 14px;
+            color: var(--ion-color-light);
+            .arrow {
+                width: 18px;
+                height: 18px;
+                margin: 0 16px;
+            }
+        }
+    }
+    .list {
+        flex-grow: 1;
+        min-height: 0;
+        flex-basis: 0;
+        overflow: auto;
+    }
+    .recharge {
+        background-color: var(--ion-color-primary-contrast);
+        margin: 12px 16px 0 16px;
+        border-radius: 4px 4px 0px 0px;
+        padding: 0 16px;
+        .title {
+            font-weight: bold;
+            font-size: 16px;
+            line-height: 56px;
+        }
+        .choose-amount {
+            .f();
+            flex-wrap: wrap;
+            justify-content: space-between;
+            .amount-item {
+                width: calc((100vw - 96px) / 3);
+                background: rgba(var(--ion-color-light-contrast-rgb), 0.12);
+                height: 40px;
+                line-height: 40px;
+                margin-bottom: 16px;
+                border-radius: 2px;
+                font-size: 26px;
+                .f();
+                justify-content: center;
+                align-items: baseline;
+                color: var(--ion-color-dark-contrast);
+                .sym {
+                    font-weight: bold;
+                    font-size: 14px;
+                }
+                &.active {
+                    background-color: #477bff;
+                }
+            }
+            .input-wrapper {
+                border-radius: 2px;
+                height: 48px;
+                width: 100%;
+                .f();
+                background-color: var(--ion-background-color);
+                border-radius: 2px;
+                padding: 0 12px;
+                margin-bottom: 16px;
+                ion-input {
+                    flex-grow: 1;
+                    margin-left: 12px;
+                }
+                .sym {
+                    color: var(--ion-color-medium);
+                    font-size: 14px;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+}
+ion-footer {
+    background: var(--ion-item-background);
+}
+.bottom-buttons {
+    .f();
+    ion-button {
+        flex-grow: 1;
+    }
+}
+</style>

+ 29 - 1
vite.config.js

@@ -2,10 +2,38 @@ import { fileURLToPath, URL } from 'node:url'
 
 import { defineConfig } from 'vite'
 import vue from '@vitejs/plugin-vue'
+import viteImagemin from 'vite-plugin-imagemin'
 
 // https://vitejs.dev/config/
 export default defineConfig({
-    plugins: [vue()],
+    plugins: [
+        vue(),
+        viteImagemin({
+            gifsicle: {
+                optimizationLevel: 7,
+                interlaced: false
+            },
+            optipng: false,
+            mozjpeg: {
+                quality: 20
+            },
+            pngquant: {
+                quality: [0.5, 0.9],
+                speed: 1
+            },
+            svgo: {
+                plugins: [
+                    {
+                        name: 'removeViewBox'
+                    },
+                    {
+                        name: 'removeEmptyAttrs',
+                        active: false
+                    }
+                ]
+            }
+        })
+    ],
     resolve: {
         alias: {
             '@': fileURLToPath(new URL('./src', import.meta.url))

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 776 - 5
yarn.lock


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio