Index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <template>
  2. <a-card :bordered="false">
  3. <a-spin :spinning="isLoading">
  4. <div class="card-title">{{ $route.meta.title }}</div>
  5. <div class="table-operator">
  6. <!-- 搜索板块 -->
  7. <a-row class="row-item-search">
  8. <a-form class="search-form" :form="searchForm" layout="inline" @submit="handleSearch">
  9. <a-form-item label="关键词">
  10. <a-input style="width: 350px" placeholder="请输入关键词" v-decorator="['searchValue']">
  11. <a-select
  12. slot="addonBefore"
  13. v-decorator="['searchType', { initialValue: 10 }]"
  14. style="width: 150px"
  15. >
  16. <a-select-option
  17. v-for="(item, index) in SearchTypeEnum"
  18. :key="index"
  19. :value="item.value"
  20. >{{ item.name }}</a-select-option>
  21. </a-select>
  22. </a-input>
  23. </a-form-item>
  24. <a-form-item label="订单来源">
  25. <a-select v-decorator="['orderSource', { initialValue: -1 }]">
  26. <a-select-option :value="-1">全部</a-select-option>
  27. <a-select-option
  28. v-for="(item, index) in OrderSourceEnum.data"
  29. :key="index"
  30. :value="item.value"
  31. >{{ item.name }}</a-select-option>
  32. </a-select>
  33. </a-form-item>
  34. <a-form-item label="支付方式">
  35. <a-select v-decorator="['payMethod', { initialValue: '' }]">
  36. <a-select-option value>全部</a-select-option>
  37. <a-select-option
  38. v-for="(item, index) in PaymentMethodEnum.data"
  39. :key="index"
  40. :value="item.value"
  41. >{{ item.name }}</a-select-option>
  42. </a-select>
  43. </a-form-item>
  44. <a-form-item label="配送方式">
  45. <a-select v-decorator="['deliveryType', { initialValue: -1 }]">
  46. <a-select-option :value="-1">全部</a-select-option>
  47. <a-select-option
  48. v-for="(item, index) in DeliveryTypeEnum.data"
  49. :key="index"
  50. :value="item.value"
  51. >{{ item.name }}</a-select-option>
  52. </a-select>
  53. </a-form-item>
  54. <a-form-item label="下单时间">
  55. <a-range-picker format="YYYY-MM-DD" v-decorator="['betweenTime']" />
  56. </a-form-item>
  57. <a-form-item class="search-btn">
  58. <a-button class="mr-15" type="primary" icon="search" html-type="submit">搜索</a-button>
  59. <a-button @click="handleReset">重置</a-button>
  60. </a-form-item>
  61. </a-form>
  62. </a-row>
  63. </div>
  64. <!-- 列表内容 -->
  65. <div class="ant-table ant-table-scroll-position-left ant-table-default ant-table-bordered">
  66. <div class="ant-table-content">
  67. <div class="ant-table-scroll">
  68. <div class="ant-table-body" style="overflow-x: scroll;">
  69. <table style="width: 1450px;">
  70. <thead class="ant-table-thead">
  71. <tr>
  72. <th v-for="(item, index) in columns" :key="index">
  73. <span class="ant-table-header-column">
  74. <div>
  75. <span class="ant-table-column-title">{{ item.title }}</span>
  76. </div>
  77. </span>
  78. </th>
  79. </tr>
  80. </thead>
  81. <tbody class="ant-table-tbody">
  82. <template v-for="(item) in orderList.data">
  83. <tr class="order-empty" :key="`order_${item.order_id}_1`">
  84. <td colspan="8"></td>
  85. </tr>
  86. <tr :key="`order_${item.order_id}_2`">
  87. <td colspan="8">
  88. <span class="mr-20">{{ item.create_time }}</span>
  89. <span class="mr-20">订单号:{{ item.order_no }}</span>
  90. <platform-icon :name="item.platform" :showTips="true" />
  91. </td>
  92. </tr>
  93. <tr
  94. v-for="(goodsItm, goodsIdx) in item.goods"
  95. :key="`orderGoods_${item.order_id}_${goodsIdx}`"
  96. >
  97. <td>
  98. <GoodsItem
  99. :data="{
  100. image: goodsItm.goods_image,
  101. imageAlt: '商品图片',
  102. title: goodsItm.goods_name,
  103. goodsProps: goodsItm.goods_props
  104. }"
  105. />
  106. </td>
  107. <td>
  108. <p>¥{{ goodsItm.goods_price }}</p>
  109. <p>×{{ goodsItm.total_num }}</p>
  110. </td>
  111. <template v-if="goodsIdx===0">
  112. <td :rowspan="item.goods.length">
  113. <p>¥{{ item.pay_price }}</p>
  114. <p class="c-muted-1">(含运费:¥{{ item.express_price }})</p>
  115. </td>
  116. <td :rowspan="item.goods.length">
  117. <UserItem :user="item.user" />
  118. </td>
  119. <td :rowspan="item.goods.length">
  120. <a-tooltip v-if="item.pay_method">
  121. <template
  122. v-if="item.trade"
  123. slot="title"
  124. >第三方支付订单号: {{ item.trade.out_trade_no }}</template>
  125. <a-tag>{{ PaymentMethodEnum[item.pay_method].name }}</a-tag>
  126. </a-tooltip>
  127. <span v-else>--</span>
  128. </td>
  129. <td :rowspan="item.goods.length">
  130. <a-tooltip>
  131. <template
  132. v-if="item.delivery_type == DeliveryTypeEnum.EXPRESS.value && item.address"
  133. slot="title"
  134. >
  135. <p>姓名:{{ item.address.name }}</p>
  136. <p>电话:{{ item.address.phone }}</p>
  137. <p>地址:{{ item.address.region.province }} {{ item.address.region.city }} {{ item.address.region.region }} {{ item.address.detail }}</p>
  138. </template>
  139. <a-tag>{{ DeliveryTypeEnum[item.delivery_type].name }}</a-tag>
  140. </a-tooltip>
  141. </td>
  142. <td :rowspan="item.goods.length">
  143. <p class="mtb-2">
  144. <span class="f-13">付款状态:</span>
  145. <a-tag
  146. :color="item.pay_status == PayStatusEnum.SUCCESS.value ? 'green' : ''"
  147. >{{ PayStatusEnum[item.pay_status].name }}</a-tag>
  148. </p>
  149. <p class="mtb-2">
  150. <span class="f-13">发货状态:</span>
  151. <a-tag
  152. :color="item.delivery_status == DeliveryStatusEnum.DELIVERED.value ? 'green' : ''"
  153. >{{ DeliveryStatusEnum[item.delivery_status].name }}</a-tag>
  154. </p>
  155. <p class="mtb-2">
  156. <span class="f-13">收货状态:</span>
  157. <a-tag
  158. :color="item.receipt_status == ReceiptStatusEnum.RECEIVED.value ? 'green' : ''"
  159. >{{ ReceiptStatusEnum[item.receipt_status].name }}</a-tag>
  160. </p>
  161. <p
  162. v-if="[OrderStatusEnum.CANCELLED.value,OrderStatusEnum.APPLY_CANCEL.value].includes(item.order_status)"
  163. class="mtb-2"
  164. >
  165. <span class="f-13">订单状态:</span>
  166. <a-tag
  167. :color="renderOrderStatusColor(item.order_status)"
  168. >{{ OrderStatusEnum[item.order_status].name }}</a-tag>
  169. </p>
  170. </td>
  171. <td :rowspan="item.goods.length">
  172. <div class="actions">
  173. <router-link
  174. v-if="$auth('/order/detail')"
  175. :to="{ path: '/order/detail', query: { orderId: item.order_id } }"
  176. target="_blank"
  177. >详情</router-link>
  178. <a
  179. v-action:deliver
  180. v-if="(
  181. item.pay_status == PayStatusEnum.SUCCESS.value
  182. && inArray(item.delivery_type, [ DeliveryTypeEnum.EXPRESS.value, DeliveryTypeEnum.NOTHING.value])
  183. && item.delivery_status != DeliveryStatusEnum.DELIVERED.value
  184. && !inArray(item.order_status, [OrderStatusEnum.CANCELLED.value, OrderStatusEnum.APPLY_CANCEL.value])
  185. )"
  186. @click="handleDelivery(item)"
  187. >发货</a>
  188. <a
  189. v-action:cancel
  190. v-if="item.order_status == OrderStatusEnum.APPLY_CANCEL.value"
  191. @click="handleCancel(item)"
  192. >审核取消</a>
  193. <a v-action:delete @click="handleDelete(item)">删除</a>
  194. </div>
  195. </td>
  196. </template>
  197. </tr>
  198. </template>
  199. </tbody>
  200. </table>
  201. </div>
  202. <!-- 空状态 -->
  203. <a-empty v-if="!orderList.data.length" :image="simpleImage" />
  204. </div>
  205. </div>
  206. </div>
  207. <!-- 分页器 -->
  208. <div v-if="orderList.data.length" class="pagination">
  209. <a-pagination
  210. :current="page"
  211. :pageSize="orderList.per_page"
  212. :total="orderList.total"
  213. @change="onChangePage"
  214. />
  215. </div>
  216. <DeliveryForm ref="DeliveryForm" @handleSubmit="handleRefresh" />
  217. <CancelForm ref="CancelForm" @handleSubmit="handleRefresh" />
  218. </a-spin>
  219. </a-card>
  220. </template>
  221. <script>
  222. import { Empty } from 'ant-design-vue'
  223. import { inArray, assignment } from '@/utils/util'
  224. import * as Api from '@/api/order'
  225. import * as EventApi from '@/api/order/event'
  226. import PlatformIcon from '@/components/PlatformIcon'
  227. import { GoodsItem, UserItem } from '@/components/Table'
  228. import {
  229. DeliveryStatusEnum,
  230. DeliveryTypeEnum,
  231. OrderSourceEnum,
  232. OrderStatusEnum,
  233. PayStatusEnum,
  234. ReceiptStatusEnum
  235. } from '@/common/enum/order'
  236. import { PaymentMethodEnum } from '@/common/enum/payment'
  237. import { DeliveryForm, CancelForm } from './modules'
  238. // 表格字段
  239. const columns = [
  240. {
  241. title: '商品信息',
  242. align: 'center',
  243. dataIndex: 'goods',
  244. scopedSlots: { customRender: 'goods' }
  245. },
  246. {
  247. title: '单价/数量',
  248. align: 'center',
  249. scopedSlots: { customRender: 'unit_price' }
  250. },
  251. {
  252. title: '实付款',
  253. align: 'center',
  254. dataIndex: 'pay_price',
  255. scopedSlots: { customRender: 'pay_price' }
  256. },
  257. {
  258. title: '买家',
  259. dataIndex: 'user',
  260. scopedSlots: { customRender: 'user' }
  261. },
  262. {
  263. title: '支付方式',
  264. dataIndex: 'pay_method',
  265. scopedSlots: { customRender: 'pay_method' }
  266. },
  267. {
  268. title: '配送方式',
  269. dataIndex: 'delivery_type',
  270. scopedSlots: { customRender: 'delivery_type' }
  271. },
  272. {
  273. title: '交易状态',
  274. dataIndex: 'status',
  275. scopedSlots: { customRender: 'status' }
  276. },
  277. {
  278. title: '操作',
  279. dataIndex: 'action',
  280. width: '180px',
  281. scopedSlots: { customRender: 'action' }
  282. }
  283. ]
  284. // 搜索关键词类型枚举
  285. const SearchTypeEnum = [
  286. { name: '商城订单号', value: 10 },
  287. { name: '第三方支付订单号', value: 60 },
  288. { name: '会员昵称', value: 20 },
  289. { name: '会员ID', value: 30 },
  290. { name: '收货人姓名', value: 40 },
  291. { name: '收货人电话', value: 50 },
  292. ]
  293. export default {
  294. name: 'Index',
  295. components: {
  296. PlatformIcon,
  297. GoodsItem,
  298. UserItem,
  299. DeliveryForm,
  300. CancelForm
  301. },
  302. data () {
  303. return {
  304. // 订单类型
  305. dataType: this.getDataType(),
  306. // 当前表单元素
  307. searchForm: this.$form.createForm(this),
  308. // 查询参数
  309. queryParam: {},
  310. // 正在加载
  311. isLoading: false,
  312. // 表格字段
  313. columns,
  314. // 当前页码
  315. page: 1,
  316. // 列表数据
  317. orderList: { data: [], total: 0, per_page: 10 }
  318. }
  319. },
  320. beforeCreate () {
  321. // 批量给当前实例赋值
  322. assignment(this, {
  323. inArray,
  324. DeliveryStatusEnum,
  325. DeliveryTypeEnum,
  326. OrderSourceEnum,
  327. OrderStatusEnum,
  328. PayStatusEnum,
  329. ReceiptStatusEnum,
  330. PaymentMethodEnum,
  331. SearchTypeEnum,
  332. simpleImage: Empty.PRESENTED_IMAGE_SIMPLE
  333. })
  334. },
  335. watch: {
  336. // 监听路由变化
  337. $route () {
  338. this.init()
  339. }
  340. },
  341. created () {
  342. // 初始化页面
  343. this.init()
  344. },
  345. methods: {
  346. // 初始化页面
  347. init () {
  348. this.dataType = this.getDataType()
  349. this.searchForm.resetFields()
  350. this.queryParam = {}
  351. this.handleRefresh(true)
  352. },
  353. // 获取当前标签
  354. getDataType () {
  355. return this.$route.path.split('/')[3].replace('-', '_')
  356. },
  357. // 获取列表数据
  358. getList () {
  359. const { dataType, queryParam, page } = this
  360. this.isLoading = true
  361. return Api.list({ dataType, ...queryParam, page })
  362. .then(response => {
  363. this.orderList = response.data.list
  364. })
  365. .finally(() => this.isLoading = false)
  366. },
  367. // 渲染订单状态标签颜色
  368. renderOrderStatusColor (orderStatus) {
  369. const { OrderStatusEnum } = this
  370. const ColorEnum = {
  371. [OrderStatusEnum.NORMAL.value]: '',
  372. [OrderStatusEnum.CANCELLED.value]: 'red',
  373. [OrderStatusEnum.APPLY_CANCEL.value]: 'red',
  374. [OrderStatusEnum.COMPLETED.value]: 'green'
  375. }
  376. return ColorEnum[orderStatus]
  377. },
  378. /**
  379. * 刷新列表
  380. * @param Boolean bool 强制刷新到第一页
  381. */
  382. handleRefresh (bool = false) {
  383. bool && (this.page = 1)
  384. this.getList()
  385. },
  386. // 确认搜索
  387. handleSearch (e) {
  388. e.preventDefault()
  389. this.searchForm.validateFields((error, values) => {
  390. if (!error) {
  391. this.queryParam = { ...this.queryParam, ...values }
  392. this.handleRefresh(true)
  393. }
  394. })
  395. },
  396. // 重置搜索表单
  397. handleReset () {
  398. this.searchForm.resetFields()
  399. },
  400. // 翻页事件
  401. onChangePage (current) {
  402. this.page = current
  403. this.handleRefresh()
  404. },
  405. // 删除记录
  406. handleDelete (item) {
  407. const app = this
  408. const modal = app.$confirm({
  409. title: '您确定要删除该订单记录吗?',
  410. content: '删除后不可恢复,请谨慎操作',
  411. onOk () {
  412. return EventApi.deleted(item.order_id)
  413. .then(result => {
  414. app.$message.success(result.message, 1.5)
  415. app.handleRefresh()
  416. })
  417. .finally(result => modal.destroy())
  418. }
  419. })
  420. },
  421. // 订单发货
  422. handleDelivery (record) {
  423. this.$refs.DeliveryForm.show(record)
  424. },
  425. // 审核取消订单
  426. handleCancel (record) {
  427. this.$refs.CancelForm.show(record)
  428. }
  429. }
  430. }
  431. </script>
  432. <style lang="less" scoped>
  433. // 分页器
  434. .pagination {
  435. margin-top: 16px;
  436. .ant-pagination {
  437. float: right;
  438. }
  439. }
  440. .ant-table {
  441. table {
  442. border: none;
  443. border-collapse: collapse;
  444. }
  445. .ant-table-thead > tr {
  446. border: 1px solid #e8e8e8;
  447. }
  448. tr.order-empty {
  449. height: 15px;
  450. border: 1px solid #fff;
  451. td {
  452. padding: 0;
  453. border-right: none;
  454. border-left: none;
  455. background: none !important;
  456. }
  457. }
  458. }
  459. .ant-table-thead > tr > th {
  460. border-right: none;
  461. border-bottom: none;
  462. padding: 12px 12px;
  463. font-weight: bold;
  464. }
  465. .ant-table-tbody > tr > td {
  466. border-right: 1px solid #e8e8e8;
  467. border-left: 1px solid #e8e8e8;
  468. padding: 12px 12px;
  469. // text-align: center;
  470. }
  471. </style>