This commit is contained in:
吴红兵
2026-03-03 00:15:54 +08:00
parent 7a7fa25df4
commit 39590902c5
13 changed files with 1503 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
<template>
<el-dialog :title="dataForm.id ? '编辑' : '新增'" v-model="visible" width="600px" :close-on-click-modal="false" draggable>
<el-form ref="formRef" :model="dataForm" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="dataForm.projectName" placeholder="请输入项目名称" clearable />
</el-form-item>
<el-form-item label="项目代码" prop="projectCode">
<el-input v-model="dataForm.projectCode" placeholder="请输入项目代码" clearable />
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="dataForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 100%" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="dataForm.sort" :min="0" placeholder="请输入排序" style="width: 100%" />
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="dataForm.remarks" type="textarea" :rows="3" placeholder="请输入备注" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="FinanceRecruitProjectForm">
import { reactive, ref, nextTick } from 'vue'
import { getObj, addObj, editObj } from '/@/api/finance/recruitProject';
import { useMessage } from '/@/hooks/message';
const emit = defineEmits(['refresh']);
const formRef = ref();
const dataForm = reactive({
id: '',
projectName: '',
projectCode: '',
year: '',
sort: 0,
remarks: '',
});
const visible = ref(false);
const loading = ref(false);
const dataRules = ref({
projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
projectCode: [{ required: true, message: '请输入项目代码', trigger: 'blur' }],
year: [{ required: true, message: '请选择年份', trigger: 'change' }],
});
const openDialog = async (type: string, rowData?: any) => {
visible.value = true;
dataForm.id = '';
dataForm.projectName = '';
dataForm.projectCode = '';
dataForm.year = '';
dataForm.sort = 0;
dataForm.remarks = '';
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' && rowData?.id) {
loading.value = true;
getObj(rowData.id).then((res: any) => {
if (res.data) {
Object.assign(dataForm, {
id: res.data.id || '',
projectName: res.data.projectName || '',
projectCode: res.data.projectCode || '',
year: res.data.year ? String(res.data.year) : '',
sort: res.data.sort || 0,
remarks: res.data.remarks || '',
});
}
loading.value = false;
}).catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
loading.value = false;
});
}
});
};
const onSubmit = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData = {
...dataForm,
year: dataForm.year ? parseInt(dataForm.year) : null,
};
if (dataForm.id) {
await editObj(submitData);
useMessage().success('编辑成功');
} else {
await addObj(submitData);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '编辑失败' : '新增失败'));
} finally {
loading.value = false;
}
};
defineExpose({ openDialog });
</script>

View File

