| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872 |
- <template>
- <view class="container">
- <!-- 吸顶固定层:状态tab + 搜索 + 筛选 -->
- <view class="sticky-header">
- <!-- 顶部 Tab (待接送/服务中...) -->
- <view class="status-tabs">
- <view class="tab-item" v-for="(tab, index) in tabs" :key="index"
- :class="{ active: currentTab === index }" @click="currentTab = index">
- <text>{{ tab }}</text>
- <view class="indicator" v-if="currentTab === index"></view>
- </view>
- </view>
- <!-- 搜索栏 -->
- <view class="search-bar">
- <view class="search-input-box">
- <input class="search-input" v-model="searchContent" placeholder="搜索地址/电话/姓名"
- placeholder-class="ph-style" />
- </view>
- </view>
- <!-- 筛选栏 (支持自定义下拉) -->
- <view class="filter-wrapper">
- <view class="filter-bar">
- <!-- 订单类型下拉视图 -->
- <view class="filter-item" :class="{ 'active': activeDropdown === 1 }" @click="toggleDropdown(1)">
- <text :class="{ 'active-text': activeDropdown === 1 || currentTypeFilterIdx > 0 }">
- {{ currentTypeFilterIdx > 0 ? typeFilterOptions[currentTypeFilterIdx] : '全部类型' }}
- </text>
- <view class="triangle" :class="activeDropdown === 1 ? 'up' : 'down'"></view>
- </view>
- <!-- 服务时间下拉视图 -->
- <view class="filter-item" :class="{ 'active': activeDropdown === 2 }" @click="toggleDropdown(2)">
- <text :class="{ 'active-text': activeDropdown === 2 || hasTimeFilter }">服务时间</text>
- <view class="triangle" :class="activeDropdown === 2 ? 'up' : 'down'"></view>
- </view>
- </view>
- <!-- 下拉内容面板与遮罩 -->
- <view class="dropdown-mask" v-if="activeDropdown !== 0" @click="closeDropdown"></view>
- <view class="dropdown-panel" v-if="activeDropdown === 1">
- <view class="type-option" v-for="(item, index) in typeFilterOptions" :key="index"
- :class="{ 'selected': currentTypeFilterIdx === index }" @click="selectType(index)">
- <text>{{ item }}</text>
- </view>
- </view>
- <view class="dropdown-panel calendar-panel" v-if="activeDropdown === 2">
- <view class="custom-calendar-container">
- <!-- 头部 -->
- <view class="cal-header">
- <text class="cal-nav-btn" @click="prevMonth">‹</text>
- <text class="cal-title">{{ currentMonth }}</text>
- <text class="cal-nav-btn" @click="nextMonth">›</text>
- </view>
- <!-- 星期条 -->
- <view class="cal-weekdays">
- <text v-for="(wk, idx) in weekDays" :key="idx" class="wk-item">{{ wk }}</text>
- </view>
- <!-- 日期网格 -->
- <view class="cal-body">
- <view v-for="(day, idx) in calendarDays" :key="idx" class="cal-day-box"
- :class="day ? getDateClass(day) : ''" @click="day && selectDateItem(day)">
- <view class="cal-day-text" v-if="day">{{ day }}</view>
- </view>
- </view>
- </view>
- <view class="calendar-actions">
- <button class="cal-btn reset" @click="resetTimeFilter">重置</button>
- <button class="cal-btn confirm" @click="confirmTimeFilter">确定</button>
- </view>
- </view>
- </view><!-- end filter-wrapper -->
- </view><!-- end sticky-header -->
- <!-- 订单列表 -->
- <view class="order-list">
- <view class="order-card" v-for="(item, index) in orderList" :key="index" :class="{ 'disabled-card': !item.serviceFlag }">
- <view class="card-header">
- <view class="type-badge">
- <image class="type-icon" :src="item.typeIcon"></image>
- <text class="type-text">{{ item.typeText }}</text>
- </view>
- <text class="status-badge" :class="getStatusClass(item)">{{ getDisplayStatus(item) }}</text>
- </view>
- <view class="card-body">
- <view class="time-row">
- <view class="time-col">
- <text class="label">{{ item.timeLabel }}:</text>
- <text class="value">{{ item.time }}</text>
- </view>
- <text class="fulfillmentCommission">¥{{ item.fulfillmentCommission }}</text>
- </view>
- <!-- 宠物信息 -->
- <view class="pet-card">
- <image class="pet-avatar" :src="item.petAvatarUrl || item.petAvatar" mode="aspectFill"></image>
- <view class="pet-info">
- <text class="pet-name">{{ item.petName }}</text>
- <text class="pet-breed">品种: {{ item.petBreed }}</text>
- </view>
- </view>
- <!-- 路线信息 (完全复用 Home 样式) -->
- <view class="route-info">
- <template v-if="item.type === 1">
- <view class="route-item" @click.stop="openNavigation(item, 'start')">
- <view class="icon-circle start">起</view>
- <view class="route-line-vertical"></view>
- <view class="address-box">
- <text class="addr-title">{{ item.startLocation }}</text>
- <text class="addr-desc">{{ item.startAddress }}</text>
- </view>
- <image class="nav-arrow" src="/static/icons/nav_arrow.svg"
- style="flex-shrink: 0; align-self: center;"></image>
- </view>
- <view class="route-item" @click.stop="openNavigation(item, 'end')">
- <view class="icon-circle end">终</view>
- <view class="address-box">
- <text class="addr-title">{{ item.endLocation }}</text>
- <text class="addr-desc">{{ item.endAddress }}</text>
- </view>
- <image class="nav-arrow" src="/static/icons/nav_arrow.svg"
- style="flex-shrink: 0; align-self: center;"></image>
- </view>
- </template>
- <template v-else>
- <view class="route-item" @click.stop="openNavigation(item, 'end')">
- <view class="icon-circle service">服</view>
- <view class="address-box">
- <text class="addr-title">{{ item.endLocation }}</text>
- <text class="addr-desc">{{ item.endAddress }}</text>
- </view>
- <image class="nav-arrow" src="/static/icons/nav_arrow.svg"
- style="flex-shrink: 0; align-self: center;"></image>
- </view>
- <view class="service-content" v-if="item.serviceContent">
- <text class="content-label">服务内容:</text>
- <text>{{ item.serviceContent }}</text>
- </view>
- </template>
- </view>
- <!-- 备注 -->
- <view class="remark-box" v-if="item.remark">
- <text>备注:{{ item.remark }}</text>
- </view>
- </view><!-- End of card-body -->
- <!-- 按钮组 (重新排版) -->
- <view class="action-btns" v-if="['接单', '到达', '出发', '开始', '送达', '结束'].includes(item.statusText)">
- <view class="action-row">
- <button class="btn normal danger" v-if="item.status === 2"
- @click.stop="handleCancelOrder(item)">取消订单</button>
- <button class="btn normal" @click.stop="reportAbnormal(item)">异常上报</button>
- <button class="btn normal" @click.stop="addOrUpdateService(item)">增改服务项</button>
- </view>
- <view class="action-row">
- <button class="btn normal" @click.stop="doCall('customer', item)">拨号</button>
- <button class="btn primary" @click.stop="mainAction(item)">到达打卡</button>
- </view>
- </view>
- </view>
- <!-- 已加载完提示文字 -->
- <view class="loading-text">已加载完</view>
- <view style="height: 160rpx;"></view>
- </view>
- <view class="call-mask" v-if="activeCallItem" @click="closeCallMenu"></view>
- </view>
- <!-- 宠物档案弹窗 (复用Home) -->
- <view class="pet-modal-mask" v-if="showPetModal" @click="closePetProfile">
- <view class="pet-modal-content" @click.stop>
- <view class="pet-modal-header">
- <text class="pet-modal-title">宠物档案</text>
- <view class="pm-header-actions">
- <view class="pm-remark-btn" @click="openRemarkInput">备注</view>
- <view class="close-icon-btn" @click="closePetProfile">×</view>
- </view>
- </view>
- <scroll-view scroll-y class="pet-modal-scroll">
- <!-- Basic Info -->
- <view class="pet-base-info">
- <image class="pm-avatar" :src="currentPetInfo.petAvatar" mode="aspectFill"></image>
- <view class="pm-info-text">
- <view class="pm-name-row">
- <text class="pm-name">{{ currentPetInfo.petName }}</text>
- <view class="pm-gender" v-if="currentPetInfo.petGender === 'M'">
- <text class="gender-icon">♂</text>
- <text>公</text>
- </view>
- <view class="pm-gender female" v-else-if="currentPetInfo.petGender === 'F'">
- <text class="gender-icon">♀</text>
- <text>母</text>
- </view>
- </view>
- <text class="pm-breed">品种:{{ currentPetInfo.petBreed }}</text>
- </view>
- </view>
- <!-- Details Grid -->
- <view class="pm-detail-grid">
- <view class="pm-grid-item half">
- <text class="pm-label">年龄</text>
- <text class="pm-val">{{ currentPetInfo.petAge || '未知' }}</text>
- </view>
- <view class="pm-grid-item half">
- <text class="pm-label">体重</text>
- <text class="pm-val">{{ currentPetInfo.petWeight || '未知' }}</text>
- </view>
- <view class="pm-grid-item full">
- <text class="pm-label">性格</text>
- <text class="pm-val">{{ currentPetInfo.petPersonality || '无' }}</text>
- </view>
- <view class="pm-grid-item full">
- <text class="pm-label">爱好</text>
- <text class="pm-val">{{ currentPetInfo.petHobby || '无' }}</text>
- </view>
- <view class="pm-grid-item full">
- <text class="pm-label">备注</text>
- <text class="pm-val">{{ currentPetInfo.petRemark || '无特殊过敏史' }}</text>
- </view>
- </view>
- <!-- Tags -->
- <view class="pm-tags" v-if="currentPetInfo.petTags && currentPetInfo.petTags.length > 0">
- <view class="pm-tag" v-for="(tag, index) in currentPetInfo.petTags" :key="index">{{ tag }}</view>
- </view>
- <!-- Log Section -->
- <view class="pm-section-title">
- <view class="orange-bar"></view>
- <text>备注日志</text>
- </view>
- <view class="pm-log-list">
- <view class="pm-log-item" v-for="(log, lIndex) in currentPetInfo.petLogs" :key="lIndex">
- <text class="pm-log-date">{{ log.date }}</text>
- <text class="pm-log-text">{{ log.content }}</text>
- <text class="pm-log-recorder">{{ log.recorder === '系统记录' ? '' : '记录人: ' }}{{ log.recorder
- }}</text>
- </view>
- </view>
- <view style="height: 30rpx;"></view>
- </scroll-view>
- </view>
- </view>
- <!-- 备注输入弹窗 -->
- <view class="remark-mask" v-if="showRemarkInput" @click="closeRemarkInput">
- <view class="remark-sheet" @click.stop>
- <view class="remark-sheet-header">
- <text class="remark-sheet-title">添加备注</text>
- <view class="close-icon-btn" @click="closeRemarkInput">×</view>
- </view>
- <textarea class="remark-textarea" v-model="remarkText" placeholder="请输入备注内容..." maxlength="500"
- auto-height />
- <view class="remark-submit-btn" @click="submitRemark">提交备注</view>
- </view>
- </view>
- <!-- 选择地图导航弹窗 (复用Home) -->
- <view class="nav-modal-mask" v-if="showNavModal" @click="closeNavModal">
- <view class="nav-action-sheet" @click.stop>
- <view class="nav-sheet-title">选择地图进行导航</view>
- <view class="nav-sheet-item" @click="chooseMap('高德')">高德地图</view>
- <view class="nav-sheet-item" @click="chooseMap('腾讯')">腾讯地图</view>
- <view class="nav-sheet-item" @click="chooseMap('百度')">百度地图</view>
- <view class="nav-sheet-gap"></view>
- <view class="nav-sheet-item cancel" @click="closeNavModal">取消</view>
- </view>
- </view>
- <custom-tabbar currentPath="pages/orders/index"></custom-tabbar>
- </template>
- <script>
- import { getMyOrders, cancelOrderApi } from '@/api/order/subOrder'
- import { listAllService } from '@/api/service/list'
- import { reportGps } from '@/utils/gps'
- import customTabbar from '@/components/custom-tabbar/index.vue'
- export default {
- components: {
- customTabbar
- },
- data() {
- return {
- currentTab: 0,
- tabs: ['待接送/服务', '配送/服务中', '已完成', '已取消'],
- typeFilterOptions: ['全部类型'],
- currentTypeFilterIdx: 0,
- activeDropdown: 0,
- hasTimeFilter: false,
- currentMonth: '',
- viewDate: new Date(),
- weekDays: ['日', '一', '二', '三', '四', '五', '六'],
- calendarDays: [],
- selectedDateRange: [],
- allOrderList: [],
- serviceList: [],
- searchContent: '',
- startServiceTime: '',
- endServiceTime: '',
- showPetModal: false,
- currentPetInfo: {},
- showNavModal: false,
- navTargetItem: null,
- navTargetPointType: '',
- activeCallItem: null,
- showRemarkInput: false,
- remarkText: ''
- }
- },
- created() {
- this.initCalendar();
- },
- async onLoad() {
- await this.loadServiceList()
- await this.loadOrders()
- // 显式请求一次定位授权
- reportGps(true).catch(e => console.log('Init GPS check skipped', e));
- },
- onShow() {
- uni.hideTabBar()
- // 此处不需要重复调用,因为逻辑可能在onLoad已处理,
- // 或者如果需要每次进入都刷新,可以保留,但需确保顺序
- this.loadOrders()
- },
- async onPullDownRefresh() {
- try {
- await this.loadServiceList()
- await this.loadOrders()
- } finally {
- uni.stopPullDownRefresh()
- }
- },
- watch: {
- currentTab() {
- this.loadOrders()
- },
- currentTypeFilterIdx() {
- this.loadOrders()
- },
- searchContent() {
- // 搜索内容变化时,自动重新加载订单
- this.loadOrders()
- }
- },
- computed: {
- orderList() {
- return this.allOrderList;
- }
- },
- methods: {
- async loadServiceList() {
- try {
- const res = await listAllService()
- this.serviceList = res.data || []
- this.typeFilterOptions = ['全部类型', ...this.serviceList.map(s => s.name)]
- } catch (err) {
- console.error('获取服务类型失败:', err)
- }
- },
- async loadOrders() {
- try {
- const statusMap = { 0: 2, 1: 3, 2: 4, 3: 5 }
- const serviceId = this.currentTypeFilterIdx > 0 ? this.serviceList[this.currentTypeFilterIdx - 1]?.id : undefined
- const params = {
- status: statusMap[this.currentTab],
- content: this.searchContent || undefined,
- service: serviceId,
- startServiceTime: this.startServiceTime || undefined,
- endServiceTime: this.endServiceTime || undefined
- }
- console.log('订单列表请求参数:', params)
- const res = await getMyOrders(params)
- console.log('订单列表响应:', res)
- const orders = res.rows || []
- console.log('订单数量:', orders.length)
- this.allOrderList = orders.map(order => this.transformOrder(order, this.currentTab))
- } catch (err) {
- console.error('获取订单列表失败:', err)
- this.allOrderList = []
- }
- },
- transformOrder(order, tabIndex) {
- const service = this.serviceList.find(s => s.id === order.service)
- const serviceText = service?.name || '未知'
- const serviceIcon = service?.iconUrl || ''
- const mode = service?.mode || 0
- const isRoundTrip = mode === 1
- // 根据 Tab 索引强制指定状态文字,忽略后端缺失的 status 字段
- let statusText = '接单'
- if (tabIndex === 0) {
- statusText = '接单' // 待接送/服务
- } else if (tabIndex === 1) {
- statusText = isRoundTrip ? '出发' : '开始' // 配送/服务中
- } else if (tabIndex === 2) {
- statusText = '已完成' // 已完成
- } else if (tabIndex === 3) {
- statusText = '已拒绝' // 已拒绝
- }
- return {
- id: order.id,
- status: order.status, // 保存原始 status 用于判断权限
- type: isRoundTrip ? 1 : 2,
- typeText: serviceText,
- typeIcon: serviceIcon,
- statusText: statusText,
- fulfillmentCommission: (order.fulfillmentCommission / 100).toFixed(2),
- timeLabel: '服务时间',
- time: order.serviceTime || '',
- petAvatar: order.petAvatar || '/static/dog.png',
- petAvatarUrl: order.petAvatarUrl || '',
- petName: order.petName || '',
- petBreed: order.breed || '',
- startLocation: order.fromAddress || '暂无起点',
- startAddress: order.fromAddress || '',
- fromAddress: order.fromAddress || '',
- fromLat: order.fromLat,
- fromLng: order.fromLng,
- startDistance: '0km',
- endLocation: (order.customerName || '') + ' ' + (order.customerPhone || ''),
- endAddress: order.toAddress || '',
- toAddress: order.toAddress || '',
- toLat: order.toLat,
- toLng: order.toLng,
- customerPhone: order.customerPhone || '',
- endDistance: '0km',
- serviceContent: order.remark || '',
- remark: order.remark || '',
- serviceFlag: !!order.serviceFlag // 是否允许服务(点击跳转)
- }
- },
- getDisplayStatus(item) {
- if (item.statusText === '已完成') return '已完成';
- if (item.statusText === '已拒绝') return '已拒绝';
- if (item.statusText === '接单') {
- return item.type === 1 ? '待接送' : '待服务';
- }
- return item.type === 1 ? '配送中' : '服务中';
- },
- getStatusClass(item) {
- let display = this.getDisplayStatus(item);
- if (display === '已完成') return 'finish';
- if (display === '已拒绝') return 'reject';
- if (display === '配送中' || display === '服务中') return 'processing';
- return 'highlight';
- },
- goToDetail(item) {
- uni.navigateTo({ url: `/pages/orders/detail/index?id=${item.id}` });
- },
- showPetProfile(item) {
- this.currentPetInfo = {
- ...item,
- petGender: 'M',
- petAge: '2岁',
- petWeight: '15kg',
- petPersonality: '活泼亲人,精力旺盛',
- petHobby: '喜欢追飞盘,爱吃肉干',
- petRemark: '肠胃较弱,不能乱喂零食;出门易爆冲,请拉紧牵引绳。',
- petTags: ['拉响警报', '不能吃鸡肉', '精力旺盛'],
- petLogs: [
- { date: '2026-02-09 14:00', content: '今天遛弯拉了两次粑粑,精神状态很好。', recorder: '王阿姨' },
- { date: '2026-02-08 10:30', content: '有些挑食,剩了小半碗狗粮。', recorder: '李师傅' },
- { date: '2026-02-05 09:00', content: '建档。', recorder: '系统记录' }
- ]
- };
- this.showPetModal = true;
- },
- closePetProfile() {
- this.showPetModal = false;
- },
- openNavigation(item, pointType) {
- this.navTargetItem = item;
- this.navTargetPointType = pointType;
- this.showNavModal = true;
- },
- closeNavModal() {
- this.showNavModal = false;
- },
- chooseMap(mapType) {
- let item = this.navTargetItem;
- let pointType = this.navTargetPointType;
- // 起 -> fromAddress ; 终 -> toAddress
- let name = pointType === 'start' ? (item.fromAddress || '起点') : (item.toAddress || '终点');
- let address = pointType === 'start' ? (item.fromAddress || '起点地址') : (item.toAddress || '终点地址');
- let latitude = pointType === 'start' ? Number(item.fromLat) : Number(item.toLat);
- let longitude = pointType === 'start' ? Number(item.fromLng) : Number(item.toLng);
- this.showNavModal = false;
- // 统一定义打开地图的函数
- const navigateTo = (lat, lng, addrName, addrDesc) => {
- uni.openLocation({
- latitude: lat,
- longitude: lng,
- name: addrName,
- address: addrDesc || '无法获取详细地址',
- success: function () {
- console.log('打开导航成功: ' + mapType);
- },
- fail: function (err) {
- console.error('打开导航失败:', err);
- uni.showToast({ title: '打开地图失败', icon: 'none' });
- }
- });
- };
- // 如果有目标经纬度,直接打开
- if (latitude && longitude && !isNaN(latitude) && !isNaN(longitude)) {
- navigateTo(latitude, longitude, name, address);
- } else {
- // 如果没有经纬度,按照需求:使用自己当前的经纬度,然后搜索 fromAddress 或者 toAddress
- uni.showLoading({ title: '获取当前位置...', mask: true });
- reportGps(true).then(res => {
- uni.hideLoading();
- // 使用用户当前经纬度作为锚点打开地图,展示目标地址信息
- navigateTo(res.latitude, res.longitude, name, address);
- }).catch(err => {
- uni.hideLoading();
- console.error('获取地理位置失败:', err);
- // 具体的授权引导已在 reportGps 内部处理
- });
- }
- },
- toggleCallMenu(item) {
- if (this.activeCallItem === item) {
- this.activeCallItem = null;
- } else {
- this.activeCallItem = item;
- }
- },
- closeCallMenu() {
- this.activeCallItem = null;
- },
- doCall(type, item) {
- let phoneNum = '';
- const targetItem = item || this.activeCallItem;
- // 1. 获取电话号码
- if (type === 'merchant') {
- phoneNum = '18900008451';
- } else if (type === 'customer') {
- phoneNum = targetItem?.customerPhone;
- }
- // 2. 基础校验
- if (!phoneNum) {
- uni.showToast({ title: '未找到电话号码', icon: 'none' });
- this.activeCallItem = null;
- return;
- }
- // 3. 清洗号码 (去除空格、横杠等非数字字符)
- phoneNum = phoneNum.replace(/[^\d]/g, '');
- // 二次校验:确保清洗后仍有数字
- if (phoneNum.length < 3) {
- uni.showToast({ title: '电话号码格式错误', icon: 'none' });
- this.activeCallItem = null;
- return;
- }
- console.log('正在发起直接呼叫:', phoneNum);
- // 4. 核心逻辑:区分环境处理
- // #ifdef APP-PLUS
- // App 端:使用 uni.makePhoneCall 直接发起呼叫
- uni.makePhoneCall({
- phoneNumber: phoneNum,
- success: () => {
- console.log('成功唤起系统拨号盘');
- },
- fail: (err) => {
- console.error('拨号失败:', err);
- // 常见错误:Permission denied (权限被拒) 或 Activity not found
- let msg = '拨号失败';
- if (err.message && err.message.includes('permission')) {
- msg = '请在手机设置中允许"电话"权限';
- }
- uni.showToast({ title: msg, icon: 'none', duration: 3000 });
- // 如果失败,尝试引导用户去设置页 (仅限 Android)
- // #ifdef APP-ANDROID
- if (err.message && err.message.includes('permission')) {
- uni.showModal({
- title: '权限提示',
- content: '拨打电话需要电话权限,是否前往设置开启?',
- success: (res) => {
- if (res.confirm) {
- plus.runtime.openURL("app-settings:");
- }
- }
- });
- }
- // #endif
- },
- complete: () => {
- this.activeCallItem = null; // 关闭弹窗
- }
- });
- // #endif
- // #ifdef H5
- // H5 端:使用 tel: 协议
- window.location.href = `tel:${phoneNum}`;
- this.activeCallItem = null;
- // #endif
- // #ifdef MP-WEIXIN
- // 小程序端:直接调用 makePhoneCall (微信小程序支持直接弹框确认拨打)
- uni.makePhoneCall({
- phoneNumber: phoneNum,
- fail: () => {
- uni.showToast({ title: '拨号失败', icon: 'none' });
- },
- complete: () => {
- this.activeCallItem = null;
- }
- });
- // #endif
- },
- reportAbnormal(item) {
- uni.navigateTo({ url: '/pages/orders/anomaly/index?orderId=' + (item.id || '') });
- },
- toggleDropdown(idx) {
- if (this.activeDropdown === idx) {
- this.activeDropdown = 0;
- } else {
- this.activeDropdown = idx;
- }
- },
- closeDropdown() {
- this.activeDropdown = 0;
- },
- selectType(index) {
- this.currentTypeFilterIdx = index;
- this.closeDropdown();
- },
- initCalendar() {
- const year = this.viewDate.getFullYear();
- const month = this.viewDate.getMonth();
- this.currentMonth = `${year}年${month + 1}月`;
- // 获取该月第一天是周几 (0-6)
- const firstDay = new Date(year, month, 1).getDay();
- // 获取该月有多少天
- const daysInMonth = new Date(year, month + 1, 0).getDate();
- let days = [];
- // 填充开头的空白
- for (let i = 0; i < firstDay; i++) {
- days.push(0);
- }
- // 填充真实日期
- for (let i = 1; i <= daysInMonth; i++) {
- days.push(i);
- }
- this.calendarDays = days;
- },
- prevMonth() {
- this.viewDate.setMonth(this.viewDate.getMonth() - 1);
- // 切换月份时强制重新创建 Date 对象以触发 Vue 响应式(如果需要)或者简单调用 init
- this.viewDate = new Date(this.viewDate);
- this.initCalendar();
- },
- nextMonth() {
- this.viewDate.setMonth(this.viewDate.getMonth() + 1);
- this.viewDate = new Date(this.viewDate);
- this.initCalendar();
- },
- selectDateItem(day) {
- if (this.selectedDateRange.length === 2) {
- this.selectedDateRange = [day];
- } else if (this.selectedDateRange.length === 1) {
- let start = this.selectedDateRange[0];
- if (day > start) {
- this.selectedDateRange = [start, day];
- } else if (day < start) {
- this.selectedDateRange = [day, start];
- } else {
- this.selectedDateRange = [];
- }
- } else {
- this.selectedDateRange = [day];
- }
- },
- getDateClass(day) {
- if (!day || this.selectedDateRange.length === 0) return '';
- if (this.selectedDateRange.length === 1) {
- return day === this.selectedDateRange[0] ? 'is-start' : '';
- }
- let start = this.selectedDateRange[0];
- let end = this.selectedDateRange[1];
- if (day === start) return 'is-start';
- if (day === end) return 'is-end';
- if (day > start && day < end) return 'is-between';
- return '';
- },
- resetTimeFilter() {
- this.hasTimeFilter = false;
- this.selectedDateRange = [];
- this.startServiceTime = '';
- this.endServiceTime = '';
- this.closeDropdown();
- this.loadOrders();
- },
- confirmTimeFilter() {
- if (this.selectedDateRange.length === 0) {
- uni.showToast({ title: '请先选择日期', icon: 'none' });
- return;
- }
- // 构建时间范围参数
- const year = this.currentMonth.replace(/[^0-9]/g, '').substring(0, 4);
- const month = this.currentMonth.replace(/[^0-9]/g, '').substring(4);
- const pad = (n) => String(n).padStart(2, '0');
- if (this.selectedDateRange.length === 2) {
- this.startServiceTime = `${year}-${pad(month)}-${pad(this.selectedDateRange[0])} 00:00:00`;
- this.endServiceTime = `${year}-${pad(month)}-${pad(this.selectedDateRange[1])} 23:59:59`;
- } else {
- this.startServiceTime = `${year}-${pad(month)}-${pad(this.selectedDateRange[0])} 00:00:00`;
- this.endServiceTime = `${year}-${pad(month)}-${pad(this.selectedDateRange[0])} 23:59:59`;
- }
- this.hasTimeFilter = true;
- this.closeDropdown();
- this.loadOrders();
- },
- getMainActionText(item) {
- return '查看详情';
- },
- mainAction(item) {
- uni.navigateTo({ url: `/pages/orders/detail/index?id=${item.id}` });
- },
- addOrUpdateService(item) {
- // 跳转到申诉(增改服务项)页面
- uni.navigateTo({ url: `/pages/orders/appeal/index?id=${item.id}` });
- },
- openRemarkInput() {
- this.remarkText = '';
- this.showRemarkInput = true;
- },
- closeRemarkInput() {
- this.showRemarkInput = false;
- this.remarkText = '';
- },
- submitRemark() {
- const text = this.remarkText.trim();
- if (!text) {
- uni.showToast({ title: '备注内容不能为空', icon: 'none' });
- return;
- }
- const now = new Date();
- const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
- if (!this.currentPetInfo.petLogs) {
- this.$set(this.currentPetInfo, 'petLogs', []);
- }
- this.currentPetInfo.petLogs.unshift({
- date: dateStr,
- content: text,
- recorder: '我'
- });
- uni.showToast({ title: '备注已添加', icon: 'success' });
- this.closeRemarkInput();
- },
- /**
- * 取消订单处理逻辑
- * @param {Object} item - 订单项
- */
- handleCancelOrder(item) {
- uni.showModal({
- title: '提示',
- content: '确认是否取消这个订单?',
- success: async (res) => {
- if (res.confirm) {
- try {
- uni.showLoading({ title: '取消中...', mask: true });
- await cancelOrderApi({ orderId: item.id });
- uni.showToast({ title: '订单已取消', icon: 'success' });
- // 延时刷新列表,防止提示框闪现
- setTimeout(() => {
- this.loadOrders();
- }, 1500);
- } catch (err) {
- console.error('取消订单失败:', err);
- uni.showToast({ title: '取消失败', icon: 'none' });
- } finally {
- uni.hideLoading();
- }
- }
- }
- });
- }
- }
- }
- </script>
- <style>
- page {
- background-color: #F8F8F8;
- }
- .custom-nav-bar {
- padding: 80rpx 30rpx 20rpx;
- background-color: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .nav-title {
- font-size: 34rpx;
- font-weight: bold;
- color: #333;
- }
- .sticky-header {
- position: sticky;
- top: 0;
- z-index: 999;
- background-color: #F8F8F8;
- }
- .container {
- background-color: #F8F8F8;
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- }
- .status-tabs {
- display: flex;
- background-color: #fff;
- padding: 0 30rpx;
- justify-content: space-between;
- }
- .tab-item {
- position: relative;
- padding: 20rpx 0;
- font-size: 26rpx;
- color: #666;
- font-weight: 500;
- }
- .tab-item.active {
- color: #FF5722;
- font-weight: bold;
- }
- .indicator {
- position: absolute;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%);
- width: 40rpx;
- height: 6rpx;
- background-color: #FF5722;
- border-radius: 3rpx;
- }
- .search-bar {
- padding: 10rpx 30rpx;
- background-color: #fff;
- }
- .search-input-box {
- display: flex;
- align-items: center;
- background-color: #F8F8F8;
- height: 64rpx;
- border-radius: 32rpx;
- padding: 0 30rpx;
- }
- .search-input {
- flex: 1;
- font-size: 26rpx;
- color: #333;
- padding-left: 20rpx;
- }
- .ph-style {
- font-size: 26rpx;
- color: #999;
- }
- .filter-wrapper {
- position: relative;
- z-index: 998;
- }
- .filter-bar {
- display: flex;
- background-color: #fff;
- padding: 5rpx 30rpx 10rpx 30rpx;
- justify-content: space-between;
- position: relative;
- z-index: 998;
- }
- .filter-item {
- width: 48%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 26rpx;
- color: #666;
- background-color: #F8F8F8;
- height: 56rpx;
- border-radius: 12rpx;
- transition: all 0.2s;
- }
- .filter-item.active {
- background-color: #FFF3E0;
- }
- .active-text {
- color: #FF5722;
- font-weight: 500;
- }
- .triangle {
- width: 0;
- height: 0;
- border-left: 8rpx solid transparent;
- border-right: 8rpx solid transparent;
- margin-left: 10rpx;
- transition: all 0.2s;
- }
- .triangle.down {
- border-top: 10rpx solid #dcdcdc;
- }
- .filter-item.active .triangle.down,
- .active-text+.triangle.down {
- border-top-color: #FF5722;
- }
- .triangle.up {
- border-bottom: 10rpx solid #FF5722;
- }
- .dropdown-mask {
- position: absolute;
- top: 100%;
- left: 0;
- right: 0;
- height: 100vh;
- background-color: rgba(0, 0, 0, 0.4);
- z-index: 80;
- }
- .dropdown-panel {
- position: absolute;
- top: 100%;
- left: 0;
- right: 0;
- background-color: #fff;
- z-index: 90;
- border-radius: 0 0 20rpx 20rpx;
- box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.05);
- overflow: hidden;
- }
- .type-option {
- padding: 30rpx 40rpx;
- font-size: 28rpx;
- color: #333;
- border-bottom: 1px solid #f5f5f5;
- }
- .type-option:last-child {
- border-bottom: none;
- }
- .type-option.selected text {
- color: #FF5722;
- font-weight: bold;
- }
- .calendar-panel {
- padding-bottom: 30rpx;
- }
- .custom-calendar-container {
- padding: 20rpx 30rpx 0;
- }
- .cal-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20rpx 0;
- }
- .cal-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .cal-weekdays {
- display: flex;
- justify-content: space-around;
- padding: 20rpx 0;
- border-bottom: 1px solid #f5f5f5;
- }
- .wk-item {
- font-size: 24rpx;
- color: #999;
- width: 14.28%;
- text-align: center;
- }
- .cal-body {
- display: flex;
- flex-wrap: wrap;
- padding-top: 20rpx;
- }
- .cal-day-box {
- width: 14.28%;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 10rpx;
- position: relative;
- }
- .cal-day-text {
- width: 64rpx;
- height: 64rpx;
- line-height: 64rpx;
- text-align: center;
- font-size: 28rpx;
- color: #333;
- border-radius: 8rpx;
- position: relative;
- z-index: 2;
- }
- .cal-day-box.is-start .cal-day-text,
- .cal-day-box.is-end .cal-day-text {
- background-color: #FF5722;
- color: #fff;
- font-weight: bold;
- }
- .cal-day-box.is-start::after {
- content: '';
- position: absolute;
- right: 0;
- top: 8rpx;
- bottom: 8rpx;
- width: 50%;
- background-color: #FFF3E0;
- z-index: 1;
- }
- .cal-day-box.is-end::after {
- content: '';
- position: absolute;
- left: 0;
- top: 8rpx;
- bottom: 8rpx;
- width: 50%;
- background-color: #FFF3E0;
- z-index: 1;
- }
- .cal-day-box.is-start.is-end::after {
- display: none;
- }
- .cal-day-box.is-between {
- background-color: #FFF3E0;
- margin-top: 8rpx;
- height: 64rpx;
- margin-bottom: 18rpx;
- }
- .cal-day-box.is-between .cal-day-text {
- color: #FF5722;
- }
- .calendar-actions {
- display: flex;
- justify-content: space-between;
- padding: 0 30rpx;
- margin-top: 20rpx;
- }
- .cal-btn {
- width: 48%;
- height: 70rpx;
- line-height: 70rpx;
- text-align: center;
- border-radius: 10rpx;
- font-size: 28rpx;
- margin: 0;
- }
- .cal-btn.reset {
- background-color: #f5f5f5;
- color: #666;
- }
- .cal-btn.confirm {
- background-color: #FF5722;
- color: #fff;
- }
- .order-list {
- padding: 0 30rpx;
- width: 100%;
- box-sizing: border-box;
- }
- .order-card {
- background-color: #fff;
- border-radius: 24rpx;
- padding: 20rpx 20rpx;
- margin-bottom: 20rpx;
- box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
- }
- .order-card:first-child {
- margin-top: 20rpx;
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15rpx;
- }
- .type-badge {
- display: flex;
- align-items: center;
- }
- .type-icon {
- width: 44rpx;
- height: 44rpx;
- margin-right: 15rpx;
- background-color: #FFF3E0;
- border-radius: 50%;
- padding: 6rpx;
- box-sizing: border-box;
- }
- .type-text {
- font-size: 30rpx;
- font-weight: bold;
- color: #333;
- }
- .status-badge {
- font-size: 28rpx;
- }
- .status-badge.highlight {
- color: #FF5722;
- }
- .status-badge.processing {
- color: #2196F3;
- }
- .status-badge.finish {
- color: #4CAF50;
- }
- .status-badge.reject {
- color: #9E9E9E;
- }
- .time-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 25rpx;
- }
- .time-row .time-col {
- display: flex;
- align-items: center;
- font-size: 26rpx;
- color: #333;
- }
- .time-row .label {
- color: #666;
- margin-right: 10rpx;
- }
- .fulfillmentCommission {
- font-size: 36rpx;
- font-weight: bold;
- color: #FF5722;
- }
- .pet-card {
- background-color: #FFF8F0;
- border-radius: 16rpx;
- padding: 15rpx 20rpx;
- display: flex;
- align-items: center;
- margin-bottom: 20rpx;
- }
- .pet-avatar {
- width: 80rpx;
- height: 80rpx;
- border-radius: 50%;
- margin-right: 20rpx;
- }
- .pet-info {
- flex: 1;
- display: flex;
- flex-direction: column;
- }
- .pet-name {
- font-size: 28rpx;
- font-weight: bold;
- color: #333;
- margin-bottom: 5rpx;
- }
- .pet-breed {
- font-size: 24rpx;
- color: #999;
- }
- .pet-profile-btn {
- font-size: 24rpx;
- color: #FF9800;
- border: 1px solid #FF9800;
- padding: 6rpx 20rpx;
- border-radius: 50rpx;
- background-color: #fff;
- }
- .route-info {
- margin-bottom: 25rpx;
- }
- .route-item {
- display: flex;
- align-items: flex-start;
- padding-bottom: 12rpx;
- position: relative;
- width: 100%;
- }
- .route-item:not(:last-child) {
- margin-bottom: 5rpx;
- }
- .route-item:last-child {
- padding-bottom: 0;
- margin-bottom: 0;
- }
- .route-line-vertical {
- position: absolute;
- left: 19rpx;
- top: 46rpx;
- bottom: -15rpx;
- border-left: 2rpx dashed #E0E0E0;
- width: 0;
- z-index: 0;
- }
- .icon-circle {
- width: 40rpx;
- height: 40rpx;
- border-radius: 50%;
- color: #fff;
- font-size: 22rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 20rpx;
- flex-shrink: 0;
- font-weight: bold;
- margin-top: 6rpx;
- position: relative;
- z-index: 1;
- }
- .icon-circle.service {
- background-color: #81C784;
- }
- .icon-circle.start {
- background-color: #FFB74D;
- }
- .icon-circle.end {
- background-color: #81C784;
- }
- .address-box {
- flex: 1;
- display: flex;
- flex-direction: column;
- margin-right: 20rpx;
- }
- .addr-title {
- font-size: 28rpx;
- font-weight: bold;
- color: #333;
- margin-bottom: 4rpx;
- }
- .addr-desc {
- font-size: 24rpx;
- color: #999;
- line-height: 1.4;
- }
- .distance-tag {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- flex-shrink: 0;
- min-width: 80rpx;
- }
- .distance-text {
- font-size: 24rpx;
- color: #FF5722;
- margin-right: 15rpx;
- font-weight: 500;
- }
- .nav-icon-circle {
- width: 48rpx;
- height: 48rpx;
- background-color: #FFF3E0;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .nav-arrow {
- width: 24rpx;
- height: 24rpx;
- }
- .service-content {
- margin-top: -10rpx;
- font-size: 24rpx;
- color: #666;
- padding-left: 60rpx;
- }
- .content-label {
- color: #999;
- margin-right: 10rpx;
- }
- .remark-box {
- background-color: #F8F8F8;
- padding: 15rpx 20rpx;
- border-radius: 8rpx;
- font-size: 26rpx;
- color: #666;
- margin-bottom: 30rpx;
- }
- .action-btns {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
- margin-top: 20rpx;
- }
- .action-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- }
- .btn {
- height: 60rpx;
- line-height: 60rpx;
- border-radius: 30rpx;
- font-size: 26rpx;
- padding: 0 30rpx;
- margin: 0;
- }
- .action-right .btn:not(:last-child) {
- margin-right: 20rpx;
- }
- .btn::after {
- border: none;
- }
- .btn.normal {
- background-color: #F8F8F8;
- color: #666;
- border: none;
- }
- .btn.primary {
- background: linear-gradient(90deg, #FF9800 0%, #FF5722 100%);
- color: #fff;
- box-shadow: 0 4rpx 12rpx rgba(255, 87, 34, 0.2);
- border: none;
- }
- .btn.normal.danger {
- background-color: #FFF2F0;
- color: #F5222D;
- }
- .pet-modal-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.4);
- z-index: 1000;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .pet-modal-content {
- width: 680rpx;
- height: 85vh;
- background-color: #fff;
- border-radius: 20rpx;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .pet-modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 30rpx;
- border-bottom: 1rpx solid #F0F0F0;
- }
- .pet-modal-title {
- font-size: 34rpx;
- font-weight: bold;
- color: #333;
- }
- .pet-modal-scroll {
- flex: 1;
- height: 0;
- padding: 30rpx;
- box-sizing: border-box;
- }
- .pet-base-info {
- display: flex;
- align-items: center;
- margin-bottom: 40rpx;
- }
- .pm-avatar {
- width: 120rpx;
- height: 120rpx;
- border-radius: 50%;
- margin-right: 30rpx;
- border: 2rpx solid #f5f5f5;
- }
- .pm-info-text {
- flex: 1;
- display: flex;
- flex-direction: column;
- }
- .pm-name-row {
- display: flex;
- align-items: center;
- margin-bottom: 15rpx;
- }
- .pm-name {
- font-size: 36rpx;
- font-weight: bold;
- color: #333;
- margin-right: 20rpx;
- }
- .pm-gender {
- display: flex;
- align-items: center;
- background-color: #E3F2FD;
- padding: 4rpx 12rpx;
- border-radius: 20rpx;
- }
- .pm-gender text {
- font-size: 22rpx;
- color: #1E88E5;
- }
- .pm-gender .gender-icon {
- font-weight: bold;
- margin-right: 4rpx;
- }
- .pm-gender.female {
- background-color: #FCE4EC;
- }
- .pm-gender.female text {
- color: #D81B60;
- }
- .pm-breed {
- font-size: 26rpx;
- color: #999;
- }
- .pm-detail-grid {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
- }
- .pm-grid-item {
- background-color: #F8F8F8;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 20rpx;
- display: flex;
- flex-direction: column;
- }
- .pm-grid-item.half {
- width: 48%;
- box-sizing: border-box;
- }
- .pm-grid-item.full {
- width: 100%;
- box-sizing: border-box;
- }
- .pm-label {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 10rpx;
- }
- .pm-val {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- }
- .pm-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 20rpx;
- margin-bottom: 40rpx;
- }
- .pm-tag {
- background-color: #FFF8EB;
- border: 2rpx solid #FFCC80;
- color: #FF9800;
- font-size: 22rpx;
- padding: 8rpx 24rpx;
- border-radius: 30rpx;
- }
- .pm-section-title {
- display: flex;
- align-items: center;
- margin-bottom: 30rpx;
- padding-top: 30rpx;
- border-top: 2rpx dashed #F0F0F0;
- }
- .pm-section-title .orange-bar {
- width: 8rpx;
- height: 32rpx;
- background-color: #FF9800;
- margin-right: 16rpx;
- border-radius: 4rpx;
- }
- .pm-section-title text {
- font-size: 30rpx;
- font-weight: bold;
- color: #333;
- }
- .pm-log-list {
- display: flex;
- flex-direction: column;
- }
- .pm-log-item {
- display: flex;
- flex-direction: column;
- padding: 24rpx 0;
- border-bottom: 1rpx solid #F0F0F0;
- }
- .pm-log-item:last-child {
- border-bottom: none;
- }
- .pm-log-date {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .pm-log-text {
- font-size: 28rpx;
- color: #333;
- line-height: 1.6;
- margin-bottom: 20rpx;
- }
- .pm-log-recorder {
- font-size: 24rpx;
- color: #FF9800;
- align-self: flex-end;
- }
- .pm-bottom-close {
- width: 100%;
- height: 80rpx;
- line-height: 80rpx;
- background-color: #F5F5F5;
- color: #666;
- border-radius: 40rpx;
- font-size: 30rpx;
- font-weight: bold;
- margin: 0;
- }
- .pm-bottom-close::after {
- border: none;
- }
- .close-icon-btn {
- font-size: 48rpx;
- color: #999;
- line-height: 1;
- padding: 0 10rpx;
- }
- .nav-modal-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 1000;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- }
- .nav-action-sheet {
- background-color: #fff;
- width: 100%;
- border-top-left-radius: 24rpx;
- border-top-right-radius: 24rpx;
- overflow: hidden;
- padding-bottom: constant(safe-area-inset-bottom);
- padding-bottom: env(safe-area-inset-bottom);
- }
- .nav-sheet-title {
- text-align: center;
- padding: 30rpx 0;
- font-size: 13px;
- color: #999;
- border-bottom: 1rpx solid #efefef;
- }
- .nav-sheet-item {
- text-align: center;
- padding: 30rpx 0;
- font-size: 13px;
- color: #333;
- background-color: #fff;
- border-bottom: 1rpx solid #efefef;
- }
- .nav-sheet-item.cancel {
- border-bottom: none;
- color: #666;
- }
- .nav-sheet-gap {
- height: 16rpx;
- background-color: #F8F8F8;
- }
- .order-list {
- flex: 1;
- overflow-y: auto;
- width: 100%;
- padding: 0 30rpx;
- box-sizing: border-box;
- }
- .loading-text {
- text-align: center;
- font-size: 24rpx;
- color: #999;
- padding: 30rpx 0;
- }
- .pm-header-actions {
- display: flex;
- align-items: center;
- gap: 16rpx;
- }
- .pm-remark-btn {
- font-size: 24rpx;
- color: #fff;
- background-color: #FF9800;
- padding: 6rpx 18rpx;
- border-radius: 20rpx;
- }
- .remark-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 3000;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .remark-sheet {
- width: 600rpx;
- background-color: #fff;
- border-radius: 24rpx;
- padding: 40rpx;
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
- .remark-sheet-header {
- width: 100%;
- text-align: center;
- margin-bottom: 30rpx;
- position: relative;
- }
- .remark-sheet-header .close-icon-btn {
- position: absolute;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- }
- .remark-sheet-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .remark-textarea {
- width: 100%;
- height: 160rpx;
- border: 1rpx solid #eee;
- border-radius: 12rpx;
- padding: 20rpx;
- font-size: 28rpx;
- color: #333;
- box-sizing: border-box;
- margin-bottom: 40rpx;
- }
- .remark-submit-btn {
- width: 100%;
- background-color: #FF5722;
- color: #fff;
- font-size: 32rpx;
- font-weight: bold;
- text-align: center;
- padding: 24rpx 0;
- border-radius: 16rpx;
- }
- .action-left {
- position: relative;
- z-index: 10;
- }
- .action-left .btn.normal {
- font-size: 26rpx;
- }
- .call-popover {
- position: absolute;
- top: calc(100% + 10rpx);
- left: 0;
- background-color: #fff;
- border-radius: 12rpx;
- box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
- z-index: 999;
- display: flex;
- flex-direction: column;
- width: 200rpx;
- }
- .call-pop-item {
- font-size: 26rpx;
- color: #333;
- text-align: center;
- padding: 24rpx 0;
- border-bottom: 1rpx solid #eee;
- }
- .call-pop-item:last-child {
- border-bottom: none;
- }
- .call-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 900;
- background: transparent;
- }
- .disabled-card {
- opacity: 0.5; /* 降低透明度以示禁用 */
- pointer-events: none; /* 禁用该卡片内背景的一切交互 */
- filter: grayscale(80%); /* 增加灰度,使视觉效果更明显 */
- }
- .disabled-card .action-row {
- pointer-events: auto; /* 允许按钮即使在置灰状态下也能点击操作 */
- }
- /* 即使使用了 pointer-events: none,外层的 @click 也会失效,为了保险我们在 JS 中也做了判断 */
- </style>
|