list.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <template>
  2. <div class="orders-lists">
  3. <el-card class="!border-none" shadow="never">
  4. <el-form :inline="true" class="mb-[-16px]">
  5. <el-form-item label="订单类型" class="w-[280px]">
  6. <el-select class="w-[280px]" v-model="queryParams.type">
  7. <el-option label="全部" value />
  8. <el-option label="吧台点单" value="0" />
  9. <el-option label="扫码点单" value="1" />
  10. </el-select>
  11. </el-form-item>
  12. <el-form-item label="订单状态" class="w-[280px]">
  13. <el-select class="w-[280px]" v-model="queryParams.status">
  14. <el-option label="全部" value />
  15. <!-- <el-option label="待下单" value="0" />-->
  16. <!-- <el-option label="待结帐" value="1" />-->
  17. <el-option label="已下单" value="2" />
  18. <el-option label="已退款" value="4" />
  19. </el-select>
  20. </el-form-item>
  21. <!-- <el-form-item label="创建时间" class="w-[280px]">-->
  22. <!-- <el-date-picker v-model="createTime" @change="setCreateTime" type="datetimerange"-->
  23. <!-- range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" />-->
  24. <!-- </el-form-item>-->
  25. <el-form-item label="下单时间" class="w-[280px]">
  26. <el-date-picker v-model="checkoutTime" @change="setCheckoutTime" type="datetimerange"
  27. range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" />
  28. </el-form-item>
  29. <el-form-item class="w-[280px]">
  30. <el-button type="primary" @click="resetPage">查询</el-button>
  31. <el-button @click="resetParams">重置</el-button>
  32. </el-form-item>
  33. </el-form>
  34. </el-card>
  35. <el-card class="!border-none mt-4" shadow="never">
  36. <!-- :expand-row-keys="expandRowKeys" @expand-change="handleExpandChange"-->
  37. <el-table v-loading="pager.loading" :data="pager.lists" row-key="id">
  38. <!-- <el-table-column type="expand" >-->
  39. <!-- <template #default="{ row }">-->
  40. <!-- <div class="order-detail-wrapper" v-loading="!orderDetails[row.id]">-->
  41. <!-- <template v-if="orderDetails[row.id]">-->
  42. <!-- <div class="detail-header">-->
  43. <!-- <span>订单详情</span>-->
  44. <!-- <span class="time">下单时间:{{ timeFormat(orderDetails[row.id].createTime) }}</span>-->
  45. <!-- </div>-->
  46. <!-- <el-table :data="orderDetails[row.id].dishes" border class="detail-table">-->
  47. <!-- <el-table-column label="菜品图片" width="120" align="center">-->
  48. <!-- <template #default="{ row }">-->
  49. <!-- <el-image :src="row.image" :preview-src-list="[row.image]"-->
  50. <!-- class="dish-image" fit="cover" />-->
  51. <!-- </template>-->
  52. <!-- </el-table-column>-->
  53. <!-- <el-table-column prop="name" label="菜品名称">-->
  54. <!-- <template #default="{ row }">-->
  55. <!-- <div>-->
  56. <!-- {{ row.name }}-->
  57. <!-- <div v-if="row.specsList && row.specsList.length" class="mt-1">-->
  58. <!-- <el-tag-->
  59. <!-- v-for="spec in row.specsList"-->
  60. <!-- :key="spec.id"-->
  61. <!-- size="small"-->
  62. <!-- class="mr-1 mb-1"-->
  63. <!-- >-->
  64. <!-- {{ spec.name }}: {{ spec.value }}-->
  65. <!-- </el-tag>-->
  66. <!-- </div>-->
  67. <!-- </div>-->
  68. <!-- </template>-->
  69. <!-- </el-table-column>-->
  70. <!-- <el-table-column prop="amount" label="单价">-->
  71. <!-- <template #default="{ row }">-->
  72. <!-- ¥{{ row.amount }}-->
  73. <!-- </template>-->
  74. <!-- </el-table-column>-->
  75. <!-- <el-table-column prop="number" label="数量" width="120" align="center" />-->
  76. <!-- <el-table-column label="小计" width="120" align="right">-->
  77. <!-- <template #default="{ row }">-->
  78. <!-- ¥{{ row.amount * row.number}}-->
  79. <!-- </template>-->
  80. <!-- </el-table-column>-->
  81. <!-- </el-table>-->
  82. <!-- <div class="detail-footer">-->
  83. <!-- <div class="total-info">-->
  84. <!-- <span>共 {{ getTotalCount(orderDetails[row.id].dishes) }} 件商品</span>-->
  85. <!-- <span class="total-price">-->
  86. <!-- 订单总价:<em>¥{{ orderDetails[row.id].amount}}</em>-->
  87. <!-- </span>-->
  88. <!-- </div>-->
  89. <!-- </div>-->
  90. <!-- </template>-->
  91. <!-- </div>-->
  92. <!-- </template>-->
  93. <!-- </el-table-column>-->
  94. <el-table-column type="index" min-width="80" />
  95. <el-table-column label="下单时间" prop="checkoutTime" min-width="120"></el-table-column>
  96. <el-table-column label="订单号" prop="number" min-width="150" show-overflow-tooltip></el-table-column>
  97. <el-table-column label="类型" prop="type" :formatter="(row: any) => (row.type == 0 ? '吧台点单' : '扫码点单')"></el-table-column>
  98. <el-table-column label="桌号/餐号" prop="deskName">
  99. <template #default="{ row }">{{
  100. row.deskName ? row.deskName : row.mealCode
  101. }}</template>
  102. </el-table-column>
  103. <el-table-column label="产品清单" prop="deskName" min-width="120">
  104. <template #default="{ row }">
  105. <div v-for="orderDish in row.orderDishes" :key="orderDish.id">
  106. {{ orderDish.name }}<span style="margin: 0 15px 0 15px;">x</span>{{orderDish.number}}
  107. <div v-if="orderDish.specsList && orderDish.specsList.length">
  108. <div v-for="spec in orderDish.specsList" :key="spec.id" class="refund-info" style="margin-left: 20px;margin-top: 0;">
  109. - {{ spec.name }}: {{ spec.value }}
  110. </div>
  111. </div>
  112. </div>
  113. </template>
  114. </el-table-column>
  115. <el-table-column label="订单金额(元)" prop="amount" min-width="100"></el-table-column>
  116. <el-table-column label="优惠金额(元)" prop="ticketAmount" min-width="100"></el-table-column>
  117. <el-table-column label="实付金额(元)" prop="payAmount" min-width="100"></el-table-column>
  118. <el-table-column label="备注" prop="remark" min-width="100" show-tooltip-when-overflow></el-table-column>
  119. <el-table-column label="状态" min-width="160" prop="status" >
  120. <template #default="{ row }">
  121. <div>
  122. {{ row.status == 4 ? '已退款' : row.status == 2 ? '已下单' : row.status == 0 ? '待下单'
  123. : row.status == 1 ? '待支付' : row.status == 5 ? '退款中' : row.status == 3 ? '支付失败' : '已关闭' }}
  124. <div v-if="row.refundStatus=='SUCCESS'" class="refund-info">
  125. <div>退款时间:{{ row.refundTime }}</div>
  126. <div>退款金额:{{ row.refundAmount }}元</div>
  127. </div>
  128. </div>
  129. </template>
  130. </el-table-column>
  131. <el-table-column label="操作" width="60" fixed="right">
  132. <template #default="{ row }">
  133. <!-- <el-button type="primary" link @click="showDetail(row)">查看已点菜品</el-button>-->
  134. <el-button v-if="row.status=='2' && !row.refundAmount" type="danger" link @click="refund(row)">退款</el-button>
  135. </template>
  136. </el-table-column>
  137. </el-table>
  138. <div class="flex justify-end mt-4">
  139. <pagination v-model="pager" @change="getLists" />
  140. </div>
  141. </el-card>
  142. <el-dialog
  143. v-model="refundDialogVisible"
  144. title="退款"
  145. width="400px"
  146. @close="refundDialogVisible = false">
  147. <div class="refund-dialog-content">
  148. <el-input
  149. v-model="refundAmount"
  150. placeholder="请输入退款金额"
  151. type="number">
  152. <template #prepend><icon :size="25" name="el-icon-money" /></template>
  153. <template #append>元</template>
  154. </el-input>
  155. </div>
  156. <template #footer>
  157. <span class="dialog-footer">
  158. <el-button @click="refundDialogVisible = false">取 消</el-button>
  159. <el-button type="primary" :loading="refundLoading" @click="confirmRefund">确 定</el-button>
  160. </span>
  161. </template>
  162. </el-dialog>
  163. </div>
  164. </template>
  165. <script setup lang="ts">
  166. import { ordersList, getOrderDetail, refundReq } from '@/api/orders'
  167. import { usePaging } from '@/hooks/usePaging'
  168. import { timeFormat } from '@/utils/util'
  169. import feedback from '@/utils/feedback'
  170. // import Money from '@/utils/money'
  171. const createTime = ref()
  172. const checkoutTime = ref()
  173. const queryParams = reactive({
  174. type: '',
  175. status: '',
  176. checkoutTime: '',
  177. createTime: ''
  178. })
  179. const { pager, getLists, resetPage, resetParams } = usePaging({
  180. fetchFun: ordersList,
  181. params: queryParams
  182. })
  183. const setCreateTime = (v : any) => {
  184. queryParams.createTime =
  185. Math.round(v[0].getTime() / 1000).toString() +
  186. ',' +
  187. Math.round(v[1].getTime() / 1000).toString()
  188. }
  189. const setCheckoutTime = (v : any) => {
  190. queryParams.checkoutTime =
  191. Math.round(v[0].getTime() / 1000).toString() +
  192. ',' +
  193. Math.round(v[1].getTime() / 1000).toString()
  194. }
  195. // watch(queryParams, (newV, oldV) => {
  196. // console.log(JSON.stringify(newV))
  197. // })
  198. const expandRowKeys = ref<string[]>([])
  199. const orderDetails = ref<Record<string, any>>({})
  200. const handleExpandChange = (row: any, expandedRows: any) => {
  201. // console.log('当前行展开状态改变:', row, expandedRows);
  202. if (!orderDetails.value[row.id]) {
  203. showDetail(row);
  204. }
  205. }
  206. // 添加这些变量到组件顶层
  207. const refundDialogVisible = ref(false)
  208. const refundAmount = ref('')
  209. var refundOrder: {
  210. refundTime: string;
  211. refundAmount: number;
  212. payAmount: number;
  213. amount: number;
  214. status: number; id: any;
  215. };
  216. // 添加loading状态变量
  217. const refundLoading = ref(false)
  218. // 修改refund函数
  219. const refund = async (row: any) => {
  220. refundDialogVisible.value = true
  221. refundAmount.value = ''
  222. refundOrder = row;
  223. }
  224. const confirmRefund = async () => {
  225. try {
  226. if(!refundAmount.value || refundAmount.value<=0){
  227. return feedback.msgError('退款金额不合法')
  228. }
  229. if(refundAmount.value > (refundOrder.payAmount || refundOrder.amount)){
  230. return feedback.msgError('退款金额不能大于订单实付金额')
  231. }
  232. refundLoading.value = true
  233. const res = await refundReq({ orderId: refundOrder.id, payAmount: refundAmount.value })
  234. if (res) {
  235. feedback.msgSuccess('退款成功')
  236. refundOrder.status = 5;
  237. // refundOrder.refundAmount = refundAmount.value;
  238. // refundOrder.refundTime = "待处理";
  239. setTimeout(()=>{
  240. getLists();
  241. },200)
  242. } else {
  243. feedback.msgError('退款失败')
  244. }
  245. } catch (error) {
  246. console.error('退款失败:', error)
  247. } finally {
  248. refundLoading.value = false
  249. refundDialogVisible.value = false
  250. }
  251. }
  252. // 展示订单详情
  253. const showDetail = async (row : any) => {
  254. try {
  255. // 如果已经加载过该订单详情,直接展开
  256. if (orderDetails.value[row.id]) {
  257. expandRowKeys.value = [row.id]
  258. return
  259. }
  260. // 调用获取订单详情接口
  261. const res = await getOrderDetail({id:row.id})
  262. console.warn("***getOrderDetail***",res)
  263. if (res) {
  264. // 定义正确的类型
  265. let orderDetail: Record<string, any> = {};
  266. orderDetail.id = res.orderItem.id;
  267. orderDetail.amount = res.orderItem.amount;//总价
  268. orderDetail.createTime = res.orderItem.createTime;
  269. orderDetail.dishes = res.orderDishList;
  270. // 保存订单详情
  271. orderDetails.value[row.id] = orderDetail
  272. // 展开当前行
  273. expandRowKeys.value = [row.id]
  274. } else {
  275. feedback.msgError('获取订单详情失败')
  276. }
  277. } catch (error) {
  278. console.error('获取订单详情失败:', error)
  279. feedback.msgError('获取订单详情失败')
  280. }
  281. }
  282. // 计算商品总数
  283. const getTotalCount = (dishes : any[]) => {
  284. return dishes.reduce((total, dish) => total + dish.number, 0)
  285. }
  286. // 计算订单总价
  287. // const getTotalPrice = (dishes: any[]) => {
  288. // return dishes.reduce((total, dish) => {
  289. // return Money.add(total, Money.multiply(dish.price, dish.count))
  290. // }, 0)
  291. // }
  292. getLists()
  293. </script>
  294. <style lang="scss" scoped>
  295. .order-detail-wrapper {
  296. padding: 20px;
  297. background: #f8f8f8;
  298. .detail-header {
  299. margin-bottom: 20px;
  300. display: flex;
  301. justify-content: space-between;
  302. align-items: center;
  303. span {
  304. font-size: 14px;
  305. color: #333;
  306. &.time {
  307. color: #999;
  308. }
  309. }
  310. }
  311. .detail-table {
  312. margin-bottom: 20px;
  313. :deep(.dish-image) {
  314. width: 60px;
  315. height: 60px;
  316. border-radius: 4px;
  317. }
  318. }
  319. .detail-footer {
  320. display: flex;
  321. justify-content: flex-end;
  322. .total-info {
  323. text-align: right;
  324. span {
  325. margin-left: 20px;
  326. font-size: 14px;
  327. color: #666;
  328. &.total-price {
  329. em {
  330. font-style: normal;
  331. font-size: 16px;
  332. color: #f56c6c;
  333. font-weight: bold;
  334. }
  335. }
  336. }
  337. }
  338. }
  339. }
  340. .refund-dialog-content {
  341. padding: 20px 0px;
  342. :deep(.el-input) {
  343. width: 100%;
  344. }
  345. }
  346. .dialog-footer {
  347. text-align: right;
  348. .el-button + .el-button {
  349. margin-left: 12px;
  350. }
  351. }
  352. .refund-info {
  353. font-size: 12px;
  354. color: #999;
  355. margin-top: 4px;
  356. }
  357. .remark-text {
  358. display: -webkit-box;
  359. -webkit-box-orient: vertical;
  360. -webkit-line-clamp: 2;
  361. overflow: hidden;
  362. text-overflow: ellipsis;
  363. word-break: break-all;
  364. line-height: 1.5;
  365. max-height: 3em; // 2行的高度
  366. }
  367. </style>