@ -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> |
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" |
||||
|
] |
||||
|
} |
@ -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", |
||||
|
"dayjs": "^1.11.11", |
||||
|
"element-plus": "^2.7.1", |
||||
|
"lodash": "^4.17.21", |
||||
|
"pinia": "^2.1.7", |
||||
|
"pinia-plugin-persist": "^1.0.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@rushstack/eslint-patch": "^1.2.0", |
||||
|
"@vitejs/plugin-vue": "^3.1.2", |
||||
|
"@vue/eslint-config-prettier": "^7.0.0", |
||||
|
"electron": "^20.3.2", |
||||
|
"electron-builder": "^23.6.0", |
||||
|
"electron-notarize": "^1.2.1", |
||||
|
"electron-vite": "^1.0.11", |
||||
|
"less": "^4.1.3", |
||||
|
"prettier": "^2.7.1", |
||||
|
"vite": "^3.1.8", |
||||
|
"vue": "^3.2.41" |
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 16 KiB |
@ -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,
|
||||
|
minWidth: 1366, |
||||
|
minHeight: 768, |
||||
|
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,21 @@ |
|||||
|
<!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'" |
||||
|
/> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<audio src="./play.mp3" autoplay id="myAudio1" loop></audio> |
||||
|
<audio src="./right.wav" id="right"></audio> |
||||
|
<audio src="./error.wav" id="error"></audio> |
||||
|
<script type="module" src="/src/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,658 @@ |
|||||
|
<template> |
||||
|
<div class="appClass" v-if="isVip"> |
||||
|
<template v-for="(perItem, perIndex) in personList" :key="perIndex"> |
||||
|
<div :class="`parent${perIndex}`"> |
||||
|
<div :class="`avatar${perIndex}`"> |
||||
|
<el-avatar size="large" shape="square"> |
||||
|
<img :src="perItem.ava" alt="头像" /> |
||||
|
</el-avatar> |
||||
|
</div> |
||||
|
<div :class="`usetime${perIndex}`" style="display: inline-block"> |
||||
|
<el-progress type="circle" striped striped-flow :stroke-width="10" :color="colors" :width="80" |
||||
|
:percentage="timeObj[`time${perIndex}`]"> |
||||
|
<span style="color: #000; font-size: 3em">{{ timeObj[`time${perIndex}`] }}</span> |
||||
|
</el-progress> |
||||
|
</div> |
||||
|
<div :class="`result${perIndex}`" style="display: inline-block"> |
||||
|
<span v-if="perItem.time"> |
||||
|
耗时 {{ perItem.time / 100 }}秒,答对 |
||||
|
<el-text :type="type[_.compact(perItem.sure).length]" style="font-size: 1.2em"> |
||||
|
{{ _.compact(perItem.sure).length }} |
||||
|
</el-text> |
||||
|
道题 |
||||
|
</span> |
||||
|
<span v-else>第{{ perItem.currentTi + 1 }}题</span> |
||||
|
</div> |
||||
|
<div :id="`yanmo${perIndex}`"></div> |
||||
|
<template v-for="(item, index) in perItem.questionList" :key="index"> |
||||
|
<template v-if="perItem.currentTi === index"> |
||||
|
<div :class="`question${perIndex}`"> |
||||
|
<div class="title"> |
||||
|
{{ item.title }} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="options"> |
||||
|
<div v-for="(optionItem, optionIndex) in item.options" :class="`option${perIndex}`" :key="optionIndex"> |
||||
|
<el-tag v-if="optionItem.disabled" :type="optionItem.type" effect="dark"> |
||||
|
{{ optionItem.text }} |
||||
|
</el-tag> |
||||
|
<el-check-tag :checked="optionItem.checked" :type="optionItem.type" v-else |
||||
|
@change="onChange1(optionItem, optionIndex, item, perItem, perIndex)" effect="dark"> |
||||
|
{{ optionItem.text }} |
||||
|
</el-check-tag> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
</template> |
||||
|
<div :class="`next${perIndex}`"> |
||||
|
<div> |
||||
|
<el-button size="large" type="primary" v-if="!perItem.time" @click="nextTi(perItem, perIndex)" :disabled="perItem.questionList[perItem.currentTi].answer === -1 || perItem.clickDisable |
||||
|
"> |
||||
|
<span style="font-size: 2em; margin: 18px 8px"> 确定 </span> |
||||
|
</el-button> |
||||
|
<el-button size="large" type="primary" v-else @click="submit(perItem, perIndex)" :disabled="!!perItem.time"> |
||||
|
<span style="font-size: 2em; padding: 8px"> 已结束 </span> |
||||
|
</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
</div> |
||||
|
<div v-else class="noVip"> |
||||
|
<h3> |
||||
|
<el-tag type="danger" class="noVipTag">体验已过期,请联系管理员。</el-tag> |
||||
|
</h3> |
||||
|
</div> |
||||
|
<el-dialog v-model="dialogInfo.show" :title="dialogInfo.title" width="80%" center :before-close="handleClose"> |
||||
|
<div> |
||||
|
<el-row> |
||||
|
<el-col :span="12" v-for="(perItem, perIndex) in personList" :key="perIndex" style="text-align: center"> |
||||
|
<div class="mt-10"> |
||||
|
<el-avatar size="large" shape="square"> |
||||
|
<img :src="perItem.ava" alt="头像" /> |
||||
|
</el-avatar> |
||||
|
</div> |
||||
|
<div class="mt-20"> |
||||
|
<span style="font-size: 3em"> |
||||
|
您一共耗时 {{ perItem.time / 100 }}秒,答对 |
||||
|
<el-text :type="type[_.compact(perItem.sure).length]"> |
||||
|
<span style="font-size: 3em"> |
||||
|
{{ _.compact(perItem.sure).length }} |
||||
|
</span> |
||||
|
</el-text> |
||||
|
道题 |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="mt-20"> |
||||
|
<el-progress type="circle" v-if="dialogInfo.winIndex === perIndex" :percentage="100" status="success"> |
||||
|
<span style="color: #000;font-size: 3em;">赢</span> |
||||
|
</el-progress> |
||||
|
<el-progress type="circle" v-else :percentage="100" status="exception"> |
||||
|
<span style="color: #000;font-size: 3em;">输</span> |
||||
|
</el-progress> |
||||
|
</div> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
<template #footer> |
||||
|
<div class="dialogFooter"> |
||||
|
<el-button type="primary" @click="continuePk" style="font-size: 2.5em;height: 2em;"> 继续比赛 </el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-dialog> |
||||
|
<div v-if="diolag" class="el-overlay-dialog" @click="handleCloseDialog" |
||||
|
style="text-align: center; align-content: center"> |
||||
|
<div><img src="/src/assets/bgimg/bgimg.gif" alt="pk" style="margin-top: 25vh" /></div> |
||||
|
</div> |
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import config from './config.js' |
||||
|
import _ from 'lodash' |
||||
|
import useConfigStore from './stores/config.js' |
||||
|
import dayjs from 'dayjs' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
export default { |
||||
|
name: 'app', |
||||
|
data() { |
||||
|
return { |
||||
|
_: _, |
||||
|
ava0: '', |
||||
|
ava1: '', |
||||
|
timeObj: { |
||||
|
timer0: null, |
||||
|
timer1: null, |
||||
|
time0: 100, |
||||
|
time1: 100 |
||||
|
}, |
||||
|
personList: [], |
||||
|
colors: [ |
||||
|
{ color: '#ff0000', percentage: 3 }, |
||||
|
{ color: '#c45656', percentage: 10 }, |
||||
|
{ color: '#F56C6C', percentage: 20 }, |
||||
|
{ color: '#f89898', percentage: 30 }, |
||||
|
{ color: '#fab6b6', percentage: 40 }, |
||||
|
{ color: '#fde2e2', percentage: 50 }, |
||||
|
{ color: '#f3d19e', percentage: 60 }, |
||||
|
{ color: '#eebe77', percentage: 70 }, |
||||
|
{ color: '#95d475', percentage: 80 }, |
||||
|
{ color: '#67C23A', percentage: 90 }, |
||||
|
{ color: '#529b2e', percentage: 100 } |
||||
|
], |
||||
|
type: ['danger', 'warning', 'info', 'primary', 'success'], |
||||
|
isVip: true, |
||||
|
configStore: useConfigStore(), |
||||
|
dialogInfo: { |
||||
|
show: false, |
||||
|
title: '比赛结果', |
||||
|
winIndex: -1 |
||||
|
}, |
||||
|
diolag: true, |
||||
|
clickCount: 0, |
||||
|
avaList: [] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
init(isc = false) { |
||||
|
this.dealData() |
||||
|
this.setTime('timer0', 'time0', isc) |
||||
|
this.setTime('timer1', 'time1', isc) |
||||
|
}, |
||||
|
setTime(name, time, isGo = true) { |
||||
|
this.timeObj[time] = 100 |
||||
|
if (this.timeObj[name]) { |
||||
|
clearInterval(this.timeObj[name]) |
||||
|
} |
||||
|
if (isGo) { |
||||
|
this.timeObj[name] = setInterval(() => { |
||||
|
this.timeObj[time] -= 1 |
||||
|
this.timeObj[time] = _.max([this.timeObj[time], 0]) |
||||
|
}, 1000) |
||||
|
} |
||||
|
}, |
||||
|
getAva() { |
||||
|
this.clickCount++ |
||||
|
console.log(777, this.clickCount, this.avaList) |
||||
|
if (this.clickCount % 2) { |
||||
|
let arr = _.values(config) |
||||
|
let random = _.shuffle(arr) |
||||
|
this.avaList = [random[0], random[1]] |
||||
|
} |
||||
|
}, |
||||
|
dealData() { |
||||
|
this.getAva() |
||||
|
this.personList = [] |
||||
|
for (let i = 0; i < 2; i++) { |
||||
|
let item = { |
||||
|
ava: this.avaList[i], |
||||
|
questionList: [ |
||||
|
{ |
||||
|
title: '衣服可以保暖,体现了产品层次模型中的哪一层次产品?', |
||||
|
options: [ |
||||
|
{ |
||||
|
text: 'A 核心产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'B 形式产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'C 期望产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'D 附加产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
} |
||||
|
], |
||||
|
answer: -1, |
||||
|
trueAnswer: 0, |
||||
|
startTime: parseInt(dayjs().valueOf() / 10), |
||||
|
endTime: 0 |
||||
|
}, |
||||
|
{ |
||||
|
title: '产品的五层次中,____是指产品的品质、样式、包装和商标等。', |
||||
|
options: [ |
||||
|
{ |
||||
|
text: 'A 核心产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'B 形式产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'C 延伸产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'D 潜在产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
} |
||||
|
], |
||||
|
answer: -1, |
||||
|
trueAnswer: 1, |
||||
|
startTime: 0, |
||||
|
endTime: 0 |
||||
|
}, |
||||
|
{ |
||||
|
title: |
||||
|
'顾客希望旅游景区能够呈现丰富的旅游资源、干净整洁的游园环境、完善的基础设施设备,体现了产品层次模型中的哪一层次产品?', |
||||
|
options: [ |
||||
|
{ |
||||
|
text: 'A 核心产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'B 期望产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'C 形式产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'D 潜在产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
} |
||||
|
], |
||||
|
answer: -1, |
||||
|
trueAnswer: 1, |
||||
|
startTime: 0, |
||||
|
endTime: 0 |
||||
|
}, |
||||
|
{ |
||||
|
title: |
||||
|
'在传统村落白虎村旅游地可以购买纪念品、旅游保险、拍摄个人照片等,增强了游客的旅游体验,这属于哪一层次产品?', |
||||
|
options: [ |
||||
|
{ |
||||
|
text: 'A 核心产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'B 形式产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'C 延伸产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'D 潜在产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
} |
||||
|
], |
||||
|
answer: -1, |
||||
|
trueAnswer: 2, |
||||
|
startTime: 0, |
||||
|
endTime: 0 |
||||
|
}, |
||||
|
{ |
||||
|
title: '传统皮影面临困境,正在向数字皮影转型,体现了产品五层次模型中的哪一层次产品?', |
||||
|
options: [ |
||||
|
{ |
||||
|
text: 'A 核心产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'B 形式产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'C 期望产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
}, |
||||
|
{ |
||||
|
text: 'D 潜在产品', |
||||
|
checked: false, |
||||
|
type: 'info', |
||||
|
disabled: false |
||||
|
} |
||||
|
], |
||||
|
answer: -1, |
||||
|
trueAnswer: 3, |
||||
|
startTime: 0, |
||||
|
endTime: 0 |
||||
|
} |
||||
|
], |
||||
|
currentTi: 0, |
||||
|
clickDisable: false, // 是否点击 |
||||
|
time: 0, |
||||
|
sure: [] |
||||
|
} |
||||
|
this.personList.push(_.cloneDeep(item)) |
||||
|
} |
||||
|
}, |
||||
|
onChange1(optionItem, optionIndex, item, perItem, perIndex) { |
||||
|
optionItem.checked = true |
||||
|
item.answer = optionIndex |
||||
|
for (let i = 0; i < item.options.length; i++) { |
||||
|
if (optionIndex !== i) { |
||||
|
item.options[i].checked = false |
||||
|
} |
||||
|
item.options[i].type = 'info' |
||||
|
} |
||||
|
optionItem.type = 'success' |
||||
|
}, |
||||
|
nextTi(perItem, perIndex) { |
||||
|
let duration = 1500 |
||||
|
perItem.clickDisable = true |
||||
|
let item = perItem.questionList[perItem.currentTi] |
||||
|
if (item.answer !== item.trueAnswer) { |
||||
|
item.options[item.answer].type = 'danger' |
||||
|
item.options[item.trueAnswer].type = 'success' |
||||
|
} else { |
||||
|
item.options[item.trueAnswer].type = 'success' |
||||
|
} |
||||
|
perItem.questionList[perItem.currentTi].endTime = parseInt(dayjs().valueOf() / 10) |
||||
|
for (let i = 0; i < perItem.questionList[perItem.currentTi].options.length; i++) { |
||||
|
let ele = perItem.questionList[perItem.currentTi].options[i] |
||||
|
ele.disabled = true |
||||
|
} |
||||
|
let _msg = -1 |
||||
|
if ( |
||||
|
perItem.questionList[perItem.currentTi].answer === |
||||
|
perItem.questionList[perItem.currentTi].trueAnswer |
||||
|
) { |
||||
|
_msg = ElMessage({ |
||||
|
type: 'info', |
||||
|
duration: duration - 500, |
||||
|
customClass: 'yanmo', |
||||
|
icon: 'check', |
||||
|
message: '恭喜你,答对了!', |
||||
|
appendTo: document.getElementById(`yanmo${perIndex}`) |
||||
|
}) |
||||
|
let rightAudio = document.getElementById('right') |
||||
|
rightAudio.play() |
||||
|
} else { |
||||
|
_msg = ElMessage({ |
||||
|
type: 'info', |
||||
|
duration: duration - 500, |
||||
|
customClass: 'yanmo', |
||||
|
icon: 'close', |
||||
|
message: '很遗憾,打错了!', |
||||
|
appendTo: document.getElementById(`yanmo${perIndex}`) |
||||
|
}) |
||||
|
let errorAudio = document.getElementById('error') |
||||
|
errorAudio.play() |
||||
|
} |
||||
|
setTimeout(() => { |
||||
|
if (_msg !== -1) { |
||||
|
_msg.close() |
||||
|
} |
||||
|
//加一防止出错循环取余 |
||||
|
if (perItem.currentTi == perItem.questionList.length - 1) { |
||||
|
this.submit(perItem, perIndex) |
||||
|
} |
||||
|
let count = perItem.questionList.length |
||||
|
perItem.currentTi += 1 |
||||
|
perItem.currentTi %= count |
||||
|
// 将下一题的初始时间定死 |
||||
|
perItem.questionList[perItem.currentTi].startTime = parseInt(dayjs().valueOf() / 10) |
||||
|
this.setTime(`timer${perIndex}`, `time${perIndex}`) |
||||
|
perItem.clickDisable = false |
||||
|
}, duration) |
||||
|
}, |
||||
|
//提交 |
||||
|
submit(perItem, perIndex) { |
||||
|
perItem.time = 0 |
||||
|
perItem.sure = [] |
||||
|
for (let i = 0; i < perItem.questionList.length; i++) { |
||||
|
let element = perItem.questionList[i] |
||||
|
perItem.time += element.endTime - element.startTime |
||||
|
perItem.sure.push(element.trueAnswer === element.answer) |
||||
|
} |
||||
|
let pass = true |
||||
|
let sure = [] |
||||
|
let time = [] |
||||
|
for (let i = 0; i < JSON.parse(JSON.stringify(this.personList)).length; i++) { |
||||
|
let ele = this.personList[i] |
||||
|
sure.push(_.compact(ele.sure)) |
||||
|
time.push(ele.time) |
||||
|
if (!ele.time) { |
||||
|
pass = false |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if (_.compact(sure[0]).length > _.compact(sure[1]).length) { |
||||
|
this.dialogInfo.winIndex = 0 |
||||
|
} else if (_.compact(sure[0]).length === _.compact(sure[1]).length) { |
||||
|
if (time[0] > time[1]) { |
||||
|
this.dialogInfo.winIndex = 1 |
||||
|
} else { |
||||
|
this.dialogInfo.winIndex = 0 |
||||
|
} |
||||
|
} else { |
||||
|
this.dialogInfo.winIndex = 1 |
||||
|
} |
||||
|
if (pass) { |
||||
|
this.dialogInfo.show = true |
||||
|
} |
||||
|
this.setTime(`timer${perIndex}`, `time${perIndex}`, false) |
||||
|
}, |
||||
|
handleClose(done) { |
||||
|
done() |
||||
|
this.continuePk() |
||||
|
}, |
||||
|
continuePk() { |
||||
|
this.dialogInfo.show = false |
||||
|
this.diolag = true |
||||
|
this.init() |
||||
|
}, |
||||
|
handleCloseDialog() { |
||||
|
this.diolag = false |
||||
|
this.init(true) |
||||
|
} |
||||
|
}, |
||||
|
async mounted() { |
||||
|
let audio = document.getElementById('myAudio1') |
||||
|
audio.addEventListener('canplay', function () { |
||||
|
audio.volume = 0.1 |
||||
|
}) |
||||
|
let fiveDay = dayjs('2024-05-21T00:00:00').valueOf() |
||||
|
if (!this.isVip) { |
||||
|
if (dayjs().valueOf() > fiveDay) { |
||||
|
this.isVip = false |
||||
|
return |
||||
|
} else { |
||||
|
this.isVip = true |
||||
|
} |
||||
|
} |
||||
|
this.dealData() |
||||
|
}, |
||||
|
watch: {}, |
||||
|
computed: {} |
||||
|
} |
||||
|
</script> |
||||
|
<style scoped> |
||||
|
.appClass { |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
color: #000; |
||||
|
background-color: #d9ecff; |
||||
|
} |
||||
|
|
||||
|
.parent0 { |
||||
|
border-right: 20vw solid #fff; |
||||
|
display: inline-block; |
||||
|
width: 40vw; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.parent1 { |
||||
|
display: inline-block; |
||||
|
width: 40vw; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.avatar0 { |
||||
|
text-align: left; |
||||
|
margin: 6px 0 0 6px; |
||||
|
min-height: 10vh; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.avatar1 { |
||||
|
text-align: right; |
||||
|
margin: 6px 16px 0 0; |
||||
|
min-height: 10vh; |
||||
|
width: 99%; |
||||
|
} |
||||
|
|
||||
|
.usetime0 { |
||||
|
text-align: center; |
||||
|
height: 0; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.usetime1 { |
||||
|
text-align: center; |
||||
|
height: 0; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.result0 { |
||||
|
text-align: center; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
min-height: 8vh; |
||||
|
width: 100%; |
||||
|
font-size: 2em; |
||||
|
} |
||||
|
|
||||
|
.result1 { |
||||
|
text-align: center; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
min-height: 8vh; |
||||
|
width: 100%; |
||||
|
font-size: 2em; |
||||
|
} |
||||
|
|
||||
|
.question0 { |
||||
|
height: 20vh; |
||||
|
text-align: center; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
} |
||||
|
|
||||
|
.question1 { |
||||
|
height: 20vh; |
||||
|
text-align: center; |
||||
|
position: relative; |
||||
|
top: -10vh; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
text-indent: 2em; |
||||
|
width: 76%; |
||||
|
margin-left: 10%; |
||||
|
height: 31vh; |
||||
|
color: #000; |
||||
|
padding: 1% 2% 0 2%; |
||||
|
font-size: 2.3em; |
||||
|
border-radius: 10px; |
||||
|
text-align: left; |
||||
|
align-content: center; |
||||
|
background-color: #4bc1ed; |
||||
|
} |
||||
|
|
||||
|
.options { |
||||
|
width: 80%; |
||||
|
border-radius: 30px; |
||||
|
margin-left: 10%; |
||||
|
height: 34vh; |
||||
|
color: #fff; |
||||
|
text-align: center; |
||||
|
padding-top: 1vh; |
||||
|
margin-top: 3vh; |
||||
|
background-color: #4bc1ed; |
||||
|
} |
||||
|
|
||||
|
.option0 { |
||||
|
height: calc(9vh - 12px); |
||||
|
margin-top: 6px; |
||||
|
} |
||||
|
|
||||
|
.option1 { |
||||
|
height: calc(9vh - 12px); |
||||
|
margin-top: 6px; |
||||
|
} |
||||
|
|
||||
|
.next0 { |
||||
|
padding-top: 4vh; |
||||
|
min-height: 5vh; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.next1 { |
||||
|
padding-top: 4vh; |
||||
|
min-height: 5vh; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.mt-10 { |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.mt-20 { |
||||
|
margin: 40px 0; |
||||
|
} |
||||
|
|
||||
|
.noVip { |
||||
|
text-align: center; |
||||
|
margin-top: 40vh; |
||||
|
} |
||||
|
|
||||
|
.noVipTag { |
||||
|
font-size: 3em; |
||||
|
line-height: 1.6em; |
||||
|
height: 1.6em; |
||||
|
} |
||||
|
</style> |
After Width: | Height: | Size: 36 KiB |
@ -0,0 +1,53 @@ |
|||||
|
html, |
||||
|
body { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
.option0 .el-check-tag { |
||||
|
padding: 20px 60px; |
||||
|
font-size: 2em; |
||||
|
padding: 13px 60px; |
||||
|
} |
||||
|
.option1 .el-check-tag { |
||||
|
padding: 20px 60px; |
||||
|
font-size: 2em; |
||||
|
padding: 13px 60px; |
||||
|
} |
||||
|
.option0 .el-tag { |
||||
|
padding: 20px 60px; |
||||
|
font-size: 2em; |
||||
|
} |
||||
|
.option1 .el-tag { |
||||
|
padding: 20px 60px; |
||||
|
font-size: 2em; |
||||
|
} |
||||
|
.yanmo { |
||||
|
position: relative; |
||||
|
height: 35vh; |
||||
|
width: 24vw; |
||||
|
top: 20vh !important; |
||||
|
background-color: #83838399; |
||||
|
border: none; |
||||
|
} |
||||
|
.yanmo .el-icon { |
||||
|
position: relative; |
||||
|
left: 6vw; |
||||
|
font-size: 20vh; |
||||
|
top: -5vh; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.yanmo .el-message__content { |
||||
|
position: relative; |
||||
|
left: -4vw; |
||||
|
width: 248px; |
||||
|
font-size: 1em; |
||||
|
top: 5vh; |
||||
|
color: #fff; |
||||
|
} |
||||
|
#yanmo0 { |
||||
|
height: 0px; |
||||
|
} |
||||
|
#yanmo1 { |
||||
|
height: 0px; |
||||
|
} |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 608 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 102 KiB |
@ -0,0 +1,30 @@ |
|||||
|
import chouni from "./assets/imgs/chouni.jpg" |
||||
|
import fugu from "./assets/imgs/fugu.png" |
||||
|
import ganfanren from "./assets/imgs/ganfanren.jpg" |
||||
|
import luobo from "./assets/imgs/luobo.jpg" |
||||
|
import paopao from "./assets/imgs/paopao.jpg" |
||||
|
import shuimian from "./assets/imgs/sou.jpg" |
||||
|
import sou from "./assets/imgs/tuzi.jpg" |
||||
|
import tuzi from "./assets/imgs/xiaoxi.jpg" |
||||
|
import xiaoxi from "./assets/imgs/xiaozhu.jpeg" |
||||
|
import yuanbao from "./assets/imgs/yuanbao.jpeg" |
||||
|
import zhangcao from "./assets/imgs/zhangcao.jpg" |
||||
|
import zhaocai from "./assets/imgs/zhaocai.jpg" |
||||
|
import zhenxiang from "./assets/imgs/zhenxiang.jpg" |
||||
|
|
||||
|
export default |
||||
|
{ |
||||
|
chouni, |
||||
|
fugu, |
||||
|
ganfanren, |
||||
|
luobo, |
||||
|
paopao, |
||||
|
shuimian, |
||||
|
sou, |
||||
|
tuzi, |
||||
|
xiaoxi, |
||||
|
yuanbao, |
||||
|
zhangcao, |
||||
|
zhaocai, |
||||
|
zhenxiang |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
// main.ts
|
||||
|
import { createApp } from 'vue' |
||||
|
import ElementPlus from 'element-plus' |
||||
|
import 'element-plus/dist/index.css' |
||||
|
import App from './App.vue' |
||||
|
import './assets/css/base.css' |
||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue' |
||||
|
|
||||
|
const app = createApp(App) |
||||
|
import pinia from './stores' |
||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { |
||||
|
app.component(key, component) |
||||
|
} |
||||
|
app.use(ElementPlus) |
||||
|
app.use(pinia) |
||||
|
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,27 @@ |
|||||
|
import { defineStore } from 'pinia' |
||||
|
const useConfigStore = defineStore('config', { |
||||
|
state: () => { |
||||
|
return { |
||||
|
tableData: [] |
||||
|
} |
||||
|
}, |
||||
|
actions: { |
||||
|
setTableData(Data = []) { |
||||
|
console.log(999, Data) |
||||
|
this.tableData = Data |
||||
|
}, |
||||
|
getTableData() { |
||||
|
return this.tableData |
||||
|
}, |
||||
|
}, |
||||
|
//整个仓库持久化存储
|
||||
|
persist: { |
||||
|
enabled: true, |
||||
|
//指定字段存储,并且指定存储方式:
|
||||
|
strategies: [ |
||||
|
{ storage: sessionStorage, paths: ['tableData'] }, // tableData字段用sessionStorage存储
|
||||
|
{ storage: localStorage, paths: ['tableData'] }, // accessToken字段用 localstorage存储
|
||||
|
], |
||||
|
}, |
||||
|
}) |
||||
|
export default useConfigStore |
@ -0,0 +1,5 @@ |
|||||
|
import { createPinia } from "pinia"; |
||||
|
import piniaPluginPersist from "pinia-plugin-persist" |
||||
|
const pinia = createPinia(); |
||||
|
pinia.use(piniaPluginPersist) |
||||
|
export default pinia; |