@@ -0,0 +1,121 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="state.queryForm.projectName" placeholder="请输入项目名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="项目代码" prop="projectCode">
<el-input v-model="state.queryForm.projectCode" placeholder="请输入项目代码" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="state.queryForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 150px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
收费项目管理
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog('add')" v-auth="'finance_recruit_project_add'">新增</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table ref="tableRef" :data="state.dataList" v-loading="state.loading" stripe :cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle" class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="projectName" label="项目名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="projectCode" label="项目代码" width="150" align="center" />
<el-table-column prop="year" label="年份" width="100" align="center" />
<el-table-column prop="sort" label="排序" width="80" align="center" />
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
<el-table-column label="操作" align="center" fixed="right" width="150">
<template #default="scope">
<el-button icon="Edit" link type="primary" v-auth="'finance_recruit_project_edit'" @click="formDialogRef.openDialog('edit', scope.row)">编辑</el-button>
<el-button icon="Delete" link type="danger" v-auth="'finance_recruit_project_del'" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="state.total > 0" :total="state.total" v-model:page="state.page" v-model:limit="state.limit" @pagination="getDataList" />
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="FinanceRecruitProject">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/recruitProject";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { Search, Document } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const tableRef = ref()
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
projectName: '',
projectCode: '',
year: '',
},
createdIsNeed: true
});
const { getDataList, tableStyle } = useTable(state);
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,135 @@
<template>
<el-dialog :title="dataForm.id ? '编辑' : '新增'" v-model="visible" width="600px" :close-on-click-modal="false" draggable>
<el-form ref="formRef" :model="dataForm" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="批次名称" prop="settingName">
<el-input v-model="dataForm.settingName" placeholder="请输入批次名称" clearable />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="dataForm.type" placeholder="请选择类型" style="width: 100%">
<el-option label="普通类型" value="0" />
<el-option label="新生收费" value="6" />
</el-select>
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="dataForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 100%" />
</el-form-item>
<el-form-item label="截止时间" prop="lastTime">
<el-date-picker v-model="dataForm.lastTime" type="datetime" placeholder="选择截止时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="dataForm.state" placeholder="请选择状态" style="width: 100%">
<el-option label="未开始" value="0" />
<el-option label="进行中" value="30" />
<el-option label="已结束" value="100" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="dataForm.remarks" type="textarea" :rows="3" placeholder="请输入备注" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="FinanceRecruitSettingForm">
import { reactive, ref, nextTick } from 'vue'
import { getObj, addObj, editObj } from '/@/api/finance/recruitSetting';
import { useMessage } from '/@/hooks/message';
const emit = defineEmits(['refresh']);
const formRef = ref();
const dataForm = reactive({
id: '',
settingName: '',
type: '6',
year: '',
lastTime: '',
state: '0',
remarks: '',
});
const visible = ref(false);
const loading = ref(false);
const dataRules = ref({
settingName: [{ required: true, message: '请输入批次名称', trigger: 'blur' }],
type: [{ required: true, message: '请选择类型', trigger: 'change' }],
year: [{ required: true, message: '请选择年份', trigger: 'change' }],
lastTime: [{ required: true, message: '请选择截止时间', trigger: 'change' }],
state: [{ required: true, message: '请选择状态', trigger: 'change' }],
});
const openDialog = async (type: string, rowData?: any) => {
visible.value = true;
dataForm.id = '';
dataForm.settingName = '';
dataForm.type = '6';
dataForm.year = '';
dataForm.lastTime = '';
dataForm.state = '0';
dataForm.remarks = '';
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' && rowData?.id) {
loading.value = true;
getObj(rowData.id).then((res: any) => {
if (res.data) {
Object.assign(dataForm, {
id: res.data.id || '',
settingName: res.data.settingName || '',
type: res.data.type || '6',
year: res.data.year ? String(res.data.year) : '',
lastTime: res.data.lastTime || '',
state: res.data.state || '0',
remarks: res.data.remarks || '',
});
}
loading.value = false;
}).catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
loading.value = false;
});
}
});
};
const onSubmit = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData = {
...dataForm,
year: dataForm.year ? parseInt(dataForm.year) : null,
};
if (dataForm.id) {
await editObj(submitData);
useMessage().success('编辑成功');
} else {
await addObj(submitData);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '编辑失败' : '新增失败'));
} finally {
loading.value = false;
}
};
defineExpose({ openDialog });
</script>

View File

