dev #8
|
|
@ -37,3 +37,13 @@ export function Edit(info: MenuItem) {
|
|||
export function Del(ids: number[]) {
|
||||
return http.request<Res<MenuItem[]>>("post", `Menu/Del`, { data: ids });
|
||||
}
|
||||
/**获取角色的菜单 */
|
||||
export function RoleMenu(roleId: number) {
|
||||
return http.request<Res<number[]>>("get", `MenuRelation/RoleMenu?roleId=${roleId}`);
|
||||
}
|
||||
/**修改角色菜单 */
|
||||
export function SetMenu(data: { roleId: number; menuId: number[] }) {
|
||||
return http.request<Res<MenuItem[]>>("post", `MenuRelation/SetMenu`, {
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,3 +56,24 @@ export function getSchoolBusinessPeopleListApi(data: object) {
|
|||
data
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description 导入excel
|
||||
* @return {object}
|
||||
*/
|
||||
export function importExcel(file: File) {
|
||||
let formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return http.request<any>(
|
||||
"post",
|
||||
`SchoolBusiness/Import`,
|
||||
{
|
||||
data: formData
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
responseType: "blob"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ const {
|
|||
userName,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
toggleSideBar
|
||||
toggleSideBar,
|
||||
} = useNav();
|
||||
</script>
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ const {
|
|||
<!-- 全屏 -->
|
||||
<LaySidebarFullScreen id="full-screen" />
|
||||
<!-- 消息通知 -->
|
||||
<LayNotice id="header-notice" />
|
||||
<LayNotice id="header-notice" v-show="false" />
|
||||
<!-- 退出登录 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||
|
|
@ -55,20 +55,13 @@ const {
|
|||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="logout">
|
||||
<IconifyIconOffline
|
||||
:icon="LogoutCircleRLine"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
<IconifyIconOffline :icon="LogoutCircleRLine" style="margin: 5px" />
|
||||
退出系统
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<span
|
||||
class="set-icon navbar-bg-hover"
|
||||
title="打开系统配置"
|
||||
@click="onPanel"
|
||||
>
|
||||
<span class="set-icon navbar-bg-hover" title="打开系统配置" @click="onPanel">
|
||||
<IconifyIconOffline :icon="Setting" />
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ const noticesNum = ref(0);
|
|||
const notices = ref(noticesData);
|
||||
const activeKey = ref(noticesData[0]?.key);
|
||||
|
||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
notices.value.map((v) => (noticesNum.value += v.list.length));
|
||||
|
||||
const getLabel = computed(
|
||||
() => item =>
|
||||
const getLabel = computed(() => (item) =>
|
||||
item.name + (item.list.length > 0 ? `(${item.list.length})` : "")
|
||||
);
|
||||
</script>
|
||||
|
|
@ -23,7 +22,7 @@ const getLabel = computed(
|
|||
'dropdown-badge',
|
||||
'navbar-bg-hover',
|
||||
'select-none',
|
||||
Number(noticesNum) !== 0 && 'mr-[10px]'
|
||||
Number(noticesNum) !== 0 && 'mr-[10px]',
|
||||
]"
|
||||
>
|
||||
<el-badge :value="Number(noticesNum) === 0 ? '' : noticesNum" :max="99">
|
||||
|
|
@ -40,11 +39,7 @@ const getLabel = computed(
|
|||
class="dropdown-tabs"
|
||||
:style="{ width: notices.length === 0 ? '200px' : '330px' }"
|
||||
>
|
||||
<el-empty
|
||||
v-if="notices.length === 0"
|
||||
description="暂无消息"
|
||||
:image-size="60"
|
||||
/>
|
||||
<el-empty v-if="notices.length === 0" description="暂无消息" :image-size="60" />
|
||||
<span v-else>
|
||||
<template v-for="item in notices" :key="item.key">
|
||||
<el-tab-pane :label="getLabel(item)" :name="`${item.key}`">
|
||||
|
|
|
|||
|
|
@ -4,16 +4,11 @@ import { ConditionalType, TableConfig } from "@/components/hTable/hTable";
|
|||
import { onMounted, ref, defineOptions } from "vue";
|
||||
import { fa } from "element-plus/es/locales.mjs";
|
||||
import { hTableAPI } from "@/api/hTable";
|
||||
import {
|
||||
ruleAccount,
|
||||
rulePassword,
|
||||
rulePhone,
|
||||
ruleRequired
|
||||
} from "@/utils/rules";
|
||||
import { ruleAccount, rulePassword, rulePhone, ruleRequired } from "@/utils/rules";
|
||||
const ControllerName = "Admin";
|
||||
|
||||
defineOptions({
|
||||
name: ControllerName
|
||||
name: ControllerName,
|
||||
});
|
||||
|
||||
function searchCallback(data) {
|
||||
|
|
@ -40,7 +35,7 @@ const tableData: TableConfig = {
|
|||
PageSize: 20,
|
||||
OrderBy: "CreateTime", // 排序
|
||||
defaultConditions: [], // 默认查询条件
|
||||
Conditions: []
|
||||
Conditions: [],
|
||||
},
|
||||
operationColumn: true, // 显示操作按钮
|
||||
operationColumnData: [
|
||||
|
|
@ -48,22 +43,22 @@ const tableData: TableConfig = {
|
|||
// 操作按钮
|
||||
topBtn: false, // 是头部按钮
|
||||
label: "修改",
|
||||
btnType: "edit" // 按钮类型 add edit del custom
|
||||
btnType: "edit", // 按钮类型 add edit del custom
|
||||
},
|
||||
{
|
||||
// 操作按钮
|
||||
topBtn: true, // 是头部按钮
|
||||
label: "添加",
|
||||
btnStyle: "success",
|
||||
btnType: "add" // 按钮类型 add edit del custom
|
||||
btnType: "add", // 按钮类型 add edit del custom
|
||||
},
|
||||
{
|
||||
topBtn: false, // 头部按钮
|
||||
show: true,
|
||||
label: "删除",
|
||||
btnType: "del", // 按钮类型 add edit del 不设置则 自定义按钮
|
||||
btnStyle: "danger" // topBtn: true才生效 success danger
|
||||
}
|
||||
btnStyle: "danger", // topBtn: true才生效 success danger
|
||||
},
|
||||
],
|
||||
column: {
|
||||
// 行数据
|
||||
|
|
@ -72,7 +67,7 @@ const tableData: TableConfig = {
|
|||
search: true,
|
||||
add: false, // 字段允许添加
|
||||
edit: false, // 字段允许修改
|
||||
width: "150px"
|
||||
width: "150px",
|
||||
},
|
||||
name: {
|
||||
label: "名称",
|
||||
|
|
@ -81,22 +76,22 @@ const tableData: TableConfig = {
|
|||
search: true,
|
||||
searchType: ConditionalType.Like,
|
||||
add: true, // 字段允许添加
|
||||
edit: true // 字段允许修改
|
||||
edit: true, // 字段允许修改
|
||||
},
|
||||
Phone: {
|
||||
phone: {
|
||||
label: "手机号",
|
||||
rules: rulePhone,
|
||||
width: "200px",
|
||||
search: true,
|
||||
add: true, // 字段允许添加
|
||||
edit: true // 字段允许修改
|
||||
edit: true, // 字段允许修改
|
||||
},
|
||||
account: {
|
||||
label: "账号",
|
||||
rules: ruleAccount,
|
||||
search: true,
|
||||
add: true, // 字段允许添加
|
||||
edit: false // 字段允许修改
|
||||
edit: false, // 字段允许修改
|
||||
},
|
||||
password: {
|
||||
label: "密码",
|
||||
|
|
@ -105,7 +100,7 @@ const tableData: TableConfig = {
|
|||
rules: rulePassword,
|
||||
search: false,
|
||||
add: true, // 字段允许添加
|
||||
edit: false // 字段允许修改
|
||||
edit: false, // 字段允许修改
|
||||
},
|
||||
enable: {
|
||||
label: "启用",
|
||||
|
|
@ -114,7 +109,7 @@ const tableData: TableConfig = {
|
|||
|
||||
add: true, // 字段允许添加
|
||||
edit: true, // 字段允许修改
|
||||
valueE: true // 编辑时的默认值
|
||||
valueE: true, // 编辑时的默认值
|
||||
},
|
||||
roleId: {
|
||||
label: "角色",
|
||||
|
|
@ -124,15 +119,15 @@ const tableData: TableConfig = {
|
|||
add: true, // 字段允许添加
|
||||
edit: false, // 字段允许修改
|
||||
setting: {
|
||||
datasource: []
|
||||
}
|
||||
}
|
||||
datasource: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
pageData: {
|
||||
total: 0
|
||||
total: 0,
|
||||
},
|
||||
selectRows: []
|
||||
selectRows: [],
|
||||
};
|
||||
|
||||
const showTable = ref(false);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { hTableAPI } from "@/api/hTable";
|
|||
const ControllerName = "AdminRole";
|
||||
|
||||
defineOptions({
|
||||
name: ControllerName
|
||||
name: ControllerName,
|
||||
});
|
||||
|
||||
function searchCallback(data) {}
|
||||
|
|
@ -24,7 +24,7 @@ const tableData: TableConfig = {
|
|||
PageSize: 20,
|
||||
OrderBy: "CreateTime", // 排序
|
||||
defaultConditions: [], // 默认查询条件
|
||||
Conditions: []
|
||||
Conditions: [],
|
||||
},
|
||||
operationColumn: true, // 显示操作按钮
|
||||
operationColumnData: [
|
||||
|
|
@ -34,28 +34,35 @@ const tableData: TableConfig = {
|
|||
label: "角色授权",
|
||||
perms: "角色授权", //按钮显示需要的权限码
|
||||
btnType: "custom", // 按钮类型 add edit del 不设置则 自定义按钮
|
||||
btnStyle: "success" // topBtn: true才生效 success danger
|
||||
btnStyle: "success", // topBtn: true才生效 success danger
|
||||
custom: {
|
||||
// 按钮类型 custom 专用
|
||||
title: "分配权限", // 弹出框title
|
||||
src: "menu/index", // 组件路径
|
||||
width: "1200px", // 弹框宽度
|
||||
height: "800px", // 弹框高度
|
||||
},
|
||||
},
|
||||
{
|
||||
// 操作按钮
|
||||
topBtn: false, // 是头部按钮
|
||||
label: "修改",
|
||||
btnType: "edit" // 按钮类型 add edit del custom
|
||||
btnType: "edit", // 按钮类型 add edit del custom
|
||||
},
|
||||
{
|
||||
// 操作按钮
|
||||
topBtn: true, // 是头部按钮
|
||||
label: "添加",
|
||||
btnStyle: "success",
|
||||
btnType: "add" // 按钮类型 add edit del custom
|
||||
btnType: "add", // 按钮类型 add edit del custom
|
||||
},
|
||||
{
|
||||
topBtn: false, // 头部按钮
|
||||
show: true,
|
||||
label: "删除",
|
||||
btnType: "del", // 按钮类型 add edit del 不设置则 自定义按钮
|
||||
btnStyle: "danger" // topBtn: true才生效 success danger
|
||||
}
|
||||
btnStyle: "danger", // topBtn: true才生效 success danger
|
||||
},
|
||||
],
|
||||
column: {
|
||||
// 行数据
|
||||
|
|
@ -64,7 +71,7 @@ const tableData: TableConfig = {
|
|||
search: true,
|
||||
add: false, // 字段允许添加
|
||||
edit: false, // 字段允许修改
|
||||
width: "150px"
|
||||
width: "150px",
|
||||
},
|
||||
name: {
|
||||
label: "角色名称",
|
||||
|
|
@ -72,7 +79,7 @@ const tableData: TableConfig = {
|
|||
search: true,
|
||||
searchType: ConditionalType.Like,
|
||||
add: true, // 字段允许添加
|
||||
edit: true // 字段允许修改
|
||||
edit: true, // 字段允许修改
|
||||
},
|
||||
enable: {
|
||||
label: "启用",
|
||||
|
|
@ -80,14 +87,14 @@ const tableData: TableConfig = {
|
|||
search: false,
|
||||
add: true, // 字段允许添加
|
||||
edit: true, // 字段允许修改
|
||||
valueE: true // 编辑时的默认值
|
||||
valueE: true, // 编辑时的默认值
|
||||
},
|
||||
createTime: {
|
||||
label: "创建时间",
|
||||
type: "datetime",
|
||||
search: true,
|
||||
add: false, // 字段允许添加
|
||||
edit: false // 字段允许修改
|
||||
edit: false, // 字段允许修改
|
||||
},
|
||||
remark: {
|
||||
label: "备注",
|
||||
|
|
@ -95,14 +102,14 @@ const tableData: TableConfig = {
|
|||
editRows: 3,
|
||||
search: false,
|
||||
add: true, // 字段允许添加
|
||||
edit: true // 字段允许修改
|
||||
}
|
||||
edit: true, // 字段允许修改
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
pageData: {
|
||||
total: 0
|
||||
total: 0,
|
||||
},
|
||||
selectRows: []
|
||||
selectRows: [],
|
||||
};
|
||||
|
||||
const showTable = ref(false);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const tableData: TableConfig = {
|
|||
btnStyle: "primary",
|
||||
custom: {
|
||||
title: "考试学生班级详情", // 弹出框title
|
||||
src: "exam/userDetails", // 组件路径
|
||||
src: "exam/classExamRecord", // 组件路径
|
||||
width: "1600px", // 弹框宽度
|
||||
height: "800px", // 弹框高度
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
<script setup lang="ts">
|
||||
import ahTable from "@/components/hTable/index.vue";
|
||||
import { ConditionalType, TableConfig } from "@/components/hTable/hTable";
|
||||
import { onMounted, ref, defineOptions } from "vue";
|
||||
import { fa } from "element-plus/es/locales.mjs";
|
||||
import { hTableAPI } from "@/api/hTable";
|
||||
import { getenum } from "@/api/enum";
|
||||
import { ruleRequired, ruleRequiredNumber } from "@/utils/rules";
|
||||
import { DeleteExamInfo, ImportExamInfo } from "@/api/exam";
|
||||
import { entryExamInfo } from "./examFun";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { average } from "@pureadmin/utils";
|
||||
const ControllerName = "ClassExamRecord";
|
||||
|
||||
defineOptions({
|
||||
name: ControllerName,
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
data: any;
|
||||
}>();
|
||||
|
||||
function searchCallback(data) {}
|
||||
const table = ref<{ initTable: (config: TableConfig) => void }>();
|
||||
const tableData: TableConfig = {
|
||||
apiUrl: `ExamClassInfo`,
|
||||
selectColumn: false, // 列表选择
|
||||
border: false, // 是否显示表格边框
|
||||
searchCallback: searchCallback,
|
||||
search: {
|
||||
// 查询条件
|
||||
show: true,
|
||||
PageIndex: 0,
|
||||
PageSize: 20,
|
||||
OrderByType: 1, // 排序方式
|
||||
OrderBy: "Id", // 排序
|
||||
defaultConditions: [
|
||||
{
|
||||
FieldName: "ClassId",
|
||||
FieldValue: props.data[0].classId + "",
|
||||
ConditionalType: ConditionalType.Equal,
|
||||
},
|
||||
], // 默认查询条件
|
||||
Conditions: [],
|
||||
},
|
||||
operationColumn: true, // 显示操作按钮
|
||||
operationColumnData: [
|
||||
{
|
||||
topBtn: false, // 头部按钮
|
||||
show: true,
|
||||
label: "学生成绩详情",
|
||||
btnType: "custom",
|
||||
btnStyle: "primary",
|
||||
custom: {
|
||||
title: "考试学生班级详情", // 弹出框title
|
||||
src: "exam/userDetails", // 组件路径
|
||||
width: "1600px", // 弹框宽度
|
||||
height: "880px", // 弹框高度
|
||||
},
|
||||
},
|
||||
],
|
||||
column: {
|
||||
// 行数据
|
||||
examName: {
|
||||
label: "考试名称",
|
||||
search: true,
|
||||
width: "150px",
|
||||
},
|
||||
type: {
|
||||
label: "考试类型",
|
||||
search: true,
|
||||
type: "dropdown",
|
||||
setting: {},
|
||||
width: "80px",
|
||||
},
|
||||
testPaperType: {
|
||||
label: "试卷类型",
|
||||
search: true,
|
||||
type: "dropdown",
|
||||
setting: {},
|
||||
width: "80px",
|
||||
},
|
||||
grade: {
|
||||
label: "年级",
|
||||
search: true,
|
||||
type: "dropdown",
|
||||
setting: {},
|
||||
width: "60px",
|
||||
},
|
||||
|
||||
onLineCount: {
|
||||
label: "重本人数",
|
||||
search: false,
|
||||
width: "80px",
|
||||
},
|
||||
onLineRate: {
|
||||
label: "重本率",
|
||||
search: false,
|
||||
custom: (row) => `${Math.round(row.onLineRate * 100)}%`,
|
||||
width: "80px",
|
||||
},
|
||||
onLineRanking: {
|
||||
label: "重本率排名",
|
||||
search: false,
|
||||
width: "100px",
|
||||
},
|
||||
maxScore: {
|
||||
label: "最高分[赋分]",
|
||||
search: false,
|
||||
width: "140px",
|
||||
},
|
||||
minScore: {
|
||||
label: "最低分[赋分]",
|
||||
search: false,
|
||||
width: "140px",
|
||||
},
|
||||
average: {
|
||||
label: "总平均分[赋分]",
|
||||
search: false,
|
||||
custom: (row) => `${Math.round(row.average)}`,
|
||||
width: "140px",
|
||||
},
|
||||
average1: {
|
||||
label: "资源校平均分[赋分]",
|
||||
search: false,
|
||||
width: "160px",
|
||||
},
|
||||
averageRank: {
|
||||
label: "总平均分排名",
|
||||
search: false,
|
||||
width: "110px",
|
||||
},
|
||||
rank: {
|
||||
label: "远端平均/资源校平均",
|
||||
search: false,
|
||||
width: "95px",
|
||||
custom: (row) =>
|
||||
`${row.average1 == 0 ? "--" : Math.round((row.average / row.average1) * 100)}%`,
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
pageData: {
|
||||
total: 0,
|
||||
},
|
||||
selectRows: [],
|
||||
};
|
||||
|
||||
const showTable = ref(false);
|
||||
onMounted(async () => {
|
||||
//初始化数据原
|
||||
|
||||
tableData.column.grade.setting.datasource = (await getenum("GradeEnum")).data;
|
||||
|
||||
tableData.column.testPaperType.setting.datasource = (
|
||||
await getenum("TestPaperTypeEnum")
|
||||
).data;
|
||||
tableData.column.type.setting.datasource = (await getenum("ExamTypeEnum")).data;
|
||||
showTable.value = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div><ahTable v-if="showTable" ref="table" :tableConfig="tableData" /></div>
|
||||
</template>
|
||||
|
|
@ -1,32 +1,34 @@
|
|||
<template>
|
||||
<div>
|
||||
<div style="padding-bottom: 5px">
|
||||
<el-button type="success" @click="() => showDialog(null)">
|
||||
<el-button type="success" v-show="!isAuthorized" @click="() => showDialog(null)">
|
||||
新增根菜单
|
||||
</el-button>
|
||||
<el-button type="primary"> 分配权限 </el-button>
|
||||
<el-button type="primary" v-show="isAuthorized" @click="callBack">
|
||||
分配权限
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
:props="treeProps"
|
||||
:default-checked-keys="defaultCheckedKeys"
|
||||
node-key="id"
|
||||
:default-expand-all="true"
|
||||
:highlight-current="true"
|
||||
:expand-on-click-node="false"
|
||||
show-checkbox
|
||||
class="menu-tree"
|
||||
ref="treeRef"
|
||||
:class="isAuthorized ? `menu-tree menu-tree1` : `menu-tree`"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div v-if="!data.isButton" class="menu-node">
|
||||
<div class="menu-node">
|
||||
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
|
||||
<span class="menu-title">{{ data.title }}</span>
|
||||
<span class="menu-path-Rank" v-if="data.rank"
|
||||
>排序[{{ data.rank }}]</span
|
||||
>
|
||||
<span class="menu-path-Rank" v-if="data.rank">排序[{{ data.rank }}]</span>
|
||||
<span class="menu-path" v-if="data.path">{{ data.path }}</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 6px">
|
||||
<div style="display: flex; gap: 6px" v-show="!isAuthorized">
|
||||
<el-button
|
||||
type="success"
|
||||
link
|
||||
|
|
@ -50,7 +52,7 @@
|
|||
{{ data.title }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 6px">
|
||||
<div style="display: flex; gap: 6px" v-show="!isAuthorized">
|
||||
<el-button type="primary" link @click="() => showDialog(data)">
|
||||
编辑
|
||||
</el-button>
|
||||
|
|
@ -78,19 +80,27 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { MenuAll, MenuItem, Del } from "@/api/menu";
|
||||
import { MenuAll, MenuItem, Del, RoleMenu, SetMenu } from "@/api/menu";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
import MenuEdit from "./edit.vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
defineOptions({
|
||||
name: "Menu"
|
||||
name: "Menu",
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
data: any;
|
||||
}>();
|
||||
const isAuthorized = props.data != null && props.data.length > 0;
|
||||
const treeRef = ref();
|
||||
|
||||
// 默认选中的节点ID数组
|
||||
const defaultCheckedKeys = ref([0]);
|
||||
/** 显示弹窗 */
|
||||
const dialogData = ref({
|
||||
visible: false,
|
||||
info: null
|
||||
info: null,
|
||||
});
|
||||
function showDialog(info: MenuItem) {
|
||||
dialogData.value.visible = true;
|
||||
|
|
@ -110,13 +120,36 @@ const handleClose = (done: () => void) => {
|
|||
// // catch error
|
||||
// });
|
||||
};
|
||||
async function callBack() {
|
||||
// 获取所有完全选中的节点
|
||||
const checkedNodes = treeRef.value.getCheckedNodes();
|
||||
// 获取所有半选中的节点(如果需要)
|
||||
const halfCheckedNodes = treeRef.value.getHalfCheckedNodes();
|
||||
//提交
|
||||
SetMenu({
|
||||
roleId: props.data[0].id,
|
||||
menuId: checkedNodes
|
||||
.map((node: MenuItem) => node.id)
|
||||
.concat(halfCheckedNodes.map((node: MenuItem) => node.id)),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("分配权限成功");
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
ElMessage.error("分配权限失败");
|
||||
});
|
||||
}
|
||||
|
||||
async function delMenu(menuId: number) {
|
||||
try {
|
||||
await ElMessageBox.confirm("确定要删除此菜单?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
});
|
||||
const res = await Del([menuId]);
|
||||
if (res.code === 200) {
|
||||
|
|
@ -134,12 +167,12 @@ const convertToTree = (menus: MenuItem[]): MenuItem[] => {
|
|||
const tree: MenuItem[] = [];
|
||||
|
||||
// 创建映射并初始化children
|
||||
menus.forEach(menu => {
|
||||
menus.forEach((menu) => {
|
||||
menuMap.set(menu.id, { ...menu, children: [] });
|
||||
});
|
||||
|
||||
// 构建树形结构
|
||||
menuMap.forEach(menu => {
|
||||
menuMap.forEach((menu) => {
|
||||
if (menu.parentId === 0) {
|
||||
tree.push(menu);
|
||||
} else {
|
||||
|
|
@ -153,7 +186,7 @@ const convertToTree = (menus: MenuItem[]): MenuItem[] => {
|
|||
// 对同级菜单排序
|
||||
const sortChildren = (nodes: MenuItem[]) => {
|
||||
nodes.sort((a, b) => a.rank - b.rank);
|
||||
nodes.forEach(node => {
|
||||
nodes.forEach((node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
sortChildren(node.children);
|
||||
}
|
||||
|
|
@ -170,7 +203,7 @@ const treeData = ref<MenuItem[]>([]);
|
|||
// 树组件配置
|
||||
const treeProps = {
|
||||
label: "title",
|
||||
children: "children"
|
||||
children: "children",
|
||||
};
|
||||
async function fetchInitData() {
|
||||
const flatData = await MenuAll();
|
||||
|
|
@ -179,6 +212,7 @@ async function fetchInitData() {
|
|||
} else {
|
||||
ElMessage.error(flatData.message);
|
||||
}
|
||||
if (isAuthorized) defaultCheckedKeys.value = (await RoleMenu(props.data[0].id)).data;
|
||||
}
|
||||
onMounted(async () => {
|
||||
await fetchInitData();
|
||||
|
|
@ -190,6 +224,11 @@ onMounted(async () => {
|
|||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
max-height: calc(88vh - 48px - 42px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.menu-tree1 {
|
||||
max-height: calc(88vh - 120px);
|
||||
}
|
||||
|
||||
.menu-node {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,7 @@
|
|||
</el-form-item> -->
|
||||
|
||||
<el-form-item v-show="search.userType === 1" style="width: 100px">
|
||||
<el-select
|
||||
v-model="search.level"
|
||||
placeholder="学生层次"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-select v-model="search.level" placeholder="学生层次" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in userLevelList"
|
||||
:key="item.value"
|
||||
|
|
@ -78,12 +73,7 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item style="width: 100px">
|
||||
<el-select
|
||||
v-model="search.classId"
|
||||
placeholder="班级"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-select v-model="search.classId" placeholder="班级" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in classList"
|
||||
:key="item.value"
|
||||
|
|
@ -95,12 +85,7 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item style="width: 100px">
|
||||
<el-select
|
||||
v-model="search.subjectId"
|
||||
placeholder="科目"
|
||||
clearable
|
||||
filterable
|
||||
>
|
||||
<el-select v-model="search.subjectId" placeholder="科目" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in subjectList"
|
||||
:key="item.value"
|
||||
|
|
@ -117,10 +102,7 @@
|
|||
>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="selectUser">
|
||||
<el-button
|
||||
type="success"
|
||||
@click="selectUserCallBack()"
|
||||
icon="el-icon-check"
|
||||
<el-button type="success" @click="selectUserCallBack()" icon="el-icon-check"
|
||||
>选择用户</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
|
|
@ -161,13 +143,9 @@
|
|||
<el-table-column prop="id" label="用户Id" width="100" />
|
||||
<el-table-column label="用户信息" width="200">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getUserTypeTag(scope.row.userType)"
|
||||
style="margin-right: 5px"
|
||||
>{{
|
||||
userTypeList.find(s => s.value == scope.row.userType)?.text
|
||||
}}</el-tag
|
||||
>
|
||||
<el-tag :type="getUserTypeTag(scope.row.userType)" style="margin-right: 5px">{{
|
||||
userTypeList.find((s) => s.value == scope.row.userType)?.text
|
||||
}}</el-tag>
|
||||
<span>{{ scope.row.realName }} </span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
@ -183,9 +161,7 @@
|
|||
<div
|
||||
v-for="(position, index) in scope.row.positions"
|
||||
:key="'Position' + index"
|
||||
v-show="
|
||||
index < 3 || (index >= 3 && showAllPosition.includes(scope.row))
|
||||
"
|
||||
v-show="index < 3 || (index >= 3 && showAllPosition.includes(scope.row))"
|
||||
>
|
||||
<div v-if="position.enable === false">
|
||||
<el-tag type="info">{{ position.schoolName || "-" }}</el-tag>
|
||||
|
|
@ -209,9 +185,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
scope.row.positions != undefined && scope.row.positions.length > 3
|
||||
"
|
||||
v-if="scope.row.positions != undefined && scope.row.positions.length > 3"
|
||||
@click="showPosition(scope.row)"
|
||||
class="userTagRow"
|
||||
>
|
||||
|
|
@ -227,6 +201,7 @@
|
|||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
style="display: flex; justify-content: center"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageIndexChange"
|
||||
:current-page="pagination.total + 1"
|
||||
|
|
@ -264,7 +239,7 @@ import {
|
|||
getSubjectData,
|
||||
getPageUserList,
|
||||
UserDetail,
|
||||
Position
|
||||
Position,
|
||||
} from "@/api/userCenter";
|
||||
import { getenum } from "@/api/enum";
|
||||
import { hTableAPI } from "@/api/hTable";
|
||||
|
|
@ -276,7 +251,7 @@ import {
|
|||
Message,
|
||||
ArrowDownBold,
|
||||
Search,
|
||||
Star
|
||||
Star,
|
||||
} from "@element-plus/icons-vue";
|
||||
import { ComboModel } from "@/components/hTable/hTable";
|
||||
|
||||
|
|
@ -344,20 +319,20 @@ interface DialogData {
|
|||
const props = defineProps({
|
||||
selectUser: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
selectCallBack: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
default: () => {},
|
||||
},
|
||||
maxTableHeight: {
|
||||
type: Number,
|
||||
default: 580
|
||||
default: 580,
|
||||
},
|
||||
searchData: {
|
||||
type: Object as () => SearchParams,
|
||||
default: undefined
|
||||
}
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
|
|
@ -378,13 +353,13 @@ const search = reactive<SearchParams>({
|
|||
grade: "",
|
||||
classId: "",
|
||||
subjectId: "",
|
||||
positionId: ""
|
||||
positionId: "",
|
||||
});
|
||||
|
||||
const userTypeList = ref<ComboModel[]>([
|
||||
{ value: 1, text: "学生" },
|
||||
{ value: 2, text: "教师" },
|
||||
{ value: 3, text: "管理员" }
|
||||
{ value: 3, text: "管理员" },
|
||||
]);
|
||||
|
||||
const userLevelList = ref<ComboModel[]>([]);
|
||||
|
|
@ -395,7 +370,7 @@ const gradeList = ref<ComboModel[]>([
|
|||
{ value: "初三", text: "初三" },
|
||||
{ value: "高一", text: "高一" },
|
||||
{ value: "高二", text: "高二" },
|
||||
{ value: "高三", text: "高三" }
|
||||
{ value: "高三", text: "高三" },
|
||||
]);
|
||||
const classList = ref<ComboModel[]>([]);
|
||||
const subjectList = ref<ComboModel[]>([]);
|
||||
|
|
@ -405,13 +380,13 @@ const table = reactive<TableData>({
|
|||
data: [],
|
||||
selectRows: [],
|
||||
sort: "",
|
||||
border: true
|
||||
border: true,
|
||||
});
|
||||
|
||||
const pagination = reactive<PaginationData>({
|
||||
index: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const dialog = reactive<DialogData>({
|
||||
|
|
@ -419,32 +394,32 @@ const dialog = reactive<DialogData>({
|
|||
update: {
|
||||
title: "",
|
||||
visible: false,
|
||||
width: "800px"
|
||||
width: "800px",
|
||||
},
|
||||
editLevel: {
|
||||
userIds: [],
|
||||
title: "",
|
||||
visible: false,
|
||||
width: "400px"
|
||||
width: "400px",
|
||||
},
|
||||
editSubjectLevel: {
|
||||
userIds: [],
|
||||
title: "",
|
||||
visible: false,
|
||||
width: "450px"
|
||||
width: "450px",
|
||||
},
|
||||
bindUser: {
|
||||
title: "分配权限码",
|
||||
visible: false,
|
||||
width: "1150px",
|
||||
height: ""
|
||||
height: "",
|
||||
},
|
||||
userBindInfo: {
|
||||
title: "用户权限码",
|
||||
visible: false,
|
||||
width: "1150px",
|
||||
height: ""
|
||||
}
|
||||
height: "",
|
||||
},
|
||||
});
|
||||
|
||||
const checkUserBindInfo = () => {
|
||||
|
|
@ -507,7 +482,7 @@ const exportUser = async () => {
|
|||
SubjectId: search.subjectId || 0,
|
||||
PositionId: search.positionId || 0,
|
||||
PageIndex: pagination.index,
|
||||
PageSize: pagination.size
|
||||
PageSize: pagination.size,
|
||||
};
|
||||
|
||||
// const res = await exportUserApi(data);
|
||||
|
|
@ -559,9 +534,9 @@ const getClass = () => {
|
|||
const data = {
|
||||
schoolId: search.schoolId || 0,
|
||||
graduationYear: search.graduationYear || 0,
|
||||
grade: search.grade
|
||||
grade: search.grade,
|
||||
};
|
||||
getClassCombo(data).then(res => {
|
||||
getClassCombo(data).then((res) => {
|
||||
if (res.code === 200) {
|
||||
classList.value = res.data;
|
||||
}
|
||||
|
|
@ -581,12 +556,12 @@ const fetchPagedData = (searchUnUse = false) => {
|
|||
PositionId: search.positionId || 0,
|
||||
PageIndex: pagination.index,
|
||||
PageSize: pagination.size,
|
||||
UnUsed: searchUnUse
|
||||
UnUsed: searchUnUse,
|
||||
};
|
||||
getPageUserList(data).then(res => {
|
||||
getPageUserList(data).then((res) => {
|
||||
if (res.code === 200) {
|
||||
pagination.total = res.data.total;
|
||||
res.data.data.forEach(item => {
|
||||
res.data.data.forEach((item) => {
|
||||
if (item.positions) {
|
||||
item.positions = PositionsSort(item.positions);
|
||||
}
|
||||
|
|
@ -666,7 +641,7 @@ const getUserLevelTag = (level: number) => {
|
|||
};
|
||||
|
||||
const getUserLevelText = (level: number) => {
|
||||
const r = userLevelList.value.filter(w => w.value === level);
|
||||
const r = userLevelList.value.filter((w) => w.value === level);
|
||||
if (r.length > 0) {
|
||||
return r[0].text;
|
||||
}
|
||||
|
|
@ -728,7 +703,7 @@ const handleEditLevel = () => {
|
|||
return;
|
||||
}
|
||||
dialog.editLevel.title = "修改学生层次";
|
||||
dialog.editLevel.userIds = table.selectRows.map(w => w.id);
|
||||
dialog.editLevel.userIds = table.selectRows.map((w) => w.id);
|
||||
dialog.editLevel.visible = true;
|
||||
};
|
||||
|
||||
|
|
@ -743,7 +718,7 @@ const handleEditSubjectLevel = () => {
|
|||
return;
|
||||
}
|
||||
dialog.editSubjectLevel.title = "修改学生科目层次";
|
||||
dialog.editSubjectLevel.userIds = table.selectRows.map(w => w.id);
|
||||
dialog.editSubjectLevel.userIds = table.selectRows.map((w) => w.id);
|
||||
dialog.editSubjectLevel.visible = true;
|
||||
};
|
||||
|
||||
|
|
@ -787,7 +762,7 @@ const importData = () => {
|
|||
};
|
||||
|
||||
const readerBlob = (data: Blob): Promise<any> => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(data, "utf-8");
|
||||
reader.onload = function () {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@
|
|||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
style="display: flex; justify-content: center"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageIndexChange"
|
||||
:current-page="pagination.total + 1"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="赴校时间">
|
||||
{{ safeDetail.startTime?.split("T")[0] }}
|
||||
{{ safeDetail.startTimeStr || safeDetail.startTime }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
<el-tab-pane
|
||||
v-for="(i, idx) in sortData(safeDetail.feedbackQuestions || [])"
|
||||
:key="idx"
|
||||
:label="'问题' + (idx + 1) + (i.solution ? '(已解决)' : '(未解决)')"
|
||||
:label="'问题' + (idx + 1) + (i.solution ? '(✅已解决)' : '(未解决)')"
|
||||
:name="idx"
|
||||
>
|
||||
<div style="font-size: 12px; margin-bottom: 4px">
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
{{ i.question }}
|
||||
</div>
|
||||
<div v-if="i.solution" style="font-size: 12px; margin-top: 10px">
|
||||
<span> 解决时间:{{ i.endTime.split("T")[0] }} </span>
|
||||
<span> 解决时间:{{ i.endTimeStr || i.endTime }} </span>
|
||||
<div style="padding: 10px; background-color: #f3f3f3">
|
||||
{{ i.solution }}
|
||||
</div>
|
||||
|
|
@ -117,15 +117,9 @@
|
|||
/>
|
||||
<el-divider />
|
||||
|
||||
<el-descriptions title="解决方案执行跟踪记录" :column="1" border>
|
||||
</el-descriptions>
|
||||
<el-descriptions title="解决方案执行跟踪记录" :column="1" border> </el-descriptions>
|
||||
<span>需求+解决方案</span>
|
||||
<el-input
|
||||
v-model="solutionText"
|
||||
:rows="4"
|
||||
type="textarea"
|
||||
:disabled="isDetail"
|
||||
/>
|
||||
<el-input v-model="solutionText" :rows="4" type="textarea" :disabled="isDetail" />
|
||||
|
||||
<!-- 添加按钮区域 -->
|
||||
<div style="margin-top: 5px; display: flex; gap: 20px">
|
||||
|
|
@ -163,7 +157,7 @@
|
|||
"
|
||||
>
|
||||
<div style="font-weight: bold; color: #409eff">
|
||||
执行记录{{ index + 1 }}:{{ record.time.split("T")[0] }}
|
||||
执行记录{{ index + 1 }}:{{ record.time }}
|
||||
</div>
|
||||
<div style="margin-top: 5px; white-space: pre-wrap">
|
||||
{{ record.content }}
|
||||
|
|
@ -183,7 +177,7 @@
|
|||
"
|
||||
>
|
||||
<div style="font-weight: bold; color: #a69400">
|
||||
完结情况:{{ finishRecord.time.split("T")[0] }}
|
||||
完结情况:{{ finishRecord.time }}
|
||||
</div>
|
||||
<div style="margin-top: 5px; white-space: pre-wrap">
|
||||
{{ finishRecord.content }}
|
||||
|
|
@ -194,12 +188,7 @@
|
|||
</el-dialog>
|
||||
|
||||
<!-- 操作弹窗 -->
|
||||
<el-dialog
|
||||
v-model="operationDialogVisible"
|
||||
title="操作"
|
||||
width="500px"
|
||||
align-center
|
||||
>
|
||||
<el-dialog v-model="operationDialogVisible" title="操作" width="500px" align-center>
|
||||
<el-form
|
||||
ref="operationFormRef"
|
||||
:model="operationForm"
|
||||
|
|
@ -239,10 +228,7 @@ import { ref, reactive, computed, defineProps, defineEmits, watch } from "vue";
|
|||
import type { FormInstance, FormRules, TabsPaneContext } from "element-plus";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getSchoolData } from "@/api/userCenter";
|
||||
import {
|
||||
getSchoolBusinessPeopleListApi,
|
||||
addOrEditApi
|
||||
} from "@/api/toschoolinfomanage";
|
||||
import { getSchoolBusinessPeopleListApi, addOrEditApi } from "@/api/toschoolinfomanage";
|
||||
import { setFips } from "crypto";
|
||||
|
||||
const activeName = ref<any>(0);
|
||||
|
|
@ -267,7 +253,7 @@ const operationDialogVisible = ref(false);
|
|||
const operationType = ref(""); // 操作类型:markSolved, addRecord, addFinish
|
||||
const operationForm = reactive({
|
||||
operationTime: "",
|
||||
operationContent: ""
|
||||
operationContent: "",
|
||||
});
|
||||
const operationFormRef = ref<FormInstance>();
|
||||
|
||||
|
|
@ -278,19 +264,19 @@ const finishRecord = ref<{ time: string; content: string } | null>(null);
|
|||
// 从父级 detailData.solutionRecord 回显本地显示数据
|
||||
watch(
|
||||
() => props.detailData,
|
||||
val => {
|
||||
(val) => {
|
||||
const sr = (val as any)?.solutionRecord || {};
|
||||
// 执行记录回显
|
||||
const recs = Array.isArray(sr?.record) ? sr.record : [];
|
||||
executionRecords.value = recs.map((r: any) => ({
|
||||
time: r?.executionTime || "",
|
||||
content: r?.executionRecords || ""
|
||||
time: r?.executionTimeStr || r?.executionTime || "",
|
||||
content: r?.executionRecords || "",
|
||||
}));
|
||||
// 完结情况回显
|
||||
if (sr?.endRecordTime || sr?.endRecord) {
|
||||
finishRecord.value = {
|
||||
time: sr?.endRecordTime || "",
|
||||
content: sr?.endRecord || ""
|
||||
time: sr?.endRecordTimeStr || "",
|
||||
content: sr?.endRecord || "",
|
||||
};
|
||||
} else {
|
||||
finishRecord.value = null;
|
||||
|
|
@ -318,21 +304,19 @@ const operationContentLabel = computed(() => {
|
|||
|
||||
// 表单验证规则
|
||||
const operationRules: FormRules = {
|
||||
operationTime: [
|
||||
{ required: true, message: "请选择操作时间", trigger: "change" }
|
||||
],
|
||||
operationTime: [{ required: true, message: "请选择操作时间", trigger: "change" }],
|
||||
operationContent: [
|
||||
{
|
||||
required: true,
|
||||
message: `请输入${operationContentLabel.value}`,
|
||||
trigger: "blur"
|
||||
}
|
||||
]
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: v => emit("update:visible", v)
|
||||
set: (v) => emit("update:visible", v),
|
||||
});
|
||||
|
||||
const closeModal = () => {
|
||||
|
|
@ -344,39 +328,37 @@ const queType = {
|
|||
10: "双师课堂",
|
||||
15: "设备",
|
||||
20: "学生",
|
||||
999: "其他"
|
||||
999: "其他",
|
||||
};
|
||||
/**
|
||||
* 获取未解决问题数量
|
||||
* @param data
|
||||
*/
|
||||
const handleUnHandleQust = (data: Array<any>) => {
|
||||
return (data || []).filter(i => !i?.solution).length;
|
||||
return (data || []).filter((i) => !i?.solution).length;
|
||||
};
|
||||
const sortData = (data: Array<any>) => {
|
||||
const categorizedData = [
|
||||
...data
|
||||
.filter(item => item.questionType === 1)
|
||||
.filter((item) => item.questionType === 1)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort)),
|
||||
...data
|
||||
.filter(item => item.questionType === 10)
|
||||
.filter((item) => item.questionType === 10)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort)),
|
||||
...data
|
||||
.filter(item => item.questionType === 15)
|
||||
.filter((item) => item.questionType === 15)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort)),
|
||||
...data
|
||||
.filter(item => item.questionType === 20)
|
||||
.filter((item) => item.questionType === 20)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort)),
|
||||
...data
|
||||
.filter(item => item.questionType === 999)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort))
|
||||
.filter((item) => item.questionType === 999)
|
||||
.sort((a, b) => a.sort.localeCompare(b.sort)),
|
||||
];
|
||||
return categorizedData;
|
||||
};
|
||||
const safeDetail = computed(() => props.detailData || {});
|
||||
const statusText = computed(() =>
|
||||
safeDetail.value?.solutionEnd ? "已完结" : "跟进中"
|
||||
);
|
||||
const statusText = computed(() => (safeDetail.value?.solutionEnd ? "已完结" : "跟进中"));
|
||||
const statusType = computed(() =>
|
||||
safeDetail.value?.solutionEnd ? "success" : "warning"
|
||||
);
|
||||
|
|
@ -387,13 +369,13 @@ const solutionText = computed({
|
|||
safeDetail.value.solutionRecord = {};
|
||||
}
|
||||
safeDetail.value.solutionRecord.solution = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 未解决问题数量(依赖每个问题项的 solution 字段,确保标记已解决后自动更新)
|
||||
const unresolvedCount = computed(() => {
|
||||
const list = (safeDetail.value?.feedbackQuestions as any[]) || [];
|
||||
return list.filter(item => !item?.solution).length;
|
||||
return list.filter((item) => !item?.solution).length;
|
||||
});
|
||||
const markTitle = (data: any) => {
|
||||
console.log("标记已解决", data);
|
||||
|
|
@ -416,7 +398,7 @@ const addFinish = () => {
|
|||
const sr = (props.detailData as any)?.solutionRecord;
|
||||
// 优先从父级已有完结情况预填,其次使用本地 finishRecord
|
||||
if (sr && (sr.endRecordTime || sr.endRecord)) {
|
||||
operationForm.operationTime = sr.endRecordTime || "";
|
||||
operationForm.operationTime = sr.endRecordTimeStr || sr.endRecordTime || "";
|
||||
operationForm.operationContent = sr.endRecord || "";
|
||||
} else if (finishRecord.value) {
|
||||
operationForm.operationTime = finishRecord.value.time;
|
||||
|
|
@ -455,27 +437,25 @@ const confirmOperation = async () => {
|
|||
// 添加执行记录
|
||||
executionRecords.value.push({
|
||||
time: operationTime,
|
||||
content: operationContent
|
||||
content: operationContent,
|
||||
});
|
||||
// 同步到父数据 solutionRecord.record
|
||||
if (!props.detailData.solutionRecord)
|
||||
props.detailData.solutionRecord = {} as any;
|
||||
if (!props.detailData.solutionRecord) props.detailData.solutionRecord = {} as any;
|
||||
if (!Array.isArray(props.detailData.solutionRecord.record))
|
||||
props.detailData.solutionRecord.record = [];
|
||||
props.detailData.solutionRecord.record.push({
|
||||
executionTime: operationTime,
|
||||
executionRecords: operationContent
|
||||
executionRecords: operationContent,
|
||||
});
|
||||
break;
|
||||
case "addFinish":
|
||||
// 添加或修改完结情况
|
||||
finishRecord.value = {
|
||||
time: operationTime,
|
||||
content: operationContent
|
||||
content: operationContent,
|
||||
};
|
||||
// 同步到父数据 solutionRecord.endRecordTime / endRecord
|
||||
if (!props.detailData.solutionRecord)
|
||||
props.detailData.solutionRecord = {} as any;
|
||||
if (!props.detailData.solutionRecord) props.detailData.solutionRecord = {} as any;
|
||||
props.detailData.solutionRecord.endRecordTime = operationTime;
|
||||
props.detailData.solutionRecord.endRecord = operationContent;
|
||||
break;
|
||||
|
|
@ -491,7 +471,7 @@ const confirmOperation = async () => {
|
|||
console.log("确认操作", {
|
||||
type: operationType.value,
|
||||
time: operationTime,
|
||||
content: operationContent
|
||||
content: operationContent,
|
||||
});
|
||||
closeOperationDialog();
|
||||
} catch (error) {
|
||||
|
|
@ -514,7 +494,7 @@ function onClickSave() {
|
|||
console.log("保存", props.detailData);
|
||||
let copyParams = JSON.parse(JSON.stringify(props.detailData));
|
||||
delete copyParams.solutionEnd;
|
||||
addOrEditApi(copyParams).then(res => {
|
||||
addOrEditApi(copyParams).then((res) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("提交成功");
|
||||
// 关闭弹窗
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div style="padding: 20px">
|
||||
<div>
|
||||
<!-- 搜索区域 -->
|
||||
<el-form :model="query" inline label-width="80px" class="search-form">
|
||||
<el-form :model="query" inline class="search-form">
|
||||
<el-form-item label="学校">
|
||||
<el-select
|
||||
v-model="query.school"
|
||||
|
|
@ -72,11 +72,14 @@
|
|||
style="width: 300px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item> </el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<div style="margin-bottom: 15px">
|
||||
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</div>
|
||||
<!-- 操作按钮区域 -->
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button type="primary" @click="handleAdd">新建</el-button>
|
||||
|
|
@ -85,30 +88,8 @@
|
|||
<el-button type="info" @click="downLoadTpl">下载模版</el-button>
|
||||
</div>
|
||||
<!-- 表格区域 -->
|
||||
<el-table :data="listData" border style="width: 100%">
|
||||
<el-table-column prop="school" label="学校" min-width="140" />
|
||||
<el-table-column prop="grade" label="年级" min-width="100" />
|
||||
<el-table-column prop="people" label="赴校人员" min-width="120" />
|
||||
<el-table-column prop="times" label="赴校时间" min-width="140" />
|
||||
<el-table-column
|
||||
prop="feedbackTotals"
|
||||
label="反馈问题数量"
|
||||
min-width="140"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="solveTotals"
|
||||
label="解决问题数量"
|
||||
min-width="140"
|
||||
/>
|
||||
<el-table-column label="状态" min-width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.solutionEnd ? 'success' : 'warning'">
|
||||
{{ row.solutionEnd ? "已完结" : "跟进中" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="lastTime" label="最后跟进时间" min-width="160" /> -->
|
||||
<el-table-column label="操作" fixed="right" min-width="220">
|
||||
<el-table :data="listData" style="width: 100%">
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="{ row }">
|
||||
<!-- <el-button size="small" type="danger" plain @click="onDelete(row)"
|
||||
>删除</el-button
|
||||
|
|
@ -121,27 +102,38 @@
|
|||
@confirm="onDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
<el-button type="danger" text size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
@click="onDetailOrFollow(row, true)"
|
||||
<el-button size="small" type="primary" text @click="onDetailOrFollow(row, true)"
|
||||
>详情</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="!row.solutionEnd"
|
||||
v-if="row.canOperate && !row.solutionEnd"
|
||||
size="small"
|
||||
type="success"
|
||||
plain
|
||||
text
|
||||
@click="onDetailOrFollow(row, false)"
|
||||
>跟进</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="school" label="学校" min-width="140" />
|
||||
<el-table-column label="状态" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.solutionEnd ? 'success' : 'warning'">
|
||||
{{ row.solutionEnd ? "已完结" : "跟进中" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="grade" label="年级" min-width="100" />
|
||||
<el-table-column prop="people" label="赴校人员" min-width="120" />
|
||||
<el-table-column prop="times" label="赴校时间" min-width="140" />
|
||||
<el-table-column prop="feedbackTotals" label="反馈问题数量" min-width="140" />
|
||||
<el-table-column prop="solveTotals" label="解决问题数量" min-width="140" />
|
||||
|
||||
<!-- <el-table-column prop="lastTime" label="最后跟进时间" min-width="160" /> -->
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
|
@ -157,7 +149,7 @@
|
|||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新建 -->
|
||||
<AddModal v-model:visible="isShowAddModal" @handleReset="handleReset" />
|
||||
<!-- 跟进 -->
|
||||
|
|
@ -168,6 +160,7 @@
|
|||
:isDetail="isDetail"
|
||||
@handleReset="handleReset"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 赴校信息管理菜单 -->
|
||||
<script setup lang="ts" name="Toschoolinfomanage">
|
||||
|
|
@ -176,13 +169,15 @@ import {
|
|||
getPageListApi,
|
||||
getSchoolBusinessDetailApi,
|
||||
deleteSchoolBusinessApi,
|
||||
getSchoolBusinessPeopleListApi
|
||||
getSchoolBusinessPeopleListApi,
|
||||
importExcel,
|
||||
} from "@/api/toschoolinfomanage";
|
||||
import { getSchoolData } from "@/api/userCenter";
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { ElMessage } from "element-plus";
|
||||
import AddModal from "./addModal.vue";
|
||||
import { Check, Search } from "@element-plus/icons-vue";
|
||||
import EditModal from "./editModal.vue";
|
||||
import { message } from "@/utils/message";
|
||||
interface TableItem {
|
||||
|
|
@ -190,6 +185,7 @@ interface TableItem {
|
|||
school: string;
|
||||
grade: string;
|
||||
people: string;
|
||||
canOperate: boolean; // 是否可以跟进
|
||||
times: string; // YYYY-MM-DD
|
||||
feedbackTotals: number;
|
||||
solveTotals: number;
|
||||
|
|
@ -204,11 +200,11 @@ const isDetail = ref(false);
|
|||
* 获取学校下拉数据
|
||||
*/
|
||||
const getSchoolDataFn = () => {
|
||||
getSchoolData().then(res => {
|
||||
getSchoolData().then((res) => {
|
||||
if (res.code == 200) {
|
||||
schoolOptions.value = res.data.map((i: any) => ({
|
||||
label: i.text,
|
||||
value: i.value
|
||||
value: i.value,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
|
@ -219,10 +215,10 @@ const getSchoolDataFn = () => {
|
|||
const getSchoolBusinessPeopleList = () => {
|
||||
getSchoolBusinessPeopleListApi({}).then((res: any) => {
|
||||
if (res.code == 200) {
|
||||
peopleOptions.value = (res.data || []).map(i => {
|
||||
peopleOptions.value = (res.data || []).map((i) => {
|
||||
return {
|
||||
value: i.text,
|
||||
text: i.text
|
||||
text: i.text,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
@ -245,7 +241,7 @@ const gradeOptions = [
|
|||
{ label: "初三", value: "初三" },
|
||||
{ label: "高一", value: "高一" },
|
||||
{ label: "高二", value: "高二" },
|
||||
{ label: "高三", value: "高三" }
|
||||
{ label: "高三", value: "高三" },
|
||||
];
|
||||
/**
|
||||
* 新建赴校信息提交
|
||||
|
|
@ -264,8 +260,8 @@ const addOrEdit = () => {
|
|||
{
|
||||
question: "xb测试反馈问题1(双师课堂)",
|
||||
questionType: 10,
|
||||
sort: "1111111111"
|
||||
}
|
||||
sort: "1111111111",
|
||||
},
|
||||
// {
|
||||
// question: "xb测试反馈问题2(设备)",
|
||||
// questionType: 15,
|
||||
|
|
@ -280,7 +276,7 @@ const addOrEdit = () => {
|
|||
isDiscussion: true,
|
||||
discussion: "开展座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈",
|
||||
isClassMeeting: true,
|
||||
classMeeting: "班会情况班会情况班会情况班会情况班会情况班会情况班会情况"
|
||||
classMeeting: "班会情况班会情况班会情况班会情况班会情况班会情况班会情况",
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -289,7 +285,7 @@ const query = reactive({
|
|||
grade: "" as string | undefined,
|
||||
people: "" as string | undefined,
|
||||
solutionEnd: undefined,
|
||||
times: [] as string[]
|
||||
times: [] as string[],
|
||||
});
|
||||
const page = ref(1);
|
||||
const pageSize = ref(10);
|
||||
|
|
@ -297,12 +293,8 @@ const total = ref(0);
|
|||
const listData = ref<TableItem[]>([]);
|
||||
|
||||
function mapApiItemToRow(item: any): TableItem {
|
||||
const peopleArr = Array.isArray(item.schoolBusinessUser)
|
||||
? item.schoolBusinessUser
|
||||
: [];
|
||||
const start = item.startTime
|
||||
? dayjs(item.startTime).format("YYYY-MM-DD")
|
||||
: "";
|
||||
const peopleArr = Array.isArray(item.schoolBusinessUser) ? item.schoolBusinessUser : [];
|
||||
const start = item.startTime ? dayjs(item.startTime).format("YYYY-MM-DD") : "";
|
||||
let last = start;
|
||||
const rec = item.solutionRecord?.record || [];
|
||||
if (Array.isArray(rec) && rec.length > 0) {
|
||||
|
|
@ -319,10 +311,11 @@ function mapApiItemToRow(item: any): TableItem {
|
|||
grade: item.grade || "",
|
||||
people: peopleArr.join(","),
|
||||
times: start,
|
||||
canOperate: item.canOperate || false, // 是否可以跟进
|
||||
feedbackTotals: Number(item.feedbackCount) || 0,
|
||||
solveTotals: Number(item.solveFeedbackCount) || 0,
|
||||
solutionEnd: item.solutionEnd,
|
||||
lastTime: last
|
||||
lastTime: last,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -330,15 +323,14 @@ async function loadList() {
|
|||
const payload: any = {
|
||||
pageIndex: page.value,
|
||||
pageSize: pageSize.value,
|
||||
orderBy: "startTime"
|
||||
orderBy: "startTime",
|
||||
};
|
||||
if (query.school) payload.schoolId = query.school;
|
||||
if (query.grade) payload.grade = query.grade;
|
||||
if (query.people) {
|
||||
payload.UserName = query.people;
|
||||
}
|
||||
if (typeof query.solutionEnd !== "undefined")
|
||||
payload.solutionEnd = query.solutionEnd;
|
||||
if (typeof query.solutionEnd !== "undefined") payload.solutionEnd = query.solutionEnd;
|
||||
if (Array.isArray(query.times) && query.times.length === 2) {
|
||||
payload.startTime = query.times[0];
|
||||
payload.endTime = query.times[1];
|
||||
|
|
@ -396,7 +388,7 @@ function handleSizeChange(s: number) {
|
|||
|
||||
function onDelete(row: TableItem) {
|
||||
console.log(`删除`, row);
|
||||
deleteSchoolBusinessApi([row.id]).then(res => {
|
||||
deleteSchoolBusinessApi([row.id]).then((res) => {
|
||||
if (res.code === 200) {
|
||||
message("删除成功", { type: "success" });
|
||||
loadList();
|
||||
|
|
@ -412,7 +404,7 @@ function onDetailOrFollow(row: TableItem, disabled = false) {
|
|||
isShowEditModal.value = true;
|
||||
editModalLoading.value = true;
|
||||
getSchoolBusinessDetailApi(row.id)
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
detailData.value = res.data;
|
||||
}
|
||||
|
|
@ -446,23 +438,63 @@ function handleAdd() {
|
|||
}
|
||||
function handleImport() {
|
||||
console.log("批量导入");
|
||||
|
||||
let fileE = document.createElement("input");
|
||||
fileE.type = "file";
|
||||
var formData = new window.FormData();
|
||||
fileE.onchange = async function () {
|
||||
formData.append("file", fileE.files[0]);
|
||||
let res = await importExcel(fileE.files[0]);
|
||||
if (res.code != undefined) {
|
||||
if (res.code !== 200) return ElMessage.error(res.message);
|
||||
else return ElMessage.success("所有数据录入成功");
|
||||
} else if (res.type === "application/json") {
|
||||
let json = await res.text();
|
||||
if (json !== undefined && json.Code !== 200) {
|
||||
return ElMessage.error(json.Message);
|
||||
} else {
|
||||
return ElMessage.success("操所有数据录入成功作成功");
|
||||
}
|
||||
} else if (res === undefined || res.size === 0)
|
||||
return ElMessage.success("所有数据录入成功");
|
||||
const url = res && window.URL.createObjectURL(res);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.setAttribute("download", "未成功导入的考试信息数据" + ".xlsx");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
ElMessage.success("导入失败,已导出错误数据");
|
||||
};
|
||||
try {
|
||||
fileE.click();
|
||||
} catch (error) {}
|
||||
}
|
||||
function handleExport() {
|
||||
console.log("导出");
|
||||
}
|
||||
function downLoadTpl() {
|
||||
console.log("下载模版");
|
||||
const baseUrl = import.meta.env.VITE_API_BASEURL;
|
||||
const excelImportUsersUrl = `${baseUrl}/SchoolBusiness/DwImportTemplate`;
|
||||
const link = document.createElement("a");
|
||||
link.href = excelImportUsersUrl;
|
||||
link.setAttribute("download", "导入赴校信息模板" + ".xlsx");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
ElMessage.success("下载成功!");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-form {
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue