打分系统账户管理

This commit is contained in:
马梓彭 2026-08-10 22:26:55 +08:00
parent bc9d477d88
commit 712998124a
3 changed files with 451 additions and 0 deletions

View File

@ -0,0 +1,96 @@
import { http } from '../../utils/request';
// ==================== 单位管理(报名平台 / 打分系统账号) ====================
/**
* 单位账号分页查询
* @param {Object} params - 查询参数
* @param {number} params.Platform - 所属平台 (0=报名平台, 1=打分系统)
* @param {string} [params.UnitName] - 单位名称 (模糊查询)
* @param {string} [params.Account] - 账号 (模糊查询)
* @param {string} [params.StartTime] - 生成开始时间
* @param {string} [params.EndTime] - 生成结束时间
* @param {number} [params.PageNo] - 当前页码
* @param {number} [params.PageSize] - 每页条数
* @returns {Promise<{
* pageNo: number,
* pageSize: number,
* pages: number,
* total: number,
* rows: Array<{
* id: number,
* unitName: string,
* contactName: string,
* account: string,
* platform: number,
* status: number,
* createdTime: string
* }>
* }>}
*/
export const unitaccountPageGet = (params) => {
return http.get('/unitaccount/page', { params });
};
/**
* 单位账号详情
* @param {Object} params - 查询参数
* @param {number} params.id - 单位账号ID
*/
export const unitaccountDetailGet = (params) => {
return http.get('/unitaccount/detail', { params });
};
/**
* 添加单位账号
* @param {Object} data - 单位账号信息
* @param {number} data.platform - 所属平台 (0=报名平台, 1=打分系统)
* @param {string} data.unitName - 单位名称
* @param {string} data.contactName - 联系人
* @param {string} data.account - 账号
* @param {string} data.password - 密码 (6-32位)
*/
export const unitaccountAddPost = (data) => {
return http.post('/unitaccount/add', data);
};
/**
* 更新单位账号(不含密码,密码走重置接口)
* @param {Object} data - 单位账号信息
* @param {number} data.id - 单位账号ID
* @param {string} data.unitName - 单位名称
* @param {string} data.contactName - 联系人
* @param {string} data.account - 账号
*/
export const unitaccountEditPut = (data) => {
return http.put('/unitaccount/edit', data);
};
/**
* 重置单位账号密码
* @param {Object} data - 重置参数
* @param {number} data.id - 单位账号ID
* @param {string} data.password - 新密码 (6-32位)
*/
export const unitaccountResetPasswordPatch = (data) => {
return http.patch('/unitaccount/resetPassword', data);
};
/**
* 启用/禁用单位账号(禁用后禁止登录)
* @param {Object} data - 状态参数
* @param {number} data.id - 单位账号ID
* @param {number} data.status - 状态 (0=启用, 1=禁用)
*/
export const unitaccountSetstatusPatch = (data) => {
return http.patch('/unitaccount/setStatus', data);
};
/**
* 删除单位账号
* @param {Object} data - 删除参数
* @param {number} data.id - 单位账号ID
*/
export const unitaccountDeleteDelete = (data) => {
return http.delete('/unitaccount/delete', { data });
};

View File

