commit
b73725b658
25 changed files with 66676 additions and 0 deletions
@ -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 |
@ -0,0 +1,4 @@ |
|||
node_modules |
|||
dist |
|||
out |
|||
*.log* |
@ -0,0 +1 @@ |
|||
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ |
@ -0,0 +1,6 @@ |
|||
out |
|||
dist |
|||
pnpm-lock.yaml |
|||
LICENSE.md |
|||
tsconfig.json |
|||
tsconfig.*.json |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"singleQuote": true, |
|||
"semi": false, |
|||
"printWidth": 100, |
|||
"trailingComma": "none" |
|||
} |
@ -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 |
|||
``` |
@ -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> |
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -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}.`) |
|||
} |
@ -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 |
@ -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()] |
|||
} |
|||
}) |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"exclude": [ |
|||
"node_modules", |
|||
"public" |
|||
] |
|||
} |
File diff suppressed because it is too large
@ -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" |
|||
} |
|||
} |
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
@ -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.
|
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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> |
@ -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> |
@ -0,0 +1,5 @@ |
|||
html, |
|||
body { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
File diff suppressed because it is too large
@ -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…
Reference in new issue