Browse Source

78784

master
lichong 7 months ago
commit
72be571c80
  1. 9
      front/.editorconfig
  2. 5
      front/.gitignore
  3. 1
      front/.npmrc
  4. 6
      front/.prettierignore
  5. 6
      front/.prettierrc
  6. 34
      front/README.md
  7. 12
      front/build/entitlements.mac.plist
  8. BIN
      front/build/icon.icns
  9. BIN
      front/build/icon.ico
  10. 36
      front/build/notarize.js
  11. 43
      front/electron-builder.yml
  12. 28
      front/electron.vite.config.js
  13. 6
      front/jsconfig.json
  14. 41
      front/package.json
  15. BIN
      front/public/icon/icon.jpg
  16. 72
      front/src/main/index.js
  17. 21
      front/src/preload/dl.js
  18. 21
      front/src/preload/index.js
  19. 18
      front/src/renderer/index.html
  20. 141
      front/src/renderer/src/App.vue
  21. 5
      front/src/renderer/src/assets/css/base.css
  22. 9
      front/src/renderer/src/assets/js/db.js
  23. 343
      front/src/renderer/src/assets/json/student.json
  24. 77
      front/src/renderer/src/components/formcomponent.vue
  25. 146
      front/src/renderer/src/components/tablecomponent.vue
  26. 27
      front/src/renderer/src/main.js
  27. 390
      front/src/renderer/src/views/student.vue
  28. 9
      jihuoqi/.editorconfig
  29. 5
      jihuoqi/.gitignore
  30. 1
      jihuoqi/.npmrc
  31. 6
      jihuoqi/.prettierignore
  32. 6
      jihuoqi/.prettierrc
  33. 34
      jihuoqi/README.md
  34. 12
      jihuoqi/build/entitlements.mac.plist
  35. BIN
      jihuoqi/build/icon.icns
  36. BIN
      jihuoqi/build/icon.ico
  37. 36
      jihuoqi/build/notarize.js
  38. 43
      jihuoqi/electron-builder.yml
  39. 28
      jihuoqi/electron.vite.config.js
  40. 6
      jihuoqi/jsconfig.json
  41. 36
      jihuoqi/package.json
  42. BIN
      jihuoqi/public/icon/icon.jpg
  43. 71
      jihuoqi/src/main/index.js
  44. 21
      jihuoqi/src/preload/dl.js
  45. 21
      jihuoqi/src/preload/index.js
  46. 18
      jihuoqi/src/renderer/index.html
  47. 119
      jihuoqi/src/renderer/src/App.vue
  48. 5
      jihuoqi/src/renderer/src/assets/css/base.css
  49. 25
      jihuoqi/src/renderer/src/main.js

9
front/.editorconfig

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

5
front/.gitignore

@ -0,0 +1,5 @@
node_modules
dist
out
*.log*
package-lock.json

1
front/.npmrc

@ -0,0 +1 @@
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/

6
front/.prettierignore

@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json

6
front/.prettierrc

@ -0,0 +1,6 @@
{
"singleQuote": true,
"semi": false,
"printWidth": 100,
"trailingComma": "none"
}

34
front/README.md

@ -0,0 +1,34 @@
# my-app
An Electron application with Vue
## Recommended IDE Setup
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## Project Setup
### Install(node 16.20.1)
```bash
$ npm install
```
### Development
```bash
$ npm run dev
```
### Build
```bash
# For windows
$ npm run build:win
# For macOS
$ npm run build:mac
# For Linux
$ npm run build:linux
```

12
front/build/entitlements.mac.plist

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

BIN
front/build/icon.icns

Binary file not shown.

BIN
front/build/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

36
front/build/notarize.js

@ -0,0 +1,36 @@
const { notarize } = require('electron-notarize')
module.exports = async (context) => {
if (process.platform !== 'darwin') return
console.log('aftersign hook triggered, start to notarize app.')
if (!process.env.CI) {
console.log(`skipping notarizing, not in CI.`)
return
}
if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) {
console.warn('skipping notarizing, APPLE_ID and APPLE_ID_PASS env variables must be set.')
return
}
const appId = 'com.electron.app'
const { appOutDir } = context
const appName = context.packager.appInfo.productFilename
try {
await notarize({
appBundleId: appId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLEIDPASS
})
} catch (error) {
console.error(error)
}
console.log(`done notarizing ${appId}.`)
}

43
front/electron-builder.yml

@ -0,0 +1,43 @@
appId: com.electron.lichong
productName: 桌面应用
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
asarUnpack:
- '**/*.{node,dll}'
afterSign: build/notarize.js
win:
executableName: lichong-app
nsis:
oneClick: false
artifactName: ${name}-${version}-setup.${ext}
allowToChangeInstallationDirectory: true
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://example.com/auto-updates

28
front/electron.vite.config.js

@ -0,0 +1,28 @@
import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
},
preload: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: {
dl: resolve(__dirname, 'src/preload/dl.js'),
index: resolve(__dirname, 'src/preload/index.js')
}
}
}
},
renderer: {
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
}
},
plugins: [vue()]
}
})

6
front/jsconfig.json

@ -0,0 +1,6 @@
{
"exclude": [
"node_modules",
"public"
]
}

41
front/package.json