@ -0,0 +1,144 @@
<template>
<div class="system-unit-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="600px" @close="onClose">
<el-form ref="unitDialogFormRef" :rules="rules" v-loading="state.dialog.loading" :model="state.ruleForm" size="default" label-width="100px">
<el-form-item label="单位名称:" prop="unitName">
<el-input v-model="state.ruleForm.unitName" placeholder="请输入单位名称" maxlength="128" clearable></el-input>
</el-form-item>
<el-form-item label="联系人:" prop="contactName">
<el-input v-model="state.ruleForm.contactName" placeholder="请输入联系人" maxlength="16" clearable></el-input>
</el-form-item>
<el-form-item label="账号:" prop="account">
<el-input v-model="state.ruleForm.account" placeholder="请输入账号" maxlength="32" clearable></el-input>
</el-form-item>
<el-form-item v-if="!state.ruleForm.id" label="密码:" prop="password">
<el-input v-model="state.ruleForm.password" placeholder="请输入密码" type="password" maxlength="32" show-password clearable></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default">取消</el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="state.dialog.submitting">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="systemUnitDialog">
import { reactive, ref, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { unitaccountAddPost, unitaccountEditPut, unitaccountDetailGet } from '/@/api/match/unitaccount';
const emit = defineEmits(['refresh']);
const unitDialogFormRef = ref();
const rules = reactive({
unitName: [{ required: true, message: '请输入单位名称', trigger: 'blur' }],
contactName: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 32, message: '密码长度为6-32位', trigger: 'blur' },
],
});
const defaultForm = () => ({
id: 0,
platform: 0,
unitName: '',
contactName: '',
account: '',
password: '',
});
const state = reactive({
ruleForm: defaultForm(),
dialog: {
isShowDialog: false,
title: '',
loading: false,
submitting: false,
},
});
// 打开弹窗:platform 为当前所在的平台页签,row 有值为编辑
const openDialog = async (platform, row) => {
state.ruleForm = defaultForm();
state.ruleForm.platform = platform;
state.dialog.isShowDialog = true;
state.dialog.title = row ? '编辑' : '新增';
if (row) {
state.dialog.loading = true;
const { succeeded, data } = await unitaccountDetailGet({ id: row.id });
if (succeeded) {
state.ruleForm = {
id: data.id,
platform: data.platform ?? platform,
unitName: data.unitName ?? '',
contactName: data.contactName ?? '',
account: data.account ?? '',
password: '',
};
}
state.dialog.loading = false;
} else {
nextTick(() => {
unitDialogFormRef.value?.clearValidate();
});
}
};
const closeDialog = () => {
state.dialog.isShowDialog = false;
};
const onClose = () => {
unitDialogFormRef.value?.resetFields();
state.ruleForm = defaultForm();
};
const onCancel = () => {
closeDialog();
};
const onSubmit = () => {
unitDialogFormRef.value?.validate(async (valid) => {
if (!valid) return;
state.dialog.submitting = true;
try {
const isEdit = !!state.ruleForm.id;
// 失败提示由 request.ts 响应拦截器统一弹出,此处只处理成功分支
const { succeeded } = isEdit
? await unitaccountEditPut({
id: state.ruleForm.id,
unitName: state.ruleForm.unitName,
contactName: state.ruleForm.contactName,
account: state.ruleForm.account,
})
: await unitaccountAddPost({
platform: state.ruleForm.platform,
unitName: state.ruleForm.unitName,
contactName: state.ruleForm.contactName,
account: state.ruleForm.account,
password: state.ruleForm.password,
});
if (succeeded) {
ElMessage.success(`${state.dialog.title}成功`);
closeDialog();
emit('refresh');
}
} finally {
state.dialog.submitting = false;
}
});
};
defineExpose({
openDialog,
});
</script>

View File

