修复 登录授权问题

优化 直接调用数据中心API
This commit is contained in:
小肥羊 2025-08-14 18:20:28 +08:00
parent e1fdca95b8
commit f13a4261a1
14 changed files with 647 additions and 60 deletions

View File

@ -6,5 +6,7 @@ VITE_PUBLIC_PATH = /
# 开发环境路由历史模式Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数"
VITE_ROUTER_HISTORY = "hash"
# 接口地址
VITE_API_BASEURL = "http://localhost:5199/api"
#数据中心后台地址
VITE_API_USERCENTER_URL = "https://dca.w.23544.com:8843/api/back"

12
src/api/class.ts Normal file
View File

@ -0,0 +1,12 @@
import { http } from "@/utils/http";
import type { Res } from "@/utils/http/types";
/**
* @description
* @return {object}
*/
export function addClasses(info: any) {
return http.request<Res<any>>("post", `classes/addclass`, {
data: info
});
}

View File

@ -11,13 +11,9 @@ export class hTableAPI {
PageList(data = {}) {
return http.request<Res<any>>("post", `${this.url}/PageList`, { data });
}
Info(tag = {}) {
const pUrl = `${this.url}/Info`;
Info(tag) {
const pUrl = `${this.url}/${tag}`;
let getUrl = pUrl;
for (const key in tag) {
const el = tag[key];
getUrl += (getUrl === pUrl ? "?" : "&") + key + "=" + el;
}
return http.request<Res<any>>("get", getUrl);
}
edit(data) {

View File

@ -33,5 +33,5 @@ export function getregion(r) {
* @return {void}
*/
export function EditSchool(data: any) {
return http.request<Res<any>>("post", `School/Edit`, { data });
return http.request<Res<any>>("post", `schools/add`, { data });
}

View File

@ -118,7 +118,7 @@ function fetchFormData() {
editData.value.loading = false;
if (editData.value.isedit) {
handleResetForm();
Api.Info({ id: props.id }).then(res => {
Api.Info(props.id).then(res => {
if (res.code === 200) {
editData.value.frorm = res.data;
for (const key in column.value) {

View File

@ -13,6 +13,59 @@ import { stringify } from "qs";
import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
import { string } from "vue-types";
import router from "@/router";
/**请求后端的地址 未配置则访问BaseURL */
const apiServiceConfig = {
classes: import.meta.env.VITE_API_USERCENTER_URL,
schools: import.meta.env.VITE_API_USERCENTER_URL
};
function getAPIUrl(url: string): string {
let token = url.startsWith("/") ? url.split("/")[1] : url.split("/")[0];
if (apiServiceConfig[token] != null) return apiServiceConfig[token];
else return import.meta.env.VITE_API_BASEURL;
}
const snakeToCamel = (str: string): string => {
// 处理蛇形命名user_id → userId
let result = str.replace(/_([a-zA-Z0-9])/g, (_, letter) =>
letter.toUpperCase()
);
// 处理大驼峰命名UserName → userName
if (result.length > 0) {
result = result.charAt(0).toLowerCase() + result.slice(1);
}
// 特殊场景处理连续下划线__type → Type
result = result.replace(/^_+/, "");
return result;
};
/**
* /
*/
const convertKeysToCamelCase = <T>(data: any): T => {
if (Array.isArray(data)) {
return data.map(item => convertKeysToCamelCase(item)) as unknown as T;
}
if (data !== null && typeof data === "object") {
return Object.keys(data).reduce((result, key) => {
const camelKey = snakeToCamel(key);
const value = data[key];
return {
...result,
[camelKey]: convertKeysToCamelCase(value)
};
}, {}) as T;
}
return data as T;
};
// 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
@ -64,6 +117,9 @@ class PureHttp {
async (config: PureHttpRequestConfig): Promise<any> => {
// 开启进度条动画
NProgress.start();
if (config.url.indexOf("http") === -1) {
config.baseURL = getAPIUrl(config.url);
}
// 优先判断post/get等方法是否传入回调否则执行初始化设置等回调
if (typeof config.beforeRequestCallback === "function") {
config.beforeRequestCallback(config);
@ -124,6 +180,7 @@ class PureHttp {
const $config = response.config;
// 关闭进度条动画
NProgress.done();
response.data = convertKeysToCamelCase(response.data);
// 优先判断post/get等方法是否传入回调否则执行初始化设置等回调
if (typeof $config.beforeResponseCallback === "function") {
$config.beforeResponseCallback(response);
@ -140,6 +197,12 @@ class PureHttp {
$error.isCancelRequest = Axios.isCancel($error);
// 关闭进度条动画
NProgress.done();
if (error.response?.status === 403) {
// 跳转到403页面
router.push({
path: "/error/403"
});
}
// 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error);
}

View File

@ -9,7 +9,7 @@ import {
rulePassword,
rulePhone,
ruleRequired
} from "@/utils/roles";
} from "@/utils/rules";
const ControllerName = "Admin";
defineOptions({

347
src/views/class/edit.vue Normal file
View File

@ -0,0 +1,347 @@
<template>
<div>
<el-form
ref="classesAddForm"
:model="form"
:label-width="formLabelWidth"
clearable
>
<el-form-item label="学校" prop="SchoolId" :rules="ruleRequired">
<el-select
v-model="form.SchoolId"
filterable
placeholder="学校"
style="width: 100%"
>
<el-option
v-for="(item, id) in schoolList"
:key="id"
autocomplete="off"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="年级" prop="Grade" :rules="ruleRequired">
<el-select
v-model="form.Grade"
filterable
placeholder="年级"
style="width: 100%"
>
<el-option
v-for="(item, i) in gradeList"
:key="i"
autocomplete="off"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="班级类型" prop="Type" :rules="ruleRequired">
<el-select
v-model="form.Type"
filterable
placeholder="班级类型"
style="width: 100%"
>
<el-option
v-for="(item, i) in TypeList"
:key="i"
autocomplete="off"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="选修方向">
<el-col :span="24">
<div style="display: flex; gap: 10px">
<el-select
v-model="form.Elective1"
clearable
filterable
placeholder="历史/地理"
style="width: 180px"
>
<el-option
v-for="(item, id) in subject1"
:key="id"
:label="item.text"
:value="item.value"
/>
</el-select>
<el-select
v-model="form.Elective2"
clearable
filterable
placeholder="小学科"
style="width: 180px"
>
<el-option
v-for="(item, id) in subject2"
:key="id"
:label="item.text"
:value="item.value"
/>
</el-select>
<el-select
v-model="form.Elective3"
clearable
filterable
placeholder="小学科"
style="width: 180px"
>
<el-option
v-for="(item, id) in subject2"
:key="id"
:label="item.text"
:value="item.value"
/>
</el-select>
</div>
</el-col>
</el-form-item>
<el-form-item
label="班级名称"
v-if="isEdit"
prop="Name"
:rules="ruleRequired"
>
<el-select
v-model="form.Name"
filterable
placeholder="班级名称"
style="width: 100%"
>
<el-option
v-for="(item, i) in ClassNameList"
:key="i"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item label="添加班级" v-if="!isEdit">
<el-col :span="21">
<el-select
v-model="form.Name"
filterable
placeholder="班级名称"
style="width: 100%"
>
<el-option
v-for="(item, i) in filteredClassNameList"
:key="i"
:label="item"
:value="item"
/>
</el-select>
</el-col>
<el-col :span="1">
<el-button type="success" @click="ClassNameAdd" round>添加</el-button>
</el-col>
</el-form-item>
<el-form-item label="班级列表" :rules="ruleRequired" v-if="!isEdit">
<span v-show="ClassNames.length === 0">暂未添加!</span>
<el-tag
v-for="tag in ClassNames"
:key="tag"
closable
:disable-transitions="false"
@close="TagClose(tag)"
>
{{ tag }}
</el-tag>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleSubmitForm">
立即提交
</el-button>
<el-button @click="handleResetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import type { FormInstance } from "element-plus";
import { getenum } from "@/api/enum";
import { hTableAPI } from "@/api/hTable";
import { ruleRequired } from "@/utils/rules";
import { addClasses } from "@/api/class";
import { ComboModel } from "@/components/hTable/hTable";
interface Formdata {
Id: number;
Name: string;
SchoolId: string;
Grade: string;
Type: number;
Elective1: string;
Elective2: string;
Elective3: string;
}
const props = defineProps<{
id: number;
}>();
const emit = defineEmits(["handlePagedCallback"]);
//
const classesAddForm = ref<FormInstance>();
//
const form = ref<Formdata>({
Id: props.id,
Name: "",
SchoolId: "",
Grade: "",
Type: 0,
Elective1: "",
Elective2: "",
Elective3: ""
});
const subject1 = ref<ComboModel[]>([
{ value: 4, text: "物理" },
{ value: 8, text: "历史" }
]);
const subject2 = ref<ComboModel[]>([
{ value: 5, text: "化学" },
{ value: 6, text: "生物" },
{ value: 9, text: "地理" },
{ value: 7, text: "政治" }
]);
const formLabelWidth = "120px";
const size = "small";
const loading = ref(false);
const ClassNames = ref<string[]>([]);
const ClassNameList = ref<string[]>([]);
const schoolList = ref<ComboModel[]>([]);
const TypeList = ref<ComboModel[]>([]);
const gradeList = ref<ComboModel[]>([]);
//
const isEdit = computed(() => props.id > 0);
const filteredClassNameList = computed(() =>
ClassNameList.value.filter(s => !ClassNames.value.includes(s))
);
//
const TagClose = (tag: string) => {
ClassNames.value = ClassNames.value.filter(t => t !== tag);
};
const ClassNameAdd = () => {
if (form.value.Name === "") {
ElMessage.error("添加的班级名称不能为空");
return;
}
ClassNames.value.push(form.value.Name);
form.value.Name = "";
};
const handlePagedCallback = () => {
emit("handlePagedCallback");
};
const handleSubmitForm = async () => {
debugger;
if (props.id <= 0 || ClassNames.value.length < 1) {
ElMessage.error("班级列表为空!");
return;
}
if (!classesAddForm.value) return;
await classesAddForm.value.validate(valid => {
if (valid) {
loading.value = true;
let dataf = {
...form.value,
Id: props.id,
Name: ClassNames.value.join()
};
addClasses(dataf).then(res => {
loading.value = false;
if (res.code === 200) {
ElMessage.success("操作成功");
handlePagedCallback();
} else {
ElMessage.error(res.message);
}
});
}
});
};
const handleResetForm = () => {
ClassNames.value = [];
classesAddForm.value?.resetFields();
};
const SchoolApi = new hTableAPI("schools");
const fetchInitdata = async () => {
//
ClassNameList.value = Array.from({ length: 500 }, (_, i) => `${i + 1}`);
//
const gradeRes = await getenum("GradeEnum");
gradeList.value = gradeRes.data.map((s: ComboModel) => ({
text: s.text,
value: s.text
}));
//
SchoolApi.querycombo({ TextName: "Name", ValueName: "Id" }).then(res => {
if (res.code === 200) {
schoolList.value = res.data;
}
});
//
const typeRes = await getenum("ClassTypeEnum");
TypeList.value = typeRes.data;
};
const fetchFormdata = () => {
handleResetForm();
};
//
onMounted(() => {
fetchInitdata();
fetchFormdata();
});
// props
watch(
() => props.id,
newVal => {
form.value.Id = newVal;
fetchFormdata();
}
);
</script>
<style scoped>
.el-tag {
margin-top: 4px;
margin-right: 10px;
}
</style>

View File

@ -5,13 +5,14 @@ import { onMounted, ref } from "vue";
import { fa } from "element-plus/es/locales.mjs";
import { hTableAPI } from "@/api/hTable";
import { getenum } from "@/api/enum";
const ControllerName = "Class";
import { ruleRequired } from "@/utils/rules";
const ControllerName = "classes";
defineOptions({
name: ControllerName
});
const SchoolApi = new hTableAPI("School");
const SchoolApi = new hTableAPI("schools");
function searchCallback(data) {}
const table = ref<{ initTable: (config: TableConfig) => void }>();
@ -38,11 +39,17 @@ const tableData: TableConfig = {
btnType: "edit" // add edit del custom
},
{
//
topBtn: true, //
label: "添加",
btnStyle: "success",
btnType: "add" // add edit del custom
topBtn: true, //
label: "新增",
btnType: "custom", // add edit del custom
btnStyle: "success", // topBtn: true success danger
custom: {
// custom
title: "新增班级", // title
src: "class/edit", //
width: "550px", //
height: "520px" //
}
},
{
topBtn: false, //
@ -63,6 +70,7 @@ const tableData: TableConfig = {
},
schoolId: {
label: "学校",
rules: ruleRequired,
width: "180px",
search: true,
type: "dropdown",
@ -72,6 +80,7 @@ const tableData: TableConfig = {
},
name: {
label: "名称",
rules: ruleRequired,
width: "180px",
search: true,
searchType: "Like",
@ -80,6 +89,7 @@ const tableData: TableConfig = {
},
Grade: {
label: "年级",
rules: ruleRequired,
width: "180px",
type: "dropdown",
custom: row => `${row.grade ?? ""}`,
@ -89,31 +99,56 @@ const tableData: TableConfig = {
add: true, //
edit: false //
},
GradeLevel: {
label: "年级",
search: false,
type: "dropdown",
searchType: "Like", // Equal/NoEqual/Like/GreaterThan/LessThan/NoLike
add: true, //
edit: true, //
width: "70px",
setting: {
datasource: [
{ text: "初", value: "初" },
{ text: "高", value: "高" },
{ text: "小", value: "小" }
]
}
},
GraduationYear: {
label: "届",
search: false,
searchType: "Like", // Equal/NoEqual/Like/GreaterThan/LessThan/NoLike
add: true, //
edit: true, //
width: "80px"
},
type: {
label: "类型",
width: "150px",
rules: ruleRequired,
// width: "150px",
type: "dropdown",
search: true,
add: true, //
edit: true, //
setting: {}
},
createTime: {
label: "创建时间",
width: "180px",
type: "datetime",
search: true,
add: false, //
edit: false //
},
remark: {
label: "备注",
type: "textarea",
editRows: 3,
search: false,
add: true, //
edit: true //
}
// createTime: {
// label: "",
// width: "180px",
// type: "datetime",
// search: true,
// add: false, //
// edit: false //
// },
// remark: {
// label: "",
// type: "textarea",
// editRows: 3,
// search: false,
// add: true, //
// edit: true //
// }
},
data: [],
pageData: {
@ -126,8 +161,20 @@ const showTable = ref(false);
onMounted(async () => {
//
tableData.column.Grade.setting.datasource = (await getenum("GradeEnum")).data;
tableData.column.Grade.setting.datasource = [
{ text: "初一", value: "初一" },
{ text: "初二", value: "初二" },
{ text: "初三", value: "初三" },
{ text: "高一", value: "高一" },
{ text: "高二", value: "高二" },
{ text: "高三", value: "高三" },
{ text: "一年级", value: "一年级" },
{ text: "二年级", value: "二年级" },
{ text: "三年级", value: "三年级" },
{ text: "四年级", value: "四年级" },
{ text: "五年级", value: "五年级" },
{ text: "六年级", value: "六年级" }
];
tableData.column.type.setting.datasource = (
await getenum("ClassTypeEnum")
).data;

136
src/views/grade/index.vue Normal file
View File

@ -0,0 +1,136 @@
<script setup lang="ts">
import ahTable from "@/components/hTable/index.vue";
import { TableConfig } from "@/components/hTable/hTable";
import { onMounted, ref } from "vue";
import { fa } from "element-plus/es/locales.mjs";
import { hTableAPI } from "@/api/hTable";
import { getenum } from "@/api/enum";
import { ruleRequired } from "@/utils/rules";
const ControllerName = "Grade";
defineOptions({
name: ControllerName
});
const SchoolApi = new hTableAPI("School");
function searchCallback(data) {}
const table = ref<{ initTable: (config: TableConfig) => void }>();
const tableData: TableConfig = {
apiUrl: ControllerName,
selectColumn: false, //
border: false, //
searchCallback: searchCallback,
search: {
//
show: true,
PageIndex: 0,
PageSize: 20,
OrderBy: "CreateTime", //
defaultConditions: [], //
Conditions: []
},
operationColumn: true, //
operationColumnData: [
{
//
topBtn: false, //
label: "修改",
btnType: "edit" // add edit del custom
},
{
//
topBtn: true, //
label: "添加",
btnStyle: "success",
btnType: "add" // add edit del custom
},
{
topBtn: false, //
show: true,
label: "删除",
btnType: "del", // add edit del
btnStyle: "danger" // topBtn: true success danger
}
],
column: {
//
id: {
label: "编号",
search: true,
add: false, //
edit: false, //
width: "150px"
},
schoolId: {
label: "学校",
rules: ruleRequired,
width: "200px",
search: true,
type: "dropdown",
add: true, //
edit: true, //
setting: {}
},
name: {
label: "名称[动态]",
rules: ruleRequired,
width: "100px",
search: false,
searchType: "Like",
add: false, //
edit: false //
},
level: {
label: "年级",
rules: ruleRequired,
width: "80px",
type: "dropdown",
search: true,
setting: {},
add: true, //
edit: true //
},
year: {
label: "毕业届",
width: "80px",
rules: ruleRequired,
search: true,
setting: {},
add: true, //
edit: true //
},
createTime: {
label: "创建时间",
type: "datetime",
search: true,
add: false, //
edit: false //
}
},
data: [],
pageData: {
total: 0
},
selectRows: []
};
const showTable = ref(false);
onMounted(async () => {
//
tableData.column.level.setting.datasource = (
await getenum("GradeLevelEnum")
).data;
tableData.column.schoolId.setting.datasource = (
await SchoolApi.querycombo({ TextName: "Name", ValueName: "Id" })
).data;
showTable.value = true;
});
</script>
<template>
<div><ahTable v-if="showTable" ref="table" :tableConfig="tableData" /></div>
</template>

View File

@ -42,7 +42,7 @@
</el-button>
</div>
</div>
<div v-else class="menu-node">
<div v-else class="menu-node" style="background-color: #f7f7f7">
<div>
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
<span>菜单按钮权限: </span>

View File

@ -90,8 +90,6 @@
</el-switch>
</el-form-item>
<div style="padding-left: 4rem">
创建学校会生成一个默认管理员账号
<br />
<br />
</div>
<el-form-item>

View File

@ -8,19 +8,10 @@ defineOptions({
});
onMounted(() => {});
function searchCallback(data) {
let c = data.Conditions.find(s => s.FieldName === "Enable");
if (c) {
if (c.FieldValue == "true") {
c.FieldValue = 1;
} else {
c.FieldValue = 0;
}
}
}
function searchCallback(data) {}
const table = ref<{ initTable: (config: TableConfig) => void }>(null);
const tableData: TableConfig = {
apiUrl: "School",
apiUrl: "schools",
selectColumn: false, //
border: false, //
searchCallback: searchCallback,
@ -84,21 +75,16 @@ const tableData: TableConfig = {
width: "300px",
search: true,
custom: row => `${row.pname}-${row.cname}-${row.rname}`,
add: true, //
edit: true //
add: false, //
edit: false //
},
enable: {
label: "启用",
type: "dropdown",
type: "switch",
search: true,
custom: row => (row.enable ? "启用" : "禁用"),
add: true, //
edit: true, //
setting: {
datasource: [
{ value: "true", text: "√" },
{ value: "false", text: "X" }
]
}
edit: true //
}
},
data: [],