刘洋 9 місяців тому
батько
коміт
0d72f702b7

+ 1 - 1
package.json

@@ -16,7 +16,7 @@
   "main": "dist/main.js",
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",
-    "@qmth/ui": "^1.0.14",
+    "@qmth/ui": "^1.0.15",
     "@vueuse/core": "^10.11.0",
     "axios": "^1.5.0",
     "core-js": "^3.32.2",

+ 61 - 1
src/main/index.ts

@@ -3,6 +3,9 @@ import fs from "fs";
 import { app, BrowserWindow, Menu, screen, dialog, ipcMain } from "electron";
 import installExtension, { VUEJS3_DEVTOOLS } from "electron-devtools-installer";
 
+import child_process from "child_process";
+import process from "process";
+
 const isDev = process.env.NODE_ENV === "development";
 let win: any = null;
 let loadWin: any = null;
@@ -116,7 +119,7 @@ function createLoadWin() {
 //       createLoadWin();
 //     });
 
-ipcMain.on("change-win-size", (event, args) => {
+ipcMain.on("change-win-size", (event, args: string) => {
   const { width, height } = screen.getPrimaryDisplay().workAreaSize;
   let w = args === "big" ? width : 840;
   let h = args === "big" ? height : 500;
@@ -130,6 +133,63 @@ ipcMain.on("window-min", () => {
   win.minimize();
 });
 
+function startExe(exePath: string) {
+  const childProcess = child_process.spawn(exePath);
+
+  childProcess.on("close", (code: any) => {
+    console.log("close---- code:", code);
+  });
+}
+
+// ipcMain.on("startExe", (event, args: string) => {
+//   startExe(args);
+// });
+
+// ipcMain.handle("checkProcessExist", async (event, processName) => {
+//   try {
+//     const processExists = await checkProcessExist(processName);
+//     if (processExists) {
+//       dialog.showErrorBox("tip", `${processName}已启动`);
+//     } else {
+//       const exePath = path.join(
+//         process.env.PORTABLE_EXECUTABLE_DIR as string,
+//         "text.exe"
+//       );
+//       const fileExists = fs.existsSync(exePath);
+//       if (fileExists) {
+//         startExe(exePath);
+//       } else {
+//         dialog.showErrorBox("tip", ".exe not exist!");
+//       }
+//     }
+//     return processExists;
+//   } catch (error) {
+//     console.error(error);
+//     return false;
+//   }
+// });
+
+// function checkProcessExist(processName: string) {
+//   return new Promise((resolve, reject) => {
+//     const command = process.platform === "win32" ? "tasklist" : "ps aux";
+//     const childProcess = child_process.spawn(command);
+
+//     childProcess.stdout.on("data", (data: any) => {
+//       if (data.toString().indexOf(processName) !== -1) {
+//         resolve(true);
+//       }
+//     });
+
+//     childProcess.stderr.on("data", (data: any) => {
+//       reject(data.toString());
+//     });
+
+//     childProcess.on("close", (code: any) => {
+//       resolve(false);
+//     });
+//   });
+// }
+
 app.on("ready", async () => {
   if (isDev) {
     try {

+ 9 - 0
src/main/preload/index.ts

@@ -5,6 +5,7 @@
 
 const { contextBridge, ipcRenderer } = require("electron");
 const os = require("os");
+const path = require("path");
 
 contextBridge.exposeInMainWorld("electronApi", {
   getComputerName: () => {
@@ -16,4 +17,12 @@ contextBridge.exposeInMainWorld("electronApi", {
   windowMin: () => {
     ipcRenderer.send("window-min");
   },
+  startScanExe: () => {
+    console.log("process.env", process.env.INIT_CWD);
+    const exePath = path.join(
+      process.env.PORTABLE_EXECUTABLE_DIR as string,
+      "text.exe"
+    );
+    ipcRenderer.send("startExe", exePath);
+  },
 });

+ 19 - 1
src/render/Layout/index.vue

@@ -22,12 +22,21 @@
           >最小化</qm-button
         >
         <qm-button
+          v-if="isIndexPage"
           type="text"
           :icon="h(LogoutOutlined)"
           class="m-l-6px"
           @click="toLogout"
           >退出账号</qm-button
         >
+        <qm-button
+          v-else
+          type="text"
+          :icon="h(HomeOutlined)"
+          class="m-l-6px"
+          @click="backToIndexPage"
+          >返回首页</qm-button
+        >
       </div>
     </div>
     <div class="radius-wrap">
@@ -41,20 +50,28 @@
 
 <script setup name="AppLayout" lang="ts">
 import { h, computed } from "vue";
-import { useRoute } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
 import FooterInfo from "@/components/FooterInfo/index.vue";
 import {
   UserOutlined,
   MinusOutlined,
   LogoutOutlined,
+  HomeOutlined,
 } from "@ant-design/icons-vue";
 import { useUserStore } from "@/store";
 
 const userStore = useUserStore();
 const route = useRoute();
+const router = useRouter();
 const matched = computed(() => {
   return route.matched || [];
 });
+const isIndexPage = computed(() => {
+  return route.name === "CurExam";
+});
+const backToIndexPage = () => {
+  router.back();
+};
 
 const windowToMin = () => {
   window.electronApi?.windowMin();
@@ -99,6 +116,7 @@ const toLogout = () => {
     .sub-page-wrap {
       overflow: auto;
       height: 100%;
+      background-color: #fff;
     }
   }
 }

+ 3 - 0
src/render/components.d.ts

@@ -10,9 +10,12 @@ declare module 'vue' {
     ABreadcrumb: typeof import('@qmth/ui')['Breadcrumb']
     AButton: typeof import('@qmth/ui')['Button']
     AInput: typeof import('@qmth/ui')['Input']
+    AInputNumber: typeof import('@qmth/ui')['InputNumber']
     ARadio: typeof import('@qmth/ui')['Radio']
     ARadioGroup: typeof import('@qmth/ui')['RadioGroup']
     ASpin: typeof import('@qmth/ui')['Spin']
+    ATabPane: typeof import('@qmth/ui')['TabPane']
+    ATabs: typeof import('@qmth/ui')['Tabs']
     ATag: typeof import('@qmth/ui')['Tag']
     QmButton: typeof import('@qmth/ui')['QmButton']
     QmConfigProvider: typeof import('@qmth/ui')['QmConfigProvider']

+ 8 - 0
src/render/router/routes.ts

@@ -22,6 +22,14 @@ const routes: RouteRecordRaw[] = [
           title: "当前考试信息",
         },
       },
+      {
+        path: "base-data-config",
+        name: "BaseDataConfig",
+        component: () => import("@/views/BaseDataConfig/index.vue"),
+        meta: {
+          title: "基础数据配置",
+        },
+      },
     ],
   },
 ];

+ 12 - 0
src/render/utils/tool.ts

@@ -229,3 +229,15 @@ export function randomCode(len = 16) {
 
   return stepNums.join("");
 }
+
+export function setValueFromObj(targetObj: any, fromObj: any) {
+  fromObj = fromObj ? fromObj : {};
+  let keys = Object.keys(targetObj);
+  for (let i = 0; i < keys.length; i++) {
+    const key = keys[i];
+    if (fromObj.hasOwnProperty(key)) {
+      targetObj[key] = fromObj[key];
+    }
+  }
+  return targetObj;
+}

+ 5 - 0
src/render/views/BaseDataConfig/CardImport.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="card-import">卡格式导入</div>
+</template>
+<script name="CardImport" lang="ts" setup></script>
+<style lang="less" scoped></style>

+ 99 - 0
src/render/views/BaseDataConfig/ScanParams.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="scan-params h-full flex justify-center items-center">
+    <div class="center">
+      <qm-low-form
+        :fields="fields"
+        :params="params"
+        :label-width="150"
+        labelAlign="left"
+      >
+        <template #a>
+          <div class="flex items-center">
+            <a-input class="w-150px" v-model:value="params.a" />
+            <qm-button class="m-l-8px" :icon="h(PlusCircleOutlined)"
+              >新增</qm-button
+            >
+          </div>
+          <div class="p-t-10px p-b-10px">
+            <div
+              class="tag"
+              v-for="(item, index) in tagList"
+              :key="item"
+              :style="{ color: token.colorPrimary }"
+            >
+              {{ item }}
+              <span class="close" @click="delTag(index)">&times;</span>
+            </div>
+          </div>
+        </template>
+
+        <template #b>
+          <div class="flex items-center">
+            <div class="flex items-center m-r-50px">
+              <span>抽查比例:</span>
+              <a-input-number v-model:value="params.b" :max="100" :min="0" />
+            </div>
+            <div class="flex items-center"></div>
+          </div>
+        </template>
+      </qm-low-form>
+    </div>
+  </div>
+</template>
+<script name="ScanParams" lang="ts" setup>
+import { ref, h } from "vue";
+import { PlusCircleOutlined } from "@ant-design/icons-vue";
+import useToken from "@/hooks/useToken";
+const { token } = useToken();
+//todo 入参名
+const params = ref({
+  a: "",
+  b: "",
+  c: "",
+  d: "",
+  e: "",
+  f: "",
+});
+const fields = ref([
+  {
+    cell: "a",
+    label: "卷型条码内容",
+    colSpan: 24,
+  },
+  {
+    cell: "b",
+    label: "图片检查",
+    colSpan: 24,
+  },
+]);
+const tagList = ref(["666666", "888888", "999999"]);
+const delTag = (index: number) => {
+  tagList.value.splice(index, 1);
+};
+</script>
+<style lang="less" scoped>
+.scan-params {
+  .center {
+    width: 1100px;
+    max-height: 100%;
+    overflow: auto;
+    .tag {
+      height: 32px;
+      padding: 0 10px;
+      border-radius: 16px;
+      line-height: 32px;
+      background: #e8f3ff;
+      display: inline-block;
+      margin-right: 8px;
+      .close {
+        font-size: 18px;
+        padding: 0 3px;
+        cursor: pointer;
+        &:hover {
+          font-weight: bold;
+        }
+      }
+    }
+  }
+}
+</style>

+ 5 - 0
src/render/views/BaseDataConfig/StuImport.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="stu-import">考生导入</div>
+</template>
+<script name="StuImport" lang="ts" setup></script>
+<style lang="less" scoped></style>

+ 5 - 0
src/render/views/BaseDataConfig/UserManage.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="user-manage">用户管理</div>
+</template>
+<script name="UserManage" lang="ts" setup></script>
+<style lang="less" scoped></style>

+ 42 - 0
src/render/views/BaseDataConfig/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="base-data-config h-full">
+    <a-tabs v-model:activeKey="activeKey">
+      <a-tab-pane key="1" tab="扫描参数配置">
+        <ScanParams></ScanParams>
+      </a-tab-pane>
+      <a-tab-pane key="2" tab="用户管理">
+        <UserManage />
+      </a-tab-pane>
+      <a-tab-pane key="3" tab="卡格式导入">
+        <CardImport />
+      </a-tab-pane>
+      <a-tab-pane key="4" tab="考生导入">
+        <StuImport />
+      </a-tab-pane>
+    </a-tabs>
+  </div>
+</template>
+<script name="BaseDataConfig" lang="ts" setup>
+import { ref } from "vue";
+import ScanParams from "./ScanParams.vue";
+import UserManage from "./UserManage.vue";
+import CardImport from "./CardImport.vue";
+import StuImport from "./StuImport.vue";
+const activeKey = ref("1");
+</script>
+<style lang="less" scoped>
+.base-data-config {
+  :deep(.ant-tabs) {
+    height: 100%;
+    .ant-tabs-nav {
+      padding: 8px 15px 0 15px;
+    }
+    .ant-tabs-content-holder {
+      overflow: auto;
+      .ant-tabs-content {
+        height: 100%;
+      }
+    }
+  }
+}
+</style>

+ 18 - 1
src/render/views/CurExam/AddExamDialog.vue

@@ -1,7 +1,24 @@
 <template>
-  <my-modal v-model:open="visible" title="新建考试"></my-modal>
+  <my-modal v-model:open="visible" title="新建考试">
+    <qm-low-form :params="params" :fields="fields" :label-width="80">
+      <template #b> CET </template>
+    </qm-low-form>
+  </my-modal>
 </template>
 <script name="AddExam" lang="ts" setup>
+import { ref } from "vue";
+import { setValueFromObj } from "@/utils/tool";
 const visible = defineModel();
+const props = defineProps<{ curRow: any }>();
+
+//todo 入参名
+const params = ref({
+  a: "",
+});
+params.value = setValueFromObj(params.value, props.curRow);
+const fields = ref([
+  { prop: "a", label: "名称", colSpan: 24 },
+  { colSpan: 24, cell: "b", label: "扫描模式" },
+]);
 </script>
 <style lang="less" scoped></style>

+ 7 - 0
src/render/views/CurExam/index.vue

@@ -52,6 +52,7 @@
               <div
                 class="flex items-center cursor-pointer"
                 :style="{ color: token.colorPrimary }"
+                @click="toPage('BaseDataConfig')"
               >
                 <span>进入</span>
                 <RightOutlined />
@@ -257,6 +258,7 @@ import { onMounted, ref, computed, h, watch, reactive } from "vue";
 import { getExamList } from "@/ap/exam";
 import { useUserStore } from "@/store";
 import AddExamDialog from "./AddExamDialog.vue";
+import { useRouter } from "vue-router";
 import {
   SwapOutlined,
   EditOutlined,
@@ -266,6 +268,10 @@ import {
 import useToken from "@/hooks/useToken";
 
 const { token } = useToken();
+const router = useRouter();
+const toPage = (name: string) => {
+  router.push({ name });
+};
 
 const showAddDialog = ref(false);
 
@@ -313,6 +319,7 @@ const addExam = () => {
 };
 const editExam = () => {
   curRow.value = curExam.value;
+  showAddDialog.value = true;
 };
 onMounted(() => {
   _getExamList();

+ 1 - 0
src/render/views/Login/LoginWays.vue

@@ -51,6 +51,7 @@ const activeName = ref("scan");
 const nextStep = () => {
   if (activeName.value === "scan") {
     //todo,拉起本地扫描端exe程序,隐藏本browserWindow
+    window.electronApi.startScanExe();
   } else {
     emit("next");
   }

+ 0 - 52
src/render/views/test.vue

@@ -1,52 +0,0 @@
-<template>
-  <div class="login h-full">
-    <!-- <qm-button type="primary" @click="changeColor('#000')"
-      >切换主题色</qm-button
-    >
-    <QmButton type="primary" @click="login">登录</QmButton>
-    <QmButton type="primary" @click="visible = true">打开弹框</QmButton>
-    <div>
-      <a-input v-model:value="ccc" />
-    </div>
-    <QmLowForm :fields="fields" :params="params" :labelWidth="100"></QmLowForm>
-    <MyModal v-model:open="visible"></MyModal> -->
-    <!-- http://192.168.10.224:9000/sheet/1/409/1500409-1.jpg -->
-    <div class="img-box">
-      <v3-drag-zoom-container style="height: 100%">
-        <img src="http://192.168.10.224:9000/sheet/1/409/1500409-1.jpg" />
-      </v3-drag-zoom-container>
-    </div>
-  </div>
-</template>
-<script lang="ts" name="Login" setup>
-import { useRouter } from "vue-router";
-import { useThemeColor } from "@qmth/ui";
-import { reactive, ref } from "vue";
-import MyModal from "@/components/MyModal";
-const ccc = ref("ccc");
-const visible = ref(false);
-const { publishPrimaryColor } = useThemeColor();
-const changeColor = (color: string) => {
-  publishPrimaryColor(color);
-};
-const router = useRouter();
-const login = () => {
-  router.push({ name: "CreateExam" });
-};
-const params = reactive({ a: "12" });
-
-const fields = ref([
-  {
-    prop: "a",
-    label: "输入框",
-    colSpan: 8,
-  },
-]);
-</script>
-<style lang="less" scoped>
-.img-box {
-  height: 500px;
-  width: 500px;
-  background-color: #ddd;
-}
-</style>