zhangjie 1 年之前
父節點
當前提交
e12761970c

+ 2 - 2
src/App.vue

@@ -44,8 +44,8 @@ watchEffect(() => {
 
 <style>
 #app {
-  font-family: "PingFang SC", tahoma, arial, "Hiragino Sans GB", "宋体",
-    sans-serif;
+  font-family: SourceHanSansCN, "PingFang SC", "Microsoft YaHei",
+    "Hiragino Sans GB", "宋体", arial, sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   background-color: var(--app-main-bg-color);

二進制
src/assets/bg-home-logo.png


+ 1 - 3
src/components/Layout.vue

@@ -2,9 +2,7 @@
   <div class="home">
     <div class="home-header">
       <div class="home-menu">
-        <div class="home-title">
-          <h1>研究生成绩分析</h1>
-        </div>
+        <div class="home-title"></div>
         <div
           v-for="menu in menus"
           :key="menu.url"

+ 1 - 1
src/features/login/Login.vue

@@ -2,7 +2,7 @@
   <div class="login login-home">
     <div class="login-box">
       <div class="login-theme">
-        <h1>研究生成绩分析</h1>
+        <h1></h1>
         <p>Copyright ©启明泰和 Rights Reserved.</p>
       </div>
       <div class="login-body" @keyup.enter="submit">

+ 99 - 50
src/features/projectDataManagement/ProjectDataManagement.vue

@@ -17,15 +17,24 @@
     <div class="part-box">
       <a-tabs v-model:activeKey="dataSourceChannel" type="card">
         <a-tab-pane key="资料同步" tab="资料同步">
-          <a-form :labelCol="{ style: { width: '72px' } }" style="width: 400px">
-            <a-form-item label="系统域名">
+          <a-form
+            ref="formRef"
+            :labelCol="{ style: { width: '85px' } }"
+            :model="formData"
+            :rules="rules"
+            style="width: 400px"
+          >
+            <a-form-item label="系统域名" name="domain">
               <a-input
-                v-model:value="domain"
+                v-model:value="formData.domain"
                 placeholder="http://192.168.10.100"
               />
             </a-form-item>
-            <a-form-item label="考试标识">
-              <a-select v-model:value="examId" placeholder="请选择考试">
+            <a-form-item label="考试标识" name="examId">
+              <a-select
+                v-model:value="formData.examId"
+                placeholder="请选择考试"
+              >
                 <a-select-option
                   v-for="(item, index) in exams"
                   :key="index"
@@ -36,10 +45,18 @@
               </a-select>
             </a-form-item>
             <div>
-              <a-button type="primary" @click="handleTestServer"
+              <a-button
+                type="success"
+                :loading="testLoading"
+                @click="handleTestServer"
                 >接口检测</a-button
               >
-              <a-button type="primary" @click="handleSync">数据计算</a-button>
+              <a-button
+                type="primary"
+                :loading="submitLoading"
+                @click="handleSync"
+                >数据计算</a-button
+              >
             </div>
           </a-form>
         </a-tab-pane>
@@ -96,10 +113,11 @@ import { downloadFileURL, goBack } from "@/utils/utils";
 import { message } from "ant-design-vue";
 import { debounce } from "lodash-es";
 import QueryString from "qs";
-import { onMounted, computed, watch } from "vue";
+import { onMounted, computed, watch, reactive, ref } from "vue";
 import { useRoute } from "vue-router";
 import useLoading from "@/hooks/loading";
 import type { UploadProps } from "ant-design-vue";
+import type { Rule, FormInstance } from "ant-design-vue/es/form";
 
 const store = useMainStore();
 store.currentLocation = "项目列表 / 数据管理";
@@ -129,6 +147,10 @@ onMounted(async () => {
   await search();
 });
 
+const plainOptions = ["数据上传", "资料同步"] as const;
+let dataSourceChannel: (typeof plainOptions)[number] = $ref("资料同步");
+
+/** 数据上传  */
 const { loading: downLoading, setLoading: setDownLoading } = useLoading();
 async function downloadTpl() {
   if (downLoading.value) return;
@@ -167,48 +189,44 @@ async function handleImport() {
 
   void message.success({ content: "导入成功" });
 }
+/** 数据上传 ---end */
 
-const plainOptions = ["数据上传", "资料同步"] as const;
-let dataSourceChannel: (typeof plainOptions)[number] = $ref("数据上传");
-
-const domain = $ref("");
-
-async function handleTestServer() {
-  if (!domain) {
-    void message.warn({ content: "请先填写域名" });
-    return;
-  }
-  try {
-    const res = await httpApp.post<any, { data: string }>(
-      "/api/ess/task/sync/test",
-      QueryString.stringify({
-        domain,
-        projectId,
-      })
-    );
-    // console.log(res);
-    void message.info(res.data);
-  } catch (error) {
-    console.log(error);
-    void message.info("网络失败,请检查网络或联系管理员!");
-  }
-}
+/**  资料同步 */
+const defaultFormData = {
+  domain: "",
+  examId: "",
+};
+type FormDataType = typeof defaultFormData;
+const formRef = ref<FormInstance>();
+const formData = reactive<FormDataType>({ ...defaultFormData });
+const rules: Record<keyof FormDataType, Rule[]> = {
+  domain: [
+    {
+      required: true,
+      message: "请输入系统域名",
+    },
+  ],
+  examId: [
+    {
+      required: true,
+      message: "请选择考试",
+    },
+  ],
+};
 
 interface Exam {
   id: number;
   name: string;
 }
-
 let exams: Exam[] = $ref([]);
 async function fetchExamsFromOtherServer() {
-  if (!domain) {
-    return;
-  }
+  if (!formData.domain) return;
+
   try {
     const res = await httpApp.post<any, { data: Exam[] }>(
       "/api/ess/task/exams",
       QueryString.stringify({
-        domain,
+        domain: formData.domain,
         projectId,
       }),
       { noErrorMessage: true }
@@ -224,25 +242,56 @@ async function fetchExamsFromOtherServer() {
 
 const deboundcedFunc = debounce(fetchExamsFromOtherServer, 5000);
 watch(
-  () => [domain],
+  () => formData.domain,
   () => {
     void deboundcedFunc();
   }
 );
 
-let examId: string = $ref(null as unknown as string);
+const { loading: testLoading, setLoading: setTestLoading } = useLoading();
+async function handleTestServer() {
+  const valid = await formRef.value?.validateFields("domain").catch(() => {});
+  if (!valid) return;
 
-async function handleSync() {
-  if (typeof examId !== "number") {
-    void message.warn("请先选择考试");
-    return;
+  if (testLoading.value) return;
+  setTestLoading(true);
+  try {
+    const res = await httpApp.post<any, { data: string }>(
+      "/api/ess/task/sync/test",
+      QueryString.stringify({
+        domain: formData.domain,
+        projectId,
+      })
+    );
+    // console.log(res);
+    void message.info(res.data);
+  } catch (error) {
+    console.log(error);
+    void message.info("网络失败,请检查网络或联系管理员!");
   }
-  await httpApp.post("/api/ess/task/sync", {
-    domain,
-    examId,
-    projectId,
-  });
+  setTestLoading(false);
+}
+
+const { loading: submitLoading, setLoading: setSubmitLoading } = useLoading();
+async function handleSync() {
+  const valid = await formRef.value?.validate().catch(() => {});
+  if (!valid) return;
 
-  void message.warn("正在同步中...");
+  if (submitLoading.value) return;
+  setSubmitLoading(true);
+  let err = false;
+  await httpApp
+    .post("/api/ess/task/sync", {
+      domain: formData.domain,
+      examId: formData.examId,
+      projectId,
+    })
+    .catch(() => {
+      err = true;
+    });
+  setSubmitLoading(false);
+
+  if (err) return;
+  void message.warn("操作成功,正在同步中...");
 }
 </script>

+ 14 - 24
src/features/projectManagement/ModifyProject.vue

@@ -9,12 +9,8 @@
       <a-form-item v-if="isEdit" label="科目Id">
         <a-input v-model:value="formData.id" disabled></a-input>
       </a-form-item>
-      <a-form-item label="年份" name="year">
-        <a-select
-          v-model:value="formData.year"
-          :options="years"
-          style="width: 120px"
-        ></a-select>
+      <a-form-item label="项目简称" name="year">
+        <a-input v-model:value="formData.year" style="width: 160px"></a-input>
       </a-form-item>
       <a-form-item label="项目名称" name="name">
         <a-input v-model:value="formData.name"></a-input>
@@ -37,7 +33,7 @@ import type { Rule, FormInstance } from "ant-design-vue/es/form";
 import useLoading from "@/hooks/loading";
 import { updateProject } from "@/api/projectManagementPage";
 import { message } from "ant-design-vue";
-import { objAssign, objModifyAssign } from "@/utils/utils";
+import { objAssign, objModifyAssign, strGbLen } from "@/utils/utils";
 import { Project } from "@/types";
 import { useMainStore } from "@/store";
 
@@ -59,7 +55,7 @@ const defaultFormData = {
   id: undefined,
   code: "",
   name: "",
-  year: new Date().getFullYear(),
+  year: "",
   enable: true,
   type: undefined,
   rootOrgId: store.userInfo.rootOrgId,
@@ -82,25 +78,19 @@ const rules: Partial<Record<keyof FormDataType, Rule[]>> = {
   year: [
     {
       required: true,
-      message: "请选择年份",
+      message: "请输入项目简称",
+    },
+    {
+      validator: (_rule: Rule, value: string) => {
+        if (strGbLen(value) > 8) {
+          return Promise.reject("最多4个中文字符,或者8个英文字符");
+        } else {
+          return Promise.resolve();
+        }
+      },
     },
   ],
 };
-const years = ref<Array<{ value: number; label: number }>>([]);
-function getYears() {
-  const startYear = 2010;
-  const count = 50;
-  const datas = [];
-  for (let index = 0; index < count; index++) {
-    const year = startYear + index;
-    datas[index] = {
-      value: year,
-      label: year,
-    };
-  }
-  years.value = datas;
-}
-getYears();
 
 /* confirm */
 const { loading, setLoading } = useLoading();

+ 2 - 2
src/features/projectManagement/ProjectManagement.vue

@@ -251,9 +251,9 @@ const columns = [
     width: 80,
   },
   {
-    title: "年份",
+    title: "项目简称",
     dataIndex: "year",
-    width: 100,
+    width: 120,
   },
   {
     title: "项目名称",

+ 2 - 14
src/features/projectParamsManagement/ProjectCourseLineSet.vue

@@ -29,14 +29,14 @@
           :max="1000"
         />
       </a-form-item>
-      <a-form-item label="国家单科线" name="nationalScore">
+      <a-form-item label="国家单科线">
         <a-input-number
           v-model:value="formData.nationalScore"
           :min="0"
           :max="1000"
         ></a-input-number>
       </a-form-item>
-      <a-form-item label="复试科目线" name="retestScore">
+      <a-form-item label="复试科目线">
         <a-input-number
           v-model:value="formData.retestScore"
           :min="0"
@@ -104,18 +104,6 @@ const rules: Partial<Record<keyof FormDataType, Rule[]>> = {
       message: "请输入起始计算分",
     },
   ],
-  nationalScore: [
-    {
-      required: true,
-      message: "请输入国家单科线",
-    },
-  ],
-  retestScore: [
-    {
-      required: true,
-      message: "请输入复试科目线",
-    },
-  ],
 };
 
 /* confirm */

+ 7 - 7
src/features/report/ReportCompare.vue

@@ -32,7 +32,7 @@
           </colgroup>
           <thead>
             <tr>
-              <th>年份</th>
+              <th>项目简称</th>
               <th>科目代码</th>
               <th>满分</th>
               <th>最高分</th>
@@ -259,11 +259,11 @@ function countChartOption() {
     },
     {
       name: "国家线",
-      field: "reliability1",
+      field: "nationalScore",
     },
     {
-      name: "线",
-      field: "difficulty",
+      name: "复试线",
+      field: "retestScore",
     },
   ];
 
@@ -279,7 +279,7 @@ function countChartOption() {
       },
       xAxis: {
         type: "category",
-        data: props.comparePapers.map((v) => v.year),
+        data: props.comparePapers.map((v) => v.year || "-"),
         axisLine: {
           show: false,
         },
@@ -311,7 +311,7 @@ function countChartOption() {
       series: [
         {
           // eslint-disable-next-line @typescript-eslint/no-unsafe-return
-          data: props.comparePapers.map((v) => v[item.field]),
+          data: props.comparePapers.map((v) => v[item.field] || 0),
           type: "line",
           symbolSize: 6,
           lineStyle: {
@@ -382,7 +382,7 @@ function scoreChartOption() {
       areaStyle: {
         color: config.areaStyleColor,
       },
-      name: item.projectName,
+      name: item.year || "-",
     };
   });
   return {

+ 14 - 0
src/features/report/ReportMain.vue

@@ -311,6 +311,18 @@ function scoreTitle(rangeConfig: RangeConfig) {
   }${rangeConfig.adjustScore > 0 ? "+" : ""}${rangeConfig.adjustScore})-`;
 }
 
+function sortProjects(datas: Array<{ projectId: number }>) {
+  const ids = [projectId, ...compareProjectId];
+  const idsno: Record<any, number> = {};
+  ids.forEach((item, index) => {
+    idsno[item] = index;
+  });
+
+  datas.sort((a, b) => {
+    return idsno[a.projectId] - idsno[b.projectId];
+  });
+}
+
 async function fetchComparePaperData() {
   const res = await getPaperCompareList({
     projectIds: compareProjectId,
@@ -325,6 +337,7 @@ async function fetchComparePaperData() {
     v.reliability1 = numberPrecision(v.reliability1);
     return v;
   });
+  void sortProjects(comparePapers);
 }
 
 async function fetchCompareCourseData() {
@@ -336,6 +349,7 @@ async function fetchCompareCourseData() {
   compareCourses = res.data.map((v) => {
     return parseSasCourse(v);
   });
+  void sortProjects(compareCourses);
 }
 
 function parseSasCourse(course: SasCourse) {

+ 16 - 0
src/styles/ant-custom.less

@@ -43,6 +43,15 @@
     }
   }
 }
+.ant-btn-success {
+  color: #fff;
+  background-color: var(--color-success);
+  box-shadow: 0 2px 0 rgba(5, 115, 255, 0.1);
+
+  &:not(:disabled):hover {
+    opacity: 0.8;
+  }
+}
 
 // ant-modal
 .ant-modal {
@@ -85,6 +94,13 @@
       margin: 0;
     }
   }
+  .ant-form-item {
+    color: var(--app-main-text-color);
+
+    .ant-form-item-label > label {
+      color: var(--app-mid-text-color);
+    }
+  }
 }
 
 // .ant-pagination

+ 3 - 6
src/styles/home.less

@@ -22,13 +22,10 @@
       margin-right: 16px;
       font-size: var(--app-title-font-size);
       height: 24px;
-      font-weight: bold;
-      line-height: 24px;
+      width: 142px;
 
-      padding-left: 32px;
-      background-image: url(../assets/bg-login-title.jpg);
-      background-size: auto 100%;
-      background-repeat: no-repeat;
+      background-image: url(../assets/bg-home-logo.png);
+      background-size: 100% 100%;
     }
 
     &-item {

+ 3 - 7
src/styles/login.less

@@ -53,14 +53,10 @@
     width: 100%;
     top: 48px;
     left: 48px;
+    height: 28px;
+    width: 200px;
 
-    font-weight: bold;
-    font-size: 22px;
-    color: var(--app-main-text-color);
-    line-height: 28px;
-    padding-left: 36px;
-
-    background-image: url(../assets/bg-login-title.jpg);
+    background-image: url(../assets/bg-home-logo.png);
     background-size: auto 100%;
     background-repeat: no-repeat;
   }

+ 5 - 1
src/types/index.ts

@@ -93,6 +93,7 @@ export interface SasCourse {
     scoreRate: number;
   }[];
   totalScoreRangeTitle: any; // {title}
+  year: string;
 
   segements: number[][]; // 前端自用
   rangeSegements: (string | number | boolean)[][]; // 前端自用
@@ -155,6 +156,9 @@ export interface SASPaper {
   totalCount: number;
   totalScore: number;
   updateTime: string;
+  year: string;
+  nationalScore: number;
+  retestScore: number;
 }
 
 // 试卷分析-查询指定试卷全卷信息
@@ -172,7 +176,7 @@ export interface Project {
   creator: string;
   id: number;
   name: string;
-  year: number;
+  year: string;
   needCompute: boolean;
   rootOrgCode: string;
   rootOrgName: string;

+ 17 - 0
src/utils/utils.ts

@@ -84,3 +84,20 @@ export function objModifyAssign(target: object, sources: object) {
     }
   });
 }
+
+/**
+ * 获取字符串字符长度:中文2字符,英文1字符
+ * @param content 字符
+ * @returns 字符长度
+ */
+export function strGbLen(content) {
+  var len = 0;
+  for (var i = 0; i < content.length; i++) {
+    if (content.charCodeAt(i) > 127 || content.charCodeAt(i) == 94) {
+      len += 2;
+    } else {
+      len++;
+    }
+  }
+  return len;
+}