@ -0,0 +1,41 @@
{
"name": "electron",
"version": "1.0.0",
"description": "An Electron application with Vue",
"main": "./out/main/index.js",
"author": "lichong",
"homepage": "https://www.electronjs.org",
"scripts": {
"npmi": "npm i",
"dev": "electron-vite dev",
"build": "electron-vite build",
"build:win": "npm run build && electron-builder --win --config",
"build:mac": "npm run build && electron-builder --mac --config",
"build:linux": "npm run build && electron-builder --linux --config"
},
"dependencies": {
"@electron-toolkit/preload": "^1.0.2",
"@electron-toolkit/utils": "^1.0.2",
"dayjs": "^1.11.11",
"dexie": "^4.0.8",
"element-plus": "^2.7.1",
"js-sha256": "^0.11.0",
"lodash": "^4.17.21",
"node-machine-id": "^1.1.12",
"vue-web-screen-shot": "^1.5.3",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@vitejs/plugin-vue": "^3.1.2",
"@vue/eslint-config-prettier": "^7.0.0",
"electron": "^20.3.2",
"electron-builder": "^23.6.0",
"electron-notarize": "^1.2.1",
"electron-vite": "^1.0.11",
"less": "^4.1.3",
"prettier": "^2.7.1",
"vite": "^3.1.8",
"vue": "^3.2.41"
}
}

BIN
front/public/icon/icon.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

72
front/src/main/index.js

@ -0,0 +1,72 @@
import { app, shell, BrowserWindow, nativeImage, ipcMain } from 'electron'
import * as path from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import { machineIdSync } from 'node-machine-id';
// logo
const logoIcon = nativeImage.createFromPath(path.join(__dirname, '../../public/icon/icon.jpg'))
// 主窗口
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
minWidth: 1366,
minHeight: 980,
show: false,
autoHideMenuBar: true,
icon: logoIcon,
webPreferences: {
preload: path.resolve(__dirname, '../preload/index.js'),
sandbox: false
}
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
// mainWindow.webContents.openDevTools()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// mainWindow.loadURL('http://localhost:5173/')
mainWindow.on('close', () => {
app.exit()
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
}
}
app.whenReady().then(() => {
electronApp.setAppUserModelId('com.electron')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
ipcMain.on('deviceIdentify', () => {
let res = machineIdSync()
mainWindow.webContents.send(`deviceResult`, res)
})
})
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.

21
front/src/preload/dl.js

@ -0,0 +1,21 @@
import { contextBridge, clipboard } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
contextBridge.exposeInMainWorld('elecClipboard', clipboard)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}

21
front/src/preload/index.js

@ -0,0 +1,21 @@
import { contextBridge, clipboard } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
contextBridge.exposeInMainWorld('elecClipboard', clipboard)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}

18
front/src/renderer/index.html

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据统计</title>
<!-- <link rel="icon" href="/icon/icon.jpg" /> -->
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self';img-src 'self' data:; style-src 'self' 'unsafe-inline'" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

141
front/src/renderer/src/App.vue

@ -0,0 +1,141 @@
<template>
<div class="appClass" v-if="isVip">
<studentComponent ref="student"> </studentComponent>
</div>
<div v-else class="noVip">
<div style="margin: 98px 0;">
<el-input v-model="zhucema" disabled style="width: 840px" placeholder="请输入激活码" size="large">
<template #prepend>
<el-button type="primary" @click="copyzuce">复制注册码</el-button>
</template>
<template #append>
<el-button type="primary" @click="zhuce">生成注册码</el-button>
</template>
</el-input>
</div>
<div>
<el-input v-model.trim="jihuoma" style="width: 840px" placeholder="请输入激活码" size="large">
<template #append>
<el-button type="primary" @click="jihuo">激活</el-button>
</template>
</el-input>
</div>
</div>
</template>
<script>
import _ from 'lodash'
import dayjs from 'dayjs'
import { sha256 } from 'js-sha256';
import { myDatabase } from './assets/js/db.js'
import studentComponent from "./views/student.vue"
import { ElMessage } from 'element-plus';
export default {
name: 'app',
components: { studentComponent },
data() {
return {
_: _,
dayjs: dayjs,
isVip: false,
jihuoma: "",
zhucema: "",
}
},
watch: {},
computed: {},
methods: {
zhuce() {
window.electron.ipcRenderer.send('deviceIdentify')
},
//
watchDevice() {
let that = this
window.electron.ipcRenderer.on('deviceResult', (eve, res) => {
that.zhucema = res
localStorage.setItem('zhucema', res)
})
},
async copyzuce() {
if (!_.trim(this.zhucema)) {
ElMessage({
type: "error",
message: "请生成注册码后,在复制",
duration: 3000
})
} else {
try {
await navigator.clipboard.writeText(_.trim(this.zhucema));
ElMessage(
{
type: 'success',
message: '注册码复制成功,请发送给管理员进行激活',
duration: 3000
}
);
} catch (err) {
alert('无法复制,请手动选择复制');
}
}
},
jihuo() {
let minsha = sha256(_.trim(this.zhucema))
minsha = _.toUpper(minsha)
if (minsha === _.trim(this.jihuoma)) {
this.isVip = true
localStorage.setItem('isVip', JSON.stringify({ isVip: this.isVip }))
} else {
this.isVip = false
ElMessage({
type: 'error',
message: '激活码错误,请输入正确的激活码后使用',
showClose: true,
duration: 0
})
}
let params = {
jihuoma: _.trim(this.jihuoma),
zhucema: _.trim(this.zhucema),
isVip: this.isVip,
create_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
update_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
}
myDatabase.jihuoshijian.add(params)
},
},
async mounted() {
this.watchDevice()
this.zhucema = localStorage.getItem('zhucema')
let isVipObj = JSON.parse(localStorage.getItem('isVip') || "{}")
if (!_.isEmpty(isVipObj)) {
if (dayjs().isBefore(dayjs(isVipObj.endTime)) && dayjs().isAfter(dayjs(isVipObj.startTime))) {
this.isVip = isVipObj.isVip
} else {
this.isVip = false
}
}
if (!this.isVip) {
this.zhuce()
}
},
}
</script>
<style scoped>
.appClass {
width: 100vw;
height: 100vh;
position: relative;
}
.noVip {
text-align: center;
margin-top: calc(50vh - 98px);
}
.noVipTag {
font-size: 48px;
line-height: 48px;
padding: 36px;
}
</style>

