From 87accd3904fd407bc3b626d88cbeca1c7fefead3 Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期二, 05 八月 2025 17:39:03 +0800 Subject: [PATCH] 完成财务管理-收入管理 --- src/views/financialManagement/expenseManagement/index.vue | 0 src/api/financialManagement/revenueManagement.js | 78 ++++++ src/views/financialManagement/revenueManagement/index.vue | 279 +++++++++++++++++++++ src/views/financialManagement/revenueManagement/Form.vue | 123 +++++++++ src/views/financialManagement/revenueManagement/Modal.vue | 69 +++++ src/components/filePreview/index.vue | 3 src/views/financialManagement/financialStatements/index.vue | 0 src/views/financialManagement/revenueManagement/filesDia.vue | 202 +++++++++++++++ 8 files changed, 753 insertions(+), 1 deletions(-) diff --git a/src/api/financialManagement/revenueManagement.js b/src/api/financialManagement/revenueManagement.js new file mode 100644 index 0000000..090ddf8 --- /dev/null +++ b/src/api/financialManagement/revenueManagement.js @@ -0,0 +1,78 @@ +import request from "@/utils/request"; + +// 鏌ヨ鍒楄〃 +export const listPage = (params) => { + return request({ + url: "/account/accountIncome/listPage", + method: "get", + params, + }); +}; + +// 鏂板 +export function add(data) { + return request({ + url: "/account/accountIncome/add", + method: "post", + data: data, + }); +} + +// 缂栬緫 +export function update(data) { + return request({ + url: "/account/accountIncome/update", + method: "post", + data: data, + }); +} + +//瀵煎嚭 +export const exportAccountIncome = (query) => { + return request({ + url: "/account/accountIncome/export", + method: "post", + data: query, + responseType: "blob", + }); +}; + +export const delAccountIncome = (query) => { + return request({ + url: `account/accountIncome/del`, + method: "delete", + data: query, + }); +}; + +export const getAccountIncome = (id) => { + return request({ + url: `/account/accountIncome/${id}`, + method: "get", + }); +}; + +// 鏌ヨ闄勪欢鍒楄〃 +export function fileListPage(query) { + return request({ + url: "/account/accountFile/listPage", + method: "get", + params: query, + }); +} +// 淇濆瓨闄勪欢鍒楄〃 +export function fileAdd(query) { + return request({ + url: "/account/accountFile/add", + method: "post", + data: query, + }); +} +// 鍒犻櫎闄勪欢鍒楄〃 +export function fileDel(query) { + return request({ + url: "/account/accountFile/del", + method: "delete", + data: query, + }); +} diff --git a/src/components/filePreview/index.vue b/src/components/filePreview/index.vue index 7221b25..cda5b56 100644 --- a/src/components/filePreview/index.vue +++ b/src/components/filePreview/index.vue @@ -88,6 +88,7 @@ }); const isPdf = computed(() => { + console.log(fileUrl.value) return /\.pdf$/i.test(fileUrl.value); }); @@ -163,7 +164,7 @@ }; const open = (url) => { - fileUrl.value = javaApi + url; + fileUrl.value = window.location.protocol+'//'+window.location.host+ url; dialogVisible.value = true; }; const handleClose = () => { diff --git a/src/views/financialManagement/expenseManagement/index.vue b/src/views/financialManagement/expenseManagement/index.vue new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/views/financialManagement/expenseManagement/index.vue diff --git a/src/views/financialManagement/financialStatements/index.vue b/src/views/financialManagement/financialStatements/index.vue new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/views/financialManagement/financialStatements/index.vue diff --git a/src/views/financialManagement/revenueManagement/Form.vue b/src/views/financialManagement/revenueManagement/Form.vue new file mode 100644 index 0000000..67b175e --- /dev/null +++ b/src/views/financialManagement/revenueManagement/Form.vue @@ -0,0 +1,123 @@ +<template> + <el-form :model="form" label-width="100px" :rules="formRules" ref="formRef"> + <el-form-item label="鏀跺叆鏃ユ湡" prop="incomeDate"> + <el-date-picker + style="width: 100%" + v-model="form.incomeDate" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + type="date" + placeholder="璇烽�夋嫨鏃ユ湡" + clearable + /> + </el-form-item> + <el-form-item label="鏀跺叆绫诲瀷" prop="incomeType"> + <el-select + v-model="form.incomeType" + placeholder="璇烽�夋嫨" + clearable + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in income_types" :key="index" /> + </el-select> + </el-form-item> + <el-form-item label="瀹㈡埛鍚嶇О" prop="customerName"> + <el-input v-model="form.customerName" placeholder="璇疯緭鍏�" /> + </el-form-item> + <el-form-item label="鏀跺叆閲戦" prop="incomeMoney"> + <el-input-number :step="0.01" :min="0" style="width: 100%" + v-model="form.incomeMoney" + placeholder="璇疯緭鍏�" + /> + </el-form-item> + <el-form-item label="鏀跺叆鎻忚堪" prop="incomeDescribed"> + <el-input v-model="form.incomeDescribed" placeholder="璇疯緭鍏�" /> + </el-form-item> + <el-form-item label="鏀舵鏂瑰紡" prop="incomeMethod"> + <el-select + v-model="form.incomeMethod" + placeholder="璇烽�夋嫨" + clearable + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in payment_methods" :key="index" /> + </el-select> + </el-form-item> + <el-form-item label="鍙戠エ鍙风爜" prop="invoiceNumber"> + <el-input v-model="form.invoiceNumber" placeholder="璇疯緭鍏�" /> + </el-form-item> + <el-form-item label="澶囨敞" prop="note"> + <el-input + v-model="form.note" + placeholder="澶囨敞" + /> + </el-form-item> + + </el-form> +</template> + +<script setup> +import useFormData from "@/hooks/useFormData"; +import { getAccountIncome } from "@/api/financialManagement/revenueManagement"; +import {ref} from "vue"; +const { proxy } = getCurrentInstance(); + + +defineOptions({ + name: "鏂板鏀跺叆", +}); +const { income_types } = proxy.useDict("income_types"); +const { payment_methods } = proxy.useDict("payment_methods"); +const formRef = ref(null); +const formRules = { + customerName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }], + incomeMoney: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }], + incomeDescribed: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }], + incomeDate: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + incomeType: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + incomeMethod: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], +} + +const { form, resetForm } = useFormData({ + incomeDate: undefined, // 鏀跺叆鏃ユ湡 + incomeType: undefined, // 鏀跺叆绫诲瀷 + customerName: undefined, // 瀹㈡埛鍚嶇О + incomeMoney: undefined, // 鏀跺叆閲戦 + incomeDescribed: undefined, // 鏀跺叆鎻忚堪 + incomeMethod: undefined, // 鏀舵鏂瑰紡 + invoiceNumber: undefined, // 鍙戠エ鍙风爜 + note: undefined, // 澶囨敞 +}); + +const loadForm = async (id) => { + const { code, data } = await getAccountIncome(id); + if (code == 200) { + form.incomeDate = data.incomeDate; + form.incomeType = data.incomeType; + form.customerName = data.customerName; + form.incomeMoney = data.incomeMoney; + form.incomeDescribed = data.incomeDescribed; + form.incomeMethod = data.incomeMethod; + form.invoiceNumber = data.invoiceNumber; + form.note = data.note; + } +}; + +// 娓呴櫎琛ㄥ崟鏍¢獙鐘舵�� +const clearValidate = () => { + formRef.value?.clearValidate(); +}; + +// 閲嶇疆琛ㄥ崟鏁版嵁鍜屾牎楠岀姸鎬� +const resetFormAndValidate = () => { + resetForm(); + clearValidate(); +}; + +defineExpose({ + form, + loadForm, + resetForm, + clearValidate, + resetFormAndValidate, + formRef, +}); +</script> diff --git a/src/views/financialManagement/revenueManagement/Modal.vue b/src/views/financialManagement/revenueManagement/Modal.vue new file mode 100644 index 0000000..480b4fd --- /dev/null +++ b/src/views/financialManagement/revenueManagement/Modal.vue @@ -0,0 +1,69 @@ +<template> + <el-dialog :title="modalOptions.title" v-model="visible" @close="close" width="30%"> + <Form ref="formRef"></Form> + <template #footer> + <el-button type="primary" @click="sendForm" :loading="loading"> + {{ modalOptions.confirmText }} + </el-button> + <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button> + </template> + </el-dialog> +</template> + +<script setup> +import { useModal } from "@/hooks/useModal"; +import { add, update } from "@/api/financialManagement/revenueManagement"; +import Form from "./Form.vue"; +import { ElMessage } from "element-plus"; +const { proxy } = getCurrentInstance() + +defineOptions({ + name: "鏀跺叆鏂板缂栬緫", +}); + +const emits = defineEmits(["success"]); + +const formRef = ref(); +const { + id, + visible, + loading, + openModal, + modalOptions, + handleConfirm, + closeModal, +} = useModal({ title: "鏀跺叆" }); + +const sendForm = () => { + proxy.$refs.formRef.$refs.formRef.validate(async valid => { + if (valid) { + const {code} = id.value + ? await update({id: id.value, ...formRef.value.form}) + : await add(formRef.value.form); + if (code == 200) { + emits("success"); + ElMessage({message: "鎿嶄綔鎴愬姛", type: "success"}); + close(); + } else { + loading.value = false; + } + } + }) +}; + +const close = () => { + formRef.value.resetFormAndValidate(); + closeModal(); +}; + +const loadForm = async (id) => { + openModal(id); + await nextTick(); + formRef.value.loadForm(id); +}; + +defineExpose({ + openModal, + loadForm, +}); +</script> diff --git a/src/views/financialManagement/revenueManagement/filesDia.vue b/src/views/financialManagement/revenueManagement/filesDia.vue new file mode 100644 index 0000000..f752496 --- /dev/null +++ b/src/views/financialManagement/revenueManagement/filesDia.vue @@ -0,0 +1,202 @@ +<template> + <div> + <el-dialog + v-model="dialogFormVisible" + title="涓婁紶闄勪欢" + width="50%" + @close="closeDia" + > + <div style="margin-bottom: 10px;text-align: right"> + <el-upload + v-model:file-list="fileList" + class="upload-demo" + :action="uploadUrl" + :on-success="handleUploadSuccess" + :on-error="handleUploadError" + name="file" + :show-file-list="false" + :headers="headers" + style="display: inline;margin-right: 10px" + > + <el-button type="primary">涓婁紶闄勪欢</el-button> + </el-upload> + <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> + </div> + <PIMTable + rowKey="id" + :column="tableColumn" + :tableData="tableData" + :tableLoading="tableLoading" + :isSelection="true" + @selection-change="handleSelectionChange" + height="500" + > + </PIMTable> + <pagination + style="margin: 10px 0" + v-show="total > 0" + @pagination="paginationSearch" + :total="total" + :page="page.current" + :limit="page.size" + /> + <template #footer> + <div class="dialog-footer"> + <el-button @click="closeDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + <filePreview ref="filePreviewRef" /> + </div> +</template> + +<script setup> +import {ref} from "vue"; +import {ElMessageBox} from "element-plus"; +import {getToken} from "@/utils/auth.js"; +import filePreview from '@/components/filePreview/index.vue' +import { + fileAdd, + fileDel, + fileListPage +} from "@/api/financialManagement/revenueManagement.js"; +import Pagination from "@/components/PIMTable/Pagination.vue"; +const { proxy } = getCurrentInstance() +const emit = defineEmits(['close']) + +const dialogFormVisible = ref(false); +const currentId = ref('') +const selectedRows = ref([]); +const filePreviewRef = ref() +const tableColumn = ref([ + { + label: "鏂囦欢鍚嶇О", + prop: "name", + }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + operation: [ + { + name: "涓嬭浇", + type: "text", + clickFun: (row) => { + downLoadFile(row); + }, + }, + { + name: "棰勮", + type: "text", + clickFun: (row) => { + lookFile(row); + }, + } + ], + }, +]); +const page = reactive({ + current: 1, + size: 100, +}); +const total = ref(0); +const tableData = ref([]); +const fileList = ref([]); +const tableLoading = ref(false); +const accountType = ref('') +const headers = ref({ + Authorization: "Bearer " + getToken(), +}); +const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃 + +// 鎵撳紑寮规 +const openDialog = (row,type) => { + accountType.value = type; + dialogFormVisible.value = true; + currentId.value = row.id; + getList() +} +const paginationSearch = (obj) => { + page.current = obj.page; + page.size = obj.limit; + getList(); +}; +const getList = () => { + fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => { + tableData.value = res.data.records; + total.value = res.data.total; + }) +} +// 琛ㄦ牸閫夋嫨鏁版嵁 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; + +// 鍏抽棴寮规 +const closeDia = () => { + dialogFormVisible.value = false; + emit('close') +}; +// 涓婁紶鎴愬姛澶勭悊 +function handleUploadSuccess(res, file) { + // 濡傛灉涓婁紶鎴愬姛 + if (res.code == 200) { + const fileRow = {} + fileRow.name = res.data.originalName + fileRow.url = res.data.tempPath + uploadFile(fileRow) + } else { + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); + } +} +function uploadFile(file) { + file.accountId = currentId.value; + file.accountType = accountType.value; + fileAdd(file).then(res => { + proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛"); + getList() + }) +} +// 涓婁紶澶辫触澶勭悊 +function handleUploadError() { + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); +} +// 涓嬭浇闄勪欢 +const downLoadFile = (row) => { + proxy.$download.name(row.url); +} +// 鍒犻櫎 +const handleDelete = () => { + let ids = []; + if (selectedRows.value.length > 0) { + ids = selectedRows.value.map((item) => item.id); + } else { + proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁"); + return; + } + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(() => { + fileDel(ids).then((res) => { + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); + getList(); + }); + }).catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +// 棰勮闄勪欢 +const lookFile = (row) => { + filePreviewRef.value.open(row.url) +} + +defineExpose({ + openDialog, +}); +</script> + +<style scoped> + +</style> \ No newline at end of file diff --git a/src/views/financialManagement/revenueManagement/index.vue b/src/views/financialManagement/revenueManagement/index.vue new file mode 100644 index 0000000..f653c15 --- /dev/null +++ b/src/views/financialManagement/revenueManagement/index.vue @@ -0,0 +1,279 @@ +<template> + <div class="app-container"> + <el-form :model="filters" :inline="true"> + <el-form-item label="褰曞叆鏃ユ湡:"> + <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" + placeholder="璇烽�夋嫨" clearable @change="changeDaterange" /> + </el-form-item> + <el-form-item label="鏀舵鏂瑰紡:"> + <el-select + v-model="filters.incomeMethod" + placeholder="璇烽�夋嫨" + clearable + style="width: 200px;" + > + <el-option + v-for="item in payment_methods" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="getTableData">鎼滅储</el-button> + <el-button @click="resetFilters">閲嶇疆</el-button> + </el-form-item> + </el-form> + <div class="table_list"> + <div class="actions"> + <div></div> + <div> + <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button> + <el-button @click="handleOut" icon="download">瀵煎嚭</el-button> + <el-button + type="danger" + icon="Delete" + :disabled="multipleList.length <= 0" + @click="deleteRow(multipleList.map((item) => item.id))" + > + 鎵归噺鍒犻櫎 + </el-button> + </div> + </div> + <PIMTable + rowKey="id" + isSelection + :column="columns" + :tableData="dataList" + :page="{ + current: pagination.currentPage, + size: pagination.pageSize, + total: pagination.total, + }" + @selection-change="handleSelectionChange" + @pagination="changePage" + > + <template #operation="{ row }"> + <el-button type="primary" text @click="edit(row.id)" icon="editPen"> + 缂栬緫 + </el-button> + <el-button + type="primary" + text + @click="openFilesFormDia(row)" + > + 闄勪欢 + </el-button> + </template> + </PIMTable> + </div> + <Modal ref="modalRef" @success="getTableData"></Modal> + <files-dia ref="filesDia" @close="handleQuery"></files-dia> + </div> +</template> + +<script setup> +import { usePaginationApi } from "@/hooks/usePaginationApi"; +import { listPage, delAccountIncome } from "@/api/financialManagement/revenueManagement"; +import { onMounted, getCurrentInstance } from "vue"; +import Modal from "./Modal.vue"; +import { ElMessageBox, ElMessage } from "element-plus"; +import dayjs from "dayjs"; +import FilesDia from "./filesDia.vue"; + +defineOptions({ + name: "鏀跺叆绠$悊", +}); + +// 琛ㄦ牸澶氶�夋閫変腑椤� +const multipleList = ref([]); +const { proxy } = getCurrentInstance(); +const modalRef = ref(); +const { payment_methods } = proxy.useDict("payment_methods"); +const { income_types } = proxy.useDict("income_types"); +const filesDia = ref() + +const { + filters, + columns, + dataList, + pagination, + getTableData, + resetFilters, + onCurrentChange, +} = usePaginationApi( + listPage, + { + searchText: undefined, + }, + [ + { + label: "鏀跺叆鏃ユ湡", + align: "center", + prop: "incomeDate", + }, + { + label: "鏀跺叆绫诲瀷", + align: "center", + prop: "incomeType", + dataType: "tag", + formatData: (params) => { + if (income_types.value.find((m) => m.value == params)) { + return income_types.value.find((m) => m.value == params).label; + } else { + return null + } + }, + }, + { + label: "瀹㈡埛鍚嶇О", + align: "center", + prop: "customerName", + + }, + { + label: "鏀跺叆閲戦", + align: "center", + prop: "incomeMoney", + + }, + { + label: "鏀跺叆鎻忚堪", + align: "center", + prop: "incomeDescribed", + + }, + { + label: "鏀舵鏂瑰紡", + align: "center", + prop: "incomeMethod", + dataType: "tag", + formatData: (params) => { + if (payment_methods.value.find((m) => m.value == params)) { + return payment_methods.value.find((m) => m.value == params).label; + } else { + return null + } + }, + }, + { + label: "鍙戠エ鍙风爜", + align: "center", + prop: "invoiceNumber", + + }, + { + label: "澶囨敞", + align: "center", + prop: "note", + + }, + { + label: "褰曞叆浜�", + align: "center", + prop: "inputUser", + }, + { + label: "褰曞叆鏃ユ湡", + align: "center", + prop: "inputTime", + + }, + { + fixed: "right", + label: "鎿嶄綔", + dataType: "slot", + slot: "operation", + align: "center", + width: "200px", + }, + ] +); + +// 澶氶�夊悗鍋氫粈涔� +const handleSelectionChange = (selectionList) => { + multipleList.value = selectionList; +}; + +const add = () => { + modalRef.value.openModal(); +}; +const edit = (id) => { + modalRef.value.loadForm(id); +}; +const changePage = ({ page, limit }) => { + pagination.currentPage = page; + pagination.pageSize = limit; + onCurrentChange(page); +}; +const deleteRow = (id) => { + ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹�, 鏄惁缁х画?", "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(async () => { + const { code } = await delAccountIncome(id); + if (code == 200) { + ElMessage({ + type: "success", + message: "鍒犻櫎鎴愬姛", + }); + getTableData(); + } + }); +}; + +const changeDaterange = (value) => { + if (value) { + filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); + filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); + } else { + filters.entryDateStart = undefined; + filters.entryDateEnd = undefined; + } + getTableData(); +}; + +const handleOut = () => { + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + proxy.download(`/account/accountIncome/export`, {}, "鏀跺叆鍙拌处.xlsx"); + }) + .catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +// 鎵撳紑闄勪欢寮规 +const openFilesFormDia = (row) => { + nextTick(() => { + filesDia.value?.openDialog( row,'鏀跺叆') + }) +}; + +onMounted(() => { + filters.entryDate = [ + dayjs().format("YYYY-MM-DD"), + dayjs().add(1, "day").format("YYYY-MM-DD"), + ] + filters.entryDateStart = dayjs().format("YYYY-MM-DD") + filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD") + getTableData(); +}); +</script> + +<style lang="scss" scoped> +.table_list { + margin-top: unset; +} +.actions { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} +</style> + -- Gitblit v1.9.3