lichong
4 weeks ago
commit
bf671dcf59
28 changed files with 8311 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,5 @@ |
|||
node_modules |
|||
dist |
|||
out |
|||
*.log* |
|||
package-lock.json |
@ -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: 档案管理-${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" |
|||
] |
|||
} |
@ -0,0 +1,42 @@ |
|||
{ |
|||
"name": "electron", |
|||
"version": "1.0.5", |
|||
"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", |
|||
"vxe-table": "^4.7.87", |
|||
"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" |
|||
} |
|||
} |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,73 @@ |
|||
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: 900, |
|||
height: 1260, |
|||
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.
|
@ -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';img-src 'self' data:; style-src 'self' 'unsafe-inline';font-src 'self' data:;" /> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="app"></div> |
|||
<script type="module" src="/src/main.js"></script> |
|||
</body> |
|||
|
|||
</html> |
@ -0,0 +1,137 @@ |
|||
<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: 3000 |
|||
}) |
|||
} |
|||
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)) { |
|||
this.isVip = isVipObj.isVip |
|||
} |
|||
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> |
@ -0,0 +1,13 @@ |
|||
html, |
|||
body { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
.red { |
|||
color: #f56c6c; |
|||
} |
|||
|
|||
.blue { |
|||
color: #409eff; |
|||
} |
@ -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, lungsscore, 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' |
|||
}); |
File diff suppressed because it is too large
@ -0,0 +1,545 @@ |
|||
[ |
|||
{ |
|||
"label": "年级编号", |
|||
"prop": "nianjicode", |
|||
"type": "text", |
|||
"index": "0", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"searchType": "select", |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "班级编号", |
|||
"prop": "banjicode", |
|||
"type": "text", |
|||
"index": "1", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"searchType": "select", |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "年级", |
|||
"prop": "nianji", |
|||
"type": "text", |
|||
"index": "-1", |
|||
"fixed": false, |
|||
"tableShow": false, |
|||
"setTableShow": false, |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "班级名称", |
|||
"prop": "banji", |
|||
"type": "text", |
|||
"index": "2", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "学籍号", |
|||
"prop": "xuejihao", |
|||
"type": "text", |
|||
"index": "3", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "民族代码", |
|||
"prop": "minzudaima", |
|||
"type": "text", |
|||
"index": "4", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "民族", |
|||
"prop": "minzu", |
|||
"type": "text", |
|||
"index": "-1", |
|||
"fixed": false, |
|||
"tableShow": false, |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "姓名", |
|||
"prop": "name", |
|||
"type": "text", |
|||
"index": "5", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "性别", |
|||
"prop": "gender", |
|||
"tableDisplay": [ |
|||
{ |
|||
"label": "男", |
|||
"value": "1" |
|||
}, |
|||
{ |
|||
"label": "女", |
|||
"value": "2" |
|||
} |
|||
], |
|||
"type": "select", |
|||
"index": "6", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "出生日期", |
|||
"prop": "birth", |
|||
"type": "date", |
|||
"default": 10, |
|||
"index": "7", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "家庭住址", |
|||
"prop": "address", |
|||
"type": "text", |
|||
"index": "8", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"isSort": true, |
|||
"isSearch": true, |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "身高", |
|||
"prop": "height", |
|||
"type": "number", |
|||
"index": "9", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "体重", |
|||
"prop": "weight", |
|||
"type": "number", |
|||
"index": "10", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "BMI", |
|||
"prop": "BMI", |
|||
"type": "number", |
|||
"index": "11", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "BMI得分", |
|||
"prop": "BMIscore", |
|||
"type": "number", |
|||
"index": "12", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "肺活量", |
|||
"prop": "lungs", |
|||
"type": "number", |
|||
"index": "13", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "肺活量得分", |
|||
"prop": "lungsscore", |
|||
"type": "number", |
|||
"index": "14", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "50米跑", |
|||
"prop": "fifty", |
|||
"type": "text", |
|||
"index": "15", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "50米跑得分", |
|||
"prop": "fiftyscore", |
|||
"type": "number", |
|||
"index": "16", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "坐位体前屈", |
|||
"prop": "sitforward", |
|||
"type": "number", |
|||
"index": "17", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "坐位体前屈得分", |
|||
"prop": "sitforwardscore", |
|||
"type": "number", |
|||
"index": "18", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "一分钟跳绳", |
|||
"prop": "jump", |
|||
"type": "number", |
|||
"index": "19", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "一分钟跳绳得分", |
|||
"prop": "jumpscore", |
|||
"type": "number", |
|||
"index": "20", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "一分钟仰卧起坐", |
|||
"prop": "situp", |
|||
"type": "number", |
|||
"index": "21", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "一分钟仰卧起坐得分", |
|||
"prop": "situpscore", |
|||
"type": "number", |
|||
"index": "22", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "50米×8往返跑", |
|||
"prop": "fiftyeight", |
|||
"type": "text", |
|||
"index": "23", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "50米×8往返跑得分", |
|||
"prop": "fiftyeightscore", |
|||
"type": "number", |
|||
"index": "24", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "立定跳远", |
|||
"prop": "stand", |
|||
"type": "number", |
|||
"index": "25", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "立定跳远得分", |
|||
"prop": "standscore", |
|||
"type": "number", |
|||
"index": "26", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "800米跑", |
|||
"prop": "eighty", |
|||
"type": "text", |
|||
"index": "27", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "800米跑得分", |
|||
"prop": "eightyscore", |
|||
"type": "number", |
|||
"index": "28", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "1000米跑", |
|||
"prop": "thousand", |
|||
"type": "text", |
|||
"index": "29", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "1000米跑得分", |
|||
"prop": "thousandscore", |
|||
"type": "number", |
|||
"index": "30", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "引体向上", |
|||
"prop": "pullup", |
|||
"type": "number", |
|||
"index": "31", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": true, |
|||
"export": true |
|||
}, |
|||
{ |
|||
"label": "得分", |
|||
"label1": "引体向上得分", |
|||
"prop": "pullupscore", |
|||
"type": "number", |
|||
"index": "32", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "加分项", |
|||
"prop": "addScore", |
|||
"type": "text", |
|||
"index": "-1", |
|||
"fixed": false, |
|||
"tableShow": true, |
|||
"setTableShow": true, |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "总分", |
|||
"prop": "totalScore", |
|||
"type": "number", |
|||
"index": "33", |
|||
"tableShow": true, |
|||
"fixed": "right", |
|||
"setTableShow": false, |
|||
"sort": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "等级", |
|||
"prop": "level", |
|||
"type": "text", |
|||
"index": "34", |
|||
"tableShow": true, |
|||
"fixed": "right", |
|||
"setTableShow": false, |
|||
"sort": "", |
|||
"isSearch": true, |
|||
"searchType": "select", |
|||
"openPopoverVisible": false, |
|||
"searchValue": "", |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "备注2", |
|||
"prop": "nodt2", |
|||
"type": "text", |
|||
"index": "-1", |
|||
"fixed": false, |
|||
"tableShow": false, |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "创建时间", |
|||
"prop": "create_at", |
|||
"type": "date", |
|||
"default": 0, |
|||
"tableShow": false, |
|||
"formShow": false, |
|||
"export": false |
|||
}, |
|||
{ |
|||
"label": "更新时间", |
|||
"prop": "update_at", |
|||
"type": "date", |
|||
"default": 0, |
|||
"tableShow": false, |
|||
"formShow": false, |
|||
"export": false |
|||
} |
|||
] |
@ -0,0 +1,84 @@ |
|||
<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-if="formItem.type === 'select'"> |
|||
<el-select v-model="formData[formItem.prop]" size="large" style="width: 240px"> |
|||
<el-option v-for="item in formItem.tableDisplay" :key="item.value" :label="item.label" |
|||
:value="item.value" /> |
|||
</el-select> |
|||
</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> |
@ -0,0 +1,210 @@ |
|||
<template> |
|||
<div class="tableClass"> |
|||
<vxe-table show-overflow :data="tableData" round :height="690" :scroll-y="{ enabled: true, gt: 20 }" |
|||
:checkbox-config="{ labelField: 'seq', highlight: true }" @checkbox-all="selectAllChangeEvent" |
|||
@checkbox-change="selectChange" ref="tableRef" border :column-config="{ resizable: true }"> |
|||
<vxe-column type="seq" width="70" v-if="hiddenXuhao"></vxe-column> |
|||
<vxe-column type="checkbox" width="70" fixed="left"></vxe-column> |
|||
<vxe-column field="totalScore" title="总分" width="70" fixed="left" class-name="red" |
|||
header-class-name="red"></vxe-column> |
|||
<vxe-column field="level" title="等级" width="70" fixed="left" class-name="blue" |
|||
header-class-name="blue"></vxe-column> |
|||
<template v-for="(headerItem, headerIndex) in tableHeader" :key="headerIndex"> |
|||
<vxe-column :field="headerItem.prop" :title="headerItem.label" :width="`${headerItem.label.length * 23 + 24}`" |
|||
:class-name="colorList[headerItem.label] || ''" :header-class-name="colorList[headerItem.label] || ''" |
|||
:fixed="!!headerItem.fixed ? 'left' : ''" :sortable="headerItem.isSort" |
|||
v-if="['level', 'totalScore'].indexOf(headerItem.prop) === -1"> |
|||
<template #default="{ row }"> |
|||
<template v-if="headerItem.type === 'text'"> |
|||
<span>{{ row[headerItem.prop] }}</span> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'select'"> |
|||
<span>{{ (_.find(headerItem.tableDisplay, { value: `${row[headerItem.prop]}` }) || {}).label }}</span> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'date'"> |
|||
<span>{{ row[headerItem.prop] }}</span> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'number'"> |
|||
<span>{{ row[headerItem.prop] }}</span> |
|||
</template> |
|||
<template v-else>{{ row[headerItem.prop] }}</template> |
|||
</template> |
|||
</vxe-column> |
|||
</template> |
|||
<vxe-column fixed="right" title="操作" width="145"> |
|||
<template #default="{ row }"> |
|||
<el-button type="primary" circle @click="optClick(row, 'edit')"> |
|||
<el-icon> |
|||
<Edit /> |
|||
</el-icon> |
|||
</el-button> |
|||
<!-- <el-button type="info" circle @click="optClick(row, 'info')"> |
|||
<el-icon> |
|||
<InfoFilled /> |
|||
</el-icon> |
|||
</el-button> --> |
|||
<el-button type="danger" circle @click="optClick(row, 'del')"> |
|||
<el-icon> |
|||
<Delete /> |
|||
</el-icon> |
|||
</el-button> |
|||
</template> |
|||
</vxe-column> |
|||
</vxe-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", "searchValue", "headerSort"], |
|||
props: { |
|||
tableHeader: { |
|||
type: Array, |
|||
default: () => { |
|||
return [] |
|||
} |
|||
}, |
|||
tableData: { |
|||
type: Array, |
|||
default: () => { |
|||
return [] |
|||
} |
|||
}, |
|||
pageSizes: { |
|||
type: Array, |
|||
default: () => { |
|||
return [15, 50, 75, 100] |
|||
} |
|||
}, |
|||
total: { |
|||
type: Number, |
|||
default: () => { |
|||
return 0 |
|||
} |
|||
}, |
|||
searchParams: { |
|||
type: Object, |
|||
default: () => { |
|||
return {} |
|||
} |
|||
}, |
|||
hiddenXuhao: { |
|||
type: Boolean, |
|||
default: () => { |
|||
return false |
|||
} |
|||
}, |
|||
|
|||
}, |
|||
data() { |
|||
return { |
|||
_: _, |
|||
dayjs: dayjs, |
|||
currentPage: 1, |
|||
pageSize: 15, |
|||
colorList: { |
|||
得分: "red", |
|||
BMI得分: "red", |
|||
总分: "red", |
|||
等级: "blue", |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
selectChange({ checked }) { |
|||
let $table = this.$refs.tableRef |
|||
if ($table) { |
|||
let selection = $table.getCheckboxRecords() |
|||
this.$emit("selectChange", selection) |
|||
} |
|||
}, |
|||
selectAllChangeEvent({ checked }) { |
|||
let $table = this.$refs.tableRef |
|||
if ($table) { |
|||
let selection = $table.getCheckboxRecords() |
|||
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() { |
|||
const $table = this.$refs.tableRef |
|||
if ($table) { |
|||
$table.clearCheckboxRow() |
|||
} |
|||
}, |
|||
searchValue(item) { |
|||
item.popoverVisible = false |
|||
this.$emit("searchValue") |
|||
}, |
|||
// 打开popoverp |
|||
openPopoverVisible(item) { |
|||
for (let i = 0; i < this.tableHeader.length; i++) { |
|||
let headerItem = this.tableHeader[i]; |
|||
headerItem.popoverVisible = false |
|||
} |
|||
item.popoverVisible = true |
|||
}, |
|||
headerSort(headerItem) { |
|||
for (let i = 0; i < this.tableHeader.length; i++) { |
|||
let item = this.tableHeader[i]; |
|||
if (headerItem.prop !== item.prop) { |
|||
item.sort = "" |
|||
} |
|||
} |
|||
let sortOld = headerItem.sort |
|||
if (sortOld === "") { |
|||
headerItem.sort = 1 |
|||
} else if (sortOld === 1) { |
|||
headerItem.sort = -1 |
|||
} else if (sortOld === -1) { |
|||
headerItem.sort = "" |
|||
} |
|||
|
|||
this.$emit("headerSort", headerItem) |
|||
}, |
|||
}, |
|||
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 { |
|||
height: calc(100vh - 325px); |
|||
text-align: center; |
|||
text-align-last: center; |
|||
} |
|||
</style> |
@ -0,0 +1,34 @@ |
|||
// 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 VxeUI from 'vxe-pc-ui' |
|||
import 'vxe-pc-ui/lib/style.css' |
|||
// ...
|
|||
|
|||
// 完整导入 表格库
|
|||
import VxeUITable from 'vxe-table' |
|||
import 'vxe-table/lib/style.css' |
|||
|
|||
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 }).use(VxeUI).use(VxeUITable) |
|||
app.mount('#app') |
|||
|
|||
document.addEventListener('keydown', (event) => { |
|||
const { ctrlKey, shiftKey, key } = event |
|||
if ((ctrlKey && shiftKey && key === 'I') || key === 'F12') { |
|||
return event.preventDefault() |
|||
} |
|||
}) |
@ -0,0 +1,774 @@ |
|||
<template> |
|||
<div class="rightClass"> |
|||
<div> |
|||
<el-row> |
|||
<el-col v-for="(headerItem, headerIndex) in _.filter(tableHeader, item => item.isSearch)" :key="headerIndex" |
|||
:span="headerItem.searchType === 'select' ? 5 : 4"> |
|||
<div> |
|||
<span style="margin-right: 4px;">{{ headerItem.label }}:</span> |
|||
<template v-if="headerItem.searchType === 'select'"> |
|||
<el-select v-model="searchParams[headerItem.prop]" size="large" multiple |
|||
style="width: 150px;padding-right: 8px;margin:8px 0" clearable collapse-tags collapse-tags-tooltip |
|||
:max-collapse-tags="1" @change="searchData()"> |
|||
<el-option v-for="item in headerItem.options" :key="item.value" :label="item.label" |
|||
:value="item.value" /> |
|||
</el-select> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'text'"> |
|||
<el-input v-model="searchParams[headerItem.prop]" style="width: 150px;padding-right: 8px;margin:8px 0" |
|||
clearable @keyup.enter="searchData()" size="large" /> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'select'"> |
|||
<el-select v-model="searchParams[headerItem.prop]" size="large" |
|||
style="width: 150px;padding-right: 8px;margin:8px 0" clearable @change="searchData()"> |
|||
<el-option v-for="item in headerItem.tableDisplay" :key="item.value" :label="item.label" |
|||
:value="item.value" /> |
|||
</el-select> |
|||
</template> |
|||
<template v-else-if="headerItem.type === 'date'"> |
|||
<el-date-picker v-model="searchParams[headerItem.prop]" type="date" |
|||
:disabled="['create_at', 'update_at'].indexOf(headerItem.prop) !== -1" |
|||
:placeholder="`请选择${headerItem.label}`" format="YYYY-MM-DD" value-format="YYYY-MM-DD" |
|||
style="width: 150px;padding-right: 8px;margin:8px 0" size="large" clearable @change="searchData()" /> |
|||
</template> |
|||
</div> |
|||
</el-col> |
|||
<el-col :span="3"> |
|||
<el-button type="primary" @click="searchData()" size="large" style="margin-top: 8px;"> |
|||
<el-icon> |
|||
<Search /> |
|||
</el-icon> |
|||
<span>搜索</span> |
|||
</el-button> |
|||
</el-col> |
|||
</el-row> |
|||
<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> |
|||
<el-button type="primary" @click="clearSelection"> |
|||
<el-icon> |
|||
<Refresh /> |
|||
</el-icon> |
|||
<span>清空搜索</span> |
|||
</el-button> |
|||
<el-button type="primary" @click="clearLocalstory"> |
|||
<el-icon> |
|||
<Refresh /> |
|||
</el-icon> |
|||
<span>清空表头缓存</span> |
|||
</el-button> |
|||
<el-tooltip effect="dark" content="设置表头" placement="top"> |
|||
<el-button type="primary" @click="setHeader"> |
|||
<el-icon> |
|||
<Setting /> |
|||
</el-icon> |
|||
<span>设置表头</span> |
|||
</el-button> |
|||
</el-tooltip> |
|||
|
|||
<!-- <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> |
|||
<el-descriptions direction="vertical" :column="5" border style="margin: 8px 0;"> |
|||
<el-descriptions-item label="总人数">{{ allData.length }}</el-descriptions-item> |
|||
<el-descriptions-item label="优秀人数(优秀率)"> |
|||
<span style="margin: 0 8px 0 0;"> |
|||
{{ _.filter(allData, { level: "优秀" }).length }} |
|||
</span> |
|||
<span> |
|||
<el-tag size="small" style="position: relative;top: -1px;"> |
|||
{{ |
|||
(Number(((_.filter(allData, { level: "优秀" }).length) / (allData.length || 1)).toFixed(2)) * |
|||
100).toFixed(0) |
|||
}}% |
|||
</el-tag> |
|||
</span> |
|||
</el-descriptions-item> |
|||
<el-descriptions-item label="良好人数(良好率)"> |
|||
<span style="margin: 0 8px 0 0;"> |
|||
{{ _.filter(allData, { level: "良好" }).length }} |
|||
</span> |
|||
<span> |
|||
<el-tag size="small" style="position: relative;top: -1px;"> |
|||
{{ |
|||
(Number(((_.filter(allData, { level: "良好" })).length / (allData.length || 1)).toFixed(2)) * |
|||
100).toFixed(0) |
|||
}}% |
|||
</el-tag> |
|||
</span> |
|||
</el-descriptions-item> |
|||
<el-descriptions-item label="及格人数(及格率)"> |
|||
<span style="margin: 0 8px 0 0;"> |
|||
{{ _.filter(allData, { level: "及格" }).length }} |
|||
</span> |
|||
<span> |
|||
<el-tag size="small" style="position: relative;top: -1px;"> |
|||
{{ |
|||
(Number(((_.filter(allData, { level: "及格" })).length / (allData.length || 1)).toFixed(2)) * |
|||
100).toFixed(0) |
|||
}}% |
|||
</el-tag> |
|||
</span> |
|||
</el-descriptions-item> |
|||
<el-descriptions-item label="不及格人数(不及格率)"> |
|||
<span style="margin: 0 8px 0 0;"> |
|||
{{ _.filter(allData, { level: "不及格" }).length }} |
|||
</span> |
|||
<span> |
|||
<el-tag size="small" style="position: relative;top: -1px;"> |
|||
{{ |
|||
(Number(((_.filter(allData, { level: "不及格" })).length / (allData.length || 1)).toFixed(2)) * |
|||
100).toFixed(0) |
|||
}}% |
|||
</el-tag> |
|||
</span> |
|||
</el-descriptions-item> |
|||
</el-descriptions> |
|||
</div> |
|||
<div v-loading="loading"> |
|||
<tablecomponent :tableHeader="tableHeader" :tableData="tableData" :searchParams="searchParams" |
|||
:pageSizes="pageSizes" :total="total" @selectChange="selectChange" @handleCurrentChange="handleCurrentChange" |
|||
@handleSizeChange="handleSizeChange" @edit="edit" @info="info" @del="del" @searchValue="searchData" |
|||
ref="tableComponentRef" @headerSort="headerSort"> |
|||
</tablecomponent> |
|||
</div> |
|||
<div style="text-align: center;line-height: 60px;font-size: 1.2em"> |
|||
<span>守望者网络科技有限公司 Copyright@{{ dayjs().format("YYYY") }} 版权所有:北京市房山区 资深教育工作者 李竞飞</span> |
|||
</div> |
|||
<el-dialog v-model="dialogFrom.visible" :title="dialogFrom.title" width="80%" :close-on-click-modal="false"> |
|||
<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> |
|||
<el-dialog v-model="setHeaderVisible" title="设置表头" width="80%"> |
|||
<el-row> |
|||
<el-col v-for="(item, index) in setTableHeader" :key="index" :span="8"> |
|||
<el-checkbox v-model="item.setTableShow" :label="item.label1 || item.label" size="large" /> |
|||
<el-checkbox v-model="item.fixed" label="固定在左侧" size="large" /> |
|||
</el-col> |
|||
</el-row> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import _ from 'lodash' |
|||
import * as XLSX from 'xlsx'; |
|||
import tableHeaderLocal from '../assets/json/student.json' |
|||
import jifen from '../assets/json/jifen.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 }, |
|||
watch: {}, |
|||
computed: {}, |
|||
data() { |
|||
return { |
|||
_: _, |
|||
dayjs: dayjs, |
|||
tableHeader: [], |
|||
setTableHeader: [], |
|||
formHeader: [], |
|||
exportHeader: [], |
|||
tableData: [], |
|||
allData: [], |
|||
currentPage: 1, |
|||
pageSize: 15, |
|||
pageSizes: [15, 50, 75, 100], |
|||
total: 0, |
|||
fileOriData: null, |
|||
searchParams: {}, |
|||
selectionData: [], |
|||
dialogFrom: { |
|||
visible: false, |
|||
title: "新增学生", |
|||
type: "add", |
|||
formHeader: [], |
|||
formData: {} |
|||
}, |
|||
setHeaderVisible: false, |
|||
sortObj: {}, |
|||
loading: false, |
|||
} |
|||
}, |
|||
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 = XLSX.read(data, { type: 'binary' }) |
|||
let sheetName = workbook.SheetNames[0] |
|||
let sheetData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]) |
|||
for (let i = 0; i < sheetData.length; i++) { |
|||
let element = sheetData[i]; |
|||
let dealItem = {} |
|||
for (let key in element) { |
|||
let headerItem = _.find(tableHeaderLocal, o => o.label === key) |
|||
if (headerItem) { |
|||
dealItem[headerItem.prop] = element[key] |
|||
} |
|||
} |
|||
try { |
|||
await myDatabase.student.add({ |
|||
...dealItem, |
|||
...that.dealRow(dealItem), |
|||
create_at: dayjs().format('YYYY-MM-DD HH:mm:ss'), |
|||
update_at: dayjs().format('YYYY-MM-DD HH:mm:ss') |
|||
}) |
|||
} catch (error) { |
|||
ElMessage({ |
|||
message: `${dealItem.name}(${dealItem.xuejihao})学籍号重复,不可再次新增该学生信息`, |
|||
type: 'error', |
|||
}) |
|||
} |
|||
} |
|||
await that.searchData(true) |
|||
} |
|||
fileReader.onerror = function (error) { |
|||
ElMessage({ |
|||
message: `Error reading file:, ${error}`, |
|||
type: 'error', |
|||
}) |
|||
} |
|||
fileReader.readAsArrayBuffer(file) |
|||
}, |
|||
// 处理每行的数据 |
|||
dealRow(row) { |
|||
row["addScore"] = 0 |
|||
row["BMI"] = Number(Number(row.weight / (row.height * row.height / 10000)).toFixed(2)) || 0 |
|||
// let nianjicodeList = ["11", "12","13","14", "15","16","21", "22","23","31", "32","33","41","42"] |
|||
let nianjicodeList = ["11", "12", "13", "14", "15", "16", "21", "22", "23"] |
|||
if (nianjicodeList.includes(`${row.nianjicode}`)) { |
|||
for (let key in jifen) { |
|||
let value = jifen[key] |
|||
let scoreItem = {} |
|||
for (let i = 0; i < value.list.length; i++) { |
|||
let item = value.list[i]; |
|||
let rowValue = this.transTime(value, row[value.prop], true) |
|||
let itemValue = this.transTime(value, item[`${row.nianjicode}`], false) |
|||
if (`${item.gender}` === `${row.gender}` && `${item.gender}` === `${row.gender}` && this.createComparator(value.fuhao)(rowValue, itemValue)) { |
|||
scoreItem = { ...item, [`${key.slice(0, key.length - 5)}_org`]: row[value.prop] } |
|||
break |
|||
} |
|||
} |
|||
row[key] = scoreItem.score || "" |
|||
row["addScore"] = (Number(row["addScore"] || 0) || 0) + (Number(scoreItem.add_score || 0) || 0) |
|||
} |
|||
} |
|||
// todo处理总分 |
|||
// G2_性别、M2_BMI得分、o2_肺活量、q2_50米、s2_坐位体前屈、u2_一分钟跳绳、w2_一分钟仰卧起坐、y2_50米X8、AA2_立定跳远、Ac2_800米跑、AE2_1000米跑、Ag2_引体向上 |
|||
// 小学一、二年级总分: |
|||
// =M2 * 0.15 + O2 * 0.15 + Q2 * 0.2 + S2 * 0.3 + U2 * 0.2 |
|||
// 小学三、四年级总分: |
|||
// =M2 * 0.15 + O2 * 0.15 + Q2 * 0.2 + S2 * 0.2 + U2 * 0.2 + W2 * 0.1 |
|||
// 小学五、六年级总分: |
|||
// =M2 * 0.15 + O2 * 0.15 + Q2 * 0.2 + S2 * 0.1 + U2 * 0.1 + W2 * 0.2 + Y2 * 0.1 |
|||
// 初中总分: |
|||
// =IF(G2 = 1, M2 * 0.15 + O2 * 0.15 + Q2 * 0.2 + S2 * 0.1 + AA2 * 0.1 + AG2 * 0.1 + AE2 * 0.2, IF(G2 = 2, M2 * 0.15 + O2 * 0.15 + Q2 * 0.2 + S2 * 0.1 + AA2 * 0.1 + W2 * 0.1 + AC2 * 0.2)) |
|||
if (["11", "12"].indexOf(`${row.nianjicode}`) !== -1) { |
|||
row["totalScore"] = Number(Number(row.BMIscore * 0.15 + row.lungsscore * 0.15 + row.fiftyscore * 0.2 + row.sitforwardscore * 0.3 + row.jumpscore * 0.2).toFixed(2)) |
|||
} else if (["13", "14"].indexOf(`${row.nianjicode}`) !== -1) { |
|||
row["totalScore"] = Number(Number(row.BMIscore * 0.15 + row.lungsscore * 0.15 + row.fiftyscore * 0.2 + row.sitforwardscore * 0.2 + row.jumpscore * 0.2 + row.situpscore * 0.1).toFixed(2)) |
|||
} else if (["15", "16"].indexOf(`${row.nianjicode}`) !== -1) { |
|||
row["totalScore"] = Number(Number(row.BMIscore * 0.15 + row.lungsscore * 0.15 + row.fiftyscore * 0.2 + row.sitforwardscore * 0.1 + row.jumpscore * 0.1 + row.situpscore * 0.2 + row.fiftyeightscore * 0.1).toFixed(2)) |
|||
} else if (["21", "22", "23"].indexOf(`${row.nianjicode}`) !== -1) { |
|||
if (`${row.gender}` === "1") { |
|||
row["totalScore"] = Number(Number(row.BMIscore * 0.15 + row.lungsscore * 0.15 + row.fiftyscore * 0.2 + row.sitforwardscore * 0.1 + row.standscore * 0.1 + row.pullupscore * 0.1 + row.thousandscore * 0.2).toFixed(2)) |
|||
} else if (`${row.gender}` === "2") { |
|||
row["totalScore"] = Number(Number(row.BMIscore * 0.15 + row.lungsscore * 0.15 + row.fiftyscore * 0.2 + row.sitforwardscore * 0.1 + row.standscore * 0.1 + row.situpscore * 0.1 + row.eightyscore * 0.2).toFixed(2)) |
|||
} |
|||
} |
|||
row["totalScore"] = Number(Number(row["totalScore"] + row["addScore"]).toFixed(2)) |
|||
// 等级: |
|||
// = IF(AH2 >= 90, "优秀", IF(AH2 >= 80, "良好", IF(AH2 >= 60, "及格", "不及格"))) |
|||
let totalScoreNumber = Number(row.totalScore) |
|||
if (totalScoreNumber >= 90) { |
|||
row["level"] = "优秀" |
|||
} else if (totalScoreNumber >= 80) { |
|||
row["level"] = "良好" |
|||
} else if (totalScoreNumber >= 60) { |
|||
row["level"] = "及格" |
|||
} else if (totalScoreNumber >= 0) { |
|||
row["level"] = "不及格" |
|||
} else { |
|||
row["level"] = "" |
|||
} |
|||
return row |
|||
}, |
|||
transTime(item, value, isReturn) { |
|||
if (item.prop === "fifty" && isReturn) { |
|||
return Number(value) |
|||
} |
|||
if (typeof (value) === "number") { |
|||
return value |
|||
} else if (typeof (value) === "string") { |
|||
let time = value.trim(); |
|||
let seconds = 0; |
|||
if (time.includes("'") && time.includes('"')) { |
|||
// 处理 2'20" 格式 |
|||
let parts = time.split(/['"]/).filter(Boolean); |
|||
let minutes = parseInt(parts[0], 10); |
|||
let secondsPart = parseInt(parts[1], 10); |
|||
seconds = minutes * 60 + secondsPart; |
|||
} else if (time.includes('.')) { |
|||
// 处理 2.20 格式 |
|||
let parts = time.split('.'); |
|||
let minutes = parseInt(parts[0], 10); |
|||
let secondsPart = parseFloat('.' + parts[1]) * 60; |
|||
seconds = minutes * 60 + secondsPart; |
|||
} else { |
|||
seconds = Number(time) |
|||
} |
|||
return seconds |
|||
} |
|||
}, |
|||
createComparator(operator) { |
|||
switch (operator) { |
|||
case '<=': |
|||
return (a, b) => a <= b; |
|||
case '>=': |
|||
return (a, b) => a >= b; |
|||
case '==': |
|||
return (a, b) => a == b; |
|||
case '!=': |
|||
return (a, b) => a != b; |
|||
case '<': |
|||
return (a, b) => a < b; |
|||
case '>': |
|||
return (a, b) => a > b; |
|||
default: |
|||
throw new Error('Invalid operator'); |
|||
} |
|||
}, |
|||
|
|||
//更新数据 |
|||
async updateSeach(params = {}, isAll = false) { |
|||
this.loading = true |
|||
let allStudentList = _.cloneDeep(await myDatabase.student.toArray()) |
|||
if (!_.isEmpty(params)) { |
|||
let paramsTemp = {} |
|||
for (let key in params) { |
|||
let value = params[key] |
|||
if (["undefined", "", "null"].indexOf(`${value}`) === -1) { |
|||
if (value.indexOf(",")) { |
|||
paramsTemp[key] = [..._.split(value, ","), ..._.split(value, ",").map(item => Number(item))] |
|||
} else { |
|||
paramsTemp[key] = [`${value}`, Number(value)] |
|||
|
|||
} |
|||
} |
|||
} |
|||
let dataTemp = [] |
|||
if (!_.isEmpty(paramsTemp)) { |
|||
for (let i = 0; i < allStudentList.length; i++) { |
|||
let element = allStudentList[i]; |
|||
let istrue = true |
|||
for (let key in paramsTemp) { |
|||
if (paramsTemp[key].includes(element[key])) { |
|||
} else { |
|||
istrue = false |
|||
} |
|||
} |
|||
if (istrue) { |
|||
dataTemp.push({ ...element }) |
|||
} |
|||
} |
|||
allStudentList = _.cloneDeep(dataTemp) |
|||
} |
|||
} |
|||
if (_.get(this.sortObj, ["sort"], "")) { |
|||
// 带条件排序 |
|||
allStudentList = _.cloneDeep(_.orderBy(allStudentList, [this.sortObj.prop], [_.get(this.sortObj, ["sort"], "") === 1 ? 'desc' : 'asc'])) |
|||
} |
|||
let allData = _.uniqBy(allStudentList, "id") |
|||
this.allData = _.cloneDeep(allData) |
|||
this.total = allData.length |
|||
this.tableData = allData |
|||
if (isAll) { |
|||
let groupObj = { |
|||
nianjicodeGroupyData: _.groupBy(allStudentList, "nianjicode"), |
|||
banjicodeGroupyData: _.groupBy(allStudentList, "banjicode"), |
|||
levelGroupyData: _.groupBy(allStudentList, "level"), |
|||
} |
|||
for (let key in groupObj) { |
|||
let value = groupObj[key] |
|||
let findIndex = _.findIndex(this.tableHeader, { prop: `${key.slice(0, key.length - 10)}` }) |
|||
let headerItem = this.tableHeader[findIndex] |
|||
let keys = _.keys(value) |
|||
let options = [] |
|||
for (let j = 0; j < keys.length; j++) { |
|||
let element = keys[j]; |
|||
if ((!_.find(options, { label: element })) && element) { |
|||
options.push( |
|||
{ |
|||
label: `${element}`, |
|||
value: `${element}` |
|||
} |
|||
) |
|||
} |
|||
|
|||
} |
|||
headerItem["options"] = options |
|||
} |
|||
} |
|||
this.loading = false |
|||
// this.tableData = allStudentList.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize) |
|||
}, |
|||
//搜索数据 |
|||
async searchData(isAll = false) { |
|||
await this.updateSeach(this.searchParams, isAll) |
|||
}, |
|||
// 排序 |
|||
async headerSort(headerSort) { |
|||
this.sortObj = headerSort |
|||
await this.updateSeach(this.searchParams) |
|||
}, |
|||
// 清空搜索 |
|||
async clearSelection() { |
|||
for (let key in this.searchParams) { |
|||
this.searchParams[key] = "" |
|||
} |
|||
for (let i = 0; i < this.tableHeader.length; i++) { |
|||
let item = this.tableHeader; |
|||
item.sort = "" |
|||
} |
|||
this.sortObj = {} |
|||
await this.searchData() |
|||
}, |
|||
//选中数据 |
|||
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, ...this.dealRow(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]({ ...params, 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 === "select") { |
|||
formData[element.prop] = "" |
|||
} else if (element.type === "date") { |
|||
formData[element.prop] = dayjs().subtract(element.default || 0, 'year').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: '取消删除', |
|||
}) |
|||
}) |
|||
}, |
|||
// 导出数据 |
|||
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 < this.exportHeader.length; j++) { |
|||
let headerItem = this.exportHeader[j]; |
|||
listItem[headerItem.label] = item[`${headerItem.prop}_org`] || 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: '当前无数据,请输入数据后导出', |
|||
}) |
|||
} |
|||
}, |
|||
// 表头设置 |
|||
setHeader() { |
|||
this.setHeaderVisible = true |
|||
}, |
|||
// 清空缓存 |
|||
clearLocalstory() { |
|||
// 清空缓存 |
|||
localStorage.removeItem("setTableHeader") |
|||
this.setHeaderFun() |
|||
ElMessage({ |
|||
type: 'success', |
|||
message: '清除表头缓存成功', |
|||
}) |
|||
}, |
|||
setHeaderFun() { |
|||
this.setTableHeader = [] |
|||
if (!localStorage.getItem('setTableHeader')) { |
|||
for (let i = 0; i < tableHeaderLocal.length; i++) { |
|||
let headerItem = tableHeaderLocal[i]; |
|||
if (headerItem.isSearch) { |
|||
this.searchParams[headerItem.prop] = "" |
|||
} |
|||
if (headerItem.setTableShow) { |
|||
this.setTableHeader.push({ ...headerItem, fixed: false }) |
|||
} |
|||
} |
|||
localStorage.setItem('setTableHeader', JSON.stringify(this.setTableHeader)) |
|||
} else { |
|||
this.setTableHeader = JSON.parse(localStorage.getItem('setTableHeader')) |
|||
} |
|||
} |
|||
}, |
|||
async mounted() { |
|||
this.setHeaderFun() |
|||
this.tableHeader = _.filter(tableHeaderLocal, o => o.tableShow) |
|||
this.formHeader = _.filter(tableHeaderLocal, o => o.formShow) |
|||
this.exportHeader = _.filter(tableHeaderLocal, o => o.export) |
|||
await this.updateSeach({}, true) |
|||
this.$watch("setTableHeader", (val) => { |
|||
// this.tableHeader = _.filter(val, o => o.setTableShow) |
|||
for (let i = 0; i < val.length; i++) { |
|||
let element = val[i]; |
|||
let item = _.find(this.tableHeader, o => o.prop === element.prop) |
|||
item.fixed = element.fixed |
|||
} |
|||
localStorage.setItem('setTableHeader', JSON.stringify(val)) |
|||
}, { deep: true }) |
|||
}, |
|||
} |
|||
</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> |
Loading…
Reference in new issue