5
front/src/renderer/src/assets/css/base.css

@ -0,0 +1,5 @@
html,
body {
margin: 0;
padding: 0;
}

9
front/src/renderer/src/assets/js/db.js

@ -0,0 +1,9 @@
import Dexie from 'dexie';
export const myDatabase = new Dexie('myDatabase');
// 定义数据库版本
myDatabase.version(1).stores({
student: '++id, nianjicode, banjicode, nianji, banji, xuejihao, minzudaima, minzu, name, gender, birth, address, height, weight, BMI, BMIscore, lungs, lungsccore, fifty, fiftyscore, sitforward, sitforwardscore, jump, jumpscore, situp, situpscore, fiftyeight, fiftyeightscore, stand, standscore, eighty, eightyscore, thousand ,thousandscore, pullup, pullupscore, totalscore, level, note1, nodt2, create_at, update_at',
jihuoshijian: '++id, jihuoma, isVip, zhucema, create_at, update_at'
});

343
front/src/renderer/src/assets/json/student.json

@ -0,0 +1,343 @@
[
{
"label": "年级编号",
"prop": "nianjicode",
"type": "text",
"index": "0",
"fixed": true,
"tableShow": true,
"formShow": true
},
{
"label": "班级编号",
"prop": "banjicode",
"type": "text",
"index": "1",
"fixed": true,
"tableShow": true,
"formShow": true
},
{
"label": "年级",
"prop": "nianji",
"type": "text",
"index": "-1",
"tableShow": false,
"formShow": false
},
{
"label": "班级名称",
"prop": "banji",
"type": "text",
"index": "2",
"fixed": true,
"tableShow": true,
"formShow": true
},
{
"label": "学籍号",
"prop": "xuejihao",
"type": "text",
"index": "3",
"fixed": true,
"tableShow": true,
"formShow": true
},
{
"label": "民族代码",
"prop": "minzudaima",
"type": "text",
"index": "4",
"tableShow": true,
"formShow": true
},
{
"label": "民族",
"prop": "minzu",
"type": "text",
"index": "-1",
"tableShow": true,
"formShow": true
},
{
"label": "姓名",
"prop": "name",
"type": "text",
"index": "5",
"fixed": true,
"tableShow": true,
"formShow": true
},
{
"label": "性别",
"prop": "gender",
"tableDisplay": [
{
"label": "男",
"value": "1"
},
{
"label": "女",
"value": "2"
}
],
"type": "text",
"index": "6",
"tableShow": true,
"formShow": true
},
{
"label": "出生日期",
"prop": "birth",
"type": "date",
"index": "7",
"tableShow": true,
"formShow": true
},
{
"label": "家庭住址",
"prop": "address",
"type": "text",
"index": "8",
"tableShow": true,
"formShow": true
},
{
"label": "身高",
"prop": "height",
"type": "number",
"index": "9",
"tableShow": true,
"formShow": true
},
{
"label": "体重",
"prop": "weight",
"type": "number",
"index": "10",
"tableShow": true,
"formShow": true
},
{
"label": "BMI",
"prop": "BMI",
"type": "number",
"index": "11",
"tableShow": true,
"formShow": true
},
{
"label": "BMI得分",
"prop": "BMIscore",
"type": "number",
"index": "12",
"tableShow": true,
"formShow": true
},
{
"label": "肺活量",
"prop": "lungs",
"type": "number",
"index": "13",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "lungsccore",
"type": "number",
"index": "14",
"tableShow": true,
"formShow": true
},
{
"label": "50米跑",
"prop": "fifty",
"type": "number",
"index": "15",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "fiftyscore",
"type": "number",
"index": "16",
"tableShow": true,
"formShow": true
},
{
"label": "坐位体前屈",
"prop": "sitforward",
"type": "number",
"index": "17",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "sitforwardscore",
"type": "number",
"index": "18",
"tableShow": true,
"formShow": true
},
{
"label": "一分钟跳绳",
"prop": "jump",
"type": "number",
"index": "19",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "jumpscore",
"type": "number",
"index": "20",
"tableShow": true,
"formShow": true
},
{
"label": "一分钟仰卧起坐",
"prop": "situp",
"type": "number",
"index": "21",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "situpscore",
"type": "number",
"index": "22",
"tableShow": true,
"formShow": true
},
{
"label": "50米×8往返跑",
"prop": "fiftyeight",
"type": "number",
"index": "23",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "fiftyeightscore",
"type": "number",
"index": "24",
"tableShow": true,
"formShow": true
},
{
"label": "立定跳远",
"prop": "stand",
"type": "number",
"index": "25",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "standscore",
"type": "number",
"index": "26",
"tableShow": true,
"formShow": true
},
{
"label": "800米跑",
"prop": "eighty",
"type": "number",
"index": "27",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "eightyscore",
"type": "number",
"index": "28",
"tableShow": true,
"formShow": true
},
{
"label": "1000米跑",
"prop": "thousand",
"type": "number",
"index": "29",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "thousandscore",
"type": "number",
"index": "30",
"tableShow": true,
"formShow": true
},
{
"label": "引体向上",
"prop": "pullup",
"type": "number",
"index": "31",
"tableShow": true,
"formShow": true
},
{
"label": "得分",
"prop": "pullupscore",
"type": "number",
"index": "32",
"tableShow": true,
"formShow": true
},
{
"label": "总分",
"prop": "totalscore",
"type": "number",
"index": "33",
"tableShow": true,
"formShow": true
},
{
"label": "等级",
"prop": "level",
"type": "text",
"index": "34",
"tableShow": true,
"formShow": true
},
{
"label": "备注1",
"prop": "nodt1",
"type": "text",
"index": "35",
"tableShow": false,
"formShow": false
},
{
"label": "备注2",
"prop": "nodt2",
"type": "text",
"index": "36",
"tableShow": false,
"formShow": false
},
{
"label": "创建时间",
"prop": "create_at",
"type": "date",
"tableShow": false,
"formShow": false
},
{
"label": "更新时间",
"prop": "update_at",
"type": "date",
"tableShow": false,
"formShow": false
}
]

