From 6570b36a352edd87532dcf13a124181d4d815a39 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期二, 30 六月 2026 13:23:26 +0800
Subject: [PATCH] 销项发票页面新增录入发票功能
---
src/views/financialManagement/assets/intangibleAssets.vue | 494 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 494 insertions(+), 0 deletions(-)
diff --git a/src/views/financialManagement/assets/intangibleAssets.vue b/src/views/financialManagement/assets/intangibleAssets.vue
new file mode 100644
index 0000000..b2c13b1
--- /dev/null
+++ b/src/views/financialManagement/assets/intangibleAssets.vue
@@ -0,0 +1,494 @@
+<template>
+ <div class="app-container">
+ <el-form :model="filters" :inline="true">
+ <el-form-item label="璧勪骇缂栧彿:">
+ <el-input v-model="filters.assetCode" placeholder="璇疯緭鍏ヨ祫浜х紪鍙�" clearable style="width: 200px;" />
+ </el-form-item>
+ <el-form-item label="璧勪骇鍚嶇О:">
+ <el-input v-model="filters.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" clearable style="width: 200px;" />
+ </el-form-item>
+ <el-form-item label="璧勪骇绫诲埆:">
+ <el-select v-model="filters.category" placeholder="璇烽�夋嫨绫诲埆" clearable style="width: 150px;">
+ <el-option label="涓撳埄鏉�" value="patent" />
+ <el-option label="鍟嗘爣鏉�" value="trademark" />
+ <el-option label="钁椾綔鏉�" value="copyright" />
+ <el-option label="杞欢" value="software" />
+ <el-option label="鍦熷湴浣跨敤鏉�" value="land" />
+ <el-option label="鍏朵粬" value="other" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐘舵��:">
+ <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+ <el-option label="鍦ㄧ敤" value="in_use" />
+ <el-option label="闂茬疆" value="idle" />
+ <el-option label="宸叉憡閿�瀹屾瘯" value="amortized" />
+ </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>
+ <el-statistic title="璧勪骇鍘熷�煎悎璁�" :value="totalOriginalValue" precision="2" prefix="楼" />
+ <el-statistic title="绱鎽婇攢鍚堣" :value="totalAmortization" precision="2" prefix="楼" style="margin-left: 30px;" />
+ <el-statistic title="鍑�鍊煎悎璁�" :value="totalNetValue" precision="2" prefix="楼" style="margin-left: 30px;" />
+ </div>
+ <div>
+ <el-button type="primary" @click="add" icon="Plus">鏂板璧勪骇</el-button>
+ <el-button type="warning" @click="handleAmortization" icon="Money">鎽婇攢璁℃彁</el-button>
+ <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</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 #originalValue="{ row }">
+ <span class="text-primary">楼{{ formatMoney(row.originalValue) }}</span>
+ </template>
+ <template #accumulatedAmortization="{ row }">
+ <span class="text-warning">楼{{ formatMoney(row.accumulatedAmortization) }}</span>
+ </template>
+ <template #netValue="{ row }">
+ <span class="text-success">楼{{ formatMoney(row.netValue) }}</span>
+ </template>
+ <template #category="{ row }">
+ <el-tag>{{ getCategoryLabel(row.category) }}</el-tag>
+ </template>
+ <template #status="{ row }">
+ <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+ </template>
+ <template #operation="{ row }">
+ <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+ <el-button v-if="row.status !== 'amortized'" type="primary" link @click="edit(row)">缂栬緫</el-button>
+ <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+ </template>
+ </PIMTable>
+ </div>
+
+ <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+ <el-form :model="form" :rules="rules" :disabled="isView" ref="formRef" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璧勪骇缂栧彿" prop="assetCode">
+ <el-input v-model="form.assetCode" placeholder="淇濆瓨鍚庤嚜鍔ㄧ敓鎴�" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璧勪骇鍚嶇О" prop="assetName">
+ <el-input v-model="form.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璧勪骇绫诲埆" prop="category">
+ <el-select v-model="form.category" placeholder="璇烽�夋嫨璧勪骇绫诲埆" style="width: 100%;">
+ <el-option label="涓撳埄鏉�" value="patent" />
+ <el-option label="鍟嗘爣鏉�" value="trademark" />
+ <el-option label="钁椾綔鏉�" value="copyright" />
+ <el-option label="杞欢" value="software" />
+ <el-option label="鍦熷湴浣跨敤鏉�" value="land" />
+ <el-option label="鍏朵粬" value="other" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璇佷功缂栧彿" prop="certificateNo">
+ <el-input v-model="form.certificateNo" placeholder="璇疯緭鍏ヨ瘉涔︾紪鍙�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍙栧緱鏃ユ湡" prop="acquisitionDate">
+ <el-date-picker v-model="form.acquisitionDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璧勪骇鍘熷��" prop="originalValue">
+ <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鎽婇攢骞撮檺" prop="amortizationPeriod">
+ <el-input-number v-model="form.amortizationPeriod" :min="1" :max="50" style="width: 100%;" />
+ <span style="margin-left: 10px;">骞�</span>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="娈嬪�肩巼" prop="residualRate">
+ <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" />
+ <span style="margin-left: 10px;">%</span>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="绱鎽婇攢">
+ <el-input v-model="form.accumulatedAmortization" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璧勪骇鍑�鍊�">
+ <el-input v-model="form.netValue" disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鏈夋晥鏈熻嚦" prop="validityDate">
+ <el-date-picker v-model="form.validityDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐘舵��" prop="status">
+ <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%;">
+ <el-option label="鍦ㄧ敤" value="in_use" />
+ <el-option label="闂茬疆" value="idle" />
+ <el-option v-if="isView" label="宸叉憡閿�瀹屾瘯" value="amortized" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍒涘缓鏃堕棿" prop="createTime">
+ <el-date-picker v-model="createTimeDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="璧勪骇鎻忚堪" prop="description">
+ <el-input v-model="form.description" type="textarea" :rows="3" placeholder="璇疯緭鍏ヨ祫浜ф弿杩�" />
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button v-if="!isView" type="primary" @click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ </template>
+ </FormDialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import dayjs from "dayjs";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import {
+ listIntangibleAssetPage,
+ addIntangibleAsset,
+ updateIntangibleAsset,
+ deleteIntangibleAsset,
+ amortizeIntangibleAsset,
+} from "@/api/financialManagement/intangibleAsset";
+
+defineOptions({
+ name: "鏃犲舰璧勪骇",
+});
+
+const filters = reactive({
+ assetCode: "",
+ assetName: "",
+ category: "",
+ status: "",
+});
+
+const pagination = reactive({
+ currentPage: 1,
+ pageSize: 10,
+ total: 0,
+});
+
+const columns = [
+ { label: "璧勪骇缂栧彿", prop: "assetCode", width: "130" },
+ { label: "璧勪骇鍚嶇О", prop: "assetName", width: "150" },
+ { label: "璧勪骇绫诲埆", prop: "category", dataType: "slot", slot: "category" },
+ { label: "璇佷功缂栧彿", prop: "certificateNo", width: "150" },
+ { label: "璧勪骇鍘熷��", prop: "originalValue", dataType: "slot", slot: "originalValue" },
+ { label: "绱鎽婇攢", prop: "accumulatedAmortization", dataType: "slot", slot: "accumulatedAmortization" },
+ { label: "璧勪骇鍑�鍊�", prop: "netValue", dataType: "slot", slot: "netValue" },
+ { label: "鐘舵��", prop: "status", dataType: "slot", slot: "status" },
+ { label: "鎿嶄綔", prop: "operation", dataType: "slot", slot: "operation", width: "180", fixed: "right" },
+];
+
+const dataList = ref([]);
+const multipleList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const isView = ref(false);
+const currentId = ref(null);
+const selectedIds = computed(() =>
+ multipleList.value
+ .map(item => item?.id)
+ .filter(id => id !== undefined && id !== null && id !== "")
+);
+
+const createDefaultForm = () => ({
+ id: null,
+ assetCode: "",
+ assetName: "",
+ category: "",
+ certificateNo: "",
+ acquisitionDate: "",
+ originalValue: 0,
+ amortizationPeriod: 10,
+ residualRate: 0,
+ accumulatedAmortization: 0,
+ netValue: 0,
+ validityDate: "",
+ status: "in_use",
+ description: "",
+ remark: "",
+ createTime: "",
+});
+
+const form = reactive({
+ ...createDefaultForm(),
+});
+const createTimeDate = computed({
+ get: () => (form.createTime ? String(form.createTime).split(" ")[0] : ""),
+ set: (value) => {
+ form.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : "";
+ },
+});
+
+const rules = {
+ assetName: [{ required: true, message: "璇疯緭鍏ヨ祫浜у悕绉�", trigger: "blur" }],
+ category: [{ required: true, message: "璇烽�夋嫨璧勪骇绫诲埆", trigger: "change" }],
+ acquisitionDate: [{ required: true, message: "璇烽�夋嫨鍙栧緱鏃ユ湡", trigger: "change" }],
+ originalValue: [{ required: true, message: "璇疯緭鍏ヨ祫浜у師鍊�", trigger: "blur" }],
+ amortizationPeriod: [{ required: true, message: "璇疯緭鍏ユ憡閿�骞撮檺", trigger: "blur" }],
+};
+
+const totalOriginalValue = computed(() => {
+ return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0);
+});
+
+const totalAmortization = computed(() => {
+ return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedAmortization), 0);
+});
+
+const totalNetValue = computed(() => {
+ return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0);
+});
+
+const formatMoney = (value) => {
+ if (value === undefined || value === null) return "0.00";
+ return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getCategoryLabel = (category) => {
+ const map = {
+ patent: "涓撳埄鏉�",
+ trademark: "鍟嗘爣鏉�",
+ copyright: "钁椾綔鏉�",
+ software: "杞欢",
+ land: "鍦熷湴浣跨敤鏉�",
+ other: "鍏朵粬",
+ };
+ return map[category] || category;
+};
+
+const getStatusLabel = (status) => {
+ const key = String(status || "").toLowerCase();
+ const map = {
+ in_use: "鍦ㄧ敤",
+ idle: "闂茬疆",
+ expired: "宸插埌鏈�",
+ amortized: "宸叉憡閿�瀹屾瘯",
+ };
+ return map[key] || status;
+};
+
+const getStatusType = (status) => {
+ const key = String(status || "").toLowerCase();
+ const map = { in_use: "success", idle: "warning", expired: "warning", amortized: "info" };
+ return map[key] || "";
+};
+
+const calculateNetValue = () => {
+ const originalValue = Number(form.originalValue || 0);
+ const accumulatedAmortization = Number(form.accumulatedAmortization || 0);
+ form.netValue = Number((originalValue - accumulatedAmortization).toFixed(2));
+};
+
+// 鑱旇皟绾﹀畾锛氬垎椤靛弬鏁板浐瀹氫负 current/size锛岃繑鍥� data.records/data.total
+const getTableData = async () => {
+ try {
+ const { data } = await listIntangibleAssetPage({
+ current: pagination.currentPage,
+ size: pagination.pageSize,
+ assetCode: filters.assetCode,
+ assetName: filters.assetName,
+ category: filters.category,
+ status: filters.status,
+ });
+ dataList.value = data?.records || [];
+ multipleList.value = [];
+ pagination.total = Number(data?.total || 0);
+ } catch (error) {
+ // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
+ }
+};
+
+const handleSelectionChange = (selectionList) => {
+ multipleList.value = selectionList;
+};
+
+const resetFilters = () => {
+ filters.assetCode = "";
+ filters.assetName = "";
+ filters.category = "";
+ filters.status = "";
+ pagination.currentPage = 1;
+ getTableData();
+};
+
+const changePage = ({ current, size }) => {
+ pagination.currentPage = current;
+ pagination.pageSize = size;
+ getTableData();
+};
+
+const add = () => {
+ isEdit.value = false;
+ isView.value = false;
+ currentId.value = null;
+ dialogTitle.value = "鏂板鏃犲舰璧勪骇";
+ Object.assign(form, createDefaultForm(), {
+ acquisitionDate: new Date().toISOString().split('T')[0],
+ createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+ });
+ dialogVisible.value = true;
+};
+
+const edit = (row) => {
+ isEdit.value = true;
+ isView.value = false;
+ currentId.value = row.id;
+ dialogTitle.value = "缂栬緫鏃犲舰璧勪骇";
+ Object.assign(form, createDefaultForm(), row);
+ dialogVisible.value = true;
+};
+
+const view = (row) => {
+ edit(row);
+ isView.value = true;
+};
+
+const handleDelete = (row) => {
+ ElMessageBox.confirm("纭鍒犻櫎璇ユ棤褰㈣祫浜у悧锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(async () => {
+ // 鑱旇皟绾﹀畾锛氬垹闄ゆ帴鍙d娇鐢� ids=1&ids=2
+ await deleteIntangibleAsset([row.id]);
+ if (dataList.value.length === 1 && pagination.currentPage > 1) {
+ pagination.currentPage -= 1;
+ }
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ await getTableData();
+ });
+};
+
+const handleAmortization = () => {
+ const ids = selectedIds.value;
+ const confirmText = ids.length
+ ? `纭瀵归�変腑鐨� ${ids.length} 鏉¤祫浜ц繘琛屾湰鏈堟憡閿�璁℃彁鍚楋紵`
+ : "纭杩涜鏈湀鎽婇攢璁℃彁鍚楋紵";
+ ElMessageBox.confirm(confirmText, "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "info",
+ }).then(async () => {
+ await amortizeIntangibleAsset({ ids });
+ ElMessage.success("鎽婇攢璁℃彁瀹屾垚");
+ await getTableData();
+ });
+};
+
+const handleOut = () => {
+ ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+ if (isView.value) {
+ dialogVisible.value = false;
+ return;
+ }
+ formRef.value.validate(async valid => {
+ if (valid) {
+ try {
+ calculateNetValue();
+ const payload = { ...form };
+ if (isEdit.value) {
+ payload.id = currentId.value;
+ await updateIntangibleAsset(payload);
+ ElMessage.success("缂栬緫鎴愬姛");
+ } else {
+ await addIntangibleAsset(payload);
+ ElMessage.success("鏂板鎴愬姛");
+ }
+ dialogVisible.value = false;
+ await getTableData();
+ } catch (error) {
+ // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
+ }
+ }
+ });
+};
+
+onMounted(() => {
+ getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+
+ > div:first-child {
+ display: flex;
+ align-items: center;
+ }
+}
+
+.text-primary {
+ color: #409eff;
+ font-weight: bold;
+}
+
+.text-warning {
+ color: #e6a23c;
+ font-weight: bold;
+}
+
+.text-success {
+ color: #67c23a;
+ font-weight: bold;
+}
+</style>
--
Gitblit v1.9.3