This commit is contained in:
2026-07-20 11:50:47 +08:00
parent 95d43746f6
commit 019ed28d37
401 changed files with 61622 additions and 23869 deletions
+870
View File
@@ -0,0 +1,870 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="list" tab="名单列表">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:scroll="{ x: 3800 }"
tool-class="ele-toolbar-form"
>
<template #toolbar>
<a-space>
<a-button
class="gxmu-toolbar-btn gxmu-add-btn"
@click="openEdit()"
>
<template #icon>
<plus-outlined />
</template>
<span>{{ props.popup ? '立即申报' : '添加' }}</span>
</a-button>
<a-button
v-if="!props.popup"
class="gxmu-toolbar-btn gxmu-import-btn"
@click="openImport"
>
<template #icon>
<UploadOutlined />
</template>
<span>导入</span>
</a-button>
<a-button
v-if="!props.popup"
class="gxmu-toolbar-btn"
:loading="exporting"
@click="exportData"
>
<template #icon>
<DownloadOutlined />
</template>
<span>导出</span>
</a-button>
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入姓名/学院/联系方式"
@search="reload"
@pressEnter="reload"
/>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'currentReviewStatus'">
<ReviewStatusTag :review-list="record.reviewList" />
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openDetail(record)">详情</a>
<a-divider type="vertical" />
<a @click="openSheet(record)">申报表</a>
<a-divider type="vertical" />
<a @click="openReviewHistory(record)">审核历史</a>
<a-divider type="vertical" />
<template
v-if="!shouldHideEditByReviewList(record.reviewList)"
>
<a @click="openEdit(record)">编辑</a>
<a-divider type="vertical" />
</template>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-tab-pane>
<a-tab-pane key="form" tab="申报表">
<div class="sheet-wrap">
<div class="sheet-title">
广西医科大学2025年“优秀共青团员”申报表
</div>
<a-form layout="vertical">
<div class="sheet-table-wrap">
<table class="sheet-table">
<colgroup>
<col style="width: 10%" />
<col style="width: 15%" />
<col style="width: 10%" />
<col style="width: 15%" />
<col style="width: 12%" />
<col style="width: 18%" />
<col style="width: 10%" />
<col style="width: 10%" />
</colgroup>
<tbody>
<tr>
<td class="sheet-label">姓名</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.name"
placeholder="请输入姓名"
/>
</td>
<td class="sheet-label">性别</td>
<td class="sheet-value">
<a-select
v-model:value="sheetForm.gender"
:options="genderOptions"
placeholder="请选择"
/>
</td>
<td class="sheet-label">民族</td>
<td class="sheet-value">
<a-select
allow-clear
show-search
v-model:value="sheetForm.nation"
:options="nationOptions"
:filter-option="filterSelectOption"
placeholder="请选择民族"
/>
</td>
<td class="sheet-label">政治面貌</td>
<td class="sheet-value">
<a-select
allow-clear
show-search
v-model:value="sheetForm.politics"
:options="politicsOptions"
:filter-option="filterSelectOption"
placeholder="请选择政治面貌"
/>
</td>
</tr>
<tr>
<td class="sheet-label">出生年月</td>
<td class="sheet-value">
<a-date-picker
v-model:value="sheetForm.birthMonth"
picker="month"
value-format="YYYY-MM"
style="width: 100%"
placeholder="请选择出生年月"
/>
</td>
<td class="sheet-label">入团时间</td>
<td class="sheet-value">
<a-date-picker
v-model:value="sheetForm.joinTime"
picker="month"
value-format="YYYY-MM"
style="width: 100%"
placeholder="请选择入团时间"
/>
</td>
<td class="sheet-label">所在学院、专业、班级</td>
<td class="sheet-value left" colspan="2">
<class-cascader
v-model:value="sheetForm.collegeMajorClass"
mode="collegeClass"
placeholder="请先选择学院,再选择班级"
/>
</td>
<td class="sheet-label">职务</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.position"
placeholder="请输入职务"
/>
</td>
</tr>
<tr>
<td class="sheet-label">成为注册志愿者时间</td>
<td class="sheet-value">
<a-date-picker
v-model:value="sheetForm.volunteerRegTime"
picker="month"
value-format="YYYY-MM"
style="width: 100%"
placeholder="请选择成为注册志愿者时间"
/>
</td>
<td class="sheet-label">上一年度团员教育评议等次</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.eduEval2024"
placeholder="如:优秀"
/>
</td>
<td class="sheet-label">是否已登录“智慧团建”系统</td>
<td class="sheet-value">
<a-select
v-model:value="sheetForm.smartSystem"
placeholder="请选择"
>
<a-select-option value="是">是</a-select-option>
<a-select-option value="否">否</a-select-option>
</a-select>
</td>
<td class="sheet-label">联系电话</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.contact"
placeholder="请输入联系电话"
/>
</td>
</tr>
<tr>
<td class="sheet-label">累计志愿服务时长</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.totalVolunteerHours"
placeholder="如:200小时"
/>
</td>
<td class="sheet-label">上一年度志愿服务时长</td>
<td class="sheet-value">
<a-input
v-model:value="sheetForm.volunteerHours2024"
placeholder="如:40小时"
/>
</td>
<td class="sheet-label">发展团员编号</td>
<td class="sheet-value left" colspan="3">
<a-input
v-model:value="sheetForm.memberNo"
placeholder="请输入发展团员编号"
/>
</td>
</tr>
<tr>
<td class="sheet-label">近五年获得荣誉情况</td>
<td class="sheet-value sheet-textarea" colspan="7">
<div class="sheet-tip"
>(2020年1月1日以后,所获荣誉填3-5项)</div
>
<a-textarea
v-model:value="sheetForm.honorsFiveYears"
:rows="4"
placeholder="如:2021年10月 被评为校级优秀团员"
/>
</td>
</tr>
<tr>
<td class="sheet-label">主要事迹</td>
<td class="sheet-value sheet-textarea" colspan="7">
<div class="sheet-tip"
>(围绕参评条件,突出重点,简明扼要,不超过300字)</div
>
<a-textarea
v-model:value="sheetForm.mainStory"
:rows="5"
placeholder="请输入主要事迹"
/>
</td>
</tr>
<tr>
<td class="sheet-label">意见栏</td>
<td class="sheet-value" colspan="7">
<div class="opinion-grid">
<div class="opinion-box">
<div class="opinion-title"
>学生社团指导教师意见 / 所在单位团委</div
>
<a-textarea
v-model:value="sheetForm.leagueOpinion"
:rows="5"
placeholder="请输入意见"
/>
<div class="opinion-sign">(签章) 年 月 日</div>
</div>
<div class="opinion-box">
<div class="opinion-title"
>业务指导单位意见 / 所在单位党组织</div
>
<a-textarea
v-model:value="sheetForm.partyOpinion"
:rows="5"
placeholder="请输入意见"
/>
<div class="opinion-sign">(签章) 年 月 日</div>
</div>
</div>
</td>
</tr>
<tr>
<td class="sheet-label">校团委意见</td>
<td class="sheet-value sheet-textarea" colspan="7">
<a-textarea
v-model:value="sheetForm.schoolOpinion"
:rows="4"
placeholder="请输入校团委意见"
/>
<div class="opinion-sign align-right"
>(盖章) 年 月 日</div
>
</td>
</tr>
</tbody>
</table>
</div>
<div class="sheet-actions">
<a-space>
<a-button type="primary" @click="saveSheet"
>保存表单</a-button
>
<a-button @click="resetSheet">重置</a-button>
</a-space>
</div>
</a-form>
</div>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
<edit-modal
v-model:visible="showEdit"
:data="current"
:fixed-year="props.fixedYear"
@done="reload"
/>
<import-modal v-model:visible="showImport" @done="reload" />
<ModuleDataModal
v-model:visible="showDetail"
:module="REVIEW_MODULE"
module-name="优秀共青团员"
:pk="detailCurrent?.id"
/>
<ReviewHistoryModal
v-model:visible="showReviewHistory"
:module="REVIEW_MODULE"
:pk="reviewCurrent?.id"
:review-list="reviewCurrent?.reviewList"
/>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { message } from 'ant-design-vue';
import {
DownloadOutlined,
UploadOutlined,
PlusOutlined
} from '@ant-design/icons-vue';
import EditModal from './components/edit.vue';
import ImportModal from './components/import.vue';
import ReviewHistoryModal from '../components/reviewHistoryModal.vue';
import ModuleDataModal from '../reviewList/components/moduleDataModal.vue';
import ReviewStatusTag from '../components/reviewStatusTag.vue';
import ClassCascader from '@/views/gxmu/components/class-cascader.vue';
import { shouldHideEditByReviewList } from '../utils/review';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import {
addYxgqtyForm,
exportYxgqtyForm,
userPageYxgqtyForm,
removeYxgqtyForm,
updateYxgqtyForm
} from '@/api/gxmu/yxgqty-form';
import type {
YxgqtyForm,
YxgqtyFormParam
} from '@/api/gxmu/yxgqty-form/model';
import {
filterSelectOption,
useGxmuFormSelectOptions
} from '@/views/gxmu/useFormSelectOptions';
const props = withDefaults(
defineProps<{
popup?: boolean;
fixedYear?: string | number;
}>(),
{
popup: false
}
);
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
const searchText = ref('');
const showEdit = ref(false);
const showImport = ref(false);
const showDetail = ref(false);
const current = ref<YxgqtyForm>();
const detailCurrent = ref<YxgqtyForm | null>(null);
const activeKey = ref('list');
const REVIEW_MODULE = 'gxmu_yxgqty';
const showReviewHistory = ref(false);
const reviewCurrent = ref<YxgqtyForm | null>(null);
const exporting = ref(false);
const { genderOptions, nationOptions, politicsOptions, loadAll } =
useGxmuFormSelectOptions();
const columns = ref<ColumnItem[]>([
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: 'center',
width: 100
},
{
title: '年份',
dataIndex: 'year',
key: 'year',
align: 'center',
width: 100
},
{
title: '性别',
dataIndex: 'gender',
key: 'gender',
align: 'center',
width: 80
},
{
title: '民族',
dataIndex: 'nation',
key: 'nation',
align: 'center',
width: 100
},
{
title: '政治面貌',
dataIndex: 'politics',
key: 'politics',
align: 'center',
width: 120
},
{
title: '出生年月',
dataIndex: 'birthMonth',
key: 'birthMonth',
align: 'center',
width: 120
},
{
title: '入团时间',
dataIndex: 'joinTime',
key: 'joinTime',
align: 'center',
width: 120
},
{
title: '所在学院、专业、班级',
dataIndex: 'collegeMajorClass',
key: 'collegeMajorClass',
align: 'center',
width: 220,
ellipsis: true
},
{
title: '职务',
dataIndex: 'position',
key: 'position',
align: 'center',
width: 120
},
{
title: '成为注册志愿者时间',
dataIndex: 'volunteerRegTime',
key: 'volunteerRegTime',
align: 'center',
width: 180
},
{
title: '上一年度团员教育评议等次',
dataIndex: 'eduEval2024',
key: 'eduEval2024',
align: 'center',
width: 200
},
{
title: '是否已登录智慧团建系统',
dataIndex: 'smartSystem',
key: 'smartSystem',
align: 'center',
width: 200
},
{
title: '联系电话',
dataIndex: 'contact',
key: 'contact',
align: 'center',
width: 140
},
{
title: '累计志愿服务时长',
dataIndex: 'totalVolunteerHours',
key: 'totalVolunteerHours',
align: 'center',
width: 160
},
{
title: '上一年度志愿服务时长',
dataIndex: 'volunteerHours2024',
key: 'volunteerHours2024',
align: 'center',
width: 180
},
{
title: '发展团员编号',
dataIndex: 'memberNo',
key: 'memberNo',
align: 'center',
width: 160
},
{
title: '近五年获得荣誉情况',
dataIndex: 'honorsFiveYears',
key: 'honorsFiveYears',
align: 'center',
width: 240,
ellipsis: true
},
{
title: '主要事迹',
dataIndex: 'mainStory',
key: 'mainStory',
align: 'center',
width: 220,
ellipsis: true
},
{
title: '学生社团指导教师意见/所在单位团委',
dataIndex: 'leagueOpinion',
key: 'leagueOpinion',
align: 'center',
width: 240,
ellipsis: true
},
{
title: '业务指导单位意见/所在单位党组织',
dataIndex: 'partyOpinion',
key: 'partyOpinion',
align: 'center',
width: 240,
ellipsis: true
},
{
title: '校团委意见',
dataIndex: 'schoolOpinion',
key: 'schoolOpinion',
align: 'center',
width: 200,
ellipsis: true
},
{
title: '当前审核状态',
dataIndex: 'reviewList',
key: 'currentReviewStatus',
align: 'center',
width: 140
},
{
title: '操作',
key: 'action',
fixed: 'right',
align: 'center',
width: 260
}
]);
const createSheetDefault = (): YxgqtyForm => ({
id: undefined,
name: '',
gender: '',
nation: '',
politics: '',
birthMonth: '',
joinTime: '',
collegeMajorClass: '',
position: '',
volunteerRegTime: '',
eduEval2024: '',
smartSystem: '',
contact: '',
totalVolunteerHours: '',
volunteerHours2024: '',
memberNo: '',
honorsFiveYears: '',
mainStory: '',
leagueOpinion: '',
partyOpinion: '',
schoolOpinion: ''
});
const sheetForm = reactive<YxgqtyForm>(createSheetDefault());
const datasource: DatasourceFunction = ({ page, limit }) => {
return userPageYxgqtyForm({
page,
limit,
year: props.fixedYear ? String(props.fixedYear) : undefined,
keywords: searchText.value.trim() || undefined
} as YxgqtyFormParam).catch((e) => {
message.error(e.message);
return { list: [], count: 0 };
});
};
const buildQueryParams = (): YxgqtyFormParam => {
return {
year: props.fixedYear ? String(props.fixedYear) : undefined,
keywords: searchText.value.trim() || undefined
} as YxgqtyFormParam;
};
const reload = () => {
tableRef.value?.reload();
};
const openImport = () => {
showImport.value = true;
};
const resolveExportUrl = (payload: any) => {
return String(payload?.url || payload?.downloadUrl || payload?.path || '');
};
const exportData = async () => {
if (exporting.value) {
return;
}
exporting.value = true;
const hide = message.loading('导出中..', 0);
try {
const result = await exportYxgqtyForm(buildQueryParams());
const url = resolveExportUrl(result);
if (!url) {
throw new Error('导出地址缺失');
}
window.open(url, '_blank');
hide();
message.success('导出成功');
} catch (e: any) {
hide();
message.error(e.message || '导出失败');
} finally {
exporting.value = false;
}
};
const openReviewHistory = (record: YxgqtyForm) => {
reviewCurrent.value = record;
showReviewHistory.value = true;
};
const openDetail = (record: YxgqtyForm) => {
detailCurrent.value = record;
showDetail.value = true;
};
const openEdit = (record?: YxgqtyForm) => {
current.value = record
? (JSON.parse(JSON.stringify(record)) as YxgqtyForm)
: undefined;
showEdit.value = true;
};
const openSheet = (record: YxgqtyForm) => {
loadAll().catch((e: any) => {
message.error(e.message || '下拉选项加载失败');
});
current.value = JSON.parse(JSON.stringify(record)) as YxgqtyForm;
Object.assign(sheetForm, createSheetDefault(), current.value);
activeKey.value = 'form';
};
const remove = (record: YxgqtyForm) => {
const hide = message.loading('请求中..', 0);
removeYxgqtyForm(record.id)
.then((msg) => {
hide();
message.success(msg);
if (sheetForm.id === record.id) {
Object.assign(sheetForm, createSheetDefault());
activeKey.value = 'list';
}
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
const saveSheet = () => {
const hide = message.loading('请求中..', 0);
const saveOrUpdate = sheetForm.id ? updateYxgqtyForm : addYxgqtyForm;
saveOrUpdate(sheetForm)
.then((msg) => {
hide();
message.success(msg);
activeKey.value = 'list';
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
const resetSheet = () => {
Object.assign(sheetForm, createSheetDefault(), current.value ?? {});
};
</script>
<script lang="ts">
export default {
name: 'YXGQTYForm'
};
</script>
<style lang="less" scoped>
@import '../toolbar.less';
.sheet-wrap {
background: #fff;
border: 1px solid #d9d9d9;
padding: 16px 18px 20px;
}
.sheet-title {
text-align: center;
font-size: 20px;
font-weight: 600;
letter-spacing: 1px;
margin-bottom: 12px;
}
.sheet-table-wrap {
overflow-x: auto;
}
.sheet-table {
width: 100%;
min-width: 980px;
border-collapse: collapse;
table-layout: fixed;
}
.sheet-table td {
border: 1px solid #3b3b3b;
padding: 8px 6px;
font-size: 13px;
line-height: 1.4;
vertical-align: middle;
}
.sheet-label {
text-align: center;
font-weight: 600;
}
.sheet-value {
text-align: center;
}
.sheet-value.left {
text-align: left;
}
.sheet-value :deep(.ant-input),
.sheet-value :deep(.ant-select-selector) {
border: 0;
box-shadow: none;
padding: 0;
background: transparent;
}
.sheet-value :deep(.ant-input) {
text-align: center;
}
.sheet-value.left :deep(.ant-input) {
text-align: left;
}
.sheet-value :deep(.ant-select-selector) {
height: 24px;
min-height: 24px;
align-items: center;
}
.sheet-value :deep(.ant-select-selection-item),
.sheet-value :deep(.ant-select-selection-placeholder) {
text-align: center;
}
.sheet-textarea {
text-align: left;
}
.sheet-textarea :deep(.ant-input) {
border: 0;
box-shadow: none;
padding: 4px 6px;
background: transparent;
resize: none;
}
.sheet-tip {
color: #606266;
font-size: 12px;
margin-bottom: 6px;
}
.opinion-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.opinion-box {
border: 1px dashed #6b6b6b;
padding: 8px;
min-height: 180px;
display: flex;
flex-direction: column;
}
.opinion-title {
font-weight: 600;
margin-bottom: 8px;
}
.opinion-sign {
text-align: right;
margin-top: 8px;
color: #444;
}
.opinion-sign.align-right {
text-align: right;
}
.sheet-actions {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
@media (max-width: 768px) {
.sheet-wrap {
padding: 12px;
}
.sheet-title {
font-size: 18px;
}
.opinion-grid {
grid-template-columns: 1fr;
}
}
</style>