77
front/src/renderer/src/components/formcomponent.vue

@ -0,0 +1,77 @@
<template>
<div class="tableClass">
<el-form :model="formData" label-suffix="" :disabled="disabled" inline>
<el-form-item :label="formItem.label" :key="formIndex" v-for="(formItem, formIndex) in formHeader">
<template v-if="formItem.type === 'text'">
<el-input v-model="formData[formItem.prop]" />
</template>
<template v-else-if="formItem.type === 'number'">
<el-input-number v-model="formData[formItem.prop]" :min="formItem.min" :max="formItem.max">
<template #decrease-icon>
<el-icon>
<ArrowDown />
</el-icon>
</template>
<template #increase-icon>
<el-icon>
<ArrowUp />
</el-icon>
</template>
</el-input-number>
</template>
<template v-else-if="formItem.type === 'date'">
<el-date-picker v-model="formData[formItem.prop]" type="date"
:disabled="['create_at', 'update_at'].indexOf(formItem.prop) !== -1" :placeholder="`请选择${formItem.label}`" />
</template>
</el-form-item>
</el-form>
</div>
</template>
<script>
import _, { max, min } from 'lodash'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'formcomponent',
components: {},
props: {
formHeader: {
type: Array,
default: () => {
return []
}
},
formData: {
type: Object,
default: () => {
return {}
}
},
disabled: {
type: Boolean,
default: () => {
return false
}
}
},
data() {
return {
_: _,
dayjs: dayjs,
}
},
methods: {},
async mounted() { },
watch: {},
computed: {}
}
</script>
<style scoped>
.tableClass {
text-align: left;
text-align-last: left;
}
</style>

146
front/src/renderer/src/components/tablecomponent.vue

