页面优化
|
|
@ -106,6 +106,35 @@
|
|||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 页面顶图 -->
|
||||
<el-tab-pane label="页面顶图" name="page_banners">
|
||||
<el-form label-width="100px" style="max-width: 600px;">
|
||||
<el-form-item v-for="item in pageBannerList" :key="item.key" :label="item.label">
|
||||
<el-upload
|
||||
action="/api/upload/image"
|
||||
:headers="uploadHeaders"
|
||||
:show-file-list="false"
|
||||
:on-success="(res) => pageBanners[item.key] = res.url"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-image
|
||||
v-if="pageBanners[item.key]"
|
||||
:src="pageBanners[item.key]"
|
||||
style="width: 300px; height: 100px; cursor: pointer; border-radius: 8px;"
|
||||
fit="cover"
|
||||
/>
|
||||
<el-button v-else size="small" type="primary">选择图片</el-button>
|
||||
</el-upload>
|
||||
<div v-if="pageBanners[item.key]" style="margin-top: 4px;">
|
||||
<el-button size="small" text type="danger" @click="pageBanners[item.key] = ''">移除</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="savePageBanners">保存全部顶图</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -126,6 +155,24 @@ const commissionRules = ref([])
|
|||
const configs = reactive({ qrcode: '', agreement: '', privacy: '', withdrawal_guide: '' })
|
||||
const freezeDays = ref(1)
|
||||
|
||||
// 页面顶图配置
|
||||
const pageBannerList = [
|
||||
{ key: 'page_banner_pickup', label: '代取' },
|
||||
{ key: 'page_banner_delivery', label: '代送' },
|
||||
{ key: 'page_banner_help', label: '万能帮' },
|
||||
{ key: 'page_banner_purchase', label: '代购' },
|
||||
{ key: 'page_banner_food', label: '美食街' },
|
||||
{ key: 'page_banner_order-hall', label: '接单页' }
|
||||
]
|
||||
const pageBanners = reactive({
|
||||
page_banner_pickup: '',
|
||||
page_banner_delivery: '',
|
||||
page_banner_help: '',
|
||||
page_banner_purchase: '',
|
||||
page_banner_food: '',
|
||||
'page_banner_order-hall': ''
|
||||
})
|
||||
|
||||
function addRule() {
|
||||
commissionRules.value.push({ minAmount: 0, maxAmount: 0, rateType: 0, rate: 0 })
|
||||
}
|
||||
|
|
@ -148,7 +195,7 @@ async function saveCommissionRules() {
|
|||
|
||||
async function fetchConfig(key) {
|
||||
try {
|
||||
const res = await request.get(`/config/${key}`)
|
||||
const res = await request.get(`/admin/config/${key}`)
|
||||
configs[key] = res.value || ''
|
||||
} catch { /* 忽略 */ }
|
||||
}
|
||||
|
|
@ -180,6 +227,27 @@ async function saveFreezeDays() {
|
|||
}
|
||||
}
|
||||
|
||||
async function fetchPageBanners() {
|
||||
for (const item of pageBannerList) {
|
||||
try {
|
||||
const res = await request.get(`/admin/config/${item.key}`)
|
||||
pageBanners[item.key] = res.value || ''
|
||||
} catch { /* 忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
async function savePageBanners() {
|
||||
saving.value = true
|
||||
try {
|
||||
for (const item of pageBannerList) {
|
||||
await request.put(`/admin/config/${item.key}`, { value: pageBanners[item.key] })
|
||||
}
|
||||
ElMessage.success('页面顶图保存成功')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchCommissionRules()
|
||||
await Promise.all([
|
||||
|
|
@ -187,7 +255,8 @@ onMounted(async () => {
|
|||
fetchConfig('agreement'),
|
||||
fetchConfig('privacy'),
|
||||
fetchConfig('withdrawal_guide'),
|
||||
fetchFreezeDays()
|
||||
fetchFreezeDays(),
|
||||
fetchPageBanners()
|
||||
])
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "",
|
||||
"appid" : "wx626fd4a47944e3ea",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,157 +9,183 @@
|
|||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
"navigationBarTitleText": "登录",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order-hall/order-hall",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单大厅"
|
||||
"navigationBarTitleText": "接单",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息"
|
||||
"navigationBarTitleText": "消息",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pickup/pickup",
|
||||
"style": {
|
||||
"navigationBarTitleText": "代取"
|
||||
"navigationBarTitleText": "代取",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/delivery/delivery",
|
||||
"style": {
|
||||
"navigationBarTitleText": "代送"
|
||||
"navigationBarTitleText": "代送",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/help/help",
|
||||
"style": {
|
||||
"navigationBarTitleText": "万能帮"
|
||||
"navigationBarTitleText": "万能帮",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/purchase/purchase",
|
||||
"style": {
|
||||
"navigationBarTitleText": "代购"
|
||||
"navigationBarTitleText": "代购",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/food/food",
|
||||
"style": {
|
||||
"navigationBarTitleText": "美食街"
|
||||
"navigationBarTitleText": "美食街",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/food/shop-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "门店详情"
|
||||
"navigationBarTitleText": "门店详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/food/food-order",
|
||||
"style": {
|
||||
"navigationBarTitleText": "美食街订单"
|
||||
"navigationBarTitleText": "美食街订单",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/food/food-order-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "美食街订单详情"
|
||||
"navigationBarTitleText": "美食街订单详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/order-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情"
|
||||
"navigationBarTitleText": "订单详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/my-orders",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单"
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/my-taken",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的接单"
|
||||
"navigationBarTitleText": "我的接单",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/complete-order",
|
||||
"style": {
|
||||
"navigationBarTitleText": "完成订单确认"
|
||||
"navigationBarTitleText": "完成订单确认",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/system-msg",
|
||||
"style": {
|
||||
"navigationBarTitleText": "系统消息"
|
||||
"navigationBarTitleText": "系统消息",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/order-notify",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单通知"
|
||||
"navigationBarTitleText": "订单通知",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/chat",
|
||||
"style": {
|
||||
"navigationBarTitleText": "聊天"
|
||||
"navigationBarTitleText": "聊天",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/earnings",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的收益"
|
||||
"navigationBarTitleText": "我的收益",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/earnings-record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "收益记录"
|
||||
"navigationBarTitleText": "收益记录",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/runner/certification",
|
||||
"style": {
|
||||
"navigationBarTitleText": "跑腿认证"
|
||||
"navigationBarTitleText": "跑腿认证",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/config/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户协议"
|
||||
"navigationBarTitleText": "用户协议",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/config/privacy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "隐私政策"
|
||||
"navigationBarTitleText": "隐私政策",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/config/qrcode",
|
||||
"style": {
|
||||
"navigationBarTitleText": "客服二维码"
|
||||
"navigationBarTitleText": "客服二维码",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/webview/webview",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="agreement-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">用户协议</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<rich-text v-if="content" :nodes="content" class="rich-content"></rich-text>
|
||||
<view v-else class="empty-tip">
|
||||
<text>暂无内容</text>
|
||||
|
|
@ -13,13 +24,17 @@ import { getAgreement } from '../../utils/api'
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
content: ''
|
||||
content: '',
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadContent()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载用户协议内容 */
|
||||
async loadContent() {
|
||||
try {
|
||||
|
|
@ -34,6 +49,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.agreement-page {
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="privacy-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">隐私政策</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<rich-text v-if="content" :nodes="content" class="rich-content"></rich-text>
|
||||
<view v-else class="empty-tip">
|
||||
<text>暂无内容</text>
|
||||
|
|
@ -13,13 +24,17 @@ import { getPrivacy } from '../../utils/api'
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
content: ''
|
||||
content: '',
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadContent()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载隐私政策内容 */
|
||||
async loadContent() {
|
||||
try {
|
||||
|
|
@ -34,6 +49,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.privacy-page {
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="qrcode-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">客服二维码</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<view v-if="qrcodeUrl" class="qrcode-card">
|
||||
<text class="qrcode-title">客服二维码</text>
|
||||
<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" @click="previewImage"></image>
|
||||
|
|
@ -17,13 +28,17 @@ import { getQrcode } from '../../utils/api'
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
qrcodeUrl: ''
|
||||
qrcodeUrl: '',
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadQrcode()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载客服二维码 */
|
||||
async loadQrcode() {
|
||||
try {
|
||||
|
|
@ -43,6 +58,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.qrcode-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,55 +1,91 @@
|
|||
<template>
|
||||
<view class="order-page">
|
||||
<view class="form-section">
|
||||
<!-- 代送物品 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代送物品</text>
|
||||
<input class="form-input" v-model="form.itemName" placeholder="请输入代送物品" />
|
||||
</view>
|
||||
<!-- 取货地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">取货地点</text>
|
||||
<input class="form-input" v-model="form.pickupLocation" placeholder="请输入取货地点" />
|
||||
</view>
|
||||
<!-- 送达地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">送达地点</text>
|
||||
<input class="form-input" v-model="form.deliveryLocation" placeholder="请输入送达地点" />
|
||||
</view>
|
||||
<!-- 备注信息 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注信息</text>
|
||||
<textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注信息(选填)" />
|
||||
</view>
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入联系手机号" maxlength="11" />
|
||||
</view>
|
||||
<!-- 跑腿佣金 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">跑腿佣金</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.commission" type="digit" placeholder="最低1.0元" />
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">代送</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 顶部大图 -->
|
||||
<view class="top-banner" v-if="bannerUrl">
|
||||
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<!-- 1.代送物品 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">1.代送物品</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.itemName" placeholder="请输入要代送的物品名" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2.取货地点 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">2.取货地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.pickupLocation" placeholder="请输入取货地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3.送达地点 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">3.送达地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.deliveryLocation" placeholder="请输入送达地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4.备注信息 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">4.备注信息</text>
|
||||
<view class="input-box textarea-box">
|
||||
<textarea v-model="form.remark" placeholder="请输入备注信息,如注意事项等" placeholder-class="placeholder" :maxlength="200" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 5.如何联系您 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">5.如何联系您?</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 6.跑腿佣金 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">6.跑腿佣金</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金,最低1.0元" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<text class="form-tip">佣金先由平台保管,接单方完成订单后才会收到佣金</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">确定</button>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">
|
||||
支付佣金 确定下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createOrder } from '../../utils/api'
|
||||
import { createOrder, getPageBanner } from '../../utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: '',
|
||||
form: {
|
||||
itemName: '',
|
||||
pickupLocation: '',
|
||||
|
|
@ -61,97 +97,79 @@ export default {
|
|||
submitting: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 支付金额 = 跑腿佣金(代送类型) */
|
||||
payAmount() {
|
||||
const c = parseFloat(this.form.commission)
|
||||
return isNaN(c) ? '0.0' : c.toFixed(1)
|
||||
}
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
},
|
||||
methods: {
|
||||
/** 校验佣金 */
|
||||
goBack() { uni.navigateBack() },
|
||||
async loadBanner() {
|
||||
try {
|
||||
const res = await getPageBanner('delivery')
|
||||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
validateCommission() {
|
||||
const val = this.form.commission
|
||||
if (!val) {
|
||||
uni.showToast({ title: '请输入跑腿佣金', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入跑腿佣金', icon: 'none' }); return false
|
||||
}
|
||||
const num = parseFloat(val)
|
||||
if (isNaN(num) || num < 1.0) {
|
||||
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' }); return false
|
||||
}
|
||||
if (val.includes('.') && val.split('.')[1].length > 1) {
|
||||
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' }); return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
/** 校验表单 */
|
||||
validateForm() {
|
||||
if (!this.form.itemName.trim()) {
|
||||
uni.showToast({ title: '请输入代送物品', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入代送物品', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.pickupLocation.trim()) {
|
||||
uni.showToast({ title: '请输入取货地点', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入取货地点', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.deliveryLocation.trim()) {
|
||||
uni.showToast({ title: '请输入送达地点', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入送达地点', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.phone.trim()) {
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' }); return false
|
||||
}
|
||||
return this.validateCommission()
|
||||
},
|
||||
|
||||
/** 提交订单 */
|
||||
async onSubmit() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.navigateTo({ url: '/pages/login/login' }); return
|
||||
}
|
||||
this.submitting = true
|
||||
try {
|
||||
const commission = parseFloat(this.form.commission)
|
||||
const orderData = {
|
||||
const result = await createOrder({
|
||||
orderType: 'Delivery',
|
||||
itemName: this.form.itemName.trim(),
|
||||
pickupLocation: this.form.pickupLocation.trim(),
|
||||
deliveryLocation: this.form.deliveryLocation.trim(),
|
||||
remark: this.form.remark.trim(),
|
||||
phone: this.form.phone.trim(),
|
||||
commission: commission,
|
||||
commission,
|
||||
totalAmount: commission
|
||||
}
|
||||
|
||||
const result = await createOrder(orderData)
|
||||
|
||||
if (result.paymentParams) {
|
||||
await this.wxPay(result.paymentParams)
|
||||
}
|
||||
|
||||
})
|
||||
if (result.paymentParams) await this.wxPay(result.paymentParams)
|
||||
uni.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => { uni.navigateBack() }, 1500)
|
||||
} catch (e) {
|
||||
// 错误已在 request 中处理
|
||||
} finally {
|
||||
this.submitting = false
|
||||
}
|
||||
} catch (e) {} finally { this.submitting = false }
|
||||
},
|
||||
|
||||
/** 调用微信支付 */
|
||||
wxPay(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
...params,
|
||||
success: resolve,
|
||||
...params, success: resolve,
|
||||
fail: (err) => {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel')
|
||||
uni.showToast({ title: '支付失败', icon: 'none' })
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
|
@ -163,94 +181,124 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
padding-bottom: 40rpx;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.commission-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.commission-unit {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
margin-right: 8rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commission-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.pay-amount {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
/* 顶部大图 */
|
||||
.top-banner {
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 330rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
padding: 10rpx 24rpx 0;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.form-label {
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.input-box {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input-box input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.textarea-box {
|
||||
height: 240rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.textarea-box textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.form-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-top: 12rpx;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 提交按钮 */
|
||||
.submit-section {
|
||||
padding: 20rpx 24rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
background: #FAD146;
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 10rpx;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
.submit-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="detail-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">美食街订单详情</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
|
||||
<view v-else-if="order" class="detail-content">
|
||||
|
|
@ -105,7 +116,8 @@ export default {
|
|||
showCertModal: false,
|
||||
certForm: { realName: '', phone: '' },
|
||||
certSubmitting: false,
|
||||
certStatus: null
|
||||
certStatus: null,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -128,10 +140,13 @@ export default {
|
|||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.orderId = options.id
|
||||
this.loadDetail()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载订单详情 */
|
||||
async loadDetail() {
|
||||
this.loading = true
|
||||
|
|
@ -210,6 +225,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.detail-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 180rpx;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="food-order-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">美食街订单</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 订单商品摘要 -->
|
||||
<view class="order-summary">
|
||||
<text class="section-title">订单商品</text>
|
||||
|
|
@ -65,7 +76,8 @@ export default {
|
|||
phone: '',
|
||||
commission: ''
|
||||
},
|
||||
submitting: false
|
||||
submitting: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -90,6 +102,8 @@ export default {
|
|||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
// 如果购物车为空,返回上一页
|
||||
if (this.cartStore.totalCount === 0) {
|
||||
uni.showToast({ title: '购物车为空', icon: 'none' })
|
||||
|
|
@ -97,6 +111,7 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 校验佣金 */
|
||||
validateCommission() {
|
||||
const val = this.form.commission
|
||||
|
|
@ -196,6 +211,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.food-order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
<template>
|
||||
<view class="food-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">美食街</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 顶部大图 -->
|
||||
<view class="top-banner" v-if="bannerUrl">
|
||||
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 门店列表 -->
|
||||
<view class="shop-list">
|
||||
<view
|
||||
|
|
@ -25,19 +42,31 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getShops } from '../../utils/api'
|
||||
import { getShops, getPageBanner } from '../../utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
shops: [],
|
||||
loading: true
|
||||
loading: true,
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: ''
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
this.loadShops()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
async loadBanner() {
|
||||
try {
|
||||
const res = await getPageBanner('food')
|
||||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
/** 加载门店列表 */
|
||||
async loadShops() {
|
||||
this.loading = true
|
||||
|
|
@ -60,6 +89,53 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
/* 顶部大图 */
|
||||
.top-banner {
|
||||
padding: 0 24rpx 0;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.food-page {
|
||||
padding: 24rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="shop-detail-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">门店详情</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 门店 Banner -->
|
||||
<swiper
|
||||
v-if="shop.banners && shop.banners.length > 0"
|
||||
|
|
@ -111,7 +122,8 @@ export default {
|
|||
return {
|
||||
shop: {},
|
||||
dishes: [],
|
||||
showCartPopup: false
|
||||
showCartPopup: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -146,11 +158,14 @@ export default {
|
|||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
if (options.id) {
|
||||
this.loadShopDetail(options.id)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载门店详情 */
|
||||
async loadShopDetail(id) {
|
||||
try {
|
||||
|
|
@ -200,6 +215,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.shop-detail-page {
|
||||
padding-bottom: 140rpx;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,45 +1,71 @@
|
|||
<template>
|
||||
<view class="order-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">万能帮</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 顶部大图 -->
|
||||
<view class="top-banner" v-if="bannerUrl">
|
||||
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<!-- 要做的事情 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">要做的事情</text>
|
||||
<textarea class="form-textarea" v-model="form.itemName" placeholder="请描述您需要帮忙做的事情" />
|
||||
<!-- 1.要做的事情 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">1.要做的事情</text>
|
||||
<view class="input-box textarea-box">
|
||||
<textarea v-model="form.itemName" placeholder="请描述您需要帮忙做的事情" placeholder-class="placeholder" :maxlength="500" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入联系手机号" maxlength="11" />
|
||||
|
||||
<!-- 2.如何联系您 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">2.如何联系您?</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 代购商品总金额 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代购商品总金额</text>
|
||||
|
||||
<!-- 3.商品总金额 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">3.商品总金额</text>
|
||||
<text class="form-tip">需包含商品总额和包装费等额外费用</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" />
|
||||
<view class="input-box">
|
||||
<input v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 跑腿佣金 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">跑腿佣金</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.commission" type="digit" placeholder="最低1.0元" />
|
||||
|
||||
<!-- 4.跑腿佣金 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">4.跑腿佣金</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金,最低1.0元" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<text class="form-tip">佣金先由平台保管,接单方完成订单后才会收到佣金</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<!-- 支付金额 + 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">确定</button>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">
|
||||
支付佣金 确定下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createOrder } from '../../utils/api'
|
||||
import { createOrder, getPageBanner } from '../../utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -50,7 +76,9 @@ export default {
|
|||
goodsAmount: '',
|
||||
commission: ''
|
||||
},
|
||||
submitting: false
|
||||
submitting: false,
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -61,7 +89,19 @@ export default {
|
|||
return (goods + commission).toFixed(1)
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
async loadBanner() {
|
||||
try {
|
||||
const res = await getPageBanner('help')
|
||||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
/** 校验佣金 */
|
||||
validateCommission() {
|
||||
const val = this.form.commission
|
||||
|
|
@ -80,7 +120,6 @@ export default {
|
|||
}
|
||||
return true
|
||||
},
|
||||
|
||||
/** 校验表单 */
|
||||
validateForm() {
|
||||
if (!this.form.itemName.trim()) {
|
||||
|
|
@ -102,49 +141,42 @@ export default {
|
|||
}
|
||||
return this.validateCommission()
|
||||
},
|
||||
|
||||
/** 提交订单 */
|
||||
async onSubmit() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
// 未登录跳转登录页
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
return
|
||||
}
|
||||
|
||||
this.submitting = true
|
||||
try {
|
||||
const commission = parseFloat(this.form.commission)
|
||||
const goodsAmount = parseFloat(this.form.goodsAmount)
|
||||
const orderData = {
|
||||
const result = await createOrder({
|
||||
orderType: 'Help',
|
||||
itemName: this.form.itemName.trim(),
|
||||
phone: this.form.phone.trim(),
|
||||
goodsAmount: goodsAmount,
|
||||
commission: commission,
|
||||
goodsAmount,
|
||||
commission,
|
||||
totalAmount: goodsAmount + commission
|
||||
}
|
||||
|
||||
const result = await createOrder(orderData)
|
||||
|
||||
if (result.paymentParams) {
|
||||
await this.wxPay(result.paymentParams)
|
||||
}
|
||||
|
||||
})
|
||||
if (result.paymentParams) await this.wxPay(result.paymentParams)
|
||||
uni.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => { uni.navigateBack() }, 1500)
|
||||
} catch (e) {
|
||||
// 错误已在 request 中处理
|
||||
} finally {
|
||||
this.submitting = false
|
||||
}
|
||||
} catch (e) {} finally { this.submitting = false }
|
||||
},
|
||||
|
||||
/** 调用微信支付 */
|
||||
wxPay(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
...params,
|
||||
success: resolve,
|
||||
...params, success: resolve,
|
||||
fail: (err) => {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel')
|
||||
uni.showToast({ title: '支付失败', icon: 'none' })
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
|
@ -156,99 +188,137 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
padding-bottom: 40rpx;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
/* 顶部大图 */
|
||||
.top-banner {
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
padding: 10rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
border-bottom: none;
|
||||
.form-group {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-box input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.textarea-box {
|
||||
height: 240rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.textarea-box textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 12rpx;
|
||||
margin-top: 12rpx;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.commission-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.commission-unit {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
margin-right: 8rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commission-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 支付金额 + 提交按钮 */
|
||||
.submit-section {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pay-amount {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background-color: #007AFF;
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
background: #FAD146;
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 10rpx;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="login-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<text class="back-arrow">‹</text>
|
||||
</view>
|
||||
<text class="navbar-title">登录</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- Logo 和应用名称 -->
|
||||
<view class="login-header">
|
||||
<image class="login-logo" src="/static/logo.png" mode="aspectFit"></image>
|
||||
|
|
@ -11,12 +22,11 @@
|
|||
<view class="login-actions">
|
||||
<button
|
||||
class="login-btn"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="onGetPhoneNumber"
|
||||
@click="onWxLogin"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
>
|
||||
手机号快捷登录
|
||||
微信一键登录
|
||||
</button>
|
||||
<text class="login-tip">{{ tipText }}</text>
|
||||
</view>
|
||||
|
|
@ -31,91 +41,128 @@
|
|||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { login } from '../../utils/api'
|
||||
import { useUserStore } from '../../stores/user'
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { wxLogin } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tipText: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 微信手机号授权回调
|
||||
* */
|
||||
async onGetPhoneNumber(e) {
|
||||
// 用户拒绝授权
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
this.tipText = '需要授权手机号才能使用服务'
|
||||
return
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
const loading = ref(false)
|
||||
const tipText = ref('使用微信账号快速登录')
|
||||
const statusBarHeight = ref(0)
|
||||
|
||||
this.loading = true
|
||||
this.tipText = ''
|
||||
// 获取状态栏高度
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = sysInfo.statusBarHeight || 0
|
||||
|
||||
try {
|
||||
// 获取微信 login code
|
||||
const loginRes = await new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: resolve,
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
// 调用后端登录接口
|
||||
const result = await login({
|
||||
code: loginRes.code,
|
||||
encryptedData: e.detail.encryptedData,
|
||||
iv: e.detail.iv
|
||||
})
|
||||
|
||||
// 存储 token 和用户信息
|
||||
const userStore = useUserStore()
|
||||
userStore.setLoginInfo(result.token, result.userInfo || {})
|
||||
|
||||
// 跳转至首页
|
||||
uni.switchTab({ url: '/pages/index/index' })
|
||||
} catch (err) {
|
||||
// 登录失败提示
|
||||
this.tipText = '登录失败,请重试'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/** 跳转用户协议 */
|
||||
goAgreement() {
|
||||
uni.navigateTo({ url: '/pages/config/agreement' })
|
||||
},
|
||||
|
||||
/** 跳转隐私政策 */
|
||||
goPrivacy() {
|
||||
uni.navigateTo({ url: '/pages/config/privacy' })
|
||||
}
|
||||
/** 返回上一页 */
|
||||
function goBack() {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
uni.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
}
|
||||
|
||||
/** 微信一键登录 */
|
||||
async function onWxLogin() {
|
||||
loading.value = true
|
||||
tipText.value = '登录中...'
|
||||
try {
|
||||
// 调用 wx.login 获取 code
|
||||
const loginRes = await new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: resolve,
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
if (!loginRes?.code) {
|
||||
tipText.value = '获取微信授权失败,请重试'
|
||||
return
|
||||
}
|
||||
// 发送 code 到后端换取 token
|
||||
console.log('[登录] 获取到 code:', loginRes.code)
|
||||
const res = await wxLogin({ code: loginRes.code })
|
||||
if (res.token && res.userInfo) {
|
||||
userStore.setLoginInfo(res.token, res.userInfo)
|
||||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
} else {
|
||||
tipText.value = '登录失败,请重试'
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('登录失败:', e)
|
||||
tipText.value = '登录失败,请重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 跳转用户协议 */
|
||||
function goAgreement() {
|
||||
uni.navigateTo({ url: '/pages/config/agreement' })
|
||||
}
|
||||
|
||||
/** 跳转隐私政策 */
|
||||
function goPrivacy() {
|
||||
uni.navigateTo({ url: '/pages/config/privacy' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100vh;
|
||||
justify-content: center;
|
||||
background: linear-gradient(180deg, #FFF8E1 0%, #FFFFFF 40%);
|
||||
padding: 0 60rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-arrow {
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 200rpx;
|
||||
margin-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
|
|
@ -127,58 +174,59 @@ export default {
|
|||
.login-title {
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.login-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
border-radius: 44rpx;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
background: linear-gradient(135deg, #FFB700, #FF9500);
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 48rpx;
|
||||
border: none;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
.login-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.login-btn[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
margin-top: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #e64340;
|
||||
min-height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
color: #bbb;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
position: fixed;
|
||||
bottom: 60rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
font-size: 22rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
font-size: 24rpx;
|
||||
color: #007AFF;
|
||||
font-size: 22rpx;
|
||||
color: #FFB700;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="chat-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">聊天</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 顶部订单信息栏 -->
|
||||
<view class="order-bar" v-if="orderInfo.id" @click="goOrderDetail">
|
||||
<text class="order-bar-text">
|
||||
|
|
@ -214,7 +225,8 @@ export default {
|
|||
showPriceChangePopup: false,
|
||||
priceChangeType: '', // 'Commission' 或 'GoodsAmount'
|
||||
newPriceInput: '',
|
||||
submittingPriceChange: false
|
||||
submittingPriceChange: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -255,12 +267,15 @@ export default {
|
|||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.orderId = options.orderId
|
||||
this.targetUserId = options.targetUserId
|
||||
this.loadOrderInfo()
|
||||
this.loadChatMessages()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载订单信息 */
|
||||
async loadOrderInfo() {
|
||||
if (!this.orderId) return
|
||||
|
|
@ -516,6 +531,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.chat-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
<template>
|
||||
<view class="message-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<text class="navbar-title">消息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<view class="message-content">
|
||||
<!-- 系统消息入口 -->
|
||||
<view class="entry-item" @click="goSystemMsg">
|
||||
<view class="entry-icon system-icon">
|
||||
|
|
@ -56,6 +66,7 @@
|
|||
<view v-if="chatList.length === 0" class="empty-chat">
|
||||
<text class="empty-text">暂无聊天记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -65,6 +76,7 @@ import { getUnreadCount } from '../../utils/api'
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: 0,
|
||||
unreadCount: {
|
||||
systemUnread: 0,
|
||||
orderNotificationUnread: 0,
|
||||
|
|
@ -75,6 +87,8 @@ export default {
|
|||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadUnreadCount()
|
||||
this.loadChatList()
|
||||
this.updateTabBarBadge()
|
||||
|
|
@ -149,6 +163,34 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.message-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: linear-gradient(to right, #FFB700, #FFD59B);
|
||||
}
|
||||
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="order-notify-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">订单通知</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 分类标签 -->
|
||||
<view class="tabs">
|
||||
<view
|
||||
|
|
@ -56,13 +67,17 @@ export default {
|
|||
{ label: '已取消', value: 'cancelled' }
|
||||
],
|
||||
notifications: [],
|
||||
loading: false
|
||||
loading: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadNotifications()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 切换分类标签 */
|
||||
switchTab(value) {
|
||||
this.currentTab = value
|
||||
|
|
@ -117,6 +132,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.order-notify-page {
|
||||
padding: 0 24rpx 20rpx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="system-msg-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">系统消息</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 系统消息列表 -->
|
||||
<view
|
||||
class="msg-item"
|
||||
|
|
@ -59,13 +70,17 @@ export default {
|
|||
messages: [],
|
||||
loading: false,
|
||||
showDetail: false,
|
||||
detailData: {}
|
||||
detailData: {},
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadMessages()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载系统消息列表 */
|
||||
async loadMessages() {
|
||||
this.loading = true
|
||||
|
|
@ -110,6 +125,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.system-msg-page {
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="record-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">收益记录</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="records.length === 0" class="empty-tip">
|
||||
<text>暂无收益记录</text>
|
||||
|
|
@ -44,13 +55,17 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
records: [],
|
||||
loading: false
|
||||
loading: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadRecords()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载收益记录 */
|
||||
async loadRecords() {
|
||||
this.loading = true
|
||||
|
|
@ -80,6 +95,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.record-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="earnings-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">我的收益</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 四种金额状态展示 -->
|
||||
<view class="amount-section">
|
||||
<view class="amount-grid">
|
||||
|
|
@ -146,13 +157,17 @@ export default {
|
|||
withdrawSubmitting: false,
|
||||
// 提现说明弹窗
|
||||
showGuideModal: false,
|
||||
guideContent: ''
|
||||
guideContent: '',
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载收益和提现数据 */
|
||||
async loadData() {
|
||||
try {
|
||||
|
|
@ -276,6 +291,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.earnings-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,85 +1,95 @@
|
|||
<template>
|
||||
<view class="mine-page">
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="user-header">
|
||||
<!-- 顶部渐变导航栏 -->
|
||||
<view class="header-bg">
|
||||
<view :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="nav-bar">
|
||||
<text class="nav-title">我的</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card" @click="onUserClick">
|
||||
<image class="user-avatar" :src="userInfo.avatarUrl || '/static/logo.png'" mode="aspectFill"></image>
|
||||
<text class="user-name">{{ userInfo.nickname || '用户' }}</text>
|
||||
<text class="user-name">{{ isLoggedIn ? (userInfo.nickname || '用户') : '点击注册/登录' }}</text>
|
||||
<image class="arrow-icon" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
|
||||
<!-- 订单统计 -->
|
||||
<view class="stats-section">
|
||||
<view class="stats-row">
|
||||
<view class="stats-block" @click="goMyOrders">
|
||||
<text class="stats-title">我的下单</text>
|
||||
<view class="stats-nums">
|
||||
<view class="stats-item">
|
||||
<text class="stats-num">{{ stats.orderCount }}</text>
|
||||
<text class="stats-label">下单数</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="stats-num">{{ stats.orderCompleted }}</text>
|
||||
<text class="stats-label">完成数</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 我的订单 -->
|
||||
<view class="card" @click="goMyOrders">
|
||||
<view class="card-header">
|
||||
<text class="card-title">我的订单</text>
|
||||
<image class="arrow-icon" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="card-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">进行中</text>
|
||||
<text class="stat-num">{{ stats.orderOngoing }}</text>
|
||||
</view>
|
||||
<view class="stats-block" @click="goMyTaken">
|
||||
<text class="stats-title">我的接单</text>
|
||||
<view class="stats-nums">
|
||||
<view class="stats-item">
|
||||
<text class="stats-num">{{ stats.takenCount }}</text>
|
||||
<text class="stats-label">接单数</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="stats-num">{{ stats.takenCompleted }}</text>
|
||||
<text class="stats-label">完成数</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">已完成</text>
|
||||
<text class="stat-num">{{ stats.orderCompleted }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能入口 -->
|
||||
<view class="menu-section">
|
||||
<view class="menu-item" @click="contactService">
|
||||
<text class="menu-icon">📞</text>
|
||||
<text class="menu-text">联系客服</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<!-- 我的接单 -->
|
||||
<view class="card" @click="goMyTaken">
|
||||
<view class="card-header">
|
||||
<text class="card-title">我的接单</text>
|
||||
<image class="arrow-icon" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="card-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">进行中</text>
|
||||
<text class="stat-num">{{ stats.takenOngoing }}</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">已完成</text>
|
||||
<text class="stat-num">{{ stats.takenCompleted }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 菜单列表 -->
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goQrcode">
|
||||
<text class="menu-icon">📱</text>
|
||||
<text class="menu-text">客服二维码</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<image class="menu-icon" src="/static/ic_customer_service.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">联系客服</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="menu-item" @click="goCertification">
|
||||
<text class="menu-icon">🏃</text>
|
||||
<image class="menu-icon" src="/static/ic_certification.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">跑腿认证</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="menu-item" @click="goEarnings">
|
||||
<text class="menu-icon">💰</text>
|
||||
<image class="menu-icon" src="/static/ic_revenue.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">我的收益</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-section">
|
||||
<view class="menu-item" @click="goAgreement">
|
||||
<text class="menu-icon">📄</text>
|
||||
<image class="menu-icon" src="/static/ic_agreement1.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">用户协议</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="menu-item" @click="goPrivacy">
|
||||
<text class="menu-icon">🔒</text>
|
||||
<text class="menu-text">隐私政策</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
<image class="menu-icon" src="/static/ic_agreement2.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">隐私协议</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="menu-item" @click="goRunnerAgreement">
|
||||
<image class="menu-icon" src="/static/ic_agreement3.png" mode="aspectFit"></image>
|
||||
<text class="menu-text">跑腿协议</text>
|
||||
<image class="menu-arrow" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section">
|
||||
<view class="logout-btn" @click="onLogout">
|
||||
<text>退出登录</text>
|
||||
</view>
|
||||
<view v-if="isLoggedIn" class="logout-section" @click="onLogout">
|
||||
<text class="logout-text">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -92,18 +102,25 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
userInfo: {},
|
||||
isLoggedIn: false,
|
||||
statusBarHeight: 0,
|
||||
stats: {
|
||||
orderCount: 0,
|
||||
orderOngoing: 0,
|
||||
orderCompleted: 0,
|
||||
takenCount: 0,
|
||||
takenOngoing: 0,
|
||||
takenCompleted: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
const userStore = useUserStore()
|
||||
this.userInfo = userStore.userInfo || {}
|
||||
this.loadStats()
|
||||
this.isLoggedIn = userStore.isLoggedIn
|
||||
if (this.isLoggedIn) {
|
||||
this.loadStats()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 加载订单统计 */
|
||||
|
|
@ -115,57 +132,31 @@ export default {
|
|||
])
|
||||
const orders = ordersRes?.items || ordersRes || []
|
||||
const taken = takenRes?.items || takenRes || []
|
||||
this.stats.orderCount = orders.length
|
||||
this.stats.orderOngoing = orders.filter(o => o.status !== 'Completed' && o.status !== 'Cancelled').length
|
||||
this.stats.orderCompleted = orders.filter(o => o.status === 'Completed').length
|
||||
this.stats.takenCount = taken.length
|
||||
this.stats.takenOngoing = taken.filter(o => o.status !== 'Completed' && o.status !== 'Cancelled').length
|
||||
this.stats.takenCompleted = taken.filter(o => o.status === 'Completed').length
|
||||
} catch (e) {
|
||||
// 静默处理
|
||||
}
|
||||
},
|
||||
|
||||
/** 跳转我的订单 */
|
||||
goMyOrders() {
|
||||
uni.navigateTo({ url: '/pages/order/my-orders' })
|
||||
/** 点击用户区域 */
|
||||
onUserClick() {
|
||||
if (!this.isLoggedIn) {
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
}
|
||||
},
|
||||
|
||||
/** 跳转我的接单 */
|
||||
goMyTaken() {
|
||||
uni.navigateTo({ url: '/pages/order/my-taken' })
|
||||
},
|
||||
|
||||
/** 联系客服 */
|
||||
contactService() {
|
||||
// 跳转小程序自带客服页
|
||||
// uni-app 中通过 button open-type="contact" 实现,这里用 navigateTo 模拟
|
||||
uni.showToast({ title: '请使用客服按钮', icon: 'none' })
|
||||
},
|
||||
|
||||
/** 客服二维码 */
|
||||
goQrcode() {
|
||||
uni.navigateTo({ url: '/pages/config/qrcode' })
|
||||
},
|
||||
|
||||
/** 跑腿认证 */
|
||||
goCertification() {
|
||||
uni.navigateTo({ url: '/pages/runner/certification' })
|
||||
},
|
||||
|
||||
/** 我的收益 */
|
||||
goEarnings() {
|
||||
uni.navigateTo({ url: '/pages/mine/earnings' })
|
||||
},
|
||||
|
||||
/** 用户协议 */
|
||||
goAgreement() {
|
||||
goMyOrders() { uni.navigateTo({ url: '/pages/order/my-orders' }) },
|
||||
goMyTaken() { uni.navigateTo({ url: '/pages/order/my-taken' }) },
|
||||
goQrcode() { uni.navigateTo({ url: '/pages/config/qrcode' }) },
|
||||
goCertification() { uni.navigateTo({ url: '/pages/runner/certification' }) },
|
||||
goEarnings() { uni.navigateTo({ url: '/pages/mine/earnings' }) },
|
||||
goAgreement() { uni.navigateTo({ url: '/pages/config/agreement' }) },
|
||||
goPrivacy() { uni.navigateTo({ url: '/pages/config/privacy' }) },
|
||||
goRunnerAgreement() {
|
||||
// 跑腿协议,复用用户协议页面或单独页面
|
||||
uni.navigateTo({ url: '/pages/config/agreement' })
|
||||
},
|
||||
|
||||
/** 隐私政策 */
|
||||
goPrivacy() {
|
||||
uni.navigateTo({ url: '/pages/config/privacy' })
|
||||
},
|
||||
|
||||
/** 退出登录 */
|
||||
onLogout() {
|
||||
uni.showModal({
|
||||
|
|
@ -187,91 +178,127 @@ export default {
|
|||
.mine-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
/* 用户信息头部 */
|
||||
.user-header {
|
||||
/* 顶部渐变背景 */
|
||||
.header-bg {
|
||||
background: linear-gradient(180deg, #FFB700 0%, #FFD59B 100%);
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 60rpx 0 40rpx;
|
||||
background-color: #007AFF;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 4rpx solid #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 订单统计 */
|
||||
.stats-section {
|
||||
margin: -20rpx 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.stats-block {
|
||||
flex: 1;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.stats-nums {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-num {
|
||||
font-size: 36rpx;
|
||||
color: #007AFF;
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
margin-top: 4rpx;
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20rpx 30rpx 0;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
/* 菜单区域 */
|
||||
.menu-section {
|
||||
margin: 0 24rpx 24rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
.user-avatar {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 24rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
/* 卡片 */
|
||||
.card {
|
||||
margin: 20rpx 30rpx 0;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 32rpx;
|
||||
color: #FFB700;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-header .arrow-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.card-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.stat-num {
|
||||
font-size: 48rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 2rpx;
|
||||
height: 60rpx;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
/* 菜单列表 */
|
||||
.menu-list {
|
||||
margin: 20rpx 30rpx 0;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 28rpx 30rpx;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
|
|
@ -280,34 +307,31 @@ export default {
|
|||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 20rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #cccccc;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
/* 退出登录 */
|
||||
.logout-section {
|
||||
margin: 40rpx 24rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin: 40rpx 30rpx 120rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logout-btn text {
|
||||
.logout-text {
|
||||
font-size: 30rpx;
|
||||
color: #e64340;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="complete-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">完成订单确认</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 订单信息展示 -->
|
||||
<view class="info-section">
|
||||
<view class="info-title">
|
||||
|
|
@ -91,16 +102,20 @@ export default {
|
|||
proofImageUrl: '',
|
||||
submitting: false,
|
||||
// 是否为单主确认模式
|
||||
isOwnerConfirm: false
|
||||
isOwnerConfirm: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.orderId = options.id
|
||||
// mode=confirm 表示单主审核模式
|
||||
this.isOwnerConfirm = options.mode === 'confirm'
|
||||
this.loadOrderInfo()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载订单信息 */
|
||||
async loadOrderInfo() {
|
||||
if (!this.orderId) return
|
||||
|
|
@ -225,6 +240,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.complete-page {
|
||||
padding: 30rpx;
|
||||
min-height: 100vh;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="my-orders-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">我的订单</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 状态筛选标签 -->
|
||||
<scroll-view class="tab-bar" scroll-x>
|
||||
<view
|
||||
|
|
@ -179,7 +190,8 @@ export default {
|
|||
showReviewModal: false,
|
||||
reviewingOrder: null,
|
||||
reviewForm: { rating: 5, content: '' },
|
||||
reviewSubmitting: false
|
||||
reviewSubmitting: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -193,9 +205,12 @@ export default {
|
|||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadOrders()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载我的订单 */
|
||||
async loadOrders() {
|
||||
this.loading = true
|
||||
|
|
@ -314,6 +329,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.my-orders-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="my-taken-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">我的接单</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 状态筛选标签 -->
|
||||
<scroll-view class="tab-bar" scroll-x>
|
||||
<view
|
||||
|
|
@ -120,7 +131,8 @@ export default {
|
|||
currentStatus: '',
|
||||
currentType: '',
|
||||
orders: [],
|
||||
loading: false
|
||||
loading: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -133,9 +145,12 @@ export default {
|
|||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadOrders()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载我的接单 */
|
||||
async loadOrders() {
|
||||
this.loading = true
|
||||
|
|
@ -193,6 +208,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.my-taken-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="detail-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">订单详情</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 页面标题 -->
|
||||
<view class="page-title">
|
||||
<text>我的{{ getTypeLabel(order.orderType) }}订单详情</text>
|
||||
|
|
@ -209,7 +220,8 @@ export default {
|
|||
// 评价弹窗
|
||||
showReviewModal: false,
|
||||
reviewForm: { rating: 5, content: '' },
|
||||
reviewSubmitting: false
|
||||
reviewSubmitting: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -231,10 +243,13 @@ export default {
|
|||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.orderId = options.id
|
||||
this.loadDetail()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载订单详情 */
|
||||
async loadDetail() {
|
||||
if (!this.orderId) return
|
||||
|
|
@ -346,6 +361,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.detail-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
|
|
@ -1,55 +1,91 @@
|
|||
<template>
|
||||
<view class="order-page">
|
||||
<view class="form-section">
|
||||
<!-- 代取物品 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代取物品</text>
|
||||
<input class="form-input" v-model="form.itemName" placeholder="请输入代取物品" />
|
||||
</view>
|
||||
<!-- 代取地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代取地点</text>
|
||||
<input class="form-input" v-model="form.pickupLocation" placeholder="请输入代取地点" />
|
||||
</view>
|
||||
<!-- 送达地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">送达地点</text>
|
||||
<input class="form-input" v-model="form.deliveryLocation" placeholder="请输入送达地点" />
|
||||
</view>
|
||||
<!-- 备注信息 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注信息</text>
|
||||
<textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注信息(选填)" />
|
||||
</view>
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入联系手机号" maxlength="11" />
|
||||
</view>
|
||||
<!-- 跑腿佣金 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">跑腿佣金</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.commission" type="digit" placeholder="最低1.0元" />
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">代取</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 顶部大图 -->
|
||||
<view class="top-banner" v-if="bannerUrl">
|
||||
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<!-- 1.代取物品 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">1.代取物品</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.itemName" placeholder="请输入要代取的物品名" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2.取货地点 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">2.取货地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.pickupLocation" placeholder="请输入代取地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3.送达地点 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">3.送达地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.deliveryLocation" placeholder="请输入送达地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4.备注信息 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">4.备注信息</text>
|
||||
<view class="input-box textarea-box">
|
||||
<textarea v-model="form.remark" placeholder="请输入备注信息,如注意事项、订单号、取货码等" placeholder-class="placeholder" :maxlength="200" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 5.如何联系您 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">5.如何联系您?</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 6.跑腿佣金 -->
|
||||
<view class="form-group">
|
||||
<text class="form-label">6.跑腿佣金</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金,最低1.0元" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<text class="form-tip">佣金先由平台保管,接单方完成订单后才会收到佣金</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">确定</button>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">
|
||||
支付佣金 确定下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createOrder } from '../../utils/api'
|
||||
import { createOrder, getPageBanner } from '../../utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: '',
|
||||
form: {
|
||||
itemName: '',
|
||||
pickupLocation: '',
|
||||
|
|
@ -61,15 +97,19 @@ export default {
|
|||
submitting: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 支付金额 = 跑腿佣金(代取类型) */
|
||||
payAmount() {
|
||||
const c = parseFloat(this.form.commission)
|
||||
return isNaN(c) ? '0.0' : c.toFixed(1)
|
||||
}
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
},
|
||||
methods: {
|
||||
/** 校验佣金 */
|
||||
goBack() { uni.navigateBack() },
|
||||
async loadBanner() {
|
||||
try {
|
||||
const res = await getPageBanner('pickup')
|
||||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
validateCommission() {
|
||||
const val = this.form.commission
|
||||
if (!val) {
|
||||
|
|
@ -81,82 +121,62 @@ export default {
|
|||
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
// 限制小数点后1位
|
||||
if (val.includes('.') && val.split('.')[1].length > 1) {
|
||||
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
/** 校验表单 */
|
||||
validateForm() {
|
||||
if (!this.form.itemName.trim()) {
|
||||
uni.showToast({ title: '请输入代取物品', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入代取物品', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.pickupLocation.trim()) {
|
||||
uni.showToast({ title: '请输入代取地点', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入代取地点', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.deliveryLocation.trim()) {
|
||||
uni.showToast({ title: '请输入送达地点', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入送达地点', icon: 'none' }); return false
|
||||
}
|
||||
if (!this.form.phone.trim()) {
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' })
|
||||
return false
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' }); return false
|
||||
}
|
||||
return this.validateCommission()
|
||||
},
|
||||
|
||||
/** 提交订单 */
|
||||
async onSubmit() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
// 未登录跳转登录页
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
return
|
||||
}
|
||||
|
||||
this.submitting = true
|
||||
try {
|
||||
// 拉起微信支付
|
||||
const commission = parseFloat(this.form.commission)
|
||||
const orderData = {
|
||||
const result = await createOrder({
|
||||
orderType: 'Pickup',
|
||||
itemName: this.form.itemName.trim(),
|
||||
pickupLocation: this.form.pickupLocation.trim(),
|
||||
deliveryLocation: this.form.deliveryLocation.trim(),
|
||||
remark: this.form.remark.trim(),
|
||||
phone: this.form.phone.trim(),
|
||||
commission: commission,
|
||||
commission,
|
||||
totalAmount: commission
|
||||
}
|
||||
|
||||
const result = await createOrder(orderData)
|
||||
|
||||
// 调用微信支付
|
||||
if (result.paymentParams) {
|
||||
await this.wxPay(result.paymentParams)
|
||||
}
|
||||
|
||||
})
|
||||
if (result.paymentParams) await this.wxPay(result.paymentParams)
|
||||
uni.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (e) {
|
||||
// 错误已在 request 中处理
|
||||
} finally {
|
||||
this.submitting = false
|
||||
}
|
||||
setTimeout(() => { uni.navigateBack() }, 1500)
|
||||
} catch (e) {} finally { this.submitting = false }
|
||||
},
|
||||
|
||||
/** 调用微信支付 */
|
||||
wxPay(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
...params,
|
||||
success: resolve,
|
||||
...params, success: resolve,
|
||||
fail: (err) => {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel')
|
||||
uni.showToast({ title: '支付失败', icon: 'none' })
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
|
@ -168,92 +188,128 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
padding-bottom: 40rpx;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.commission-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.commission-unit {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
margin-right: 8rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commission-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
.pay-amount {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
/* 顶部大图 */
|
||||
.top-banner {
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
.banner-img {
|
||||
width: 100%;
|
||||
height: 330rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
padding: 10rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-box input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.textarea-box {
|
||||
height: 240rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.textarea-box textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-top: 12rpx;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 提交按钮 */
|
||||
.submit-section {
|
||||
padding: 20rpx 24rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background-color: #007AFF;
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
background: #FAD146;
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 10rpx;
|
||||
border: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
|
|
|
|||
|
|
@ -1,59 +1,82 @@
|
|||
<template>
|
||||
<view class="order-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">代购</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
|
||||
<!-- 顶部大图 -->
|
||||
<view class="top-banner" v-if="bannerUrl">
|
||||
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<!-- 代购物品 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代购物品</text>
|
||||
<input class="form-input" v-model="form.itemName" placeholder="请输入代购物品" />
|
||||
</view>
|
||||
<!-- 买货地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">买货地点</text>
|
||||
<input class="form-input" v-model="form.pickupLocation" placeholder="请输入买货地点" />
|
||||
</view>
|
||||
<!-- 送达地点 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">送达地点</text>
|
||||
<input class="form-input" v-model="form.deliveryLocation" placeholder="请输入送达地点" />
|
||||
</view>
|
||||
<!-- 备注信息 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注信息</text>
|
||||
<textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注信息(选填)" />
|
||||
</view>
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入联系手机号" maxlength="11" />
|
||||
</view>
|
||||
<!-- 代购商品总金额 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">代购商品总金额</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" />
|
||||
<view class="form-group">
|
||||
<text class="form-label">1.代购物品</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.itemName" placeholder="请输入代购物品名" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 跑腿佣金 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">跑腿佣金</text>
|
||||
<view class="commission-input">
|
||||
<text class="commission-unit">¥</text>
|
||||
<input class="form-input commission-field" v-model="form.commission" type="digit" placeholder="最低1.0元" />
|
||||
<view class="form-group">
|
||||
<text class="form-label">2.买货地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.pickupLocation" placeholder="请输入买货地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">3.送达地点</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.deliveryLocation" placeholder="请输入送达地点" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">4.备注信息</text>
|
||||
<view class="input-box textarea-box">
|
||||
<textarea v-model="form.remark" placeholder="请输入备注信息,如规格、数量、品牌等" placeholder-class="placeholder" :maxlength="200" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">5.如何联系您?</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">6.商品总金额</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<text class="form-tip">需包含商品总额和包装费等额外费用</text>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">7.跑腿佣金</text>
|
||||
<view class="input-box">
|
||||
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金,最低1.0元" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<text class="form-tip">佣金先由平台保管,接单方完成订单后才会收到佣金</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<!-- 支付金额 + 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">确定</button>
|
||||
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">
|
||||
支付佣金 确定下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createOrder } from '../../utils/api'
|
||||
import { createOrder, getPageBanner } from '../../utils/api'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -67,7 +90,9 @@ export default {
|
|||
goodsAmount: '',
|
||||
commission: ''
|
||||
},
|
||||
submitting: false
|
||||
submitting: false,
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -78,101 +103,67 @@ export default {
|
|||
return (goods + commission).toFixed(1)
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
},
|
||||
methods: {
|
||||
/** 校验佣金 */
|
||||
goBack() { uni.navigateBack() },
|
||||
async loadBanner() {
|
||||
try {
|
||||
const res = await getPageBanner('purchase')
|
||||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
validateCommission() {
|
||||
const val = this.form.commission
|
||||
if (!val) {
|
||||
uni.showToast({ title: '请输入跑腿佣金', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!val) { uni.showToast({ title: '请输入跑腿佣金', icon: 'none' }); return false }
|
||||
const num = parseFloat(val)
|
||||
if (isNaN(num) || num < 1.0) {
|
||||
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (val.includes('.') && val.split('.')[1].length > 1) {
|
||||
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (isNaN(num) || num < 1.0) { uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' }); return false }
|
||||
if (val.includes('.') && val.split('.')[1].length > 1) { uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' }); return false }
|
||||
return true
|
||||
},
|
||||
|
||||
/** 校验表单 */
|
||||
validateForm() {
|
||||
if (!this.form.itemName.trim()) {
|
||||
uni.showToast({ title: '请输入代购物品', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.pickupLocation.trim()) {
|
||||
uni.showToast({ title: '请输入买货地点', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.deliveryLocation.trim()) {
|
||||
uni.showToast({ title: '请输入送达地点', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.phone.trim()) {
|
||||
uni.showToast({ title: '请输入手机号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.goodsAmount) {
|
||||
uni.showToast({ title: '请输入商品总金额', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.form.itemName.trim()) { uni.showToast({ title: '请输入代购物品', icon: 'none' }); return false }
|
||||
if (!this.form.pickupLocation.trim()) { uni.showToast({ title: '请输入买货地点', icon: 'none' }); return false }
|
||||
if (!this.form.deliveryLocation.trim()) { uni.showToast({ title: '请输入送达地点', icon: 'none' }); return false }
|
||||
if (!this.form.phone.trim()) { uni.showToast({ title: '请输入手机号', icon: 'none' }); return false }
|
||||
if (!this.form.goodsAmount) { uni.showToast({ title: '请输入商品总金额', icon: 'none' }); return false }
|
||||
const goodsNum = parseFloat(this.form.goodsAmount)
|
||||
if (isNaN(goodsNum) || goodsNum <= 0) {
|
||||
uni.showToast({ title: '请输入正确的商品总金额', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (isNaN(goodsNum) || goodsNum <= 0) { uni.showToast({ title: '请输入正确的商品总金额', icon: 'none' }); return false }
|
||||
return this.validateCommission()
|
||||
},
|
||||
|
||||
/** 提交订单 */
|
||||
async onSubmit() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) { uni.navigateTo({ url: '/pages/login/login' }); return }
|
||||
this.submitting = true
|
||||
try {
|
||||
const commission = parseFloat(this.form.commission)
|
||||
const goodsAmount = parseFloat(this.form.goodsAmount)
|
||||
const orderData = {
|
||||
const result = await createOrder({
|
||||
orderType: 'Purchase',
|
||||
itemName: this.form.itemName.trim(),
|
||||
pickupLocation: this.form.pickupLocation.trim(),
|
||||
deliveryLocation: this.form.deliveryLocation.trim(),
|
||||
remark: this.form.remark.trim(),
|
||||
phone: this.form.phone.trim(),
|
||||
goodsAmount: goodsAmount,
|
||||
commission: commission,
|
||||
goodsAmount, commission,
|
||||
totalAmount: goodsAmount + commission
|
||||
}
|
||||
|
||||
const result = await createOrder(orderData)
|
||||
|
||||
if (result.paymentParams) {
|
||||
await this.wxPay(result.paymentParams)
|
||||
}
|
||||
|
||||
})
|
||||
if (result.paymentParams) await this.wxPay(result.paymentParams)
|
||||
uni.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => { uni.navigateBack() }, 1500)
|
||||
} catch (e) {
|
||||
// 错误已在 request 中处理
|
||||
} finally {
|
||||
this.submitting = false
|
||||
}
|
||||
} catch (e) {} finally { this.submitting = false }
|
||||
},
|
||||
|
||||
/** 调用微信支付 */
|
||||
wxPay(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
...params,
|
||||
success: resolve,
|
||||
...params, success: resolve,
|
||||
fail: (err) => {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||||
if (err.errMsg !== 'requestPayment:fail cancel')
|
||||
uni.showToast({ title: '支付失败', icon: 'none' })
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
|
@ -184,95 +175,37 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 200rpx;
|
||||
padding-bottom: 40rpx;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
.custom-navbar {
|
||||
position: fixed; top: 0; left: 0; width: 100%; z-index: 999; background: #FFB700;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
.navbar-content {
|
||||
height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
border-bottom: none;
|
||||
.nav-back { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
|
||||
.back-icon { width: 40rpx; height: 40rpx; }
|
||||
.navbar-title { font-size: 34rpx; font-weight: bold; color: #363636; }
|
||||
.nav-placeholder { width: 60rpx; }
|
||||
.top-banner { padding: 20rpx 24rpx 0; }
|
||||
.banner-img { width: 100%; height: 280rpx; border-radius: 20rpx; }
|
||||
.form-section { padding: 10rpx 24rpx 0; }
|
||||
.form-group { margin-bottom: 24rpx; }
|
||||
.form-label { font-size: 30rpx; color: #333333; font-weight: bold; display: block; margin-bottom: 16rpx; }
|
||||
.input-box {
|
||||
background-color: #ffffff; border-radius: 16rpx; padding: 0 24rpx; height: 88rpx; display: flex; align-items: center;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.commission-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.commission-unit {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
margin-right: 8rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commission-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pay-amount {
|
||||
font-size: 32rpx;
|
||||
color: #e64340;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input-box input { width: 100%; height: 100%; font-size: 28rpx; color: #333; }
|
||||
.textarea-box { height: 240rpx; padding: 20rpx 24rpx; align-items: flex-start; }
|
||||
.textarea-box textarea { width: 100%; height: 100%; font-size: 28rpx; color: #333; }
|
||||
.form-tip { font-size: 24rpx; color: #999999; margin-top: 12rpx; display: block; text-align: center; }
|
||||
.submit-section { padding: 20rpx 24rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); }
|
||||
.pay-amount { font-size: 32rpx; color: #e64340; font-weight: bold; display: block; text-align: center; margin-bottom: 20rpx; }
|
||||
.submit-btn {
|
||||
width: 240rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
opacity: 0.6;
|
||||
width: 100%; height: 96rpx; line-height: 96rpx; background: #FAD146; color: #ffffff;
|
||||
font-size: 32rpx; font-weight: bold; border-radius: 10rpx; border: none; text-align: center;
|
||||
}
|
||||
.submit-btn[disabled] { opacity: 0.6; }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<template>
|
||||
<view class="cert-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="nav-back" @click="goBack">
|
||||
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="navbar-title">跑腿认证</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 已认证状态 -->
|
||||
<view v-if="certStatus === 'Approved'" class="status-card success">
|
||||
<text class="status-icon">✓</text>
|
||||
|
|
@ -44,13 +55,17 @@ export default {
|
|||
return {
|
||||
certStatus: null,
|
||||
form: { realName: '', phone: '' },
|
||||
submitting: false
|
||||
submitting: false,
|
||||
statusBarHeight: 0
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadStatus()
|
||||
},
|
||||
methods: {
|
||||
goBack() { uni.navigateBack() },
|
||||
/** 加载认证状态 */
|
||||
async loadStatus() {
|
||||
try {
|
||||
|
|
@ -91,6 +106,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义导航栏 */
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
background: #FFB700;
|
||||
}
|
||||
.navbar-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.nav-back {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
}
|
||||
.nav-placeholder {
|
||||
width: 60rpx;
|
||||
}
|
||||
.cert-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
|||
BIN
miniapp/static/ic_agreement1.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
miniapp/static/ic_agreement2.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
miniapp/static/ic_agreement3.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
miniapp/static/ic_arrow.png
Normal file
|
After Width: | Height: | Size: 604 B |
BIN
miniapp/static/ic_back.png
Normal file
|
After Width: | Height: | Size: 648 B |
BIN
miniapp/static/ic_certification.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
miniapp/static/ic_customer_service.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
miniapp/static/ic_default.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
miniapp/static/ic_modify.png
Normal file
|
After Width: | Height: | Size: 752 B |
BIN
miniapp/static/ic_refresh.png
Normal file
|
After Width: | Height: | Size: 781 B |
BIN
miniapp/static/ic_revenue.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -34,7 +34,7 @@ export const useUserStore = defineStore('user', () => {
|
|||
token.value = ''
|
||||
userInfo.value = {}
|
||||
clearAuth()
|
||||
uni.reLaunch({ url: '/pages/login/login' })
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ export function login(data) {
|
|||
return request({ url: '/api/auth/login', method: 'POST', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信快捷登录(仅需 code)
|
||||
* @param {Object} data - { code }
|
||||
*/
|
||||
export function wxLogin(data) {
|
||||
return request({ url: '/api/auth/wx-login', method: 'POST', data })
|
||||
}
|
||||
|
||||
// ==================== Banner ====================
|
||||
|
||||
/** 获取已启用的 Banner 列表 */
|
||||
|
|
@ -188,3 +196,8 @@ export function getPrivacy() {
|
|||
export function getWithdrawalGuide() {
|
||||
return request({ url: '/api/config/withdrawal-guide' })
|
||||
}
|
||||
|
||||
/** 获取页面顶图配置 */
|
||||
export function getPageBanner(page) {
|
||||
return request({ url: `/api/config/page-banner/${page}` })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,16 @@ using System.ComponentModel.DataAnnotations;
|
|||
|
||||
namespace CampusErrand.Models.Dtos;
|
||||
|
||||
/// <summary>
|
||||
/// 微信快捷登录请求(仅需 code)
|
||||
/// </summary>
|
||||
public class WxLoginRequest
|
||||
{
|
||||
/// <summary>微信 wx.login() 返回的 code</summary>
|
||||
[Required(ErrorMessage = "code 不能为空")]
|
||||
public string Code { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信登录请求
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -151,6 +151,56 @@ app.MapPost("/api/auth/login", async (
|
|||
});
|
||||
});
|
||||
|
||||
// 微信快捷登录(仅需 code,通过 openid 注册/登录)
|
||||
app.MapPost("/api/auth/wx-login", async (
|
||||
WxLoginRequest request,
|
||||
IWeChatService weChatService,
|
||||
JwtService jwtService,
|
||||
AppDbContext db) =>
|
||||
{
|
||||
// 1. 调用微信 code2Session 获取 openid
|
||||
Console.WriteLine($"[wx-login] 收到 code: {request.Code}");
|
||||
var sessionResult = await weChatService.Code2SessionAsync(request.Code);
|
||||
if (!sessionResult.Success)
|
||||
{
|
||||
return Results.BadRequest(new { code = 400, message = sessionResult.ErrorMessage });
|
||||
}
|
||||
|
||||
// 2. 根据 openid 查找或创建用户
|
||||
var openId = sessionResult.OpenId!;
|
||||
var user = await db.Users.FirstOrDefaultAsync(u => u.OpenId == openId);
|
||||
if (user == null)
|
||||
{
|
||||
// 自动注册,手机号暂时留空
|
||||
var randomSuffix = Random.Shared.Next(1000, 9999).ToString();
|
||||
user = new User
|
||||
{
|
||||
OpenId = openId,
|
||||
Phone = "",
|
||||
Nickname = $"微信用户{randomSuffix}",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
db.Users.Add(user);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// 3. 生成 JWT 返回
|
||||
var token = jwtService.GenerateToken(user.Id, user.Role.ToString());
|
||||
|
||||
return Results.Ok(new LoginResponse
|
||||
{
|
||||
Token = token,
|
||||
UserInfo = new UserInfo
|
||||
{
|
||||
Id = user.Id,
|
||||
Phone = user.Phone,
|
||||
Nickname = user.Nickname,
|
||||
AvatarUrl = user.AvatarUrl,
|
||||
Role = user.Role.ToString()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 受保护的测试端点(用于验证认证和授权)
|
||||
app.MapGet("/api/protected", () => Results.Ok("ok"))
|
||||
.RequireAuthorization();
|
||||
|
|
@ -2511,6 +2561,19 @@ app.MapPost("/api/upload/image", async (IFormFile file, IConfiguration config) =
|
|||
|
||||
// ========== 系统配置接口 ==========
|
||||
|
||||
// 获取页面顶图配置
|
||||
app.MapGet("/api/config/page-banner/{page}", async (string page, AppDbContext db) =>
|
||||
{
|
||||
var key = $"page_banner_{page}";
|
||||
var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == key);
|
||||
return Results.Ok(new ConfigResponse
|
||||
{
|
||||
Key = key,
|
||||
Value = config?.Value ?? "",
|
||||
UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue
|
||||
});
|
||||
});
|
||||
|
||||
// 获取客服二维码
|
||||
app.MapGet("/api/config/qrcode", async (AppDbContext db) =>
|
||||
{
|
||||
|
|
@ -2577,7 +2640,9 @@ app.MapPut("/api/admin/config/{key}", async (string key, UpdateConfigRequest req
|
|||
// 允许的配置键白名单
|
||||
var allowedKeys = new HashSet<string>
|
||||
{
|
||||
"qrcode", "agreement", "privacy", "withdrawal_guide", "freeze_days"
|
||||
"qrcode", "agreement", "privacy", "withdrawal_guide", "freeze_days",
|
||||
"page_banner_pickup", "page_banner_delivery", "page_banner_help", "page_banner_purchase", "page_banner_food",
|
||||
"page_banner_order-hall"
|
||||
};
|
||||
|
||||
if (!allowedKeys.Contains(key))
|
||||
|
|
|
|||
|
|
@ -38,10 +38,12 @@ public class WeChatService : IWeChatService
|
|||
}
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
Console.WriteLine($"[WeChat] code2session 响应: {json}");
|
||||
var result = JsonSerializer.Deserialize<WeChatCode2SessionResponse>(json);
|
||||
|
||||
if (result == null || result.ErrCode != 0)
|
||||
{
|
||||
Console.WriteLine($"[WeChat] code2session 失败: errcode={result?.ErrCode}, errmsg={result?.ErrMsg}");
|
||||
return new WeChatSessionResult
|
||||
{
|
||||
Success = false,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
"ExpireMinutes": 10080
|
||||
},
|
||||
"WeChat": {
|
||||
"AppId": "your_wechat_appid",
|
||||
"AppSecret": "your_wechat_appsecret"
|
||||
"AppId": "wx626fd4a47944e3ea",
|
||||
"AppSecret": "352b4f8238e43df92d5131ba31e6f33d"
|
||||
},
|
||||
"Upload": {
|
||||
"MaxFileSizeBytes": 5242880,
|
||||
|
|
|
|||