UnsettledPlanPanel.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <template>
  2. <div class="freight-tab-panel">
  3. <div class="filter-block">
  4. <el-form :model="searchForm" class="filter-form" @submit.prevent>
  5. <div class="filter-grid">
  6. <el-form-item label="运输公司">
  7. <el-select v-model="searchForm.carrierId" placeholder="全部" clearable filterable>
  8. <el-option
  9. v-for="c in carrierList"
  10. :key="c.id"
  11. :label="c.carrierName"
  12. :value="c.id"
  13. />
  14. </el-select>
  15. </el-form-item>
  16. <el-form-item label="计划编号">
  17. <el-input
  18. v-model="searchForm.q"
  19. placeholder="搜索计划编号/客户"
  20. clearable
  21. @keyup.enter="handleSearch"
  22. />
  23. </el-form-item>
  24. <el-form-item label="单据类型">
  25. <el-select v-model="searchForm.planType" placeholder="全部" clearable>
  26. <el-option label="直销单" value="direct" />
  27. <el-option label="入库单" value="inbound" />
  28. <el-option label="出库单" value="outbound" />
  29. </el-select>
  30. </el-form-item>
  31. <el-form-item label="车牌号">
  32. <el-input v-model="searchForm.licensePlate" placeholder="搜索车牌号" clearable />
  33. </el-form-item>
  34. </div>
  35. <div class="filter-actions">
  36. <div class="filter-actions-end">
  37. <el-button class="btn-ghost" :loading="loading" @click="handleReset">
  38. <el-icon><RefreshLeft /></el-icon>重置
  39. </el-button>
  40. <el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
  41. <el-icon><Search /></el-icon>搜索
  42. </el-button>
  43. </div>
  44. </div>
  45. </el-form>
  46. </div>
  47. <div class="list-toolbar">
  48. <div class="list-toolbar-row">
  49. <div class="list-toolbar-selected">
  50. 已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
  51. </div>
  52. <el-button
  53. class="btn-main"
  54. type="primary"
  55. :disabled="!selectedRows.length"
  56. :loading="createLoading"
  57. @click="handleCreateSettlement"
  58. >
  59. <el-icon><DocumentAdd /></el-icon>生成结算单
  60. </el-button>
  61. </div>
  62. </div>
  63. <div class="list-body list-wrap">
  64. <el-table
  65. class="list-table"
  66. :data="planList"
  67. v-loading="loading"
  68. stripe
  69. row-key="id"
  70. @selection-change="handleSelectionChange"
  71. >
  72. <el-table-column type="selection" min-width="48" align="center" />
  73. <el-table-column prop="planNo" label="计划编号" min-width="160" show-overflow-tooltip />
  74. <el-table-column prop="planDate" label="计划日期" min-width="120" align="center" />
  75. <el-table-column prop="planType" label="单据类型" min-width="100" align="center">
  76. <template #default="{ row }">
  77. <el-tag :type="getPlanTypeTagType(row.planType)" size="small">
  78. {{ getPlanTypeLabel(row.planType) }}
  79. </el-tag>
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="客户/供应商" min-width="140" show-overflow-tooltip>
  83. <template #default="{ row }">
  84. {{ row.customerName || row.supplierName || '—' }}
  85. </template>
  86. </el-table-column>
  87. <el-table-column prop="licensePlate" label="车牌号" min-width="110" align="center" />
  88. <el-table-column prop="transportCompany" label="运输公司" min-width="140" show-overflow-tooltip />
  89. <el-table-column prop="freightAmount" label="运费(元)" min-width="120" align="right">
  90. <template #default="{ row }">{{ formatAmount(row.freightAmount) }}</template>
  91. </el-table-column>
  92. <el-table-column prop="archiveTime" label="归档时间" min-width="160" align="center">
  93. <template #default="{ row }">{{
  94. row.archiveTime ? formatDateTime(row.archiveTime) : '—'
  95. }}</template>
  96. </el-table-column>
  97. <el-table-column label="操作" min-width="100" fixed="right" align="center">
  98. <template #default="{ row }">
  99. <el-button link type="primary" @click="handleViewPlan(row)">
  100. <el-icon><View /></el-icon>查看
  101. </el-button>
  102. </template>
  103. </el-table-column>
  104. </el-table>
  105. <div class="pagination list-pager-bar">
  106. <el-pagination
  107. v-model:current-page="pagination.page"
  108. v-model:page-size="pagination.pageSize"
  109. :total="pagination.total"
  110. :page-sizes="[10, 20, 50, 100]"
  111. layout="total, sizes, prev, pager, next"
  112. @size-change="handleSizeChange"
  113. @current-change="handlePageChange"
  114. />
  115. </div>
  116. </div>
  117. <PlPlanViewDialog v-model:visible="planViewVisible" :plan-id="viewPlanId" />
  118. </div>
  119. </template>
  120. <script setup lang="ts">
  121. import { DocumentAdd, RefreshLeft, Search, View } from '@element-plus/icons-vue'
  122. import { carrierApi, freightApi, type Carrier, type UnsettledPlanItem } from '@/api/pl'
  123. import PlPlanViewDialog from '@/components/business/pl-plan-view-dialog/index.vue'
  124. import { getPlanTypeLabel, getPlanTypeTagType } from '@/views/pl/freight/freight-status'
  125. import { formatMoneyDisplay } from '@/utils/money-format'
  126. import { formatDateTime } from '@/utils/format'
  127. const emit = defineEmits<{ created: [settlementId: string] }>()
  128. const loading = ref(false)
  129. const createLoading = ref(false)
  130. const carrierList = ref<Carrier[]>([])
  131. const planList = ref<UnsettledPlanItem[]>([])
  132. const selectedRows = ref<UnsettledPlanItem[]>([])
  133. const planViewVisible = ref(false)
  134. const viewPlanId = ref('')
  135. const searchForm = reactive({
  136. carrierId: '',
  137. q: '',
  138. planType: '',
  139. licensePlate: ''
  140. })
  141. const pagination = reactive({
  142. page: 1,
  143. pageSize: 20,
  144. total: 0
  145. })
  146. function formatAmount(amount: number) {
  147. if (!amount) return '¥0.00'
  148. return formatMoneyDisplay(amount)
  149. }
  150. async function fetchData() {
  151. loading.value = true
  152. try {
  153. const res = await freightApi.listUnsettledPlans({
  154. carrierId: searchForm.carrierId || undefined,
  155. q: searchForm.q || undefined,
  156. planType: searchForm.planType || undefined,
  157. licensePlate: searchForm.licensePlate || undefined,
  158. page: pagination.page,
  159. pageSize: pagination.pageSize
  160. })
  161. const data = res.data ?? res
  162. planList.value = data.list || []
  163. pagination.total = data.total || 0
  164. } finally {
  165. loading.value = false
  166. }
  167. }
  168. function handleSearch() {
  169. pagination.page = 1
  170. fetchData()
  171. }
  172. function handleReset() {
  173. searchForm.carrierId = ''
  174. searchForm.q = ''
  175. searchForm.planType = ''
  176. searchForm.licensePlate = ''
  177. pagination.page = 1
  178. fetchData()
  179. }
  180. async function loadCarrierList() {
  181. try {
  182. const res = await carrierApi.list({ status: '1' })
  183. carrierList.value = (res.data ?? res) || []
  184. } catch {
  185. carrierList.value = []
  186. }
  187. }
  188. function handlePageChange() {
  189. fetchData()
  190. }
  191. function handleSizeChange() {
  192. pagination.page = 1
  193. fetchData()
  194. }
  195. function handleSelectionChange(rows: UnsettledPlanItem[]) {
  196. selectedRows.value = rows
  197. }
  198. function handleViewPlan(row: UnsettledPlanItem) {
  199. viewPlanId.value = row.id
  200. planViewVisible.value = true
  201. }
  202. async function handleCreateSettlement() {
  203. if (!selectedRows.value.length) return
  204. await ElMessageBox.confirm(
  205. `确定将选中的 ${selectedRows.value.length} 条计划生成一张结算单吗?`,
  206. '提示',
  207. { type: 'warning' }
  208. )
  209. createLoading.value = true
  210. try {
  211. const res = await freightApi.create({
  212. planIds: selectedRows.value.map((r) => r.id)
  213. })
  214. const settlement = (res.data ?? res) as { id: string; settlementNo?: string }
  215. ElMessage.success(`结算单 ${settlement.settlementNo || ''} 创建成功`)
  216. selectedRows.value = []
  217. emit('created', settlement.id)
  218. fetchData()
  219. } finally {
  220. createLoading.value = false
  221. }
  222. }
  223. function refresh() {
  224. fetchData()
  225. }
  226. onMounted(() => {
  227. loadCarrierList()
  228. fetchData()
  229. })
  230. defineExpose({ refresh })
  231. </script>