This commit is contained in:
X1627315083
2025-10-09 09:29:36 +08:00
commit cc8c064d9c
107 changed files with 12520 additions and 0 deletions

View File

@@ -0,0 +1 @@
VITE_APP_URL = ''

View File

@@ -0,0 +1 @@
VITE_APP_URL = http://18.167.251.121:10096

1
activities/.env.test Normal file
View File

@@ -0,0 +1 @@
VITE_APP_URL = http://18.167.251.121:10095

15
activities/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,15 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
}
}

28
activities/.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

View File

@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}

3
activities/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

46
activities/README.md Normal file
View File

@@ -0,0 +1,46 @@
# vue3demo
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```

8
activities/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-auto-import
export {}
declare global {
}

23
activities/components.d.ts vendored Normal file
View File

@@ -0,0 +1,23 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
}
}

1
activities/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

15
activities/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> -->
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<title>Activities</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

8955
activities/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

54
activities/package.json Normal file
View File

@@ -0,0 +1,54 @@
{
"name": "vue3demo",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"test": "vite --mode test",
"build": "run-p type-check build-only",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/",
"postinstall": "husky install"
},
"dependencies": {
"axios": "^1.3.6",
"element-plus": "^2.10.4",
"gsap": "^3.13.0",
"normalize.css": "^8.0.1",
"pinia": "^2.0.32",
"pinia-persistedstate-plugin": "^0.1.0",
"pinia-plugin-persistedstate": "^3.1.0",
"vue": "^3.2.47",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@types/node": "^18.16.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.34.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0",
"husky": "^8.0.3",
"less": "^4.3.0",
"lint-staged": "^13.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.4",
"typescript": "~4.8.4",
"unplugin-auto-import": "^0.15.3",
"unplugin-vue-components": "^0.24.1",
"vite": "^4.1.4",
"vue-tsc": "^1.2.0"
},
"lint-staged": {
"*.{vue,js}": [
"npm run lint"
]
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,29 @@
/* cyrillic-ext */
@font-face {
font-family: 'poppinsRegular';
font-style: italic;
font-weight: 700;
src: url(./Poppins/Poppins-Regular.ttf) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'poppinsSemiBold';
font-style: italic;
font-weight: 700;
src: url(./Poppins/Poppins-SemiBold.ttf) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'poppinsBold';
font-style: italic;
font-weight: 700;
src: url(./Poppins/Poppins-Bold.ttf) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'poppinsMedium';
font-style: italic;
font-weight: 700;
src: url(./Poppins/Poppins-Medium.ttf) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

40
activities/src/App.vue Normal file
View File

@@ -0,0 +1,40 @@
<template>
<RouterView />
</template>
<style lang="less">
#app{
font-size: 1.6rem;
box-sizing: border-box;
overscroll-behavior: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
*{
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
touch-action: pan-x pan-y; /* 允许单指平移(滚动)但禁用捏合缩放 */
}
// touch-action: none; /* 完全禁用浏览器默认触摸行为 */
// -webkit-user-select: none;
// -moz-user-select: none;
// -ms-user-select: none;
// user-select: none;
// -webkit-touch-callout: none;
@media (min-width: 1024px) {
width: 100vw;
height: 100vh;
position:relative
}
/* touch-action: none; */
}
</style>

View File

@@ -0,0 +1,39 @@
import request from '@/utils/request'
//创建用户
export function userCreate(params: Object) {
return request({
url: '/api/user/create',
method: 'get',
params
})
}
//记录用户选择的信息
export function userSave(params: Object) {
return request({
url: '/api/user/save',
method: 'get',
params,
meta: {
// responseAll: true // 返回所有的信息包括状态码和message和data
}
} as any)
}
//获取题目
export function getQueryPage(data: Object) {
return request({
url: '/api/outfits/queryPage',
method: 'post',
data,
// repeatRequest: true // 配置为true则可以同一时间多次调用
}as any)
}
//查报告
export function getCalculateReport() {
return request({
url: '/api/outfits/calculateReport',
method: 'get',
})
}

View File

@@ -0,0 +1,74 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -0,0 +1,10 @@
* {
padding: 0;
margin: 0;
list-style: none;
box-sizing: border-box;
}
html,
body {
height: 100%;
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.77201 14.9283L8.9297 15.7417C8.57304 16.0861 7.99632 16.0861 7.64345 15.7417L0.267492 8.62285C-0.0891642 8.27845 -0.0891642 7.72155 0.267492 7.38081L7.64345 0.258301C8.00011 -0.0861003 8.57683 -0.0861003 8.9297 0.258301L9.77201 1.07167C10.1325 1.41974 10.1249 1.98763 9.75683 2.32837L5.1848 6.53446H16.0894C16.594 6.53446 17 6.92649 17 7.41378V8.58622C17 9.07351 16.594 9.46554 16.0894 9.46554H5.1848L9.75683 13.6716C10.1287 14.0124 10.1363 14.5803 9.77201 14.9283Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,31 @@
@import './base.css';
#app {
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
/* @media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
} */

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>

View File

@@ -0,0 +1,86 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank">Cypress Component Testing</a>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>

View File

@@ -0,0 +1,86 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View File

@@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

5
activities/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

32
activities/src/main.ts Normal file
View File

@@ -0,0 +1,32 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// import ElementPlus from 'element-plus'
import store from './stores/index'
import 'normalize.css/normalize.css'
import './assets/css/style.css'
import flexible from "./utils/flexible.js";
//引入element-plus相关样式
import 'element-plus/dist/index.css'
import './assets/main.css'
import "./router/router-config" // 路由守卫,做动态路由的地方
var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
const app = createApp(App)
// app.use(ElementPlus)
app.use(router)
app.use(store)
flexible();
app.mount('#app')

View File

@@ -0,0 +1,80 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory('/'),
// history: createWebHistory(import.meta.env.VITE_APP_URL),
routes: [
{
path: '/',
redirect: "/activities",
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
},{
path: '/activities',
name: 'Activities',
component: () => import('../views/Activities/index.vue'),
children: [
// {
// path: '',
// name: 'activitiesWorkshop',
// redirect: "/activities/workshop",
// },
{
path: "workshop",
name: "Workshop",
component: () => import('../views/Activities/workshop/index.vue'),
children: [
{
path: '',
name: 'adtivitiesWorkshopLogin',
redirect: "/activities/workshop/login",
},{
path: "login",
name: "login",
component: () => import('../views/Activities/workshop/views/login.vue'),
},{
path: "edit",
name: "edit",
component: () => import('../views/Activities/workshop/views/edit.vue'),
},{
path: "success",
name: "success",
component: () => import('../views/Activities/workshop/views/success.vue'),
},
]
},
{
path: "workshop2",
name: "Workshop2",
component: () => import('../views/Activities/workshop2/index.vue'),
children: [
{
path: '',
name: 'adtivitiesWorkshop2Login',
redirect: "/activities/workshop2/login",
},{
path: "login",
name: "login2",
component: () => import('../views/Activities/workshop2/views/login.vue'),
},{
path: "edit",
name: "edit2",
component: () => import('../views/Activities/workshop2/views/edit.vue'),
},{
path: "success",
name: "success2",
component: () => import('../views/Activities/workshop2/views/success.vue'),
},
]
},
]
},
]
})
export default router

View File

@@ -0,0 +1,14 @@
import router from './index'
// 白名单页面直接进入
const whiteList = ['/login']
console.log(whiteList)
router.beforeEach((to, from, next) => {
next()
})
router.afterEach(() => {
// finish progress bar
// NProgress.done()
})

View File

@@ -0,0 +1,7 @@
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-persistedstate-plugin'
// 创建store实例
const store = createPinia()
// 使用持久化插件(全局持久化)
store.use(createPersistedState())
export default store

View File

@@ -0,0 +1,26 @@
// 每一个存储的模块命名规则use开头store结尾
import { defineStore } from 'pinia'
export const useUserInfoStore = defineStore({
id: 'userInfo', // 必须指明唯一的pinia仓库的id
state: () => {
return {
num: 0,
name: '张三',
token: ''
}
},
getters: {
doubleCount: (state) => state.num * 2
},
actions: {
changeNum() {
this.num++
},
loginOut() {
// 处理退出登录的一些逻辑
return new Promise((rez) => {
rez('111')
})
}
}
})

View File

@@ -0,0 +1,31 @@
// 每一个存储的模块命名规则use开头store结尾
import { defineStore } from 'pinia'
export const useWorkshopStore = defineStore({
id: 'workshop', // 必须指明唯一的pinia仓库的id
state: () => {
return {
userData:{
title: '123123',
name: '123123',
describe: '',
id:'',
},
queryList:[]
}
},
getters: {
// doubleCount: (state) => state.num * 2
},
actions: {
setLogin(data:any) {
this.userData.title= data.title
this.userData.name= data.name
this.userData.describe= data.describe
this.userData.id= data.id
console.log(this.userData)
},
setQueryList(list:any) {
this.queryList = list
},
}
})

View File

@@ -0,0 +1,48 @@
import { getUniversalZoomLevel } from '@/utils/tools'
let flexible = (designWidth, maxWidth,minWidth) =>{
var doc = document, win = window, docEl = doc.documentElement, remStyle = document.createElement("style"), tid;
designWidth = designWidth || 1920;
// maxWidth = maxWidth || 1920;
// minWidth = minWidth || 500;
// minWidth = minWidth || 1024;
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
var height = docEl.getBoundingClientRect().height;
width = getUniversalZoomLevel() * width
height = getUniversalZoomLevel() * height
// width > maxWidth && (width = maxWidth);
// width < minWidth && (width = minWidth);
if(width >= 1024){
designWidth = 1920
}else{
designWidth = 375
}
var rem = Math.round(width * 10 / designWidth);
docEl.style.fontSize = rem+'px'
remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
}
//要等 wiewport 设置好后才能执行 refreshRem不然 refreshRem 会执行2次
refreshRem();
win.addEventListener("resize", function() {
clearTimeout(tid); //防止执行两次
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener("pageshow", function(e) {
if (e.persisted) { // 浏览器后退的时候重新计算
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === "complete") {
doc.body.style.fontSize = "16px";
} else {
doc.addEventListener("DOMContentLoaded", function(e) {
doc.body.style.fontSize = "16px";
}, false);
}
};
export default flexible

View File

@@ -0,0 +1,12 @@
function getLocal(key = 'token') {
return localStorage.getItem(key)
}
// 删除
function removeLocal(key = 'token') {
window.localStorage.removeItem(key)
}
// 保存
function setLocal(value: any, key = 'token') {
window.localStorage.setItem(key, value)
}
export { getLocal, removeLocal, setLocal }

View File

@@ -0,0 +1,173 @@
import axios from 'axios'
// import { Message, Loading, MessageBox } from 'element-ui'
import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
import { useUserInfoStore } from '@/stores/modules/userInfo'
const store = useUserInfoStore()
import { getLocal } from '@/utils/local'
// 创建axios实例
console.log(import.meta.env.VITE_APP_URL)
const service = axios.create({
baseURL: import.meta.env.VITE_APP_URL, // api的base_url
// baseURL: import.meta.env.VITE_APP_URL, // api的base_url
timeout: 5000 // 请求超时时间
})
axios.defaults.headers.post["Content-Type"] = "application/json";
axios.defaults.headers.post['lang'] = 'en'; //配置语言请求头
axios.defaults.withCredentials = true; //跨域携带cookie
// request拦截器
service.interceptors.request.use(
(config: any) => {
removePending(config)
// 如果repeatRequest不配置那么默认该请求就取消重复接口请求
!config.repeatRequest && addPending(config)
// 打开loading
if (config.loading) {
LoadingInstance._count++
if (LoadingInstance._count === 1) {
openLoading(config.loadingDom)
}
}
// 如果登录了有token则请求携带token
// Do something before request is sent
if (store.token) {
config.headers.Authorization = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
// config.headers['X-Token'] = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
}
return config
},
(error) => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// respone拦截器
service.interceptors.response.use(
// response => response,
/**
* 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/
(response: any) => {
// 已完成请求的删除请求中数组
removePending(response.config)
// 关闭loading
if (response.config.loading) {
closeLoading()
}
const res = response.data
// 处理异常的情况
if (res.errCode != 0) {
ElMessage({
message: res.errMsg,
type: 'error',
duration: 5 * 1000
})
// 403:非法的token; 50012:其他客户端登录了; 401:Token 过期了;
if (res.errCode === 403 || res.errCode === 50012 || res.errCode === 401) {
ElMessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.loginOut().then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
}
return Promise.reject(new Error('error'))
} else {
// 默认只返回data不返回状态码和message
// 通过 meta 中的 responseAll 配置来取决后台是否返回所有数据(包括状态码message和data)
const isbackAll = response.config.meta && response.config.meta.responseAll
if (isbackAll) {
return res
} else {
return res.data
}
}
},
(error) => {
error.config && removePending(error.config)
// 关闭loading
if (error.config?.loading) {
closeLoading()
}
console.log('err' + error) // for debug
ElMessage({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
// --------------------------------取消接口重复请求的函数-----------------------------------
// axios.js
const pendingMap = new Map()
/**
* 生成每个请求唯一的键
* @param {*} config
* @returns string
*/
function getPendingKey(config: any) {
const { url, method, params } = config
let { data } = config
if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}
/**
* 储存每个请求唯一值, 也就是cancel()方法, 用于取消请求
* @param {*} config
*/
function addPending(config: any) {
const pendingKey = getPendingKey(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel)
}
})
}
/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config: any) {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
cancelToken(pendingKey)
pendingMap.delete(pendingKey)
}
}
// ----------------------------------loading的函数-------------------------------
const LoadingInstance: { _target: any; _count: number } = {
_target: null, // 保存Loading实例
_count: 0
}
function openLoading(loadingDom: any) {
LoadingInstance._target = ElLoading.service({
lock: true,
text: '数据正在加载中',
spinner: 'el-icon-loading',
background: 'rgba(25, 32, 53, 1)',
target: loadingDom || 'body'
})
}
function closeLoading() {
if (LoadingInstance._count > 0) LoadingInstance._count--
if (LoadingInstance._count === 0) {
LoadingInstance._target.close()
LoadingInstance._target = null
}
}
export default service

View File

@@ -0,0 +1,48 @@
function getUniversalZoomLevel() {
// 现代浏览器方案
if (window.visualViewport) {
return window.visualViewport.scale;
}
// 备用方案1
if (window.devicePixelRatio) {
return window.devicePixelRatio;
}
// 备用方案2不精确
return window.outerWidth / window.innerWidth;
}
const getMousePosition = (e:any,bor:any) => {
// if(e?.stopPropagation)e.stopPropagation()
// if(e?.preventDefault)e.preventDefault();
let event:any
if(bor){
const touch = e.changedTouches[0] as any;
event = {
offsetX:touch.clientX - e.target.getBoundingClientRect().left,
offsetY: touch.clientY - e.target.getBoundingClientRect().top,
clientX:touch.clientX,
clientY:touch.clientY,
screenX:touch.screenX,
screenY:touch.screenY,
target:e.target,
}
// if(dom){
// event.offsetX = touch.clientX - dom.getBoundingClientRect().left
// event.offsetY = touch.clientY - dom.getBoundingClientRect().top
// }
}else{
event = {
offsetX:e.offsetX,
offsetY:e.offsetY,
clientX:e.clientX,
clientY:e.clientY,
screenX:e.screenX,
screenY:e.screenY,
target:e.target,
}
}
return event
}
export {
getUniversalZoomLevel,
getMousePosition,
}

View File

@@ -0,0 +1,15 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,22 @@
<!-- <script setup lang="ts">
</script> -->
<template>
<div class="activities">
<RouterView />
</div>
</template>
<style lang="less" scoped>
.activities {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
font-family: poppinsMedium;
}
// @media (min-width: 1024px) {
// }
</style>

View File

@@ -0,0 +1,169 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import {getCalculateReport} from '@/api/workshop'
const props = defineProps({
isSummaryReport:{
type:Boolean,
default:false,
},
calculateReport:{
type:Object,
default:()=>{},
},
})
const emit = defineEmits([
'update:isSummaryReport'
])
let data = reactive({
})
const goBack = ()=>{
emit('update:isSummaryReport',false)
}
onMounted(()=>{
})
onUnmounted(()=>{
})
const {} = toRefs(data);
</script>
<template>
<div class="summaryReport">
<div class="header">
Swipe Style
<img @click="goBack" src="@/assets/images/workshop/back.svg" alt="">
</div>
<div class="body">
<div class="text">
<div class="title">Summary Report!</div>
<div class="progress">Current progress: {{ calculateReport?.count }}/{{ calculateReport?.count + calculateReport?.answeringCount }} people</div>
<!-- <div class="info">Based on the choice of the majority, system<br /> detected the most popular trends for you: <span>XXX</span>.</div> -->
</div>
<div class="popularChoices swiper">
<div class="title">Popular Choices</div>
<div class="imgBox">
<div class="imgList">
<div class="item" v-for="item in calculateReport?.mostLikeOutfits">
<img :src="item?.outfitImagePath" alt="">
</div>
</div>
</div>
</div>
<!-- <div class="text middle">
<div class="info">System detected the most unpopular trends for you: <br /> <span>XXX/XXX/XXX</span>.</div>
</div> -->
<div class="unpopularChoices swiper">
<div class="title">Unpopular Choices</div>
<div class="imgBox">
<div class="imgList">
<div class="item" v-for="item in calculateReport?.leastLikeOutfits">
<img :src="item?.outfitImagePath" alt="">
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.summaryReport{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
> .header{
padding: .9rem 0;
position: relative;
font-weight: 600;
font-size: 2.2rem;
text-align: center;
font-family: poppinsBold;
> img{
position: absolute;
left: 3rem;
width: 1.7rem;
height: 1.7rem;
top: 50%;
transform: translateY(-50%);
}
}
> .body{
flex: 1;
overflow-y: auto;
> .text{
margin-top: 2rem;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
> .progress{
font-size: 1rem;
color: #cdcdcd;
font-weight: 500;
}
> .title{
font-weight: 500;
font-size: 2rem;
color: #4D52C3;
}
> .info{
font-weight: 400;
font-size: 1.2rem;
color: #000;
margin-top: 1.2rem;
> span{
font-weight: 600;
color: #4d52c3;
}
}
}
> .swiper{
width: calc(100% - 3.2rem * 2);
margin: 0 auto;
margin-top: 2.4rem;
> .title{
font-weight: 500;
font-size: 1.6rem;
// margin-bottom: 1.2rem;
}
> .imgBox{
margin-top: 1.6rem;
> .imgList{
width: auto;
overflow-x: auto;
display: flex;
> .item{
shrink: 0;
padding-bottom: 1.2rem;
padding-right: 1.2rem;
> img{
// margin-right: 1.2rem;
width: 12.8rem;
height: 19.2rem;
border-radius: 1rem;
box-shadow: .7rem .4rem 1rem 0rem rgba(0,0,0,.2);
object-fit: contain;
&:last-child{
}
}
}
}
}
}
> .popularChoices{
> .imgBox{
> .imgList{
padding-bottom: calc(5.2rem - 2.4rem);
}
> .imgList::-webkit-scrollbar {
display: none;
}
}
}
> .unpopularChoices{
}
}
}
</style>

View File

@@ -0,0 +1,90 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import {getQueryPage} from '@/api/workshop'
import { useWorkshopStore } from '@/stores/modules/workshop'
import { useRouter,useRoute, onBeforeRouteLeave } from 'vue-router'
const route = useRoute()
const router = useRouter()
const store = useWorkshopStore()
let dataDom = reactive({
loadingDom:null as any,
})
let data = reactive({
})
const handleBeforeUnload = (e:any) => {
if (window.location.pathname === '/activities/workshop/success') {
localStorage.setItem('shouldRedirectAfterRefresh', 'true');
// e.preventDefault();
// e.returnValue = '刷新后将跳转到登录页';
}
};
onBeforeRouteLeave((to, from, next) => {
if (confirm('自定义提示:确定离开当前页面吗?')) {
next();
} else {
next(false);
}
});
onMounted(()=>{
getQueryPage({page:1,size:200}).then((rv:any)=>{
store.setQueryList(rv.content)
})
setTimeout(()=>{
dataDom.loadingDom.style.opacity = '0';
setTimeout(()=>{
dataDom.loadingDom.style.display = 'none';
},1000)
},3000)
if (localStorage.getItem('shouldRedirectAfterRefresh') === 'true') {
localStorage.removeItem('shouldRedirectAfterRefresh'); // 清除标记
router.push('/activities/workshop/login'); // 跳转
}
window.addEventListener('pagehide', handleBeforeUnload)
})
onUnmounted(()=>{
window.removeEventListener('pagehide', handleBeforeUnload)
})
const { loadingDom } = toRefs(dataDom);
</script>
<template>
<div class="workshop">
<div class="loading" ref="loadingDom">
<img src="@/assets/images/workshop/loadingPage.png" alt="">
</div>
<RouterView />
</div>
</template>
<style lang="less" scoped>
.workshop {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
> .loading{
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
transition: all 1s;
background: #fff;
img{
width: 25rem;
}
}
}
@media (min-width: 1024px) {
}
</style>

View File

@@ -0,0 +1,332 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs, computed, watch } from "vue";
import { getMousePosition } from '@/utils/tools'
import { useWorkshopStore } from '@/stores/modules/workshop'
import { gsap, TweenMax } from "gsap";
import {userSave} from '@/api/workshop'
import { useRouter,useRoute } from 'vue-router'
const store = useWorkshopStore()
const router = useRouter()
let data = reactive({
imgEvent:{} as any,
tlRight:gsap.timeline({ paused: true }),
tlLeft:gsap.timeline({ paused: true }),
editList:[
{
id:1,
},
{
id:2,
},
],
selectIndex:0 as any,
maskShow:false,
dressStyle:{},
})
let queryList = computed(()=>store.queryList) as any
let imgDom = ref()
let editDom = ref()
let noLikeDom = ref()
let likeDom = ref()
let maskDom = ref()
const next = (moveSize:any)=>{
editDom.value.style.transition = 'all .3s'
editDom.value.style.opacity = '0'
if(moveSize > 0){
queryList.value[data.selectIndex].count = false
}else{
queryList.value[data.selectIndex].count = true
}
setTimeout(()=>{
data.selectIndex++
editDom.value.style.transition = ''
editDom.value.style.opacity = '1'
data.tlLeft.progress(0)
data.tlRight.progress(0)
},300)
}
const setLikeOrOnLike = (event:any)=>{
imgDom.value.style.transition = ''
data.imgEvent = event
// document.addEventListener('mousemove', sizeMouseMove);
document.addEventListener('touchmove', sizeTouchmove);
// document.addEventListener('mouseup', sizeMouseup);
document.addEventListener('touchend', sizeMouseup);
}
const sizeTouchmove = (event:any)=>{
let e:any = getMousePosition(event,true)
let moveSize = data.imgEvent.screenX - e?.screenX
let domWidth = e?.target.parentNode.parentNode.offsetWidth
if(moveSize > 0){
data.tlLeft.progress(Math.abs(moveSize) / domWidth)
}else{
data.tlRight.progress(Math.abs(moveSize) / domWidth)
}
}
const sizeMouseup = (event:any)=>{
let e:any = getMousePosition(event,true)
let moveSize = data.imgEvent.screenX - e?.screenX
let domWidth = e?.target.parentNode.parentNode.offsetWidth
let moveScale = Math.abs(moveSize) / domWidth
if(moveScale > .4){
next(moveSize)
// emit('setLikeOrOnLike',moveSize > 0?'onLike':'like')
}else{
imgDom.value.style.transition = 'all .3s'
data.tlLeft.progress(0)
data.tlRight.progress(0)
}
// document.removeEventListener('mousemove',this.sizeMouseMove)
document.removeEventListener('touchmove',sizeTouchmove)
// document.removeEventListener('mouseup',this.sizeMouseup)
document.removeEventListener('touchend',sizeMouseup)
}
const openMask = ()=>{
let imgDomStyle = imgDom.value.getBoundingClientRect()
data.dressStyle = {
top:imgDomStyle.top + 'px',
left:imgDomStyle.left + 'px',
width:imgDomStyle.width + 'px',
height:imgDomStyle.height + 'px',
}
maskDom.value.style.opacity = '1'
maskDom.value.style.display = 'block'
document.addEventListener('touchend', touchendUp);
}
const touchendUp = ()=>{
maskDom.value.style.opacity = '0'
setTimeout(()=>{
maskDom.value.style.display = 'none'
}, 300)
document.removeEventListener('touchend',touchendUp)
}
watch(()=>data.selectIndex,(newVal,oldVal)=>{
console.log(newVal)
let value = {
userId:store.userData.id,
questionNum:queryList.value[oldVal].id,
isLike:queryList.value[oldVal].count,
}
userSave(value)
if(newVal == 30){
router.push({
path:'/activities/workshop/success',
})
}
})
const cliSetNext = (num:number)=>{
imgDom.value.style.transition = 'all .3s'
if(num > 0){
data.tlLeft.progress(1)
}else{
data.tlRight.progress(1)
}
setTimeout(()=>{
imgDom.value.style.transition = 'all .0s'
next(num)
},300)
}
onMounted(()=>{
data.tlRight.to(imgDom.value, { rotation: 20, duration: 1 }, 0) // 第 0 秒开始
.to(likeDom.value, { scale:1.3, duration: 1 }, '-=1')
data.tlLeft.to(imgDom.value, { rotation: -20, duration: 1 }, 0) // 第 0 秒开始
.to(noLikeDom.value, { scale:1.3, duration: 1 }, '-=1')
})
onUnmounted(()=>{
})
const { selectIndex, maskShow, dressStyle } = toRefs(data)
</script>
<template>
<div class="workshopEdit">
<div class="header">
Swipe Style
</div>
<div class="user">
<div class="header">
<div class="profile">
<img src="@/assets/images/workshop/editProfile.png" alt="">
</div>
<div class="text">
<div class="info">{{ store.userData.title }}</div>
<div class="info">{{ store.userData.name }}</div>
</div>
</div>
<div class="progress">
<div class="icon">
<img src="@/assets/images/workshop/editHanger.png" alt="">
</div>
<div class="text">{{ selectIndex + 1 }}/{{queryList.length}}</div>
</div>
</div>
<div class="edit" ref="editDom">
<img
ref="imgDom"
@mousedown.stop="setLikeOrOnLike(getMousePosition($event,false))"
@touchstart.passive="setLikeOrOnLike(getMousePosition($event,true))"
:src="queryList?.[selectIndex]?.itemImagePath" alt="">
<div class="btnBox">
<div class="likeOrNoLike">
<img ref="noLikeDom" @click="cliSetNext(1)" src="@/assets/images/workshop/editNoLike.png" alt="">
<img ref="likeDom" @click="cliSetNext(-1)" class="likeDom" src="@/assets/images/workshop/editLike.png" alt="">
</div>
<div class="tryOn" @touchstart.passive="openMask"></div>
</div>
</div>
<div class="mask" ref="maskDom">
<img :style="dressStyle" :src="queryList?.[selectIndex]?.outfitImagePath" alt="">
</div>
</div>
</template>
<style lang="less" scoped>
.workshopEdit {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #fff;
touch-action: none; /* 完全禁用浏览器默认触摸行为 */
> .header{
margin-top: .9rem;
position: relative;
font-weight: 600;
font-size: 2.2rem;
text-align: center;
font-family: poppinsSemiBold;
}
> .user{
margin-top: 1.3rem;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 3rem;
> .header{
display: flex;
align-items: center;
> .profile{
display: flex;
align-items: center;
margin-right: .8rem;
> img{
width: 3rem;
height: 3rem;
object-fit: cover;
border-radius: 50%;
}
}
> .text{
> .info{
font-weight: 500;
font-size: 1.2rem;
}
}
}
> .progress{
border: 1px solid #cbcdf1;
width: 7.2rem;
height: 2.4rem;
background: #f6f7ff;
border-radius: 1rem;
color: #4d52c3;
display: flex;
align-items: center;
justify-content: center;
> .icon{
margin-right: .1rem;
> img{
transform: translateY(.2rem);
width: 1.5rem;
height: 1.5rem;
}
}
> .text{
font-weight: 700;
font-size: 1.3rem;
}
}
}
> .edit{
padding: 0 3rem;
margin-top: 1.7rem;
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 5rem;
> img{
width: 100%;
height: 50%;
flex: 1;
object-fit: contain;
// height: 50rem;
border-radius: 1.2rem;
box-shadow: .7rem .4rem 1rem -0rem rgba(0,0,0,.2);
transform-origin: center bottom;
margin-bottom: 2rem;
}
> .btnBox{
margin-top: 4.7rem;
padding: 0 1.1rem;
width: 100%;
> .likeOrNoLike{
width: 100%;
display: flex;
justify-content: space-between;
> img{
width: 6.6rem;
height: 6.6rem
}
}
> .tryOn{
background:
url('@/assets/images/workshop/editOpenGroup.png')
center/contain /* 位置/尺寸 */
no-repeat /* 不重复 */
fixed; /* 背景色 */
width: 7.4rem;
height: 7.4rem;
position: absolute;
left: 50%;
transform: translate(-50%,-50%);
top: 0;
}
}
}
> .mask{
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background: rgba(255,255,255,.5);
transition: all .3s;
opacity: 0;
display: none;
img{
position: fixed;
border-radius: 1.2rem;
box-shadow: .7rem .4rem 1rem -0rem rgba(0,0,0,.2);
object-fit: contain;
background-color: #fff;
}
}
}
@media (min-width: 1024px) {
}
</style>

View File

@@ -0,0 +1,178 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { useRouter,useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useWorkshopStore } from '@/stores/modules/workshop'
import {userCreate} from '@/api/workshop'
const route = useRoute()
const router = useRouter()
const store = useWorkshopStore()
const props = defineProps({
})
const emit = defineEmits([
]);
let data = reactive({
describe:'',
title:'',
name:'',
})
const goEditWorkshop = ()=>{
if(!data.title){
return ElMessage({
message: 'Please enter the title.',
type: 'primary' as any,
})
}
if(!data.name){
return ElMessage({
message: 'Please enter your name.',
type: 'primary' as any,
})
}
let value = {
title:data.title,
name:data.name,
description:data.describe,
}
userCreate(value).then((rv:any)=>{
store.setLogin({title:data.title,name:data.name,describe:data.describe,id:rv.id})
router.push('/activities/workshop/edit')
})
}
const textareaChange = (e:any)=>{
if(e.target.value.length <= 1000){
data.describe = e.target.value
}else{
e.target.value = data.describe
}
}
onMounted(()=>{
console.log(useWorkshopStore())
})
onUnmounted(()=>{
})
const { describe, title, name } = toRefs(data);
</script>
<template>
<div class="workshopLogin">
<div class="title">
<p>Swipe Style</p>
<img src="@/assets/images/workshop/loginTitleImg.png" alt="">
</div>
<div class="userInput">
<label>
<span>Job Title</span>
<input type="text" v-model="title" placeholder="Your job title">
</label>
<label>
<span>Name</span>
<input type="text" v-model="name" placeholder="Your name">
</label>
<label>
<span>Describe your favourite style in one sentence...</span>
<div class="textarea">
<textarea placeholder="Describe..." @input="textareaChange"></textarea>
</div>
<div class="textareaNum">{{ describe.length }}/300</div>
</label>
</div>
<div class="enter" @click="goEditWorkshop">Enter</div>
</div>
</template>
<style lang="less" scoped>
.workshopLogin {
.title{
padding-top: 3.3rem;
margin: 0 auto;
margin-top: 4rem;
color: #4D52C3;
position: relative;
margin-bottom: 2rem;
width: 31.3rem;
> p{
text-align: center;
font-weight: 700;
font-size: 3rem;
font-family: poppinsBold;
}
> img{
width: 3.5rem;
height: 3.5rem;
position: absolute;
right: 0;
top: 0;
margin-left: 11rem;
}
}
> .userInput{
width: 31.3rem;
margin: 0 auto;
> label{
display: flex;
flex-direction: column;
margin-bottom: .8rem;
> span{
font-size: 1.2rem;
color: #626262;
margin-bottom: .8rem;
font-weight: 500;
}
input, textarea{
font-size: 1.2rem;
&:focus{
outline: none; /* 清除默认焦点样式 */
}
}
> input{
border-radius: .87rem;
padding: 1.7rem;
background: #f1f4ff;
border: 1.7px #4d52c3 solid;
}
> .textarea{
background: #f1f4ff;
border: none;
height: 13rem;
border-radius: .87rem;
padding: 1.7rem;
> textarea{
border: none;
width: 100%;
height: 100%;
background: transparent;
}
}
> .textareaNum{
font-size: 1.2rem;
color: #626262;
margin-left: auto;
margin-top: 1rem;
}
}
}
> .enter{
width: 31.3rem;
margin: 0 auto;
font-size: 1.74rem;
color: #fff;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
background: #4d52c3;
border-radius: 1.3rem;
height: 5.2rem;
line-height: 5.2rem;
box-shadow: 0px .8rem 1.7rem 0px #cbd6ff;
cursor: pointer;
font-family: poppinsSemiBold;
}
}
@media (min-width: 1024px) {
}
</style>

View File

@@ -0,0 +1,186 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs, computed } from "vue";
import { useRouter,useRoute } from 'vue-router'
import SummaryReport from '../components/summaryReport/index.vue'
import { useWorkshopStore } from '@/stores/modules/workshop'
import {getCalculateReport} from '@/api/workshop'
const router = useRouter()
const props = defineProps({
})
const emit = defineEmits({
})
const store = useWorkshopStore()
let data = reactive({
loadingSuccess:false,
isSummaryReport:false,
calculateReport:null as any,
count:0,
})
let queryList = computed(()=>store.queryList) as any
const goSummaryReport = ()=>{
// data.loadingSuccess = true
if(!data.loadingSuccess)return
data.isSummaryReport = true
}
let time = null as any;
const getCalculate = ()=>{
getCalculateReport().then((rv:any)=>{
data.calculateReport = rv
if(rv.count < 3){
data.loadingSuccess = false
}else{
data.loadingSuccess = true
}
if(rv.answeringCount > 0){
time = setTimeout(()=>{
getCalculate()
},1000)
}
})
}
onMounted(()=>{
getCalculate()
})
onUnmounted(()=>{
clearTimeout(time)
})
const { loadingSuccess, isSummaryReport, calculateReport } = toRefs(data);
</script>
<template>
<div class="workshopSuccess">
<div class="loadingSuccess" v-show="!isSummaryReport">
<div class="congratulations">
<div class="icon">🎉</div>
<div class="text">
<div class="title">Congratulations!</div>
<div class="info">You have completed all your selections!</div>
<div class="describe">Your Result: Like: <span>{{ queryList.filter((item:any)=>item?.count).length }}</span> sets, Dislike: <span>{{ queryList.filter((item:any)=>!item.count).length }}</span> sets.</div>
</div>
</div>
<div class="botderBottom"></div>
<div class="statusBox">
<div class="title" @click="goSummaryReport">Check Report</div>
<div class="icon">
<img src="@/assets/images/workshop/successGoTo.png" alt="">
</div>
<div class="status" @click="goSummaryReport">
<img v-show="!loadingSuccess" src="@/assets/images/workshop/loading.png" alt="">
<img v-show="loadingSuccess" src="@/assets/images/workshop/success.png" alt="">
</div>
<div class="info" v-show="!loadingSuccess">Current progress: {{calculateReport?.count}}/{{ calculateReport?.count + calculateReport?.answeringCount }} people</div>
<div class="info" v-show="loadingSuccess">Done!</div>
<!-- <div class="goSummaryReport" @click="goSummaryReport" v-show="loadingSuccess">
Load More >
</div> -->
</div>
</div>
<div class="summaryReport" v-show="isSummaryReport">
<SummaryReport v-model:isSummaryReport="isSummaryReport" :calculateReport="calculateReport" ref="summaryReport"></SummaryReport>
</div>
</div>
</template>
<style lang="less" scoped>
.workshopSuccess{
width: 100%;
height: 100%;
position: relative;
> .loadingSuccess{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
> .congratulations{
margin: 0 auto;
// margin-top: 11.7rem;
width: 27rem;
margin-bottom: 4rem;
> img{
margin: 0 auto;
width: 15.4rem;
height: 15.4rem;
}
> div{
text-align: center;
}
> .icon{
font-size: 12.8rem;
display: flex;
justify-content: center;
}
> .text{
> .title{
font-size: 3rem;
font-weight: 700;
color: #4d52c3;
font-family: poppinsBold;
}
> .info{
margin: 1.2rem 1.8rem;
font-weight: 500;
font-size: 1.6rem;
}
> .describe{
font-weight: 500;
font-size: 1.2rem;
> span{
color: #4D52C3;
}
}
}
}
> .botderBottom{
width: 31rem;
border-bottom: 1px solid #d9d9d9;
margin: 0 auto;
}
> .statusBox{
margin-top: 1rem;
display: flex;
flex-direction: column;
align-items: center;
> .title{
font-size: 1.6rem;
font-weight: 600;
color: #4d52c3;
font-family: poppinsBold;
}
> .status{
margin-top: 1rem;
> img{
width: 9rem;
height: 9rem
}
}
> .info{
color: #cdcdcd;
font-size: 1.2rem;
font-weight: 500;
}
> .goSummaryReport{
margin-top: 2rem;
cursor: pointer;
font-size: 1.4rem;
font-weight: 600;
color: #4d52c3;
border-radius: 3.4rem;
line-height: 3rem;
width: 11rem;
text-align: center;
border: .6px solid #e3e3e3;
box-shadow: 0 .4rem 1.5rem -.5rem rgba(0,0,0,.2);
}
}
}
> .summaryReport{
position: absolute;
background: #fff;
width: 100%;
height: 100%;
}
}
</style>

View File

@@ -0,0 +1,111 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { getMousePosition } from '@/utils/tools'
import { gsap, TweenMax } from "gsap";
const props = defineProps({
item: {
type: Object,
default: () => ({})
},
})
const emit = defineEmits([
'result',
])
let data = reactive({
tlRight:gsap.timeline({ paused: true }),
tlLeft:gsap.timeline({ paused: true }),
imgEvent:{} as any,
})
let imgDom = ref()
const submit = (index:number)=>{
emit('result',index)
}
onMounted(()=>{
})
onUnmounted(()=>{
})
const {} = toRefs(data);
defineExpose({
})
</script>
<template>
<div class="judge">
<div class="edit">
<div class="title">Whose style do you think this is?</div>
<img
ref="imgDom"
:src="item.itemImagePath" alt="">
<div class="btnBox">
<div class="select">
<div class="item" @click="submit(1)">123</div>
<div class="item" @click="submit(2)">123</div>
<div class="item" @click="submit(3)">123</div>
<div class="item" @click="submit(4)">123</div>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.judge{
width: 100%;
height: 100%;
position: relative;
display: flex;
> .edit{
padding: 0 3rem;
padding-bottom: 1.6rem;
margin-top: 1.7rem;
flex: 1;
display: flex;
overflow: hidden;
justify-content: center;
align-items: center;
flex-direction: column;
padding-bottom: 7rem;
> .title{
font-weight: 500;
font-size: 2rem;
text-align: center;
margin-bottom: .8rem;
}
> img{
flex: 1;
overflow: hidden;
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 1.2rem;
box-shadow: .7rem .4rem 1rem -0rem rgba(0,0,0,.2);
transform-origin: center bottom;
}
> .btnBox{
// margin-top: 1.6rem;
margin-top: 1.6rem;
width: 100%;
> .select{
width: 100%;
display: flex;
gap: 1.2rem;
> .item{
text-align: center;
flex: 1;
padding: 0 1.8rem;
line-height: 2.4rem;
color: #4D52C3;
font-weight: 500;
font-size: 1.6rem;
background-color: #fff;
border: 2px solid #dadada;
border-radius: 1rem;
box-shadow: 0px .4rem .4rem 0px rgba(0,0,0,0.25);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,160 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { getMousePosition } from '@/utils/tools'
import { gsap, TweenMax } from "gsap";
const props = defineProps({
item: {
type: Object,
default: () => ({})
},
})
const emit = defineEmits([
'result',
])
let data = reactive({
tlRight:gsap.timeline({ paused: true }),
tlLeft:gsap.timeline({ paused: true }),
imgEvent:{} as any,
})
let imgDom = ref()
let noLikeDom = ref()
let likeDom = ref()
const setLikeOrOnLike = (event:any)=>{
imgDom.value.style.transition = ''
data.imgEvent = event
// document.addEventListener('mousemove', sizeMouseMove);
document.addEventListener('touchmove', sizeTouchmove);
// document.addEventListener('mouseup', sizeMouseup);
document.addEventListener('touchend', sizeMouseup);
}
const sizeTouchmove = (event:any)=>{
let e:any = getMousePosition(event,true)
let moveSize = data.imgEvent.screenX - e?.screenX
let domWidth = e?.target.parentNode.parentNode.offsetWidth
if(moveSize > 0){
data.tlLeft.progress(Math.abs(moveSize) / domWidth)
}else{
data.tlRight.progress(Math.abs(moveSize) / domWidth)
}
}
const sizeMouseup = (event:any)=>{
let e:any = getMousePosition(event,true)
let moveSize = data.imgEvent.screenX - e?.screenX
let domWidth = e?.target.parentNode.parentNode.offsetWidth
let moveScale = Math.abs(moveSize) / domWidth
if(moveScale > .4){
emit('result',moveSize)
}else{
imgDom.value.style.transition = 'all .3s'
data.tlLeft.progress(0)
data.tlRight.progress(0)
}
// document.removeEventListener('mousemove',this.sizeMouseMove)
document.removeEventListener('touchmove',sizeTouchmove)
// document.removeEventListener('mouseup',this.sizeMouseup)
document.removeEventListener('touchend',sizeMouseup)
}
const reset = ()=>{
data.tlLeft.progress(0)
data.tlRight.progress(0)
}
const cliSetNext = (num:number)=>{
imgDom.value.style.transition = 'all .3s'
if(num > 0){
data.tlLeft.progress(1)
}else{
data.tlRight.progress(1)
}
setTimeout(()=>{
imgDom.value.style.transition = 'all .0s'
emit('result',num)
},300)
}
onMounted(()=>{
data.tlRight.to(imgDom.value, { rotation: 20, duration: 1 }, 0) // 第 0 秒开始
.to(likeDom.value, { scale:1.3, duration: 1 }, '-=1')
data.tlLeft.to(imgDom.value, { rotation: -20, duration: 1 }, 0) // 第 0 秒开始
.to(noLikeDom.value, { scale:1.3, duration: 1 }, '-=1')
})
onUnmounted(()=>{
})
const {} = toRefs(data);
defineExpose({
reset,
})
</script>
<template>
<div class="judge">
<div class="edit">
<div class="title">123123123</div>
<img
ref="imgDom"
@mousedown.stop="setLikeOrOnLike(getMousePosition($event,false))"
@touchstart.passive="setLikeOrOnLike(getMousePosition($event,true))"
:src="item.itemImagePath" alt="">
<div class="btnBox">
<div class="likeOrNoLike">
<img ref="noLikeDom" @click="cliSetNext(1)" src="@/assets/images/workshop/workshop2No.png" alt="">
<img ref="likeDom" @click="cliSetNext(-1)" class="likeDom" src="@/assets/images/workshop/workshop2Yes.png" alt="">
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.judge{
width: 100%;
height: 100%;
position: relative;
display: flex;
> .edit{
padding: 0 3rem 3rem 3rem;
padding-bottom: 1.6rem;
margin-top: 1.7rem;
flex: 1;
display: flex;
overflow: hidden;
justify-content: center;
align-items: center;
flex-direction: column;
padding-bottom: 3.5rem;
> .title{
font-weight: 500;
font-size: 2rem;
text-align: center;
margin-bottom: .8rem;
}
> img{
flex: 1;
overflow: hidden;
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 1.2rem;
box-shadow: .7rem .4rem 1rem -0rem rgba(0,0,0,.2);
transform-origin: center bottom;
}
> .btnBox{
// margin-top: 1.6rem;
padding: 0 1.8rem;
margin-top: 1.6rem;
width: 100%;
> .likeOrNoLike{
width: 100%;
display: flex;
justify-content: space-between;
> img{
width: 6.6rem;
height: 6.6rem
}
}
}
}
}
</style>

View File

@@ -0,0 +1,113 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
// const props = defineProps({
// })
const emit = defineEmits([
'next'
])
let data = reactive({
})
const next = ()=>{
emit('next')
}
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
const {} = toRefs(data);
</script>
<template>
<div class="result">
<div class="icon">😄</div>
<div class="title">Correct! You got it!</div>
<div class="keyPoint">
<div class="aiBot">
<img src="@/assets/images/workshop/workshop2Bot.png" alt="">
<div>AI Bot:</div>
</div>
<div class="text">1312312312312312323123123123</div>
</div>
<div class="next" @click="next">
<div>Next</div>
<img src="@/assets/images/workshop/workshopRight.png" alt="">
</div>
</div>
</template>
<style lang="less" scoped>
.result{
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
overflow-y: auto;
> div{
flex-shrink: 0;
}
> .icon{
font-size: 14.3rem;
font-weight: 400;
text-align: center;
margin-bottom: .6rem;
}
> .title{
font-size: 2.8rem;
font-family: poppinsBold;
font-weight: 700;
text-align: center;
margin-bottom: 2rem;
color: #4D52C3;
}
> .keyPoint{
font-size: 1.6rem;
font-weight: 500;
text-align: center;
margin-bottom: 2rem;
width: calc(100% - 30px * 2);
margin: 0 auto;
height: 21.5rem;
padding: 1.8rem 1.6rem 0;
border: 2px solid #b7b9e9;
border-radius: 2rem;
box-shadow: 0px .7rem .4rem 0px rgba(0, 0, 0, 0.15);
margin-bottom: 10rem;
display: flex;
flex-direction: column;
text-align: left;
> .aiBot{
margin-bottom: .6rem;
font-size: 1.6rem;
font-weight: 500;
display: flex;
align-items: center;
> img{
margin-right: .4rem;
}
}
> .text{
font-size: 1.2rem;
font-weight: 400;
font-family: poppinsRegular;
}
}
> .next{
margin: 0 3rem 0 auto;
border: 1px solid #4d52c3;
height: 3.6rem;
display: flex;
font-size: 1.6rem;
border-radius: 1rem;
padding: 0 1.3rem 0 1.6rem;
color: #4D52C3;
align-items: center;
margin-bottom: 2rem;
> img{
margin-left: .6rem;
width: .5rem;
height: 1rem;
}
}
}
</style>

View File

@@ -0,0 +1,274 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import {getCalculateReport} from '@/api/workshop'
const props = defineProps({
isSummaryReport:{
type:Boolean,
default:false,
},
calculateReport:{
type:Object,
default:()=>{},
},
})
const emit = defineEmits([
'update:isSummaryReport'
])
let data = reactive({
})
const goBack = ()=>{
emit('update:isSummaryReport',false)
}
onMounted(()=>{
})
onUnmounted(()=>{
})
const {} = toRefs(data);
</script>
<template>
<div class="summaryReport">
<img class="bg" src="@/assets/images/workshop/workshop2SuccessBg.png" alt="">
<div class="header">
Swipe Style
<img @click="goBack" src="@/assets/images/workshop/workshop2Back.png" alt="">
</div>
<div class="body">
<div class="topThree">
<div class="item item1">
<div class="head">
<img class="one" src="@/assets/images/workshop/workshop2TopicOne.png" alt="">
<img class="profile" src="@/assets/images/workshop/editProfile.png" alt="">
</div>
<div class="name">Name</div>
<div class="points">
99
points
</div>
</div>
<div class="item item2">
<div class="head">
<img class="profile" src="@/assets/images/workshop/editProfile.png" alt="">
</div>
<div class="name">Name</div>
<div class="points">
99
points
</div>
</div>
<div class="item item3">
<div class="head">
<img class="profile" src="@/assets/images/workshop/editProfile.png" alt="">
</div>
<div class="name">Name</div>
<div class="points">
99
points
</div>
</div>
<div class="topThreeBg">
<img src="@/assets/images/workshop/workshopTopThree.png" alt="">
</div>
</div>
</div>
<div class="tabulate">
<div class="bgImg">
<img src="@/assets/images/workshop/workshop2TopicList.png" alt="">
<div class="placeholder"></div>
</div>
<div class="content">
<div class="roll">
<div class="item">
<div class="serialNum">1</div>
<div class="profile">
<img src="@/assets/images/workshop/editProfile.png" alt="">
</div>
<div class="user">
<div class="name">Name</div>
<div class="points">90 points</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.summaryReport{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
> .bg{
position: absolute;
width: 100%;
height: auto;
object-fit: contain;
top: 0;
right: 0;
background-color: #4d52c3;
}
> .header{
padding: .9rem 0;
position: relative;
font-weight: 600;
font-size: 2.4rem;
text-align: center;
color: #fff;
font-family: poppinsBold;
margin-bottom: 1.2rem;
> img{
position: absolute;
left: 3rem;
width: 1.7rem;
height: 1.7rem;
top: 50%;
transform: translateY(-50%);
}
}
> .body{
flex: 1;
> .topThree{
position: relative;
width: 31.8rem;
margin: 0 auto;
> .topThreeBg{
width: 31.8rem;
padding-top: 17rem;
height: auto;
> img{
width: 100%;
height: auto;
}
}
> .item{
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
> .head{
position: relative;
> .one{
width: 4rem;
height: 4rem;
position: absolute;
z-index: 2;
left: 50%;
transform: translateX(-50%);
top: 0;
}
> .profile{
margin-top: 3rem;
width: 5.6rem;
height: 5.6rem;
}
}
> .name{
margin-bottom: .4rem;
color: #fff;
font-size: 1.6rem;
font-weight: 500;
}
> .points{
border-radius: 1.2rem;
font-size: 1.2rem;
font-weight: 500;
padding: 0 1.2rem;
line-height: 3.4rem;
background-color: #cbcdf1;
color: #4D52C3;
}
}
> .item1{
left: 50%;
transform: translateX(-50%);
top: 0;
}
> .item2{
left: 1.9rem;
top: calc(6.2rem - 3rem);
}
> .item3{
top: calc(9.4rem - 3rem);
left: auto;
right: 1.9rem;
}
}
}
> .tabulate{
margin: 0 .8rem;
margin-top: -12rem;
flex: 1;
overflow: hidden;
> .bgImg{
width: 100%;
position: absolute;
margin-top: auto;
display: flex;
flex-direction: column;
> img{
width: 100%;
height: auto;
}
> .placeholder{
margin-top: -1rem;
width: 100%;
height: 13rem;
background-color: #efeefc;
}
}
> .content{
padding: 3.2rem 1.6rem 0 1.6rem;
height: 100%;
// height: calc(100% + 12rem);
> .roll{
overflow-y: auto;
height: 100%;
> .item{
margin-bottom: 1.6rem;
background-color: #ffffff;
border-radius: 2rem;
display: flex;
align-items: center;
height: 9.2rem;
> .serialNum{
font-size: 1.6rem;
font-weight: 350;
color: #858494;
margin-left: 2.3rem;
}
> .profile{
margin-left: 2.3rem;
width: 5.6rem;
height: 5.6rem;
> img{
width: 100%;
height: 100%;
}
}
> .user{
margin-left: 1.6rem;
> .name{
font-size: 1.6rem;
font-weight: 500;
color: #0c092a;
margin-bottom: .4rem;
}
> .points{
font-size: 1.4rem;
font-weight: 400;
color: #858494;
}
}
&:last-child{
margin-bottom: 0;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import {getQueryPage} from '@/api/workshop'
import { useWorkshopStore } from '@/stores/modules/workshop'
const store = useWorkshopStore()
let dataDom = reactive({
loadingDom:null as any,
})
let data = reactive({
})
onMounted(()=>{
// getQueryPage({page:1,size:200}).then((rv:any)=>{
// store.setQueryList(rv.content)
// })
getQueryPage({page:1,size:200}).then((rv:any)=>{
store.setQueryList(rv.content)
})
setTimeout(()=>{
dataDom.loadingDom.style.opacity = '0';
setTimeout(()=>{
dataDom.loadingDom.style.display = 'none';
},1000)
},3000)
})
onUnmounted(()=>{
})
const { loadingDom } = toRefs(dataDom);
</script>
<template>
<div class="workshop">
<div class="loading" ref="loadingDom">
<img src="@/assets/images/workshop/loadingPage.png" alt="">
</div>
<RouterView />
</div>
</template>
<style lang="less" scoped>
.workshop {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
> .loading{
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
transition: all 1s;
background: #fff;
img{
width: 25rem;
}
}
}
@media (min-width: 1024px) {
}
</style>

Some files were not shown because too many files have changed in this diff Show More