spring
2025-08-06 a6011b5117fd3d19f3d9d0b75103e1d32808326e
完成支出管理
已修改2个文件
已添加3个文件
551 ■■■■■ 文件已修改
src/api/financialManagement/expenseManagement.js 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/Form.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/Modal.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/index.vue 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/expenseManagement.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
import request from "@/utils/request";
// æŸ¥è¯¢åˆ—表
export const listPage = (params) => {
  return request({
    url: "/account/accountExpense/listPage",
    method: "get",
    params,
  });
};
// æ–°å¢ž
export function add(data) {
  return request({
    url: "/account/accountExpense/add",
    method: "post",
    data: data,
  });
}
// ç¼–辑
export function update(data) {
  return request({
    url: "/account/accountExpense/update",
    method: "post",
    data: data,
  });
}
//导出
export const exportAccountExpense = (query) => {
  return request({
    url: "/account/accountExpense/export",
    method: "post",
    data: query,
    responseType: "blob",
  });
};
export const delAccountExpense = (query) => {
  return request({
    url: `account/accountExpense/del`,
    method: "delete",
    data: query,
  });
};
export const getAccountExpense = (id) => {
  return request({
    url: `/account/accountExpense/${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,
  });
}
src/views/financialManagement/expenseManagement/Form.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
<template>
  <el-form :model="form" label-width="100px" :rules="formRules" ref="formRef">
    <el-form-item label="支出日期" prop="expenseDate">
          <el-date-picker
            style="width: 100%"
            v-model="form.expenseDate"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            type="date"
            placeholder="请选择日期"
            clearable
          />
        </el-form-item>
        <el-form-item label="支出类型" prop="expenseType">
          <el-select
            v-model="form.expenseType"
            placeholder="请选择"
            clearable
          >
            <el-option :label="item.label" :value="item.value" v-for="(item,index) in expense_types" :key="index" />
          </el-select>
        </el-form-item>
        <el-form-item label="供应商名称" prop="supplierName">
          <el-input v-model="form.supplierName" placeholder="请输入" />
        </el-form-item>
        <el-form-item label="支出金额" prop="expenseMoney">
          <el-input-number :step="0.01" :min="0" style="width: 100%"
            v-model="form.expenseMoney"
            placeholder="请输入"
          />
        </el-form-item>
        <el-form-item label="支出描述" prop="expenseDescribed">
          <el-input v-model="form.expenseDescribed" placeholder="请输入" />
        </el-form-item>
        <el-form-item label="付款方式" prop="expenseMethod">
          <el-select
            v-model="form.expenseMethod"
            placeholder="请选择"
            clearable
          >
            <el-option :label="item.label" :value="item.value" v-for="(item,index) in checkout_payment" :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 { getAccountExpense } from "@/api/financialManagement/expenseManagement";
import {ref} from "vue";
const { proxy } = getCurrentInstance();
defineOptions({
  name: "新增支出",
});
const { expense_types } = proxy.useDict("expense_types");
const { checkout_payment } = proxy.useDict("checkout_payment");
const formRef = ref(null);
const formRules = {
    supplierName: [{ required: true, trigger: "blur", message: "请输入" }],
    expenseMoney: [{ required: true, trigger: "blur", message: "请输入" }],
    expenseDescribed: [{ required: true, trigger: "blur", message: "请输入" }],
    expenseDate: [{ required: true, trigger: "change", message: "请选择" }],
  expenseType: [{ required: true, trigger: "change", message: "请选择" }],
  expenseMethod: [{ required: true, trigger: "change", message: "请选择" }],
}
const { form, resetForm } = useFormData({
  expenseDate: undefined, // æ”¯å‡ºæ—¥æœŸ
  expenseType: undefined, // æ”¯å‡ºç±»åž‹
  supplierName: undefined, // å®¢æˆ·åç§°
  expenseMoney: undefined, // æ”¯å‡ºé‡‘额
  expenseDescribed: undefined, // æ”¯å‡ºæè¿°
  expenseMethod: undefined, // æ”¶æ¬¾æ–¹å¼
  invoiceNumber: undefined, // å‘票号码
  note: undefined, // å¤‡æ³¨
});
const loadForm = async (id) => {
  const { code, data } = await getAccountExpense(id);
  if (code == 200) {
    form.expenseDate = data.expenseDate;
    form.expenseType = data.expenseType;
    form.supplierName = data.supplierName;
    form.expenseMoney = data.expenseMoney;
    form.expenseDescribed = data.expenseDescribed;
    form.expenseMethod = data.expenseMethod;
    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>
src/views/financialManagement/expenseManagement/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/expenseManagement";
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>
src/views/financialManagement/expenseManagement/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.expenseMethod"
                placeholder="请选择"
                clearable
                style="width: 200px;"
              >
                <el-option
                  v-for="item in checkout_payment"
                  :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, delAccountExpense } from "@/api/financialManagement/expenseManagement";
import { onMounted, getCurrentInstance } from "vue";
import Modal from "./Modal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
import dayjs from "dayjs";
import FilesDia from "../revenueManagement/filesDia.vue";
defineOptions({
  name: "支出管理",
});
// è¡¨æ ¼å¤šé€‰æ¡†é€‰ä¸­é¡¹
const multipleList = ref([]);
const { proxy } = getCurrentInstance();
const modalRef = ref();
const { checkout_payment } = proxy.useDict("checkout_payment");
const { expense_types } = proxy.useDict("expense_types");
const filesDia = ref()
const {
  filters,
  columns,
  dataList,
  pagination,
  getTableData,
  resetFilters,
  onCurrentChange,
} = usePaginationApi(
  listPage,
  {
    expenseMethod: undefined,
  },
  [
    {
      label: "支出日期",
      align: "center",
      prop: "expenseDate",
    },
    {
      label: "支出类型",
      align: "center",
      prop: "expenseType",
      dataType: "tag",
      formatData: (params) => {
        if (expense_types.value.find((m) => m.value == params)) {
          return expense_types.value.find((m) => m.value == params).label;
        } else {
          return null
        }
      },
    },
    {
      label: "供应商名称",
      align: "center",
      prop: "supplierName",
    },
    {
      label: "支出金额",
      align: "center",
      prop: "expenseMoney",
    },
    {
      label: "支出描述",
      align: "center",
      prop: "expenseDescribed",
    },
    {
      label: "付款方式",
      align: "center",
      prop: "expenseMethod",
      dataType: "tag",
      formatData: (params) => {
        if (checkout_payment.value.find((m) => m.value == params)) {
          return checkout_payment.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 delAccountExpense(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/accountExpense/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>
src/views/financialManagement/revenueManagement/index.vue
@@ -105,7 +105,7 @@
} = usePaginationApi(
  listPage,
  {
    searchText: undefined,
    incomeMethod: undefined,
  },
  [
    {