爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1252 lines
33 KiB

3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 months ago
3 months ago
3 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
3 months ago
2 weeks ago
3 months ago
3 months ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
3 months ago
2 weeks ago
  1. <template>
  2. <view class="order-detail-container">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20" color="#222" />
  7. </view>
  8. <text class="nav-title">订单详情</text>
  9. <view class="nav-icons">
  10. <!-- <uni-icons type="more" size="24" color="#222" /> -->
  11. </view>
  12. </view>
  13. <view class="main-content">
  14. <!-- 回收信息卡片高保真参考流程卡片 -->
  15. <view class="info-card process-card">
  16. <view v-if="order.cancelReason" class="cancel-reason">{{ order.cancelReason }}</view>
  17. <view class="info-card-header">
  18. <view class="info-title-wrap">
  19. <text class="info-title">回收信息</text>
  20. <view v-if="order.statusText === '不包邮'" class="tag-nobaoyou">不包邮</view>
  21. </view>
  22. <view class="status-tag" :class="order.statusClass" style="margin-left:auto;">{{ order.statusLabel }}</view>
  23. </view>
  24. <!-- 步骤条横向卡片式 -->
  25. <view class="steps-bar">
  26. <view v-for="(step, idx) in steps" :key="step.text" :class="['step-item', { active: idx === currentStep }]">
  27. <image :src="step.icon" class="step-icon" />
  28. <view class="step-label" :class="{ active: idx === currentStep }">
  29. <text class="step-text">{{ step.text }}</text>
  30. </view>
  31. </view>
  32. </view>
  33. <!-- 订单基础信息左对齐分隔线按钮箭头标签等细节 -->
  34. <view class="base-info">
  35. <view v-for="(item, i) in baseInfo" :key="item.label" class="base-info-row"
  36. :class="{ 'user-stat-trigger': item.label === '用户名' }"
  37. @tap="item.label === '用户名' ? onUserStatClick() : null">
  38. <view class="base-label-wrap">
  39. <text class="base-label">{{ item.label }}</text>
  40. </view>
  41. <view class="base-value-wrap">
  42. <text class="base-value">{{ item.value }}</text>
  43. <view class="base-actions">
  44. <text v-if="item.viewLogistics" class="view-logistics-btn"
  45. @tap="viewLogistics(item.value, item.expressCompany)">查看物流</text>
  46. <text v-if="item.copy" class="copy-btn" @tap="copyText(item.value)">复制</text>
  47. </view>
  48. <uni-icons v-if="item.arrow" type="right" size="18" color="#bbb" />
  49. </view>
  50. <view v-if="i < baseInfo.length - 1" class="divider"></view>
  51. </view>
  52. </view>
  53. </view>
  54. <!-- 用户统计弹窗 -->
  55. <view v-if="showUserStatModal" class="user-stat-modal-mask">
  56. <view class="user-stat-modal-box">
  57. <view class="user-stat-modal-title">{{ userStatData.name }}</view>
  58. <view class="user-stat-modal-row">
  59. <text class="user-stat-label">回收总次数</text>
  60. <text class="user-stat-value">{{ userStatData.unit_num }}</text>
  61. </view>
  62. <view class="user-stat-modal-divider"></view>
  63. <view class="user-stat-modal-row">
  64. <text class="user-stat-label">总件数</text>
  65. <text class="user-stat-value">{{ userStatData.sum }}</text>
  66. </view>
  67. <view class="user-stat-modal-divider"></view>
  68. <view class="user-stat-modal-row">
  69. <text class="user-stat-label">回收总额</text>
  70. <text class="user-stat-value">{{ userStatData.order_money }}</text>
  71. </view>
  72. <button class="user-stat-modal-btn" @tap="showUserStatModal = false">我了解了</button>
  73. </view>
  74. </view>
  75. <!-- 订单明细卡片开始质检状态 -->
  76. <view v-if="order.status === 2 && order.state === 1" class="info-card detail-card custom-inspect-card">
  77. <view class="custom-header-bg">
  78. <view class="custom-card-title">订单详情</view>
  79. </view>
  80. <view class="custom-detail-content">
  81. <!-- 只显示商品明细 -->
  82. <view v-for="item in order.commonOrderList" :key="item.id" class="custom-goods-row">
  83. <image :src="item.image" class="custom-goods-img" />
  84. <view class="custom-goods-info">
  85. <text class="custom-goods-name">{{ item.title }}</text>
  86. <text class="custom-goods-desc">{{ item.pinName }}</text>
  87. <view class="custom-goods-desc" v-if="item.brandName && item.styleName">品牌{{ item.brandName }} | 款式{{
  88. item.styleName }}</view>
  89. <view class="custom-goods-desc" v-else-if="item.brandName">品牌{{ item.brandName }}</view>
  90. <view class="custom-goods-desc" v-else-if="item.styleName">款式{{ item.styleName }}</view>
  91. <text class="custom-goods-desc" v-else>{{ item.details }}</text>
  92. <view class="custom-goods-meta">
  93. <text class="custom-goods-price" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{ item.onePrice
  94. }}<text class="custom-goods-unit"> /{{ item.unit }}</text></text>
  95. <text class="custom-goods-price" v-else>¥{{ getSinglePriceRange(item.estimatedPrice, item.num) }}<text
  96. class="custom-goods-unit"> /{{ item.unit }}</text></text>
  97. <text class="custom-goods-count">x{{ item.num }}</text>
  98. </view>
  99. </view>
  100. <text class="custom-goods-total" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{
  101. parseFloat(item.estimatedPrice.split('-')[0]).toFixed(2) }}</text>
  102. <text class="custom-goods-total" v-else>¥{{ item.estimatedPrice.replace('-', '~') }}</text>
  103. </view>
  104. </view>
  105. </view>
  106. <!-- 现金打款时展示完整订单详情卡片 -->
  107. <view v-else-if="order.status === 3" class="info-card detail-card custom-inspect-card">
  108. <view class="custom-header-bg">
  109. <view class="custom-card-title">订单详情</view>
  110. </view>
  111. <view class="custom-detail-content">
  112. <view class="custom-detail-row">
  113. <text class="custom-detail-label">订单编号</text>
  114. <text class="custom-detail-value">{{ order.ordeNo || order.id }}</text>
  115. </view>
  116. <view class="custom-detail-row">
  117. <text class="custom-detail-label">预估回收</text>
  118. <text class="custom-detail-value highlight">¥ {{ order.estimatedPrice }}</text>
  119. </view>
  120. <view class="custom-detail-row">
  121. <text class="custom-detail-label">合格结算</text>
  122. <text class="custom-detail-value highlight">¥ {{ order.price || order.estimate }}</text>
  123. </view>
  124. <view class="custom-detail-row">
  125. <text class="custom-detail-label">运费扣除</text>
  126. <text class="custom-detail-value">¥ 0</text>
  127. </view>
  128. <view class="custom-detail-row total-row">
  129. <text class="custom-detail-label">结算金额</text>
  130. <text class="custom-detail-value total highlight">¥ {{ order.price || order.estimate }}</text>
  131. </view>
  132. <view class="custom-divider"></view>
  133. <!-- 商品明细 -->
  134. <view v-for="item in order.commonOrderList" :key="item.id" class="custom-goods-row">
  135. <image :src="item.image" class="custom-goods-img" />
  136. <view class="custom-goods-info">
  137. <text class="custom-goods-name">{{ item.title }}</text>
  138. <text class="custom-goods-desc">{{ item.pinName }}</text>
  139. <view class="custom-goods-desc" v-if="item.brandName && item.styleName">品牌{{ item.brandName }} | 款式{{
  140. item.styleName }}</view>
  141. <view class="custom-goods-desc" v-else-if="item.brandName">品牌{{ item.brandName }}</view>
  142. <view class="custom-goods-desc" v-else-if="item.styleName">款式{{ item.styleName }}</view>
  143. <text class="custom-goods-desc" v-else>{{ item.details }}</text>
  144. <view class="custom-goods-meta">
  145. <text class="custom-goods-price" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{ item.onePrice
  146. }}<text class="custom-goods-unit"> /{{ item.unit }}</text></text>
  147. <text class="custom-goods-price" v-else>¥{{ getSinglePriceRange(item.estimatedPrice, item.num) }}<text
  148. class="custom-goods-unit"> /{{ item.unit }}</text></text>
  149. <text class="custom-goods-count">x{{ item.num }}</text>
  150. </view>
  151. </view>
  152. <text class="custom-goods-total" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{
  153. parseFloat(item.estimatedPrice.split('-')[0]).toFixed(2) }}</text>
  154. <text class="custom-goods-total" v-else>¥{{ item.estimatedPrice.replace('-', '~') }}</text>
  155. </view>
  156. </view>
  157. </view>
  158. <!-- 质检结果卡片 -->
  159. <view v-if="order.status === 3" class="inspect-card">
  160. <view class="inspect-header">
  161. <text class="inspect-title">质检结果</text>
  162. </view>
  163. <view class="inspect-content">
  164. <view v-for="(item, i) in qcInfo" :key="item.label" class="inspect-row">
  165. <text class="inspect-label">{{ item.label }}</text>
  166. <text class="inspect-value">{{ item.value }}</text>
  167. <view v-if="i < qcInfo.length - 1" class="inspect-divider"></view>
  168. </view>
  169. <view class="inspect-btn" @tap="goToInspection">点此查看质检报告详情</view>
  170. </view>
  171. </view>
  172. <!-- 已预约状态下订单详情卡片 -->
  173. <view v-else-if="order.status === 1 || order.status === 0" class="info-card detail-card custom-inspect-card">
  174. <view class="custom-header-bg">
  175. <view class="custom-card-title">订单详情</view>
  176. </view>
  177. <view class="custom-detail-content">
  178. <view class="custom-detail-row">
  179. <text class="custom-detail-label">预估回收</text>
  180. <text class="custom-detail-value highlight">¥ {{ order.estimatedPrice }}</text>
  181. </view>
  182. <view class="custom-divider"></view>
  183. <!-- 商品明细 -->
  184. <view v-for="item in order.commonOrderList" :key="item.id" class="custom-goods-row">
  185. <image :src="item.image" class="custom-goods-img" />
  186. <view class="custom-goods-info">
  187. <text class="custom-goods-name">{{ item.title }}</text>
  188. <text class="custom-goods-desc">{{ item.pinName }}</text>
  189. <view class="custom-goods-desc" v-if="item.brandName && item.styleName">品牌{{ item.brandName }} | 款式{{
  190. item.styleName }}</view>
  191. <view class="custom-goods-desc" v-else-if="item.brandName">品牌{{ item.brandName }}</view>
  192. <view class="custom-goods-desc" v-else-if="item.styleName">款式{{ item.styleName }}</view>
  193. <text class="custom-goods-desc" v-else>{{ item.details }}</text>
  194. <view class="custom-goods-meta">
  195. <text class="custom-goods-price" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{ item.onePrice
  196. }}<text class="custom-goods-unit"> /{{ item.unit }}</text></text>
  197. <text class="custom-goods-price" v-else>¥{{ getSinglePriceRange(item.estimatedPrice, item.num) }}<text
  198. class="custom-goods-unit"> /{{ item.unit }}</text></text>
  199. <text class="custom-goods-count">x{{ item.num }}</text>
  200. </view>
  201. </view>
  202. <text class="custom-goods-total" v-if="shouldShowSinglePrice(item.estimatedPrice)">¥{{
  203. parseFloat(item.estimatedPrice.split('-')[0]).toFixed(2) }}</text>
  204. <text class="custom-goods-total" v-else>¥{{ item.estimatedPrice.replace('-', '~') }}</text>
  205. </view>
  206. </view>
  207. </view>
  208. </view>
  209. <!-- 底部操作按钮 -->
  210. <view v-if="order.status === 0" class="footer-btns">
  211. <button class="btn-outline" @tap="rejectOrderAction">驳回</button>
  212. <button class="btn-main" @tap="passOrderAction">通过</button>
  213. </view>
  214. <view v-else-if="order.status === 2 && order.state === 1" class="footer-btns">
  215. <button class="btn-main" @tap="goToInspect">开始质检</button>
  216. </view>
  217. </view>
  218. </template>
  219. <script>
  220. import pullRefreshMixin from '../mixins/pullRefreshMixin.js'
  221. export default {
  222. mixins: [pullRefreshMixin],
  223. data() {
  224. return {
  225. statusBarHeight: 0,
  226. order: {},
  227. // 固定4步流程
  228. steps: [],
  229. currentStep: 0,
  230. baseInfo: [],
  231. qcInfo: [],
  232. showUserStatModal: false,
  233. userStatData: { name: '', unit_num: '', sum: '', order_money: '' }
  234. }
  235. },
  236. onLoad(options) {
  237. this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
  238. this.getAreaList();
  239. if (options && options.id) {
  240. this.fetchOrderDetail(options.id)
  241. }
  242. },
  243. onShow() {
  244. this.getAreaList()
  245. },
  246. methods: {
  247. async fetchOrderDetail(orderId) {
  248. this.$api('getOrderDetail', { orderId }, res => {
  249. if (res && res.code === 200 && res.result) {
  250. const data = res.result
  251. this.order = data
  252. // 步骤条高亮
  253. this.currentStep = this.getCurrentStep(data.status, data.state)
  254. // 基础信息
  255. this.baseInfo = [
  256. { label: '订单编号', value: data.ordeNo || data.id, copy: true },
  257. ...(data.wliuNo && data.wliu ? [{ label: '快递单号', value: data.wliuNo, copy: true, viewLogistics: true, expressCompany: data.wliu }] : []),
  258. { label: '用户名', value: data.name, arrow: true },
  259. { label: '取件地址', value: (data.address || '') + (data.addressDetail || '') },
  260. { label: '预约时间', value: data.goTime || data.createTime }
  261. ]
  262. // 质检信息(如有)
  263. let sum = (data.qualifiedNum || 0) + (data.noQualifiedNum || 0) + (data.unrecyclable || 0)
  264. this.qcInfo = [
  265. { label: '质检数量', value: sum ? sum + ' 件' : '' },
  266. { label: '质检合格', value: data.qualifiedNum ? data.qualifiedNum + ' 件' : '' },
  267. { label: '质量问题', value: data.noQualifiedNum ? data.noQualifiedNum + ' 件' : '' },
  268. { label: '不可回收', value: data.unrecyclable ? data.unrecyclable + ' 件' : '' }
  269. ]
  270. }
  271. })
  272. },
  273. getCurrentStep(status, state) {
  274. // status: 0=在线预约, 1=快递上门, 2=透明质检, 3=现金打款
  275. // state: 0=待取件, 1=已取件, 2=已完成, 3=已取消
  276. if (state == 3) return -1; // 已取消,不高亮任何步骤
  277. if (status == 0) return 0; // 在线预约
  278. if (status == 1) return 1; // 快递上门
  279. if (status == 2) return 2; // 透明质检
  280. if (status == 3) return 3; // 现金打款
  281. return 0;
  282. },
  283. async onRefresh() {
  284. if (this.order.id) await this.fetchOrderDetail(this.order.id)
  285. },
  286. goBack() {
  287. uni.navigateBack()
  288. },
  289. copyText(text) {
  290. uni.setClipboardData({ data: text })
  291. },
  292. viewLogistics(expressNumber, expressCompany) {
  293. if (!expressNumber) {
  294. uni.showToast({ title: '暂无快递单号', icon: 'none' });
  295. return;
  296. }
  297. uni.navigateTo({
  298. url: `/pages/subcomponent/logistics?wliuNo=${expressNumber}&expressCompany=${expressCompany || ''}`
  299. });
  300. },
  301. maskPhone(phone) {
  302. if (!phone) return '';
  303. return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  304. },
  305. goToInspect() {
  306. const orderData = encodeURIComponent(JSON.stringify(this.order))
  307. uni.navigateTo({
  308. url: `/pages/manager/inspect?orderData=${orderData}`
  309. })
  310. },
  311. async onUserStatClick() {
  312. if (!this.order || !this.order.userId) return;
  313. this.$api('getUserOrderNum', { userId: this.order.userId }, res => {
  314. if (res && res.code === 200 && res.result) {
  315. this.userStatData = {
  316. name: this.order.name,
  317. unit_num: res.result.unit_num,
  318. sum: res.result.sum,
  319. order_money: res.result.order_money
  320. }
  321. this.showUserStatModal = true
  322. }
  323. })
  324. },
  325. getAreaList() {
  326. this.$api('getAreaList', {}, (res) => {
  327. console.log(res, 'getAreaList');
  328. if (res.code == 200 && Array.isArray(res.result)) {
  329. // 按sort升序排序
  330. const sorted = res.result.slice().sort((a, b) => a.sort - b.sort)
  331. this.steps = sorted.map(item => ({
  332. // id: item.id,
  333. icon: item.image,
  334. text: item.title
  335. }))
  336. }
  337. })
  338. },
  339. passOrderAction() {
  340. if (!this.order.id) return;
  341. this.$api('passOrder', { orderId: this.order.id }, res => {
  342. if (res && res.code === 200) {
  343. uni.showToast({ title: '操作成功', icon: 'success' })
  344. this.fetchOrderDetail(this.order.id)
  345. } else {
  346. uni.showToast({ title: res.msg || '操作失败', icon: 'none' })
  347. }
  348. })
  349. },
  350. rejectOrderAction() {
  351. if (!this.order.id) return;
  352. this.$api('rejectOrder', { orderId: this.order.id }, res => {
  353. if (res && res.code === 200) {
  354. uni.showToast({ title: '操作成功', icon: 'success' })
  355. this.fetchOrderDetail(this.order.id)
  356. } else {
  357. uni.showToast({ title: res.msg || '操作失败', icon: 'none' })
  358. }
  359. })
  360. },
  361. goToInspection() {
  362. if (!this.order.id) return;
  363. uni.navigateTo({
  364. url: `/pages/subcomponent/inspection-report?orderId=${this.order.id}&isSelf=true`
  365. })
  366. },
  367. // 判断是否显示单价格(根据estimatedPrice格式判断)
  368. shouldShowSinglePrice(estimatedPrice) {
  369. if (!estimatedPrice || typeof estimatedPrice !== 'string') return true;
  370. // 解析estimatedPrice,格式如"25-0"或"25-50"
  371. const parts = estimatedPrice.split('-');
  372. if (parts.length !== 2) return true;
  373. const maxPrice = parseFloat(parts[1]);
  374. return maxPrice === 0;
  375. },
  376. // 从总价反推单件价格区间
  377. getSinglePriceRange(estimatedPrice, quantity) {
  378. if (!estimatedPrice || typeof estimatedPrice !== 'string' || !quantity) return '';
  379. // 解析estimatedPrice,格式如"25-50"
  380. const parts = estimatedPrice.split('-');
  381. if (parts.length !== 2) return estimatedPrice;
  382. const minTotal = parseFloat(parts[0]);
  383. const maxTotal = parseFloat(parts[1]);
  384. // 反推单件价格
  385. const minPrice = (minTotal / quantity).toFixed(2);
  386. const maxPrice = (maxTotal / quantity).toFixed(2);
  387. return `${minPrice}~${maxPrice}`;
  388. },
  389. }
  390. }
  391. </script>
  392. <style lang="scss" scoped>
  393. $order-card-radius: 32px;
  394. $order-card-padding: 40px 28px;
  395. .order-detail-container {
  396. background: #f8f8f8;
  397. min-height: 100vh;
  398. font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
  399. display: flex;
  400. flex-direction: column;
  401. align-items: center;
  402. }
  403. .nav-bar {
  404. display: flex;
  405. align-items: center;
  406. height: calc(150rpx + var(--status-bar-height));
  407. padding: 0 32rpx;
  408. padding-top: var(--status-bar-height);
  409. background: #fff;
  410. position: fixed;
  411. top: 0;
  412. left: 0;
  413. right: 0;
  414. z-index: 999;
  415. box-sizing: border-box;
  416. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
  417. .back {
  418. padding: 20rpx;
  419. margin-left: -20rpx;
  420. }
  421. .nav-title {
  422. flex: 1;
  423. text-align: center;
  424. font-size: 32rpx;
  425. font-weight: 500;
  426. color: #222;
  427. }
  428. .nav-icons {
  429. display: flex;
  430. align-items: center;
  431. gap: 30px;
  432. }
  433. }
  434. .main-content {
  435. margin-top: calc(200rpx + var(--status-bar-height));
  436. width: 100vw;
  437. min-width: 0;
  438. box-sizing: border-box;
  439. padding: 0 16px 60px 16px;
  440. display: flex;
  441. flex-direction: column;
  442. align-items: center;
  443. }
  444. .info-card {
  445. width: 100%;
  446. max-width: 375px;
  447. min-width: 0;
  448. box-sizing: border-box;
  449. margin: 0 auto 28px auto;
  450. padding: 16px 8px;
  451. background: #fff;
  452. border-radius: 20px;
  453. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
  454. // color: blue;
  455. &.process-card {
  456. position: relative;
  457. .status-tag {
  458. position: absolute;
  459. right: 18px;
  460. top: 18px;
  461. font-size: 13px;
  462. border-radius: 12px;
  463. padding: 2px 10px;
  464. font-weight: 500;
  465. background: #e6f9e6;
  466. color: #1ecb1e;
  467. height: 22px;
  468. line-height: 18px;
  469. display: flex;
  470. align-items: center;
  471. &.orange {
  472. background: #fff7e6;
  473. color: #ffb400;
  474. font-size: 14px;
  475. border-radius: 12px;
  476. padding: 2px 14px;
  477. font-weight: 400;
  478. height: 22px;
  479. display: flex;
  480. align-items: center;
  481. }
  482. &.gray {
  483. background: #f5f5f5;
  484. color: #444;
  485. font-size: 14px;
  486. border-radius: 12px;
  487. padding: 2px 14px;
  488. font-weight: 400;
  489. height: 22px;
  490. display: flex;
  491. align-items: center;
  492. }
  493. &.blue {
  494. background: #eaf6ff;
  495. color: #2a9cfb;
  496. font-size: 14px;
  497. border-radius: 12px;
  498. padding: 2px 14px;
  499. font-weight: 400;
  500. height: 22px;
  501. display: flex;
  502. align-items: center;
  503. }
  504. }
  505. .status-tag.red {
  506. background: #fff0f0;
  507. color: #ff4d4f;
  508. }
  509. }
  510. .info-card-header {
  511. display: flex;
  512. align-items: center;
  513. justify-content: flex-start;
  514. .info-title-wrap {
  515. display: flex;
  516. align-items: center;
  517. .info-title {
  518. font-size: 18px;
  519. font-weight: bold;
  520. color: #222;
  521. }
  522. .tag-nobaoyou {
  523. margin-left: 8px;
  524. background: #fff0f0;
  525. color: #ff4d4f;
  526. font-size: 14px;
  527. border-radius: 12px;
  528. padding: 2px 14px;
  529. font-weight: 400;
  530. height: 22px;
  531. display: flex;
  532. align-items: center;
  533. }
  534. }
  535. .status-tag {
  536. margin-left: auto;
  537. }
  538. }
  539. .steps-bar {
  540. display: flex;
  541. justify-content: space-between;
  542. align-items: flex-start;
  543. margin: 24px 0 24px 0;
  544. gap: 14px;
  545. .step-item {
  546. background: #fff8ea;
  547. border-radius: 16px;
  548. width: 76px;
  549. height: 76px;
  550. display: flex;
  551. flex-direction: column;
  552. align-items: center;
  553. box-shadow: 0 2px 8px rgba(255, 156, 0, 0.04);
  554. overflow: hidden;
  555. position: relative;
  556. transition: all 0.3s ease;
  557. .step-icon {
  558. width: 44px;
  559. height: 44px;
  560. margin: 6px 0 0 0;
  561. transition: transform 0.3s ease;
  562. }
  563. .step-label {
  564. width: 100%;
  565. height: 28px;
  566. background: transparent;
  567. position: absolute;
  568. left: 0;
  569. bottom: 0;
  570. border-radius: 0 0 12px 12px;
  571. display: flex;
  572. align-items: center;
  573. justify-content: center;
  574. transition: all 0.3s ease;
  575. overflow: hidden;
  576. line-height: 1;
  577. .step-num,
  578. .step-text {
  579. font-size: 11px;
  580. color: #9b9b9b;
  581. font-weight: 600;
  582. line-height: 1;
  583. vertical-align: middle;
  584. transition: color 0.3s ease;
  585. }
  586. .step-num {
  587. margin-right: 2px;
  588. }
  589. }
  590. &.active {
  591. transform: translateY(-2px);
  592. box-shadow: 0 4px 12px rgba(255, 156, 0, 0.08);
  593. .step-icon {
  594. transform: scale(1.05);
  595. }
  596. .step-label {
  597. background: linear-gradient(90deg, #ffd01e 0%, #ff8917 100%);
  598. }
  599. .step-num,
  600. .step-text {
  601. color: #fff;
  602. font-weight: bold;
  603. }
  604. }
  605. &:not(.active):hover {
  606. transform: translateY(-1px);
  607. box-shadow: 0 2px 12px rgba(255, 156, 0, 0.06);
  608. }
  609. }
  610. }
  611. .base-info {
  612. margin-top: 18px;
  613. .base-info-row {
  614. display: flex;
  615. align-items: flex-start;
  616. padding: 0 0 0 0;
  617. position: relative;
  618. min-height: 36px;
  619. justify-content: space-between;
  620. align-items: center;
  621. .base-label-wrap {
  622. min-width: 80px;
  623. text-align: left;
  624. color: #8b8b8b;
  625. font-size: 13px;
  626. }
  627. .base-value-wrap {
  628. display: flex;
  629. align-items: center;
  630. flex: 1;
  631. justify-content: flex-end;
  632. }
  633. .base-value {
  634. color: #222;
  635. font-size: 15px;
  636. font-weight: 600;
  637. word-break: break-all;
  638. text-align: right;
  639. }
  640. .base-actions {
  641. display: flex;
  642. align-items: center;
  643. gap: 8px;
  644. }
  645. .view-logistics-btn {
  646. color: #2a9cfb;
  647. font-size: 13px;
  648. font-weight: 400;
  649. padding: 1px 6px;
  650. border-radius: 6px;
  651. transition: background 0.2s;
  652. }
  653. .view-logistics-btn:active {
  654. background: #eaf6ff;
  655. }
  656. .copy-btn {
  657. color: #ffb400;
  658. font-size: 13px;
  659. font-weight: 400;
  660. padding: 1px 6px;
  661. border-radius: 6px;
  662. transition: background 0.2s;
  663. }
  664. .copy-btn:active {
  665. background: #fff7e6;
  666. }
  667. .divider {
  668. position: absolute;
  669. left: 16px;
  670. right: 16px;
  671. bottom: -1px;
  672. height: 1px;
  673. background: #f0f0f0;
  674. }
  675. uni-icons {
  676. margin-left: 6px;
  677. }
  678. }
  679. .base-info-row .base-value-wrap {
  680. flex-wrap: wrap;
  681. }
  682. .base-info-row .base-value {
  683. white-space: pre-line;
  684. line-height: 1.5;
  685. }
  686. }
  687. &.detail-card {
  688. position: relative;
  689. padding: 0;
  690. background: transparent;
  691. .card-header-bg {
  692. height: 56px;
  693. background: linear-gradient(180deg, #fff7e6 0%, #fff 100%);
  694. border-radius: 20px 20px 0 0;
  695. display: flex;
  696. align-items: flex-end;
  697. padding-left: 20px;
  698. padding-top: 12px;
  699. }
  700. .detail-content {
  701. background: #fff;
  702. border-radius: 0 0 20px 20px;
  703. padding: 20px 16px 16px 16px;
  704. }
  705. .detail-row {
  706. display: flex;
  707. align-items: center;
  708. min-height: 32px;
  709. .detail-label {
  710. color: #bcbcbc;
  711. font-size: 14px;
  712. width: 90px;
  713. flex-shrink: 0;
  714. }
  715. .detail-value {
  716. color: #222;
  717. font-size: 15px;
  718. font-weight: 500;
  719. text-align: left;
  720. margin-left: 0;
  721. flex: 1;
  722. }
  723. &.total-row .detail-value.total {
  724. color: #ffb400;
  725. font-size: 20px;
  726. font-weight: bold;
  727. text-align: right;
  728. flex: unset;
  729. margin-left: auto;
  730. min-width: 100px;
  731. }
  732. .orange {
  733. color: #ffb400;
  734. }
  735. }
  736. .divider {
  737. height: 1px;
  738. background: #f0f0f0;
  739. margin: 16px 0;
  740. }
  741. .goods-row {
  742. display: flex;
  743. align-items: flex-start;
  744. margin-bottom: 18px;
  745. }
  746. .goods-img {
  747. width: 48px;
  748. height: 48px;
  749. border-radius: 12px;
  750. margin-right: 12px;
  751. }
  752. .goods-info {
  753. flex: 1;
  754. }
  755. .goods-name {
  756. font-size: 15px;
  757. font-weight: bold;
  758. color: #222;
  759. margin-bottom: 2px;
  760. }
  761. .goods-desc {
  762. font-size: 13px;
  763. color: #bbb;
  764. margin-bottom: 8px;
  765. }
  766. .goods-price-row {
  767. display: flex;
  768. align-items: center;
  769. }
  770. .goods-price {
  771. color: #ffb400;
  772. font-size: 15px;
  773. font-weight: bold;
  774. margin-right: 8px;
  775. }
  776. .goods-unit {
  777. font-size: 13px;
  778. color: #bbb;
  779. }
  780. .goods-count {
  781. color: #bbb;
  782. font-size: 13px;
  783. margin-left: 8px;
  784. }
  785. .goods-total {
  786. color: #222;
  787. font-size: 15px;
  788. font-weight: bold;
  789. margin-left: auto;
  790. }
  791. }
  792. }
  793. .footer-btns {
  794. position: fixed;
  795. left: 0;
  796. right: 0;
  797. bottom: 0;
  798. background: #fff;
  799. display: flex;
  800. gap: 16px;
  801. padding: 16px 24px 32px 24px;
  802. z-index: 101;
  803. .btn-outline {
  804. flex: 1;
  805. height: 40px;
  806. border-radius: 16px;
  807. border: 1px solid #ffe09a;
  808. color: #ffb400;
  809. background: #fff0d2;
  810. font-size: 15px;
  811. font-weight: 500;
  812. box-shadow: none;
  813. padding: 0 18px;
  814. }
  815. .btn-main {
  816. flex: 1;
  817. height: 40px;
  818. border-radius: 16px;
  819. background: linear-gradient(90deg, #ffbe3d 0%, #ffac04 100%);
  820. color: #fff;
  821. border: none;
  822. font-size: 15px;
  823. font-weight: 500;
  824. box-shadow: none;
  825. padding: 0 18px;
  826. }
  827. }
  828. .cancel-reason {
  829. color: #bbb;
  830. font-size: 13px;
  831. margin-bottom: 10px;
  832. padding-left: 2px;
  833. }
  834. .inspect-card {
  835. max-width: 335px;
  836. width: 100%;
  837. margin: 0 auto 28px auto;
  838. border-radius: 24px;
  839. overflow: hidden;
  840. .inspect-header {
  841. height: 60px;
  842. background: linear-gradient(180deg, #fff7e6 0%, #fffbe6 100%);
  843. border-radius: 24px 24px 0 0;
  844. display: flex;
  845. align-items: flex-end;
  846. padding-left: 24px;
  847. }
  848. .inspect-title {
  849. font-size: 20px;
  850. font-weight: bold;
  851. color: #222;
  852. line-height: 60px;
  853. }
  854. .inspect-content {
  855. background: #fff;
  856. border-radius: 0 0 24px 24px;
  857. padding: 24px 24px 20px 24px;
  858. }
  859. .inspect-row {
  860. display: flex;
  861. align-items: center;
  862. justify-content: space-between;
  863. min-height: 40px;
  864. .inspect-label {
  865. color: #bcbcbc;
  866. font-size: 16px;
  867. }
  868. .inspect-value {
  869. color: #222;
  870. font-size: 16px;
  871. font-weight: 500;
  872. }
  873. .inspect-divider {
  874. height: 1px;
  875. background: #f0f0f0;
  876. margin: 0;
  877. width: 100%;
  878. position: absolute;
  879. left: 0;
  880. bottom: 0;
  881. }
  882. position: relative;
  883. }
  884. .inspect-btn {
  885. width: 100%;
  886. margin: 24px 0 0 0;
  887. height: 48px;
  888. border-radius: 24px;
  889. border: 1.5px solid #ffb400;
  890. color: #ffb400;
  891. background: #fff0d2;
  892. font-size: 18px;
  893. font-weight: 500;
  894. box-shadow: none;
  895. text-align: center;
  896. line-height: 48px;
  897. }
  898. }
  899. .card-title {
  900. font-size: 20px;
  901. font-weight: bold;
  902. color: #222;
  903. margin-bottom: 12px;
  904. }
  905. .custom-inspect-card {
  906. background: linear-gradient(180deg, #fffbe6 0%, #fff 90%);
  907. border-radius: 20px;
  908. box-shadow: 0 8px 24px rgba(255, 156, 0, 0.03);
  909. padding: 0;
  910. margin: 24rpx 0 0 0;
  911. overflow: hidden;
  912. }
  913. .custom-header-bg {
  914. height: 56px;
  915. background: linear-gradient(180deg, #fff7e6 0%, #fff 100%);
  916. border-radius: 20px 20px 0 0;
  917. display: flex;
  918. align-items: flex-end;
  919. padding-left: 24px;
  920. padding-top: 12px;
  921. }
  922. .custom-card-title {
  923. font-size: 20px;
  924. font-weight: bold;
  925. color: #222;
  926. margin-bottom: 12px;
  927. }
  928. .custom-detail-content {
  929. background: #fff;
  930. border-radius: 0 0 20px 20px;
  931. padding: 20px 16px 16px 16px;
  932. }
  933. .custom-detail-row {
  934. display: flex;
  935. align-items: center;
  936. min-height: 32px;
  937. font-size: 15px;
  938. .custom-detail-label {
  939. color: #bcbcbc;
  940. font-size: 14px;
  941. width: 90px;
  942. flex-shrink: 0;
  943. }
  944. .custom-detail-value {
  945. color: #222;
  946. font-size: 15px;
  947. font-weight: 500;
  948. text-align: left;
  949. margin-left: 0;
  950. flex: 1;
  951. }
  952. &.total-row .custom-detail-value.total {
  953. color: #ffb400;
  954. font-size: 20px;
  955. font-weight: bold;
  956. text-align: right;
  957. flex: unset;
  958. margin-left: auto;
  959. min-width: 100px;
  960. }
  961. .highlight {
  962. color: #ffb400;
  963. font-size: 18px;
  964. font-weight: bold;
  965. }
  966. }
  967. .custom-divider {
  968. width: 100%;
  969. height: 1px;
  970. background: #f0f0f0;
  971. margin: 16px 0;
  972. }
  973. .custom-goods-row {
  974. background: #fff;
  975. border-radius: 24px;
  976. display: flex;
  977. align-items: center;
  978. padding: 24px 16px;
  979. box-shadow: 0 2px 8px rgba(255, 156, 0, 0.04);
  980. position: relative;
  981. margin-bottom: 18px;
  982. min-width: 0;
  983. flex-wrap: nowrap;
  984. }
  985. .custom-goods-img {
  986. width: 48px;
  987. height: 48px;
  988. border-radius: 12px;
  989. margin-right: 12px;
  990. flex-shrink: 0;
  991. }
  992. .custom-goods-info {
  993. flex: 1;
  994. display: flex;
  995. flex-direction: column;
  996. justify-content: center;
  997. min-width: 0;
  998. margin-right: 16px;
  999. }
  1000. .custom-goods-name {
  1001. font-size: 15px;
  1002. color: #222;
  1003. font-weight: bold;
  1004. margin-bottom: 2px;
  1005. overflow: hidden;
  1006. text-overflow: ellipsis;
  1007. white-space: nowrap;
  1008. }
  1009. .custom-goods-desc {
  1010. font-size: 13px;
  1011. color: #bcbcbc;
  1012. margin-bottom: 8px;
  1013. overflow: hidden;
  1014. text-overflow: ellipsis;
  1015. white-space: nowrap;
  1016. &:last-child {
  1017. margin-bottom: 10px;
  1018. }
  1019. }
  1020. .custom-goods-meta {
  1021. display: flex;
  1022. align-items: center;
  1023. gap: 10px;
  1024. flex-wrap: nowrap;
  1025. white-space: nowrap;
  1026. }
  1027. .custom-goods-price {
  1028. color: #ffb400;
  1029. font-size: 15px;
  1030. font-weight: bold;
  1031. white-space: nowrap;
  1032. flex-shrink: 0;
  1033. }
  1034. .custom-goods-unit {
  1035. color: #bcbcbc;
  1036. font-size: 13px;
  1037. font-weight: normal;
  1038. }
  1039. .custom-goods-count {
  1040. color: #bcbcbc;
  1041. font-size: 13px;
  1042. margin-left: 8px;
  1043. white-space: nowrap;
  1044. flex-shrink: 0;
  1045. }
  1046. .custom-goods-total {
  1047. color: #222;
  1048. font-size: 15px;
  1049. font-weight: bold;
  1050. margin-left: 12px;
  1051. flex-shrink: 0;
  1052. white-space: nowrap;
  1053. min-width: 60px;
  1054. text-align: right;
  1055. }
  1056. .user-stat-modal-mask {
  1057. position: fixed;
  1058. left: 0;
  1059. right: 0;
  1060. top: 0;
  1061. bottom: 0;
  1062. background: rgba(0, 0, 0, 0.45);
  1063. z-index: 9999;
  1064. display: flex;
  1065. align-items: center;
  1066. justify-content: center;
  1067. }
  1068. .user-stat-modal-box {
  1069. width: 80vw;
  1070. max-width: 420px;
  1071. background: linear-gradient(180deg, #fff7f2 0%, #fff 100%);
  1072. border-radius: 32px;
  1073. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
  1074. padding: 38px 0 32px 0;
  1075. display: flex;
  1076. flex-direction: column;
  1077. align-items: center;
  1078. }
  1079. .user-stat-modal-title {
  1080. font-size: 24px;
  1081. font-weight: bold;
  1082. color: #222;
  1083. text-align: center;
  1084. margin-bottom: 32px;
  1085. }
  1086. .user-stat-modal-row {
  1087. width: 80%;
  1088. display: flex;
  1089. align-items: center;
  1090. justify-content: space-between;
  1091. font-size: 18px;
  1092. margin: 0 auto;
  1093. padding: 12px 0;
  1094. }
  1095. .user-stat-label {
  1096. color: #999;
  1097. font-size: 16px;
  1098. }
  1099. .user-stat-value {
  1100. color: #222;
  1101. font-size: 18px;
  1102. font-weight: 500;
  1103. }
  1104. .user-stat-modal-divider {
  1105. width: 80%;
  1106. height: 1px;
  1107. background: #f0f0f0;
  1108. margin: 0 auto;
  1109. }
  1110. .user-stat-modal-btn {
  1111. width: 80%;
  1112. margin: 32px auto 0 auto;
  1113. height: 48px;
  1114. border-radius: 24px;
  1115. background: #fffbe6;
  1116. color: #ffb400;
  1117. font-size: 20px;
  1118. font-weight: bold;
  1119. border: 2px solid #ffd01e;
  1120. text-align: center;
  1121. line-height: 48px;
  1122. }
  1123. .user-stat-trigger {
  1124. color: #222;
  1125. font-weight: 500;
  1126. cursor: pointer;
  1127. }
  1128. </style>