staging #23

Merged
hy merged 38 commits from staging into master 2025-10-14 11:13:16 +08:00
13 changed files with 305 additions and 129 deletions
Showing only changes of commit cf856c98f7 - Show all commits

View File

@ -286,24 +286,39 @@ export interface ConditionalModel {
} }
/** 搜索条件 */ /** 搜索条件 */
export interface SearchConditions { export class SearchConditions {
/**
*
*/
constructor() {
this.show = true;
this.showPage = true;
this.PageIndex = 0;
this.PageSize = 20;
this.OrderBy = "Id";
this.OrderByType = 1;
this.defaultConditions = [];
this.Conditions = [];
}
/** 是否显示搜索 */ /** 是否显示搜索 */
show: boolean; show?: boolean;
/** 显示分页器 */
showPage?:boolean;
/** 当前页码 */ /** 当前页码 */
PageIndex: number; PageIndex?: number;
/** 每页大小 */ /** 每页大小 */
PageSize: number; PageSize?: number;
/** 排序字段 */ /** 排序字段 */
OrderBy: string; OrderBy?: string;
/** /**
* @tips 0:升序 1:降序 * @tips 0:升序 1:降序
* @默认 = 1 * @默认 = 1
*/ */
OrderByType?: 0 | 1; OrderByType?: 0 | 1;
/** 默认查询条件 */ /** 默认查询条件 */
defaultConditions: ConditionalModel[]; defaultConditions?: ConditionalModel[];
/** 查询条件 */ /** 查询条件 */
Conditions: any[]; Conditions?: any[];
} }
/** 表格配置 */ /** 表格配置 */
@ -345,6 +360,9 @@ export function intTableData(tValue: TableConfig): TableConfig {
if (!tValue.pageData) tValue.pageData = { total: 0 }; if (!tValue.pageData) tValue.pageData = { total: 0 };
if (tValue.operationTop === undefined) tValue.operationTop = true; if (tValue.operationTop === undefined) tValue.operationTop = true;
//分页查询配置
tValue.search= { ...new SearchConditions(), ...tValue.search };
// 处理 column 的属性 // 处理 column 的属性
for (const key in tValue.column) { for (const key in tValue.column) {
tValue.column[key] = { ...new TableColumn(), ...tValue.column[key] }; tValue.column[key] = { ...new TableColumn(), ...tValue.column[key] };

View File

@ -429,39 +429,6 @@ function fetchPagedData() {
<el-button type="default" @click="searchReload()">重置</el-button> <el-button type="default" @click="searchReload()">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="dialog-container">
<el-dialog
v-if="dialog.visible"
v-model="dialog.visible"
:class="dialog.title ? '' : 'noHeader'"
:title="dialog.title"
:width="dialog.width"
:close-on-click-modal="false"
:close-on-press-escape="dialog.close"
:before-close="tableClose"
append-to-body
>
<hTableEdit
v-if="dialog.edit.visible"
:id="dialog.edit.id"
:tableData="table"
:row="dialog.edit.row"
:tagData="dialog.edit.tagData"
@handlePagedCallback="handleAddCallback"
/>
<component
:is="dialog.custom.component"
v-if="dialog.custom.visible"
:style="{ height: 'calc( ' + dialog.custom.height + ' - 84px )' }"
:iscomponent="true"
:custom="dialog.custom.custom"
:CancelCallback="handleAddCallback"
:data="dialog.custom.data"
@handlePagedCallback="handleAddCallback"
/>
</el-dialog>
</div>
</div> </div>
<div v-if="table.operationTop" class="toolbar-container"> <div v-if="table.operationTop" class="toolbar-container">
@ -484,7 +451,6 @@ function fetchPagedData() {
:data="table.data" :data="table.data"
:border="table.border" :border="table.border"
:highlight-current-row="true" :highlight-current-row="true"
style="width: 100%"
:row-key="rowKeyFun" :row-key="rowKeyFun"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@sort-change="sortChange" @sort-change="sortChange"
@ -563,6 +529,7 @@ function fetchPagedData() {
</el-table-column> </el-table-column>
</el-table> </el-table>
<div <div
v-if="table.search.showPage"
style=" style="
padding-top: 15px; padding-top: 15px;
display: flex; display: flex;
@ -580,6 +547,40 @@ function fetchPagedData() {
@current-change="pageIndexChange" @current-change="pageIndexChange"
/> />
</div> </div>
<div class="dialog-container">
<el-dialog
v-if="dialog.visible"
v-model="dialog.visible"
:class="dialog.title ? '' : 'noHeader'"
:title="dialog.title"
:width="dialog.width"
:close-on-click-modal="false"
:close-on-press-escape="dialog.close"
:before-close="tableClose"
class="max-w-[95vw] overflow-x-auto"
append-to-body
>
<hTableEdit
v-if="dialog.edit.visible"
:id="dialog.edit.id"
:tableData="table"
:row="dialog.edit.row"
:tagData="dialog.edit.tagData"
@handlePagedCallback="handleAddCallback"
/>
<component
:is="dialog.custom.component"
v-if="dialog.custom.visible"
:style="{ height: 'calc( ' + dialog.custom.height + ' - 84px )' }"
:iscomponent="true"
:custom="dialog.custom.custom"
:CancelCallback="handleAddCallback"
:data="dialog.custom.data"
@handlePagedCallback="handleAddCallback"
/>
</el-dialog>
</div>
</div> </div>
</template> </template>

View File

@ -86,6 +86,7 @@ const tableData: TableConfig = intTableData({
grade: { grade: {
label: "年级", label: "年级",
width: "100px", width: "100px",
custom: (row) => row.gradeLevel + row.gradeYear + "届",
search: new TableColumnSearch(true), search: new TableColumnSearch(true),
}, },
className: { className: {

View File

@ -31,12 +31,7 @@ const tableData: TableConfig = intTableData({
search: { search: {
// //
show: true, show: true,
PageIndex: 0, PageSize: 999,
PageSize: 20,
OrderBy: "Id", //
OrderByType: 1, //
defaultConditions: [], //
Conditions: [],
}, },
operationColumn: true, // operationColumn: true, //
operationColumnData: [ operationColumnData: [
@ -47,7 +42,7 @@ const tableData: TableConfig = intTableData({
btnType: "custom", btnType: "custom",
btnStyle: "primary", btnStyle: "primary",
custom: { custom: {
title: "考试学生班级详情", // title title: "考试班级详情", // title
src: "exam/classExamRecord", // src: "exam/classExamRecord", //
width: "1600px", // width: "1600px", //
height: "800px", // height: "800px", //
@ -64,6 +59,7 @@ const tableData: TableConfig = intTableData({
grade: { grade: {
label: "年级", label: "年级",
width: "120px", width: "120px",
custom: (row) => row.gradeLevel + row.gradeYear + "届",
search: new TableColumnSearch(true), search: new TableColumnSearch(true),
}, },
className: { className: {
@ -90,7 +86,6 @@ const tableData: TableConfig = intTableData({
}, },
onLineRanking: { onLineRanking: {
label: "重本率排名", label: "重本率排名",
width: "100px",
}, },
}, },
data: [], data: [],

View File

@ -36,7 +36,7 @@ const tableData: TableConfig = intTableData({
// //
show: true, show: true,
PageIndex: 0, PageIndex: 0,
PageSize: 20, PageSize: 999,
OrderByType: 1, // OrderByType: 1, //
OrderBy: "Id", // OrderBy: "Id", //
defaultConditions: [ defaultConditions: [
@ -85,7 +85,8 @@ const tableData: TableConfig = intTableData({
}, },
grade: { grade: {
label: "年级", label: "年级",
width: "60px", width: "90px",
custom: (row) => row.gradeLevel + row.gradeYear + "届",
search: new TableColumnSearch(true), search: new TableColumnSearch(true),
}, },
@ -104,24 +105,23 @@ const tableData: TableConfig = intTableData({
}, },
maxScore: { maxScore: {
label: "最高分[赋分]", label: "最高分[赋分]",
width: "140px", width: "130px",
}, },
minScore: { minScore: {
label: "最低分[赋分]", label: "最低分[赋分]",
width: "140px", width: "130px",
}, },
average: { average: {
label: "总平均分[赋分]", label: "总平均分[赋分]",
custom: (row) => `${Math.round(row.average)}`, custom: (row) => `${Math.round(row.average)}`,
width: "140px", width: "130px",
}, },
average1: { average1: {
label: "资源校平均分[赋分]", label: "资源校平均分[赋分]",
width: "160px", width: "100px",
}, },
averageRank: { averageRank: {
label: "总平均分排名", label: "总平均分排名",
width: "110px",
}, },
rank: { rank: {
label: "远端平均/资源校平均", label: "远端平均/资源校平均",

View File

@ -110,14 +110,27 @@ const tableData: TableConfig = intTableData({
rules: ruleRequired, rules: ruleRequired,
}, },
}, },
grade: { gradeLevel: {
label: "年级", label: "年级",
width: "100px", width: "100px",
type: "dropdown",
custom: (row) => row.gradeLevel + row.gradeYear + "届",
search: new TableColumnSearch(true), search: new TableColumnSearch(true),
edit: { edit: {
add: true, add: true,
edit: false, edit: false,
rules: ruleRequiredGrade, rules: ruleRequired,
},
},
gradeYear: {
label: "毕业年份",
width: "100px",
show: false,
search: new TableColumnSearch(true),
edit: {
add: true,
edit: false,
rules: ruleRequiredNumber,
}, },
}, },
testPaperType: { testPaperType: {
@ -162,7 +175,7 @@ const tableData: TableConfig = intTableData({
}, },
startTime: { startTime: {
label: "考试时间", label: "考试时间",
width: "210px", width: "180px",
type: "datetime", type: "datetime",
custom: (row) => row.startTime?.replace("T", " ").substring(0, 10) ?? "", custom: (row) => row.startTime?.replace("T", " ").substring(0, 10) ?? "",
search: new TableColumnSearch(true), search: new TableColumnSearch(true),
@ -197,7 +210,8 @@ const showTable = ref(false);
onMounted(async () => { onMounted(async () => {
// //
// tableData.column.level.setting.datasource = (await getenum("GradeEnum")).data; tableData.column.gradeLevel.setting.datasource =
(await getenum("GradeLevelEnum")).data.map(s=>{return {value : s.text,text:s.text}});
tableData.column.testPaperType.setting.datasource = ( tableData.column.testPaperType.setting.datasource = (
await getenum("TestPaperTypeEnum") await getenum("TestPaperTypeEnum")

View File

@ -31,8 +31,8 @@ const tableData: TableConfig = intTableData({
search: { search: {
// //
show: true, show: true,
PageIndex: 0, showPage: false,
PageSize: 60, PageSize: 9999,
OrderBy: "AssignRanking", // OrderBy: "AssignRanking", //
OrderByType: 0, OrderByType: 0,
defaultConditions: [ defaultConditions: [
@ -153,7 +153,7 @@ const exam = props.data[0];
<div> <div>
<div class="p-[10px] text-[1.5rem]"> <div class="p-[10px] text-[1.5rem]">
<strong>学校</strong>{{ exam.schoolName }}&nbsp;&nbsp; <strong>年级</strong <strong>学校</strong>{{ exam.schoolName }}&nbsp;&nbsp; <strong>年级</strong
>{{ exam.gradeLevel + exam.gradeYear }}&nbsp;&nbsp; <strong>班级</strong >{{ exam.gradeLevel + exam.gradeYear }}&nbsp;&nbsp; <strong>班级</strong
>{{ exam.className }}&nbsp;&nbsp; <strong>考试名称</strong >{{ exam.className }}&nbsp;&nbsp; <strong>考试名称</strong
>{{ exam.examName }}&nbsp;&nbsp; >{{ exam.examName }}&nbsp;&nbsp;
<!-- <strong>考试类型</strong>{{exam.className}}&nbsp;&nbsp; <!-- <strong>考试类型</strong>{{exam.className}}&nbsp;&nbsp;

View File

@ -32,8 +32,9 @@ const tableData: TableConfig = intTableData({
search: { search: {
// //
show: true, show: true,
showPage:false,
PageIndex: 0, PageIndex: 0,
PageSize: 999, PageSize: 9999,
OrderBy: "Id", // OrderBy: "Id", //
OrderByType: 1, OrderByType: 1,
defaultConditions: [ defaultConditions: [

View File

@ -109,7 +109,9 @@
<div class="subjectTagEnableDiv"> <div class="subjectTagEnableDiv">
<el-tag v-if="position.enable === false" type="info">已禁用</el-tag> <el-tag v-if="position.enable === false" type="info">已禁用</el-tag>
<el-tag>{{ position.schoolName || "-" }}</el-tag> <el-tag>{{ position.schoolName || "-" }}</el-tag>
<el-tag type="success">{{ position.grade || "-" }}</el-tag> <el-tag type="success">{{
position.graduationYear ? position.graduationYear + "届" : "-"
}}</el-tag>
<el-tag type="primary" class="classTag">{{ <el-tag type="primary" class="classTag">{{
position.className || "-" position.className || "-"
}}</el-tag> }}</el-tag>

View File

@ -159,7 +159,7 @@
<el-table-column prop="studentId" label="职务" width="120" /> <el-table-column prop="studentId" label="职务" width="120" />
<el-table-column label="任教信息"> <el-table-column label="任教信息" min-width="500">
<template #default="scope"> <template #default="scope">
<div <div
v-for="(position, index) in scope.row.positions" v-for="(position, index) in scope.row.positions"
@ -171,7 +171,6 @@
<el-tag type="info">{{ <el-tag type="info">{{
position.graduationYear ? position.graduationYear + "届" : "-" position.graduationYear ? position.graduationYear + "届" : "-"
}}</el-tag> }}</el-tag>
<el-tag type="info">{{ position.grade || "-" }}</el-tag>
<el-tag type="info">{{ position.className || "-" }}</el-tag> <el-tag type="info">{{ position.className || "-" }}</el-tag>
<el-tag type="info">{{ position.subjectName || "-" }}</el-tag> <el-tag type="info">{{ position.subjectName || "-" }}</el-tag>
<el-tag type="info">{{ position.name || "-" }}</el-tag> <el-tag type="info">{{ position.name || "-" }}</el-tag>

View File

@ -28,8 +28,13 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="年级" prop="baseInfo.grade"> <el-form-item label="年级" prop="baseInfo.gradeLevel">
<el-select v-model="form.baseInfo.grade" placeholder="请选择年级" clearable> <el-select
v-model="form.baseInfo.gradeLevel"
placeholder="请年级"
clearable
style="width: 120px"
>
<el-option <el-option
v-for="g in gradeOptions" v-for="g in gradeOptions"
:key="g.value" :key="g.value"
@ -37,6 +42,12 @@
:value="g.value" :value="g.value"
/> />
</el-select> </el-select>
<el-input-number
style="width: 120px"
v-model="form.baseInfo.gradeYear"
:min="2020"
:max="2100"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -69,6 +80,25 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item label="预计解决时间" prop="baseInfo.endTime">
<el-date-picker
v-model="form.baseInfo.endTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选预计解决时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<div>
提示:到期前<span class="text-yellow-500">{3}</span>会黄色警告, 小于<span
class="text-red-500"
>{1}</span
>红色警告
</div>
</el-col>
</el-row> </el-row>
<el-divider>基础工作</el-divider> <el-divider>基础工作</el-divider>
@ -198,13 +228,11 @@ const getSchoolBusinessPeopleList = () => {
}; };
getSchoolDataFn(); getSchoolDataFn();
getSchoolBusinessPeopleList(); getSchoolBusinessPeopleList();
const gradeOptions = [ const gradeOptions = [
{ label: "初一", value: "初一" }, { label: "高", value: "高" },
{ label: "初二", value: "初二" }, { label: "初", value: "初" },
{ label: "初三", value: "初三" }, { label: "小", value: "小" },
{ label: "高一", value: "高一" },
{ label: "高二", value: "高二" },
{ label: "高三", value: "高三" },
]; ];
type FeedbackKey = "leaders" | "classroom" | "equipment" | "students" | "others"; type FeedbackKey = "leaders" | "classroom" | "equipment" | "students" | "others";
@ -215,8 +243,10 @@ interface FeedbackItem {
interface FormModel { interface FormModel {
baseInfo: { baseInfo: {
school?: string; school?: string;
grade?: string; gradeYear?: number;
gradeLevel?: string;
date?: string; date?: string;
endTime?: string;
people: string[]; people: string[];
}; };
work: { work: {
@ -231,7 +261,8 @@ interface FormModel {
const form = reactive<FormModel>({ const form = reactive<FormModel>({
baseInfo: { baseInfo: {
school: undefined, school: undefined,
grade: undefined, gradeLevel: "高",
gradeYear: 2025,
date: undefined, date: undefined,
people: [], people: [],
}, },
@ -263,7 +294,7 @@ watch(
); );
const rules: FormRules = { const rules: FormRules = {
"baseInfo.school": [{ required: true, message: "请选择学校", trigger: "change" }], "baseInfo.school": [{ required: true, message: "请选择学校", trigger: "change" }],
"baseInfo.grade": [{ required: true, message: "请选择年级", trigger: "change" }], "baseInfo.gradeLevel": [{ required: true, message: "请选择年级", trigger: "change" }],
"baseInfo.date": [{ required: true, message: "请选择赴校时间", trigger: "change" }], "baseInfo.date": [{ required: true, message: "请选择赴校时间", trigger: "change" }],
"baseInfo.people": [ "baseInfo.people": [
{ required: true, message: "请选择赴校人员", trigger: "change" }, { required: true, message: "请选择赴校人员", trigger: "change" },
@ -412,9 +443,9 @@ async function onSubmit() {
id: 0, //id0 id: 0, //id0
schoolId: form.baseInfo.school, schoolId: form.baseInfo.school,
schoolName: schoolOptions.value.find((i) => i.value == form.baseInfo.school).label, schoolName: schoolOptions.value.find((i) => i.value == form.baseInfo.school).label,
grade: form.baseInfo.grade,
// //
gradeLevel: "", gradeLevel: form.baseInfo.gradeLevel,
gradeYear: form.baseInfo.gradeYear,
schoolBusinessUser: form.baseInfo.people, schoolBusinessUser: form.baseInfo.people,
startTime: form.baseInfo.date, startTime: form.baseInfo.date,
isDiscussion: form.work.talk, isDiscussion: form.work.talk,
@ -445,7 +476,7 @@ async function onSubmit() {
function resetForm() { function resetForm() {
form.baseInfo.school = undefined; form.baseInfo.school = undefined;
form.baseInfo.grade = undefined; form.baseInfo.gradeLevel = undefined;
form.baseInfo.date = undefined; form.baseInfo.date = undefined;
form.baseInfo.people = []; form.baseInfo.people = [];
form.work.talk = false; form.work.talk = false;

View File

@ -29,7 +29,7 @@
{{ safeDetail.schoolName || safeDetail.school || "-" }} {{ safeDetail.schoolName || safeDetail.school || "-" }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="年级"> <el-descriptions-item label="年级">
{{ safeDetail.grade || safeDetail.gradeLevel || "-" }} {{ safeDetail.gradeLevel + safeDetail.gradeYear }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="赴校人员"> <el-descriptions-item label="赴校人员">
{{ {{
@ -41,6 +41,10 @@
<el-descriptions-item label="赴校时间"> <el-descriptions-item label="赴校时间">
{{ safeDetail.startTimeStr || safeDetail.startTime }} {{ safeDetail.startTimeStr || safeDetail.startTime }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="预计完结时间">
{{ safeDetail.endTime != null ? safeDetail.endTime.split("T")[0] : "--" }}
</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-divider /> <el-divider />
@ -76,34 +80,63 @@
<span> 未解决问题{{ unresolvedCount }} </span> <span> 未解决问题{{ unresolvedCount }} </span>
</div> </div>
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane <el-tab-pane v-for="(i, idx) in questions" :key="idx" :name="idx">
v-for="(i, idx) in sortData(safeDetail.feedbackQuestions || [])" <template #label>
:key="idx" <span class="custom-tabs-label" :class="i.solution ? `text-[#67c23a]` : ``">
:label="'问题' + (idx + 1) + (i.solution ? '(✅已解决)' : '(未解决)')" <span>问题{{ idx + 1 }}</span>
:name="idx" <el-icon>
> <Select v-if="i.solution" />
</el-icon>
</span>
</template>
<div style="font-size: 12px; margin-bottom: 4px"> <div style="font-size: 12px; margin-bottom: 4px">
<span>问题类型</span> <span>{{ queType[i.questionType] }}</span> <span>问题类型</span> <span>{{ queType[i.questionType] }}</span>
</div> </div>
<div style="padding: 10px; background-color: #f3f3f3"> <div style="padding: 10px; background-color: #f3f3f3">
{{ i.question }} {{ i.question }}
</div> </div>
<div v-if="i.solution" style="font-size: 12px; margin-top: 10px"> <div
<span> 解决时间{{ i.endTimeStr || i.endTime }} </span> v-for="(rec, rIdx) in i.recordArr || []"
:key="rIdx"
style="margin-top: 10px"
>
<div>
执行记录{{ rIdx + 1 }}{{ rec.operator || "" }} {{ rec.executionTimeStr }}
</div>
<div style="padding: 5px; background-color: #f3f3f3">
{{ rec.executionRecords }}
</div>
</div>
<div v-if="i.solution" style="font-size: 12px; margin-top: 20px">
<span class="text-amber-400 font-bold">
解决时间{{ i.endTimeStr || i.endTime }}
</span>
<div style="padding: 10px; background-color: #f3f3f3"> <div style="padding: 10px; background-color: #f3f3f3">
{{ i.solution }} {{ i.solution }}
</div> </div>
</div> </div>
<el-button <div v-else class="mt-1.5">
v-show="!isDetail" <el-button
type="text" v-show="!isDetail"
v-else link
style="margin-top: 5px; font-size: 12px" :disabled="i.solution"
class="markTitle" style="margin-top: 5px; font-size: 12px"
@click="markTitle(i)" class="markTitle"
> @click="addQRecord(i)"
标记已解决 >
</el-button> 添加执行记录
</el-button>
<el-button
v-show="!isDetail"
link
style="margin-top: 5px; font-size: 12px"
class="markTitle"
@click="markTitle(i)"
>
标记已解决
</el-button>
</div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-divider /> <el-divider />
@ -128,7 +161,7 @@
<!-- 添加按钮区域 --> <!-- 添加按钮区域 -->
<div style="margin-top: 5px; display: flex; gap: 20px"> <div style="margin-top: 5px; display: flex; gap: 20px">
<el-button <el-button
type="text" link
style="margin-top: 5px; font-size: 12px" style="margin-top: 5px; font-size: 12px"
class="markTitle" class="markTitle"
v-show="!isDetail" v-show="!isDetail"
@ -139,8 +172,10 @@
<el-button <el-button
style="margin-top: 5px; font-size: 12px" style="margin-top: 5px; font-size: 12px"
class="markTitle" class="markTitle"
type="text" link
v-show="!isDetail" v-show="!isDetail"
:disabled="unresolvedCount != 0"
title="请先解决所有问题后再添加完结情况"
@click="addFinish" @click="addFinish"
> >
{{ finishRecord ? "修改完结情况" : "添加完结情况" }} {{ finishRecord ? "修改完结情况" : "添加完结情况" }}
@ -160,7 +195,7 @@
border-radius: 4px; border-radius: 4px;
" "
> >
<div style="font-weight: bold; color: #409eff"> <div style="font-weight: bold; color: #67c23a">
执行记录{{ index + 1 }}{{ record.operator }} {{ record.time }} 执行记录{{ index + 1 }}{{ record.operator }} {{ record.time }}
</div> </div>
<div style="margin-top: 5px; white-space: pre-wrap"> <div style="margin-top: 5px; white-space: pre-wrap">
@ -180,9 +215,7 @@
border: 1px solid #b3d8ff; border: 1px solid #b3d8ff;
" "
> >
<div style="font-weight: bold; color: #a69400"> <div class="text-amber-400 font-bold">完结情况{{ finishRecord.time }}</div>
完结情况{{ finishRecord.time }}
</div>
<div style="margin-top: 5px; white-space: pre-wrap"> <div style="margin-top: 5px; white-space: pre-wrap">
{{ finishRecord.content }} {{ finishRecord.content }}
</div> </div>
@ -230,7 +263,7 @@
</template> </template>
<script setup lang="ts" name="EditModal"> <script setup lang="ts" name="EditModal">
import { ref, reactive, computed, defineProps, defineEmits, watch } from "vue"; import { ref, reactive, computed, defineProps, defineEmits, watch, onMounted } from "vue";
import type { FormInstance, FormRules, TabsPaneContext } from "element-plus"; import type { FormInstance, FormRules, TabsPaneContext } from "element-plus";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { getSchoolData } from "@/api/userCenter"; import { getSchoolData } from "@/api/userCenter";
@ -238,13 +271,23 @@ import { getSchoolBusinessPeopleListApi, addOrEditApi } from "@/api/toschoolinfo
import { setFips } from "crypto"; import { setFips } from "crypto";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import { isAllEmpty } from "@pureadmin/utils"; import { isAllEmpty } from "@pureadmin/utils";
import { Select, CloseBold } from "@element-plus/icons-vue";
const activeName = ref<any>(0); const activeName = ref<any>(0);
onMounted(() => {
console.log("onMounted ");
questions = ref(sortData(safeDetail.value.feedbackQuestions));
});
const handleClick = (tab: TabsPaneContext, event: Event) => { const handleClick = (tab: TabsPaneContext, event: Event) => {
console.log(tab.props.name, event); console.log(tab.props.name, event);
activeName.value = tab.props.name; activeName.value = tab.props.name;
}; };
const gradeOptions = [
{ label: "高", value: "高" },
{ label: "初", value: "初" },
{ label: "小", value: "小" },
];
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
detailData: any; detailData: any;
@ -305,6 +348,7 @@ const operationContentLabel = computed(() => {
case "markSolved": case "markSolved":
return "解决情况"; return "解决情况";
case "addRecord": case "addRecord":
case "addQRecord":
return "执行记录"; return "执行记录";
case "addFinish": case "addFinish":
return "完结情况"; return "完结情况";
@ -345,10 +389,12 @@ const queType = {
* 获取未解决问题数量 * 获取未解决问题数量
* @param data * @param data
*/ */
const handleUnHandleQust = (data: Array<any>) => { const handleUnHandleQust = (data: Array<any>): number => {
return (data || []).filter((i) => !i?.solution).length; return (data || []).filter((i) => !i?.solution).length;
}; };
const sortData = (data: Array<any>) => { const sortData = (data: Array<any>) => {
if (data == null || data.length == 0) return [];
const categorizedData = [ const categorizedData = [
...data ...data
.filter((item) => item.questionType === 1) .filter((item) => item.questionType === 1)
@ -373,6 +419,7 @@ const statusText = computed(() => (safeDetail.value?.solutionEnd ? "已完结" :
const statusType = computed(() => const statusType = computed(() =>
safeDetail.value?.solutionEnd ? "success" : "warning" safeDetail.value?.solutionEnd ? "success" : "warning"
); );
let questions = ref(sortData(safeDetail.value.feedbackQuestions));
const solutionText = computed({ const solutionText = computed({
get: () => safeDetail.value?.solutionRecord?.solution || "", get: () => safeDetail.value?.solutionRecord?.solution || "",
set: (value: string) => { set: (value: string) => {
@ -388,6 +435,7 @@ const unresolvedCount = computed(() => {
const list = (safeDetail.value?.feedbackQuestions as any[]) || []; 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) => { const markTitle = (data: any) => {
console.log("标记已解决", data); console.log("标记已解决", data);
operationType.value = "markSolved"; operationType.value = "markSolved";
@ -398,6 +446,14 @@ const markTitle = (data: any) => {
operationFormRef.value?.clearValidate(); operationFormRef.value?.clearValidate();
operationDialogVisible.value = true; operationDialogVisible.value = true;
}; };
const addQRecord = (data: any) => {
console.log("添加问题执行记录", data);
operationType.value = "addQRecord";
currentMarkedQuestion.value = data;
//
operationFormRef.value?.clearValidate();
operationDialogVisible.value = true;
};
const addRecord = () => { const addRecord = () => {
console.log("添加执行记录"); console.log("添加执行记录");
operationType.value = "addRecord"; operationType.value = "addRecord";
@ -433,7 +489,11 @@ const closeOperationDialog = () => {
// //
currentMarkedQuestion.value = null; currentMarkedQuestion.value = null;
}; };
const userName = computed(() => {
return isAllEmpty(useUserStoreHook()?.nickName)
? useUserStoreHook()?.userName
: useUserStoreHook()?.nickName;
});
const confirmOperation = async () => { const confirmOperation = async () => {
if (!operationFormRef.value) return; if (!operationFormRef.value) return;
@ -441,9 +501,21 @@ const confirmOperation = async () => {
await operationFormRef.value.validate(); await operationFormRef.value.validate();
const { operationTime, operationContent } = operationForm; const { operationTime, operationContent } = operationForm;
// //
switch (operationType.value) { switch (operationType.value) {
case "addQRecord":
//
if (currentMarkedQuestion.value) {
if (currentMarkedQuestion.value.recordArr == undefined) {
currentMarkedQuestion.value.recordArr = [];
}
currentMarkedQuestion.value.recordArr.push({
executionTime: operationTime,
executionRecords: operationContent,
operator: userName.value,
});
}
break;
case "addRecord": case "addRecord":
// //
executionRecords.value.push({ executionRecords.value.push({
@ -454,11 +526,7 @@ const confirmOperation = async () => {
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)) if (!Array.isArray(props.detailData.solutionRecord.record))
props.detailData.solutionRecord.record = []; props.detailData.solutionRecord.record = [];
const userName = computed(() => {
return isAllEmpty(useUserStoreHook()?.nickName)
? useUserStoreHook()?.userName
: useUserStoreHook()?.nickName;
});
props.detailData.solutionRecord.record.push({ props.detailData.solutionRecord.record.push({
executionTime: operationTime, executionTime: operationTime,
executionRecords: operationContent, executionRecords: operationContent,
@ -532,6 +600,14 @@ function onClickSave() {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.custom-tabs-label .el-icon {
vertical-align: middle;
}
.custom-tabs-label span {
vertical-align: middle;
margin-left: 4px;
}
.modal-header { .modal-header {
display: flex; display: flex;
align-items: center; align-items: center;
@ -555,7 +631,6 @@ function onClickSave() {
} }
.markTitle:hover { .markTitle:hover {
text-decoration: underline; text-decoration: underline;
cursor: pointer;
user-select: none; user-select: none;
} }
</style> </style>

View File

@ -23,7 +23,7 @@
v-model="query.grade" v-model="query.grade"
placeholder="请选择年级" placeholder="请选择年级"
clearable clearable
style="width: 140px" style="width: 120px"
> >
<el-option <el-option
v-for="g in gradeOptions" v-for="g in gradeOptions"
@ -31,7 +31,8 @@
:label="g.label" :label="g.label"
:value="g.value" :value="g.value"
/> />
</el-select> </el-select >
<el-input-number v-show="query.grade" v-model="query.gradeYear" :min="2020" :max="2100"/>
</el-form-item> </el-form-item>
<el-form-item label="赴校人员"> <el-form-item label="赴校人员">
<el-select <el-select
@ -88,7 +89,12 @@
<el-button type="info" @click="downLoadTpl">下载模版</el-button> <el-button type="info" @click="downLoadTpl">下载模版</el-button>
</div> </div>
<!-- 表格区域 --> <!-- 表格区域 -->
<el-table :data="listData" style="width: 100%" :max-height="500"> <el-table
:data="listData"
style="width: 100%"
:max-height="500"
:row-class-name="tableRowClassName"
>
<el-table-column label="操作" width="200"> <el-table-column label="操作" width="200">
<template #default="{ row }"> <template #default="{ row }">
<!-- <el-button size="small" type="danger" plain @click="onDelete(row)" <!-- <el-button size="small" type="danger" plain @click="onDelete(row)"
@ -127,9 +133,17 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="grade" label="年级" min-width="100" /> <el-table-column label="年级" min-width="100" >
<template #default="{ row }">
{{ row.gradeLevel+row.gradeYear }}
</template>
</el-table-column>
<el-table-column prop="people" label="赴校人员" min-width="120" /> <el-table-column prop="people" label="赴校人员" min-width="120" />
<el-table-column prop="times" label="赴校时间" min-width="140" /> <el-table-column label="赴校时间/解决时间" min-width="140">
<template #default="{ row }">
{{ row.times }}{{ row.endTime ? " / " + row.endTime : "" }}
</template>
</el-table-column>
<el-table-column prop="feedbackTotals" 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="solveTotals" label="解决问题数量" min-width="140" />
@ -155,6 +169,7 @@
<!-- 跟进 --> <!-- 跟进 -->
<EditModal <EditModal
v-model:visible="isShowEditModal" v-model:visible="isShowEditModal"
v-if="isShowEditModal"
:editModalLoading="editModalLoading" :editModalLoading="editModalLoading"
:detailData="detailData" :detailData="detailData"
:isDetail="isDetail" :isDetail="isDetail"
@ -187,6 +202,7 @@ interface TableItem {
people: string; people: string;
canOperate: boolean; // canOperate: boolean; //
times: string; // YYYY-MM-DD times: string; // YYYY-MM-DD
endTime: string; //
feedbackTotals: number; feedbackTotals: number;
solveTotals: number; solveTotals: number;
solutionEnd: boolean; // true: , false: solutionEnd: boolean; // true: , false:
@ -196,6 +212,20 @@ interface TableItem {
const schoolOptions = ref([]); const schoolOptions = ref([]);
const peopleOptions = ref([]); const peopleOptions = ref([]);
const isDetail = ref(false); const isDetail = ref(false);
const tableRowClassName = ({ row, rowIndex }: { row: any; rowIndex: number }) => {
if (row.endTime == null || row.endTime == "" || row.solutionEnd) return "";
const nD = new Date();
const d = new Date(row.endTime + " ");
const daysDiff = Math.floor((d.getTime() - nD.getTime()) / (1000 * 60 * 60 * 24));
if (daysDiff >= 2) {
return "warning-row";
} else if (daysDiff < 2) {
return "error-row";
}
return "";
};
/** /**
* 获取学校下拉数据 * 获取学校下拉数据
*/ */
@ -236,12 +266,9 @@ onMounted(() => {
}); });
const gradeOptions = [ const gradeOptions = [
{ label: "初一", value: "初一" }, { label: "高", value: "高" },
{ label: "初二", value: "初二" }, { label: "初", value: "初" },
{ label: "初三", value: "初三" }, { label: "小", value: "小" },
{ label: "高一", value: "高一" },
{ label: "高二", value: "高二" },
{ label: "高三", value: "高三" },
]; ];
/** /**
* 新建赴校信息提交 * 新建赴校信息提交
@ -283,6 +310,7 @@ const addOrEdit = () => {
const query = reactive({ const query = reactive({
school: "" as string | undefined, school: "" as string | undefined,
grade: "" as string | undefined, grade: "" as string | undefined,
gradeYear: 2025 as number ,
people: "" as string | undefined, people: "" as string | undefined,
solutionEnd: undefined, solutionEnd: undefined,
times: [] as string[], times: [] as string[],
@ -309,8 +337,11 @@ function mapApiItemToRow(item: any): TableItem {
id: item.id, id: item.id,
school: item.schoolName || "", school: item.schoolName || "",
grade: item.grade || "", grade: item.grade || "",
gradeYear: item.gradeYear || "",
gradeLevel: item.gradeLevel || "",
people: peopleArr.join(""), people: peopleArr.join(""),
times: start, times: start,
endTime: item.endTime ? dayjs(item.endTime).format("YYYY-MM-DD") : "",
canOperate: item.canOperate || false, // canOperate: item.canOperate || false, //
feedbackTotals: Number(item.feedbackCount) || 0, feedbackTotals: Number(item.feedbackCount) || 0,
solveTotals: Number(item.solveFeedbackCount) || 0, solveTotals: Number(item.solveFeedbackCount) || 0,
@ -326,7 +357,7 @@ async function loadList() {
orderBy: "startTime", orderBy: "startTime",
}; };
if (query.school) payload.schoolId = query.school; if (query.school) payload.schoolId = query.school;
if (query.grade) payload.grade = query.grade; if (query.grade && query.gradeYear) payload.grade = query.grade+query.gradeYear;
if (query.people) { if (query.people) {
payload.UserName = query.people; payload.UserName = query.people;
} }
@ -401,12 +432,12 @@ function onDelete(row: TableItem) {
*/ */
function onDetailOrFollow(row: TableItem, disabled = false) { function onDetailOrFollow(row: TableItem, disabled = false) {
isDetail.value = disabled; isDetail.value = disabled;
isShowEditModal.value = true;
editModalLoading.value = true; editModalLoading.value = true;
getSchoolBusinessDetailApi(row.id) getSchoolBusinessDetailApi(row.id)
.then((res) => { .then((res) => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
detailData.value = res.data; detailData.value = res.data;
isShowEditModal.value = true;
} }
}) })
.finally(() => { .finally(() => {
@ -504,4 +535,12 @@ function downLoadTpl() {
justify-content: center; justify-content: center;
margin-top: 12px; margin-top: 12px;
} }
:deep(.el-table .warning-row) {
--el-table-tr-bg-color: rgb(253, 246, 236);
}
:deep(.el-table .error-row) {
--el-table-tr-bg-color: rgb(255, 227, 227);
}
</style> </style>