@@ -0,0 +1,139 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="批次名称" prop="settingName">
<el-input v-model="state.queryForm.settingName" placeholder="请输入批次名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="state.queryForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 150px" />
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="state.queryForm.state" placeholder="请选择状态" clearable style="width: 150px">
<el-option label="未开始" value="0" />
<el-option label="进行中" value="30" />
<el-option label="已结束" value="100" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
收费批次设置
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog('add')" v-auth="'finance_recruit_setting_add'">新增</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table ref="tableRef" :data="state.dataList" v-loading="state.loading" stripe :cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle" class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="settingName" label="批次名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="type" label="类型" width="120" align="center">
<template #default="{ row }">
<el-tag v-if="row.type === '0'" type="info">普通类型</el-tag>
<el-tag v-else-if="row.type === '6'" type="success">新生收费</el-tag>
<span v-else>{{ row.type }}</span>
</template>
</el-table-column>
<el-table-column prop="year" label="年份" width="100" align="center" />
<el-table-column prop="lastTime" label="截止时间" width="180" align="center" />
<el-table-column prop="state" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag v-if="row.state === '0'" type="info">未开始</el-tag>
<el-tag v-else-if="row.state === '30'" type="warning">进行中</el-tag>
<el-tag v-else-if="row.state === '100'" type="success">已结束</el-tag>
<span v-else>{{ row.state }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
<el-table-column label="操作" align="center" fixed="right" width="150">
<template #default="scope">
<el-button icon="Edit" link type="primary" v-auth="'finance_recruit_setting_edit'" @click="formDialogRef.openDialog('edit', scope.row)">编辑</el-button>
<el-button icon="Delete" link type="danger" v-auth="'finance_recruit_setting_del'" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="state.total > 0" :total="state.total" v-model:page="state.page" v-model:limit="state.limit" @pagination="getDataList" />
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="FinanceRecruitSetting">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/recruitSetting";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { Search, Document } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const tableRef = ref()
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
settingName: '',
year: '',
state: '',
},
createdIsNeed: true
});
const { getDataList, tableStyle } = useTable(state);
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,211 @@
<template>
<el-dialog title="收费明细" v-model="visible" width="900px" :close-on-click-modal="false" draggable>
<div class="fee-header">
<span>学号: {{ serialNumber }}</span>
<span style="margin-left: 20px;">姓名: {{ realName }}</span>
<el-button type="primary" size="small" style="margin-left: 20px;" @click="handleBindProject">绑定收费项目</el-button>
</div>
<el-table :data="feeList" v-loading="loading" stripe border style="margin-top: 15px;">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="projectCode" label="项目代码" width="120" align="center" />
<el-table-column prop="money" label="应缴金额" width="120" align="right">
<template #default="{ row }">
{{ row.money ? Number(row.money).toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag v-if="row.status === '0'" type="info">未缴费</el-tag>
<el-tag v-else-if="row.status === '10'" type="success">已缴费</el-tag>
</template>
</el-table-column>
<el-table-column prop="payType" label="缴费方式" width="120" align="center">
<template #default="{ row }">
<span v-if="row.payType === '0'">-</span>
<span v-else>{{ row.payType }}</span>
</template>
</el-table-column>
<el-table-column prop="paiedMoney" label="实缴金额" width="120" align="right">
<template #default="{ row }">
{{ row.paiedMoney ? Number(row.paiedMoney).toFixed(2) : '-' }}
</template>
</el-table-column>
<el-table-column prop="payTime" label="缴费时间" width="180" align="center" />
<el-table-column label="操作" align="center" fixed="right" width="180">
<template #default="scope">
<template v-if="scope.row.status === '0'">
<el-button link type="primary" size="small" @click="handlePay(scope.row)">缴费</el-button>
<el-button link type="danger" size="small" @click="handleDeleteFee(scope.row)">删除</el-button>
</template>
<template v-else>
<el-button link type="warning" size="small" @click="handleCancelPay(scope.row)">取消缴费</el-button>
</template>
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
</span>
</template>
</el-dialog>
<el-dialog title="绑定收费项目" v-model="bindDialogVisible" width="500px">
<el-form label-width="100px">
<el-form-item label="选择项目">
<el-select v-model="selectedProjectCodes" multiple placeholder="请选择收费项目" style="width: 100%">
<el-option v-for="item in projectList" :key="item.projectCode" :label="item.projectName" :value="item.projectCode" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="bindDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitBind"> </el-button>
</template>
</el-dialog>
<el-dialog title="缴费登记" v-model="payDialogVisible" width="400px">
<el-form label-width="100px">
<el-form-item label="缴费金额">
<el-input-number v-model="payForm.money" :min="0" :precision="2" style="width: 100%" />
</el-form-item>
<el-form-item label="缴费方式">
<el-select v-model="payForm.payType" placeholder="请选择缴费方式" style="width: 100%">
<el-option label="现金" value="20" />
<el-option label="银行转账" value="30" />
<el-option label="微信" value="40" />
<el-option label="支付宝" value="21" />
<el-option label="其他" value="22" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="payDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitPay"> </el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="FinanceRecruitStuFeeDetail">
import { ref, reactive } from 'vue'
import { getBySerialNumber, bindProject, payRegister, cancelPay, delObj } from '/@/api/finance/recruitStuProject';
import { getList } from '/@/api/finance/recruitProject';
import { useMessage, useMessageBox } from '/@/hooks/message';
const visible = ref(false);
const loading = ref(false);
const serialNumber = ref('');
const realName = ref('');
const feeList = ref<any[]>([]);
const bindDialogVisible = ref(false);
const projectList = ref<any[]>([]);
const selectedProjectCodes = ref<string[]>([]);
const payDialogVisible = ref(false);
const payForm = reactive({
id: '',
money: 0,
payType: '',
});
const openDialog = async (stuSerialNumber: string, stuRealName: string) => {
serialNumber.value = stuSerialNumber;
realName.value = stuRealName;
visible.value = true;
await loadFeeList();
};
const loadFeeList = async () => {
loading.value = true;
try {
const res = await getBySerialNumber(serialNumber.value);
feeList.value = res?.data || [];
} catch (err: any) {
useMessage().error(err.msg || '获取收费明细失败');
feeList.value = [];
} finally {
loading.value = false;
}
};
const handleBindProject = async () => {
try {
const res = await getList({});
projectList.value = res?.data || [];
selectedProjectCodes.value = [];
bindDialogVisible.value = true;
} catch (err: any) {
useMessage().error(err.msg || '获取收费项目失败');
}
};
const submitBind = async () => {
if (selectedProjectCodes.value.length === 0) {
useMessage().warning('请选择收费项目');
return;
}
try {
await bindProject(serialNumber.value, selectedProjectCodes.value);
useMessage().success('绑定成功');
bindDialogVisible.value = false;
await loadFeeList();
} catch (err: any) {
useMessage().error(err.msg || '绑定失败');
}
};
const handlePay = (row: any) => {
payForm.id = row.id;
payForm.money = Number(row.money) || 0;
payForm.payType = '';
payDialogVisible.value = true;
};
const submitPay = async () => {
if (!payForm.payType) {
useMessage().warning('请选择缴费方式');
return;
}
try {
await payRegister(payForm.id, payForm.payType);
useMessage().success('缴费成功');
payDialogVisible.value = false;
await loadFeeList();
} catch (err: any) {
useMessage().error(err.msg || '缴费失败');
}
};
const handleCancelPay = async (row: any) => {
try {
await useMessageBox().confirm('确定要取消该缴费记录吗?', '提示', { type: 'warning' });
await cancelPay(row.id);
useMessage().success('取消成功');
await loadFeeList();
} catch {
// 用户取消操作
}
};
const handleDeleteFee = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该收费项目吗?', '提示', { type: 'warning' });
await delObj(row.id);
useMessage().success('删除成功');
await loadFeeList();
} catch {
// 用户取消操作
}
};
defineExpose({ openDialog });
</script>
<style scoped>
.fee-header {
padding: 10px 0;
font-size: 14px;
color: #606266;
}
</style>