@ -0,0 +1,146 @@
<template>
<div class="tableClass">
<el-table :data="tableData" height="calc(100vh - 96px)" border @select="selectChange" @select-all="selectChange" fit
:row-key="getRowKeys" ref="tableRef">
<el-table-column type="selection" width="50" :reserve-selection="true" fixed="left">
</el-table-column>
<el-table-column type="index" v-if="hiddenXuhao" width="60" :reserve-selection="true" fixed="left" label="序号">
<template #default="scope">
<span>{{ pageSize * (currentPage - 1) + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column v-for="(headerItem, headerIndex) in tableHeader" :label="headerItem.label" show-overflow-tooltip
:key="headerIndex" :min-width="`${headerItem.label.length * 23 + 8}px`" :fixed="!!headerItem.fixed">
<template #default="scope">
<template v-if="headerItem.type === 'text'">
<span>{{ scope.row[headerItem.prop] }}</span>
</template>
<template v-else-if="headerItem.type === 'date'">
<span>{{ scope.row[headerItem.prop] }}</span>
</template>
<template v-else-if="headerItem.type === 'number'">
<span>{{ scope.row[headerItem.prop] }}</span>
</template>
<template v-else>{{ scope.row[headerItem.prop] }}</template>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="145">
<template #default="scope">
<el-button type="primary" circle @click="optClick(scope.row, 'edit')">
<el-icon>
<Edit />
</el-icon>
</el-button>
<el-button type="info" circle @click="optClick(scope.row, 'info')">
<el-icon>
<InfoFilled />
</el-icon>
</el-button>
<el-button type="danger" circle @click="optClick(scope.row, 'del')">
<el-icon>
<Delete />
</el-icon>
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination style="margin:8px 0 0 0;place-content:center;" v-model:current-page="currentPage"
v-model:page-size="pageSize" :total="total" layout="total, sizes, prev, pager, next, jumper"
:page-sizes="pageSizes"></el-pagination>
</div>
</template>
<script>
import _ from 'lodash'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'tablecomponent',
components: {},
emits: ["selectChange", "handleCurrentChange", "handleSizeChange", "edit", "info", "del"],
props: {
tableHeader: {
type: Array,
default: () => {
return []
}
},
tableData: {
type: Array,
default: () => {
return []
}
},
pageSizes: {
type: Array,
default: () => {
return [12, 50, 100, 200]
}
},
total: {
type: Number,
default: () => {
return 0
}
},
hiddenXuhao: {
type: Boolean,
default: () => {
return false
}
},
},
data() {
return {
_: _,
dayjs: dayjs,
currentPage: 1,
pageSize: 12,
}
},
methods: {
selectChange(selection) {
this.$emit("selectChange", selection)
},
handleSizeChange(pageSize) {
this.$emit("handleSizeChange", pageSize)
},
handleCurrentChange(currentPage) {
this.$emit("handleCurrentChange", currentPage)
},
optClick(row, type) {
this.$emit(type, row)
},
// key
getRowKeys(row) {
return row.id; //
},
clearSelection() {
this.$refs.tableRef.clearSelection()
}
},
async mounted() { },
watch: {
currentPage: {
handler(val) {
this.$emit("handleCurrentChange", val)
},
immediate: true
},
pageSize: {
handler(val) {
this.$emit("handleSizeChange", val)
},
immediate: true
},
},
computed: {}
}
</script>
<style scoped>
.tableClass {
text-align: center;
text-align-last: center;
}
</style>

27
front/src/renderer/src/main.js

@ -0,0 +1,27 @@
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import App from './App.vue'
import './assets/css/base.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import screenShort from "vue-web-screen-shot";
const app = createApp(App)
// window.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus, {
locale: zhCn,
})
app.use(screenShort, { enableWebRtc: false,level:300001,hiddenScrollBar:true })
app.mount('#app')
document.addEventListener('keydown', (event) => {
const { ctrlKey, shiftKey, key } = event
if ((ctrlKey && shiftKey && key === 'I') || key === 'F12') {
return event.preventDefault()
}
})

390
front/src/renderer/src/views/student.vue

