进销存升级
1.财务模块前端页面(routerjs文件后面要回退)
| | |
| | | }, |
| | | ], |
| | | }, |
| | | // è´¢å¡ç®¡ç模åè·¯ç± |
| | | { |
| | | path: "/financial", |
| | | component: Layout, |
| | | hidden: false, |
| | | redirect: "/financial/general-ledger", |
| | | alwaysShow: true, |
| | | meta: { title: "è´¢å¡ç®¡ç", icon: "money" }, |
| | | children: [ |
| | | { |
| | | path: "general-ledger", |
| | | component: () => import("@/views/financialManagement/generalLedger/index.vue"), |
| | | name: "GeneralLedger", |
| | | meta: { title: "æ»å¸ç§ç®" } |
| | | }, |
| | | { |
| | | path: "sales-out", |
| | | component: () => import("@/views/financialManagement/receivable/salesOut.vue"), |
| | | name: "SalesOut", |
| | | meta: { title: "éå®åºåº" } |
| | | }, |
| | | { |
| | | path: "sales-return", |
| | | component: () => import("@/views/financialManagement/receivable/salesReturn.vue"), |
| | | name: "SalesReturn", |
| | | meta: { title: "éå®éè´§" } |
| | | }, |
| | | { |
| | | path: "receivable-reconciliation", |
| | | component: () => import("@/views/financialManagement/receivable/reconciliation.vue"), |
| | | name: "ReceivableReconciliation", |
| | | meta: { title: "åºæ¶å¯¹è´¦" } |
| | | }, |
| | | { |
| | | path: "invoice-apply", |
| | | component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"), |
| | | name: "InvoiceApply", |
| | | meta: { title: "å¼ç¥¨ç³è¯·" } |
| | | }, |
| | | { |
| | | path: "output-invoice", |
| | | component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"), |
| | | name: "OutputInvoice", |
| | | meta: { title: "é项å票" } |
| | | }, |
| | | { |
| | | path: "receipt", |
| | | component: () => import("@/views/financialManagement/receivable/receipt.vue"), |
| | | name: "Receipt", |
| | | meta: { title: "æ¶æ¬¾å" } |
| | | }, |
| | | { |
| | | path: "purchase-in", |
| | | component: () => import("@/views/financialManagement/payable/purchaseIn.vue"), |
| | | name: "PurchaseIn", |
| | | meta: { title: "éè´å
¥åº" } |
| | | }, |
| | | { |
| | | path: "payable-reconciliation", |
| | | component: () => import("@/views/financialManagement/payable/reconciliation.vue"), |
| | | name: "PayableReconciliation", |
| | | meta: { title: "åºä»å¯¹è´¦" } |
| | | }, |
| | | { |
| | | path: "input-invoice", |
| | | component: () => import("@/views/financialManagement/payable/input-invoice.vue"), |
| | | name: "InputInvoice", |
| | | meta: { title: "è¿é¡¹å票" } |
| | | }, |
| | | { |
| | | path: "payment-apply", |
| | | component: () => import("@/views/financialManagement/payable/paymentApply.vue"), |
| | | name: "PaymentApply", |
| | | meta: { title: "仿¬¾ç³è¯·" } |
| | | }, |
| | | { |
| | | path: "payment", |
| | | component: () => import("@/views/financialManagement/payable/payment.vue"), |
| | | name: "Payment", |
| | | meta: { title: "仿¬¾å" } |
| | | }, |
| | | { |
| | | path: "fixed-assets", |
| | | component: () => import("@/views/financialManagement/assets/fixedAssets.vue"), |
| | | name: "FixedAssets", |
| | | meta: { title: "åºå®èµäº§" } |
| | | }, |
| | | { |
| | | path: "intangible-assets", |
| | | component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"), |
| | | name: "IntangibleAssets", |
| | | meta: { title: "æ å½¢èµäº§" } |
| | | }, |
| | | { |
| | | path: "voucher", |
| | | component: () => import("@/views/financialManagement/voucher/index.vue"), |
| | | name: "Voucher", |
| | | meta: { title: "åè¯" } |
| | | }, |
| | | { |
| | | path: "voucher-general-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/generalLedger.vue"), |
| | | name: "VoucherGeneralLedger", |
| | | meta: { title: "ç§ç®æ»å¸" } |
| | | }, |
| | | { |
| | | path: "voucher-detail-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/detailLedger.vue"), |
| | | name: "VoucherDetailLedger", |
| | | meta: { title: "ç§ç®æç»å¸" } |
| | | } |
| | | ] |
| | | } |
| | | ]; |
| | | |
| | | // å¨æè·¯ç±ï¼åºäºç¨æ·æé卿å»å è½½ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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="building" /> |
| | | <el-option label="æºå¨è®¾å¤" value="machine" /> |
| | | <el-option label="è¿è¾å·¥å
·" value="vehicle" /> |
| | | <el-option label="çµå设å¤" value="electronic" /> |
| | | <el-option label="åå
¬å®¶å
·" value="furniture" /> |
| | | </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="scrapped" /> |
| | | </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="totalDepreciation" 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="handleDepreciation" icon="Money">ææ§è®¡æ</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #originalValue="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.originalValue) }}</span> |
| | | </template> |
| | | <template #accumulatedDepreciation="{ row }"> |
| | | <span class="text-warning">Â¥{{ formatMoney(row.accumulatedDepreciation) }}</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 type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" 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="building" /> |
| | | <el-option label="æºå¨è®¾å¤" value="machine" /> |
| | | <el-option label="è¿è¾å·¥å
·" value="vehicle" /> |
| | | <el-option label="çµå设å¤" value="electronic" /> |
| | | <el-option label="åå
¬å®¶å
·" value="furniture" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·" prop="specification"> |
| | | <el-input v-model="form.specification" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è´ç½®æ¥æ" prop="purchaseDate"> |
| | | <el-date-picker v-model="form.purchaseDate" 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="usefulLife"> |
| | | <el-input-number v-model="form.usefulLife" :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.accumulatedDepreciation" 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="location"> |
| | | <el-input v-model="form.location" placeholder="请è¾å
¥åæ¾å°ç¹" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="使ç¨é¨é¨" prop="department"> |
| | | <el-input v-model="form.department" placeholder="请è¾å
¥ä½¿ç¨é¨é¨" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¿ç®¡äºº" prop="keeper"> |
| | | <el-input v-model="form.keeper" placeholder="请è¾å
¥ä¿ç®¡äºº" /> |
| | | </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 label="æ¥åº" value="scrapped" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | 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", slot: "category" }, |
| | | { label: "è§æ ¼åå·", prop: "specification", width: "120" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", slot: "originalValue" }, |
| | | { label: "ç´¯è®¡ææ§", prop: "accumulatedDepreciation", slot: "accumulatedDepreciation" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const form = reactive({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | specification: "", |
| | | purchaseDate: "", |
| | | originalValue: 0, |
| | | usefulLife: 5, |
| | | residualRate: 5, |
| | | accumulatedDepreciation: 0, |
| | | netValue: 0, |
| | | location: "", |
| | | department: "", |
| | | keeper: "", |
| | | status: "in_use", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | assetName: [{ required: true, message: "请è¾å
¥èµäº§åç§°", trigger: "blur" }], |
| | | category: [{ required: true, message: "è¯·éæ©èµäº§ç±»å«", trigger: "change" }], |
| | | purchaseDate: [{ required: true, message: "è¯·éæ©è´ç½®æ¥æ", trigger: "change" }], |
| | | originalValue: [{ required: true, message: "请è¾å
¥èµäº§åå¼", trigger: "blur" }], |
| | | usefulLife: [{ required: true, message: "请è¾å
¥ä½¿ç¨å¹´é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, assetCode: "GD2024001", assetName: "åå
¬çµè", category: "electronic", specification: "èæ³ThinkPad X1", purchaseDate: "2023-01-15", originalValue: 8000, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 1520, netValue: 6480, location: "åå
¬å®¤", department: "è´¢å¡é¨", keeper: "å¼ ä¸", status: "in_use", remark: "" }, |
| | | { id: 2, assetCode: "GD2024002", assetName: "æå°æº", category: "electronic", specification: "æ æ®M479fdw", purchaseDate: "2023-03-20", originalValue: 3500, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 532, netValue: 2968, location: "æå°å®¤", department: "è¡æ¿é¨", keeper: "æå", status: "in_use", remark: "" }, |
| | | { id: 3, assetCode: "GD2024003", assetName: "åå
¬æ¡æ¤
", category: "furniture", specification: "宿¨åå
¬æ¡", purchaseDate: "2023-06-10", originalValue: 2500, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 118.75, netValue: 2381.25, location: "åå
¬å®¤", department: "éå®é¨", keeper: "çäº", status: "in_use", remark: "" }, |
| | | { id: 4, assetCode: "GD2024004", assetName: "åå¡è½¦", category: "vehicle", specification: "å«å
GL8", purchaseDate: "2022-08-01", originalValue: 280000, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 53200, netValue: 226800, location: "å车åº", department: "è¡æ¿é¨", keeper: "èµµå
", status: "in_use", remark: "" }, |
| | | ]; |
| | | |
| | | const totalOriginalValue = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0); |
| | | }); |
| | | |
| | | const totalDepreciation = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedDepreciation), 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 = { |
| | | building: "æ¿å±å»ºç", |
| | | machine: "æºå¨è®¾å¤", |
| | | vehicle: "è¿è¾å·¥å
·", |
| | | electronic: "çµå设å¤", |
| | | furniture: "åå
¬å®¶å
·", |
| | | }; |
| | | return map[category] || category; |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { in_use: "å¨ç¨", idle: "é²ç½®", scrapped: "æ¥åº" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { in_use: "success", idle: "warning", scrapped: "info" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | form.netValue = Number((form.originalValue - form.accumulatedDepreciation).toFixed(2)); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.assetCode) { |
| | | result = result.filter(item => item.assetCode.includes(filters.assetCode)); |
| | | } |
| | | if (filters.assetName) { |
| | | result = result.filter(item => item.assetName.includes(filters.assetName)); |
| | | } |
| | | if (filters.category) { |
| | | result = result.filter(item => item.category === filters.category); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | 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; |
| | | dialogTitle.value = "æ°å¢åºå®èµäº§"; |
| | | Object.assign(form, { |
| | | assetCode: "GD" + Date.now().toString().slice(-8), |
| | | assetName: "", |
| | | category: "", |
| | | specification: "", |
| | | purchaseDate: new Date().toISOString().split('T')[0], |
| | | originalValue: 0, |
| | | usefulLife: 5, |
| | | residualRate: 5, |
| | | accumulatedDepreciation: 0, |
| | | netValue: 0, |
| | | location: "", |
| | | department: "", |
| | | keeper: "", |
| | | status: "in_use", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºå®èµäº§"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çèµäº§: ${row.assetName}`); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥åºå®èµäº§åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleDepreciation = () => { |
| | | ElMessageBox.confirm("确认è¿è¡æ¬æææ§è®¡æåï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | mockData.forEach(item => { |
| | | if (item.status === "in_use") { |
| | | const monthlyDepreciation = (item.originalValue * (1 - item.residualRate / 100)) / (item.usefulLife * 12); |
| | | item.accumulatedDepreciation = Number((item.accumulatedDepreciation + monthlyDepreciation).toFixed(2)); |
| | | item.netValue = Number((item.originalValue - item.accumulatedDepreciation).toFixed(2)); |
| | | } |
| | | }); |
| | | ElMessage.success("ææ§è®¡æå®æ"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | calculateNetValue(); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | 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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @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 type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" 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 label="å·²æé宿¯" value="amortized" /> |
| | | </el-select> |
| | | </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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | 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", slot: "category" }, |
| | | { label: "è¯ä¹¦ç¼å·", prop: "certificateNo", width: "150" }, |
| | | { label: "èµäº§åå¼", prop: "originalValue", slot: "originalValue" }, |
| | | { label: "累计æé", prop: "accumulatedAmortization", slot: "accumulatedAmortization" }, |
| | | { label: "èµäº§åå¼", prop: "netValue", slot: "netValue" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const form = reactive({ |
| | | assetCode: "", |
| | | assetName: "", |
| | | category: "", |
| | | certificateNo: "", |
| | | acquisitionDate: "", |
| | | originalValue: 0, |
| | | amortizationPeriod: 10, |
| | | residualRate: 0, |
| | | accumulatedAmortization: 0, |
| | | netValue: 0, |
| | | validityDate: "", |
| | | status: "in_use", |
| | | description: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | 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 mockData = [ |
| | | { id: 1, assetCode: "WX2024001", assetName: "ERP软件许å¯", category: "software", certificateNo: "SW-2023-001", acquisitionDate: "2023-01-01", originalValue: 50000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 5000, netValue: 45000, validityDate: "2033-01-01", status: "in_use", description: "ä¼ä¸èµæºè®¡å管çç³»ç»", remark: "" }, |
| | | { id: 2, assetCode: "WX2024002", assetName: "åæä¸å©", category: "patent", certificateNo: "ZL202210123456.7", acquisitionDate: "2022-06-15", originalValue: 100000, amortizationPeriod: 20, residualRate: 0, accumulatedAmortization: 3750, netValue: 96250, validityDate: "2042-06-15", status: "in_use", description: "ä¸ç§æ°åç产工èº", remark: "" }, |
| | | { id: 3, assetCode: "WX2024003", assetName: "åæ æ", category: "trademark", certificateNo: "TM-2023-008", acquisitionDate: "2023-03-10", originalValue: 20000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 1500, netValue: 18500, validityDate: "2033-03-10", status: "in_use", description: "å
¬å¸åçåæ ", remark: "" }, |
| | | { id: 4, assetCode: "WX2024004", assetName: "åå°ä½¿ç¨æ", category: "land", certificateNo: "åå½ç¨(2023)第001å·", acquisitionDate: "2023-07-01", originalValue: 500000, amortizationPeriod: 50, residualRate: 0, accumulatedAmortization: 5000, netValue: 495000, validityDate: "2073-07-01", status: "in_use", description: "å·¥ä¸ç¨å°ä½¿ç¨æ", remark: "" }, |
| | | ]; |
| | | |
| | | 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 map = { in_use: "å¨ç¨", idle: "é²ç½®", amortized: "å·²æé宿¯" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { in_use: "success", idle: "warning", amortized: "info" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const calculateNetValue = () => { |
| | | form.netValue = Number((form.originalValue - form.accumulatedAmortization).toFixed(2)); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.assetCode) { |
| | | result = result.filter(item => item.assetCode.includes(filters.assetCode)); |
| | | } |
| | | if (filters.assetName) { |
| | | result = result.filter(item => item.assetName.includes(filters.assetName)); |
| | | } |
| | | if (filters.category) { |
| | | result = result.filter(item => item.category === filters.category); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | 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; |
| | | dialogTitle.value = "æ°å¢æ å½¢èµäº§"; |
| | | Object.assign(form, { |
| | | assetCode: "WX" + Date.now().toString().slice(-8), |
| | | assetName: "", |
| | | category: "", |
| | | certificateNo: "", |
| | | acquisitionDate: new Date().toISOString().split('T')[0], |
| | | originalValue: 0, |
| | | amortizationPeriod: 10, |
| | | residualRate: 0, |
| | | accumulatedAmortization: 0, |
| | | netValue: 0, |
| | | validityDate: "", |
| | | status: "in_use", |
| | | description: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ å½¢èµäº§"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çèµäº§: ${row.assetName}`); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥æ å½¢èµäº§åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleAmortization = () => { |
| | | ElMessageBox.confirm("确认è¿è¡æ¬ææé计æåï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | mockData.forEach(item => { |
| | | if (item.status === "in_use") { |
| | | const monthlyAmortization = (item.originalValue * (1 - item.residualRate / 100)) / (item.amortizationPeriod * 12); |
| | | item.accumulatedAmortization = Number((item.accumulatedAmortization + monthlyAmortization).toFixed(2)); |
| | | item.netValue = Number((item.originalValue - item.accumulatedAmortization).toFixed(2)); |
| | | if (item.netValue <= 0) { |
| | | item.status = "amortized"; |
| | | item.netValue = 0; |
| | | } |
| | | } |
| | | }); |
| | | ElMessage.success("æé计æå®æ"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | calculateNetValue(); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | 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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ç§ç®ç¼ç :"> |
| | | <el-input v-model="filters.subjectCode" placeholder="请è¾å
¥ç§ç®ç¼ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®åç§°:"> |
| | | <el-input v-model="filters.subjectName" placeholder="请è¾å
¥ç§ç®åç§°" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç±»å:"> |
| | | <el-select v-model="filters.subjectType" placeholder="è¯·éæ©" clearable style="width: 200px;"> |
| | | <el-option label="èµäº§ç±»" value="asset" /> |
| | | <el-option label="è´åºç±»" value="liability" /> |
| | | <el-option label="æçç±»" value="equity" /> |
| | | <el-option label="ææ¬ç±»" value="cost" /> |
| | | <el-option label="æçç±»" value="profit_loss" /> |
| | | </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> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #subjectType="{ row }"> |
| | | <el-tag :type="getSubjectTypeType(row.subjectType)">{{ getSubjectTypeLabel(row.subjectType) }}</el-tag> |
| | | </template> |
| | | <template #balanceDirection="{ row }"> |
| | | <el-tag :type="row.balanceDirection === 'debit' ? 'success' : 'danger'"> |
| | | {{ row.balanceDirection === 'debit' ? 'åæ¹' : 'è´·æ¹' }} |
| | | </el-tag> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="row.status === 'active' ? 'success' : 'info'"> |
| | | {{ row.status === 'active' ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-form-item label="ç§ç®ç¼ç " prop="subjectCode"> |
| | | <el-input v-model="form.subjectCode" placeholder="请è¾å
¥ç§ç®ç¼ç " /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®åç§°" prop="subjectName"> |
| | | <el-input v-model="form.subjectName" placeholder="请è¾å
¥ç§ç®åç§°" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç§ç®ç±»å" prop="subjectType"> |
| | | <el-select v-model="form.subjectType" placeholder="è¯·éæ©ç§ç®ç±»å" style="width: 100%;"> |
| | | <el-option label="èµäº§ç±»" value="asset" /> |
| | | <el-option label="è´åºç±»" value="liability" /> |
| | | <el-option label="æçç±»" value="equity" /> |
| | | <el-option label="ææ¬ç±»" value="cost" /> |
| | | <el-option label="æçç±»" value="profit_loss" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ä½é¢æ¹å" prop="balanceDirection"> |
| | | <el-radio-group v-model="form.balanceDirection"> |
| | | <el-radio label="debit">åæ¹</el-radio> |
| | | <el-radio label="credit">è´·æ¹</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio label="active">å¯ç¨</el-radio> |
| | | <el-radio label="inactive">ç¦ç¨</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "æ»å¸ç§ç®", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "ç§ç®ç¼ç ", prop: "subjectCode", width: "120" }, |
| | | { label: "ç§ç®åç§°", prop: "subjectName", width: "150" }, |
| | | { label: "ç§ç®ç±»å", prop: "subjectType", slot: "subjectType" }, |
| | | { label: "ä½é¢æ¹å", prop: "balanceDirection", slot: "balanceDirection" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "150", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const form = reactive({ |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | balanceDirection: "debit", |
| | | status: "active", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | subjectCode: [{ required: true, message: "请è¾å
¥ç§ç®ç¼ç ", trigger: "blur" }], |
| | | subjectName: [{ required: true, message: "请è¾å
¥ç§ç®åç§°", trigger: "blur" }], |
| | | subjectType: [{ required: true, message: "è¯·éæ©ç§ç®ç±»å", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, subjectCode: "1001", subjectName: "åºåç°é", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" }, |
| | | { id: 2, subjectCode: "1002", subjectName: "é¶è¡å款", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" }, |
| | | { id: 3, subjectCode: "1122", subjectName: "åºæ¶è´¦æ¬¾", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" }, |
| | | { id: 4, subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", subjectType: "liability", balanceDirection: "credit", status: "active", remark: "" }, |
| | | { id: 5, subjectCode: "4001", subjectName: "宿¶èµæ¬", subjectType: "equity", balanceDirection: "credit", status: "active", remark: "" }, |
| | | { id: 6, subjectCode: "5001", subjectName: "çäº§ææ¬", subjectType: "cost", balanceDirection: "debit", status: "active", remark: "" }, |
| | | { id: 7, subjectCode: "6001", subjectName: "主è¥ä¸å¡æ¶å
¥", subjectType: "profit_loss", balanceDirection: "credit", status: "active", remark: "" }, |
| | | { id: 8, subjectCode: "6401", subjectName: "主è¥ä¸å¡ææ¬", subjectType: "profit_loss", balanceDirection: "debit", status: "active", remark: "" }, |
| | | ]; |
| | | |
| | | const getSubjectTypeLabel = (type) => { |
| | | const map = { |
| | | asset: "èµäº§ç±»", |
| | | liability: "è´åºç±»", |
| | | equity: "æçç±»", |
| | | cost: "ææ¬ç±»", |
| | | profit_loss: "æçç±»", |
| | | }; |
| | | return map[type] || type; |
| | | }; |
| | | |
| | | const getSubjectTypeType = (type) => { |
| | | const map = { |
| | | asset: "success", |
| | | liability: "danger", |
| | | equity: "warning", |
| | | cost: "info", |
| | | profit_loss: "primary", |
| | | }; |
| | | return map[type] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.subjectCode) { |
| | | result = result.filter(item => item.subjectCode.includes(filters.subjectCode)); |
| | | } |
| | | if (filters.subjectName) { |
| | | result = result.filter(item => item.subjectName.includes(filters.subjectName)); |
| | | } |
| | | if (filters.subjectType) { |
| | | result = result.filter(item => item.subjectType === filters.subjectType); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subjectCode = ""; |
| | | filters.subjectName = ""; |
| | | filters.subjectType = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ç§ç®"; |
| | | Object.assign(form, { |
| | | subjectCode: "", |
| | | subjectName: "", |
| | | subjectType: "", |
| | | balanceDirection: "debit", |
| | | status: "active", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾ç§ç®"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥ç§ç®åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å票代ç :"> |
| | | <el-input v-model="filters.invoiceCode" placeholder="请è¾å
¥å票代ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="å票å·ç :"> |
| | | <el-input v-model="filters.invoiceNo" placeholder="请è¾å
¥å票å·ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="认è¯ç¶æ:"> |
| | | <el-select v-model="filters.certifyStatus" placeholder="è¯·éæ©è®¤è¯ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="æªè®¤è¯" value="uncertified" /> |
| | | <el-option label="已认è¯" value="certified" /> |
| | | <el-option label="认è¯å¤±è´¥" value="failed" /> |
| | | </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-button type="success" @click="handleBatchCertify" icon="Check" :disabled="selectedRows.length === 0">æ¹é认è¯</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">å½å
¥å票</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 #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #taxAmount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.taxAmount) }}</span> |
| | | </template> |
| | | <template #totalAmount="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span> |
| | | </template> |
| | | <template #certifyStatus="{ row }"> |
| | | <el-tag :type="getCertifyStatusType(row.certifyStatus)">{{ getCertifyStatusLabel(row.certifyStatus) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleCertify(row)" v-if="row.certifyStatus === 'uncertified'">认è¯</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票代ç " prop="invoiceCode"> |
| | | <el-input v-model="form.invoiceCode" placeholder="请è¾å
¥å票代ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ç " prop="invoiceNo"> |
| | | <el-input v-model="form.invoiceNo" placeholder="请è¾å
¥å票å·ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼ç¥¨æ¥æ" prop="invoiceDate"> |
| | | <el-date-picker v-model="form.invoiceDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éé¢(ä¸å«ç¨)" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨ç" prop="taxRate"> |
| | | <el-select v-model="form.taxRate" placeholder="è¯·éæ©ç¨ç" style="width: 100%;" @change="calculateTax"> |
| | | <el-option label="0%" :value="0" /> |
| | | <el-option label="3%" :value="3" /> |
| | | <el-option label="6%" :value="6" /> |
| | | <el-option label="9%" :value="9" /> |
| | | <el-option label="13%" :value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨é¢"> |
| | | <el-input v-model="form.taxAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="认è¯ç¶æ" prop="certifyStatus"> |
| | | <el-select v-model="form.certifyStatus" placeholder="è¯·éæ©è®¤è¯ç¶æ" style="width: 100%;" disabled> |
| | | <el-option label="æªè®¤è¯" value="uncertified" /> |
| | | <el-option label="已认è¯" value="certified" /> |
| | | <el-option label="认è¯å¤±è´¥" value="failed" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è®¤è¯æ¥æ" prop="certifyDate"> |
| | | <el-date-picker v-model="form.certifyDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å票å
容" prop="content"> |
| | | <el-input v-model="form.content" 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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "è¿é¡¹å票", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | certifyStatus: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å票代ç ", prop: "invoiceCode", width: "130" }, |
| | | { label: "å票å·ç ", prop: "invoiceNo", width: "120" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "å¼ç¥¨æ¥æ", prop: "invoiceDate", width: "120" }, |
| | | { label: "éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¨é¢", prop: "taxAmount", slot: "taxAmount" }, |
| | | { label: "ä»·ç¨å计", prop: "totalAmount", slot: "totalAmount" }, |
| | | { label: "认è¯ç¶æ", prop: "certifyStatus", slot: "certifyStatus" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const selectedRows = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | invoiceDate: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | certifyStatus: "uncertified", |
| | | certifyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | invoiceCode: [{ required: true, message: "请è¾å
¥å票代ç ", trigger: "blur" }], |
| | | invoiceNo: [{ required: true, message: "请è¾å
¥å票å·ç ", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | invoiceDate: [{ required: true, message: "è¯·éæ©å¼ç¥¨æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©ç¨ç", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", supplierId: 1, supplierName: "åäº¬åææä¾åºå", invoiceDate: "2024-01-08", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, certifyStatus: "certified", certifyDate: "2024-01-15", content: "åææéè´", remark: "" }, |
| | | { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", invoiceDate: "2024-01-10", amount: 12000, taxRate: 13, taxAmount: 1560, totalAmount: 13560, certifyStatus: "uncertified", certifyDate: "", content: "çµåå
å¨ä»¶", remark: "" }, |
| | | { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", supplierId: 3, supplierName: "广å·å
è£
ææå", invoiceDate: "2024-01-12", amount: 3500, taxRate: 13, taxAmount: 455, totalAmount: 3955, certifyStatus: "certified", certifyDate: "2024-01-18", content: "å
è£
ææ", remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const calculateTax = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | }; |
| | | |
| | | const getCertifyStatusLabel = (status) => { |
| | | const map = { uncertified: "æªè®¤è¯", certified: "已认è¯", failed: "认è¯å¤±è´¥" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getCertifyStatusType = (status) => { |
| | | const map = { uncertified: "info", certified: "success", failed: "danger" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.invoiceCode) { |
| | | result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode)); |
| | | } |
| | | if (filters.invoiceNo) { |
| | | result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.certifyStatus) { |
| | | result = result.filter(item => item.certifyStatus === filters.certifyStatus); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.invoiceCode = ""; |
| | | filters.invoiceNo = ""; |
| | | filters.supplierId = ""; |
| | | filters.certifyStatus = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "å½å
¥å票"; |
| | | Object.assign(form, { |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | supplierId: "", |
| | | invoiceDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | taxRate: 13, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | certifyStatus: "uncertified", |
| | | certifyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å票"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çå票: ${row.invoiceCode}-${row.invoiceNo}`); |
| | | }; |
| | | |
| | | const handleCertify = (row) => { |
| | | ElMessageBox.confirm("确认认è¯è¯¥å票åï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].certifyStatus = "certified"; |
| | | mockData[index].certifyDate = new Date().toISOString().split('T')[0]; |
| | | } |
| | | ElMessage.success("è®¤è¯æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleBatchCertify = () => { |
| | | ElMessageBox.confirm(`确认æ¹é认è¯éä¸ç ${selectedRows.value.length} å¼ å票åï¼`, "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | selectedRows.value.forEach(row => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1 && mockData[index].certifyStatus === "uncertified") { |
| | | mockData[index].certifyStatus = "certified"; |
| | | mockData[index].certifyDate = new Date().toISOString().split('T')[0]; |
| | | } |
| | | }); |
| | | ElMessage.success("æ¹éè®¤è¯æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name }); |
| | | ElMessage.success("å½å
¥æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="仿¬¾åå·:"> |
| | | <el-input v-model="filters.paymentCode" placeholder="请è¾å
¥ä»æ¬¾åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="仿¬¾æ¹å¼:"> |
| | | <el-select v-model="filters.paymentMethod" placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" clearable style="width: 150px;"> |
| | | <el-option label="é¶è¡è½¬è´¦" value="bank_transfer" /> |
| | | <el-option label="ç°é" value="cash" /> |
| | | <el-option label="æ¯ç¥¨" value="check" /> |
| | | <el-option label="æ±ç¥¨" value="draft" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="å¾
仿¬¾" value="pending" /> |
| | | <el-option label="已宿" value="completed" /> |
| | | <el-option label="已忶" value="cancelled" /> |
| | | </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="totalPaymentAmount" precision="2" prefix="Â¥" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢ä»æ¬¾</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #paymentMethod="{ row }"> |
| | | <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="row.status === 'completed' ? 'success' : row.status === 'pending' ? 'warning' : 'info'"> |
| | | {{ row.status === 'completed' ? '已宿' : row.status === 'pending' ? 'å¾
仿¬¾' : '已忶' }} |
| | | </el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleComplete(row)" v-if="row.status === 'pending'">宿</el-button> |
| | | <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'pending'">åæ¶</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾åå·" prop="paymentCode"> |
| | | <el-input v-model="form.paymentCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
³èç³è¯·å" prop="applyCode"> |
| | | <el-select v-model="form.applyCode" placeholder="è¯·éæ©å
³èç³è¯·å" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in applyList" :key="item.applyCode" :label="item.applyCode" :value="item.applyCode" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¥æ" prop="paymentDate"> |
| | | <el-date-picker v-model="form.paymentDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼" prop="paymentMethod"> |
| | | <el-select v-model="form.paymentMethod" placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" style="width: 100%;"> |
| | | <el-option label="é¶è¡è½¬è´¦" value="bank_transfer" /> |
| | | <el-option label="ç°é" value="cash" /> |
| | | <el-option label="æ¯ç¥¨" value="check" /> |
| | | <el-option label="æ±ç¥¨" value="draft" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¶è¡è´¦å·" prop="bankAccount" v-if="form.paymentMethod === 'bank_transfer'"> |
| | | <el-input v-model="form.bankAccount" placeholder="请è¾å
¥é¶è¡è´¦å·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="弿·è¡" prop="bankName" v-if="form.paymentMethod === 'bank_transfer'"> |
| | | <el-input v-model="form.bankName" placeholder="请è¾å
¥å¼æ·è¡" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "仿¬¾å", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | paymentCode: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "仿¬¾åå·", prop: "paymentCode", width: "150" }, |
| | | { label: "å
³èç³è¯·å", prop: "applyCode", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "仿¬¾æ¥æ", prop: "paymentDate", width: "120" }, |
| | | { label: "仿¬¾éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "仿¬¾æ¹å¼", prop: "paymentMethod", slot: "paymentMethod" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "220", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const applyList = [ |
| | | { applyCode: "FK2024001", supplierId: 1, amount: 5000 }, |
| | | { applyCode: "FK2024002", supplierId: 2, amount: 8000 }, |
| | | { applyCode: "FK2024003", supplierId: 3, amount: 3000 }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | paymentCode: "", |
| | | applyCode: "", |
| | | supplierId: "", |
| | | paymentDate: "", |
| | | amount: 0, |
| | | paymentMethod: "bank_transfer", |
| | | bankAccount: "", |
| | | bankName: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | applyCode: [{ required: true, message: "è¯·éæ©å
³èç³è¯·å", trigger: "change" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | paymentDate: [{ required: true, message: "è¯·éæ©ä»æ¬¾æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥ä»æ¬¾éé¢", trigger: "blur" }], |
| | | paymentMethod: [{ required: true, message: "è¯·éæ©ä»æ¬¾æ¹å¼", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, paymentCode: "FKD2024001", applyCode: "FK2024001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", paymentDate: "2024-01-15", amount: 5000, paymentMethod: "bank_transfer", status: "completed", bankAccount: "6222021234567890123", bankName: "å·¥åé¶è¡", remark: "" }, |
| | | { id: 2, paymentCode: "FKD2024002", applyCode: "FK2024002", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", paymentDate: "2024-01-18", amount: 8000, paymentMethod: "bank_transfer", status: "pending", bankAccount: "6222029876543210987", bankName: "建设é¶è¡", remark: "" }, |
| | | { id: 3, paymentCode: "FKD2024003", applyCode: "FK2024003", supplierId: 3, supplierName: "广å·å
è£
ææå", paymentDate: "2024-01-20", amount: 3000, paymentMethod: "cash", status: "completed", remark: "" }, |
| | | ]; |
| | | |
| | | const totalPaymentAmount = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0); |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getPaymentMethodLabel = (method) => { |
| | | const map = { |
| | | bank_transfer: "é¶è¡è½¬è´¦", |
| | | cash: "ç°é", |
| | | check: "æ¯ç¥¨", |
| | | draft: "æ±ç¥¨", |
| | | }; |
| | | return map[method] || method; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.paymentCode) { |
| | | result = result.filter(item => item.paymentCode.includes(filters.paymentCode)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.paymentMethod) { |
| | | result = result.filter(item => item.paymentMethod === filters.paymentMethod); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.paymentCode = ""; |
| | | filters.supplierId = ""; |
| | | filters.paymentMethod = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ä»æ¬¾"; |
| | | Object.assign(form, { |
| | | paymentCode: "FKD" + Date.now().toString().slice(-8), |
| | | applyCode: "", |
| | | supplierId: "", |
| | | paymentDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | paymentMethod: "bank_transfer", |
| | | bankAccount: "", |
| | | bankName: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾ä»æ¬¾"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥ç仿¬¾å: ${row.paymentCode}`); |
| | | }; |
| | | |
| | | const handleComplete = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤è¯¥ä»æ¬¾å已宿åï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "completed"; |
| | | } |
| | | ElMessage.success("仿¬¾å®æ"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleCancel = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤åæ¶è¯¥ä»æ¬¾ååï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "cancelled"; |
| | | } |
| | | ElMessage.success("已忶"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ç³è¯·åå·:"> |
| | | <el-input v-model="filters.applyCode" placeholder="请è¾å
¥ç³è¯·åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="å¾
审æ¹" value="pending" /> |
| | | <el-option label="已审æ¹" value="approved" /> |
| | | <el-option label="已驳å" value="rejected" /> |
| | | <el-option label="已仿¬¾" value="paid" /> |
| | | </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="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">æ¹éç³è¯·</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 #amount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #paymentMethod="{ row }"> |
| | | <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</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 type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审æ¹</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·åå·" prop="applyCode"> |
| | | <el-input v-model="form.applyCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼" prop="paymentMethod"> |
| | | <el-select v-model="form.paymentMethod" placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" style="width: 100%;"> |
| | | <el-option label="é¶è¡è½¬è´¦" value="bank_transfer" /> |
| | | <el-option label="ç°é" value="cash" /> |
| | | <el-option label="æ¯ç¥¨" value="check" /> |
| | | <el-option label="æ±ç¥¨" value="draft" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·æ¥æ" prop="applyDate"> |
| | | <el-date-picker v-model="form.applyDate" 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="expectedDate"> |
| | | <el-date-picker v-model="form.expectedDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å
³èå
¥åºå" prop="relatedDocs"> |
| | | <el-select v-model="form.relatedDocs" multiple placeholder="è¯·éæ©å
³èå
¥åºå" style="width: 100%;"> |
| | | <el-option v-for="item in inList" :key="item.inCode" :label="item.inCode" :value="item.inCode" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="仿¬¾äºç±" prop="reason"> |
| | | <el-input v-model="form.reason" 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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "仿¬¾ç³è¯·", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | applyCode: "", |
| | | supplierId: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "ç³è¯·åå·", prop: "applyCode", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "仿¬¾éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "仿¬¾æ¹å¼", prop: "paymentMethod", slot: "paymentMethod" }, |
| | | { label: "ç³è¯·æ¥æ", prop: "applyDate", width: "120" }, |
| | | { label: "ææä»æ¬¾æ¥", prop: "expectedDate", width: "120" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const selectedRows = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const inList = [ |
| | | { inCode: "RK2024001", supplierId: 1 }, |
| | | { inCode: "RK2024002", supplierId: 2 }, |
| | | { inCode: "RK2024003", supplierId: 3 }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | applyCode: "", |
| | | supplierId: "", |
| | | amount: 0, |
| | | paymentMethod: "bank_transfer", |
| | | applyDate: "", |
| | | expectedDate: "", |
| | | relatedDocs: [], |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥ä»æ¬¾éé¢", trigger: "blur" }], |
| | | paymentMethod: [{ required: true, message: "è¯·éæ©ä»æ¬¾æ¹å¼", trigger: "change" }], |
| | | applyDate: [{ required: true, message: "è¯·éæ©ç³è¯·æ¥æ", trigger: "change" }], |
| | | expectedDate: [{ required: true, message: "è¯·éæ©ææä»æ¬¾æ¥æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, applyCode: "FK2024001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", amount: 5000, paymentMethod: "bank_transfer", applyDate: "2024-01-12", expectedDate: "2024-01-15", status: "pending", relatedDocs: ["RK2024001"], reason: "æ¯ä»åææè´§æ¬¾", remark: "" }, |
| | | { id: 2, applyCode: "FK2024002", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", amount: 8000, paymentMethod: "bank_transfer", applyDate: "2024-01-14", expectedDate: "2024-01-18", status: "approved", relatedDocs: ["RK2024002"], reason: "æ¯ä»çµåå
å¨ä»¶è´§æ¬¾", remark: "" }, |
| | | { id: 3, applyCode: "FK2024003", supplierId: 3, supplierName: "广å·å
è£
ææå", amount: 3000, paymentMethod: "cash", applyDate: "2024-01-16", expectedDate: "2024-01-20", status: "paid", relatedDocs: ["RK2024003"], reason: "æ¯ä»å
è£
ææè´§æ¬¾", remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getPaymentMethodLabel = (method) => { |
| | | const map = { |
| | | bank_transfer: "é¶è¡è½¬è´¦", |
| | | cash: "ç°é", |
| | | check: "æ¯ç¥¨", |
| | | draft: "æ±ç¥¨", |
| | | }; |
| | | return map[method] || method; |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
审æ¹", approved: "已审æ¹", rejected: "已驳å", paid: "已仿¬¾" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger", paid: "primary" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.applyCode) { |
| | | result = result.filter(item => item.applyCode.includes(filters.applyCode)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.applyCode = ""; |
| | | filters.supplierId = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢ä»æ¬¾ç³è¯·"; |
| | | Object.assign(form, { |
| | | applyCode: "FK" + Date.now().toString().slice(-8), |
| | | supplierId: "", |
| | | amount: 0, |
| | | paymentMethod: "bank_transfer", |
| | | applyDate: new Date().toISOString().split('T')[0], |
| | | expectedDate: "", |
| | | relatedDocs: [], |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾ä»æ¬¾ç³è¯·"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çç³è¯·å: ${row.applyCode}`); |
| | | }; |
| | | |
| | | const handleAudit = (row) => { |
| | | ElMessageBox.confirm("确认审æ¹éè¿è¯¥ä»æ¬¾ç³è¯·åï¼", "æç¤º", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | distinguishCancelAndClose: true, |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "approved"; |
| | | } |
| | | ElMessage.success("审æ¹éè¿"); |
| | | getTableData(); |
| | | }).catch((action) => { |
| | | if (action === "cancel") { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "rejected"; |
| | | } |
| | | ElMessage.warning("已驳å"); |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleBatchApply = () => { |
| | | ElMessage.success(`æ¹éç³è¯· ${selectedRows.value.length} æ¡è®°å½`); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å
¥åºåå·:"> |
| | | <el-input v-model="filters.inCode" placeholder="请è¾å
¥å
¥åºåå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å
¥åºæ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | </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> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </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 type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºåå·" prop="inCode"> |
| | | <el-input v-model="form.inCode" placeholder="请è¾å
¥å
¥åºåå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select v-model="form.supplierId" placeholder="è¯·éæ©ä¾åºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºæ¥æ" prop="inDate"> |
| | | <el-date-picker v-model="form.inDate" 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="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å
¥åºæç»" prop="details"> |
| | | <el-table :data="form.details" border style="width: 100%"> |
| | | <el-table-column prop="materialName" label="ç©æåç§°" width="150"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.details[$index].materialName" placeholder="ç©æåç§°" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="spec" label="è§æ ¼" width="120"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.details[$index].spec" placeholder="è§æ ¼" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="quantity" label="æ°é" width="100"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.details[$index].quantity" :min="0" style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unitPrice" label="åä»·" width="120"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.details[$index].unitPrice" :min="0" :precision="2" style="width: 100%;" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="total" label="éé¢" width="120"> |
| | | <template #default="{ row }"> |
| | | <span>Â¥{{ formatMoney(row.quantity * row.unitPrice) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ $index }"> |
| | | <el-button type="danger" link @click="removeDetail($index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-button type="primary" link @click="addDetail" style="margin-top: 10px;">+ æ·»å æç»</el-button> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "éè´å
¥åº", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | inCode: "", |
| | | supplierId: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å
¥åºåå·", prop: "inCode", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "å
¥åºæ¥æ", prop: "inDate", width: "120" }, |
| | | { label: "å
¥åºéé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | inCode: "", |
| | | supplierId: "", |
| | | inDate: "", |
| | | amount: 0, |
| | | details: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | inCode: [{ required: true, message: "请è¾å
¥å
¥åºåå·", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | inDate: [{ required: true, message: "è¯·éæ©å
¥åºæ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥å
¥åºéé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, inCode: "RK2024001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", inDate: "2024-01-10", amount: 8000, status: "approved", details: [{ materialName: "颿", spec: "Q235", quantity: 10, unitPrice: 500 }], remark: "" }, |
| | | { id: 2, inCode: "RK2024002", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", inDate: "2024-01-12", amount: 12000, status: "pending", details: [{ materialName: "è¯ç", spec: "STM32", quantity: 100, unitPrice: 80 }], remark: "" }, |
| | | { id: 3, inCode: "RK2024003", supplierId: 3, supplierName: "广å·å
è£
ææå", inDate: "2024-01-15", amount: 3500, status: "approved", details: [{ materialName: "纸箱", spec: "50*40*30", quantity: 500, unitPrice: 5 }], remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.inCode) { |
| | | result = result.filter(item => item.inCode.includes(filters.inCode)); |
| | | } |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.inDate >= filters.dateRange[0] && item.inDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.inCode = ""; |
| | | filters.supplierId = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const addDetail = () => { |
| | | form.details.push({ materialName: "", spec: "", quantity: 0, unitPrice: 0 }); |
| | | }; |
| | | |
| | | const removeDetail = (index) => { |
| | | form.details.splice(index, 1); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢å
¥åº"; |
| | | Object.assign(form, { |
| | | inCode: "RK" + Date.now().toString().slice(-8), |
| | | supplierId: "", |
| | | inDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | details: [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }], |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å
¥åº"; |
| | | Object.assign(form, row); |
| | | if (!form.details || form.details.length === 0) { |
| | | form.details = [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }]; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çå
¥åºå: ${row.inCode}`); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥å
¥åºååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const supplier = supplierList.find(item => item.id === form.supplierId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="对账æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </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-button type="primary" @click="generateStatement" icon="Document">çæå¯¹è´¦å</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut" icon="Download">导åºå¯¹è´¦å</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #beginBalance="{ row }"> |
| | | <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span> |
| | | </template> |
| | | <template #currentPayable="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.currentPayable) }}</span> |
| | | </template> |
| | | <template #currentPayment="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.currentPayment) }}</span> |
| | | </template> |
| | | <template #endBalance="{ row }"> |
| | | <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="viewDetail(row)">æ¥çæç»</el-button> |
| | | <el-button type="primary" link @click="printStatement(row)">æå°</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog title="对账æç»" v-model="detailDialogVisible" width="900px" append-to-body> |
| | | <div class="statement-header"> |
| | | <h3>{{ currentSupplier }} åºä»å¯¹è´¦å</h3> |
| | | <p>对账æé´: {{ currentPeriod }}</p> |
| | | </div> |
| | | <el-table :data="detailData" border style="width: 100%"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="type" label="ç±»å" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.type === 'å
¥åº' ? 'success' : row.type === 'éè´§' ? 'danger' : 'primary'">{{ row.type }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="code" label="åæ®ç¼å·" width="150" /> |
| | | <el-table-column prop="debit" label="åæ¹(仿¬¾)" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-success">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="credit" label="è´·æ¹(åºä»)" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-danger">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="balance" label="ä½é¢" width="120"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.balance) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" show-overflow-tooltip /> |
| | | </el-table> |
| | | <template #footer> |
| | | <el-button @click="detailDialogVisible = false">å
³é</el-button> |
| | | <el-button type="primary" @click="printDetail">æå°</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "åºä»å¯¹è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | supplierId: "", |
| | | startMonth: "", |
| | | endMonth: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "对账åå·", prop: "statementCode", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "对账æé´", prop: "period", width: "150" }, |
| | | { label: "æåä½é¢", prop: "beginBalance", slot: "beginBalance" }, |
| | | { label: "æ¬æåºä»", prop: "currentPayable", slot: "currentPayable" }, |
| | | { label: "æ¬æä»æ¬¾", prop: "currentPayment", slot: "currentPayment" }, |
| | | { label: "ææ«ä½é¢", prop: "endBalance", slot: "endBalance" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "150", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const detailDialogVisible = ref(false); |
| | | const currentSupplier = ref(""); |
| | | const currentPeriod = ref(""); |
| | | const detailData = ref([]); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "åäº¬åææä¾åºå" }, |
| | | { id: 2, name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { id: 3, name: "广å·å
è£
ææå" }, |
| | | { id: 4, name: "æ·±å³äºéé
ä»¶å
¬å¸" }, |
| | | ]; |
| | | |
| | | const mockData = [ |
| | | { id: 1, statementCode: "DZ202401001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", period: "2024-01", beginBalance: 20000, currentPayable: 15000, currentPayment: 10000, endBalance: 25000 }, |
| | | { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "䏿µ·çµåå
å¨ä»¶å
¬å¸", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 }, |
| | | { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "åäº¬åææä¾åºå", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | if (filters.startMonth && filters.endMonth) { |
| | | result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.supplierId = ""; |
| | | filters.startMonth = ""; |
| | | filters.endMonth = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const generateStatement = () => { |
| | | ElMessage.success("对账åçææå"); |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | const supplier = supplierList[Math.floor(Math.random() * supplierList.length)]; |
| | | mockData.unshift({ |
| | | id: newId, |
| | | statementCode: "DZ" + Date.now(), |
| | | supplierId: supplier.id, |
| | | supplierName: supplier.name, |
| | | period: "2024-03", |
| | | beginBalance: Math.floor(Math.random() * 20000), |
| | | currentPayable: Math.floor(Math.random() * 25000), |
| | | currentPayment: Math.floor(Math.random() * 20000), |
| | | endBalance: Math.floor(Math.random() * 25000), |
| | | }); |
| | | getTableData(); |
| | | }; |
| | | |
| | | const viewDetail = (row) => { |
| | | currentSupplier.value = row.supplierName; |
| | | currentPeriod.value = row.period; |
| | | detailData.value = [ |
| | | { date: row.period + "-01", type: "æå", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "æåä½é¢" }, |
| | | { date: row.period + "-05", type: "å
¥åº", code: "RK2024001", debit: 0, credit: 8000, balance: row.beginBalance + 8000, remark: "" }, |
| | | { date: row.period + "-10", type: "仿¬¾", code: "FK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 3000, remark: "" }, |
| | | { date: row.period + "-15", type: "å
¥åº", code: "RK2024002", credit: 12000, balance: row.beginBalance + 15000, remark: "" }, |
| | | { date: row.period + "-20", type: "éè´§", code: "TH2024001", debit: 2000, credit: 0, balance: row.beginBalance + 13000, remark: "" }, |
| | | { date: row.period + "-25", type: "仿¬¾", code: "FK2024002", debit: row.currentPayment - 5000, balance: row.endBalance, remark: "" }, |
| | | ]; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | const printStatement = (row) => { |
| | | ElMessage.info(`æå°å¯¹è´¦å: ${row.statementCode}`); |
| | | }; |
| | | |
| | | const printDetail = () => { |
| | | ElMessage.info("æå°æç»"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .statement-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h3 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #909399; |
| | | margin: 0; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ç³è¯·åå·:"> |
| | | <el-input v-model="filters.applyCode" placeholder="请è¾å
¥ç³è¯·åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="å¾
å®¡æ ¸" value="pending" /> |
| | | <el-option label="å·²å®¡æ ¸" value="approved" /> |
| | | <el-option label="已驳å" value="rejected" /> |
| | | <el-option label="å·²å¼ç¥¨" value="invoiced" /> |
| | | </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="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">æ¹éç³è¯·</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 #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #taxRate="{ row }"> |
| | | <span>{{ row.taxRate }}%</span> |
| | | </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 type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">å®¡æ ¸</el-button> |
| | | <el-button type="warning" link @click="handleInvoice(row)" v-if="row.status === 'approved'">å¼ç¥¨</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·åå·" prop="applyCode"> |
| | | <el-input v-model="form.applyCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼ç¥¨éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç" prop="taxRate"> |
| | | <el-select v-model="form.taxRate" placeholder="è¯·éæ©ç¨ç" style="width: 100%;"> |
| | | <el-option label="0%" :value="0" /> |
| | | <el-option label="3%" :value="3" /> |
| | | <el-option label="6%" :value="6" /> |
| | | <el-option label="9%" :value="9" /> |
| | | <el-option label="13%" :value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类å" prop="invoiceType"> |
| | | <el-select v-model="form.invoiceType" placeholder="è¯·éæ©å票类å" style="width: 100%;"> |
| | | <el-option label="å¢å¼ç¨ä¸ç¨å票" value="special" /> |
| | | <el-option label="å¢å¼ç¨æ®éå票" value="normal" /> |
| | | <el-option label="çµåå票" value="electronic" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·æ¥æ" prop="applyDate"> |
| | | <el-date-picker v-model="form.applyDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å票å
容" prop="content"> |
| | | <el-input v-model="form.content" 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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "å¼ç¥¨ç³è¯·", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | applyCode: "", |
| | | customerId: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "ç³è¯·åå·", prop: "applyCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "å¼ç¥¨éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¨ç", prop: "taxRate", slot: "taxRate" }, |
| | | { label: "å票类å", prop: "invoiceTypeLabel", width: "130" }, |
| | | { label: "ç³è¯·æ¥æ", prop: "applyDate", width: "120" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const selectedRows = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | applyCode: "", |
| | | customerId: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | | invoiceType: "special", |
| | | applyDate: "", |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥å¼ç¥¨éé¢", trigger: "blur" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©ç¨ç", trigger: "change" }], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©å票类å", trigger: "change" }], |
| | | applyDate: [{ required: true, message: "è¯·éæ©ç³è¯·æ¥æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, applyCode: "KP2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", amount: 5000, taxRate: 13, invoiceType: "special", invoiceTypeLabel: "å¢å¼ç¨ä¸ç¨å票", applyDate: "2024-01-15", status: "pending", content: "软件æå¡è´¹", remark: "" }, |
| | | { id: 2, applyCode: "KP2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", amount: 8000, taxRate: 13, invoiceType: "normal", invoiceTypeLabel: "å¢å¼ç¨æ®éå票", applyDate: "2024-01-16", status: "approved", content: "ååéå®", remark: "" }, |
| | | { id: 3, applyCode: "KP2024003", customerId: 3, customerName: "广å·å®ä¸æéå
¬å¸", amount: 12000, taxRate: 6, invoiceType: "electronic", invoiceTypeLabel: "çµåå票", applyDate: "2024-01-18", status: "invoiced", content: "ææ¯æå¡è´¹", remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å", invoiced: "å·²å¼ç¥¨" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger", invoiced: "primary" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.applyCode) { |
| | | result = result.filter(item => item.applyCode.includes(filters.applyCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.applyCode = ""; |
| | | filters.customerId = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢å¼ç¥¨ç³è¯·"; |
| | | Object.assign(form, { |
| | | applyCode: "KP" + Date.now().toString().slice(-8), |
| | | customerId: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | | invoiceType: "special", |
| | | applyDate: new Date().toISOString().split('T')[0], |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å¼ç¥¨ç³è¯·"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çç³è¯·å: ${row.applyCode}`); |
| | | }; |
| | | |
| | | const handleAudit = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤å®¡æ ¸éè¿è¯¥å¼ç¥¨ç³è¯·åï¼", "æç¤º", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | distinguishCancelAndClose: true, |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "approved"; |
| | | } |
| | | ElMessage.success("å®¡æ ¸éè¿"); |
| | | getTableData(); |
| | | }).catch((action) => { |
| | | if (action === "cancel") { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "rejected"; |
| | | } |
| | | ElMessage.warning("已驳å"); |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleInvoice = (row) => { |
| | | ElMessageBox.confirm("确认已å¼å
·å票ï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "invoiced"; |
| | | } |
| | | ElMessage.success("å¼ç¥¨å®æ"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleBatchApply = () => { |
| | | ElMessage.success(`æ¹éç³è¯· ${selectedRows.value.length} æ¡è®°å½`); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | const invoiceTypeMap = { special: "å¢å¼ç¨ä¸ç¨å票", normal: "å¢å¼ç¨æ®éå票", electronic: "çµåå票" }; |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType], status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å票代ç :"> |
| | | <el-input v-model="filters.invoiceCode" placeholder="请è¾å
¥å票代ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="å票å·ç :"> |
| | | <el-input v-model="filters.invoiceNo" placeholder="请è¾å
¥å票å·ç " clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </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="handleImport" icon="Upload">导å
¥</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #taxAmount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.taxAmount) }}</span> |
| | | </template> |
| | | <template #totalAmount="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span> |
| | | </template> |
| | | <template #invoiceType="{ row }"> |
| | | <el-tag :type="row.invoiceType === 'special' ? 'danger' : 'primary'">{{ row.invoiceTypeLabel }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">ä½åº</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票代ç " prop="invoiceCode"> |
| | | <el-input v-model="form.invoiceCode" placeholder="请è¾å
¥å票代ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ç " prop="invoiceNo"> |
| | | <el-input v-model="form.invoiceNo" placeholder="请è¾å
¥å票å·ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¼ç¥¨æ¥æ" prop="invoiceDate"> |
| | | <el-date-picker v-model="form.invoiceDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类å" prop="invoiceType"> |
| | | <el-select v-model="form.invoiceType" placeholder="è¯·éæ©å票类å" style="width: 100%;" @change="handleInvoiceTypeChange"> |
| | | <el-option label="å¢å¼ç¨ä¸ç¨å票" value="special" /> |
| | | <el-option label="å¢å¼ç¨æ®éå票" value="normal" /> |
| | | <el-option label="çµåå票" value="electronic" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç" prop="taxRate"> |
| | | <el-select v-model="form.taxRate" placeholder="è¯·éæ©ç¨ç" style="width: 100%;" @change="calculateTax"> |
| | | <el-option label="0%" :value="0" /> |
| | | <el-option label="3%" :value="3" /> |
| | | <el-option label="6%" :value="6" /> |
| | | <el-option label="9%" :value="9" /> |
| | | <el-option label="13%" :value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éé¢(ä¸å«ç¨)" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¨é¢"> |
| | | <el-input v-model="form.taxAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ä»·ç¨å计"> |
| | | <el-input v-model="form.totalAmount" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å票å
容" prop="content"> |
| | | <el-input v-model="form.content" 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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "é项å票", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | customerId: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å票代ç ", prop: "invoiceCode", width: "130" }, |
| | | { label: "å票å·ç ", prop: "invoiceNo", width: "120" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "å¼ç¥¨æ¥æ", prop: "invoiceDate", width: "120" }, |
| | | { label: "éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "ç¨é¢", prop: "taxAmount", slot: "taxAmount" }, |
| | | { label: "ä»·ç¨å计", prop: "totalAmount", slot: "totalAmount" }, |
| | | { label: "å票类å", prop: "invoiceType", slot: "invoiceType" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "180", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | customerId: "", |
| | | invoiceDate: "", |
| | | invoiceType: "special", |
| | | taxRate: 13, |
| | | amount: 0, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | invoiceCode: [{ required: true, message: "请è¾å
¥å票代ç ", trigger: "blur" }], |
| | | invoiceNo: [{ required: true, message: "请è¾å
¥å票å·ç ", trigger: "blur" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | invoiceDate: [{ required: true, message: "è¯·éæ©å¼ç¥¨æ¥æ", trigger: "change" }], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©å票类å", trigger: "change" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©ç¨ç", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", invoiceDate: "2024-01-15", amount: 5000, taxRate: 13, taxAmount: 650, totalAmount: 5650, invoiceType: "special", invoiceTypeLabel: "å¢å¼ç¨ä¸ç¨å票", content: "软件æå¡è´¹", remark: "" }, |
| | | { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", invoiceDate: "2024-01-16", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, invoiceType: "normal", invoiceTypeLabel: "å¢å¼ç¨æ®éå票", content: "ååéå®", remark: "" }, |
| | | { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", customerId: 3, customerName: "广å·å®ä¸æéå
¬å¸", invoiceDate: "2024-01-18", amount: 12000, taxRate: 6, taxAmount: 720, totalAmount: 12720, invoiceType: "electronic", invoiceTypeLabel: "çµåå票", content: "ææ¯æå¡è´¹", remark: "" }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const calculateTax = () => { |
| | | form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2)); |
| | | form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2)); |
| | | }; |
| | | |
| | | const handleInvoiceTypeChange = () => { |
| | | if (form.invoiceType === "special") { |
| | | form.taxRate = 13; |
| | | } else { |
| | | form.taxRate = 13; |
| | | } |
| | | calculateTax(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.invoiceCode) { |
| | | result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode)); |
| | | } |
| | | if (filters.invoiceNo) { |
| | | result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.invoiceCode = ""; |
| | | filters.invoiceNo = ""; |
| | | filters.customerId = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "å½å
¥å票"; |
| | | Object.assign(form, { |
| | | invoiceCode: "", |
| | | invoiceNo: "", |
| | | customerId: "", |
| | | invoiceDate: new Date().toISOString().split('T')[0], |
| | | invoiceType: "special", |
| | | taxRate: 13, |
| | | amount: 0, |
| | | taxAmount: 0, |
| | | totalAmount: 0, |
| | | content: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾å票"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çå票: ${row.invoiceCode}-${row.invoiceNo}`); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认ä½åºè¯¥å票åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("ä½åºæå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleImport = () => { |
| | | ElMessage.info("导å
¥åè½"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | const invoiceTypeMap = { special: "å¢å¼ç¨ä¸ç¨å票", normal: "å¢å¼ç¨æ®éå票", electronic: "çµåå票" }; |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] }); |
| | | ElMessage.success("å½å
¥æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="æ¶æ¬¾åå·:"> |
| | | <el-input v-model="filters.receiptCode" placeholder="请è¾å
¥æ¶æ¬¾åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶æ¬¾æ¹å¼:"> |
| | | <el-select v-model="filters.receiptMethod" placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" clearable style="width: 150px;"> |
| | | <el-option label="é¶è¡è½¬è´¦" value="bank_transfer" /> |
| | | <el-option label="ç°é" value="cash" /> |
| | | <el-option label="æ¯ç¥¨" value="check" /> |
| | | <el-option label="æ±ç¥¨" value="draft" /> |
| | | <el-option label="æ¯ä»å®" value="alipay" /> |
| | | <el-option label="微信" value="wechat" /> |
| | | </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="totalReceiptAmount" precision="2" prefix="Â¥" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢æ¶æ¬¾</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #amount="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | | <template #receiptMethod="{ row }"> |
| | | <el-tag>{{ getReceiptMethodLabel(row.receiptMethod) }}</el-tag> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <el-tag :type="row.status === 'confirmed' ? 'success' : 'warning'">{{ row.status === 'confirmed' ? '已确认' : 'å¾
确认' }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleConfirm(row)" v-if="row.status === 'pending'">确认</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾åå·" prop="receiptCode"> |
| | | <el-input v-model="form.receiptCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾æ¥æ" prop="receiptDate"> |
| | | <el-date-picker v-model="form.receiptDate" 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="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾æ¹å¼" prop="receiptMethod"> |
| | | <el-select v-model="form.receiptMethod" placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" style="width: 100%;"> |
| | | <el-option label="é¶è¡è½¬è´¦" value="bank_transfer" /> |
| | | <el-option label="ç°é" value="cash" /> |
| | | <el-option label="æ¯ç¥¨" value="check" /> |
| | | <el-option label="æ±ç¥¨" value="draft" /> |
| | | <el-option label="æ¯ä»å®" value="alipay" /> |
| | | <el-option label="微信" value="wechat" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¶è¡è´¦å·" prop="bankAccount" v-if="form.receiptMethod === 'bank_transfer'"> |
| | | <el-input v-model="form.bankAccount" placeholder="请è¾å
¥é¶è¡è´¦å·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å
³èåæ®" prop="relatedDocs"> |
| | | <el-select v-model="form.relatedDocs" multiple placeholder="è¯·éæ©å
³èåæ®" style="width: 100%;"> |
| | | <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "æ¶æ¬¾å", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | receiptCode: "", |
| | | customerId: "", |
| | | receiptMethod: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "æ¶æ¬¾åå·", prop: "receiptCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "æ¶æ¬¾æ¥æ", prop: "receiptDate", width: "120" }, |
| | | { label: "æ¶æ¬¾éé¢", prop: "amount", slot: "amount" }, |
| | | { label: "æ¶æ¬¾æ¹å¼", prop: "receiptMethod", slot: "receiptMethod" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "220", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const outList = [ |
| | | { outCode: "CK2024001", customerId: 1 }, |
| | | { outCode: "CK2024002", customerId: 2 }, |
| | | { outCode: "CK2024003", customerId: 3 }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | receiptCode: "", |
| | | customerId: "", |
| | | receiptDate: "", |
| | | amount: 0, |
| | | receiptMethod: "bank_transfer", |
| | | bankAccount: "", |
| | | relatedDocs: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | receiptDate: [{ required: true, message: "è¯·éæ©æ¶æ¬¾æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥æ¶æ¬¾éé¢", trigger: "blur" }], |
| | | receiptMethod: [{ required: true, message: "è¯·éæ©æ¶æ¬¾æ¹å¼", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, receiptCode: "SK2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", receiptDate: "2024-01-16", amount: 3000, receiptMethod: "bank_transfer", status: "confirmed", relatedDocs: ["CK2024001"], remark: "" }, |
| | | { id: 2, receiptCode: "SK2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", receiptDate: "2024-01-18", amount: 5000, receiptMethod: "cash", status: "pending", relatedDocs: ["CK2024002"], remark: "" }, |
| | | { id: 3, receiptCode: "SK2024003", customerId: 3, customerName: "广å·å®ä¸æéå
¬å¸", receiptDate: "2024-01-20", amount: 8000, receiptMethod: "alipay", status: "confirmed", relatedDocs: ["CK2024003"], remark: "" }, |
| | | ]; |
| | | |
| | | const totalReceiptAmount = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0); |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getReceiptMethodLabel = (method) => { |
| | | const map = { |
| | | bank_transfer: "é¶è¡è½¬è´¦", |
| | | cash: "ç°é", |
| | | check: "æ¯ç¥¨", |
| | | draft: "æ±ç¥¨", |
| | | alipay: "æ¯ä»å®", |
| | | wechat: "微信", |
| | | }; |
| | | return map[method] || method; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.receiptCode) { |
| | | result = result.filter(item => item.receiptCode.includes(filters.receiptCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.receiptMethod) { |
| | | result = result.filter(item => item.receiptMethod === filters.receiptMethod); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.receiptCode = ""; |
| | | filters.customerId = ""; |
| | | filters.receiptMethod = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢æ¶æ¬¾"; |
| | | Object.assign(form, { |
| | | receiptCode: "SK" + Date.now().toString().slice(-8), |
| | | customerId: "", |
| | | receiptDate: new Date().toISOString().split('T')[0], |
| | | amount: 0, |
| | | receiptMethod: "bank_transfer", |
| | | bankAccount: "", |
| | | relatedDocs: [], |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ¶æ¬¾"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çæ¶æ¬¾å: ${row.receiptCode}`); |
| | | }; |
| | | |
| | | const handleConfirm = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤è¯¥æ¶æ¬¾ååï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "confirmed"; |
| | | } |
| | | ElMessage.success("确认æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥æ¶æ¬¾ååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="对账æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </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-button type="primary" @click="generateStatement" icon="Document">çæå¯¹è´¦å</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut" icon="Download">导åºå¯¹è´¦å</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #beginBalance="{ row }"> |
| | | <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span> |
| | | </template> |
| | | <template #currentReceivable="{ row }"> |
| | | <span class="text-primary">Â¥{{ formatMoney(row.currentReceivable) }}</span> |
| | | </template> |
| | | <template #currentReceipt="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.currentReceipt) }}</span> |
| | | </template> |
| | | <template #endBalance="{ row }"> |
| | | <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="viewDetail(row)">æ¥çæç»</el-button> |
| | | <el-button type="primary" link @click="printStatement(row)">æå°</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog title="对账æç»" v-model="detailDialogVisible" width="900px" append-to-body> |
| | | <div class="statement-header"> |
| | | <h3>{{ currentCustomer }} åºæ¶å¯¹è´¦å</h3> |
| | | <p>对账æé´: {{ currentPeriod }}</p> |
| | | </div> |
| | | <el-table :data="detailData" border style="width: 100%"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="type" label="ç±»å" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.type === 'åºåº' ? 'success' : row.type === 'éè´§' ? 'danger' : 'primary'">{{ row.type }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="code" label="åæ®ç¼å·" width="150" /> |
| | | <el-table-column prop="debit" label="åæ¹(åºæ¶)" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="credit" label="è´·æ¹(æ¶æ¬¾)" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="balance" label="ä½é¢" width="120"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.balance) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" show-overflow-tooltip /> |
| | | </el-table> |
| | | <template #footer> |
| | | <el-button @click="detailDialogVisible = false">å
³é</el-button> |
| | | <el-button type="primary" @click="printDetail">æå°</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "åºæ¶å¯¹è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | customerId: "", |
| | | startMonth: "", |
| | | endMonth: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "对账åå·", prop: "statementCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "对账æé´", prop: "period", width: "150" }, |
| | | { label: "æåä½é¢", prop: "beginBalance", slot: "beginBalance" }, |
| | | { label: "æ¬æåºæ¶", prop: "currentReceivable", slot: "currentReceivable" }, |
| | | { label: "æ¬ææ¶æ¬¾", prop: "currentReceipt", slot: "currentReceipt" }, |
| | | { label: "ææ«ä½é¢", prop: "endBalance", slot: "endBalance" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "150", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const detailDialogVisible = ref(false); |
| | | const currentCustomer = ref(""); |
| | | const currentPeriod = ref(""); |
| | | const detailData = ref([]); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const mockData = [ |
| | | { id: 1, statementCode: "DZ202401001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", period: "2024-01", beginBalance: 10000, currentReceivable: 15000, currentReceipt: 8000, endBalance: 17000 }, |
| | | { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 }, |
| | | { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 }, |
| | | ]; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.startMonth && filters.endMonth) { |
| | | result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.customerId = ""; |
| | | filters.startMonth = ""; |
| | | filters.endMonth = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const generateStatement = () => { |
| | | ElMessage.success("对账åçææå"); |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | const customer = customerList[Math.floor(Math.random() * customerList.length)]; |
| | | mockData.unshift({ |
| | | id: newId, |
| | | statementCode: "DZ" + Date.now(), |
| | | customerId: customer.id, |
| | | customerName: customer.name, |
| | | period: "2024-03", |
| | | beginBalance: Math.floor(Math.random() * 10000), |
| | | currentReceivable: Math.floor(Math.random() * 20000), |
| | | currentReceipt: Math.floor(Math.random() * 15000), |
| | | endBalance: Math.floor(Math.random() * 20000), |
| | | }); |
| | | getTableData(); |
| | | }; |
| | | |
| | | const viewDetail = (row) => { |
| | | currentCustomer.value = row.customerName; |
| | | currentPeriod.value = row.period; |
| | | detailData.value = [ |
| | | { date: row.period + "-01", type: "æå", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "æåä½é¢" }, |
| | | { date: row.period + "-05", type: "åºåº", code: "CK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 5000, remark: "" }, |
| | | { date: row.period + "-10", type: "æ¶æ¬¾", code: "SK2024001", debit: 0, credit: 3000, balance: row.beginBalance + 2000, remark: "" }, |
| | | { date: row.period + "-15", type: "åºåº", code: "CK2024002", debit: 8000, credit: 0, balance: row.beginBalance + 10000, remark: "" }, |
| | | { date: row.period + "-20", type: "éè´§", code: "TH2024001", debit: 0, credit: 2000, balance: row.beginBalance + 8000, remark: "" }, |
| | | { date: row.period + "-25", type: "æ¶æ¬¾", code: "SK2024002", credit: row.currentReceipt - 3000, balance: row.endBalance, remark: "" }, |
| | | ]; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | const printStatement = (row) => { |
| | | ElMessage.info(`æå°å¯¹è´¦å: ${row.statementCode}`); |
| | | }; |
| | | |
| | | const printDetail = () => { |
| | | ElMessage.info("æå°æç»"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | } |
| | | |
| | | .statement-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h3 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #909399; |
| | | margin: 0; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="åºåºåå·:"> |
| | | <el-input v-model="filters.outCode" placeholder="请è¾å
¥åºåºåå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åºåºæ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | </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> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <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 type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºåå·" prop="outCode"> |
| | | <el-input v-model="form.outCode" placeholder="请è¾å
¥åºåºåå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºæ¥æ" prop="outDate"> |
| | | <el-date-picker v-model="form.outDate" 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="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "éå®åºåº", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | outCode: "", |
| | | customerId: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "åºåºåå·", prop: "outCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "åºåºæ¥æ", prop: "outDate", width: "120" }, |
| | | { label: "éé¢", prop: "amount", width: "120" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | outCode: "", |
| | | customerId: "", |
| | | outDate: "", |
| | | amount: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | outCode: [{ required: true, message: "请è¾å
¥åºåºåå·", trigger: "blur" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | outDate: [{ required: true, message: "è¯·éæ©åºåºæ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, outCode: "CK2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", outDate: "2024-01-15", amount: 5000, status: "approved", remark: "" }, |
| | | { id: 2, outCode: "CK2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", outDate: "2024-01-16", amount: 8000, status: "pending", remark: "" }, |
| | | { id: 3, outCode: "CK2024003", customerId: 3, customerName: "广å·å®ä¸æéå
¬å¸", outDate: "2024-01-18", amount: 12000, status: "approved", remark: "" }, |
| | | { id: 4, outCode: "CK2024004", customerId: 4, customerName: "æ·±å³çµåå
¬å¸", outDate: "2024-01-20", amount: 3500, status: "pending", remark: "" }, |
| | | ]; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.outCode) { |
| | | result = result.filter(item => item.outCode.includes(filters.outCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.outDate >= filters.dateRange[0] && item.outDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.outCode = ""; |
| | | filters.customerId = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åºåº"; |
| | | Object.assign(form, { |
| | | outCode: "CK" + Date.now(), |
| | | customerId: "", |
| | | outDate: "", |
| | | amount: 0, |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åºåº"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çåºåºå: ${row.outCode}`); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥åºåºååï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="éè´§åå·:"> |
| | | <el-input v-model="filters.returnCode" placeholder="请è¾å
¥éè´§åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="éè´§æ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | </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> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <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 type="primary" link @click="edit(row)" v-if="row.status === 'pending'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">å®¡æ ¸</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§åå·" prop="returnCode"> |
| | | <el-input v-model="form.returnCode" placeholder="请è¾å
¥éè´§åå·" :disabled="isEdit" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
³èåºåºå" prop="outCode"> |
| | | <el-select v-model="form.outCode" placeholder="è¯·éæ©åºåºå" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©å®¢æ·" style="width: 100%;" :disabled="isEdit"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§æ¥æ" prop="returnDate"> |
| | | <el-date-picker v-model="form.returnDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§éé¢" prop="amount"> |
| | | <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§åå " prop="reason"> |
| | | <el-input v-model="form.reason" placeholder="请è¾å
¥éè´§åå " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "éå®éè´§", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | returnCode: "", |
| | | customerId: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "éè´§åå·", prop: "returnCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "å
³èåºåºå", prop: "outCode", width: "150" }, |
| | | { label: "éè´§æ¥æ", prop: "returnDate", width: "120" }, |
| | | { label: "éè´§éé¢", prop: "amount", width: "120" }, |
| | | { label: "éè´§åå ", prop: "reason", width: "150", showOverflowTooltip: true }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { id: 2, name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { id: 3, name: "广å·å®ä¸æéå
¬å¸" }, |
| | | { id: 4, name: "æ·±å³çµåå
¬å¸" }, |
| | | ]; |
| | | |
| | | const outList = [ |
| | | { outCode: "CK2024001", customerId: 1 }, |
| | | { outCode: "CK2024002", customerId: 2 }, |
| | | { outCode: "CK2024003", customerId: 3 }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | returnCode: "", |
| | | outCode: "", |
| | | customerId: "", |
| | | returnDate: "", |
| | | amount: 0, |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | returnCode: [{ required: true, message: "请è¾å
¥éè´§åå·", trigger: "blur" }], |
| | | outCode: [{ required: true, message: "è¯·éæ©å
³èåºåºå", trigger: "change" }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | returnDate: [{ required: true, message: "è¯·éæ©éè´§æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥éè´§éé¢", trigger: "blur" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, returnCode: "TH2024001", outCode: "CK2024001", customerId: 1, customerName: "åäº¬ç§ææéå
¬å¸", returnDate: "2024-01-20", amount: 1000, reason: "è´¨éé®é¢", status: "approved", remark: "" }, |
| | | { id: 2, returnCode: "TH2024002", outCode: "CK2024002", customerId: 2, customerName: "䏿µ·è´¸æå
¬å¸", returnDate: "2024-01-22", amount: 500, reason: "è§æ ¼ä¸ç¬¦", status: "pending", remark: "" }, |
| | | ]; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { pending: "å¾
å®¡æ ¸", approved: "å·²å®¡æ ¸", rejected: "已驳å" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { pending: "warning", approved: "success", rejected: "danger" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.returnCode) { |
| | | result = result.filter(item => item.returnCode.includes(filters.returnCode)); |
| | | } |
| | | if (filters.customerId) { |
| | | result = result.filter(item => item.customerId === filters.customerId); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.returnDate >= filters.dateRange[0] && item.returnDate <= filters.dateRange[1]); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.returnCode = ""; |
| | | filters.customerId = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢éè´§"; |
| | | Object.assign(form, { |
| | | returnCode: "TH" + Date.now(), |
| | | outCode: "", |
| | | customerId: "", |
| | | returnDate: "", |
| | | amount: 0, |
| | | reason: "", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾éè´§"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çéè´§å: ${row.returnCode}`); |
| | | }; |
| | | |
| | | const handleAudit = (row) => { |
| | | ElMessageBox.confirm("ç¡®è®¤å®¡æ ¸éè¿è¯¥éè´§ååï¼", "æç¤º", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | distinguishCancelAndClose: true, |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "approved"; |
| | | } |
| | | ElMessage.success("å®¡æ ¸éè¿"); |
| | | getTableData(); |
| | | }).catch((action) => { |
| | | if (action === "cancel") { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "rejected"; |
| | | } |
| | | ElMessage.warning("已驳å"); |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const customer = customerList.find(item => item.id === form.customerId); |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, customerName: customer?.name }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ä¼è®¡ç§ç®:"> |
| | | <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="è¯·éæ©ä¼è®¡ç§ç®" clearable style="width: 250px;" filterable /> |
| | | </el-form-item> |
| | | <el-form-item label="è¾
婿 ¸ç®:"> |
| | | <el-select v-model="filters.auxiliary" placeholder="è¯·éæ©è¾
婿 ¸ç®" clearable style="width: 180px;"> |
| | | <el-option label="客æ·" value="customer" /> |
| | | <el-option label="ä¾åºå" value="supplier" /> |
| | | <el-option label="é¨é¨" value="department" /> |
| | | <el-option label="åå·¥" value="employee" /> |
| | | <el-option label="项ç®" value="project" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ ¸ç®å¯¹è±¡:"> |
| | | <el-select v-model="filters.auxiliaryItem" placeholder="è¯·éæ©æ ¸ç®å¯¹è±¡" clearable style="width: 200px;" :disabled="!filters.auxiliary"> |
| | | <el-option v-for="item in auxiliaryItems" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="ledger-header" v-if="currentSubject"> |
| | | <h2>ç§ç®æç»è´¦</h2> |
| | | <p>ç§ç®: {{ currentSubject.code }} {{ currentSubject.name }}</p> |
| | | <p v-if="filters.auxiliary && filters.auxiliaryItem">è¾
婿 ¸ç®: {{ getAuxiliaryLabel() }}</p> |
| | | <p>æé´: {{ filters.startMonth }} è³ {{ filters.endMonth }}</p> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æç»è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | subject: [], |
| | | auxiliary: "", |
| | | auxiliaryItem: "", |
| | | startMonth: "2024-01", |
| | | endMonth: "2024-03", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | |
| | | const subjectOptions = [ |
| | | { |
| | | code: "1122", |
| | | name: "åºæ¶è´¦æ¬¾", |
| | | children: [ |
| | | { code: "112201", name: "åäº¬ç§ææéå
¬å¸" }, |
| | | { code: "112202", name: "䏿µ·è´¸æå
¬å¸" }, |
| | | { code: "112203", name: "广å·å®ä¸æéå
¬å¸" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "2202", |
| | | name: "åºä»è´¦æ¬¾", |
| | | children: [ |
| | | { code: "220201", name: "åäº¬åææä¾åºå" }, |
| | | { code: "220202", name: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { code: "220203", name: "广å·å
è£
ææå" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "6602", |
| | | name: "管çè´¹ç¨", |
| | | children: [ |
| | | { code: "660201", name: "åå
¬è´¹" }, |
| | | { code: "660202", name: "å·®æ
è´¹" }, |
| | | { code: "660203", name: "ä¸å¡æå¾
è´¹" }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | const auxiliaryItems = computed(() => { |
| | | const map = { |
| | | customer: [ |
| | | { value: "1", label: "åäº¬ç§ææéå
¬å¸" }, |
| | | { value: "2", label: "䏿µ·è´¸æå
¬å¸" }, |
| | | { value: "3", label: "广å·å®ä¸æéå
¬å¸" }, |
| | | ], |
| | | supplier: [ |
| | | { value: "1", label: "åäº¬åææä¾åºå" }, |
| | | { value: "2", label: "䏿µ·çµåå
å¨ä»¶å
¬å¸" }, |
| | | { value: "3", label: "广å·å
è£
ææå" }, |
| | | ], |
| | | department: [ |
| | | { value: "1", label: "è´¢å¡é¨" }, |
| | | { value: "2", label: "éå®é¨" }, |
| | | { value: "3", label: "éè´é¨" }, |
| | | ], |
| | | employee: [ |
| | | { value: "1", label: "å¼ ä¸" }, |
| | | { value: "2", label: "æå" }, |
| | | { value: "3", label: "çäº" }, |
| | | ], |
| | | project: [ |
| | | { value: "1", label: "项ç®A" }, |
| | | { value: "2", label: "项ç®B" }, |
| | | { value: "3", label: "项ç®C" }, |
| | | ], |
| | | }; |
| | | return map[filters.auxiliary] || []; |
| | | }); |
| | | |
| | | watch(() => filters.auxiliary, () => { |
| | | filters.auxiliaryItem = ""; |
| | | }); |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | }); |
| | | |
| | | const findSubject = (options, code) => { |
| | | for (const item of options) { |
| | | if (item.code === code) return item; |
| | | if (item.children && item.children.length > 0) { |
| | | const found = findSubject(item.children, code); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const getAuxiliaryLabel = () => { |
| | | const item = auxiliaryItems.value.find(i => i.value === filters.auxiliaryItem); |
| | | return item ? item.label : ""; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { date: "2024-01-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 10000 }, |
| | | { date: "2024-01-05", voucherNo: "è®°-0001", summary: "éå®åºåº", debit: 5000, credit: 0, direction: "å", balance: 15000 }, |
| | | { date: "2024-01-10", voucherNo: "è®°-0002", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 3000, direction: "å", balance: 12000 }, |
| | | { date: "2024-01-15", voucherNo: "è®°-0003", summary: "éå®åºåº", debit: 8000, credit: 0, direction: "å", balance: 20000 }, |
| | | { date: "2024-01-20", voucherNo: "è®°-0004", summary: "éå®éè´§", debit: 0, credit: 2000, direction: "å", balance: 18000 }, |
| | | { date: "2024-01-25", voucherNo: "è®°-0005", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 5000, direction: "å", balance: 13000 }, |
| | | { date: "2024-01-31", voucherNo: "-", summary: "æ¬æå计", debit: 13000, credit: 10000, direction: "å", balance: 13000 }, |
| | | { date: "2024-02-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 13000 }, |
| | | { date: "2024-02-10", voucherNo: "è®°-0006", summary: "éå®åºåº", debit: 6000, credit: 0, direction: "å", balance: 19000 }, |
| | | { date: "2024-02-15", voucherNo: "è®°-0007", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 4000, direction: "å", balance: 15000 }, |
| | | { date: "2024-02-28", voucherNo: "-", summary: "æ¬æå计", debit: 6000, credit: 4000, direction: "å", balance: 15000 }, |
| | | { date: "2024-03-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 15000 }, |
| | | { date: "2024-03-05", voucherNo: "è®°-0008", summary: "éå®åºåº", debit: 7000, credit: 0, direction: "å", balance: 22000 }, |
| | | { date: "2024-03-10", voucherNo: "è®°-0009", summary: "æ¶å°è´§æ¬¾", debit: 0, credit: 6000, direction: "å", balance: 16000 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬æå计", debit: 7000, credit: 6000, direction: "å", balance: 16000 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬å¹´ç´¯è®¡", debit: 26000, credit: 20000, direction: "å", balance: 16000 }, |
| | | ]; |
| | | |
| | | const getTableData = () => { |
| | | if (!currentSubject.value) { |
| | | dataList.value = []; |
| | | return; |
| | | } |
| | | dataList.value = [...mockData]; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subject = []; |
| | | filters.auxiliary = ""; |
| | | filters.auxiliaryItem = ""; |
| | | filters.startMonth = "2024-01"; |
| | | filters.endMonth = "2024-03"; |
| | | dataList.value = []; |
| | | }; |
| | | |
| | | const getSummaries = (param) => { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (column.property === "debit") { |
| | | const values = data.map(item => Number(item.debit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else if (column.property === "credit") { |
| | | const values = data.map(item => Number(item.credit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handlePrint = () => { |
| | | ElMessage.info("æå°åè½"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ledger-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h2 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #606266; |
| | | margin: 5px 0; |
| | | } |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="ä¼è®¡ç§ç®:"> |
| | | <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="è¯·éæ©ä¼è®¡ç§ç®" clearable style="width: 250px;" filterable /> |
| | | </el-form-item> |
| | | <el-form-item label="æé´:"> |
| | | <el-date-picker v-model="filters.startMonth" type="month" placeholder="å¼å§æä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | <span style="margin: 0 10px;">è³</span> |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="ç»ææä»½" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | <el-button @click="handlePrint" icon="Printer">æå°</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="ledger-header" v-if="currentSubject"> |
| | | <h2>ç§ç®æ»è´¦</h2> |
| | | <p>ç§ç®: {{ currentSubject.code }} {{ currentSubject.name }}</p> |
| | | <p>æé´: {{ filters.startMonth }} è³ {{ filters.endMonth }}</p> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries"> |
| | | <el-table-column prop="date" label="æ¥æ" width="120" /> |
| | | <el-table-column prop="voucherNo" label="åè¯åå·" width="120" /> |
| | | <el-table-column prop="summary" label="æè¦" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="åæ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.debit > 0" class="text-danger">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è´·æ¹" width="150"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-success">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¹å" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.direction === 'å' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä½é¢" width="150"> |
| | | <template #default="{ row }"> |
| | | <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">Â¥{{ formatMoney(Math.abs(row.balance)) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <el-empty v-if="!currentSubject" description="è¯·éæ©ä¼è®¡ç§ç®æ¥è¯¢" style="margin-top: 50px;" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "ç§ç®æ»è´¦", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | subject: [], |
| | | startMonth: "2024-01", |
| | | endMonth: "2024-03", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | |
| | | const subjectOptions = [ |
| | | { |
| | | code: "1001", |
| | | name: "åºåç°é", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "1002", |
| | | name: "é¶è¡å款", |
| | | children: [ |
| | | { code: "100201", name: "å·¥åé¶è¡" }, |
| | | { code: "100202", name: "建设é¶è¡" }, |
| | | ], |
| | | }, |
| | | { |
| | | code: "1122", |
| | | name: "åºæ¶è´¦æ¬¾", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "2202", |
| | | name: "åºä»è´¦æ¬¾", |
| | | children: [], |
| | | }, |
| | | { |
| | | code: "6001", |
| | | name: "主è¥ä¸å¡æ¶å
¥", |
| | | children: [], |
| | | }, |
| | | ]; |
| | | |
| | | const currentSubject = computed(() => { |
| | | if (!filters.subject || filters.subject.length === 0) return null; |
| | | const code = filters.subject[filters.subject.length - 1]; |
| | | return findSubject(subjectOptions, code); |
| | | }); |
| | | |
| | | const findSubject = (options, code) => { |
| | | for (const item of options) { |
| | | if (item.code === code) return item; |
| | | if (item.children && item.children.length > 0) { |
| | | const found = findSubject(item.children, code); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { date: "2024-01-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 100000 }, |
| | | { date: "2024-01-05", voucherNo: "è®°-0001", summary: "é宿¶å
¥", debit: 5650, credit: 0, direction: "å", balance: 105650 }, |
| | | { date: "2024-01-10", voucherNo: "è®°-0002", summary: "éè´æ¯åº", debit: 0, credit: 8000, direction: "å", balance: 97650 }, |
| | | { date: "2024-01-15", voucherNo: "è®°-0003", summary: "æ¶å°è´§æ¬¾", debit: 10000, credit: 0, direction: "å", balance: 107650 }, |
| | | { date: "2024-01-20", voucherNo: "è®°-0004", summary: "æ¯ä»è´¹ç¨", debit: 0, credit: 5000, direction: "å", balance: 102650 }, |
| | | { date: "2024-01-31", voucherNo: "-", summary: "æ¬æå计", debit: 15650, credit: 13000, direction: "å", balance: 102650 }, |
| | | { date: "2024-02-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 102650 }, |
| | | { date: "2024-02-10", voucherNo: "è®°-0005", summary: "é宿¶å
¥", debit: 8000, credit: 0, direction: "å", balance: 110650 }, |
| | | { date: "2024-02-15", voucherNo: "è®°-0006", summary: "éè´æ¯åº", debit: 0, credit: 12000, direction: "å", balance: 98650 }, |
| | | { date: "2024-02-28", voucherNo: "-", summary: "æ¬æå计", debit: 8000, credit: 12000, direction: "å", balance: 98650 }, |
| | | { date: "2024-03-01", voucherNo: "-", summary: "æåä½é¢", debit: 0, credit: 0, direction: "å", balance: 98650 }, |
| | | { date: "2024-03-05", voucherNo: "è®°-0007", summary: "é宿¶å
¥", debit: 12000, credit: 0, direction: "å", balance: 110650 }, |
| | | { date: "2024-03-10", voucherNo: "è®°-0008", summary: "æ¯ä»å·¥èµ", debit: 0, credit: 15000, direction: "å", balance: 95650 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬æå计", debit: 12000, credit: 15000, direction: "å", balance: 95650 }, |
| | | { date: "2024-03-31", voucherNo: "-", summary: "æ¬å¹´ç´¯è®¡", debit: 35650, credit: 40000, direction: "å", balance: 95650 }, |
| | | ]; |
| | | |
| | | const getTableData = () => { |
| | | if (!currentSubject.value) { |
| | | dataList.value = []; |
| | | return; |
| | | } |
| | | dataList.value = [...mockData]; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.subject = []; |
| | | filters.startMonth = "2024-01"; |
| | | filters.endMonth = "2024-03"; |
| | | dataList.value = []; |
| | | }; |
| | | |
| | | const getSummaries = (param) => { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (column.property === "debit") { |
| | | const values = data.map(item => Number(item.debit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else if (column.property === "credit") { |
| | | const values = data.map(item => Number(item.credit)); |
| | | const sum = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = "Â¥" + formatMoney(sum); |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | return sums; |
| | | }; |
| | | |
| | | const handlePrint = () => { |
| | | ElMessage.info("æå°åè½"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // é»è®¤ä¸å è½½æ°æ®ï¼éè¦éæ©ç§ç® |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ledger-header { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | h2 { |
| | | margin: 0 0 10px 0; |
| | | } |
| | | p { |
| | | color: #606266; |
| | | margin: 5px 0; |
| | | } |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="åè¯åå·:"> |
| | | <el-input v-model="filters.voucherNo" placeholder="请è¾å
¥åè¯åå·" clearable style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="åè¯æ¥æ:"> |
| | | <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="å¶å人:"> |
| | | <el-select v-model="filters.creator" placeholder="è¯·éæ©å¶å人" clearable style="width: 150px;"> |
| | | <el-option label="å¼ ä¸" value="å¼ ä¸" /> |
| | | <el-option label="æå" value="æå" /> |
| | | <el-option label="çäº" value="çäº" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="filters.status" placeholder="è¯·éæ©ç¶æ" clearable style="width: 150px;"> |
| | | <el-option label="æªè¿è´¦" value="unposted" /> |
| | | <el-option label="å·²è¿è´¦" value="posted" /> |
| | | <el-option label="å·²ä½åº" value="cancelled" /> |
| | | </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="totalDebit" precision="2" prefix="Â¥" /> |
| | | <el-statistic title="è´·æ¹å计" :value="totalCredit" precision="2" prefix="Â¥" style="margin-left: 30px;" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢åè¯</el-button> |
| | | <el-button @click="handleImport" icon="Upload">导å
¥</el-button> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | <template #debit="{ row }"> |
| | | <span class="text-danger" v-if="row.debit > 0">Â¥{{ formatMoney(row.debit) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | <template #credit="{ row }"> |
| | | <span class="text-success" v-if="row.credit > 0">Â¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else>-</span> |
| | | </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 type="primary" link @click="edit(row)" v-if="row.status === 'unposted'">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handlePost(row)" v-if="row.status === 'unposted'">è¿è´¦</el-button> |
| | | <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'unposted'">ä½åº</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="900px" append-to-body> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="åè¯åå·" prop="voucherNo"> |
| | | <el-input v-model="form.voucherNo" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="åè¯æ¥æ" prop="voucherDate"> |
| | | <el-date-picker v-model="form.voucherDate" type="date" placeholder="éæ©æ¥æ" value-format="YYYY-MM-DD" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éä»¶å¼ æ°" prop="attachmentCount"> |
| | | <el-input-number v-model="form.attachmentCount" :min="0" style="width: 100%;" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="åè¯åå½" prop="entries"> |
| | | <el-table :data="form.entries" border style="width: 100%"> |
| | | <el-table-column type="index" label="åºå·" width="60" /> |
| | | <el-table-column prop="subjectCode" label="ç§ç®ç¼ç " width="120"> |
| | | <template #default="{ $index }"> |
| | | <el-select v-model="form.entries[$index].subjectCode" placeholder="éæ©ç§ç®" filterable style="width: 100%;" @change="(val) => handleSubjectChange(val, $index)"> |
| | | <el-option v-for="item in subjectList" :key="item.code" :label="item.code" :value="item.code" /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="subjectName" label="ç§ç®åç§°" width="150"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.entries[$index].subjectName" disabled /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="summary" label="æè¦"> |
| | | <template #default="{ $index }"> |
| | | <el-input v-model="form.entries[$index].summary" placeholder="请è¾å
¥æè¦" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="debit" label="åæ¹éé¢" width="130"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.entries[$index].debit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="credit" label="è´·æ¹éé¢" width="130"> |
| | | <template #default="{ $index }"> |
| | | <el-input-number v-model="form.entries[$index].credit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ $index }"> |
| | | <el-button type="danger" link @click="removeEntry($index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div style="display: flex; justify-content: space-between; margin-top: 10px;"> |
| | | <el-button type="primary" link @click="addEntry">+ æ·»å åå½</el-button> |
| | | <div> |
| | | <span style="margin-right: 20px;">å计: åæ¹ <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalDebitEntry) }}</span></span> |
| | | <span>è´·æ¹ <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalCreditEntry) }}</span></span> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="å¶å人" prop="creator"> |
| | | <el-input v-model="form.creator" disabled /> |
| | | </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 @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm" :disabled="totalDebitEntry !== totalCreditEntry">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | defineOptions({ |
| | | name: "åè¯ç®¡ç", |
| | | }); |
| | | |
| | | const filters = reactive({ |
| | | voucherNo: "", |
| | | dateRange: [], |
| | | creator: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "åè¯åå·", prop: "voucherNo", width: "120" }, |
| | | { label: "åè¯æ¥æ", prop: "voucherDate", width: "120" }, |
| | | { label: "æè¦", prop: "summary", showOverflowTooltip: true }, |
| | | { label: "åæ¹éé¢", prop: "debit", slot: "debit" }, |
| | | { label: "è´·æ¹éé¢", prop: "credit", slot: "credit" }, |
| | | { label: "å¶å人", prop: "creator", width: "100" }, |
| | | { label: "ç¶æ", prop: "status", slot: "status" }, |
| | | { label: "æä½", prop: "operation", slot: "operation", width: "220", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | |
| | | const subjectList = [ |
| | | { code: "1001", name: "åºåç°é" }, |
| | | { code: "1002", name: "é¶è¡å款" }, |
| | | { code: "1122", name: "åºæ¶è´¦æ¬¾" }, |
| | | { code: "2202", name: "åºä»è´¦æ¬¾" }, |
| | | { code: "5001", name: "çäº§ææ¬" }, |
| | | { code: "6001", name: "主è¥ä¸å¡æ¶å
¥" }, |
| | | { code: "6401", name: "主è¥ä¸å¡ææ¬" }, |
| | | ]; |
| | | |
| | | const form = reactive({ |
| | | voucherNo: "", |
| | | voucherDate: "", |
| | | attachmentCount: 0, |
| | | entries: [], |
| | | creator: "å¼ ä¸", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | voucherDate: [{ required: true, message: "è¯·éæ©åè¯æ¥æ", trigger: "change" }], |
| | | }; |
| | | |
| | | const mockData = [ |
| | | { id: 1, voucherNo: "è®°-0001", voucherDate: "2024-01-15", summary: "é宿¶å
¥", debit: 5650, credit: 5650, creator: "å¼ ä¸", status: "posted", entries: [{ subjectCode: "1002", subjectName: "é¶è¡å款", summary: "é宿¶å
¥", debit: 5650, credit: 0 }, { subjectCode: "6001", subjectName: "主è¥ä¸å¡æ¶å
¥", summary: "é宿¶å
¥", debit: 0, credit: 5000 }, { subjectCode: "2221", subjectName: "åºäº¤ç¨è´¹", summary: "é项ç¨é¢", debit: 0, credit: 650 }] }, |
| | | { id: 2, voucherNo: "è®°-0002", voucherDate: "2024-01-16", summary: "éè´åææ", debit: 9040, credit: 9040, creator: "æå", status: "unposted", entries: [{ subjectCode: "5001", subjectName: "çäº§ææ¬", summary: "éè´åææ", debit: 8000, credit: 0 }, { subjectCode: "2221", subjectName: "åºäº¤ç¨è´¹", summary: "è¿é¡¹ç¨é¢", debit: 1040, credit: 0 }, { subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", summary: "éè´åææ", debit: 0, credit: 9040 }] }, |
| | | { id: 3, voucherNo: "è®°-0003", voucherDate: "2024-01-18", summary: "æ¯ä»è´§æ¬¾", debit: 5000, credit: 5000, creator: "å¼ ä¸", status: "posted", entries: [{ subjectCode: "2202", subjectName: "åºä»è´¦æ¬¾", summary: "æ¯ä»è´§æ¬¾", debit: 5000, credit: 0 }, { subjectCode: "1002", subjectName: "é¶è¡å款", summary: "æ¯ä»è´§æ¬¾", debit: 0, credit: 5000 }] }, |
| | | ]; |
| | | |
| | | const totalDebit = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.debit), 0); |
| | | }); |
| | | |
| | | const totalCredit = computed(() => { |
| | | return dataList.value.reduce((sum, item) => sum + Number(item.credit), 0); |
| | | }); |
| | | |
| | | const totalDebitEntry = computed(() => { |
| | | return form.entries.reduce((sum, item) => sum + Number(item.debit || 0), 0); |
| | | }); |
| | | |
| | | const totalCreditEntry = computed(() => { |
| | | return form.entries.reduce((sum, item) => sum + Number(item.credit || 0), 0); |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | const map = { unposted: "æªè¿è´¦", posted: "å·²è¿è´¦", cancelled: "å·²ä½åº" }; |
| | | return map[status] || status; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const map = { unposted: "warning", posted: "success", cancelled: "info" }; |
| | | return map[status] || ""; |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.voucherNo) { |
| | | result = result.filter(item => item.voucherNo.includes(filters.voucherNo)); |
| | | } |
| | | if (filters.dateRange && filters.dateRange.length === 2) { |
| | | result = result.filter(item => item.voucherDate >= filters.dateRange[0] && item.voucherDate <= filters.dateRange[1]); |
| | | } |
| | | if (filters.creator) { |
| | | result = result.filter(item => item.creator === filters.creator); |
| | | } |
| | | if (filters.status) { |
| | | result = result.filter(item => item.status === filters.status); |
| | | } |
| | | pagination.total = result.length; |
| | | dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.voucherNo = ""; |
| | | filters.dateRange = []; |
| | | filters.creator = ""; |
| | | filters.status = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const addEntry = () => { |
| | | form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }); |
| | | }; |
| | | |
| | | const removeEntry = (index) => { |
| | | form.entries.splice(index, 1); |
| | | calculateTotal(); |
| | | }; |
| | | |
| | | const handleSubjectChange = (val, index) => { |
| | | const subject = subjectList.find(item => item.code === val); |
| | | if (subject) { |
| | | form.entries[index].subjectName = subject.name; |
| | | } |
| | | }; |
| | | |
| | | const calculateTotal = () => { |
| | | // èªå¨è®¡ç®ï¼ç±computed屿§å¤ç |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ°å¢åè¯"; |
| | | Object.assign(form, { |
| | | voucherNo: "è®°-" + String(mockData.length + 1).padStart(4, "0"), |
| | | voucherDate: new Date().toISOString().split('T')[0], |
| | | attachmentCount: 0, |
| | | entries: [{ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }], |
| | | creator: "å¼ ä¸", |
| | | remark: "", |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾åè¯"; |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`æ¥çåè¯: ${row.voucherNo}`); |
| | | }; |
| | | |
| | | const handlePost = (row) => { |
| | | ElMessageBox.confirm("确认è¿è´¦è¯¥åè¯åï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "info", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "posted"; |
| | | } |
| | | ElMessage.success("è¿è´¦æå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleCancel = (row) => { |
| | | ElMessageBox.confirm("确认ä½åºè¯¥åè¯åï¼", "æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "cancelled"; |
| | | } |
| | | ElMessage.success("ä½åºæå"); |
| | | getTableData(); |
| | | }); |
| | | }; |
| | | |
| | | const handleImport = () => { |
| | | ElMessage.info("导å
¥åè½"); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("å¯¼åºæå"); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | if (totalDebitEntry.value !== totalCreditEntry.value) { |
| | | ElMessage.error("åè´·ä¸å¹³è¡¡ï¼è¯·æ£æ¥åå½"); |
| | | return; |
| | | } |
| | | const summary = form.entries.find(e => e.debit > 0)?.summary || ""; |
| | | if (isEdit.value) { |
| | | const index = mockData.findIndex(item => item.id === currentId.value); |
| | | if (index !== -1) { |
| | | mockData[index] = { ...mockData[index], ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value }; |
| | | } |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } else { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | mockData.push({ id: newId, ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value, status: "unposted" }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | 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-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |