orderList.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <template>
  2. <div class="store-page service-page">
  3. <div class="service-page__inner service-page__inner--wide">
  4. <Breadcrumb />
  5. <header class="page-header">
  6. <div>
  7. <h1 class="store-section-title">我的工单</h1>
  8. <p class="store-section-desc">查看售后处理进度与历史沟通记录</p>
  9. </div>
  10. <a-button type="primary" class="submit-btn" @click="$router.push('/service/createOrder')">
  11. <template #icon><icon-plus /></template>
  12. 提交工单
  13. </a-button>
  14. </header>
  15. <a-alert v-if="notice" type="info" closable class="notice">{{ notice }}</a-alert>
  16. <a-spin :loading="loading" class="store-spin">
  17. <a-empty v-if="!loading && data.length === 0" description="暂无工单">
  18. <a-button type="primary" @click="$router.push('/service/createOrder')">去提交</a-button>
  19. </a-empty>
  20. <div v-else class="ticket-list">
  21. <article
  22. v-for="record in data"
  23. :key="record.id"
  24. class="ticket-card"
  25. @click="$router.push(`/service/orderDetail/${record.id}`)"
  26. >
  27. <div class="ticket-card__head">
  28. <span class="ticket-id">#{{ record.id }}</span>
  29. <WorkOrderStateTag :state="record.state" />
  30. </div>
  31. <h3 class="ticket-card__title">{{ record.title }}</h3>
  32. <div class="ticket-card__meta">
  33. <span><icon-email /> {{ record.email }}</span>
  34. <span><icon-clock-circle /> {{ formatTime(record.update_time) }}</span>
  35. </div>
  36. <div class="ticket-card__action">
  37. <a-button type="text" size="small" @click.stop="$router.push(`/service/orderDetail/${record.id}`)">
  38. 查看详情
  39. </a-button>
  40. </div>
  41. </article>
  42. </div>
  43. <div v-if="pagination.total > pagination.pagesize" class="pager">
  44. <a-pagination
  45. :total="pagination.total"
  46. :current="pagination.current"
  47. :page-size="pagination.pagesize"
  48. show-total
  49. show-page-size
  50. @change="handlePageChange"
  51. @page-size-change="handlePageSizeChange"
  52. />
  53. </div>
  54. </a-spin>
  55. </div>
  56. </div>
  57. </template>
  58. <script setup>
  59. import { ref, reactive, onMounted } from 'vue'
  60. import { orderList } from '@/api/workOrder'
  61. import { Notification } from '@arco-design/web-vue'
  62. import { useRoute } from 'vue-router'
  63. import { getNotice } from '@/utils/util'
  64. import { formatStoreTime } from '@/utils/storeFormat'
  65. import WorkOrderStateTag from '@/components/service/WorkOrderStateTag.vue'
  66. const notice = ref('')
  67. const loading = ref(false)
  68. const data = ref([])
  69. const pagination = reactive({ total: 0, current: 1, pagesize: 15 })
  70. const formatTime = (time) => formatStoreTime(time)
  71. const getOrderList = async () => {
  72. try {
  73. loading.value = true
  74. const res = await orderList({
  75. pagesize: pagination.pagesize,
  76. current: pagination.current
  77. })
  78. if (!res || res.code !== 0) {
  79. return Notification.error({
  80. title: '获取工单失败',
  81. content: res?.msg ?? '请稍后再试'
  82. })
  83. }
  84. data.value = res.data ?? []
  85. pagination.total = res.pagination?.total ?? 0
  86. } catch (error) {
  87. Notification.error({
  88. title: '获取工单失败',
  89. content: error.message || '请稍后再试'
  90. })
  91. } finally {
  92. loading.value = false
  93. }
  94. }
  95. const handlePageChange = (page) => {
  96. pagination.current = page
  97. getOrderList()
  98. }
  99. const handlePageSizeChange = (size) => {
  100. pagination.pagesize = size
  101. pagination.current = 1
  102. getOrderList()
  103. }
  104. const GetNotice = async () => {
  105. const { path } = useRoute()
  106. notice.value = await getNotice(path)
  107. }
  108. onMounted(() => {
  109. getOrderList()
  110. GetNotice()
  111. })
  112. </script>
  113. <style lang="less" scoped>
  114. @import '@/styles/store-theme.less';
  115. .page-header {
  116. display: flex;
  117. flex-wrap: wrap;
  118. align-items: flex-end;
  119. justify-content: space-between;
  120. gap: 16px;
  121. margin-bottom: 20px;
  122. }
  123. .submit-btn {
  124. border-radius: 999px;
  125. background: @store-primary !important;
  126. border-color: @store-primary !important;
  127. }
  128. .notice {
  129. margin-bottom: 16px;
  130. border-radius: @store-radius-sm;
  131. }
  132. .ticket-list {
  133. display: flex;
  134. flex-direction: column;
  135. gap: 12px;
  136. width: 100%;
  137. }
  138. .ticket-card {
  139. width: 100%;
  140. box-sizing: border-box;
  141. background: @store-card-bg;
  142. border: 1px solid @store-card-border;
  143. border-radius: @store-radius;
  144. padding: 16px 20px;
  145. cursor: pointer;
  146. transition: box-shadow 0.2s, border-color 0.2s;
  147. &:hover {
  148. box-shadow: @store-shadow;
  149. border-color: fade(@store-accent, 40%);
  150. }
  151. &__head {
  152. display: flex;
  153. align-items: center;
  154. justify-content: space-between;
  155. margin-bottom: 10px;
  156. }
  157. .ticket-id {
  158. font-size: 0.8rem;
  159. color: @store-text-muted;
  160. font-family: ui-monospace, monospace;
  161. }
  162. &__title {
  163. margin: 0 0 10px;
  164. font-size: 1.05rem;
  165. font-weight: 600;
  166. color: @store-primary;
  167. }
  168. &__meta {
  169. display: flex;
  170. flex-wrap: wrap;
  171. gap: 16px;
  172. font-size: 0.85rem;
  173. color: @store-text-muted;
  174. span {
  175. display: inline-flex;
  176. align-items: center;
  177. gap: 4px;
  178. }
  179. }
  180. &__action {
  181. margin-top: 12px;
  182. display: flex;
  183. justify-content: flex-end;
  184. }
  185. }
  186. .pager {
  187. margin-top: 20px;
  188. display: flex;
  189. justify-content: center;
  190. }
  191. </style>