@ -0,0 +1,390 @@
<template>
<div class="rightClass">
<div>
<el-button type="primary" @click="addData">
<el-icon>
<Plus />
</el-icon>
<span>新增</span>
</el-button>
<el-button type="danger" @click="deleteData(selectionData)" :disabled="selectionData.length == 0">
<el-icon>
<Delete />
</el-icon>
<span>删除</span>
</el-button>
<el-button type="danger" @click="delAll">
<el-icon>
<Delete />
</el-icon>
<span>清空</span>
</el-button>
<div class="inputClass uploadClass">
<el-upload :show-file-list="false" v-model="fileOriData" :before-upload="beforeAvatarUpload"
:http-request="successSubmit" accept=".xls,.xlsx,.csv">
<el-button type="success">
<el-icon>
<Upload />
</el-icon>
<span>导入</span>
</el-button>
</el-upload>
</div>
<el-button type="primary" @click="exportData">
<el-icon>
<Download />
</el-icon>
<span>导出</span>
</el-button>
<el-button type="primary" @click="searchData">
<el-icon>
<Refresh />
</el-icon>
<span>刷新</span>
</el-button>
<div class="inputClass">
<el-input v-model.trim="searchParams.name" style="max-width: 600px" placeholder="请输入姓名" clearable
@clear="searchData" @keyup.enter="searchData">
<template #append>
<el-button @click="searchData">
<el-icon>
<Search />
</el-icon>
</el-button>
</template>
</el-input>
</div>
</div>
<div>
<tablecomponent :tableHeader="tableHeader" :tableData="tableData" :pageSizes="pageSizes" :total="total"
@selectChange="selectChange" @handleCurrentChange="handleCurrentChange" @handleSizeChange="handleSizeChange"
@edit="edit" @info="info" @del="del" ref="tableComponentRef">
</tablecomponent>
</div>
<el-dialog v-model="dialogFrom.visible" :title="dialogFrom.title" width="80%">
<formcomponent :formHeader="dialogFrom.formHeader" :formData="dialogFrom.formData"
:disabled="dialogFrom.disabled">
</formcomponent>
<template #footer>
<div>
<el-button @click="cancelDialog">取消</el-button>
<el-button type="primary" @click="submitDialog"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import _ from 'lodash'
import * as XLSX from 'xlsx';
import tableHeaderLocal from '../assets/json/student.json'
import { myDatabase } from '../assets/js/db.js'
import dayjs from 'dayjs'
import tablecomponent from "../components/tablecomponent.vue"
import formcomponent from "../components/formcomponent.vue"
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'student',
components: { tablecomponent, formcomponent },
data() {
return {
_: _,
dayjs: dayjs,
tableHeader: [],
formHeader: [],
tableData: [],
currentPage: 1,
pageSize: 12,
pageSizes: [12, 50, 100, 200],
total: 0,
searchParams: {
name: ""
},
selectionData: [],
dialogFrom: {
visible: false,
title: "新增学生",
type: "add",
formHeader: [],
formData: {}
},
}
},
methods: {
/**
* 上传表格检查
*/
beforeAvatarUpload(rawFile) {
let imgList = ['text/csv', 'application/vnd.ms-excel', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
if (imgList.indexOf(rawFile.type) === -1) {
this.$msgbox.alert('请上传excel,csv格式的表格文件!')
return false
} else if (rawFile.size / 1024 / 1024 > 50) {
this.$msgbox.alert('表格文件的大小为小于50MB,数据过多时会处理过慢')
return true
}
return true
},
//
async successSubmit(opts) {
let that = this
let file = opts.file
this.fileDealData = []
let fileReader = new FileReader()
fileReader.onload = async function () {
let data = this.result
let workbook = that.$XLSX.read(data, { type: 'binary' })
let sheetName = workbook.SheetNames[0]
let sheetData = that.$XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1, defval: '' })
let list = []
}
fileReader.onerror = function (error) {
console.error('Error reading file:', error)
}
fileReader.readAsArrayBuffer(file)
},
//
async updateSeach(params = {}) {
let studentList = []
let totalList = 0
if (params.name) {
studentList = await myDatabase.student.where('name').anyPass(studentItem => studentItem.includes(params.name)).offset((this.currentPage - 1) * this.pageSize).limit(this.pageSize).toArray()
totalList = await myDatabase.student.where('name').anyPass(studentItem => studentItem.includes(params.name)).toArray()
} else {
studentList = await myDatabase.student.offset((this.currentPage - 1) * this.pageSize).limit(this.pageSize).toArray()
totalList = await myDatabase.student.toArray()
}
this.total = totalList.length
this.tableData = _.cloneDeep(studentList)
},
//
async searchData() {
let params = {}
if (this.searchParams.name) {
params["name"] = { $regex: `${_.trim(this.searchParams.name)}`, $options: 'i' }
}
await this.updateSeach(params)
},
//
selectChange(selection) {
this.selectionData = _.cloneDeep(selection)
},
//
async handleCurrentChange(currentPage) {
this.currentPage = currentPage
await this.searchData()
},
//
async handleSizeChange(pageSize) {
this.currentPage = 1
this.pageSize = pageSize
await this.searchData()
},
//
cancelDialog() {
this.dialogFrom = {
visible: false,
title: "新增学生",
type: "add",
formHeader: [],
formData: {}
}
},
//
async submitDialog() {
for (let i = 0; i < this.formHeader.length; i++) {
let headItem = this.formHeader[i];
if (headItem.type === "date") {
this.dialogFrom.formData[headItem.prop] = dayjs(this.dialogFrom.formData[headItem.prop]).format("YYYY-MM-DD")
}
}
let params = {
...this.dialogFrom.formData, create_at: dayjs().format('YYYY-MM-DD HH:mm:ss'), update_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
}
if (this.dialogFrom.type === "put") {
delete params.create_at
}
try {
await myDatabase.student[this.dialogFrom.type]({ ...this.dialogFrom.formData, update_at: dayjs().format('YYYY-MM-DD HH:mm:ss') })
await this.searchData()
this.cancelDialog()
} catch (e) {
ElMessage(
{
type: "error",
message: "时间不允许重复,请重新设置时间"
}
)
}
},
//
addData() {
let formData = {}
for (let i = 0; i < this.formHeader.length; i++) {
let element = this.formHeader[i];
if (element.type === "text") {
formData[element.prop] = ""
} else if (element.type === "date") {
formData[element.prop] = dayjs().format("YYYY-MM-DD")
} else if (element.type === "number") {
formData[element.prop] = 0
}
}
this.dialogFrom = {
visible: true,
title: "新增学生",
type: "add",
disabled: false,
formHeader: this.formHeader,
formData
}
},
//
edit(row) {
this.dialogFrom = {
visible: true,
title: "编辑学生",
type: "put",
disabled: false,
formHeader: this.formHeader,
formData: { ...row }
}
},
//
info(row) {
this.dialogFrom = {
visible: true,
title: "查看学生",
type: "info",
disabled: true,
formHeader: this.formHeader,
formData: { ...row }
}
},
//
del(row) {
this.deleteData([row])
},
//
deleteData(delList) {
let tooltipList = []
let idList = []
for (let i = 0; i < delList.length; i++) {
let element = delList[i];
tooltipList.push(element.name)
idList.push(element.id)
}
ElMessageBox.confirm(
`是否删除(${_.join(tooltipList, ",")})?`,
'danger',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'danger',
}
)
.then(async () => {
this.$refs.tableComponentRef.clearSelection()
await myDatabase.student.bulkDelete(idList)
await this.searchData()
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消删除',
})
})
},
//
delAll() {
ElMessageBox.confirm(
`是否删除全部数据?此操作不可逆!`,
'危险操作',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'danger',
}
)
.then(async () => {
this.$refs.tableComponentRef.clearSelection()
await myDatabase.student.clear()
await this.searchData()
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消删除',
})
})
},
//
importStudent() { },
//
async exportData() {
let listCopy = _.cloneDeep(this.selectionData)
if (!listCopy.length) {
listCopy = await myDatabase.student.toArray()
}
let lilstLocal = []
if (listCopy.length) {
for (let i = 0; i < listCopy.length; i++) {
let item = listCopy[i];
let listItem = {}
for (let j = 0; j < tableHeaderLocal.length; j++) {
let headerItem = tableHeaderLocal[j];
listItem[headerItem.label] = item[headerItem.prop]
}
lilstLocal.push(listItem)
}
let jsonWorkSheet = XLSX.utils.json_to_sheet(lilstLocal);
let workBook = {
SheetNames: ["sheet1"],
Sheets: {
["sheet1"]: jsonWorkSheet,
}
};
XLSX.writeFile(workBook, `学生信息${dayjs().format("YYYY-MM-DD_HH-mm-ss")}.xls`);
} else {
ElMessage({
type: 'error',
message: '当前无数据,请输入数据后导出',
})
}
}
},
async mounted() {
this.tableHeader = _.filter(tableHeaderLocal, o => o.tableShow)
this.formHeader = _.filter(tableHeaderLocal, o => o.formShow)
await this.updateSeach()
},
watch: {},
computed: {}
}
</script>
<style scoped>
.rightClass {
padding: 8px;
}
.uploadClass {
top: 3px;
}
.inputClass {
display: inline-block;
margin: 0 8px;
position: relative;
}
.tagClass {
display: inline-block;
margin: 0 8px;
width: 200px;
position: relative;
}
</style>

