Merge branch 'developer'

This commit is contained in:
吴红兵
2026-03-09 15:28:46 +08:00
33 changed files with 619 additions and 735 deletions

140
auto-imports.d.ts vendored
View File

@@ -1,73 +1,73 @@
// Generated by 'unplugin-auto-import' // Generated by 'unplugin-auto-import'
export {} export {};
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope'];
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'];
const computed: typeof import('vue')['computed'] const computed: typeof import('vue')['computed'];
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp'];
const createPinia: typeof import('pinia')['createPinia'] const createPinia: typeof import('pinia')['createPinia'];
const customRef: typeof import('vue')['customRef'] const customRef: typeof import('vue')['customRef'];
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'];
const defineComponent: typeof import('vue')['defineComponent'] const defineComponent: typeof import('vue')['defineComponent'];
const defineStore: typeof import('pinia')['defineStore'] const defineStore: typeof import('pinia')['defineStore'];
const effectScope: typeof import('vue')['effectScope'] const effectScope: typeof import('vue')['effectScope'];
const getActivePinia: typeof import('pinia')['getActivePinia'] const getActivePinia: typeof import('pinia')['getActivePinia'];
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'];
const getCurrentScope: typeof import('vue')['getCurrentScope'] const getCurrentScope: typeof import('vue')['getCurrentScope'];
const h: typeof import('vue')['h'] const h: typeof import('vue')['h'];
const inject: typeof import('vue')['inject'] const inject: typeof import('vue')['inject'];
const isProxy: typeof import('vue')['isProxy'] const isProxy: typeof import('vue')['isProxy'];
const isReactive: typeof import('vue')['isReactive'] const isReactive: typeof import('vue')['isReactive'];
const isReadonly: typeof import('vue')['isReadonly'] const isReadonly: typeof import('vue')['isReadonly'];
const isRef: typeof import('vue')['isRef'] const isRef: typeof import('vue')['isRef'];
const mapActions: typeof import('pinia')['mapActions'] const mapActions: typeof import('pinia')['mapActions'];
const mapGetters: typeof import('pinia')['mapGetters'] const mapGetters: typeof import('pinia')['mapGetters'];
const mapState: typeof import('pinia')['mapState'] const mapState: typeof import('pinia')['mapState'];
const mapStores: typeof import('pinia')['mapStores'] const mapStores: typeof import('pinia')['mapStores'];
const mapWritableState: typeof import('pinia')['mapWritableState'] const mapWritableState: typeof import('pinia')['mapWritableState'];
const markRaw: typeof import('vue')['markRaw'] const markRaw: typeof import('vue')['markRaw'];
const nextTick: typeof import('vue')['nextTick'] const nextTick: typeof import('vue')['nextTick'];
const onActivated: typeof import('vue')['onActivated'] const onActivated: typeof import('vue')['onActivated'];
const onBeforeMount: typeof import('vue')['onBeforeMount'] const onBeforeMount: typeof import('vue')['onBeforeMount'];
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'];
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'];
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'];
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'];
const onDeactivated: typeof import('vue')['onDeactivated'] const onDeactivated: typeof import('vue')['onDeactivated'];
const onErrorCaptured: typeof import('vue')['onErrorCaptured'] const onErrorCaptured: typeof import('vue')['onErrorCaptured'];
const onMounted: typeof import('vue')['onMounted'] const onMounted: typeof import('vue')['onMounted'];
const onRenderTracked: typeof import('vue')['onRenderTracked'] const onRenderTracked: typeof import('vue')['onRenderTracked'];
const onRenderTriggered: typeof import('vue')['onRenderTriggered'] const onRenderTriggered: typeof import('vue')['onRenderTriggered'];
const onScopeDispose: typeof import('vue')['onScopeDispose'] const onScopeDispose: typeof import('vue')['onScopeDispose'];
const onServerPrefetch: typeof import('vue')['onServerPrefetch'] const onServerPrefetch: typeof import('vue')['onServerPrefetch'];
const onUnmounted: typeof import('vue')['onUnmounted'] const onUnmounted: typeof import('vue')['onUnmounted'];
const onUpdated: typeof import('vue')['onUpdated'] const onUpdated: typeof import('vue')['onUpdated'];
const provide: typeof import('vue')['provide'] const provide: typeof import('vue')['provide'];
const reactive: typeof import('vue')['reactive'] const reactive: typeof import('vue')['reactive'];
const readonly: typeof import('vue')['readonly'] const readonly: typeof import('vue')['readonly'];
const ref: typeof import('vue')['ref'] const ref: typeof import('vue')['ref'];
const resolveComponent: typeof import('vue')['resolveComponent'] const resolveComponent: typeof import('vue')['resolveComponent'];
const resolveDirective: typeof import('vue')['resolveDirective'] const resolveDirective: typeof import('vue')['resolveDirective'];
const setActivePinia: typeof import('pinia')['setActivePinia'] const setActivePinia: typeof import('pinia')['setActivePinia'];
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'];
const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReactive: typeof import('vue')['shallowReactive'];
const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowReadonly: typeof import('vue')['shallowReadonly'];
const shallowRef: typeof import('vue')['shallowRef'] const shallowRef: typeof import('vue')['shallowRef'];
const storeToRefs: typeof import('pinia')['storeToRefs'] const storeToRefs: typeof import('pinia')['storeToRefs'];
const toRaw: typeof import('vue')['toRaw'] const toRaw: typeof import('vue')['toRaw'];
const toRef: typeof import('vue')['toRef'] const toRef: typeof import('vue')['toRef'];
const toRefs: typeof import('vue')['toRefs'] const toRefs: typeof import('vue')['toRefs'];
const triggerRef: typeof import('vue')['triggerRef'] const triggerRef: typeof import('vue')['triggerRef'];
const unref: typeof import('vue')['unref'] const unref: typeof import('vue')['unref'];
const useAttrs: typeof import('vue')['useAttrs'] const useAttrs: typeof import('vue')['useAttrs'];
const useCssModule: typeof import('vue')['useCssModule'] const useCssModule: typeof import('vue')['useCssModule'];
const useCssVars: typeof import('vue')['useCssVars'] const useCssVars: typeof import('vue')['useCssVars'];
const useLink: typeof import('vue-router')['useLink'] const useLink: typeof import('vue-router')['useLink'];
const useRoute: typeof import('vue-router')['useRoute'] const useRoute: typeof import('vue-router')['useRoute'];
const useRouter: typeof import('vue-router')['useRouter'] const useRouter: typeof import('vue-router')['useRouter'];
const useSlots: typeof import('vue')['useSlots'] const useSlots: typeof import('vue')['useSlots'];
const watch: typeof import('vue')['watch'] const watch: typeof import('vue')['watch'];
const watchEffect: typeof import('vue')['watchEffect'] const watchEffect: typeof import('vue')['watchEffect'];
const watchPostEffect: typeof import('vue')['watchPostEffect'] const watchPostEffect: typeof import('vue')['watchPostEffect'];
const watchSyncEffect: typeof import('vue')['watchSyncEffect'] const watchSyncEffect: typeof import('vue')['watchSyncEffect'];
} }

View File

@@ -153,3 +153,13 @@ export function getUserListByRole(obj: string) {
method: 'get', method: 'get',
}); });
} }
/**
* 模拟登录(管理员以指定用户身份登录)
*/
export function simulateLogin(userId: string) {
return request({
url: '/auth/token/simulate/' + userId,
method: 'get',
});
}

View File