View File

@@ -0,0 +1,147 @@
<template>
<el-dialog :title="dataForm.id ? '编辑' : '新增'" v-model="visible" width="600px" :close-on-click-modal="false" draggable>
<el-form ref="formRef" :model="dataForm" :rules="dataRules" label-width="100px" v-loading="loading">
<el-form-item label="学号" prop="serialNumber">
<el-input v-model="dataForm.serialNumber" placeholder="请输入学号" clearable />
</el-form-item>
<el-form-item label="姓名" prop="realName">
<el-input v-model="dataForm.realName" placeholder="请输入姓名" clearable />
</el-form-item>
<el-form-item label="学生来源" prop="stuSource">
<el-select v-model="dataForm.stuSource" placeholder="请选择学生来源" style="width: 100%">
<el-option label="本地" value="1" />
<el-option label="外地" value="2" />
<el-option label="其他" value="3" />
</el-select>
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="dataForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 100%" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="dataForm.phone" placeholder="请输入手机号" clearable />
</el-form-item>
<el-form-item label="缴费码" prop="payCode">
<el-input v-model="dataForm.payCode" placeholder="请输入缴费码" clearable />
</el-form-item>
<el-form-item label="是否取消" prop="isCancal">
<el-radio-group v-model="dataForm.isCancal">
<el-radio label="0">正常</el-radio>
<el-radio label="1">已取消</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="dataForm.remarks" type="textarea" :rows="3" placeholder="请输入备注" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="FinanceRecruitStuForm">
import { reactive, ref, nextTick } from 'vue'
import { getObj, addObj, editObj } from '/@/api/finance/recruitStu';
import { useMessage } from '/@/hooks/message';
const emit = defineEmits(['refresh']);
const formRef = ref();
const dataForm = reactive({
id: '',
serialNumber: '',
realName: '',
stuSource: '1',
year: '',
phone: '',
payCode: '',
isCancal: '0',
remarks: '',
});
const visible = ref(false);
const loading = ref(false);
const dataRules = ref({
serialNumber: [{ required: true, message: '请输入学号', trigger: 'blur' }],
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
stuSource: [{ required: true, message: '请选择学生来源', trigger: 'change' }],
year: [{ required: true, message: '请选择年份', trigger: 'change' }],
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
});
const openDialog = async (type: string, rowData?: any) => {
visible.value = true;
dataForm.id = '';
dataForm.serialNumber = '';
dataForm.realName = '';
dataForm.stuSource = '1';
dataForm.year = '';
dataForm.phone = '';
dataForm.payCode = '';
dataForm.isCancal = '0';
dataForm.remarks = '';
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' && rowData?.id) {
loading.value = true;
getObj(rowData.id).then((res: any) => {
if (res.data) {
Object.assign(dataForm, {
id: res.data.id || '',
serialNumber: res.data.serialNumber || '',
realName: res.data.realName || '',
stuSource: res.data.stuSource || '1',
year: res.data.year ? String(res.data.year) : '',
phone: res.data.phone || '',
payCode: res.data.payCode || '',
isCancal: res.data.isCancal || '0',
remarks: res.data.remarks || '',
});
}
loading.value = false;
}).catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
loading.value = false;
});
}
});
};
const onSubmit = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData = {
...dataForm,
year: dataForm.year ? parseInt(dataForm.year) : null,
};
if (dataForm.id) {
await editObj(submitData);
useMessage().success('编辑成功');
} else {
await addObj(submitData);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '编辑失败' : '新增失败'));
} finally {
loading.value = false;
}
};
defineExpose({ openDialog });
</script>

