From b3a91b2cb4b3b13fddccdb307eda45a7acc0a3ec Mon Sep 17 00:00:00 2001 From: Jeffrey Hsu Date: Tue, 4 Feb 2025 03:44:33 +0800 Subject: [PATCH] implement CreateStart.vue feature --- src/main/index.ts | 28 ++++-- src/preload/index.d.ts | 7 +- src/preload/index.ts | 7 +- src/renderer/src/main.ts | 3 +- src/renderer/src/store/index.ts | 97 ++++++++++++++++++- src/renderer/src/views/create/CreateStart.vue | 81 ++++++++++++++-- 6 files changed, 198 insertions(+), 25 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index ba91fcc..d6e2706 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,13 +1,15 @@ -import { app, shell, BrowserWindow, ipcMain } from 'electron' +import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron' import { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' +import * as XLSX from 'xlsx' +import { WorkBook } from 'xlsx' function createWindow(): void { // Create the browser window. const mainWindow = new BrowserWindow({ - width: 900, - height: 670, + width: 1024, + height: 728, show: false, autoHideMenuBar: true, titleBarStyle: 'hidden', @@ -52,10 +54,7 @@ app.whenReady().then(() => { // IPC ipcMain.on('title_bar', (_, ev: 'minimize' | 'maximize' | 'close') => { - const win = BrowserWindow.getFocusedWindow() - if (win === null) { - return - } + const win = BrowserWindow.getFocusedWindow() as BrowserWindow if (ev === 'close') { win.close() @@ -70,6 +69,21 @@ app.whenReady().then(() => { } }) + ipcMain.handle('read-xlsx', async (): Promise => { + const win = BrowserWindow.getFocusedWindow() as BrowserWindow + const result = await dialog.showOpenDialog(win, { + properties: ['openFile'], + filters: [{ name: 'Excel文件', extensions: ['xlsx', 'xls'] }] + }) + if (result.filePaths.length) { + try { + return XLSX.readFile(result.filePaths[0]) + } catch (e) { + console.error(e) + } + } + return null + }) createWindow() app.on('activate', function () { diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index a153669..7d5f4a5 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,8 +1,13 @@ import { ElectronAPI } from '@electron-toolkit/preload' +import { WorkBook } from 'xlsx' + +interface ElectronAPIWithOthers extends ElectronAPI { + readXLSX(): Promise +} declare global { interface Window { - electron: ElectronAPI + electron: ElectronAPIWithOthers; api: unknown } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 2d18524..3491cc5 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,4 +1,4 @@ -import { contextBridge } from 'electron' +import { contextBridge, ipcRenderer } from 'electron' import { electronAPI } from '@electron-toolkit/preload' // Custom APIs for renderer @@ -9,7 +9,10 @@ const api = {} // just add to the DOM global. if (process.contextIsolated) { try { - contextBridge.exposeInMainWorld('electron', electronAPI) + contextBridge.exposeInMainWorld('electron', { + ...electronAPI, + readXLSX: () => ipcRenderer.invoke('read-xlsx') + }) contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) diff --git a/src/renderer/src/main.ts b/src/renderer/src/main.ts index 715d0c6..338ac0a 100644 --- a/src/renderer/src/main.ts +++ b/src/renderer/src/main.ts @@ -4,5 +4,6 @@ import router from '@renderer/router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import '@renderer/assets/main.css' +import { createPinia } from 'pinia' -createApp(App).use(router).use(ElementPlus).mount('#app') +createApp(App).use(router).use(ElementPlus).use(createPinia()).mount('#app') diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index d1dc8d4..1a692ed 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -1,17 +1,106 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -import { ICourse } from '@renderer/types' +import { ICourse, IStudent } from '@renderer/types' +import { ElMessage } from 'element-plus' export const useGlobalStore = defineStore('global', () => { const courseInfo = ref(null) + const studentList = ref([]) - const saveCourseInfo = (new_course: ICourse) => { + function saveCourseInfo(new_course: ICourse) { courseInfo.value = new_course } - const clearCourseInfo = () => { + function clearCourseInfo() { courseInfo.value = null } - return { courseInfo, saveCourseInfo, clearCourseInfo } + async function createCourseFromXlsx(): Promise { + const wb = await window.electron.readXLSX() + if (wb === null) { + ElMessage({ + type: 'error', + message: '无法打开文件' + }) + return false + } + + const sheet = wb.Sheets['Sheet1'] + if (!sheet) { + ElMessage({ + type: 'error', + message: '没有名为 Sheet1 的工作簿,请检查文件是否正确' + }) + return false + } + + studentList.value = [] + const classList: string[] = [] + + for (let i = 6; ; i++) { + if (sheet[`A${i}`] === undefined) { + break + } + + const stu = { + className: sheet[`E${i}`]['v'], + major: sheet[`D${i}`]['v'], + name: sheet[`C${i}`]['v'], + resitTag: false, + serialNumber: sheet[`B${i}`]['v'] + } + + if (!classList.includes(stu.className)) { + classList.push(stu.className) + } + + studentList.value.push(stu) + } + + let classStr = '' + if (classList.length > 0) { + try { + // 假设字符串格式为:两位数字 + 非空白字符(基础部分),后面紧跟中文括号括起的数字部分 + const reg = /^([0-9]{2}\S+?)((\d+))$/ + const firstMatch = classList[0].match(reg) + if (!firstMatch) { + throw new Error('输入字符串格式不正确') + } + + const base = firstMatch[1] + const startNum = parseInt(firstMatch[2], 10) + + // 合并后保留一个基础部分,并按数组长度顺序拼接递增的括号数字 + classStr = base + for (let i = 0; i < classList.length; i++) { + classStr += `(${startNum + i})` + } + } catch (e) { + console.error(e) + classStr = '' + for (let i = 0; i < classList.length; i++) { + classStr += classList[i] + if (i !== classList.length - 1) { + classStr += '、' + } + } + } + } + + const info: ICourse = { + campus: sheet['A4']['v'].split(':')[1], + className: classStr, + credit: parseFloat(sheet['E4']['v'].split(':')[1]), + id: sheet['A3']['v'].split(':')[1], + master: '', + name: sheet['E3']['v'].split(':')[1], + teacher: sheet['D4']['v'].split(':')[1] + } + + console.log(classList) + courseInfo.value = info + return true + } + + return { courseInfo, saveCourseInfo, clearCourseInfo, createCourseFromXlsx } }) diff --git a/src/renderer/src/views/create/CreateStart.vue b/src/renderer/src/views/create/CreateStart.vue index e24fa4c..6b04fa6 100644 --- a/src/renderer/src/views/create/CreateStart.vue +++ b/src/renderer/src/views/create/CreateStart.vue @@ -1,7 +1,14 @@