@@ -69,11 +69,7 @@ export function uploadBidFileNewVersion(data: {
}); });
} }
export function submitBidFileTask(data: { export function submitBidFileTask(data: { runJobId: string; to?: number; comment?: string }) {
runJobId: string;
to?: number;
comment?: string;
}) {
return request({ return request({
url: '/purchase/purchasingbidfile/submit', url: '/purchase/purchasingbidfile/submit',
method: 'post', method: 'post',
@@ -121,13 +117,7 @@ export function getFlowPurchaseDetail(purchaseId: string) {
}); });
} }
export function tempStoreBidFile(data: { export function tempStoreBidFile(data: { purchaseId: string; fileId?: string; fileName?: string; fileUrl?: string; comment?: string }) {
purchaseId: string;
fileId?: string;
fileName?: string;
fileUrl?: string;
comment?: string;
}) {
return request({ return request({
url: '/purchase/purchasingbidfile/tempStore', url: '/purchase/purchasingbidfile/tempStore',
method: 'post', method: 'post',

View File

@@ -31,7 +31,7 @@ const init = () => {
let flowInstId = props.currJob.flowInstId; let flowInstId = props.currJob.flowInstId;
let runJobId = props.currJob.id; let runJobId = props.currJob.id;
src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`; src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`;
console.log(src.value) console.log(src.value);
}; };
function handleJob(jobBtn) { function handleJob(jobBtn) {

View File

@@ -95,9 +95,20 @@
<el-switch v-model="scope.row.lockFlag" @change="changeSwitch(scope.row)" active-value="0" inactive-value="9"></el-switch> <el-switch v-model="scope.row.lockFlag" @change="changeSwitch(scope.row)" active-value="0" inactive-value="9"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('common.action')" width="200" fixed="right"> <el-table-column :label="$t('common.action')" width="280" fixed="right">
<template #default="scope"> <template #default="scope">
<div style="display: flex"> <div style="display: flex">
<!-- 模拟登录 -->
<el-button
v-auth="'sys_user_simulate'"
icon="User"
text
type="primary"
:disabled="scope.row.username === 'admin'"
@click="handleSimulateLogin(scope.row)"
>
模拟登录
</el-button>
<!-- 重置密码 --> <!-- 重置密码 -->
<popover-input v-model="inputPassword" @confirm="changePassword(scope.row)"> <popover-input v-model="inputPassword" @confirm="changePassword(scope.row)">
<template #default> <template #default>
@@ -142,22 +153,31 @@
url="/admin/user/import" url="/admin/user/import"
@refreshDataList="getDataList" @refreshDataList="getDataList"
/> />
<change-role ref="changeRoleRef" />
</div> </div>
</template> </template>
<script lang="ts" name="systemUser" setup> <script lang="ts" name="systemUser" setup>
import { delObj, pageList, putObj } from '/@/api/admin/user'; import { delObj, pageList, putObj, simulateLogin } from '/@/api/admin/user';
import { deptTree } from '/@/api/admin/dept'; import { deptTree } from '/@/api/admin/dept';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useUserInfo } from '/@/stores/userInfo';
import { Session, Local } from '/@/utils/storage';
import { useRouter } from 'vue-router';
// 动态引入组件 // 动态引入组件
const UserForm = defineAsyncComponent(() => import('./form.vue')); const UserForm = defineAsyncComponent(() => import('./form.vue'));
const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue')); const QueryTree = defineAsyncComponent(() => import('/@/components/QueryTree/index.vue'));
const PopoverInput = defineAsyncComponent(() => import('/@/components/PopoverInput/index.vue')); const PopoverInput = defineAsyncComponent(() => import('/@/components/PopoverInput/index.vue'));
const ChangeRole = defineAsyncComponent(() => import('/@/views/admin/system/role/change-role.vue'));
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter();
const userStore = useUserInfo();
const changeRoleRef = ref();
// 定义变量内容 // 定义变量内容
const userDialogRef = ref(); const userDialogRef = ref();
@@ -260,4 +280,28 @@ const changePassword = async (row: any) => {
useMessage().success(t('common.optSuccessText')); useMessage().success(t('common.optSuccessText'));
getDataList(); getDataList();
}; };
// 模拟登录
const handleSimulateLogin = async (row: any) => {
try {
await useMessageBox().confirm('确定要以用户 "' + row.realName + '" 的身份登录吗?');
const res = await simulateLogin(row.userId);
if (res.data && res.data.access_token) {
userStore.setTokenCache(res.data.access_token, res.data.refresh_token);
Session.set('userInfo', {
user: { username: res.data.username, userId: res.data.user_id },
});
await userStore.setUserInfos();
useMessage().success('模拟登录成功');
router.push('/');
setTimeout(() => {
changeRoleRef.value?.open();
}, 500);
}
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '模拟登录失败');
}
}
};
</script> </script>

View File

@@ -20,7 +20,9 @@
<el-descriptions :column="1" border size="small"> <el-descriptions :column="1" border size="small">
<el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item> <el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item> <el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item>
<el-descriptions-item label="采购金额">{{ applyData.budget ? Number(applyData.budget).toLocaleString() + ' 元' : '-' }}</el-descriptions-item> <el-descriptions-item label="采购金额">{{
applyData.budget ? Number(applyData.budget).toLocaleString() + ' 元' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="招标代理">{{ applyData.agentName || '-' }}</el-descriptions-item> <el-descriptions-item label="招标代理">{{ applyData.agentName || '-' }}</el-descriptions-item>
<el-descriptions-item label="审批状态"> <el-descriptions-item label="审批状态">
<el-tag v-if="applyData.fileFlowStatus === '0'" type="primary">运行中</el-tag> <el-tag v-if="applyData.fileFlowStatus === '0'" type="primary">运行中</el-tag>

View File

@@ -19,11 +19,7 @@
<el-divider content-position="left">采购相关文件补充上传</el-divider> <el-divider content-position="left">采购相关文件补充上传</el-divider>
<el-alert <el-alert :type="fileTypeAlertType" :closable="false" style="margin-bottom: 16px">
:type="fileTypeAlertType"
:closable="false"
style="margin-bottom: 16px"
>
<template #title> <template #title>
<span v-if="requiredFileType">{{ requiredFileTypeName }}必传</span> <span v-if="requiredFileType">{{ requiredFileTypeName }}必传</span>
<span v-else>当前采购方式无需补充材料</span> <span v-else>当前采购方式无需补充材料</span>

View File

@@ -20,7 +20,9 @@
<el-descriptions :column="1" border size="small"> <el-descriptions :column="1" border size="small">
<el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item> <el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item> <el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item>
<el-descriptions-item label="采购金额">{{ applyData.budget ? Number(applyData.budget).toLocaleString() + ' 元' : '-' }}</el-descriptions-item> <el-descriptions-item label="采购金额">{{
applyData.budget ? Number(applyData.budget).toLocaleString() + ' 元' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="采购方式">{{ purchaseTypeLabel || '-' }}</el-descriptions-item> <el-descriptions-item label="采购方式">{{ purchaseTypeLabel || '-' }}</el-descriptions-item>
<el-descriptions-item label="补充状态"> <el-descriptions-item label="补充状态">
<el-tag v-if="applyData.supplementFlowStatus === '1'" type="success">已完成</el-tag> <el-tag v-if="applyData.supplementFlowStatus === '1'" type="success">已完成</el-tag>

View File

@@ -1586,37 +1586,16 @@ watch(
// 下载模版:统一走后端接口,按原始文件下载(避免前端静态资源被当成 HTML 返回) // 下载模版:统一走后端接口,按原始文件下载(避免前端静态资源被当成 HTML 返回)
const downloadTemplate = async (type: string) => { const downloadTemplate = async (type: string) => {
// 优先从后台模版列表获取模版名称 // 从后台模版列表获取模版名称templateName 包含原始文件扩展名)
const backendTemplate = templateList.value.find((t: any) => t.templateType === type); const backendTemplate = templateList.value.find((t: any) => t.templateType === type);
let displayName = '';
if (backendTemplate?.templateTitle) { if (!backendTemplate?.templateName) {
displayName = backendTemplate.templateTitle; useMessage().error('模版不存在或未维护模版文件,请联系管理员');
} else {
// 后备:使用本地硬编码的名称
const templateMap: Record<string, string> = {
business_negotiation: '商务洽谈表.doc',
market_purchase_minutes: '部门自行采购市场采购纪要.doc',
inquiry: '部门采购询价模版.doc',
direct_select: '服务商城项目需求模板(直选).doc',
public_select: '服务商城项目需求模板(公开比选).doc',
invite_select: '服务商城项目需求模板(邀请比选).doc',
purchase_requirement: '采购需求填报模板.doc',
import_application: '进口产品申请及专家论证意见表.doc',
single_source: '单一来源论专家证附件.docx',
feasibility_report: '项目可行性论证报告模板.doc',
gov_pur_int: '政府采购意向申请表.doc',
};
displayName = templateMap[type] || '';
}
if (!displayName) {
useMessage().error('模版不存在');
return; return;
} }
try { try {
await other.downBlobFile(`/purchase/purchasingtemplate/download?type=${encodeURIComponent(type)}`, {}, displayName); await other.downBlobFile(`/purchase/purchasingtemplate/download?type=${encodeURIComponent(type)}`, {}, backendTemplate.templateName);
useMessage().success('模版下载成功'); useMessage().success('模版下载成功');
} catch (err) { } catch (err) {
useMessage().error('模版下载失败,请联系管理员维护模版文件'); useMessage().error('模版下载失败,请联系管理员维护模版文件');

View File

@@ -232,13 +232,7 @@
<!-- 指定一人 --> <!-- 指定一人 -->
<el-form-item v-if="representorSelectMode === 'designate'" label="选择参与人"> <el-form-item v-if="representorSelectMode === 'designate'" label="选择参与人">
<el-select <el-select v-model="selectedRepresentor" placeholder="请选择参与人" filterable :disabled="isViewMode" style="width: 300px">
v-model="selectedRepresentor"
placeholder="请选择参与人"
filterable
:disabled="isViewMode"
style="width: 300px"
>
<el-option <el-option
v-for="member in deptMembers" v-for="member in deptMembers"
:key="member.teacherNo" :key="member.teacherNo"
@@ -278,9 +272,7 @@
<!-- 已选中的参与人 --> <!-- 已选中的参与人 -->
<el-form-item v-if="currentRepresentor" label="已选中参与人"> <el-form-item v-if="currentRepresentor" label="已选中参与人">
<el-tag type="success" size="large"> <el-tag type="success" size="large"> {{ currentRepresentor.realName }} ({{ currentRepresentor.teacherNo }}) </el-tag>
{{ currentRepresentor.realName }} ({{ currentRepresentor.teacherNo }})
</el-tag>
</el-form-item> </el-form-item>
<!-- 参与人身份 --> <!-- 参与人身份 -->
@@ -293,12 +285,7 @@
<!-- 保存按钮 --> <!-- 保存按钮 -->
<el-form-item> <el-form-item>
<el-button <el-button type="primary" :loading="saveRepresentorLoading" :disabled="!canSaveRepresentor || isViewMode" @click="handleSaveRepresentor">
type="primary"
:loading="saveRepresentorLoading"
:disabled="!canSaveRepresentor || isViewMode"
@click="handleSaveRepresentor"
>
保存参与人信息 保存参与人信息
</el-button> </el-button>
</el-form-item> </el-form-item>
@@ -492,7 +479,7 @@ const effectivePurchaseId = computed(() => {
if (props.currJob?.orderId) { if (props.currJob?.orderId) {
return String(props.currJob.orderId); return String(props.currJob.orderId);
} }
return route.query.id as string || route.query.purchaseId as string || ''; return (route.query.id as string) || (route.query.purchaseId as string) || '';
}); });
// 是否可以提交(非查看模式即可提交,不强制要求上传文件) // 是否可以提交(非查看模式即可提交,不强制要求上传文件)
@@ -724,7 +711,7 @@ const handleFlowSave = async () => {
fileName: uploadForm.fileName || '', fileName: uploadForm.fileName || '',
fileUrl: uploadForm.fileUrl || '', fileUrl: uploadForm.fileUrl || '',
comment: uploadForm.comment || '', comment: uploadForm.comment || '',
flowTarget: flowTarget.value || '' flowTarget: flowTarget.value || '',
}; };
const res = await tempStoreBidFile(params); const res = await tempStoreBidFile(params);
@@ -735,8 +722,6 @@ const handleFlowSave = async () => {
currElTabIsSave(props.currJob, props.currElTab.id, true, emit); currElTabIsSave(props.currJob, props.currElTab.id, true, emit);
} }
return true; return true;
} else { } else {
ElMessage.error(res.msg || '暂存失败'); ElMessage.error(res.msg || '暂存失败');
@@ -783,10 +768,7 @@ const handleRandomSelect = async () => {
try { try {
randomSelectLoading.value = true; randomSelectLoading.value = true;
const res = await randomSelectRepresentor( const res = await randomSelectRepresentor(effectivePurchaseId.value, randomCandidates.value.join(','));
effectivePurchaseId.value,
randomCandidates.value.join(',')
);
if (res.code === 0 && res.data) { if (res.code === 0 && res.data) {
currentRepresentor.value = res.data; currentRepresentor.value = res.data;
ElMessage.success(`随机抽取成功:${res.data.realName}`); ElMessage.success(`随机抽取成功:${res.data.realName}`);
@@ -816,12 +798,7 @@ const handleSaveRepresentor = async () => {
let res: any; let res: any;
if (representorSelectMode.value === 'designate') { if (representorSelectMode.value === 'designate') {
res = await saveRepresentor( res = await saveRepresentor(Number(effectivePurchaseId.value), selectedRepresentor.value, undefined, representorType.value);
Number(effectivePurchaseId.value),
selectedRepresentor.value,
undefined,
representorType.value
);
} else { } else {
res = await saveRepresentor( res = await saveRepresentor(
Number(effectivePurchaseId.value), Number(effectivePurchaseId.value),

View File

@@ -287,15 +287,11 @@ const handleSendToAgent = async () => {
// 确认弹窗 // 确认弹窗
try { try {
await ElMessageBox.confirm( await ElMessageBox.confirm('是否确认发送至招标代理启动招标文件审核流程?', '确认发送', {
'是否确认发送至招标代理启动招标文件审核流程?',
'确认发送',
{
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
} });
);
} catch { } catch {
// 用户取消 // 用户取消
return; return;

View File

@@ -283,10 +283,18 @@
<el-tag v-if="scope.row.fileFlowStatus === '0'" type="primary" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)" <el-tag v-if="scope.row.fileFlowStatus === '0'" type="primary" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)"
>运行中 >运行中
</el-tag> </el-tag>
<el-tag v-else-if="scope.row.fileFlowStatus === '1'" type="success" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)" <el-tag
v-else-if="scope.row.fileFlowStatus === '1'"
type="success"
class="status-tag-clickable"
@click="handleShowDocAudit(scope.row)"
>完成 >完成
</el-tag> </el-tag>
<el-tag v-else-if="scope.row.fileFlowStatus === '2'" type="danger" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)" <el-tag
v-else-if="scope.row.fileFlowStatus === '2'"
type="danger"
class="status-tag-clickable"
@click="handleShowDocAudit(scope.row)"
>作废 >作废
</el-tag> </el-tag>
<el-tag v-else-if="scope.row.fileFlowStatus === '3'" type="info" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)" <el-tag v-else-if="scope.row.fileFlowStatus === '3'" type="info" class="status-tag-clickable" @click="handleShowDocAudit(scope.row)"

View File

@@ -29,12 +29,7 @@
</span> </span>
</div> </div>
</template> </template>
<el-alert <el-alert v-if="requiredFileTypeName" type="info" :closable="false" style="margin-bottom: 16px">
v-if="requiredFileTypeName"
type="info"
:closable="false"
style="margin-bottom: 16px"
>
<template #title> <template #title>
<span>{{ requiredFileTypeName }}</span> <span>{{ requiredFileTypeName }}</span>
</template> </template>

View File

@@ -12,7 +12,6 @@
<div class="header-actions"> <div class="header-actions">
<el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button> <el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button> <el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList"> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
<TableColumnControl <TableColumnControl
ref="columnControlRef" ref="columnControlRef"
@@ -108,47 +107,35 @@
<form-dialog ref="formDialogRef" @refresh="getDataList" /> <form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 导入弹窗 --> <!-- 导入弹窗 -->
<el-dialog title="导入获奖活动信息" v-model="importDialogVisible" :width="500" :close-on-click-modal="false" draggable> <upload-excel
<div style="margin-bottom: 15px"> ref="uploadExcelRef"
<el-button icon="Download" type="success" @click="handleDownloadTemplate"> 下载模板 </el-button> :title="'导入活动获奖'"
</div> :url="'/stuwork/file/importActivityAwards'"
<el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :limit="1" accept=".xlsx,.xls" drag> :temp-url="'/stuwork/file/exportActivityAwardsTemplate'"
<el-icon class="el-icon--upload"><upload-filled /></el-icon> @refreshDataList="getDataList"
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> />
<template #tip>
<div class="el-upload__tip">只能上传 xlsx/xls 文件</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="importDialogVisible = false"> </el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading"> </el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="ActivityAwards"> <script setup lang="ts" name="ActivityAwards">
import { reactive, ref } from 'vue'; import { reactive, ref, defineAsyncComponent } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj } from '/@/api/stuwork/activityawards'; import { fetchList, delObj } from '/@/api/stuwork/activityawards';
import { exportActivityAwardsTemplate, importActivityAwards, downloadBlobFile } from '/@/api/stuwork/file';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import { parseTime } from '/@/utils/formatTime'; import { parseTime } from '/@/utils/formatTime';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { UploadFilled, List, Trophy, CreditCard, Avatar, Medal, Calendar, EditPen, Setting, Menu, Document } from '@element-plus/icons-vue'; import { List, Trophy, CreditCard, Avatar, Medal, Calendar, EditPen, Setting, Menu, Document } from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn'; import { useTableColumnControl } from '/@/hooks/tableColumn';
import FormDialog from './form.vue'; import FormDialog from './form.vue';
// 引入组件
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
// 定义变量内容 // 定义变量内容
const formDialogRef = ref(); const formDialogRef = ref();
const uploadRef = ref(); const uploadExcelRef = ref();
const columnControlRef = ref(); const columnControlRef = ref();
const showSearch = ref(false); const showSearch = ref(false);
const importDialogVisible = ref(false);
const importFile = ref<File | null>(null);
const importLoading = ref(false);
// 表格列配置 // 表格列配置
const tableColumns = [ const tableColumns = [
@@ -205,47 +192,8 @@ const handleDelete = async (row: any) => {
// 导入 // 导入
const handleImport = () => { const handleImport = () => {
importDialogVisible.value = true; if (uploadExcelRef.value) {
importFile.value = null; uploadExcelRef.value.show();
uploadRef.value?.clearFiles();
};
// 导出
const handleExport = async () => {
await downloadBlobFile(exportActivityAwardsTemplate(), '活动获奖导入模板.xlsx');
};
// 下载模板
const handleDownloadTemplate = async () => {
await downloadBlobFile(exportActivityAwardsTemplate(), '活动获奖导入模板.xlsx');
};
// 文件变化
const handleFileChange = (file: any) => {
importFile.value = file.raw;
};
// 提交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请选择要导入的文件');
return;
}
importLoading.value = true;
try {
const formData = new FormData();
formData.append('file', importFile.value);
await importActivityAwards(formData);
useMessage().success('导入成功');
importDialogVisible.value = false;
importFile.value = null;
uploadRef.value?.clearFiles();
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
} }
}; };
</script> </script>

View File

@@ -131,7 +131,8 @@
<script setup lang="ts" name="ActivityInfoSubSignup"> <script setup lang="ts" name="ActivityInfoSubSignup">
import { reactive, ref, onMounted, computed } from 'vue'; import { reactive, ref, onMounted, computed } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj, exportExcel, getActivityInfoList, getActivityInfoSubList } from '/@/api/stuwork/activityinfosubsignup'; import { fetchList, delObj, getActivityInfoList, getActivityInfoSubList } from '/@/api/stuwork/activityinfosubsignup';
import { makeExportActivitySignUpDetailTask } from '/@/api/stuwork/file';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { import {
@@ -246,19 +247,8 @@ const handleDelete = async (row: any) => {
const handleExport = async () => { const handleExport = async () => {
exportLoading.value = true; exportLoading.value = true;
try { try {
const res = await exportExcel(state.queryForm); await makeExportActivitySignUpDetailTask(state.queryForm);
const blob = new Blob([res.data as BlobPart], { useMessage().success('导出任务已创建,请在文件管理中下载');
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `活动报名表_${new Date().getTime()}.xlsx`;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败');
} finally { } finally {

View File

@@ -271,7 +271,8 @@
<script setup lang="ts" name="ClassFeeLog"> <script setup lang="ts" name="ClassFeeLog">
import { reactive, ref, onMounted, computed } from 'vue'; import { reactive, ref, onMounted, computed } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj, exportExcel, getSummary } from '/@/api/stuwork/classfeelog'; import { fetchList, delObj, getSummary } from '/@/api/stuwork/classfeelog';
import { makeExportClassFundTask } from '/@/api/stuwork/file';
import { getDeptList } from '/@/api/basic/basicclass'; import { getDeptList } from '/@/api/basic/basicclass';
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getClassListByRole } from '/@/api/basic/basicclass';
@@ -402,20 +403,8 @@ const handleViewAttachment = (row: any) => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
const res = await exportExcel(state.queryForm); await makeExportClassFundTask(state.queryForm);
const blob = new Blob([res], { useMessage().success('导出任务已创建,请在文件管理中下载');
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
const fileName = `班费记录_${new Date().getTime()}.xlsx`;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败');
} }

View File

@@ -47,6 +47,7 @@
</span> </span>
<div class="header-actions"> <div class="header-actions">
<el-button icon="Download" type="success" @click="handleExport"> 导出 </el-button> <el-button icon="Download" type="success" @click="handleExport"> 导出 </el-button>
<el-button icon="Download" type="primary" plain @click="handleAsyncExport"> 异步导出 </el-button>
<el-button icon="Refresh" type="primary" class="ml10" @click="handleSync"> 同步教室安排 </el-button> <el-button icon="Refresh" type="primary" class="ml10" @click="handleSync"> 同步教室安排 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList"> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
<TableColumnControl <TableColumnControl
@@ -175,6 +176,7 @@
import { reactive, ref, onMounted } from 'vue'; import { reactive, ref, onMounted } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, exportData, syncClassroomArrangement } from '/@/api/stuwork/classroombase'; import { fetchList, exportData, syncClassroomArrangement } from '/@/api/stuwork/classroombase';
import { makeExportClassAssetsTask } from '/@/api/stuwork/file';
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept'; import { getDeptListByLevelTwo } from '/@/api/basic/basicdept';
import { queryAllClass } from '/@/api/basic/basicclass'; import { queryAllClass } from '/@/api/basic/basicclass';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
@@ -360,6 +362,16 @@ const handleExport = async () => {
} }
}; };
// 异步导出
const handleAsyncExport = async () => {
try {
await makeExportClassAssetsTask(searchForm);
useMessage().success('导出任务已创建,请在文件管理中下载');
} catch (err: any) {
useMessage().error(err.msg || '导出失败');
}
};
// 同步教室安排 // 同步教室安排
const handleSync = async () => { const handleSync = async () => {
const { confirm } = useMessageBox(); const { confirm } = useMessageBox();

View File

@@ -226,7 +226,8 @@
<script setup lang="ts" name="ClassSafeEdu"> <script setup lang="ts" name="ClassSafeEdu">
import { reactive, ref, onMounted } from 'vue'; import { reactive, ref, onMounted } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj, exportExcel, statisticsByYearTerm } from '/@/api/stuwork/classsafeedu'; import { fetchList, delObj, statisticsByYearTerm } from '/@/api/stuwork/classsafeedu';
import { makeExportSafetyEducationTask } from '/@/api/stuwork/file';
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getClassListByRole } from '/@/api/basic/basicclass';
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
@@ -346,31 +347,8 @@ const handleViewImage = (url: string) => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
const res = await exportExcel(state.queryForm); await makeExportSafetyEducationTask(state.queryForm);
useMessage().success('导出任务已创建,请在文件管理中下载');
// 创建blob对象
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 设置文件名
const fileName = `安全教育_${new Date().getTime()}.xlsx`;
link.setAttribute('download', fileName);
// 触发下载
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败');
} }

View File

@@ -48,7 +48,6 @@
</span> </span>
<div class="header-actions"> <div class="header-actions">
<el-button icon="Upload" type="primary" @click="handleImport"> 导入 </el-button> <el-button icon="Upload" type="primary" @click="handleImport"> 导入 </el-button>
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" /> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div> </div>
</div> </div>
@@ -162,43 +161,68 @@
</el-dialog> </el-dialog>
<!-- 导入对话框 --> <!-- 导入对话框 -->
<upload-excel <el-dialog v-model="importDialogVisible" title="导入宿舍月卫生" width="500px" :close-on-click-modal="false">
ref="uploadExcelRef" <el-form :model="importForm" ref="importFormRef" label-width="80px" style="margin-bottom: 15px">
:title="'导入宿舍月卫生'" <el-form-item label="楼号" prop="buildingNo">
:url="'/stuwork/file/importDormHygieneMonthly'" <el-select v-model="importForm.buildingNo" placeholder="请选择楼号" filterable style="width: 100%">
:temp-url="templateUrl" <el-option v-for="item in buildingList" :key="item.buildingNo" :label="item.buildingNo" :value="item.buildingNo" />
@refreshDataList="getDataList" </el-select>
</el-form-item>
<el-form-item label="月份" prop="month">
<el-date-picker
v-model="importForm.month"
type="month"
placeholder="选择月份"
format="YYYY-MM"
value-format="YYYY-MM"
style="width: 100%"
/> />
</el-form-item>
<el-form-item>
<el-button icon="Download" type="success" @click="handleDownloadTemplate"> 下载模板 </el-button>
</el-form-item>
</el-form>
<el-upload ref="importUploadRef" :auto-upload="false" :on-change="handleImportFileChange" :limit="1" accept=".xlsx,.xls" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">只能上传 xlsx/xls 文件请先选择楼号和月份后下载模板</div>
</template>
</el-upload>
<template #footer>
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading">确认导入</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="DormHygieneMonthly"> <script setup lang="ts" name="DormHygieneMonthly">
import { reactive, ref, onMounted, defineAsyncComponent } from 'vue'; import { reactive, ref, onMounted } from 'vue';
import { fetchList, addObj, editObj, delObj, triggerEvaluation } from '/@/api/stuwork/dormhygienemonthly'; import { fetchList, addObj, editObj, delObj, triggerEvaluation } from '/@/api/stuwork/dormhygienemonthly';
import { exportDormHygieneMonthlyTemplate, downloadBlobFile } from '/@/api/stuwork/file'; import { exportDormHygieneMonthlyTemplate, importDormHygieneMonthly, downloadBlobFile } from '/@/api/stuwork/file';
import { getBuildingList } from '/@/api/stuwork/dormbuilding'; import { getBuildingList } from '/@/api/stuwork/dormbuilding';
import { useMessage } from '/@/hooks/message'; import { useMessage } from '/@/hooks/message';
import { Search, Document, Plus, Edit, Delete, Promotion } from '@element-plus/icons-vue'; import { Search, Document, Plus, Edit, Delete, Promotion } from '@element-plus/icons-vue';
// 引入组件
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
// 定义变量内容 // 定义变量内容
const searchFormRef = ref(); const searchFormRef = ref();
const formRef = ref(); const formRef = ref();
const evalFormRef = ref(); const evalFormRef = ref();
const uploadExcelRef = ref(); const importFormRef = ref();
const importUploadRef = ref();
const showSearch = ref(true); const showSearch = ref(true);
const loading = ref(false); const loading = ref(false);
const submitLoading = ref(false); const submitLoading = ref(false);
const evalLoading = ref(false); const evalLoading = ref(false);
const importLoading = ref(false);
const dataList = ref<any[]>([]); const dataList = ref<any[]>([]);
const buildingList = ref<any[]>([]); const buildingList = ref<any[]>([]);
const dialogVisible = ref(false); const dialogVisible = ref(false);
const evalDialogVisible = ref(false);
const dialogTitle = ref('新增宿舍月卫生'); const dialogTitle = ref('新增宿舍月卫生');
// 模板文件URL const evalDialogVisible = ref(false);
const templateUrl = ref('/stuwork/file/exportDormHygieneMonthlyTemplate'); const importDialogVisible = ref(false);
const importFile = ref<File | null>(null);
// 分页 // 分页
const page = reactive({ const page = reactive({
@@ -229,6 +253,12 @@ const evalForm = reactive({
month: '', month: '',
}); });
// 导入表单
const importForm = reactive({
buildingNo: '',
month: '',
});
// 表单校验规则 // 表单校验规则
const rules = { const rules = {
month: [{ required: true, message: '请选择月份', trigger: 'change' }], month: [{ required: true, message: '请选择月份', trigger: 'change' }],
@@ -344,14 +374,55 @@ const handleEvaluation = () => {
// 导入 // 导入
const handleImport = () => { const handleImport = () => {
if (uploadExcelRef.value) { importForm.buildingNo = '';
uploadExcelRef.value.show(); importForm.month = '';
importFile.value = null;
importDialogVisible.value = true;
};
// 下载模板
const handleDownloadTemplate = async () => {
if (!importForm.buildingNo) {
useMessage().warning('请先选择楼号');
return;
}
if (!importForm.month) {
useMessage().warning('请先选择月份');
return;
}
try {
await downloadBlobFile(exportDormHygieneMonthlyTemplate(importForm), `宿舍月卫生导入模板_${importForm.month}.xlsx`);
} catch (err: any) {
useMessage().error(err?.msg || '下载模板失败');
} }
}; };
// 导 // 导入文件变化
const handleExport = async () => { const handleImportFileChange = (file: any) => {
await downloadBlobFile(exportDormHygieneMonthlyTemplate(searchForm), '宿舍月卫生.xlsx'); importFile.value = file.raw;
};
// 提交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请先选择要上传的文件');
return;
}
importLoading.value = true;
try {
const formData = new FormData();
formData.append('file', importFile.value);
await importDormHygieneMonthly(formData);
useMessage().success('导入成功');
importDialogVisible.value = false;
importFile.value = null;
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
}
}; };
// 提交评比 // 提交评比

View File

@@ -187,11 +187,11 @@ import { ref, reactive, defineAsyncComponent, computed, onMounted, nextTick } fr
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, putObj, delObjs } from '/@/api/stuwork/dormreform'; import { fetchList, putObj, delObjs } from '/@/api/stuwork/dormreform';
import { makeExportDormReformTask } from '/@/api/stuwork/file';
import { getBuildingList } from '/@/api/stuwork/dormbuilding'; import { getBuildingList } from '/@/api/stuwork/dormbuilding';
import { getDormRoomDataByBuildingNo } from '/@/api/stuwork/dormroom'; import { getDormRoomDataByBuildingNo } from '/@/api/stuwork/dormroom';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
import { downBlobFile, adaptationUrl } from '/@/utils/other';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
// 引入组件 // 引入组件
@@ -345,8 +345,13 @@ const handleDelete = async (row: any) => {
}; };
// 导出 // 导出
const handleExport = () => { const handleExport = async () => {
downBlobFile(adaptationUrl('/stuwork/dormreform/export'), searchForm, '月卫生检查整改.xlsx'); try {
await makeExportDormReformTask(searchForm);
useMessage().success('导出任务已创建,请在文件管理中下载');
} catch (err: any) {
useMessage().error(err.msg || '导出失败');
}
}; };
// 格式化整改结果 // 格式化整改结果

View File

@@ -167,10 +167,10 @@ import { ref, reactive, defineAsyncComponent, onMounted, computed, nextTick } fr
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj, editDept } from '/@/api/stuwork/dormroom'; import { fetchList, delObj, editDept } from '/@/api/stuwork/dormroom';
import { makeExportDormRoomTask } from '/@/api/stuwork/file';
import { getDeptList } from '/@/api/basic/basicclass'; import { getDeptList } from '/@/api/basic/basicclass';
import { getBuildingList } from '/@/api/stuwork/dormbuilding'; import { getBuildingList } from '/@/api/stuwork/dormbuilding';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import { downBlobFile, adaptationUrl } from '/@/utils/other';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
@@ -321,8 +321,8 @@ const confirmDeptAssign = async () => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
await downBlobFile(adaptationUrl('/stuwork/dormroom/export'), searchForm, '宿舍房间.xlsx'); await makeExportDormRoomTask(searchForm);
useMessage().success('导出成功'); useMessage().success('导出任务已创建,请在文件管理中下载');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败');
} }

View File

@@ -1,6 +1,10 @@
<template> <template>
<div class="layout-padding"> <div class="layout-padding">
<div class="layout-padding-auto layout-padding-view"> <div class="layout-padding-auto layout-padding-view">
<!-- 顶部工具栏 -->
<div class="mb10">
<el-button icon="Download" type="primary" plain @click="handleExport"> 导出 </el-button>
</div>
<!-- 表格 --> <!-- 表格 -->
<el-table <el-table
:data="state.dataList" :data="state.dataList"
@@ -32,6 +36,7 @@
import { reactive, onMounted } from 'vue'; import { reactive, onMounted } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { queryStudentAbnormal } from '/@/api/stuwork/dormroomstudent'; import { queryStudentAbnormal } from '/@/api/stuwork/dormroomstudent';
import { makeExportDormStudentAbnormalTask } from '/@/api/stuwork/file';
import { useMessage } from '/@/hooks/message'; import { useMessage } from '/@/hooks/message';
// 配置 useTable // 配置 useTable
@@ -75,6 +80,16 @@ const handleView = (row: any) => {
// TODO: 实现查看详情功能 // TODO: 实现查看详情功能
}; };
// 导出
const handleExport = async () => {
try {
await makeExportDormStudentAbnormalTask();
useMessage().success('导出任务已创建,请在文件管理中下载');
} catch (err: any) {
useMessage().error(err?.msg || '导出失败');
}
};
// 初始化 // 初始化
onMounted(() => { onMounted(() => {
getDataList(); getDataList();

View File

@@ -194,7 +194,8 @@
import { reactive, ref, onMounted, computed, nextTick } from 'vue'; import { reactive, ref, onMounted, computed, nextTick } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObjs, exportEmptyPeopleRoomExcel } from '/@/api/stuwork/dormroomstudent'; import { fetchList, delObjs } from '/@/api/stuwork/dormroomstudent';
import { makeExportDormStudentTask, makeExportDormStudentAbnormalTask, makeExportDormStatisticsTask } from '/@/api/stuwork/file';
import { getDeptList } from '/@/api/basic/basicclass'; import { getDeptList } from '/@/api/basic/basicclass';
import { getBuildingList } from '/@/api/stuwork/dormbuilding'; import { getBuildingList } from '/@/api/stuwork/dormbuilding';
import { fetchDormRoomTreeList } from '/@/api/stuwork/dormroom'; import { fetchDormRoomTreeList } from '/@/api/stuwork/dormroom';
@@ -376,24 +377,14 @@ const handleExport = async () => {
stuNo: searchForm.stuNo, stuNo: searchForm.stuNo,
realName: searchForm.realName, realName: searchForm.realName,
}; };
const res = await exportEmptyPeopleRoomExcel(params); await makeExportDormStatisticsTask(params);
const blob = useMessage().success('导出任务已创建,请在文件管理中下载');
res instanceof Blob ? res : new Blob([res as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `空宿舍导出_${Date.now()}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err?.msg || '导出失败'); useMessage().error(err?.msg || '导出失败');
} }
}; };
// 名单导出:与导出共用空 n 人宿舍导出接口,文件名区分 // 名单导出:住宿学生名单导出
const handleExportList = async () => { const handleExportList = async () => {
try { try {
const params = { const params = {
@@ -406,18 +397,8 @@ const handleExportList = async () => {
stuNo: searchForm.stuNo, stuNo: searchForm.stuNo,
realName: searchForm.realName, realName: searchForm.realName,
}; };
const res = await exportEmptyPeopleRoomExcel(params); await makeExportDormStudentTask(params);
const blob = useMessage().success('导出任务已创建,请在文件管理中下载');
res instanceof Blob ? res : new Blob([res as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `住宿学生名单_${Date.now()}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err?.msg || '导出失败'); useMessage().error(err?.msg || '导出失败');
} }

View File

@@ -57,7 +57,6 @@
<div class="header-actions"> <div class="header-actions">
<el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button> <el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button> <el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList"> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
<TableColumnControl <TableColumnControl
ref="columnControlRef" ref="columnControlRef"
@@ -150,60 +149,37 @@
<!-- 新增/编辑表单弹窗 --> <!-- 新增/编辑表单弹窗 -->
<form-dialog ref="formDialogRef" @refresh="getDataList" /> <form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 导入弹窗 --> <!-- 导入对话框 -->
<el-dialog title="导入文明班级" v-model="importDialogVisible" :width="500" :close-on-click-modal="false" draggable> <upload-excel
<div style="margin-bottom: 15px"> ref="uploadExcelRef"
<el-button icon="Download" type="success" @click="handleDownloadTemplate"> 下载模板 </el-button> :title="'导入文明班级'"
</div> :url="'/stuwork/file/importRewardClass'"
<el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :limit="1" accept=".xlsx,.xls" drag> :temp-url="'/stuwork/file/exportRewardClassTemplate'"
<el-icon class="el-icon--upload"><upload-filled /></el-icon> @refreshDataList="getDataList"
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> />
<template #tip>
<div class="el-upload__tip">只能上传 xlsx/xls 文件</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="importDialogVisible = false"> </el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading"> </el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="RewardClass"> <script setup lang="ts" name="RewardClass">
import { reactive, ref, onMounted } from 'vue'; import { reactive, ref, onMounted, defineAsyncComponent } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj } from '/@/api/stuwork/rewardclass'; import { fetchList, delObj } from '/@/api/stuwork/rewardclass';
import { exportRewardClassTemplate, importRewardClass, downloadBlobFile } from '/@/api/stuwork/file';
import { getDeptList } from '/@/api/basic/basicclass'; import { getDeptList } from '/@/api/basic/basicclass';
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getClassListByRole } from '/@/api/basic/basicclass';
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue';
UploadFilled,
List,
Calendar,
Clock,
OfficeBuilding,
Grid,
UserFilled,
Trophy,
EditPen,
Setting,
Menu,
Search,
Document,
} from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn'; import { useTableColumnControl } from '/@/hooks/tableColumn';
import FormDialog from './form.vue'; import FormDialog from './form.vue';
// 引入组件
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
// 定义变量内容 // 定义变量内容
const formDialogRef = ref(); const formDialogRef = ref();
const uploadRef = ref(); const uploadExcelRef = ref();
const searchFormRef = ref(); const searchFormRef = ref();
const columnControlRef = ref(); const columnControlRef = ref();
const showSearch = ref(true); const showSearch = ref(true);
@@ -211,9 +187,6 @@ const schoolYearList = ref<any[]>([]);
const schoolTermList = ref<any[]>([]); const schoolTermList = ref<any[]>([]);
const deptList = ref<any[]>([]); const deptList = ref<any[]>([]);
const classList = ref<any[]>([]); const classList = ref<any[]>([]);
const importDialogVisible = ref(false);
const importFile = ref<File | null>(null);
const importLoading = ref(false);
// 表格列配置 // 表格列配置
const tableColumns = [ const tableColumns = [
@@ -280,47 +253,8 @@ const handleReset = () => {
// 导入 // 导入
const handleImport = () => { const handleImport = () => {
importDialogVisible.value = true; if (uploadExcelRef.value) {
importFile.value = null; uploadExcelRef.value.show();
uploadRef.value?.clearFiles();
};
// 导出
const handleExport = async () => {
await downloadBlobFile(exportRewardClassTemplate(), '文明班级导入模板.xlsx');
};
// 下载模板
const handleDownloadTemplate = async () => {
await downloadBlobFile(exportRewardClassTemplate(), '文明班级导入模板.xlsx');
};
// 文件变化
const handleFileChange = (file: any) => {
importFile.value = file.raw;
};
// 提交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请选择要导入的文件');
return;
}
importLoading.value = true;
try {
const formData = new FormData();
formData.append('file', importFile.value);
await importRewardClass(formData);
useMessage().success('导入成功');
importDialogVisible.value = false;
importFile.value = null;
uploadRef.value?.clearFiles();
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
} }
}; };

View File

@@ -46,7 +46,6 @@
<div class="header-actions"> <div class="header-actions">
<el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button> <el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button> <el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button>
<el-button icon="Download" type="warning" class="ml10" @click="handleExport"> 导出 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList"> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
<TableColumnControl <TableColumnControl
ref="columnControlRef" ref="columnControlRef"
@@ -137,53 +136,41 @@
<form-dialog ref="formDialogRef" @refresh="getDataList" /> <form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 导入对话框 --> <!-- 导入对话框 -->
<el-dialog title="导入数据" v-model="importDialogVisible" :width="500" :close-on-click-modal="false" draggable> <upload-excel
<div style="margin-bottom: 15px"> ref="uploadExcelRef"
<el-button icon="Download" type="success" @click="handleDownloadTemplate"> 下载模板 </el-button> :title="'导入文明宿舍'"
</div> :url="'/stuwork/file/importRewardDorm'"
<el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :limit="1" accept=".xlsx,.xls" drag> :temp-url="'/stuwork/file/exportRewardDormTemplate'"
<el-icon class="el-icon--upload"><upload-filled /></el-icon> @refreshDataList="getDataList"
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> />
<template #tip>
<div class="el-upload__tip">只能上传 xlsx/xls 文件</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading">确认</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="RewardDorm"> <script setup lang="ts" name="RewardDorm">
import { reactive, ref, onMounted, computed } from 'vue'; import { reactive, ref, onMounted, computed, defineAsyncComponent } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj } from '/@/api/stuwork/rewarddorm'; import { fetchList, delObj } from '/@/api/stuwork/rewarddorm';
import { exportRewardDormTemplate, importRewardDorm, downloadBlobFile } from '/@/api/stuwork/file';
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { UploadFilled, List, Calendar, Clock, House, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'; import { List, Calendar, Clock, House, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn'; import { useTableColumnControl } from '/@/hooks/tableColumn';
import FormDialog from './form.vue'; import FormDialog from './form.vue';
// 引入组件
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
// 定义变量 // 定义变量
const route = useRoute(); const route = useRoute();
const formDialogRef = ref(); const formDialogRef = ref();
const uploadRef = ref(); const uploadExcelRef = ref();
const searchFormRef = ref(); const searchFormRef = ref();
const columnControlRef = ref(); const columnControlRef = ref();
const showSearch = ref(true); const showSearch = ref(true);
const schoolYearList = ref<any[]>([]); const schoolYearList = ref<any[]>([]);
const schoolTermList = ref<any[]>([]); const schoolTermList = ref<any[]>([]);
const importDialogVisible = ref(false);
const importFile = ref<File | null>(null);
const importLoading = ref(false);
// 表格列配置 // 表格列配置
const tableColumns = [ const tableColumns = [
@@ -243,47 +230,8 @@ const handleReset = () => {
// 导入 // 导入
const handleImport = () => { const handleImport = () => {
importDialogVisible.value = true; if (uploadExcelRef.value) {
importFile.value = null; uploadExcelRef.value.show();
uploadRef.value?.clearFiles();
};
// 导出
const handleExport = async () => {
await downloadBlobFile(exportRewardDormTemplate(), '文明宿舍导入模板.xlsx');
};
// 下载模板
const handleDownloadTemplate = async () => {
await downloadBlobFile(exportRewardDormTemplate(), '文明宿舍导入模板.xlsx');
};
// 文件变化
const handleFileChange = (file: any) => {
importFile.value = file.raw;
};
// 提交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请先选择要上传的文件');
return;
}
importLoading.value = true;
try {
const formData = new FormData();
formData.append('file', importFile.value);
await importRewardDorm(formData);
useMessage().success('导入成功');
importDialogVisible.value = false;
importFile.value = null;
uploadRef.value?.clearFiles();
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
} }
}; };

View File

@@ -206,33 +206,19 @@
</template> </template>
<script setup lang="ts" name="RewardStudent"> <script setup lang="ts" name="RewardStudent">
import { reactive, ref, onMounted, computed, nextTick } from 'vue'; import { reactive, ref, onMounted, computed, nextTick } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router'
import { fetchList, exportExcel, updateStuAward, getStuRewardList, getRewardRuleList } from '/@/api/stuwork/rewardstudent'; import { fetchList, updateStuAward, getStuRewardList, getRewardRuleList } from "/@/api/stuwork/rewardstudent";
import { getDeptList } from '/@/api/basic/basicclass'; import { makeExportStudentPraiseTask } from "/@/api/stuwork/file";
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { getDeptList } from "/@/api/basic/basicclass";
import { getClassListByRole } from '/@/api/basic/basicclass'; import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from '/@/api/admin/dict'; import { getClassListByRole } from "/@/api/basic/basicclass";
import { useMessage } from '/@/hooks/message'; import { getDicts } from "/@/api/admin/dict";
import { parseTime } from '/@/utils/formatTime'; import { useMessage } from "/@/hooks/message";
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import { parseTime } from "/@/utils/formatTime";
import { import TableColumnControl from '/@/components/TableColumnControl/index.vue'
List, import { List, OfficeBuilding, Grid, CreditCard, Avatar, DataAnalysis, Warning, Trophy, Clock, Menu, Search, Document, Setting, View } from '@element-plus/icons-vue'
OfficeBuilding, import { useTableColumnControl } from '/@/hooks/tableColumn'
Grid,
CreditCard,
Avatar,
DataAnalysis,
Warning,
Trophy,
Clock,
Menu,
Search,
Document,
Setting,
View,
} from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn';
const dateTimeFormat = '{y}-{m}-{d} {h}:{i}:{s}'; const dateTimeFormat = '{y}-{m}-{d} {h}:{i}:{s}';
@@ -352,38 +338,15 @@ const handleReset = () => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
loading.value = true; loading.value = true
const res = await exportExcel(queryForm); await makeExportStudentPraiseTask(queryForm)
useMessage().success('导出任务已创建,请在文件管理中下载')
// 创建 blob
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 设置文件名
const fileName = `奖励学生_${new Date().getTime()}.xlsx`;
link.setAttribute('download', fileName);
// 触发下载
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败')
} finally { } finally {
loading.value = false; loading.value = false
}
} }
};
// 获取学年列表 // 获取学年列表
const getSchoolYearList = async () => { const getSchoolYearList = async () => {

View File

@@ -64,7 +64,9 @@
</span> </span>
<div class="header-actions"> <div class="header-actions">
<el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button> <el-button icon="Plus" type="primary" @click="formDialogRef.openDialog()"> 新增 </el-button>
<el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入 </el-button> <el-button icon="Upload" type="success" class="ml10" @click="handleImport"> 导入行为记录 </el-button>
<el-button icon="Download" type="primary" plain class="ml10" @click="handleDownloadConductTemplate"> 导入考核模板 </el-button>
<el-button icon="Upload" type="warning" class="ml10" @click="handleConductImport"> 导入考核 </el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList"> <right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList">
<TableColumnControl <TableColumnControl
ref="columnControlRef" ref="columnControlRef"
@@ -167,7 +169,7 @@
<form-dialog ref="formDialogRef" @refresh="getDataList" /> <form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 导入对话框 --> <!-- 导入对话框 -->
<el-dialog title="导入数据" v-model="importDialogVisible" :width="500" :close-on-click-modal="false" draggable> <el-dialog title="导入行为记录" v-model="importDialogVisible" :width="500" :close-on-click-modal="false" draggable>
<el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :limit="1" accept=".xlsx,.xls" drag> <el-upload ref="uploadRef" :auto-upload="false" :on-change="handleFileChange" :limit="1" accept=".xlsx,.xls" drag>
<el-icon class="el-icon--upload"><upload-filled /></el-icon> <el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
@@ -182,6 +184,26 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<!-- 导入操行考核弹窗 -->
<el-dialog title="导入操行考核数据" v-model="conductImportDialogVisible" :width="500" :close-on-click-modal="false" draggable>
<div style="margin-bottom: 15px">
<el-button icon="Download" type="success" @click="handleDownloadConductTemplate"> 下载模板 </el-button>
</div>
<el-upload :auto-upload="false" :on-change="handleConductFileChange" :limit="1" accept=".xlsx,.xls" drag>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">只能上传 xlsx/xls 文件请先下载导入模板</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="conductImportDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConductImportSubmit" :disabled="!conductImportFile || conductImportLoading">确认导入</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -190,6 +212,7 @@ import { reactive, ref, onMounted, computed, nextTick } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from '/@/hooks/table';
import { fetchList, delObj, importExcel } from '/@/api/stuwork/stuconduct'; import { fetchList, delObj, importExcel } from '/@/api/stuwork/stuconduct';
import { exportConductAssessmentTemplate, importConductAssessment, downloadBlobFile } from '/@/api/stuwork/file';
import { getDeptList } from '/@/api/basic/basicclass'; import { getDeptList } from '/@/api/basic/basicclass';
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from '/@/api/basic/basicyear';
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getClassListByRole } from '/@/api/basic/basicclass';
@@ -230,6 +253,9 @@ const typeList = ref<any[]>([]);
const importDialogVisible = ref(false); const importDialogVisible = ref(false);
const importFile = ref<File | null>(null); const importFile = ref<File | null>(null);
const importLoading = ref(false); const importLoading = ref(false);
const conductImportDialogVisible = ref(false);
const conductImportFile = ref<File | null>(null);
const conductImportLoading = ref(false);
// 表格列配置 // 表格列配置
const tableColumns = [ const tableColumns = [
@@ -365,6 +391,49 @@ const handleImportSubmit = async () => {
} }
}; };
// 下载操行考核导入模板
const handleDownloadConductTemplate = async () => {
try {
await downloadBlobFile(exportConductAssessmentTemplate(), `操行考核导入模板_${Date.now()}.xlsx`);
} catch (err: any) {
useMessage().error(err?.msg || '下载模板失败');
}
};
// 打开操行考核导入弹窗
const handleConductImport = () => {
conductImportDialogVisible.value = true;
conductImportFile.value = null;
};
// 操行考核文件变化
const handleConductFileChange = (file: any) => {
conductImportFile.value = file.raw;
};
// 提交操行考核导入
const handleConductImportSubmit = async () => {
if (!conductImportFile.value) {
useMessage().warning('请先选择要上传的文件');
return;
}
conductImportLoading.value = true;
try {
const formData = new FormData();
formData.append('file', conductImportFile.value);
await importConductAssessment(formData);
useMessage().success('导入成功');
conductImportDialogVisible.value = false;
conductImportFile.value = null;
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
conductImportLoading.value = false;
}
};
// 编辑 // 编辑
const handleEdit = (row: any) => { const handleEdit = (row: any) => {
formDialogRef.value?.openDialog('edit', row); formDialogRef.value?.openDialog('edit', row);

View File

@@ -5,18 +5,47 @@
<el-row v-show="showSearch"> <el-row v-show="showSearch">
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList"> <el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
<el-form-item label="学年" prop="schoolYear"> <el-form-item label="学年" prop="schoolYear">
<el-select v-model="queryForm.schoolYear" placeholder="请选择学年" clearable filterable style="width: 200px"> <el-select
<el-option v-for="item in schoolYearList" :key="item.year" :label="item.year" :value="item.year"> </el-option> v-model="queryForm.schoolYear"
placeholder="请选择学年"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in schoolYearList"
:key="item.year"
:label="item.year"
:value="item.year">
</el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="学期" prop="schoolTerm"> <el-form-item label="学期" prop="schoolTerm">
<el-select v-model="queryForm.schoolTerm" placeholder="请选择学期" clearable style="width: 200px"> <el-select
<el-option v-for="item in schoolTermList" :key="item.value" :label="item.label" :value="item.value"> </el-option> v-model="queryForm.schoolTerm"
placeholder="请选择学期"
clearable
style="width: 200px">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="班级" prop="classCode"> <el-form-item label="班级" prop="classCode">
<el-select v-model="queryForm.classCode" placeholder="请选择班级" clearable filterable style="width: 200px"> <el-select
<el-option v-for="item in classList" :key="item.classCode" :label="item.classNo" :value="item.classCode"> </el-option> v-model="queryForm.classCode"
placeholder="请选择班级"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in classList"
:key="item.classCode"
:label="item.classNo"
:value="item.classCode">
</el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -74,7 +103,12 @@
</div> </div>
<!-- 查看详情弹窗接口queryDataByStuNo 通过学年学号查看详情按当前学期筛选 --> <!-- 查看详情弹窗接口queryDataByStuNo 通过学年学号查看详情按当前学期筛选 -->
<el-dialog v-model="viewDialogVisible" title="学期操行考核详情" width="800px" destroy-on-close @close="viewDetailList = []"> <el-dialog
v-model="viewDialogVisible"
title="学期操行考核详情"
width="800px"
destroy-on-close
@close="viewDetailList = []">
<div v-if="viewRow" class="view-summary"> <div v-if="viewRow" class="view-summary">
<el-descriptions :column="2" border size="small"> <el-descriptions :column="2" border size="small">
<el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item> <el-descriptions-item label="学号">{{ viewRow.stuNo }}</el-descriptions-item>
@@ -94,8 +128,7 @@
size="small" size="small"
max-height="400" max-height="400"
:cell-style="tableStyle.cellStyle" :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" :header-cell-style="tableStyle.headerCellStyle">
>
<el-table-column type="index" label="序号" width="60" align="center" /> <el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="schoolTerm" label="学期" width="80" align="center" show-overflow-tooltip /> <el-table-column prop="schoolTerm" label="学期" width="80" align="center" show-overflow-tooltip />
<el-table-column prop="recordDate" label="考核日期" width="110" align="center" show-overflow-tooltip /> <el-table-column prop="recordDate" label="考核日期" width="110" align="center" show-overflow-tooltip />
@@ -122,12 +155,12 @@
</template> </template>
<script setup lang="ts" name="StuConductTerm"> <script setup lang="ts" name="StuConductTerm">
import { reactive, ref, onMounted, computed } from 'vue'; import { reactive, ref, onMounted, computed } from 'vue'
import { getStuConductTerm, queryDataByStuNo, sendConductWarning } from '/@/api/stuwork/stuconduct'; import { getStuConductTerm, queryDataByStuNo, sendConductWarning } from "/@/api/stuwork/stuconduct";
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getClassListByRole } from "/@/api/basic/basicclass";
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from "/@/api/admin/dict";
import { useMessage, useMessageBox } from '/@/hooks/message'; import { useMessage, useMessageBox } from "/@/hooks/message";
// 表格样式 - 在组件内部定义,不从外部导入 // 表格样式 - 在组件内部定义,不从外部导入
const tableStyle = { const tableStyle = {
@@ -136,18 +169,18 @@ const tableStyle = {
}; };
// 定义变量内容 // 定义变量内容
const searchFormRef = ref(); const searchFormRef = ref()
const showSearch = ref(true); const showSearch = ref(true)
const loading = ref(false); const loading = ref(false)
const warningLoading = ref(false); const warningLoading = ref(false)
const schoolYearList = ref<any[]>([]); const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([]); const schoolTermList = ref<any[]>([])
const classList = ref<any[]>([]); const classList = ref<any[]>([])
const studentList = ref<any[]>([]); const studentList = ref<any[]>([])
const viewDialogVisible = ref(false); const viewDialogVisible = ref(false)
const viewLoading = ref(false); const viewLoading = ref(false)
const viewRow = ref<any>(null); const viewRow = ref<any>(null)
const viewDetailList = ref<any[]>([]); const viewDetailList = ref<any[]>([])
// 查询表单 // 查询表单
const queryForm = reactive({ const queryForm = reactive({

View File

@@ -254,34 +254,20 @@
</template> </template>
<script setup lang="ts" name="StuLeaveApply"> <script setup lang="ts" name="StuLeaveApply">
import { reactive, ref, onMounted, computed, nextTick } from 'vue'; import { reactive, ref, onMounted, computed, nextTick } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router'
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, exportData, cancelObj } from '/@/api/stuwork/stuleaveapply'; import { fetchList, cancelObj } from "/@/api/stuwork/stuleaveapply";
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { makeExportStudentLeaveTask } from "/@/api/stuwork/file";
import { getDicts } from '/@/api/admin/dict'; import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept'; import { getDicts } from "/@/api/admin/dict";
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
import { useMessage, useMessageBox } from '/@/hooks/message'; import { getClassListByRole } from "/@/api/basic/basicclass";
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import { import FormDialog from './form.vue'
List, import { List, Calendar, Clock, OfficeBuilding, Avatar, Collection, Document, House, Warning, CircleCheck, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
Calendar, import { useTableColumnControl } from '/@/hooks/tableColumn'
Clock,
OfficeBuilding,
Avatar,
Collection,
Document,
House,
Warning,
CircleCheck,
EditPen,
Setting,
Menu,
Search,
} from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue')); const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'));
@@ -449,22 +435,12 @@ const handleCancel = async (row: any) => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
const res = await exportData(searchForm); await makeExportStudentLeaveTask(searchForm)
// 处理返回的文件流 useMessage().success('导出任务已创建,请在文件管理中下载')
const blob = new Blob([res.data]);
const elink = document.createElement('a');
elink.download = '学生请假.xlsx';
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败')
}
} }
};
// 获取学年列表 // 获取学年列表
const getSchoolYearList = async () => { const getSchoolYearList = async () => {

View File

@@ -191,33 +191,20 @@
</template> </template>
<script setup lang="ts" name="StuPunlish"> <script setup lang="ts" name="StuPunlish">
import { reactive, ref, onMounted, computed, nextTick } from 'vue'; import { reactive, ref, onMounted, computed, nextTick } from 'vue'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router'
import { BasicTableProps, useTable } from '/@/hooks/table'; import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj, exportData, revokePunishment, getDetail } from '/@/api/stuwork/stupunlish'; import { fetchList, delObj, revokePunishment, getDetail } from "/@/api/stuwork/stupunlish";
import { queryAllSchoolYear } from '/@/api/basic/basicyear'; import { makeExportStudentDisciplineTask } from "/@/api/stuwork/file";
import { getDicts } from '/@/api/admin/dict'; import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDeptListByLevelTwo } from '/@/api/basic/basicdept'; import { getDicts } from "/@/api/admin/dict";
import { getClassListByRole } from '/@/api/basic/basicclass'; import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
import { useMessage, useMessageBox } from '/@/hooks/message'; import { getClassListByRole } from "/@/api/basic/basicclass";
import TableColumnControl from '/@/components/TableColumnControl/index.vue'; import { useMessage, useMessageBox } from "/@/hooks/message";
import FormDialog from './form.vue'; import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import { import FormDialog from './form.vue'
List, import { List, Calendar, Clock, OfficeBuilding, Grid, Avatar, CreditCard, Warning, Document, CircleCheck, Setting, Menu, Search } from '@element-plus/icons-vue'
Calendar, import { useTableColumnControl } from '/@/hooks/tableColumn'
Clock,
OfficeBuilding,
Grid,
Avatar,
CreditCard,
Warning,
Document,
CircleCheck,
Setting,
Menu,
Search,
} from '@element-plus/icons-vue';
import { useTableColumnControl } from '/@/hooks/tableColumn';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue')); const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'));
@@ -414,30 +401,20 @@ const handleCancel = async (row: any) => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
const params: any = { ...searchForm }; const params: any = { ...searchForm }
// 处理处分月份参数 API // 处理处分月份参数 API
if (params.punlishMonth) { if (params.punlishMonth) {
params.punlishMonth = [params.punlishMonth]; params.punlishMonth = [params.punlishMonth]
} else { } else {
delete params.punlishMonth; delete params.punlishMonth
} }
delete params.punlishMonthArray; delete params.punlishMonthArray
const res = await exportData(params); await makeExportStudentDisciplineTask(params)
// 下载文件 useMessage().success('导出任务已创建,请在文件管理中下载')
const blob = new Blob([res.data]);
const elink = document.createElement('a');
elink.download = '学生处分.xlsx';
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
useMessage().success('导出成功');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败'); useMessage().error(err.msg || '导出失败')
}
} }
};
// 获取学年列表 // 获取学年列表
const getSchoolYearList = async () => { const getSchoolYearList = async () => {

View File

@@ -257,7 +257,8 @@
import { reactive, ref, onMounted, computed, nextTick } from 'vue' import { reactive, ref, onMounted, computed, nextTick } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { BasicTableProps, useTable } from "/@/hooks/table"; import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj, exportData, cancelObj } from "/@/api/stuwork/stuturnover"; import { fetchList, delObj, cancelObj } from "/@/api/stuwork/stuturnover";
import { makeExportStudentChangeTask } from "/@/api/stuwork/file";
import { queryAllSchoolYear } from "/@/api/basic/basicyear"; import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from "/@/api/admin/dict"; import { getDicts } from "/@/api/admin/dict";
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept"; import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
@@ -438,18 +439,8 @@ const handleDelete = async (row: any) => {
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
try { try {
const res = await exportData(searchForm) await makeExportStudentChangeTask(searchForm)
// 处理返回的文件流 useMessage().success('导出任务已创建,请在文件管理中下载')
const blob = new Blob([res.data])
const elink = document.createElement('a')
elink.download = '学籍异动.xlsx'
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
useMessage().success('导出成功')
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '导出失败') useMessage().error(err.msg || '导出失败')
} }

View File

@@ -272,6 +272,7 @@ import { reactive, ref, onMounted, computed, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { BasicTableProps, useTable } from "/@/hooks/table"; import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs, initWaterOrder } from "/@/api/stuwork/waterdetail"; import { fetchList, delObjs, initWaterOrder } from "/@/api/stuwork/waterdetail";
import { makeExportDormWaterElectricityTask } from "/@/api/stuwork/file";
import { getBuildingList } from "/@/api/stuwork/dormbuilding"; import { getBuildingList } from "/@/api/stuwork/dormbuilding";
import { useMessage, useMessageBox } from "/@/hooks/message"; import { useMessage, useMessageBox } from "/@/hooks/message";
import TableColumnControl from '/@/components/TableColumnControl/index.vue' import TableColumnControl from '/@/components/TableColumnControl/index.vue'
@@ -410,9 +411,13 @@ const handleDelete = async (row: any) => {
} }
// 导出 // 导出
const handleExport = () => { const handleExport = async () => {
// TODO: 实现导出 try {
useMessage().warning('功能开发中') await makeExportDormWaterElectricityTask(searchForm)
useMessage().success('导出任务已创建,请在文件管理中下载')
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
} }
// 统计分析 // 统计分析