withdraw.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <script setup lang="ts">
  2. import { reactive, onMounted, ref, nextTick } from "vue";
  3. import { getWithdrawList } from "@/api/finance";
  4. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  5. import { useStationScope } from "@/hooks/useStationScope";
  6. import { http } from "@/utils/http";
  7. import { ElMessage, ElMessageBox } from "element-plus";
  8. import { getDictOptions, formatDict, getDictColor } from "@/utils/dict";
  9. defineOptions({
  10. name: "AdminFinanceWithdraw"
  11. });
  12. const queryRef = ref();
  13. const tableRef = ref();
  14. const state = reactive({
  15. formQuery: {
  16. stationId: "",
  17. status: "",
  18. paymentStatus: ""
  19. },
  20. pageQuery: {
  21. pageNum: 1,
  22. pageSize: 10,
  23. total: 0
  24. },
  25. tableData: {
  26. height: 500,
  27. data: [] as Array<any>,
  28. loading: false,
  29. columns: [
  30. { label: "提现站点", prop: "stationName", width: 180 },
  31. { label: "提现金额", prop: "withdrawnAmount", width: 100 },
  32. { label: "审核状态", prop: "status", width: 100 },
  33. { label: "打款状态", prop: "paymentStatus", width: 100 },
  34. { label: "审核人", prop: "reviewer", width: 120 },
  35. { label: "审核时间", prop: "reviewTime", width: 180 },
  36. { label: "打款人", prop: "payer", width: 120 },
  37. { label: "打款时间", prop: "paymentTime", width: 180 },
  38. { label: "备注", prop: "remark", width: 180 },
  39. { label: "申请时间", prop: "createTime", width: 180 },
  40. { label: "更新时间", prop: "updateTime", width: 180 }
  41. ]
  42. },
  43. get statusOptions() { return getDictOptions("WithdrawnRecord.status"); },
  44. get paymentStatusOptions() { return getDictOptions("WithdrawnRecord.paymentStatus"); }
  45. });
  46. //站点筛选与全局当前站点联动(回调懒引用,规避 loadData 声明顺序的 TDZ 错误)
  47. useStationScope(state.formQuery, (refresh?: boolean) => loadData(refresh));
  48. onMounted(() => {
  49. loadData();
  50. nextTick(() => {
  51. const bodyHeight = document.body.clientHeight;
  52. const queryHeight = queryRef.value?.$el?.clientHeight || 0;
  53. state.tableData.height = bodyHeight - queryHeight - 230;
  54. });
  55. });
  56. const loadData = (refresh: boolean = false) => {
  57. if (refresh) {
  58. state.pageQuery.pageNum = 1;
  59. }
  60. state.tableData.loading = true;
  61. getWithdrawList({ ...state.formQuery, ...state.pageQuery })
  62. .then((res: any) => {
  63. const { list, total } = res || {};
  64. state.tableData.data = list || [];
  65. state.pageQuery.total = total || 0;
  66. })
  67. .catch(() => {
  68. state.tableData.data = [];
  69. ElMessage.error("加载提现记录失败");
  70. })
  71. .finally(() => {
  72. state.tableData.loading = false;
  73. });
  74. };
  75. const auditLoading = ref(false);
  76. const confirmLoading = ref(false);
  77. const handleWithdrawAudit = (row: any) => {
  78. ElMessageBox.confirm(
  79. `确认对站点「${row.stationName}」的提现申请执行审核通过吗?`,
  80. "提现审核",
  81. {
  82. confirmButtonText: "确定",
  83. cancelButtonText: "取消",
  84. type: "warning"
  85. }
  86. ).then(() => {
  87. auditLoading.value = true;
  88. http
  89. .request("post", "finance/reviewWithdrawn", { data: { id: row.id, status: 1 } })
  90. .then(() => {
  91. ElMessage.success("提现审核通过");
  92. loadData(true);
  93. })
  94. .catch(() => {
  95. ElMessage.error("审核操作失败,请稍后重试");
  96. })
  97. .finally(() => {
  98. auditLoading.value = false;
  99. });
  100. });
  101. };
  102. const handleWithdrawConfirm = (row: any) => {
  103. ElMessageBox.confirm(
  104. `确认对站点「${row.stationName}」的提现执行打款确认吗?`,
  105. "打款确认",
  106. {
  107. confirmButtonText: "确定",
  108. cancelButtonText: "取消",
  109. type: "warning"
  110. }
  111. ).then(() => {
  112. confirmLoading.value = true;
  113. http
  114. .request("post", "finance/confirmWithdrawnPayment", { data: { id: row.id, paymentStatus: 1 } })
  115. .then(() => {
  116. ElMessage.success("提现打款已确认");
  117. loadData(true);
  118. })
  119. .catch(() => {
  120. ElMessage.error("打款确认失败,请稍后重试");
  121. })
  122. .finally(() => {
  123. confirmLoading.value = false;
  124. });
  125. });
  126. };
  127. const handleSizeChange = (size: number) => {
  128. state.pageQuery.pageSize = size;
  129. loadData(true);
  130. };
  131. const handleCurrentChange = (page: number) => {
  132. state.pageQuery.pageNum = page;
  133. loadData();
  134. };
  135. const handleSearch = () => {
  136. loadData(true);
  137. };
  138. const handleReset = () => {
  139. state.formQuery = {
  140. stationId: "",
  141. status: "",
  142. paymentStatus: ""
  143. };
  144. loadData(true);
  145. };
  146. const formatMoney = (value: number) => {
  147. if (value === null || value === undefined) return "¥0.00";
  148. return `¥${(value / 100).toFixed(2)}`;
  149. };
  150. </script>
  151. <template>
  152. <div class="page-container">
  153. <el-card shadow="hover">
  154. <template #header>
  155. <span class="card-header">提现记录</span>
  156. </template>
  157. <el-form
  158. ref="queryRef"
  159. :model="state.formQuery"
  160. inline
  161. class="search-form"
  162. >
  163. <el-form-item label="审核状态">
  164. <el-select
  165. v-model="state.formQuery.status"
  166. placeholder="请选择审核状态"
  167. clearable
  168. @change="handleSearch"
  169. >
  170. <el-option
  171. v-for="item in state.statusOptions"
  172. :key="item.value"
  173. :label="item.label"
  174. :value="item.value"
  175. />
  176. </el-select>
  177. </el-form-item>
  178. <el-form-item label="打款状态">
  179. <el-select
  180. v-model="state.formQuery.paymentStatus"
  181. placeholder="请选择打款状态"
  182. clearable
  183. @change="handleSearch"
  184. >
  185. <el-option
  186. v-for="item in state.paymentStatusOptions"
  187. :key="item.value"
  188. :label="item.label"
  189. :value="item.value"
  190. />
  191. </el-select>
  192. </el-form-item>
  193. <el-form-item>
  194. <el-button
  195. type="primary"
  196. :icon="useRenderIcon('ri/search-line')"
  197. @click="handleSearch"
  198. >
  199. 查询
  200. </el-button>
  201. <el-button
  202. :icon="useRenderIcon('ri/refresh-line')"
  203. @click="handleReset"
  204. >
  205. 重置
  206. </el-button>
  207. </el-form-item>
  208. </el-form>
  209. <el-table
  210. ref="tableRef"
  211. v-loading="state.tableData.loading"
  212. :data="state.tableData.data"
  213. :height="state.tableData.height"
  214. border
  215. stripe
  216. >
  217. <template #empty>
  218. <el-empty description="暂无提现记录" />
  219. </template>
  220. <el-table-column
  221. v-for="col in state.tableData.columns"
  222. :key="col.prop"
  223. :prop="col.prop"
  224. :label="col.label"
  225. :width="col.width"
  226. show-overflow-tooltip
  227. >
  228. <template #default="{ row }">
  229. <template v-if="col.prop === 'withdrawnAmount'">
  230. {{ formatMoney(row[col.prop]) }}
  231. </template>
  232. <template v-else-if="col.prop === 'status'">
  233. <el-tag :type="getDictColor('WithdrawnRecord.status', row.status)" size="small">
  234. {{ formatDict('WithdrawnRecord.status', row.status) }}
  235. </el-tag>
  236. </template>
  237. <template v-else-if="col.prop === 'paymentStatus'">
  238. <el-tag :type="getDictColor('WithdrawnRecord.paymentStatus', row.paymentStatus)" size="small">
  239. {{ formatDict('WithdrawnRecord.paymentStatus', row.paymentStatus) }}
  240. </el-tag>
  241. </template>
  242. <template v-else>
  243. {{ row[col.prop] }}
  244. </template>
  245. </template>
  246. </el-table-column>
  247. <el-table-column label="操作" width="180" fixed="right">
  248. <template #default="{ row }">
  249. <el-button
  250. v-if="row.status === 0"
  251. type="warning"
  252. link
  253. size="small"
  254. @click="handleWithdrawAudit(row)"
  255. >
  256. 提现审核
  257. </el-button>
  258. <el-button
  259. v-if="row.paymentStatus === 0"
  260. type="danger"
  261. link
  262. size="small"
  263. @click="handleWithdrawConfirm(row)"
  264. >
  265. 打款确认
  266. </el-button>
  267. </template>
  268. </el-table-column>
  269. </el-table>
  270. <div class="pagination-container">
  271. <el-pagination
  272. v-model:current-page="state.pageQuery.pageNum"
  273. v-model:page-size="state.pageQuery.pageSize"
  274. :total="state.pageQuery.total"
  275. :page-sizes="[10, 20, 50, 100]"
  276. layout="total, sizes, prev, pager, next, jumper"
  277. @size-change="handleSizeChange"
  278. @current-change="handleCurrentChange"
  279. />
  280. </div>
  281. </el-card>
  282. </div>
  283. </template>
  284. <style scoped lang="scss">
  285. .page-container {
  286. padding: 20px;
  287. }
  288. .card-header {
  289. font-size: 16px;
  290. font-weight: 600;
  291. }
  292. .search-form {
  293. margin-bottom: 16px;
  294. }
  295. .pagination-container {
  296. display: flex;
  297. justify-content: flex-end;
  298. margin-top: 20px;
  299. }
  300. </style>