View File

@@ -0,0 +1,148 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="学号" prop="serialNumber">
<el-input v-model="state.queryForm.serialNumber" placeholder="请输入学号" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="姓名" prop="realName">
<el-input v-model="state.queryForm.realName" placeholder="请输入姓名" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="年份" prop="year">
<el-date-picker v-model="state.queryForm.year" type="year" placeholder="选择年份" value-format="YYYY" style="width: 150px" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="state.queryForm.phone" placeholder="请输入手机号" clearable style="width: 150px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><User /></el-icon>
新生信息管理
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog('add')" v-auth="'finance_recruit_stu_add'">新增</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table ref="tableRef" :data="state.dataList" v-loading="state.loading" stripe :cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle" class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="serialNumber" label="学号" width="120" align="center" />
<el-table-column prop="realName" label="姓名" width="100" align="center" />
<el-table-column prop="stuSource" label="学生来源" width="100" align="center">
<template #default="{ row }">
<span v-if="row.stuSource === '1'">本地</span>
<span v-else-if="row.stuSource === '2'">外地</span>
<span v-else-if="row.stuSource === '3'">其他</span>
<span v-else>{{ row.stuSource }}</span>
</template>
</el-table-column>
<el-table-column prop="year" label="年份" width="100" align="center" />
<el-table-column prop="phone" label="手机号" width="140" align="center" />
<el-table-column prop="payCode" label="缴费码" width="150" show-overflow-tooltip />
<el-table-column prop="isCancal" label="是否取消" width="100" align="center">
<template #default="{ row }">
<el-tag v-if="row.isCancal === '1'" type="danger">已取消</el-tag>
<el-tag v-else type="success">正常</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
<el-table-column label="操作" align="center" fixed="right" width="200">
<template #default="scope">
<el-button icon="View" link type="primary" v-auth="'finance_recruit_stu_project_look'" @click="viewFeeDetail(scope.row)">收费明细</el-button>
<el-button icon="Edit" link type="primary" v-auth="'finance_recruit_stu_edit'" @click="formDialogRef.openDialog('edit', scope.row)">编辑</el-button>
<el-button icon="Delete" link type="danger" v-auth="'finance_recruit_stu_del'" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="state.total > 0" :total="state.total" v-model:page="state.page" v-model:limit="state.limit" @pagination="getDataList" />
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList" />
<FeeDetailDialog ref="feeDetailDialogRef" />
</div>
</template>
<script setup lang="ts" name="FinanceRecruitStu">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/recruitStu";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { Search, User } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const FeeDetailDialog = defineAsyncComponent(() => import('./feeDetail.vue'));
const tableRef = ref()
const formDialogRef = ref()
const feeDetailDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
serialNumber: '',
realName: '',
year: '',
phone: '',
},
createdIsNeed: true
});
const { getDataList, tableStyle } = useTable(state);
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
const viewFeeDetail = (row: any) => {
feeDetailDialogRef.value.openDialog(row.serialNumber, row.realName);
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,205 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="学号" prop="serialNumber">
<el-input v-model="state.queryForm.serialNumber" placeholder="请输入学号" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="项目代码" prop="projectCode">
<el-input v-model="state.queryForm.projectCode" placeholder="请输入项目代码" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="state.queryForm.status" placeholder="请选择状态" clearable style="width: 150px">
<el-option label="未缴费" value="0" />
<el-option label="已缴费" value="10" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Money /></el-icon>
学生收费管理
</span>
<div class="header-actions">
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table ref="tableRef" :data="state.dataList" v-loading="state.loading" stripe :cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle" class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="serialNumber" label="学号" width="120" align="center" />
<el-table-column prop="projectCode" label="项目代码" width="150" align="center" />
<el-table-column prop="money" label="应缴金额" width="120" align="right">
<template #default="{ row }">
{{ row.money ? Number(row.money).toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag v-if="row.status === '0'" type="info">未缴费</el-tag>
<el-tag v-else-if="row.status === '10'" type="success">已缴费</el-tag>
</template>
</el-table-column>
<el-table-column prop="payType" label="缴费方式" width="120" align="center">
<template #default="{ row }">
<span v-if="row.payType === '0'">-</span>
<span v-else>{{ row.payType }}</span>
</template>
</el-table-column>
<el-table-column prop="paiedMoney" label="实缴金额" width="120" align="right">
<template #default="{ row }">
{{ row.paiedMoney ? Number(row.paiedMoney).toFixed(2) : '-' }}
</template>
</el-table-column>
<el-table-column prop="payTime" label="缴费时间" width="180" align="center" />
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
<el-table-column label="操作" align="center" fixed="right" width="200">
<template #default="scope">
<template v-if="scope.row.status === '0'">
<el-button link type="primary" v-auth="'finance_recruit_stu_project_edit'" @click="handlePay(scope.row)">缴费</el-button>
<el-button link type="danger" v-auth="'finance_recruit_stu_project_del'" @click="handleDelete(scope.row)">删除</el-button>
</template>
<template v-else>
<el-button link type="warning" v-auth="'finance_recruit_stu_project_edit'" @click="handleCancelPay(scope.row)">取消缴费</el-button>
</template>
</template>
</el-table-column>
</el-table>
<pagination v-show="state.total > 0" :total="state.total" v-model:page="state.page" v-model:limit="state.limit" @pagination="getDataList" />
</el-card>
</div>
<el-dialog title="缴费登记" v-model="payDialogVisible" width="400px">
<el-form label-width="100px">
<el-form-item label="缴费金额">
<el-input-number v-model="payForm.money" :min="0" :precision="2" style="width: 100%" />
</el-form-item>
<el-form-item label="缴费方式">
<el-select v-model="payForm.payType" placeholder="请选择缴费方式" style="width: 100%">
<el-option label="现金" value="20" />
<el-option label="银行转账" value="30" />
<el-option label="微信" value="40" />
<el-option label="支付宝" value="21" />
<el-option label="其他" value="22" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="payDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitPay"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="FinanceRecruitStuProject">
import { ref, reactive } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj, payRegister, cancelPay } from "/@/api/finance/recruitStuProject";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { Search, Money } from '@element-plus/icons-vue'
const tableRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
serialNumber: '',
projectCode: '',
status: '',
},
createdIsNeed: true
});
const { getDataList, tableStyle } = useTable(state);
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
const payDialogVisible = ref(false);
const payForm = reactive({
id: '',
money: 0,
payType: '',
});
const handlePay = (row: any) => {
payForm.id = row.id;
payForm.money = Number(row.money) || 0;
payForm.payType = '';
payDialogVisible.value = true;
};
const submitPay = async () => {
if (!payForm.payType) {
useMessage().warning('请选择缴费方式');
return;
}
try {
await payRegister(payForm.id, payForm.payType);
useMessage().success('缴费成功');
payDialogVisible.value = false;
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '缴费失败');
}
};
const handleCancelPay = async (row: any) => {
try {
await useMessageBox().confirm('确定要取消该缴费记录吗?', '提示', { type: 'warning' });
await cancelPay(row.id);
useMessage().success('取消成功');
getDataList();
} catch {
// 用户取消操作
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -66,6 +66,14 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="是否有资产" prop="hasAssets">
<el-radio-group v-model="dataForm.hasAssets" :disabled="flowFieldDisabled('hasAssets')">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24" class="mb12">
<el-form-item label="品目编码" prop="categoryCode">
<el-cascader v-model="categoryCodePath" :options="categoryTreeData" :props="{ value: 'code', label: 'name', children: 'children', checkStrictly: false }" placeholder="请选择品目编码(仅最后一级)" clearable filterable :show-all-levels="true" style="width: 100%" :disabled="flowFieldDisabled('categoryCode')" @change="handleCategoryChange" />
@@ -591,6 +599,7 @@ const dataForm = reactive({
budget: null as number | null,
isCentralized: '',
isSpecial: '',
hasAssets: '0',
purchaseMode: '',
purchaseType: '',
purchaseTypeUnion: '',