9
jihuoqi/.editorconfig

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

5
jihuoqi/.gitignore

@ -0,0 +1,5 @@
node_modules
dist
out
*.log*
package-lock.json

1
jihuoqi/.npmrc

@ -0,0 +1 @@
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/

6
jihuoqi/.prettierignore

@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json

6
jihuoqi/.prettierrc

@ -0,0 +1,6 @@
{
"singleQuote": true,
"semi": false,
"printWidth": 100,
"trailingComma": "none"
}

34
jihuoqi/README.md

@ -0,0 +1,34 @@
# my-app
An Electron application with Vue
## Recommended IDE Setup
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## Project Setup
### Install(node 16.20.1)
```bash
$ npm install
```
### Development
```bash
$ npm run dev
```
### Build
```bash
# For windows
$ npm run build:win
# For macOS
$ npm run build:mac
# For Linux
$ npm run build:linux
```

12
jihuoqi/build/entitlements.mac.plist

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

BIN
jihuoqi/build/icon.icns

Binary file not shown.

BIN
jihuoqi/build/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

36
jihuoqi/build/notarize.js

@ -0,0 +1,36 @@
const { notarize } = require('electron-notarize')
module.exports = async (context) => {
if (process.platform !== 'darwin') return
console.log('aftersign hook triggered, start to notarize app.')
if (!process.env.CI) {
console.log(`skipping notarizing, not in CI.`)
return
}
if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) {
console.warn('skipping notarizing, APPLE_ID and APPLE_ID_PASS env variables must be set.')
return
}
const appId = 'com.electron.jihuoqi'
const { appOutDir } = context
const appName = context.packager.appInfo.productFilename
try {
await notarize({
appBundleId: appId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLEIDPASS
})
} catch (error) {
console.error(error)
}
console.log(`done notarizing ${appId}.`)
}

43
jihuoqi/electron-builder.yml

@ -0,0 +1,43 @@
appId: com.electron.jihuoqi
productName: 激活器
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
asarUnpack:
- '**/*.{node,dll}'
afterSign: build/notarize.js
win:
executableName: jihuoqi-app
nsis:
oneClick: false
artifactName: ${name}-${version}-setup.${ext}
allowToChangeInstallationDirectory: true
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://example.com/auto-updates

28
jihuoqi/electron.vite.config.js

@ -0,0 +1,28 @@
import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
},
preload: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: {
dl: resolve(__dirname, 'src/preload/dl.js'),
index: resolve(__dirname, 'src/preload/index.js')
}
}
}
},
renderer: {
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
}
},
plugins: [vue()]
}
})

6
jihuoqi/jsconfig.json

@ -0,0 +1,6 @@
{
"exclude": [
"node_modules",
"public"
]
}

36
jihuoqi/package.json

@ -0,0 +1,36 @@
{
"name": "jihuoqi",
"version": "1.0.0",
"description": "An Electron application with Vue",
"main": "./out/main/index.js",
"author": "lichong",
"homepage": "https://www.electronjs.org",
"scripts": {
"npmi": "npm i",
"dev": "electron-vite dev",
"build": "electron-vite build",
"build:win": "npm run build && electron-builder --win --config",
"build:mac": "npm run build && electron-builder --mac --config",
"build:linux": "npm run build && electron-builder --linux --config"
},
"dependencies": {
"@electron-toolkit/preload": "^1.0.2",
"@electron-toolkit/utils": "^1.0.2",
"dayjs": "^1.11.11",
"element-plus": "^2.7.1",
"js-sha256": "^0.11.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@vitejs/plugin-vue": "^3.1.2",
"@vue/eslint-config-prettier": "^7.0.0",
"electron": "^20.3.2",
"electron-builder": "^23.6.0",
"electron-notarize": "^1.2.1",
"electron-vite": "^1.0.11",
"less": "^4.1.3",
"prettier": "^2.7.1",
"vite": "^3.1.8",
"vue": "^3.2.41"
}
}

