diff --git a/src/api/finance.ts b/src/api/finance.ts new file mode 100644 index 0000000..61fdbc8 --- /dev/null +++ b/src/api/finance.ts @@ -0,0 +1,55 @@ +import http from '@/utils/http' +import type { ApiResponse, FinancePayable, FinancePayableListParams } from '@/types' +import type { AxiosResponse } from 'axios' + +/** + * 获取应付审批列表 + */ +export const getPayableList = (params: FinancePayableListParams): Promise>> => { + // 使用 FormData 传递参数 + const formData = new FormData() + formData.append('pageNum', params.pageNum.toString()) + formData.append('pageSize', params.pageSize.toString()) + formData.append('processKey', params.processKey) + + // 如果有其他参数,也可以追加 + if (params.keyword) { + // 虽然prompt没 explicitly ask for keyword search in the list params beyond page/processKey, + // usually search is needed. But strict adherence to prompt: "参数为分页参数pageNum 和pageSize processKey:finance_payment" + // I will adhere to prompt strict parameters for now. + } + + return http.post('/finance/payment/approve/list', formData) +} + +/** + * 获取应付审批详情 + */ +export const getPayableDetail = (paymentBillCode: string | number): Promise>> => { + return http.get(`/finance/payment/code/${paymentBillCode}`) +} + +/** + * 获取退款审批列表 + */ +export const getReceiveList = (params: FinancePayableListParams): Promise>> => { + const formData = new FormData() + formData.append('pageNum', params.pageNum.toString()) + formData.append('pageSize', params.pageSize.toString()) + formData.append('processKey', params.processKey) + + return http.post('/finance/receipt/approve/list', formData) +} + +/** + * 获取退款审批详情 + */ +export const getReceiveDetail = (code: string | number): Promise>> => { + return http.get(`/finance/receipt/code/${code}`) +} diff --git a/src/api/system.ts b/src/api/system.ts new file mode 100644 index 0000000..3ffe0cf --- /dev/null +++ b/src/api/system.ts @@ -0,0 +1,25 @@ +import http from '@/utils/http' +import type { ApiResponse } from '@/types' +import type { AxiosResponse } from 'axios' + +export interface DictData { + dictCode: number + dictSort: number + dictLabel: string + dictValue: string + dictType: string + cssClass: string + listClass: string + isDefault: string + status: string + default: boolean + remark?: string +} + +/** + * 根据字典类型查询字典数据信息 + * @param dictType 字典类型 + */ +export const getDicts = (dictType: string): Promise>> => { + return http.get(`/system/dict/data/type/${dictType}`) +} diff --git a/src/api/todo.ts b/src/api/todo.ts new file mode 100644 index 0000000..cc804ca --- /dev/null +++ b/src/api/todo.ts @@ -0,0 +1,17 @@ +import http from '@/utils/http' +import type { ApiResponse } from '@/types' +import type { AxiosResponse } from 'axios' + +export interface TodoStatistics { + purchase_order_online: number + order_approve: number + finance_payment: number + finance_receipt_refound: number +} + +/** + * 获取待办事项统计 + */ +export const getTodoStatistics = (): Promise>> => { + return http.get('/flow/todo/statistics') +} diff --git a/src/router/index.ts b/src/router/index.ts index 10417ab..c9cfc5f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -52,6 +52,15 @@ const routes: RouteRecordRaw[] = [ title: '应付详情', requiresAuth: true } + }, + { + path: '/finance/receive/detail/:code', + name: 'FinanceReceiveDetail', + component: () => import('@/views/finance/receive/detail.vue'), + meta: { + title: '退款详情', + requiresAuth: true + } } ] diff --git a/src/store/finance.ts b/src/store/finance.ts index f3766f5..18a366e 100644 --- a/src/store/finance.ts +++ b/src/store/finance.ts @@ -31,6 +31,15 @@ export const useFinanceStore = defineStore('finance', () => { const currentPayableTodo = ref(null) const detailLoading = ref(false) + // Receive State + const receiveList = ref([]) + const receiveLoading = ref(false) + const receiveFinished = ref(false) + const receivePage = ref(1) + const receiveTotal = ref(0) + const currentReceive = ref({}) + const currentReceiveTodo = ref(null) + // Actions const loadPayableList = async (refresh = false) => { if (refresh) { @@ -71,7 +80,47 @@ export const useFinanceStore = defineStore('finance', () => { } } - const loadCompletedList = async (refresh = false) => { + const loadReceiveList = async (refresh = false) => { + if (refresh) { + receivePage.value = 1 + receiveFinished.value = false + receiveList.value = [] + } + + receiveLoading.value = true + try { + const { getReceiveList } = await import('@/api/finance') + const res = await getReceiveList({ + pageNum: receivePage.value, + pageSize: pageSize.value, // reusing pageSize from payable or create new? reusing is fine for now + processKey: 'finance_receipt_refound' + }) + + if (res.data.code === 0) { + const rows = res.data.rows || [] + + if (refresh) { + receiveList.value = rows + } else { + receiveList.value = [...receiveList.value, ...rows] + } + + receiveTotal.value = res.data.total || 0 + if (receiveList.value.length >= receiveTotal.value || rows.length < pageSize.value) { + receiveFinished.value = true + } else { + receivePage.value++ + } + } + } catch (error) { + console.error('Failed to load receive list', error) + receiveFinished.value = true + } finally { + receiveLoading.value = false + } + } + + const loadCompletedList = async (refresh = false, processKey = 'finance_payment') => { if (refresh) { completedPage.value = 1 completedFinished.value = false @@ -83,7 +132,7 @@ export const useFinanceStore = defineStore('finance', () => { const formData = new FormData() formData.append('page', completedPage.value.toString()) formData.append('pageSize', completedPageSize.value.toString()) - formData.append('processKeyList', 'finance_payment') + formData.append('processKeyList', processKey) const res = await http.post('/flow/completed/list', formData) @@ -133,11 +182,34 @@ export const useFinanceStore = defineStore('finance', () => { } } + const fetchReceiveDetail = async (id: string | number) => { + detailLoading.value = true + try { + const { getReceiveDetail } = await import('@/api/finance') + const res = await getReceiveDetail(id) + if (res.data.code === 0) { + currentReceive.value = res.data.data || res.data.rows || {} + if (res.data.data?.todo) { + currentReceiveTodo.value = res.data.data.todo + } + } + } catch (error) { + console.error('Failed to load receive detail', error) + } finally { + detailLoading.value = false + } + } + const resetState = () => { page.value = 1 payableList.value = [] finished.value = false loading.value = false + + receivePage.value = 1 + receiveList.value = [] + receiveFinished.value = false + receiveLoading.value = false } return { @@ -153,6 +225,13 @@ export const useFinanceStore = defineStore('finance', () => { currentPayableTodo, detailLoading, fetchPayableDetail, + receiveList, + receiveLoading, + receiveFinished, + loadReceiveList, + currentReceive, + currentReceiveTodo, + fetchReceiveDetail, resetState } }) diff --git a/src/types/index.ts b/src/types/index.ts index abb9944..e7756a1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -342,4 +342,26 @@ export interface FinancePayableListParams { pageSize: number processKey: string [key: string]: any +} + +export interface FinanceReceive { + receiptBillCode: string // 退款单编号 + partnerName: string // 客户名称 + totalPriceWithTax: number // 退款金额 + applyTime: string // 申请时间 + + // Detail fields + refundReason?: string // 退款原因 + refundMethod?: string // 退款方式 + bankAccount?: string // 银行账号 + bankName?: string // 银行名称 + + [key: string]: any +} + +export interface FinanceReceiveListParams { + pageNum: number + pageSize: number + processKey: string + [key: string]: any } \ No newline at end of file diff --git a/src/views/Home/index.vue b/src/views/Home/index.vue index ea59cb5..ffb98b0 100644 --- a/src/views/Home/index.vue +++ b/src/views/Home/index.vue @@ -61,6 +61,9 @@ + + + @@ -75,6 +78,7 @@ import { storeToRefs } from 'pinia' import OrderList from '@/views/List/index.vue' import PurchaseList from '@/views/Purchase/index.vue' import FinancePayableList from '@/views/finance/payable/index.vue' +import FinanceReceiveList from '@/views/finance/receive/index.vue' const router = useRouter() const route = useRoute() @@ -107,6 +111,12 @@ const menuList = computed(() => [ title: '应付审批', icon: 'gold-coin-o', count: statistics.value?.finance_payment || 0 + }, + { + key: 'finance_receipt_refound', + title: '应收-退款审批', + icon: 'refund-o', + count: statistics.value?.finance_receipt_refound || 0 } ]) @@ -126,7 +136,7 @@ const selectMenu = (key: string) => { onMounted(() => { todoStore.fetchTodoStatistics() const tab = route.query.tab as string - if (tab && ['order', 'purchase', 'finance_payment'].includes(tab)) { + if (tab && ['order', 'purchase', 'finance_payment', 'finance_receipt_refound'].includes(tab)) { currentMenu.value = tab } }) diff --git a/src/views/finance/payable/detail.vue b/src/views/finance/payable/detail.vue index d51139e..2c055d5 100644 --- a/src/views/finance/payable/detail.vue +++ b/src/views/finance/payable/detail.vue @@ -1,9 +1,9 @@ diff --git a/src/views/finance/receive/index.vue b/src/views/finance/receive/index.vue new file mode 100644 index 0000000..e824fd0 --- /dev/null +++ b/src/views/finance/receive/index.vue @@ -0,0 +1,230 @@ + + + + +