| | |
| | | </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-option |
| | | v-for="item in creatorOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="状态:"> |
| | |
| | | <h2 class="voucher-title">记账凭证</h2> |
| | | <div class="voucher-period">{{ form.voucherDate ? form.voucherDate.substring(0, 7) + '期' : '' }}</div> |
| | | </div> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="0"> |
| | | <el-form :model="form" :rules="rules" :disabled="isViewMode" ref="formRef" label-width="0"> |
| | | <div class="voucher-info"> |
| | | <div class="voucher-no-section"> |
| | | <span class="label">凭证字:</span> |
| | | <el-select v-model="form.voucherPrefix" style="width: 70px;"> |
| | | <el-select v-model="form.voucherPrefix" :disabled="isViewMode" style="width: 70px;"> |
| | | <el-option label="记" value="记" /> |
| | | </el-select> |
| | | <el-input v-model="form.voucherNum" style="width: 60px;" /> |
| | | <el-input v-model="form.voucherNum" :disabled="isViewMode" style="width: 60px;" /> |
| | | <span class="label" style="margin-left: 5px;">号</span> |
| | | </div> |
| | | <div class="voucher-date-section"> |
| | | <span class="label">日期:</span> |
| | | <el-date-picker v-model="form.voucherDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 140px;" /> |
| | | <el-date-picker v-model="form.voucherDate" :disabled="isViewMode" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 140px;" /> |
| | | </div> |
| | | <div class="voucher-attachment-section"> |
| | | <span class="label">附件:</span> |
| | | <el-input-number v-model="form.attachmentCount" :min="0" :controls="false" style="width: 60px;" /> |
| | | <el-input-number v-model="form.attachmentCount" :disabled="isViewMode" :min="0" :controls="false" style="width: 60px;" /> |
| | | <span class="label" style="margin-left: 5px;">张</span> |
| | | <el-button type="primary" link style="margin-left: 10px;">上传文件</el-button> |
| | | <el-button type="primary" link :disabled="isViewMode" style="margin-left: 10px;">上传文件</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="voucher-table"> |
| | |
| | | <tbody> |
| | | <tr v-for="(entry, rowIndex) in form.entries" :key="rowIndex" @click="selectRow(rowIndex)" :class="{ 'selected-row': selectedRowIndex === rowIndex }"> |
| | | <td class="col-summary"> |
| | | <el-input v-model="entry.summary" placeholder="请输入摘要" @focus="selectRow(rowIndex)" /> |
| | | <el-input v-model="entry.summary" :disabled="isViewMode" placeholder="请输入摘要" @focus="selectRow(rowIndex)" /> |
| | | </td> |
| | | <td class="col-subject"> |
| | | <el-tree-select |
| | | v-model="entry.subjectCode" |
| | | :data="subjectTreeOptions" |
| | | :props="subjectTreeSelectProps" |
| | | :disabled="isViewMode" |
| | | placeholder="选择科目" |
| | | filterable |
| | | check-strictly |
| | |
| | | <!-- 借方11列 --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'"> |
| | | <td colspan="11" class="debit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.debit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | <!-- 贷方11列 --> |
| | | <template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'"> |
| | | <td colspan="11" class="credit-input-cell"> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | <el-input-number ref="amountInputRef" v-model="entry.credit" :disabled="isViewMode" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" /> |
| | | </td> |
| | | </template> |
| | | <template v-else> |
| | |
| | | </td> |
| | | </template> |
| | | <td class="col-action"> |
| | | <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="form.entries.length <= 2">删除</el-button> |
| | | <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="isViewMode || form.entries.length <= 2">删除</el-button> |
| | | </td> |
| | | </tr> |
| | | <tr class="total-row"> |
| | |
| | | </table> |
| | | </div> |
| | | <div class="voucher-toolbar"> |
| | | <el-button type="primary" link @click="addEntry" icon="Plus">新增行</el-button> |
| | | <el-button type="primary" link @click="addEntry" icon="Plus" :disabled="isViewMode">新增行</el-button> |
| | | </div> |
| | | <div class="voucher-footer"> |
| | | <div class="creator-section"> |
| | | <span class="label">制单人:{{ form.creator }}</span> |
| | | <span class="label">制单人:</span> |
| | | <el-select |
| | | v-model="form.creator" |
| | | :disabled="isViewMode" |
| | | placeholder="请选择制单人" |
| | | filterable |
| | | clearable |
| | | style="width: 200px;" |
| | | > |
| | | <el-option |
| | | v-for="item in creatorOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | <template #footer> |
| | | <div> |
| | | <el-button type="primary" @click="submitForm" :disabled="!isBalanced">保存</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button v-if="!isViewMode" type="primary" @click="submitForm" :disabled="!isBalanced">保存</el-button> |
| | | <el-button @click="dialogVisible = false">{{ isViewMode ? '关闭' : '取消' }}</el-button> |
| | | </div> |
| | | </template> |
| | | </FormDialog> |
| | |
| | | import { ref, reactive, onMounted, computed, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | import { listAccountSubject } from "@/api/financialManagement/accountSubject"; |
| | | import { |
| | | listVoucherPage, |
| | |
| | | defineOptions({ |
| | | name: "凭证管理", |
| | | }); |
| | | |
| | | const userStore = useUserStore(); |
| | | const getDefaultCreator = () => userStore.nickName || userStore.name || "张三"; |
| | | |
| | | const filters = reactive({ |
| | | voucherNo: "", |
| | |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const dialogMode = ref("add"); |
| | | const isEdit = ref(false); |
| | | const currentId = ref(null); |
| | | const isViewMode = computed(() => dialogMode.value === "view"); |
| | | |
| | | const fallbackSubjectTree = [ |
| | | { subjectCode: "1001", subjectName: "库存现金", balanceDirection: "借方", children: [] }, |
| | |
| | | voucherDate: "", |
| | | attachmentCount: 0, |
| | | entries: [createEmptyEntry(), createEmptyEntry()], |
| | | creator: "张三", |
| | | creator: getDefaultCreator(), |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = reactive({ |
| | | ...createDefaultForm(), |
| | | }); |
| | | |
| | | const userOptions = ref([]); |
| | | |
| | | const creatorOptions = computed(() => { |
| | | const source = [ |
| | | ...userOptions.value.map(item => item.nickName || item.userName || item.name), |
| | | getDefaultCreator(), |
| | | form.creator, |
| | | filters.creator, |
| | | ]; |
| | | return [...new Set(source.filter(Boolean))]; |
| | | }); |
| | | |
| | | const selectedRowIndex = ref(-1); |
| | |
| | | } |
| | | }; |
| | | |
| | | const loadUserOptions = async () => { |
| | | try { |
| | | const { data } = await userListNoPageByTenantId(); |
| | | userOptions.value = Array.isArray(data) ? data : []; |
| | | } catch (error) { |
| | | userOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.voucherNo = ""; |
| | | filters.dateRange = []; |
| | |
| | | }; |
| | | |
| | | const addEntry = () => { |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | form.entries.push(createEmptyEntry()); |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const openAmountInput = (index, type) => { |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | editingCell.row = index; |
| | | editingCell.type = type; |
| | | nextTick(() => { |
| | |
| | | }; |
| | | |
| | | const removeEntry = (index) => { |
| | | if (isViewMode.value) { |
| | | return; |
| | | } |
| | | if (form.entries.length <= 2) { |
| | | return; |
| | | } |
| | |
| | | }; |
| | | |
| | | const add = () => { |
| | | dialogMode.value = "add"; |
| | | isEdit.value = false; |
| | | currentId.value = null; |
| | | dialogTitle.value = "新增凭证"; |
| | |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = async row => { |
| | | const openVoucherDialog = async (row, mode = "edit") => { |
| | | try { |
| | | isEdit.value = true; |
| | | dialogMode.value = mode; |
| | | isEdit.value = mode === "edit"; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "编辑凭证"; |
| | | dialogTitle.value = mode === "view" ? "查看凭证" : "编辑凭证"; |
| | | const { data } = await getVoucherDetail(row.id); |
| | | const detail = data || row; |
| | | const parts = (detail.voucherNo || "").split("-"); |
| | | Object.assign(form, createDefaultForm(), detail, { |
| | | voucherPrefix: parts[0] || "记", |
| | | voucherNum: parts[1] || "", |
| | | creator: detail.creator || getDefaultCreator(), |
| | | entries: |
| | | detail.entries?.map(item => ({ |
| | | subjectCode: item.subjectCode || "", |
| | |
| | | } |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | ElMessage.info(`查看凭证: ${row.voucherNo}`); |
| | | const edit = async row => { |
| | | await openVoucherDialog(row, "edit"); |
| | | }; |
| | | |
| | | const view = async row => { |
| | | await openVoucherDialog(row, "view"); |
| | | }; |
| | | |
| | | const handlePost = (row) => { |
| | |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | if (isViewMode.value) { |
| | | dialogVisible.value = false; |
| | | return; |
| | | } |
| | | formRef.value.validate(async valid => { |
| | | if (valid) { |
| | | // 前置校验:与后端规则对齐,减少无效请求 |
| | |
| | | }; |
| | | |
| | | onMounted(async () => { |
| | | await loadUserOptions(); |
| | | await loadSubjectList(); |
| | | await getTableData(); |
| | | }); |