BIN
jihuoqi/public/icon/icon.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

71
jihuoqi/src/main/index.js

@ -0,0 +1,71 @@
import { app, shell, BrowserWindow, nativeImage } from 'electron'
import * as path from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
// logo
const logoIcon = nativeImage.createFromPath(path.join(__dirname, '../../public/icon/icon.jpg'))
// 主窗口
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
// minWidth: 960,
width: 920,
maxWidth: 960,
// minHeight: 800,
height: 260,
maxHeight: 500,
// frame: false,
autoHideMenuBar: true,
icon: logoIcon,
webPreferences: {
preload: path.resolve(__dirname, '../preload/index.js'),
sandbox: false
}
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
// mainWindow.webContents.openDevTools()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// mainWindow.loadURL('http://localhost:5173/')
mainWindow.on('close', () => {
app.exit()
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
}
}
app.whenReady().then(() => {
electronApp.setAppUserModelId('com.electron')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.

21
jihuoqi/src/preload/dl.js

@ -0,0 +1,21 @@
import { contextBridge, clipboard } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
contextBridge.exposeInMainWorld('elecClipboard', clipboard)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}

21
jihuoqi/src/preload/index.js

@ -0,0 +1,21 @@
import { contextBridge, clipboard } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
contextBridge.exposeInMainWorld('elecClipboard', clipboard)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
}

18
jihuoqi/src/renderer/index.html

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>激活器</title>
<!-- <link rel="icon" href="/icon/icon.jpg" /> -->
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self';img-src 'self' data:; style-src 'self' 'unsafe-inline'"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

119
jihuoqi/src/renderer/src/App.vue

@ -0,0 +1,119 @@
<template>
<div class="appClass" v-if="isVip">
<div style="text-align: center;">
<el-form :model="form" label-width="auto" style="max-width: 900px;margin: 0 0 32px 0;">
<el-form-item label="注册码">
<el-input v-model="form.zhucema" />
</el-form-item>
</el-form>
</div>
<div style="text-align: center;"> &nbsp;{{ jihuoma }}</div>
<div style="text-align: center;position: absolute;bottom: 24px;left:calc(50% - 102px)">
<el-button type="primary" @click="geneJIhuoma">生成激活码</el-button>
<el-button type="primary" @click="copyJIhuoma">复制激活码</el-button>
</div>
</div>
<div v-else class="noVip">
<el-tag type="danger" class="noVipTag">体验已过期请联系管理员</el-tag>
</div>
</template>
<script>
import { sha256 } from 'js-sha256';
import dayjs from 'dayjs'
import _ from 'lodash'
import { ElMessage } from 'element-plus';
export default {
name: 'app',
components: {},
data() {
return {
isVip: false,
jihuoma: "",
form: {
zhucema: "",
},
}
},
methods: {
geneJIhuoma() {
if (!_.trim(this.form.zhucema)) {
ElMessage(
{
type: 'error',
message: '请先输入注册码',
duration: 3000
}
);
} else if (_.trim(this.form.zhucema).length !== 64) {
ElMessage(
{
type: 'error',
message: '请先输入正确的注册码',
duration: 3000
}
);
} else {
let jihuoma = sha256(`${this.form.zhucema}`)
jihuoma = _.toUpper(jihuoma)
this.jihuoma = `${jihuoma}`
}
},
async copyJIhuoma() {
//
if (_.trim(this.jihuoma).length !== 64) {
ElMessage(
{
type: 'error',
message: '请先生成正确的激活码',
duration: 3000
}
);
return
}
try {
await navigator.clipboard.writeText(this.jihuoma);
ElMessage(
{
type: 'success',
message: '复制成功',
duration: 1000
}
);
} catch (err) {
alert('无法复制,请手动选择复制');
}
}
},
async mounted() {
let fiveDay = dayjs('2024-09-27T00:00:00').valueOf()
if (!this.isVip) {
if (dayjs().valueOf() > fiveDay) {
this.isVip = false
return
} else {
this.isVip = true
}
}
},
watch: {},
computed: {}
}
</script>
<style scoped>
.appClass {
width: 100vw;
height: 100vh;
position: relative;
}
.noVip {
text-align: center;
}
.noVipTag {
font-size: 35px;
line-height: 50px;
padding: 29px;
}
</style>

5
jihuoqi/src/renderer/src/assets/css/base.css

@ -0,0 +1,5 @@
html,
body {
margin: 0;
padding: 0;
}

25
jihuoqi/src/renderer/src/main.js

@ -0,0 +1,25 @@
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import App from './App.vue'
import './assets/css/base.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
// window.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = true
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus, {
locale: zhCn,
})
app.mount('#app')
document.addEventListener('keydown', (event) => {
const { ctrlKey, shiftKey, key } = event
if ((ctrlKey && shiftKey && key === 'I') || key === 'F12') {
return event.preventDefault()
}
})
Loading…
Cancel
Save