Files
tuanwei-admin/src/views/gxmu/YXGQTYForm/index.vue
T
2026-07-20 11:50:47 +08:00

871 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>