detail.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <template>
  2. <div class="w-full min-h-[100%]" v-loading="loading">
  3. <div class="main-container" v-if="Object.values(detail).length">
  4. <el-breadcrumb class="my-[20px]" :separator-icon="ArrowRight">
  5. <el-breadcrumb-item>
  6. <span class="cursor-pointer text-[#333]" @click="router.push('/')">{{ t('index') }}</span>
  7. </el-breadcrumb-item>
  8. <el-breadcrumb-item>
  9. <span class="cursor-pointer text-[#333]" @click="router.push('/order/list')">{{ t('myOrder') }}</span>
  10. </el-breadcrumb-item>
  11. <el-breadcrumb-item >
  12. <span class="text-[#666]">{{ t('orderDetail') }}</span>
  13. </el-breadcrumb-item>
  14. </el-breadcrumb>
  15. <div>
  16. <div class="bg-[#fff] p-[30px] mb-[20px] rounded-[var(--rounded-big)]">
  17. <div class="text-[16px] text-[#333]">
  18. <span>{{ t('orderStatus') }}</span>
  19. <span>{{ detail.status_name.name }}</span>
  20. </div>
  21. <div class="mt-[40px]" v-if="['1','2','3','5'].indexOf(detail.status) != -1">
  22. <el-steps style="flex-basis: 25%;" :active="current" finish-status="success" align-center>
  23. <el-step :title="t('waitPay')" />
  24. <el-step :title="t('waitSend')" />
  25. <el-step :title="t('waitReceive')" />
  26. <el-step :title="t('finish')" />
  27. </el-steps>
  28. </div>
  29. </div>
  30. <div class="bg-[#fff] p-[30px] mb-[20px] rounded-[var(--rounded-big)]">
  31. <div class="text-[16px] text-[#333] mb-[30px]">{{ t('orderInfo') }}</div>
  32. <div class="flex justify-between ">
  33. <div class="w-[540px] text-[#333] text-[14px]">
  34. <div class="mb-[20px]">{{ t('orderNo') }}{{ detail.order_no }}</div>
  35. <div class="mb-[20px]">{{ t('createTime') }}{{ detail.create_time }}</div>
  36. <div class="mb-[20px]" v-if="detail.pay">{{ t('payTypeName') }}{{ detail.pay.type_name }}</div>
  37. <div class="mb-[20px]" v-if="detail.pay">{{ t('payTime') }}{{ detail.pay.pay_time }}</div>
  38. </div>
  39. <div class="w-[1px] h-[124px] bg-[#eee]" v-if="detail.delivery_type != 'virtual'"></div>
  40. <div class="ml-[30px] w-[565px] text-[#333] text-[14px]" v-if="detail.delivery_type != 'virtual'">
  41. <div class="mb-[20px]">{{ t('name') }}{{ detail.taker_name }}</div>
  42. <div class="mb-[20px]">{{t('mobile')}}{{ detail.taker_mobile }}</div>
  43. <div>{{ t('address')}}{{ detail.taker_full_address }}</div>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="bg-[#fff] p-[30px] mb-[20px] rounded-[var(--rounded-big)]">
  48. <div class="mb-[30px] flex items-center cursor-pointer site" v-if="detail.site" @click="router.push({ path: '/shop/detail', query: { site_id: detail.site.site_id } })">
  49. <span class="iconfont icon-Vector-25 text-[#999] mr-[6px] site-style"></span>
  50. <span class="text-[14px] text-[#333] site-style">{{ detail.site.site_name }}</span>
  51. </div>
  52. <div>
  53. <template v-for="(item,index) in detail.order_goods" :key="index">
  54. <div class="flex justify-between items-center mb-[30px]">
  55. <div class="flex cursor-pointer" @click="router.push({ path: '/goods/detail', query: { id: item.goods_id } })">
  56. <div class="flex items-center shrink-0">
  57. <el-image class="w-[100px] h-[100px] rounded-[var(--rounded-mid)]" :src="img(item.goods_image_thumb_small ? item.goods_image_thumb_small : '')" fit="cover">
  58. <template #error>
  59. <img src="@/assets/images/goods_default.png" class="w-[100px] h-[100px] rounded-[var(--rounded-mid)]">
  60. </template>
  61. </el-image>
  62. </div>
  63. <div class="ml-[20px] flex flex-col justify-center">
  64. <div class="text-[14px] text-[#333] font-500 w-[420px] truncate mb-[14px]">{{ item.goods_name }}</div>
  65. <div class="text-[14px] text-[#333] oppoSans-R" v-if="item.sku_name"> 规格:{{ item.sku_name }}</div>
  66. </div>
  67. </div>
  68. <div class="flex items-center justify-center">
  69. <div class="w-[160px] text-[14px] text-[#333] oppoSans-R">¥{{item.price}}</div>
  70. <div class="w-[100px] text-[14px] text-[#333] text-right oppoSans-R">x{{item.num}}</div>
  71. </div>
  72. <div class="w-[80px]">
  73. <div v-if="(item.status != 1) || (item.is_enable_refund == 1)">
  74. <el-button round class="w-[80px] !h-[32px] !border-[#ccc] !text-[#333] oppoSans-M" v-if="item.status != 1" @click="refundDetail(item.order_refund_no)">{{ t('refund') }}</el-button>
  75. <el-button round class="w-[80px] !h-[32px] !border-[#ccc] !text-[#333] oppoSans-M" v-else-if="item.is_enable_refund == 1" @click="applyRefund(item.order_goods_id)">{{ t('applyRefund') }}</el-button>
  76. </div>
  77. </div>
  78. </div>
  79. </template>
  80. </div>
  81. <el-divider border-style="dashed" />
  82. <div class="mb-[20px]">
  83. <div class="flex items-center text-[14px] text-[#666] mb-[20px]">
  84. <span>{{ t('deliveryStyle') }}</span>
  85. <span class="ml-[10px]">{{ detail.delivery_type_name }}</span>
  86. </div>
  87. <div class="flex items-center text-[14px] text-[#666] mb-[20px]">
  88. <span>{{ t('remark') }}</span>
  89. <span class="ml-[10px]">{{ detail.member_remark ? detail.member_remark : '-' }}</span>
  90. </div>
  91. </div>
  92. </div>
  93. <template v-if="isShowVerify">
  94. <div class="p-[30px] bg-[#fff] mb-[20px] rounded-[var(--rounded-big)]" v-if="verifyInfo && verifyInfo.length">
  95. <el-carousel height="220px" v-if="verifyInfo.length > 1" arrow="always" :autoplay="false" indicator-position="none">
  96. <el-carousel-item v-for="(item,index) in verifyInfo" :key="index">
  97. <div class="flex flex-col items-center justify-center">
  98. <el-image style="width: 150px; height: 150px" :src="item.qrcode" :fit="cover" />
  99. </div>
  100. <div class="w-full my-[10px] border-0 border-t-[1px] border-dashed border-[#e5e5e5] "></div>
  101. <div class="flex items-center justify-center">
  102. <span class="text-[14px] font-bold">{{item.code}}</span>
  103. <span class="text-[#666] text-[12px] ml-[5px] border-[1px] border-solid border-[#666] bg-[#f7f7f7] px-[6px] py-[3px] rounded" @click="copy(item.code)">{{ t('copy') }}</span>
  104. </div>
  105. </el-carousel-item>
  106. </el-carousel>
  107. <template v-else>
  108. <div class="flex flex-col items-center justify-center">
  109. <el-image style="width: 150px; height: 150px" :src="verifyInfo[0].qrcode" :fit="cover" />
  110. </div>
  111. <div class="w-full my-[10px] border-0 border-t-[1px] border-dashed border-[#e5e5e5] "></div>
  112. <div class="flex items-center justify-center">
  113. <span class="text-[14px] font-bold">{{verifyInfo[0].code}}</span>
  114. <span class="text-[#666] text-[14px] ml-[5px] border-[1px] border-solid border-[#666] bg-[#f7f7f7] px-[6px] py-[3px] rounded cursor-pointer" @click="copy(verifyInfo[0].code)">{{ t('copy') }}</span>
  115. </div>
  116. </template>
  117. </div>
  118. <div class="p-[30px] bg-[#fff] mb-[20px] rounded-[var(--rounded-big)]">
  119. <div class="pb-[10px] border-0 border-b-[1px] border-dashed border-[#e5e5e5]">核销信息</div>
  120. <div class="flex mt-[15px] justify-between leading-[16px]">
  121. <div class="text-[14px]">{{ t('verifyNum') }}</div>
  122. <div class="price-font font-500 text-[14px]">
  123. {{'剩余'+(verifyGoodsData.num - verifyGoodsData.verify_count)+'次'}}/{{'共'+verifyGoodsData.num+'次'}}
  124. </div>
  125. </div>
  126. <div class="flex mt-[15px] justify-between leading-[16px]">
  127. <div class="text-[14px]">{{ t('validity') }}</div>
  128. <div class="price-font font-500 text-[14px]">
  129. {{verifyGoodsData.verify_expire_time ? verifyGoodsData.verify_expire_time : '永久'}}
  130. </div>
  131. </div>
  132. </div>
  133. </template>
  134. <div class="p-[30px] bg-[#fff] mb-[20px] rounded-[var(--rounded-big)]">
  135. <div class="flex justify-between items-center mb-[20px] text-[14px]">
  136. <span class="text-[#333]">{{ t('goodsMoney') }}</span>
  137. <span class="text-[#666] price-font">¥{{ detail.goods_money }}</span>
  138. </div>
  139. <div class="flex justify-between items-center mb-[20px] text-[14px]">
  140. <span class="text-[#333]">{{ t('deliveryMoney') }}</span>
  141. <span class="text-[#666] price-font">¥{{ detail.delivery_money }}</span>
  142. </div>
  143. <div class="flex justify-between items-center mb-[20px] text-[14px]">
  144. <span class="text-[#333]">{{ t('discountMoney') }}</span>
  145. <span class="text-[#666] price-font">-¥{{ detail.discount_money }}</span>
  146. </div>
  147. <div class="flex justify-between items-center mb-[20px] text-[14px]">
  148. <span class="text-[#333]">{{ t('mallDiscountMoney') }}</span>
  149. <span class="text-[#666] price-font">-¥{{ detail.mall_discount_money }}</span>
  150. </div>
  151. <div class="flex justify-between items-center text-[14px]">
  152. <span class="text-[#333]">{{ t('orderMoney') }}</span>
  153. <div class="text-[var(--el-price)]">
  154. <span class="price-font">¥</span>
  155. <span class="text-[22px] font-600 price-font">{{ detail.order_money }}</span>
  156. </div>
  157. </div>
  158. </div>
  159. <div class="bg-[#fff] text-right py-[18px] px-[30px] rounded-[var(--rounded-big)]" v-if="(detail.status == 1) || (detail.status == 3) || (detail.status == 5 && detail.is_evaluate != 1 && evaluateConfig.is_evaluate == 1) || showLogistics(detail)">
  160. <el-button v-if="detail.status == 1" plain round class="oppoSans-M !text-[#666] !border-[#ccc]" @click="orderBtnFn( 'close')">{{t('orderClose')}}</el-button>
  161. <el-button v-if="detail.status == 1" type="primary" round class="oppoSans-M" @click="orderBtnFn('pay')">{{t('topay')}}</el-button>
  162. <el-button v-if="detail.status == 3" type="primary" round class="oppoSans-M" @click="orderBtnFn('finish')">{{t('orderFinish')}}</el-button>
  163. <template v-if="detail.status == 5">
  164. <el-button v-if="detail.is_evaluate != 1 && evaluateConfig.is_evaluate == 1" plain round lass="oppoSans-M !text-[#666] !border-[#ccc]" @click="orderBtnFn('evaluate')">{{t('evaluate')}}</el-button>
  165. </template>
  166. <el-button v-if="showLogistics(detail)" plain round lass="oppoSans-M !text-[#666] !border-[#ccc]" @click="orderBtnFn('logistics')">{{t('logistics')}}</el-button>
  167. </div>
  168. </div>
  169. </div>
  170. <!-- 评价 -->
  171. <add-evaluate ref="addEvaluateRef" @complete="getOrderInfoFn(orderId)" ></add-evaluate>
  172. <!-- 物流包裹 -->
  173. <delivery-package ref="packageRef"></delivery-package>
  174. <!-- 组合支付 -->
  175. <group-pay ref="groupPayRef"></group-pay>
  176. <!-- 组合关闭订单 -->
  177. <group-close ref="groupCloseRef" @confirm="closeGroupOrder"></group-close>
  178. </div>
  179. </template>
  180. <script lang="ts" setup>
  181. import { computed, ref, watch ,reactive} from 'vue'
  182. import { copy } from '@/utils/common'
  183. import { ArrowRight } from '@element-plus/icons-vue'
  184. import { ElMessage, ElMessageBox } from 'element-plus'
  185. import { useRouter, useRoute } from 'vue-router'
  186. import { getEvaluateConfig } from '@/addon/mall/api/goods'
  187. import { getMallOrderDetail, getMallOrderPay,orderClose, orderFinish, getMallOrderClose} from '@/addon/mall/api/order'
  188. import { getVerifyCode } from '@/app/api/verify';
  189. import AddEvaluate from '@/addon/mall/pages/order/components/add-evaluate.vue'
  190. import deliveryPackage from '@/addon/mall/pages/order/components/delivery-package.vue'
  191. import GroupPay from '@/addon/mall/pages/order/components/group-pay.vue'
  192. import GroupClose from '@/addon/mall/pages/order/components/group-close.vue'
  193. definePageMeta({
  194. middleware: "auth"
  195. });
  196. const route = useRoute()
  197. const router = useRouter()
  198. let detail = ref({})
  199. let orderId = route.query.order_id
  200. let loading = ref(false);
  201. const getOrderInfoFn = (order_id:any) => {
  202. loading.value = true
  203. getMallOrderDetail(order_id).then((res:any) => {
  204. detail.value = res.data
  205. if(res.data.order_goods && res.data.order_goods.length && isShowVerify.value){
  206. let obj = {};
  207. obj.order_goods_id = res.data.order_goods[0].order_goods_id
  208. obj.site_id = res.data.order_goods[0].site_id
  209. getVerifyCodeFn(obj);
  210. }
  211. getStatus()
  212. loading.value = false
  213. }).catch(() => {
  214. loading.value = false
  215. })
  216. }
  217. getOrderInfoFn(orderId)
  218. const orderBtnFn = (type = '') => {
  219. if (type == 'pay'){
  220. payFn(detail.value)
  221. }else if (type == 'close') {
  222. close(detail.value);
  223. } else if (type == 'finish') {
  224. finish(detail.value);
  225. }else if (type == 'evaluate') {
  226. evaluateFn(detail.value)
  227. }else if (type == 'logistics') {
  228. packageEvent(detail.value)
  229. }
  230. }
  231. // 获取评论设置
  232. const evaluateConfig = ref('')
  233. const evaluateEvent = () => {
  234. getEvaluateConfig().then((res:any) => {
  235. evaluateConfig.value = res.data
  236. })
  237. }
  238. evaluateEvent()
  239. // 支付
  240. let groupPayRef = ref(null)
  241. const payFn = (item:any) => {
  242. getMallOrderPay(item.order_id).then((res:any) =>{
  243. if(res.data.is_allow_pay){
  244. router.push({ path: '/pay/pay', query: {trade_type: item.order_type,trade_id: res.data.group_id } })
  245. }else{
  246. if(res.data.order_list.length){
  247. groupPayRef.value.open(res.data,item)
  248. }else{
  249. router.push({ path: '/pay/pay', query: {trade_type: item.order_type,trade_id: res.data.group_id } })
  250. }
  251. }
  252. })
  253. }
  254. //取消订单
  255. const groupCloseRef = ref(null)
  256. const close = (item:any) => {
  257. ElMessageBox.confirm('您确定要关闭该订单吗?', '提示',
  258. {
  259. confirmButtonText: '确定',
  260. cancelButtonText: '取消',
  261. confirmButtonClass:'!bg-[var(--el-color-primary)] !border-[var(--el-color-primary)]',
  262. cancelButtonClass:'!border-[#dcdfe6]',
  263. type: 'warning'
  264. }
  265. ).then(() => {
  266. orderClose(item.order_id).then((res: any) => {
  267. if(Object.keys(res.data).length && !res.data.is_allow_close){
  268. if(res.data.order_list.length){
  269. groupCloseRef.value.open(res.data)
  270. }else{
  271. getMallOrderClose(res.data.group_id).then(res =>{
  272. getOrderInfoFn(orderId)
  273. }).catch(() =>{
  274. })
  275. }
  276. }else{
  277. getOrderInfoFn(orderId)
  278. }
  279. }).catch(() => {
  280. })
  281. })
  282. }
  283. const closeGroupOrder = ()=>{
  284. getOrderInfoFn(orderId)
  285. }
  286. //订单完成
  287. const finish = (item: any) => {
  288. ElMessageBox.confirm('您确定物品已收到吗?', '提示',
  289. {
  290. confirmButtonText: '确定',
  291. cancelButtonText: '取消',
  292. confirmButtonClass:'!bg-[var(--el-color-primary)] !border-[var(--el-color-primary)]',
  293. cancelButtonClass:'!border-[#dcdfe6]',
  294. type: 'warning'
  295. }
  296. ).then(() => {
  297. orderFinish(item.order_id).then((res: any) => {
  298. getOrderInfoFn(orderId)
  299. }).catch(() => {
  300. })
  301. })
  302. }
  303. // 申请退款
  304. const applyRefund = (id:any) =>{
  305. router.push({path:'/refund/apply',query:{order_id: detail.value.order_id,order_goods_id:id}})
  306. }
  307. // 查看退款
  308. const refundDetail = (id:any) =>{
  309. router.push({path:'/refund/detail',query:{order_refund_no: id}})
  310. }
  311. //添加评论
  312. const addEvaluateRef = ref(null)
  313. const evaluateFn = (item: any) =>{
  314. addEvaluateRef.value.setFormData(item.order_id)
  315. addEvaluateRef.value.dialogEvaluateVisible = true
  316. }
  317. // 判断展示物流
  318. const showLogistics = (data: any) => {
  319. let flag = false
  320. if (data.delivery_type != 'express') return false;
  321. for (let i = 0; i < data.order_delivery.length; i++) {
  322. let item = data.order_delivery[i];
  323. if (item.sub_delivery_type === 'express' && data.status === '3') {
  324. flag = true;
  325. break;
  326. } else if (item.sub_delivery_type === 'express' && data.status === '5') {
  327. flag = true;
  328. break;
  329. } else {
  330. flag = false
  331. }
  332. }
  333. return flag
  334. }
  335. /**
  336. * 发货
  337. */
  338. const packageRef = ref(null)
  339. const packageEvent = (data:any) => {
  340. packageRef.value.setFormData(data)
  341. packageRef.value.showDialog = true
  342. }
  343. // 判断当前步骤条的状态
  344. let current = ref(0)
  345. const getStatus = () => {
  346. if(detail.value.status == 1){
  347. return current.value = 0
  348. }else if(detail.value.status == 2){
  349. return current.value = 1
  350. }else if(detail.value.status == 3){
  351. return current.value = 2
  352. }else if(detail.value.status == 5){
  353. return current.value = 4
  354. }
  355. }
  356. /************ 虚拟商品核销-start ***************/
  357. let verifyGoodsData = ref({}) //虚拟商品
  358. const isShowVerify = computed(() => {
  359. let bool = false;
  360. if(detail.value.order_goods.length == 1){
  361. verifyGoodsData.value = detail.value.order_goods[0]
  362. let data = detail.value.order_goods[0];
  363. bool = data.is_verify == 1 && data.goods_type == 'virtual' && data.delivery_status == 'delivery_finish' && detail.value.status == 3 ? true : false;
  364. }
  365. return bool
  366. })
  367. let verifyInfo = ref([])
  368. const getVerifyCodeFn = (data:any) => {
  369. verifyInfo.value = [];
  370. getVerifyCode('shopVirtualGoods', data).then((res) => {
  371. verifyInfo.value = res.data;
  372. })
  373. }
  374. /************ 虚拟商品核销-end ***************/
  375. </script>
  376. <style lang="scss" scoped>
  377. :deep(.el-step__head.is-success){
  378. --el-color-success: #e93323;
  379. }
  380. :deep(.el-step__title.is-success){
  381. --el-color-success: #333;
  382. }
  383. :deep(.el-table th.el-table__cell) {
  384. -webkit-user-select: none;
  385. user-select: none;
  386. background-color: #F5F7F9;
  387. color: #333;
  388. }
  389. .line-feed {
  390. word-wrap: break-word;
  391. word-break: break-all;
  392. }
  393. .site{
  394. &:hover{
  395. .site-style{
  396. color:var( --el-color-primary);
  397. }
  398. }
  399. }
  400. </style>