- 新增订单管理模块,包括总订单和日订单的展示 - 优化样式,添加时间线和订单状态标签 - 实现图片上传功能,支持手套、鞋套、宠物照片等 - 添加打卡功能,支持前后对比照片上传 - 完善订单详情、宠物档案和服务档案的跳转逻辑pull/6/head
| @ -0,0 +1,17 @@ | |||||
| import request from '@/utils/request' | |||||
| // 获取订单列表 | |||||
| export function getAppOrderList() { | |||||
| return request({ | |||||
| headers: { | |||||
| "isToken": true | |||||
| }, | |||||
| url: "/applet/order/getAppOrderList", | |||||
| method: 'get' | |||||
| }) | |||||
| } | |||||
| export default { | |||||
| getAppOrderList | |||||
| } | |||||
| @ -0,0 +1,415 @@ | |||||
| <template> | |||||
| <up-list @scrolltolower="scrolltolower"> | |||||
| <template v-for="(item, index) in orderList" :key="index"> | |||||
| <view class="date-label"> | |||||
| <view class="date-box">{{ item.date }}</view> | |||||
| <view class="order-tag"> | |||||
| <template v-if="item.orders[0].status === 'completed'">已完成{{ item.orders.length }}单</template> | |||||
| <template v-else>待上门{{ item.orders.length }}单</template> | |||||
| </view> | |||||
| </view> | |||||
| <view class="timeline-container"> | |||||
| <view class="timeline-line" :class="{'timeline-line-completed': item.orders[0].status === 'completed'}"> | |||||
| </view> | |||||
| <up-list-item v-for="(order, orderIndex) in item.orders" :key="orderIndex"> | |||||
| <view class="timeline-item" :class="{'item-completed': order.status === 'completed'}"> | |||||
| <view class="timeline-dot"></view> | |||||
| <view class="mb28 container-list-item"> | |||||
| <view class="order-time-tag" | |||||
| :class="{'order-time-tag-completed' : order.status === 'completed'}"> | |||||
| <view class="time-box">{{ order.time }}</view> | |||||
| <view class="order-status"> | |||||
| <view class="status-icon"></view> | |||||
| <view> | |||||
| <template v-if="order.status === 'completed'">已完成{{ order.count }}单</template> | |||||
| <template v-else>待上门{{ order.count }}单</template> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="container-list"> | |||||
| <view class="flex-between flex"> | |||||
| <view class="address-info flex"> | |||||
| <view class="address-icon"></view> | |||||
| <view class="address-text">{{ order.address }}</view> | |||||
| </view> | |||||
| <view class="expand-btn" @click="toggleExpand(index, orderIndex)"> | |||||
| {{ order.isExpanded ? '收起' : '展开' }} <text | |||||
| class="expand-icon">{{ order.isExpanded ? '∧' : '∨' }}</text> | |||||
| </view> | |||||
| </view> | |||||
| <view v-if="order.isExpanded" class="pet-info"> | |||||
| <view class="pet-title">陪伴对象</view> | |||||
| <view class="pet-list"> | |||||
| <view v-for="(pet, petIndex) in order.pets" :key="petIndex" class="pet-item"> | |||||
| <view class="pet-avatar"> | |||||
| <up-image width="70px" height="70px" :src="pet.avatar" | |||||
| shape="circle"></up-image> | |||||
| </view> | |||||
| <view class="pet-detail"> | |||||
| <view class="pet-name">{{ pet.name }} <text | |||||
| class="pet-tag">{{ pet.tag }}</text></view> | |||||
| <view class="pet-desc">{{ pet.desc }}</view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="address-detail mb28">{{ order.fullAddress }}</view> | |||||
| <view class="action-buttons flex flex-between"> | |||||
| <up-button type="primary" text="打卡" v-if="false" @click="toClock" shape="circle" | |||||
| class="mr20" color="var(--status-color)"></up-button> | |||||
| <up-button type="primary" text="查看订单详情" @click="toDetail" shape="circle" | |||||
| class="mr20" color="var(--status-color)"></up-button> | |||||
| <up-button type="primary" text="查看宠物档案" @click="toPet" shape="circle" | |||||
| class="mr20" color="var(--status-color)"></up-button> | |||||
| <up-button type="primary" text="服务档案" @click="toService" shape="circle" | |||||
| color="var(--status-color)"></up-button> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </up-list-item> | |||||
| </view> | |||||
| </template> | |||||
| </up-list> | |||||
| </template> | |||||
| <script setup> | |||||
| import { | |||||
| ref, | |||||
| reactive, | |||||
| defineProps | |||||
| } from 'vue'; | |||||
| const { | |||||
| list | |||||
| } = defineProps({ | |||||
| list: { | |||||
| type: Array, | |||||
| required: true | |||||
| }, | |||||
| }); | |||||
| // 模拟订单数据 | |||||
| const orderList = reactive([{ | |||||
| date: '12-04', | |||||
| orders: [{ | |||||
| time: '12-04', | |||||
| count: 2, | |||||
| status: 'completed', // 已完成状态 | |||||
| address: '湖南省长沙市雨花区人民东路', | |||||
| fullAddress: '湖南省长沙市雨花区人民东路88号', | |||||
| isExpanded: true, | |||||
| pets: [{ | |||||
| avatar: 'https://cdn.catmdogd.com/Work/image/work/tx.png', | |||||
| name: '小咪', | |||||
| tag: '猫', | |||||
| desc: '中华田园犬(小型犬) | 专业喂养+提前熟悉+陪玩' | |||||
| }, | |||||
| { | |||||
| avatar: 'https://cdn.catmdogd.com/Work/image/work/tx.png', | |||||
| name: 'Billion', | |||||
| tag: '狗', | |||||
| desc: '狗狗(小型狗) | 上门喂养+陪玩' | |||||
| } | |||||
| ] | |||||
| }] | |||||
| }, | |||||
| { | |||||
| date: '12-05', | |||||
| orders: [{ | |||||
| time: '12-05', | |||||
| count: 1, | |||||
| status: 'completed', // 已完成状态 | |||||
| address: '湖南省长沙市雨花区人民东路', | |||||
| fullAddress: '湖南省长沙市雨花区人民东路88号', | |||||
| isExpanded: false, | |||||
| pets: [{ | |||||
| avatar: 'https://cdn.catmdogd.com/Work/image/work/tx.png', | |||||
| name: '小黑', | |||||
| tag: '狗', | |||||
| desc: '中华田园犬(小型犬) | 专业喂养+提前熟悉+陪玩' | |||||
| }] | |||||
| }] | |||||
| }, | |||||
| { | |||||
| date: '12-08', | |||||
| orders: [{ | |||||
| time: '12-08', | |||||
| count: 1, | |||||
| status: 'pending', // 待上门状态 | |||||
| address: '湖南省长沙市雨花区人民东路', | |||||
| fullAddress: '湖南省长沙市雨花区人民东路88号', | |||||
| isExpanded: false, | |||||
| pets: [{ | |||||
| avatar: 'https://cdn.catmdogd.com/Work/image/work/tx.png', | |||||
| name: '小白', | |||||
| tag: '猫', | |||||
| desc: '中华田园猫 | 专业喂养+提前熟悉+陪玩' | |||||
| }] | |||||
| }] | |||||
| } | |||||
| ]); | |||||
| // 展开/收起订单详情 | |||||
| function toggleExpand(dateIndex, orderIndex) { | |||||
| orderList[dateIndex].orders[orderIndex].isExpanded = !orderList[dateIndex].orders[orderIndex].isExpanded; | |||||
| } | |||||
| // 滚动到底部加载更多 | |||||
| const scrolltolower = () => { | |||||
| // 实际项目中这里应该调用API加载更多数据 | |||||
| console.log('加载更多数据'); | |||||
| } | |||||
| // 页面跳转函数 | |||||
| function toClock() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/myOrdersManage/clock/index" | |||||
| }) | |||||
| } | |||||
| function toDetail() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/orderTakingManage/detail/index" | |||||
| }) | |||||
| } | |||||
| function toPet() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/orderTakingManage/pet/index" | |||||
| }) | |||||
| } | |||||
| function toService() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/myOrdersManage/service/index" | |||||
| }) | |||||
| } | |||||
| </script> | |||||
| <style scoped lang="scss"> | |||||
| @import "../index"; | |||||
| .date-label { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin: 20rpx 0; | |||||
| position: relative; | |||||
| z-index: 3; | |||||
| .date-box { | |||||
| background: #333333; | |||||
| color: #FFFFFF; | |||||
| padding: 8rpx 16rpx; | |||||
| border-radius: 8rpx; | |||||
| font-size: 24rpx; | |||||
| margin-right: 20rpx; | |||||
| } | |||||
| .order-tag { | |||||
| color: #7D8196; | |||||
| font-size: 24rpx; | |||||
| } | |||||
| } | |||||
| .order-time-tag { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| background: rgba(#FFAA48, 0.2); | |||||
| padding: 22rpx 42rpx; | |||||
| .time-box { | |||||
| background: var(--status-color); | |||||
| color: #FFFFFF; | |||||
| padding: 4rpx 12rpx; | |||||
| border-radius: 8rpx; | |||||
| font-size: 24rpx; | |||||
| margin-right: 20rpx; | |||||
| } | |||||
| .order-status { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| font-size: 24rpx; | |||||
| .status-icon { | |||||
| width: 24rpx; | |||||
| height: 24rpx; | |||||
| background: var(--status-color); | |||||
| border-radius: 50%; | |||||
| margin-right: 10rpx; | |||||
| } | |||||
| .status-icon-completed { | |||||
| background: #4CD964; | |||||
| } | |||||
| } | |||||
| } | |||||
| .order-time-tag-completed { | |||||
| background: rgba(#4CD964, 0.2); | |||||
| } | |||||
| .time-box-completed { | |||||
| background: #4CD964; | |||||
| } | |||||
| .address-info { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin: 20rpx 0; | |||||
| .address-icon { | |||||
| width: 36rpx; | |||||
| height: 36rpx; | |||||
| background: var(--status-color); | |||||
| border-radius: 50%; | |||||
| margin-right: 12rpx; | |||||
| } | |||||
| .address-icon-completed { | |||||
| background: #4CD964; | |||||
| } | |||||
| .address-text { | |||||
| font-size: 28rpx; | |||||
| color: #333333; | |||||
| font-weight: 500; | |||||
| } | |||||
| } | |||||
| .expand-btn { | |||||
| font-size: 26rpx; | |||||
| color: #FFAA48; | |||||
| font-weight: 500; | |||||
| .expand-icon { | |||||
| margin-left: 8rpx; | |||||
| } | |||||
| } | |||||
| .expand-btn-completed { | |||||
| color: #4CD964; | |||||
| } | |||||
| .pet-info { | |||||
| margin-top: 20rpx; | |||||
| .pet-title { | |||||
| font-size: 28rpx; | |||||
| color: #333333; | |||||
| margin-bottom: 16rpx; | |||||
| } | |||||
| .pet-list { | |||||
| .pet-item { | |||||
| display: flex; | |||||
| margin-bottom: 20rpx; | |||||
| .pet-avatar { | |||||
| margin-right: 20rpx; | |||||
| flex-shrink: 0; | |||||
| } | |||||
| .pet-detail { | |||||
| .pet-name { | |||||
| font-size: 28rpx; | |||||
| color: #333333; | |||||
| margin-bottom: 10rpx; | |||||
| .pet-tag { | |||||
| background: var(--status-color); | |||||
| color: #FFFFFF; | |||||
| padding: 2rpx 10rpx; | |||||
| border-radius: 20rpx; | |||||
| font-size: 20rpx; | |||||
| margin-left: 10rpx; | |||||
| } | |||||
| } | |||||
| .pet-desc { | |||||
| font-size: 24rpx; | |||||
| color: #7D8196; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| .address-detail { | |||||
| background: #F6FAFC; | |||||
| border-radius: 8rpx; | |||||
| padding: 14rpx 20rpx; | |||||
| font-size: 24rpx; | |||||
| color: #7D8196; | |||||
| margin: 20rpx 0; | |||||
| } | |||||
| .action-buttons { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-top: 20rpx; | |||||
| } | |||||
| \t | |||||
| /* 时间线样式 */ | |||||
| .timeline-container { | |||||
| position: relative; | |||||
| padding-left: 40rpx; | |||||
| margin-bottom: 30rpx; | |||||
| z-index: 1; | |||||
| } | |||||
| .timeline-line { | |||||
| position: absolute; | |||||
| left: 20rpx; | |||||
| top: 0; | |||||
| height: 100%; | |||||
| width: 2rpx; | |||||
| background-color: #FFAA48; | |||||
| z-index: 0; | |||||
| } | |||||
| .timeline-line-completed { | |||||
| background-color: #4CD964; | |||||
| } | |||||
| .timeline-item { | |||||
| position: relative; | |||||
| padding-left: 30rpx; | |||||
| margin-bottom: 20rpx; | |||||
| } | |||||
| .timeline-dot { | |||||
| position: absolute; | |||||
| left: -10rpx; | |||||
| top: 30rpx; | |||||
| width: 20rpx; | |||||
| height: 20rpx; | |||||
| border-radius: 50%; | |||||
| background-color: #FFAA48; | |||||
| z-index: 2; | |||||
| border: 4rpx solid #FFF; | |||||
| } | |||||
| .timeline-dot-completed { | |||||
| background-color: #4CD964; | |||||
| } | |||||
| /* 默认状态颜色变量 */ | |||||
| .timeline-item { | |||||
| --status-color: #FFAA48; | |||||
| } | |||||
| /* 添加状态类,集中控制样式 */ | |||||
| .item-completed { | |||||
| --status-color: #4CD964; | |||||
| } | |||||
| </style> | |||||
| @ -1,73 +1,91 @@ | |||||
| <template> | <template> | ||||
| <up-list | |||||
| @scrolltolower="scrolltolower" | |||||
| > | |||||
| <up-list-item> | |||||
| <view class="mb28 container-list-item"> | |||||
| <view class="flex-between flex" style="background: #FFF4E5;padding: 22rpx 42rpx"> | |||||
| <view>待接单</view> | |||||
| <view>本单酬劳 | |||||
| <text style="color: #FF530A">¥260</text> | |||||
| </view> | |||||
| </view> | |||||
| <view class="container-list"> | |||||
| <view class="flex-between flex mb28"> | |||||
| <up-image style="flex-shrink:0" class="mr20" width="70px" height="70px" | |||||
| src="https://cdn.catmdogd.com/Work/image/work/tx.png" | |||||
| shape="circle"></up-image> | |||||
| <view> | |||||
| <view class="font28 col3">服务天数: 共2天 I 12-07,12-08</view> | |||||
| <view style="margin: 18rpx 0">期望上门时间:早</view> | |||||
| <view>中华田园犬(小型犬) | 专业喂养+提前熟悉+陪玩</view> | |||||
| </view> | |||||
| <up-list @scrolltolower="scrolltolower"> | |||||
| <up-list-item> | |||||
| <view class="mb28 container-list-item" | |||||
| v-for="(item, index) in 1"> | |||||
| <view class="flex-between flex" style="background: #FFF4E5;padding: 22rpx 42rpx"> | |||||
| <view>待接单</view> | |||||
| <view>本单酬劳 | |||||
| <text style="color: #FF530A">¥{{ item.orderAmount }}</text> | |||||
| </view> | |||||
| </view> | |||||
| <view class="container-list"> | |||||
| <view class="flex-between flex mb28"> | |||||
| <up-image style="flex-shrink:0" class="mr20" width="70px" height="70px" | |||||
| src="https://cdn.catmdogd.com/Work/image/work/tx.png" shape="circle"></up-image> | |||||
| <view> | |||||
| <view class="font28 col3">服务天数: 共{{ item.orderServiceTime }}天 I {{ item.orderServiceDate }}</view> | |||||
| <view style="margin: 18rpx 0">期望上门时间:早</view> | |||||
| <view>中华田园犬(小型犬) | 专业喂养+提前熟悉+陪玩</view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mb28 address">{{ item.orderVisitAddress }}</view> | |||||
| <view class="mb28">订单为系统派发,请确认订单信息后再抢单</view> | |||||
| <view class="flex flex-between"> | |||||
| <up-button type="primary" | |||||
| v-if="true" | |||||
| text="打卡" @click="toClock" shape="circle" class="mr20" | |||||
| color="#FFAA48"></up-button> | |||||
| </view> | |||||
| <view class="mb28 address">重庆市南岸区长嘉汇</view> | |||||
| <view class="mb28">订单为系统派发,请确认订单信息后再抢单</view> | |||||
| <view class="flex flex-between"> | |||||
| <up-button type="primary" text="查看订单详情" shape="circle" class="mr20" color="#FFAA48"></up-button> | |||||
| <up-button type="primary" text="查看宠物档案" shape="circle" class="mr20" color="#FFAA48"></up-button> | |||||
| <up-button type="primary" text="查看服务档案" shape="circle" color="#FFAA48"></up-button> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| <view class="mb28 container-list-item"> | |||||
| <view class="flex-between flex" style="background: #FFF4E5;padding: 22rpx 42rpx"> | |||||
| <view>待接单</view> | |||||
| <view>本单酬劳 | |||||
| <text style="color: #FF530A">¥260</text> | |||||
| </view> | |||||
| </view> | |||||
| <view class="container-list"> | |||||
| <view class="flex-between flex mb28"> | |||||
| <up-image style="flex-shrink:0" class="mr20" width="70px" height="70px" | |||||
| src="https://cdn.catmdogd.com/Work/image/work/tx.png" | |||||
| shape="circle"></up-image> | |||||
| <view> | |||||
| <view class="font28 col3">服务天数: 共2天 I 12-07,12-08</view> | |||||
| <view style="margin: 18rpx 0">期望上门时间:早</view> | |||||
| <view>中华田园犬(小型犬) | 专业喂养+提前熟悉+陪玩</view> | |||||
| </view> | |||||
| <up-button type="primary" | |||||
| v-else | |||||
| text="查看订单详情" @click="toDetail" shape="circle" class="mr20" | |||||
| color="#FFAA48"></up-button> | |||||
| </view> | |||||
| <view class="mb28 address">重庆市南岸区长嘉汇</view> | |||||
| <view class="mb28">订单为系统派发,请确认订单信息后再抢单</view> | |||||
| <view class="flex flex-between"> | |||||
| <up-button type="primary" text="查看订单详情" shape="circle" class="mr20" color="#FFAA48"></up-button> | |||||
| <up-button type="primary" text="查看宠物档案" shape="circle" class="mr20" color="#FFAA48"></up-button> | |||||
| <up-button type="primary" text="查看服务档案" shape="circle" color="#FFAA48"></up-button> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </up-list-item> | |||||
| </up-list> | |||||
| <up-button type="primary" text="查看宠物档案" @click="toPet" shape="circle" class="mr20" | |||||
| color="#FFAA48"></up-button> | |||||
| <up-button type="primary" text="查看服务档案" @click="toService" shape="circle" | |||||
| color="#FFAA48"></up-button> | |||||
| </view> | |||||
| </view> | |||||
| </view> | |||||
| </up-list-item> | |||||
| </up-list> | |||||
| </template> | </template> | ||||
| <script setup> | <script setup> | ||||
| const scrolltolower = () => { | |||||
| const scrolltolower = () => { | |||||
| } | |||||
| import { | |||||
| defineProps | |||||
| } from 'vue'; | |||||
| const { list } = defineProps({ | |||||
| list: { | |||||
| type: Array, | |||||
| required: true | |||||
| }, | |||||
| }); | |||||
| function toClock() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/myOrdersManage/clock/index" | |||||
| }) | |||||
| } | |||||
| function toDetail() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/orderTakingManage/detail/index" | |||||
| }) | |||||
| } | |||||
| function toPet() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/orderTakingManage/pet/index" | |||||
| }) | |||||
| } | |||||
| } | |||||
| function toService() { | |||||
| uni.navigateTo({ | |||||
| url: "/otherPages/myOrdersManage/service/index" | |||||
| }) | |||||
| } | |||||
| </script> | </script> | ||||
| <style scoped lang="scss"> | <style scoped lang="scss"> | ||||
| @import "../index"; | |||||
| @import "../index"; | |||||
| </style> | </style> | ||||
| @ -1,67 +1,119 @@ | |||||
| <template> | <template> | ||||
| <view> | |||||
| <view class="header-buts flex flex-center"> | |||||
| <view class="flex buts-box"> | |||||
| <view class="buts" :class="{'buts-active':activeIndex===1}" @click="activeIndex=1">总订单</view> | |||||
| <view class="buts" :class="{'buts-active':activeIndex===2}" @click="activeIndex=2">日订单</view> | |||||
| </view> | |||||
| </view> | |||||
| <up-sticky bgColor="#fff"> | |||||
| <view class="container-tabs"> | |||||
| <up-tabs :list="list" lineWidth="68rpx" | |||||
| :activeStyle="{ | |||||
| color: '#FFFFFF', | |||||
| fontWeight: 'bold', | |||||
| transform: 'scale(1.05)' | |||||
| }" | |||||
| :inactiveStyle="{ | |||||
| color: '#FFFFFF', | |||||
| transform: 'scale(1)' | |||||
| }" | |||||
| :itemStyle="{height:'88rpx',padding:'0 52rpx'}" | |||||
| lineColor="#FFFFFF"></up-tabs> | |||||
| </view> | |||||
| </up-sticky> | |||||
| <view> | |||||
| <view class="header-buts flex flex-center"> | |||||
| <view class="flex buts-box"> | |||||
| <view class="buts" :class="{'buts-active':activeIndex===1}" @click="activeIndex=1">总订单</view> | |||||
| <view class="buts" :class="{'buts-active':activeIndex===2}" @click="activeIndex=2">日订单</view> | |||||
| </view> | |||||
| </view> | |||||
| <up-sticky bgColor="#fff" v-if="activeIndex == 1"> | |||||
| <view class="container-tabs"> | |||||
| <up-tabs :list="tabList1" lineWidth="68rpx" :activeStyle="{ | |||||
| color: '#FFFFFF', | |||||
| fontWeight: 'bold', | |||||
| transform: 'scale(1.05)' | |||||
| }" :inactiveStyle="{ | |||||
| color: '#FFFFFF', | |||||
| transform: 'scale(1)' | |||||
| }" :itemStyle="{height:'88rpx',padding:'0 52rpx'}" lineColor="#FFFFFF"></up-tabs> | |||||
| </view> | |||||
| </up-sticky> | |||||
| <up-sticky bgColor="#fff" v-else> | |||||
| <view class="container-tabs"> | |||||
| <up-tabs :list="tabList2" | |||||
| lineWidth="68rpx" | |||||
| :activeStyle="{ | |||||
| color: '#FFFFFF', | |||||
| fontWeight: 'bold', | |||||
| transform: 'scale(1.05)' | |||||
| }" | |||||
| :inactiveStyle="{ | |||||
| color: '#FFFFFF', | |||||
| transform: 'scale(1)' | |||||
| }" | |||||
| :itemStyle="{height:'88rpx',padding:'0 52rpx', width : '400rpx'}" | |||||
| lineColor="#FFFFFF"></up-tabs> | |||||
| </view> | |||||
| </up-sticky> | |||||
| <view class="container"> | |||||
| <systemOrder v-if="current===0"/> | |||||
| <personOrder v-if="current===1"/> | |||||
| <lossOrder v-if="current===2"/> | |||||
| </view> | |||||
| </view> | |||||
| <view class="container"> | |||||
| <systemOrder :list="list" v-if="activeIndex == 1"/> | |||||
| <orderListByData :list="list" v-else/> | |||||
| </view> | |||||
| </view> | |||||
| </template> | </template> | ||||
| <script setup> | <script setup> | ||||
| import {reactive, ref} from "vue"; | |||||
| import systemOrder from "./components/systemOrder.vue"; | |||||
| import personOrder from "./components/personOrder.vue"; | |||||
| import lossOrder from "./components/lossOrder.vue"; | |||||
| import { | |||||
| reactive, | |||||
| ref | |||||
| } from "vue"; | |||||
| import systemOrder from "./components/systemOrder.vue"; | |||||
| import orderListByData from "./components/orderListByData.vue"; | |||||
| // import personOrder from "./components/personOrder.vue"; | |||||
| // import lossOrder from "./components/lossOrder.vue"; | |||||
| import { | |||||
| getAppOrderList, | |||||
| } from "@/api/order/order.js" | |||||
| const current = ref(0) | |||||
| const activeIndex = ref(1) | |||||
| const list = reactive([ | |||||
| { | |||||
| name: '系统派单', | |||||
| badge: { | |||||
| value: 5, | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: '个人订单', | |||||
| badge: { | |||||
| value: 5, | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: '流失订单', | |||||
| badge: { | |||||
| value: 5, | |||||
| } | |||||
| }, | |||||
| ]) | |||||
| const current = ref(0) | |||||
| const activeIndex = ref(1) | |||||
| const list = ref([]) | |||||
| const tabList1 = reactive([{ | |||||
| name: '待服务', | |||||
| badge: { | |||||
| // value: 5, | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: '进行中', | |||||
| badge: { | |||||
| // value: 5, | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: '已完成', | |||||
| badge: { | |||||
| // value: 5, | |||||
| } | |||||
| }, | |||||
| ]) | |||||
| const tabList2 = reactive([ | |||||
| { | |||||
| name: '待上门', | |||||
| badge: { | |||||
| // value: 5, | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: '已完成', | |||||
| badge: { | |||||
| // value: 5, | |||||
| } | |||||
| }, | |||||
| ]) | |||||
| function getList(){ | |||||
| getAppOrderList() | |||||
| .then(res => { | |||||
| if(res.code == 200){ | |||||
| list.value = res.rows | |||||
| } | |||||
| }) | |||||
| } | |||||
| getList() | |||||
| </script> | </script> | ||||
| <style scoped lang="scss"> | <style scoped lang="scss"> | ||||
| @import "index.scss"; | |||||
| </style> | |||||
| @import "index.scss"; | |||||
| </style> | |||||
| @ -0,0 +1,95 @@ | |||||
| export const Base64 = { | |||||
| _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", | |||||
| encode: function(input) { | |||||
| var output = ""; | |||||
| var chr1, chr2, chr3, enc1, enc2, enc3, enc4; | |||||
| var i = 0; | |||||
| input = Base64._utf8_encode(input); | |||||
| while (i < input.length) { | |||||
| chr1 = input.charCodeAt(i++); | |||||
| chr2 = input.charCodeAt(i++); | |||||
| chr3 = input.charCodeAt(i++); | |||||
| enc1 = chr1 >> 2; | |||||
| enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |||||
| enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |||||
| enc4 = chr3 & 63; | |||||
| if (isNaN(chr2)) { | |||||
| enc3 = enc4 = 64; | |||||
| } else if (isNaN(chr3)) { | |||||
| enc4 = 64; | |||||
| } | |||||
| output = output + | |||||
| this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + | |||||
| this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); | |||||
| } | |||||
| return output; | |||||
| }, | |||||
| decode: function(input) { | |||||
| var output = ""; | |||||
| var chr1, chr2, chr3; | |||||
| var enc1, enc2, enc3, enc4; | |||||
| var i = 0; | |||||
| input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | |||||
| while (i < input.length) { | |||||
| enc1 = this._keyStr.indexOf(input.charAt(i++)); | |||||
| enc2 = this._keyStr.indexOf(input.charAt(i++)); | |||||
| enc3 = this._keyStr.indexOf(input.charAt(i++)); | |||||
| enc4 = this._keyStr.indexOf(input.charAt(i++)); | |||||
| chr1 = (enc1 << 2) | (enc2 >> 4); | |||||
| chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | |||||
| chr3 = ((enc3 & 3) << 6) | enc4; | |||||
| output = output + String.fromCharCode(chr1); | |||||
| if (enc3 != 64) { | |||||
| output = output + String.fromCharCode(chr2); | |||||
| } | |||||
| if (enc4 != 64) { | |||||
| output = output + String.fromCharCode(chr3); | |||||
| } | |||||
| } | |||||
| output = Base64._utf8_decode(output); | |||||
| return output; | |||||
| }, | |||||
| _utf8_encode: function(string) { | |||||
| string = string.replace(/\r\n/g, "\n"); | |||||
| var utftext = ""; | |||||
| for (var n = 0; n < string.length; n++) { | |||||
| var c = string.charCodeAt(n); | |||||
| if (c < 128) { | |||||
| utftext += String.fromCharCode(c); | |||||
| } else if ((c > 127) && (c < 2048)) { | |||||
| utftext += String.fromCharCode((c >> 6) | 192); | |||||
| utftext += String.fromCharCode((c & 63) | 128); | |||||
| } else { | |||||
| utftext += String.fromCharCode((c >> 12) | 224); | |||||
| utftext += String.fromCharCode(((c >> 6) & 63) | 128); | |||||
| utftext += String.fromCharCode((c & 63) | 128); | |||||
| } | |||||
| } | |||||
| return utftext; | |||||
| }, | |||||
| _utf8_decode: function(utftext) { | |||||
| var string = ""; | |||||
| var i = 0; | |||||
| var c = c1 = c2 = 0; | |||||
| while (i < utftext.length) { | |||||
| c = utftext.charCodeAt(i); | |||||
| if (c < 128) { | |||||
| string += String.fromCharCode(c); | |||||
| i++; | |||||
| } else if ((c > 191) && (c < 224)) { | |||||
| c2 = utftext.charCodeAt(i + 1); | |||||
| string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); | |||||
| i += 2; | |||||
| } else { | |||||
| c2 = utftext.charCodeAt(i + 1); | |||||
| c3 = utftext.charCodeAt(i + 2); | |||||
| string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); | |||||
| i += 3; | |||||
| } | |||||
| } | |||||
| return string; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,117 @@ | |||||
| var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||||
| let Crypto = {}; | |||||
| var util = Crypto.util = { | |||||
| rotl: function(n, b) { | |||||
| return (n << b) | (n >>> (32 - b)); | |||||
| }, | |||||
| rotr: function(n, b) { | |||||
| return (n << (32 - b)) | (n >>> b); | |||||
| }, | |||||
| endian: function(n) { | |||||
| if (n.constructor == Number) { | |||||
| return util.rotl(n, 8) & 0x00FF00FF | | |||||
| util.rotl(n, 24) & 0xFF00FF00; | |||||
| } | |||||
| for (var i = 0; i < n.length; i++) | |||||
| n[i] = util.endian(n[i]); | |||||
| return n; | |||||
| }, | |||||
| randomBytes: function(n) { | |||||
| for (var bytes = []; n > 0; n--) | |||||
| bytes.push(Math.floor(Math.random() * 256)); | |||||
| return bytes; | |||||
| }, | |||||
| stringToBytes: function(str) { | |||||
| var bytes = []; | |||||
| for (var i = 0; i < str.length; i++) | |||||
| bytes.push(str.charCodeAt(i)); | |||||
| return bytes; | |||||
| }, | |||||
| bytesToString: function(bytes) { | |||||
| var str = []; | |||||
| for (var i = 0; i < bytes.length; i++) | |||||
| str.push(String.fromCharCode(bytes[i])); | |||||
| return str.join(""); | |||||
| }, | |||||
| stringToWords: function(str) { | |||||
| var words = []; | |||||
| for (var c = 0, b = 0; c < str.length; c++, b += 8) | |||||
| words[b >>> 5] |= str.charCodeAt(c) << (24 - b % 32); | |||||
| return words; | |||||
| }, | |||||
| bytesToWords: function(bytes) { | |||||
| var words = []; | |||||
| for (var i = 0, b = 0; i < bytes.length; i++, b += 8) | |||||
| words[b >>> 5] |= bytes[i] << (24 - b % 32); | |||||
| return words; | |||||
| }, | |||||
| wordsToBytes: function(words) { | |||||
| var bytes = []; | |||||
| for (var b = 0; b < words.length * 32; b += 8) | |||||
| bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); | |||||
| return bytes; | |||||
| }, | |||||
| bytesToHex: function(bytes) { | |||||
| var hex = []; | |||||
| for (var i = 0; i < bytes.length; i++) { | |||||
| hex.push((bytes[i] >>> 4).toString(16)); | |||||
| hex.push((bytes[i] & 0xF).toString(16)); | |||||
| } | |||||
| return hex.join(""); | |||||
| }, | |||||
| hexToBytes: function(hex) { | |||||
| var bytes = []; | |||||
| for (var c = 0; c < hex.length; c += 2) | |||||
| bytes.push(parseInt(hex.substr(c, 2), 16)); | |||||
| return bytes; | |||||
| }, | |||||
| bytesToBase64: function(bytes) { | |||||
| if (typeof btoa == "function") return btoa(util.bytesToString(bytes)); | |||||
| var base64 = [], | |||||
| overflow; | |||||
| for (var i = 0; i < bytes.length; i++) { | |||||
| switch (i % 3) { | |||||
| case 0: | |||||
| base64.push(base64map.charAt(bytes[i] >>> 2)); | |||||
| overflow = (bytes[i] & 0x3) << 4; | |||||
| break; | |||||
| case 1: | |||||
| base64.push(base64map.charAt(overflow | (bytes[i] >>> 4))); | |||||
| overflow = (bytes[i] & 0xF) << 2; | |||||
| break; | |||||
| case 2: | |||||
| base64.push(base64map.charAt(overflow | (bytes[i] >>> 6))); | |||||
| base64.push(base64map.charAt(bytes[i] & 0x3F)); | |||||
| overflow = -1; | |||||
| } | |||||
| } | |||||
| if (overflow != undefined && overflow != -1) | |||||
| base64.push(base64map.charAt(overflow)); | |||||
| while (base64.length % 4 != 0) base64.push("="); | |||||
| return base64.join(""); | |||||
| }, | |||||
| base64ToBytes: function(base64) { | |||||
| if (typeof atob == "function") return util.stringToBytes(atob(base64)); | |||||
| base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); | |||||
| var bytes = []; | |||||
| for (var i = 0; i < base64.length; i++) { | |||||
| switch (i % 4) { | |||||
| case 1: | |||||
| bytes.push((base64map.indexOf(base64.charAt(i - 1)) << 2) | | |||||
| (base64map.indexOf(base64.charAt(i)) >>> 4)); | |||||
| break; | |||||
| case 2: | |||||
| bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) << 4) | | |||||
| (base64map.indexOf(base64.charAt(i)) >>> 2)); | |||||
| break; | |||||
| case 3: | |||||
| bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) << 6) | | |||||
| (base64map.indexOf(base64.charAt(i)))); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return bytes; | |||||
| } | |||||
| }; | |||||
| Crypto.mode = {}; | |||||
| export default Crypto; | |||||
| @ -0,0 +1,29 @@ | |||||
| import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; | |||||
| (function() { | |||||
| // Shortcut | |||||
| var util = Crypto.util; | |||||
| Crypto.HMAC = function(hasher, message, key, options) { | |||||
| // Allow arbitrary length keys | |||||
| key = key.length > hasher._blocksize * 4 ? | |||||
| hasher(key, { | |||||
| asBytes: true | |||||
| }) : | |||||
| util.stringToBytes(key); | |||||
| // XOR keys with pad constants | |||||
| var okey = key, | |||||
| ikey = key.slice(0); | |||||
| for (var i = 0; i < hasher._blocksize * 4; i++) { | |||||
| okey[i] ^= 0x5C; | |||||
| ikey[i] ^= 0x36; | |||||
| } | |||||
| var hmacbytes = hasher(util.bytesToString(okey) + | |||||
| hasher(util.bytesToString(ikey) + message, { | |||||
| asString: true | |||||
| }), { | |||||
| asBytes: true | |||||
| }); | |||||
| return options && options.asBytes ? hmacbytes : | |||||
| options && options.asString ? util.bytesToString(hmacbytes) : | |||||
| util.bytesToHex(hmacbytes); | |||||
| }; | |||||
| })(); | |||||
| @ -0,0 +1,59 @@ | |||||
| import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; | |||||
| (function() { | |||||
| // Shortcut | |||||
| var util = Crypto.util; | |||||
| // Public API | |||||
| var SHA1 = Crypto.SHA1 = function(message, options) { | |||||
| var digestbytes = util.wordsToBytes(SHA1._sha1(message)); | |||||
| return options && options.asBytes ? digestbytes : | |||||
| options && options.asString ? util.bytesToString(digestbytes) : | |||||
| util.bytesToHex(digestbytes); | |||||
| }; | |||||
| // The core | |||||
| SHA1._sha1 = function(message) { | |||||
| var m = util.stringToWords(message), | |||||
| l = message.length * 8, | |||||
| w = [], | |||||
| H0 = 1732584193, | |||||
| H1 = -271733879, | |||||
| H2 = -1732584194, | |||||
| H3 = 271733878, | |||||
| H4 = -1009589776; | |||||
| // Padding | |||||
| m[l >> 5] |= 0x80 << (24 - l % 32); | |||||
| m[((l + 64 >>> 9) << 4) + 15] = l; | |||||
| for (var i = 0; i < m.length; i += 16) { | |||||
| var a = H0, | |||||
| b = H1, | |||||
| c = H2, | |||||
| d = H3, | |||||
| e = H4; | |||||
| for (var j = 0; j < 80; j++) { | |||||
| if (j < 16) w[j] = m[i + j]; | |||||
| else { | |||||
| var n = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16]; | |||||
| w[j] = (n << 1) | (n >>> 31); | |||||
| } | |||||
| var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + ( | |||||
| j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 : | |||||
| j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 : | |||||
| j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 : | |||||
| (H1 ^ H2 ^ H3) - 899497514); | |||||
| H4 = H3; | |||||
| H3 = H2; | |||||
| H2 = (H1 << 30) | (H1 >>> 2); | |||||
| H1 = H0; | |||||
| H0 = t; | |||||
| } | |||||
| H0 += a; | |||||
| H1 += b; | |||||
| H2 += c; | |||||
| H3 += d; | |||||
| H4 += e; | |||||
| } | |||||
| return [H0, H1, H2, H3, H4]; | |||||
| }; | |||||
| // Package private blocksize | |||||
| SHA1._blocksize = 16; | |||||
| })(); | |||||
| @ -0,0 +1,51 @@ | |||||
| import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; | |||||
| import '@/utils/oss-upload/common/crypto/hmac.js'; | |||||
| import '@/utils/oss-upload/common/crypto/sha1.js'; | |||||
| import { Base64 } from '@/utils/oss-upload/common/crypto/base64.js'; | |||||
| // 阿里云配置 | |||||
| const ossConfig = { | |||||
| url: 'https://image.hhlm1688.com/', | |||||
| config: { | |||||
| //桶的地址 | |||||
| region: 'oss-cn-guangzhou', | |||||
| //id | |||||
| accessKeyId: 'LTAI5tQSs47izVy8DLVdwUU9', | |||||
| //密钥 | |||||
| accessKeySecret: 'qHI7C3PaXYZySr84HTToviC71AYlFq', | |||||
| //桶的名字 | |||||
| bucket: 'hanhaiimage', | |||||
| endpoint: 'oss-cn-shenzhen.aliyuncs.com', | |||||
| } | |||||
| } | |||||
| let date = new Date() | |||||
| date = date.setHours(date.getHours() + 1) | |||||
| let extime = "" + new Date(date).toISOString() | |||||
| let policyText = { | |||||
| "expiration": extime, | |||||
| "conditions": [ | |||||
| ["content-length-range", 0, 1024 * 1024 * 100] // 设置上传文件的大小限制 | |||||
| ] | |||||
| }; | |||||
| let config = { | |||||
| accessid: ossConfig.config.accessKeyId, | |||||
| accesskey: ossConfig.config.accessKeySecret, | |||||
| osshost: ossConfig.url, | |||||
| policyBase64: Base64.encode(JSON.stringify(policyText)) | |||||
| } | |||||
| let message = config.policyBase64; | |||||
| let bytes = Crypto.HMAC(Crypto.SHA1, message, config.accesskey, { | |||||
| asBytes: true | |||||
| }); | |||||
| let signature = Crypto.util.bytesToBase64(bytes); | |||||
| let timetamp = new Date().getTime(); | |||||
| let OSSConfig = { | |||||
| name: 'aliyun', | |||||
| host: config.osshost, | |||||
| accessid: config.accessid, | |||||
| signature: signature, | |||||
| policyBase64: config.policyBase64, | |||||
| } | |||||
| export default OSSConfig; | |||||
| @ -0,0 +1,132 @@ | |||||
| /** | |||||
| * 阿里云OSS工具类 | |||||
| */ | |||||
| import OSSConfig from "@/utils/oss-upload/oss/OSSConfig.js" | |||||
| /** | |||||
| * 生成一个随机的Key | |||||
| */ | |||||
| function storeKey() { | |||||
| let s = []; | |||||
| let hexDigits = "0123456789abcdef"; | |||||
| for (let i = 0; i < 36; i++) { | |||||
| s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); | |||||
| } | |||||
| s[14] = "4"; | |||||
| s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); | |||||
| s[8] = s[13] = s[18] = s[23] = "-"; | |||||
| return s.join(""); | |||||
| } | |||||
| /** | |||||
| * 根据当天日期在OSS端生成文件夹 | |||||
| */ | |||||
| function storeFolder() { | |||||
| const date = new Date(); | |||||
| const formatNumber = n => { | |||||
| n = n.toString() | |||||
| return n[1] ? n : '0' + n | |||||
| } | |||||
| return [date.getFullYear(), date.getMonth() + 1, date.getDate()].map(formatNumber).join('-') | |||||
| } | |||||
| /** | |||||
| * 阿里云OSS上传文件, 所有具体功能的工具函数均基于此 | |||||
| * 注意, resolve时一定为上传成功, 返回OSS上的Key | |||||
| * @param filePath 待上传文件的URI | |||||
| * @param key 存储桶中的目标文件名 | |||||
| * @param folder 存储桶中的目标文件夹 | |||||
| */ | |||||
| export function ossUpload(filePath, key = storeKey(), folder = storeFolder()) { | |||||
| return new Promise((resolve, reject) => { | |||||
| if (folder && folder?.length > 0) { | |||||
| if (folder[0] == "/") folder = folder.slice(1, folder.length) | |||||
| if (folder[folder.length - 1] != "/") folder += "/" | |||||
| key = folder + key | |||||
| } | |||||
| const filePrefixArr = filePath.split(".") | |||||
| key += `.${filePrefixArr[filePrefixArr.length - 1]}` | |||||
| let config = { | |||||
| url: OSSConfig.host, | |||||
| name: 'file', | |||||
| filePath, | |||||
| formData: { | |||||
| key, | |||||
| policy: OSSConfig.policyBase64, | |||||
| OSSAccessKeyId: OSSConfig.accessid, | |||||
| success_action_status: '200', | |||||
| signature: OSSConfig.signature, | |||||
| }, | |||||
| success(res) { | |||||
| if (res.errMsg.includes("uploadFile:ok")) { | |||||
| resolve(OSSConfig.host + key) | |||||
| } else { | |||||
| reject(res) | |||||
| } | |||||
| }, | |||||
| fail(err) { | |||||
| reject(err) | |||||
| } | |||||
| } | |||||
| uni.uploadFile(config) | |||||
| }) | |||||
| } | |||||
| /** | |||||
| * 阿里云OSS上传图片 | |||||
| * @param {compressed, key, folder, success, fail} compressed: 是否压缩 key: 存储桶中的目标文件名 folder: 存储桶中的目标文件夹 | |||||
| */ | |||||
| export function ossUploadImage({ | |||||
| key, | |||||
| folder, | |||||
| compressed = true, //是否压缩 | |||||
| }) { | |||||
| const sizeType = [compressed ? 'compressed' : 'original'] | |||||
| return new Promise((success, fail) => { | |||||
| uni.chooseImage({ | |||||
| count: 1, | |||||
| sizeType, | |||||
| success(res) { | |||||
| ossUpload(res.tempFilePaths[0], key, folder).then(success).catch(fail) | |||||
| }, | |||||
| fail | |||||
| }) | |||||
| }) | |||||
| } | |||||
| /** | |||||
| * 阿里云OSS上传视频 | |||||
| * @param { key, folder, sourceType, compressed, maxDuration, camera, success, fail} | |||||
| * key: 存储桶中的目标文件名 folder: 存储桶中的目标文件夹 其它参数同uni.chooseVideo(mpWeixin) | |||||
| */ | |||||
| export function ossUploadVideo({ | |||||
| key, | |||||
| folder, | |||||
| sourceType = ['album', 'camera'], //album 从相册选视频, camera 使用相机拍摄 | |||||
| compressed = true, //是否压缩所选的视频源文件 | |||||
| maxDuration = 60, //拍摄视频最长拍摄时间, 单位秒。最长支持 60 秒 | |||||
| camera = 'back', //调用相机方向, 'front'、'back', 默认'back' | |||||
| }) { | |||||
| return new Promise((success, fail) => { | |||||
| uni.chooseVideo({ | |||||
| sourceType, | |||||
| compressed, | |||||
| maxDuration, | |||||
| camera, | |||||
| success(res) { | |||||
| ossUpload(res.tempFilePath, key, folder).then(success).catch(fail) | |||||
| }, | |||||
| fail | |||||
| }) | |||||
| }) | |||||
| } | |||||
| const OSS = { | |||||
| ossUploadVideo, | |||||
| ossUploadImage, | |||||
| ossUpload | |||||
| } | |||||
| export default OSS; | |||||