lichong 9 months ago
commit
b73725b658
  1. 9
      .editorconfig
  2. 4
      .gitignore
  3. 1
      .npmrc
  4. 6
      .prettierignore
  5. 6
      .prettierrc
  6. 34
      README.md
  7. 12
      build/entitlements.mac.plist
  8. BIN
      build/icon.icns
  9. BIN
      build/icon.ico
  10. 36
      build/notarize.js
  11. 43
      electron-builder.yml
  12. 28
      electron.vite.config.js
  13. 6
      jsconfig.json
  14. 6095
      package-lock.json
  15. 41
      package.json
  16. BIN
      public/icon/icon.jpg
  17. BIN
      public/static/template.docx
  18. 70
      src/main/index.js
  19. 21
      src/preload/dl.js
  20. 21
      src/preload/index.js
  21. 18
      src/renderer/index.html
  22. 190
      src/renderer/src/App.vue
  23. 5
      src/renderer/src/assets/css/base.css
  24. 60002
      src/renderer/src/data.json
  25. 28
      src/renderer/src/main.js

9
.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

4
.gitignore

@ -0,0 +1,4 @@
node_modules
dist
out
*.log*

1
.npmrc

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

6
.prettierignore

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

6
.prettierrc

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

34
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
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
build/icon.icns

Binary file not shown.

BIN
build/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

36
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
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
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
jsconfig.json

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

6095
package-lock.json

File diff suppressed because it is too large

41
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": {
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "electron-vite build",
"postinstall": "electron-builder install-app-deps",
"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",
"@element-plus/icons-vue": "^2.3.1",
"dayjs": "^1.11.11",
"element-plus": "^2.7.1",
"lodash": "^4.17.21",
"vxe-table": "^4.7.38"
},
"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
public/icon/icon.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/static/template.docx

Binary file not shown.

70
src/main/index.js

@ -0,0 +1,70 @@
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'))
console.log(process.versions.node)
// 主窗口
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
// fullscreen: true,
// fullscreenable: true,
width: 800,
height: 600,
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()
})
})
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
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
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
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'; style-src 'self' 'unsafe-inline';font-src 'self' 'unsafe-inline'" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

190
src/renderer/src/App.vue

@ -0,0 +1,190 @@
<template>
<div class="appClass" v-if="isVip">
<div class="title">看看你的尾号</div>
<div class="top">
<el-form ref="ruleFormRef" @submit.prevent :model="form" status-icon label-width="auto" class="demo-ruleForm">
<el-form-item label="手机尾号后四位:" prop="shoujiweihao">
<el-input v-model="form.shoujiweihao" placeholder="请输入手机尾号后四位" autocomplete="off" show-word-limit clearable
minlength="4" maxlength="4" @keyup.enter="onSubmit">
<template #append>
<el-button @click="onSubmit">
<el-icon>
<Search />
</el-icon>
<span>预测</span>
</el-button>
</template>
</el-input>
</el-form-item>
</el-form>
</div>
<div class="bottomTable">
<el-auto-resizer>
<template #default="{ height, width }">
<vxe-table :data="tableData" :height="height" :width="width" border="inner" show-overflow="ellipsis" round>
<!-- <vxe-column type="seq" width="70"></vxe-column> -->
<vxe-column v-for="(headerItem, headerIndex) in tableHeader" :title="headerItem.title" :key="headerIndex"
:field="headerItem.key">
<template #default="{ row, column }">
<span v-if="column.field === 'pingji'">
<el-tag :color="_.split(_.get(pingjicolor, [row.pingji], ''), '_')[0]">
<span :style="`color: ${_.split(_.get(pingjicolor, [row.pingji], ''), '_')[1]};`">
{{ row[column.field] }}
</span>
</el-tag>
</span>
<span v-else-if="column.field === 'pinggu'">
<span :style="`color:${row[column.field].length > 5 ? 'red' : 'black'}`"> {{ row[column.field]
}}</span>
</span>
<span v-else>{{ row[column.field] }}</span>
</template>
</vxe-column>
</vxe-table>
</template>
</el-auto-resizer>
</div>
</div>
<div v-else class="noVip">
<h3>
<el-tag type="danger" class="noVipTag">体验已过期请联系管理员</el-tag>
</h3>
</div>
</template>
<script>
import _ from 'lodash'
import allTableData from "./data.json"
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
export default {
name: 'App',
data() {
return {
_: _,
dayjs: dayjs,
isVip: false,
tableHeader: [
{
key: 'weihao',
title: '手机尾号',
width: "25%",
},
{
key: 'pinggu',
title: '评估',
width: "25%",
},
{
key: 'pingji',
title: '评级',
width: "25%",
},
{
key: 'jianyi',
title: '建议',
width: "25%",
},
],
tableData: [],
form: {
shoujiweihao: ""
},
currentItem: {},
pingjicolor: {
"偏低": "#ecf5ff_#000000",
"一般": "#d9ecff_#000000",
"中等": "#c6e2ff_#000000",
"中上": "#a0cfff_#ffffff",
"很棒": "#79bbff_#ffffff",
"极品": "#337ecc_#ffffff"
}
}
},
async mounted() {
let fiveDay = dayjs('2024-07-01T00:00:00').valueOf()
if (!this.isVip) {
if (dayjs().valueOf() > fiveDay) {
this.isVip = false
return
} else {
this.isVip = true
}
}
this.initializeData()
},
methods: {
onSubmit() {
this.currentItem = {}
if (/^\d{4}$/.test(this.form.shoujiweihao)) {
this.currentItem = _.find(allTableData, (item) => { return item.weihao === this.form.shoujiweihao })
// ElMessage({
// message: `(${this.form.shoujiweihao})${this.currentItem.pingji}${this.currentItem.jianyi}${this.currentItem.pinggu}`,
// type: 'success',
// duration: 5000,
// })
this.tableData.unshift({ ...this.currentItem, id: `${this.currentItem.weihao}`, pinggu: `${this.currentItem.pinggu}` })
this.tableData = _.uniqBy(this.tableData, 'id')
} else {
// ElMessage({
// message: '',
// type: 'error'
// })
}
},
initializeData() {
let array = [];
let height = window.innerHeight
let size = Math.floor((height - 133) / 48) - 1
console.log(window.innerHeight)
let max = 10000
while (array.length < size) {
let num = Math.floor(Math.random() * max)
if (array.indexOf(num) === -1) {
array.push(num);
}
}
for (let i = 0; i < array.length; i++) {
let element = array[i];
let item = allTableData[element]
this.tableData.unshift({ ...item, id: item.weihao, pinggu: `${item.pinggu}` })
}
}
},
watch: {},
computed: {}
}
</script>
<style scoped>
.appClass {
width: 100vw;
height: calc(100vh - 8px);
}
.title {
font-size: 3em;
text-align: center;
margin: 8px 0
}
.top {
margin: 8px;
}
.bottomTable {
margin: 8px;
height: calc(100vh - 130px);
}
.noVip {
text-align: center;
margin-top: 40vh;
}
.noVipTag {
font-size: 3em;
line-height: 1.6em;
height: 1.6em;
}
</style>

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

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

60002
src/renderer/src/data.json

File diff suppressed because it is too large

28
src/renderer/src/main.js

@ -0,0 +1,28 @@
// main.ts
import { createApp } from 'vue'
//全局使用element-plus中文版、element-plus图标
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import './assets/css/base.css'
//导入虚拟化表格
import VxeTable from 'vxe-table'
import 'vxe-table/lib/style.css'
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus, {
locale: zhCn,
}).use(VxeTable)
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