@ -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; |