implement CreateStart.vue feature
This commit is contained in:
parent
5786e77cb1
commit
b3a91b2cb4
@ -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<WorkBook | null> => {
|
||||
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 () {
|
||||
|
7
src/preload/index.d.ts
vendored
7
src/preload/index.d.ts
vendored
@ -1,8 +1,13 @@
|
||||
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||
import { WorkBook } from 'xlsx'
|
||||
|
||||
interface ElectronAPIWithOthers extends ElectronAPI {
|
||||
readXLSX(): Promise<WorkBook | null>
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI
|
||||
electron: ElectronAPIWithOthers;
|
||||
api: unknown
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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<ICourse | null>(null)
|
||||
const studentList = ref<IStudent[]>([])
|
||||
|
||||
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<boolean> {
|
||||
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 }
|
||||
})
|
||||
|
@ -1,7 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import { useGlobalStore } from '@renderer/store'
|
||||
import router from '@renderer/router'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { ICourse } from '@renderer/types'
|
||||
import { ElMessage } from 'element-plus'
|
||||
const store = useGlobalStore()
|
||||
const { courseInfo } = storeToRefs(store)
|
||||
|
||||
const tempCourseInfo = reactive({
|
||||
const tempCourseInfo = ref<ICourse>({
|
||||
campus: '',
|
||||
className: '',
|
||||
credit: 0,
|
||||
@ -10,13 +17,54 @@ const tempCourseInfo = reactive({
|
||||
name: '',
|
||||
teacher: ''
|
||||
})
|
||||
|
||||
const isShowCopyFrom = ref<boolean>(false)
|
||||
|
||||
const enableSubmit = computed(() => !Object.values(tempCourseInfo.value).every((v) => v))
|
||||
|
||||
const handleCreateFrom = async (from: 'xlsx' | 'jwxt') => {
|
||||
if (from === 'xlsx') {
|
||||
if (await store.createCourseFromXlsx()) {
|
||||
ElMessage({ type: 'success', message: '成功' })
|
||||
tempCourseInfo.value = courseInfo.value!
|
||||
}
|
||||
} else if (from === 'jwxt') {
|
||||
console.log(1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => router.back()
|
||||
const handleFormReset = () => {
|
||||
tempCourseInfo.value = {
|
||||
campus: '',
|
||||
className: '',
|
||||
credit: 0,
|
||||
id: '',
|
||||
master: '',
|
||||
name: '',
|
||||
teacher: ''
|
||||
}
|
||||
}
|
||||
const handleShowCopyBtn = () => {
|
||||
isShowCopyFrom.value = (!tempCourseInfo.value.master && tempCourseInfo.value.teacher) as boolean
|
||||
}
|
||||
const handleCopyFromTeacher = () => {
|
||||
tempCourseInfo.value.master = tempCourseInfo.value.teacher
|
||||
isShowCopyFrom.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<p class="text-2xl text-center">创建新的文件</p>
|
||||
<div class="relative flex flex-col items-center justify-center">
|
||||
<div
|
||||
class="absolute top-0 left-32 cursor-pointer rounded border border-gray-300 px-4 shadow hover:bg-gray-100"
|
||||
@click="handleBack"
|
||||
>
|
||||
返回
|
||||
</div>
|
||||
<p class="text-center text-2xl">创建新的文件</p>
|
||||
<el-divider> 填写基本课程信息以继续 </el-divider>
|
||||
<div class="max-w-lg mx-auto">
|
||||
<div class="mx-auto w-full max-w-lg">
|
||||
<el-form :model="tempCourseInfo" label-width="auto">
|
||||
<el-form-item label="课号">
|
||||
<el-input
|
||||
@ -34,8 +82,21 @@ const tempCourseInfo = reactive({
|
||||
<el-form-item label="任课教师">
|
||||
<el-input v-model="tempCourseInfo.teacher" type="text" placeholder="任课教师" />
|
||||
</el-form-item>
|
||||
<el-form-item label="课程负责人">
|
||||
<el-input v-model="tempCourseInfo.master" type="text" placeholder="课程负责人" />
|
||||
<el-form-item label="课程负责人" class="relative">
|
||||
<el-input
|
||||
v-model="tempCourseInfo.master"
|
||||
type="text"
|
||||
placeholder="课程负责人"
|
||||
@focus="handleShowCopyBtn"
|
||||
@blur="handleShowCopyBtn"
|
||||
/>
|
||||
<div
|
||||
class="absolute right-4 text-gray-400 cursor-pointer invisible"
|
||||
:class="{ visible: isShowCopyFrom }"
|
||||
@click="handleCopyFromTeacher"
|
||||
>
|
||||
从任课教师复制
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="学分">
|
||||
<el-input v-model="tempCourseInfo.credit" type="number" placeholder="学分" />
|
||||
@ -44,14 +105,14 @@ const tempCourseInfo = reactive({
|
||||
<el-input v-model="tempCourseInfo.className" type="text" placeholder="教学班级" />
|
||||
</el-form-item>
|
||||
<div class="flex justify-end">
|
||||
<el-button>重置</el-button>
|
||||
<el-button type="primary" disabled>下一步</el-button>
|
||||
<el-button @click="handleFormReset">重置</el-button>
|
||||
<el-button type="primary" :disabled="enableSubmit">下一步</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-divider>或</el-divider>
|
||||
<div class="flex justify-center">
|
||||
<el-button>从 Excel 中导入</el-button>
|
||||
<el-button @click="handleCreateFrom('xlsx')">从 Excel 中导入</el-button>
|
||||
<el-button disabled>从教务系统中导入</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user