@ -0,0 +1,211 @@
<template>
<div class="system-unit-container layout-padding">
<el-tabs v-model="activeTab" class="unit-tabs">
<el-tab-pane label="报名平台" :name="UnitAccountPlatform.Registration" />
<el-tab-pane label="打分系统" :name="UnitAccountPlatform.Scoring" />
</el-tabs>
<ProTable
ref="tableRef"
:key="activeTab"
:request-api="unitaccountPageGet"
:columns="columns"
:init-param="initParam"
:pagination="true"
:tool-button="false"
row-key="id"
>
<template #tools>
<el-button v-auth="'system:unit:add'" type="primary" icon="ele-Plus" @click="onOpenDialog()"> 新增 </el-button>
</template>
<template #status="scope">
<el-tag :type="scope.row.status === 0 ? 'success' : 'danger'">
{{ scope.row.status === 0 ? '启用' : '禁用' }}
</el-tag>
</template>
<template #action="scope">
<el-button v-auth="'system:unit:status'" size="small" text :type="scope.row.status === 0 ? 'warning' : 'primary'" @click="onSetStatus(scope.row)">
{{ scope.row.status === 0 ? '禁用' : '启用' }}
</el-button>
<el-button v-auth="'system:unit:edit'" icon="ele-Edit" size="small" text type="primary" @click="onOpenDialog(scope.row)"> 编辑 </el-button>
<el-button v-auth="'system:unit:resetpwd'" icon="ele-Key" size="small" text type="primary" @click="onOpenResetPassword(scope.row)"> 重置密码 </el-button>
<el-popconfirm title="确认删除吗?" @confirm="onDelete(scope.row)">
<template #reference>
<el-button v-auth="'system:unit:delete'" icon="ele-Delete" size="small" text type="danger"> 删除 </el-button>
</template>
</el-popconfirm>
</template>
</ProTable>
<!-- 新增/编辑弹窗 -->
<UnitDialog ref="unitDialogRef" @refresh="refreshTable" />
<!-- 重置密码弹窗 -->
<el-dialog title="重置密码" v-model="isShowResetDialog" width="500px" @close="onResetDialogClose">
<el-form ref="resetFormRef" :model="resetForm" :rules="resetRules" label-width="100px" size="default">
<el-form-item label="单位名称:">
<el-input v-model="resetForm.unitName" disabled></el-input>
</el-form-item>
<el-form-item label="账号:">
<el-input v-model="resetForm.account" disabled></el-input>
</el-form-item>
<el-form-item label="新密码:" prop="password">
<el-input v-model="resetForm.password" placeholder="请输入新密码" type="password" maxlength="32" show-password clearable></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="isShowResetDialog = false">取消</el-button>
<el-button type="primary" @click="onSubmitResetPassword" :loading="resetSubmitting">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="systemunit">
import { computed, defineAsyncComponent, reactive, ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import ProTable from '/@/components/ProTable/index.vue';
import {
unitaccountPageGet,
unitaccountDeleteDelete,
unitaccountSetstatusPatch,
unitaccountResetPasswordPatch,
} from '/@/api/match/unitaccount';
const UnitDialog = defineAsyncComponent(() => import('/@/views/system/unit/dialog.vue'));
// 所属平台(与后端 UnitAccountPlatform 枚举一致)
const UnitAccountPlatform = {
Registration: 0, // 报名平台
Scoring: 1, // 打分系统
};
// 平台名称,用于禁用提示文案
const platformNames = {
[UnitAccountPlatform.Registration]: '报名平台',
[UnitAccountPlatform.Scoring]: '打分系统',
};
const tableRef = ref();
const unitDialogRef = ref();
const activeTab = ref(UnitAccountPlatform.Registration);
// Platform 为后端必填项;切换页签时 key 变化会重建表格,同时重置搜索条件与页码
const initParam = computed(() => ({ Platform: activeTab.value }));
const columns = ref([
{ prop: 'index', label: '序号', type: 'index', align: 'center', width: '80px' },
{ prop: 'unitName', label: '单位名称', search: { el: 'input', key: 'UnitName' } },
{ prop: 'contactName', label: '联系人' },
{ prop: 'account', label: '账号', search: { el: 'input', key: 'Account' } },
{ prop: 'status', label: '状态' },
{
prop: 'createdTime',
label: '生成时间',
search: {
el: 'date-picker',
props: { type: 'daterange', valueFormat: 'YYYY-MM-DD' },
},
},
{ prop: 'action', label: '操作', align: 'center', fixed: 'right', width: '280px' },
]);
const refreshTable = () => {
tableRef.value?.getTableList();
};
// 新增/编辑
const onOpenDialog = (row) => {
unitDialogRef.value.openDialog(activeTab.value, row);
};
// 启用/禁用
const onSetStatus = async (row) => {
const isDisable = row.status === 0;
const platformName = platformNames[row.platform ?? activeTab.value];
if (isDisable) {
try {
await ElMessageBox.confirm(`确认禁用后用户将禁止登录${platformName}。你还要继续吗?`, '这是进行一项操作时必须了解的重要信息', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
} catch {
return;
}
}
// 失败提示由 request.ts 响应拦截器统一弹出,此处只处理成功分支
const { succeeded } = await unitaccountSetstatusPatch({ id: row.id, status: isDisable ? 1 : 0 });
if (succeeded) {
ElMessage.success(isDisable ? '禁用成功' : '启用成功');
refreshTable();
}
};
// 删除
const onDelete = async (row) => {
const { succeeded } = await unitaccountDeleteDelete({ id: row.id });
if (succeeded) {
ElMessage.success('删除成功');
refreshTable();
}
};
// 重置密码
const isShowResetDialog = ref(false);
const resetSubmitting = ref(false);
const resetFormRef = ref();
const resetForm = reactive({
id: 0,
unitName: '',
account: '',
password: '',
});
const resetRules = reactive({
password: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 32, message: '密码长度为6-32位', trigger: 'blur' },
],
});
const onOpenResetPassword = (row) => {
resetForm.id = row.id;
resetForm.unitName = row.unitName;
resetForm.account = row.account;
resetForm.password = '';
isShowResetDialog.value = true;
};
const onResetDialogClose = () => {
resetFormRef.value?.resetFields();
};
const onSubmitResetPassword = () => {
resetFormRef.value?.validate(async (valid) => {
if (!valid) return;
resetSubmitting.value = true;
try {
const { succeeded } = await unitaccountResetPasswordPatch({ id: resetForm.id, password: resetForm.password });
if (succeeded) {
ElMessage.success('重置密码成功');
isShowResetDialog.value = false;
}
} finally {
resetSubmitting.value = false;
}
});
};
</script>
<style scoped lang="scss">
.system-unit-container {
.unit-tabs {
margin-bottom: 8px;
}
}
</style>