index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <template>
  2. <view class="detail-page-container">
  3. <erp-nav-bar title="订单详情" />
  4. <!-- 2. 状态横幅区域:不再包含状态栏边距 -->
  5. <view class="status-banner-container" :class="order.statusType" id="nav-header">
  6. <view class="status-banner-content">
  7. <view class="header-main">
  8. <text class="status-title">{{ order.statusName }}</text>
  9. <text class="status-sub">{{ statusSubText }}</text>
  10. </view>
  11. <view class="header-icon-wrap">
  12. <view class="status-visual-icon"></view>
  13. </view>
  14. </view>
  15. </view>
  16. <!-- 2. 主内容滚动区:计算锁定高度以支持 100% 滚动 -->
  17. <scroll-view scroll-y class="main-content-scroll" :style="{ height: scrollHeight }" :show-scrollbar="false">
  18. <view class="detail-inner-box">
  19. <!-- 卡片:规格清单 (支持多个型号展现) -->
  20. <view class="model-item-card-inner" v-for="(model, mIdx) in (order.models || [order])" :key="mIdx">
  21. <view class="data-group-card" :class="{ 'first-card': mIdx === 0 }">
  22. <view class="card-head">规格清单 #{{ mIdx + 1 }}</view>
  23. <view class="data-item"><text class="l">产品型号</text><text class="v bold">{{ model.type }}</text>
  24. </view>
  25. <view class="data-item"><text class="l">型号名称</text><text class="v">{{ model.typeName ||
  26. '铝型材主料' }}</text></view>
  27. <view class="data-item"><text class="l">单据编号</text><text class="v">{{ model.docCode || '-' }}</text></view>
  28. <view class="data-item"><text class="l">项目号</text><text class="v">{{ model.itemNo || '-' }}</text></view>
  29. <view class="data-item"><text class="l">型材材质</text><text class="v">{{ model.material ||
  30. '6063-T5' }}</text></view>
  31. <view class="line-split"></view>
  32. <view class="data-item"><text class="l">表面处理</text><text class="v">{{ model.surface }}</text>
  33. </view>
  34. <view class="data-item"><text class="l">包装方式</text><text class="v">{{ model.package ||
  35. '普通包装' }}</text></view>
  36. <view class="line-split"></view>
  37. <view class="data-item"><text class="l">订单长度</text><text class="v">{{ model.length }} mm</text>
  38. </view>
  39. <view class="data-item"><text class="l">型材壁厚</text><text class="v">{{ model.wallThickness ||
  40. '1.2' }} mm</text></view>
  41. <view class="data-item"><text class="l">需求支数</text><text class="v highlight">{{ model.count }}
  42. 支</text></view>
  43. </view>
  44. </view>
  45. <!-- 卡片:订单详情 -->
  46. <view class="data-group-card shadow-less">
  47. <view class="card-head">订单详情</view>
  48. <view class="data-item"><text class="l">订单单号</text><text class="v selectable">{{ order.orderNo
  49. }}</text></view>
  50. <view class="data-item"><text class="l">单据编号</text><text class="v selectable">{{ order.docCode || '-' }}</text></view>
  51. <view class="data-item"><text class="l">下单日期</text><text class="v">{{ order.time }}</text></view>
  52. <view class="data-item"><text class="l">支付方式</text><text class="v">月结扣款</text></view>
  53. </view>
  54. <!-- 底部占位 -->
  55. <view class="list-bottom-placeholder"></view>
  56. </view>
  57. </scroll-view>
  58. <!-- 3. 底部固定操作栏 -->
  59. <view class="detail-action-bar-fixed" id="footer-bar">
  60. <view class="action-btn-wrap single" v-if="order.isConfirmed === 0">
  61. <button class="action-btn primary" @click="callSales">呼叫业务员</button>
  62. </view>
  63. <view class="action-btn-wrap single" v-else>
  64. <button class="action-btn primary" @click="goHome">再下一单</button>
  65. </view>
  66. <view class="safe-area-bottom-support"></view>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import ErpNavBar from '@/components/erp-nav-bar.vue';
  72. import { getOrderDetail } from '@/api/erp/order.js';
  73. import { getPhone } from '@/api/system/phone.js';
  74. export default {
  75. components: { ErpNavBar },
  76. data() {
  77. return {
  78. footerHeight: 80, // px
  79. headerHeight: 120, // px
  80. rowId: '',
  81. loading: false,
  82. salesPhone: '',
  83. order: {
  84. orderNo: '-',
  85. docCode: '',
  86. isConfirmed: 1,
  87. statusName: '加载中',
  88. statusType: 'pending',
  89. models: [],
  90. erpNo: '',
  91. time: '-'
  92. }
  93. }
  94. },
  95. computed: {
  96. statusSubText() {
  97. const map = {
  98. pending: '您的订单已提交,正在等待管理端同步确认中...',
  99. finish: '订单已确认,并已成功同步到 ERP 系统中。',
  100. approved: '订单已在 ERP 系统中审核通过。',
  101. reviewed: '订单已由相关负责人签批。',
  102. exFinished: '订单关联的产品型材已挤压完成。',
  103. productionFinish: '订单所含产品型材已全部生产完成!',
  104. cancelled: '该订单已被撤销。'
  105. };
  106. return map[this.order.statusType] || '订单状态更新中';
  107. },
  108. scrollHeight() {
  109. const info = uni.getSystemInfoSync();
  110. const safeBottom = info.safeAreaInsets ? info.safeAreaInsets.bottom : 0;
  111. const statusBarHeight = info.statusBarHeight || 20;
  112. return `calc(100vh - ${statusBarHeight + 164}px - ${80 + safeBottom}px)`;
  113. }
  114. },
  115. onLoad(options) {
  116. if (options.rowId) {
  117. this.rowId = options.rowId;
  118. this.loadOrderDetail();
  119. this.loadPhone();
  120. }
  121. },
  122. methods: {
  123. async loadOrderDetail() {
  124. if (this.loading) return;
  125. this.loading = true;
  126. try {
  127. const res = await getOrderDetail(this.rowId);
  128. const data = res.data || res;
  129. if (!data) {
  130. uni.showToast({ title: '订单不存在', icon: 'none' });
  131. return;
  132. }
  133. // 状态展示映射
  134. const statusMap = {
  135. 0: { name: '待确认', type: 'pending' },
  136. 1: { name: '已确认', type: 'finish' },
  137. 2: { name: '已审核', type: 'approved' },
  138. 3: { name: '已签批', type: 'reviewed' },
  139. 4: { name: '挤压完成', type: 'exFinished' },
  140. 5: { name: '生产完成', type: 'productionFinish' },
  141. 6: { name: '已取消', type: 'cancelled' }
  142. };
  143. const s = statusMap[data.status] || { name: '待确认', type: 'pending' };
  144. this.order = {
  145. orderNo: data.code || '-',
  146. docCode: data.docCode || '',
  147. rowId: data.rowId,
  148. isConfirmed: data.isConfirmed,
  149. statusName: s.name,
  150. statusType: s.type,
  151. time: data.createTime || '-',
  152. totalCount: data.totalCount || 0,
  153. models: (data.details || []).map(d => ({
  154. type: d.modelNum || '未知型号',
  155. typeName: d.modelName || '铝型材主料',
  156. docCode: d.docCode || '',
  157. itemNo: d.itemNo || '',
  158. material: d.material || '6063-T5',
  159. surface: d.surfaceName || '无',
  160. package: d.packName || '普通包装',
  161. length: d.length ? Number(d.length).toFixed(4) : '0',
  162. wallThickness: d.wallThickness ? Number(d.wallThickness).toFixed(4) : '1.2',
  163. count: d.count || 0
  164. }))
  165. };
  166. } catch (e) {
  167. console.error('加载订单详情失败', e);
  168. uni.showToast({ title: e || '加载失败', icon: 'none' });
  169. } finally {
  170. this.loading = false;
  171. }
  172. },
  173. goBack() { uni.navigateBack(); },
  174. callSales() { uni.makePhoneCall({ phoneNumber: this.salesPhone }); },
  175. goHome() { uni.reLaunch({ url: '/pages/order/index' }); },
  176. async loadPhone() {
  177. try {
  178. const res = await getPhone();
  179. this.salesPhone = res.data.salesPhone || '13888888888';
  180. } catch (e) {
  181. console.error('加载联系电话失败', e);
  182. uni.showToast({ title: e || '加载联系电话失败', icon: 'none' });
  183. }
  184. }
  185. }
  186. }
  187. </script>
  188. <style scoped>
  189. /deep/ ::-webkit-scrollbar {
  190. display: none !important;
  191. width: 0 !important;
  192. height: 0 !important;
  193. -webkit-appearance: none;
  194. background: transparent;
  195. }
  196. .detail-page-container {
  197. width: 100vw;
  198. height: 100vh;
  199. background: #f8fbfd;
  200. display: flex;
  201. flex-direction: column;
  202. overflow: hidden;
  203. position: relative;
  204. }
  205. .status-banner-container {
  206. color: #fff;
  207. flex-shrink: 0;
  208. }
  209. .status-banner-container.pending {
  210. background: linear-gradient(135deg, #FF9900 0%, #FFB84D 100%);
  211. }
  212. .status-banner-container.finish {
  213. background: linear-gradient(135deg, #1890FF 0%, #69C0FF 100%);
  214. }
  215. .status-banner-container.approved {
  216. background: linear-gradient(135deg, #52C41A 0%, #95DE64 100%);
  217. }
  218. .status-banner-container.reviewed {
  219. background: linear-gradient(135deg, #722ED1 0%, #B37FEB 100%);
  220. }
  221. .status-banner-container.exFinished {
  222. background: linear-gradient(135deg, #13C2C2 0%, #5CDBD3 100%);
  223. }
  224. .status-banner-container.productionFinish {
  225. background: linear-gradient(135deg, #2F54EB 0%, #85A5FF 100%);
  226. }
  227. .status-banner-container.cancelled {
  228. background: linear-gradient(135deg, #F5222D 0%, #FF7875 100%);
  229. }
  230. /* 状态横幅内容 */
  231. .status-banner-content {
  232. padding: 40rpx;
  233. padding-bottom: 60rpx;
  234. display: flex;
  235. justify-content: space-between;
  236. align-items: center;
  237. }
  238. .header-main {
  239. flex: 1;
  240. }
  241. .status-title {
  242. font-size: 48rpx;
  243. font-weight: bold;
  244. display: block;
  245. margin-bottom: 12rpx;
  246. }
  247. .status-sub {
  248. font-size: 26rpx;
  249. opacity: 0.9;
  250. }
  251. .status-visual-icon {
  252. width: 60rpx;
  253. height: 60rpx;
  254. border: 4rpx solid rgba(255, 255, 255, 0.3);
  255. border-radius: 50%;
  256. opacity: 0.6;
  257. }
  258. /* 修正:移除负边距,并明确滚动方向 */
  259. .main-content-scroll {
  260. width: 100%;
  261. flex: 1;
  262. }
  263. .detail-inner-box {
  264. padding: 30rpx;
  265. padding-top: 10rpx;
  266. }
  267. .data-group-card {
  268. background: #fff;
  269. border-radius: 30rpx;
  270. padding: 40rpx;
  271. margin-bottom: 30rpx;
  272. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
  273. }
  274. /* 第一张卡片增加顶部间隔(红框1优化点) */
  275. .first-card {
  276. margin-top: 20rpx;
  277. }
  278. .card-head {
  279. font-size: 30rpx;
  280. font-weight: bold;
  281. color: #333;
  282. margin-bottom: 30rpx;
  283. border-left: 8rpx solid #C1001C;
  284. padding-left: 20rpx;
  285. }
  286. .line-split {
  287. height: 1rpx;
  288. background: #f5f5f5;
  289. margin: 24rpx 0;
  290. }
  291. .data-item {
  292. display: flex;
  293. justify-content: space-between;
  294. margin-bottom: 24rpx;
  295. font-size: 28rpx;
  296. align-items: center;
  297. }
  298. .l {
  299. color: #999;
  300. }
  301. .v {
  302. color: #333;
  303. font-weight: 500;
  304. }
  305. .v.bold {
  306. font-weight: bold;
  307. font-size: 30rpx;
  308. }
  309. .v.erp-no {
  310. color: #C1001C;
  311. font-weight: bold;
  312. }
  313. .v.highlight {
  314. color: #ff3b30;
  315. font-weight: bold;
  316. font-size: 34rpx;
  317. }
  318. .list-bottom-placeholder {
  319. height: 260rpx;
  320. }
  321. .detail-action-bar-fixed {
  322. position: fixed;
  323. bottom: 0;
  324. left: 0;
  325. right: 0;
  326. background: #fff;
  327. padding: 30rpx 40rpx;
  328. box-shadow: 0 -10rpx 40rpx rgba(0, 0, 0, 0.04);
  329. z-index: 999;
  330. flex-shrink: 0;
  331. }
  332. .action-btn-wrap {
  333. display: flex;
  334. gap: 24rpx;
  335. }
  336. .action-btn {
  337. flex: 1;
  338. height: 96rpx;
  339. border-radius: 48rpx;
  340. display: flex;
  341. align-items: center;
  342. justify-content: center;
  343. font-size: 32rpx;
  344. font-weight: bold;
  345. }
  346. .action-btn.primary {
  347. background: #C1001C;
  348. color: #fff;
  349. border: none;
  350. }
  351. .action-btn.cancel {
  352. background: #fff1f0;
  353. color: #ff3b30;
  354. border: 1rpx solid #ffccc7;
  355. font-weight: normal;
  356. }
  357. .safe-area-bottom-support {
  358. height: constant(safe-area-inset-bottom);
  359. height: env(safe-area-inset-bottom);
  360. }
  361. </style>