hookehuyr

feat: 初始化项目结构并添加核心功能

- 初始化项目结构,包括Vue 3、Pinia、Vue Router、TailwindCSS等依赖
- 添加首页、活动详情页、创建活动页等核心页面
- 实现活动分类、搜索、报名等基本功能
- 添加公共组件如按钮、输入框、模态框等
- 配置Vite、TailwindCSS、PostCSS等构建工具
Showing 53 changed files with 13111 additions and 0 deletions
1 +# Logs
2 +logs
3 +*.log
4 +npm-debug.log*
5 +yarn-debug.log*
6 +yarn-error.log*
7 +pnpm-debug.log*
8 +lerna-debug.log*
9 +
10 +node_modules
11 +dist
12 +dist-ssr
13 +*.local
14 +
15 +# Editor directories and files
16 +.vscode/*
17 +!.vscode/extensions.json
18 +.idea
19 +.DS_Store
20 +*.suo
21 +*.ntvs*
22 +*.njsproj
23 +*.sln
24 +*.sw?
25 +
26 +.history
27 +node_modules
28 +.vscode
29 +mlaj-read
1 +# React + Vite Template
2 +
3 +A modern React template for web applications and games, featuring React 18, Vite, TailwindCSS, and Material UI.
4 +
5 +## Project Structure
6 +
7 +```
8 +├── src/
9 +│ ├── App.jsx # Main application component
10 +│ ├── main.jsx # Application entry point
11 +│ └── index.css # Global styles (Tailwind)
12 +├── public/ # Static assets
13 +├── index.html # HTML template
14 +├── vite.config.js # Vite configuration
15 +├── tailwind.config.js # Tailwind configuration
16 +├── postcss.config.js # PostCSS configuration
17 +└── eslint.config.js # ESLint configuration
18 +```
19 +
20 +## Development Guidelines
21 +
22 +- Modify `index.html` and `src/App.jsx` as needed
23 +- Create new folders or files in `src/` directory as needed
24 +- Style components using TailwindCSS utility classes
25 +- Avoid modifying `src/main.jsx` and `src/index.css`
26 +- Only modify `vite.config.js` if absolutely necessary
27 +
28 +## Available Scripts
29 +- `pnpm install` - Install dependencies
30 +- `pnpm run dev` - Start development server
31 +- `pnpm run lint` - Lint source files
32 +
33 +## Tech Stack
34 +
35 +- React
36 +- Vite
37 +- TailwindCSS
38 +- ESLint
39 +- Javascript
......
1 +<!--
2 + * @Date: 2025-04-17 08:14:25
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 13:26:33
5 + * @FilePath: /reading-club-app/index.html
6 + * @Description: 文件描述
7 +-->
8 +<!doctype html>
9 +<html lang="en">
10 +
11 +<head>
12 + <meta charset="UTF-8" />
13 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
14 + <title>读书会 - 连接爱读书的人</title>
15 +</head>
16 +
17 +<body>
18 + <div id="root"></div>
19 + <script type="module" src="/src/main.js"></script>
20 +</body>
21 +
22 +</html>
1 +{
2 + "name": "vue-reading-club",
3 + "private": true,
4 + "version": "0.0.0",
5 + "type": "module",
6 + "scripts": {
7 + "dev": "vite",
8 + "build": "vite build",
9 + "lint": "eslint ./src --quiet",
10 + "preview": "vite preview"
11 + },
12 + "dependencies": {
13 + "@vitejs/plugin-vue-jsx": "4.1.2",
14 + "pinia": "^2.1.7",
15 + "vue": "^3.4.21",
16 + "vue-router": "^4.3.0"
17 + },
18 + "devDependencies": {
19 + "@vitejs/plugin-vue": "^5.0.4",
20 + "@vue/compiler-sfc": "^3.4.21",
21 + "autoprefixer": "^10.4.20",
22 + "eslint": "^9.9.0",
23 + "eslint-plugin-vue": "^9.24.0",
24 + "postcss": "^8.4.45",
25 + "tailwindcss": "^3.4.10",
26 + "vite": "^5.4.1"
27 + }
28 +}
1 +lockfileVersion: '9.0'
2 +
3 +settings:
4 + autoInstallPeers: true
5 + excludeLinksFromLockfile: false
6 +
7 +importers:
8 +
9 + .:
10 + dependencies:
11 + '@vitejs/plugin-vue-jsx':
12 + specifier: 4.1.2
13 + version: 4.1.2(vite@5.4.18)(vue@3.5.13)
14 + pinia:
15 + specifier: ^2.1.7
16 + version: 2.3.1(vue@3.5.13)
17 + vue:
18 + specifier: ^3.4.21
19 + version: 3.5.13
20 + vue-router:
21 + specifier: ^4.3.0
22 + version: 4.5.0(vue@3.5.13)
23 + devDependencies:
24 + '@vitejs/plugin-vue':
25 + specifier: ^5.0.4
26 + version: 5.2.3(vite@5.4.18)(vue@3.5.13)
27 + '@vue/compiler-sfc':
28 + specifier: ^3.4.21
29 + version: 3.5.13
30 + autoprefixer:
31 + specifier: ^10.4.20
32 + version: 10.4.21(postcss@8.5.3)
33 + eslint:
34 + specifier: ^9.9.0
35 + version: 9.24.0(jiti@1.21.7)
36 + eslint-plugin-vue:
37 + specifier: ^9.24.0
38 + version: 9.33.0(eslint@9.24.0(jiti@1.21.7))
39 + postcss:
40 + specifier: ^8.4.45
41 + version: 8.5.3
42 + tailwindcss:
43 + specifier: ^3.4.10
44 + version: 3.4.17
45 + vite:
46 + specifier: ^5.4.1
47 + version: 5.4.18
48 +
49 +packages:
50 +
51 + '@alloc/quick-lru@5.2.0':
52 + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
53 + engines: {node: '>=10'}
54 +
55 + '@ampproject/remapping@2.3.0':
56 + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
57 + engines: {node: '>=6.0.0'}
58 +
59 + '@babel/code-frame@7.26.2':
60 + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
61 + engines: {node: '>=6.9.0'}
62 +
63 + '@babel/compat-data@7.26.8':
64 + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
65 + engines: {node: '>=6.9.0'}
66 +
67 + '@babel/core@7.26.10':
68 + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==}
69 + engines: {node: '>=6.9.0'}
70 +
71 + '@babel/generator@7.27.0':
72 + resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
73 + engines: {node: '>=6.9.0'}
74 +
75 + '@babel/helper-annotate-as-pure@7.25.9':
76 + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
77 + engines: {node: '>=6.9.0'}
78 +
79 + '@babel/helper-compilation-targets@7.27.0':
80 + resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
81 + engines: {node: '>=6.9.0'}
82 +
83 + '@babel/helper-create-class-features-plugin@7.27.0':
84 + resolution: {integrity: sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==}
85 + engines: {node: '>=6.9.0'}
86 + peerDependencies:
87 + '@babel/core': ^7.0.0
88 +
89 + '@babel/helper-member-expression-to-functions@7.25.9':
90 + resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==}
91 + engines: {node: '>=6.9.0'}
92 +
93 + '@babel/helper-module-imports@7.25.9':
94 + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
95 + engines: {node: '>=6.9.0'}
96 +
97 + '@babel/helper-module-transforms@7.26.0':
98 + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
99 + engines: {node: '>=6.9.0'}
100 + peerDependencies:
101 + '@babel/core': ^7.0.0
102 +
103 + '@babel/helper-optimise-call-expression@7.25.9':
104 + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==}
105 + engines: {node: '>=6.9.0'}
106 +
107 + '@babel/helper-plugin-utils@7.26.5':
108 + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
109 + engines: {node: '>=6.9.0'}
110 +
111 + '@babel/helper-replace-supers@7.26.5':
112 + resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==}
113 + engines: {node: '>=6.9.0'}
114 + peerDependencies:
115 + '@babel/core': ^7.0.0
116 +
117 + '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
118 + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==}
119 + engines: {node: '>=6.9.0'}
120 +
121 + '@babel/helper-string-parser@7.25.9':
122 + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
123 + engines: {node: '>=6.9.0'}
124 +
125 + '@babel/helper-validator-identifier@7.25.9':
126 + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
127 + engines: {node: '>=6.9.0'}
128 +
129 + '@babel/helper-validator-option@7.25.9':
130 + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
131 + engines: {node: '>=6.9.0'}
132 +
133 + '@babel/helpers@7.27.0':
134 + resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
135 + engines: {node: '>=6.9.0'}
136 +
137 + '@babel/parser@7.27.0':
138 + resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
139 + engines: {node: '>=6.0.0'}
140 + hasBin: true
141 +
142 + '@babel/plugin-syntax-jsx@7.25.9':
143 + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==}
144 + engines: {node: '>=6.9.0'}
145 + peerDependencies:
146 + '@babel/core': ^7.0.0-0
147 +
148 + '@babel/plugin-syntax-typescript@7.25.9':
149 + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
150 + engines: {node: '>=6.9.0'}
151 + peerDependencies:
152 + '@babel/core': ^7.0.0-0
153 +
154 + '@babel/plugin-transform-typescript@7.27.0':
155 + resolution: {integrity: sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==}
156 + engines: {node: '>=6.9.0'}
157 + peerDependencies:
158 + '@babel/core': ^7.0.0-0
159 +
160 + '@babel/template@7.27.0':
161 + resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
162 + engines: {node: '>=6.9.0'}
163 +
164 + '@babel/traverse@7.27.0':
165 + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
166 + engines: {node: '>=6.9.0'}
167 +
168 + '@babel/types@7.27.0':
169 + resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
170 + engines: {node: '>=6.9.0'}
171 +
172 + '@esbuild/aix-ppc64@0.21.5':
173 + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
174 + engines: {node: '>=12'}
175 + cpu: [ppc64]
176 + os: [aix]
177 +
178 + '@esbuild/android-arm64@0.21.5':
179 + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
180 + engines: {node: '>=12'}
181 + cpu: [arm64]
182 + os: [android]
183 +
184 + '@esbuild/android-arm@0.21.5':
185 + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
186 + engines: {node: '>=12'}
187 + cpu: [arm]
188 + os: [android]
189 +
190 + '@esbuild/android-x64@0.21.5':
191 + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
192 + engines: {node: '>=12'}
193 + cpu: [x64]
194 + os: [android]
195 +
196 + '@esbuild/darwin-arm64@0.21.5':
197 + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
198 + engines: {node: '>=12'}
199 + cpu: [arm64]
200 + os: [darwin]
201 +
202 + '@esbuild/darwin-x64@0.21.5':
203 + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
204 + engines: {node: '>=12'}
205 + cpu: [x64]
206 + os: [darwin]
207 +
208 + '@esbuild/freebsd-arm64@0.21.5':
209 + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
210 + engines: {node: '>=12'}
211 + cpu: [arm64]
212 + os: [freebsd]
213 +
214 + '@esbuild/freebsd-x64@0.21.5':
215 + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
216 + engines: {node: '>=12'}
217 + cpu: [x64]
218 + os: [freebsd]
219 +
220 + '@esbuild/linux-arm64@0.21.5':
221 + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
222 + engines: {node: '>=12'}
223 + cpu: [arm64]
224 + os: [linux]
225 +
226 + '@esbuild/linux-arm@0.21.5':
227 + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
228 + engines: {node: '>=12'}
229 + cpu: [arm]
230 + os: [linux]
231 +
232 + '@esbuild/linux-ia32@0.21.5':
233 + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
234 + engines: {node: '>=12'}
235 + cpu: [ia32]
236 + os: [linux]
237 +
238 + '@esbuild/linux-loong64@0.21.5':
239 + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
240 + engines: {node: '>=12'}
241 + cpu: [loong64]
242 + os: [linux]
243 +
244 + '@esbuild/linux-mips64el@0.21.5':
245 + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
246 + engines: {node: '>=12'}
247 + cpu: [mips64el]
248 + os: [linux]
249 +
250 + '@esbuild/linux-ppc64@0.21.5':
251 + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
252 + engines: {node: '>=12'}
253 + cpu: [ppc64]
254 + os: [linux]
255 +
256 + '@esbuild/linux-riscv64@0.21.5':
257 + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
258 + engines: {node: '>=12'}
259 + cpu: [riscv64]
260 + os: [linux]
261 +
262 + '@esbuild/linux-s390x@0.21.5':
263 + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
264 + engines: {node: '>=12'}
265 + cpu: [s390x]
266 + os: [linux]
267 +
268 + '@esbuild/linux-x64@0.21.5':
269 + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
270 + engines: {node: '>=12'}
271 + cpu: [x64]
272 + os: [linux]
273 +
274 + '@esbuild/netbsd-x64@0.21.5':
275 + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
276 + engines: {node: '>=12'}
277 + cpu: [x64]
278 + os: [netbsd]
279 +
280 + '@esbuild/openbsd-x64@0.21.5':
281 + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
282 + engines: {node: '>=12'}
283 + cpu: [x64]
284 + os: [openbsd]
285 +
286 + '@esbuild/sunos-x64@0.21.5':
287 + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
288 + engines: {node: '>=12'}
289 + cpu: [x64]
290 + os: [sunos]
291 +
292 + '@esbuild/win32-arm64@0.21.5':
293 + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
294 + engines: {node: '>=12'}
295 + cpu: [arm64]
296 + os: [win32]
297 +
298 + '@esbuild/win32-ia32@0.21.5':
299 + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
300 + engines: {node: '>=12'}
301 + cpu: [ia32]
302 + os: [win32]
303 +
304 + '@esbuild/win32-x64@0.21.5':
305 + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
306 + engines: {node: '>=12'}
307 + cpu: [x64]
308 + os: [win32]
309 +
310 + '@eslint-community/eslint-utils@4.6.1':
311 + resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==}
312 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
313 + peerDependencies:
314 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
315 +
316 + '@eslint-community/regexpp@4.12.1':
317 + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
318 + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
319 +
320 + '@eslint/config-array@0.20.0':
321 + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==}
322 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
323 +
324 + '@eslint/config-helpers@0.2.1':
325 + resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
326 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
327 +
328 + '@eslint/core@0.12.0':
329 + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
330 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
331 +
332 + '@eslint/core@0.13.0':
333 + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
334 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
335 +
336 + '@eslint/eslintrc@3.3.1':
337 + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
338 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
339 +
340 + '@eslint/js@9.24.0':
341 + resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==}
342 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
343 +
344 + '@eslint/object-schema@2.1.6':
345 + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
346 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
347 +
348 + '@eslint/plugin-kit@0.2.8':
349 + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
350 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
351 +
352 + '@humanfs/core@0.19.1':
353 + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
354 + engines: {node: '>=18.18.0'}
355 +
356 + '@humanfs/node@0.16.6':
357 + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
358 + engines: {node: '>=18.18.0'}
359 +
360 + '@humanwhocodes/module-importer@1.0.1':
361 + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
362 + engines: {node: '>=12.22'}
363 +
364 + '@humanwhocodes/retry@0.3.1':
365 + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
366 + engines: {node: '>=18.18'}
367 +
368 + '@humanwhocodes/retry@0.4.2':
369 + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
370 + engines: {node: '>=18.18'}
371 +
372 + '@isaacs/cliui@8.0.2':
373 + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
374 + engines: {node: '>=12'}
375 +
376 + '@jridgewell/gen-mapping@0.3.8':
377 + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
378 + engines: {node: '>=6.0.0'}
379 +
380 + '@jridgewell/resolve-uri@3.1.2':
381 + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
382 + engines: {node: '>=6.0.0'}
383 +
384 + '@jridgewell/set-array@1.2.1':
385 + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
386 + engines: {node: '>=6.0.0'}
387 +
388 + '@jridgewell/sourcemap-codec@1.5.0':
389 + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
390 +
391 + '@jridgewell/trace-mapping@0.3.25':
392 + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
393 +
394 + '@nodelib/fs.scandir@2.1.5':
395 + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
396 + engines: {node: '>= 8'}
397 +
398 + '@nodelib/fs.stat@2.0.5':
399 + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
400 + engines: {node: '>= 8'}
401 +
402 + '@nodelib/fs.walk@1.2.8':
403 + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
404 + engines: {node: '>= 8'}
405 +
406 + '@pkgjs/parseargs@0.11.0':
407 + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
408 + engines: {node: '>=14'}
409 +
410 + '@rollup/rollup-android-arm-eabi@4.40.0':
411 + resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==}
412 + cpu: [arm]
413 + os: [android]
414 +
415 + '@rollup/rollup-android-arm64@4.40.0':
416 + resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==}
417 + cpu: [arm64]
418 + os: [android]
419 +
420 + '@rollup/rollup-darwin-arm64@4.40.0':
421 + resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==}
422 + cpu: [arm64]
423 + os: [darwin]
424 +
425 + '@rollup/rollup-darwin-x64@4.40.0':
426 + resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==}
427 + cpu: [x64]
428 + os: [darwin]
429 +
430 + '@rollup/rollup-freebsd-arm64@4.40.0':
431 + resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==}
432 + cpu: [arm64]
433 + os: [freebsd]
434 +
435 + '@rollup/rollup-freebsd-x64@4.40.0':
436 + resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==}
437 + cpu: [x64]
438 + os: [freebsd]
439 +
440 + '@rollup/rollup-linux-arm-gnueabihf@4.40.0':
441 + resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==}
442 + cpu: [arm]
443 + os: [linux]
444 +
445 + '@rollup/rollup-linux-arm-musleabihf@4.40.0':
446 + resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==}
447 + cpu: [arm]
448 + os: [linux]
449 +
450 + '@rollup/rollup-linux-arm64-gnu@4.40.0':
451 + resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==}
452 + cpu: [arm64]
453 + os: [linux]
454 +
455 + '@rollup/rollup-linux-arm64-musl@4.40.0':
456 + resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==}
457 + cpu: [arm64]
458 + os: [linux]
459 +
460 + '@rollup/rollup-linux-loongarch64-gnu@4.40.0':
461 + resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==}
462 + cpu: [loong64]
463 + os: [linux]
464 +
465 + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0':
466 + resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==}
467 + cpu: [ppc64]
468 + os: [linux]
469 +
470 + '@rollup/rollup-linux-riscv64-gnu@4.40.0':
471 + resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==}
472 + cpu: [riscv64]
473 + os: [linux]
474 +
475 + '@rollup/rollup-linux-riscv64-musl@4.40.0':
476 + resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==}
477 + cpu: [riscv64]
478 + os: [linux]
479 +
480 + '@rollup/rollup-linux-s390x-gnu@4.40.0':
481 + resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==}
482 + cpu: [s390x]
483 + os: [linux]
484 +
485 + '@rollup/rollup-linux-x64-gnu@4.40.0':
486 + resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==}
487 + cpu: [x64]
488 + os: [linux]
489 +
490 + '@rollup/rollup-linux-x64-musl@4.40.0':
491 + resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==}
492 + cpu: [x64]
493 + os: [linux]
494 +
495 + '@rollup/rollup-win32-arm64-msvc@4.40.0':
496 + resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==}
497 + cpu: [arm64]
498 + os: [win32]
499 +
500 + '@rollup/rollup-win32-ia32-msvc@4.40.0':
501 + resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==}
502 + cpu: [ia32]
503 + os: [win32]
504 +
505 + '@rollup/rollup-win32-x64-msvc@4.40.0':
506 + resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==}
507 + cpu: [x64]
508 + os: [win32]
509 +
510 + '@types/estree@1.0.7':
511 + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
512 +
513 + '@types/json-schema@7.0.15':
514 + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
515 +
516 + '@vitejs/plugin-vue-jsx@4.1.2':
517 + resolution: {integrity: sha512-4Rk0GdE0QCdsIkuMmWeg11gmM4x8UmTnZR/LWPm7QJ7+BsK4tq08udrN0isrrWqz5heFy9HLV/7bOLgFS8hUjA==}
518 + engines: {node: ^18.0.0 || >=20.0.0}
519 + peerDependencies:
520 + vite: ^5.0.0 || ^6.0.0
521 + vue: ^3.0.0
522 +
523 + '@vitejs/plugin-vue@5.2.3':
524 + resolution: {integrity: sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==}
525 + engines: {node: ^18.0.0 || >=20.0.0}
526 + peerDependencies:
527 + vite: ^5.0.0 || ^6.0.0
528 + vue: ^3.2.25
529 +
530 + '@vue/babel-helper-vue-transform-on@1.4.0':
531 + resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==}
532 +
533 + '@vue/babel-plugin-jsx@1.4.0':
534 + resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==}
535 + peerDependencies:
536 + '@babel/core': ^7.0.0-0
537 + peerDependenciesMeta:
538 + '@babel/core':
539 + optional: true
540 +
541 + '@vue/babel-plugin-resolve-type@1.4.0':
542 + resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==}
543 + peerDependencies:
544 + '@babel/core': ^7.0.0-0
545 +
546 + '@vue/compiler-core@3.5.13':
547 + resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
548 +
549 + '@vue/compiler-dom@3.5.13':
550 + resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
551 +
552 + '@vue/compiler-sfc@3.5.13':
553 + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
554 +
555 + '@vue/compiler-ssr@3.5.13':
556 + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
557 +
558 + '@vue/devtools-api@6.6.4':
559 + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
560 +
561 + '@vue/reactivity@3.5.13':
562 + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
563 +
564 + '@vue/runtime-core@3.5.13':
565 + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
566 +
567 + '@vue/runtime-dom@3.5.13':
568 + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
569 +
570 + '@vue/server-renderer@3.5.13':
571 + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
572 + peerDependencies:
573 + vue: 3.5.13
574 +
575 + '@vue/shared@3.5.13':
576 + resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
577 +
578 + acorn-jsx@5.3.2:
579 + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
580 + peerDependencies:
581 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
582 +
583 + acorn@8.14.1:
584 + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
585 + engines: {node: '>=0.4.0'}
586 + hasBin: true
587 +
588 + ajv@6.12.6:
589 + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
590 +
591 + ansi-regex@5.0.1:
592 + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
593 + engines: {node: '>=8'}
594 +
595 + ansi-regex@6.1.0:
596 + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
597 + engines: {node: '>=12'}
598 +
599 + ansi-styles@4.3.0:
600 + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
601 + engines: {node: '>=8'}
602 +
603 + ansi-styles@6.2.1:
604 + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
605 + engines: {node: '>=12'}
606 +
607 + any-promise@1.3.0:
608 + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
609 +
610 + anymatch@3.1.3:
611 + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
612 + engines: {node: '>= 8'}
613 +
614 + arg@5.0.2:
615 + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
616 +
617 + argparse@2.0.1:
618 + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
619 +
620 + autoprefixer@10.4.21:
621 + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
622 + engines: {node: ^10 || ^12 || >=14}
623 + hasBin: true
624 + peerDependencies:
625 + postcss: ^8.1.0
626 +
627 + balanced-match@1.0.2:
628 + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
629 +
630 + binary-extensions@2.3.0:
631 + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
632 + engines: {node: '>=8'}
633 +
634 + boolbase@1.0.0:
635 + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
636 +
637 + brace-expansion@1.1.11:
638 + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
639 +
640 + brace-expansion@2.0.1:
641 + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
642 +
643 + braces@3.0.3:
644 + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
645 + engines: {node: '>=8'}
646 +
647 + browserslist@4.24.4:
648 + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
649 + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
650 + hasBin: true
651 +
652 + callsites@3.1.0:
653 + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
654 + engines: {node: '>=6'}
655 +
656 + camelcase-css@2.0.1:
657 + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
658 + engines: {node: '>= 6'}
659 +
660 + caniuse-lite@1.0.30001714:
661 + resolution: {integrity: sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==}
662 +
663 + chalk@4.1.2:
664 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
665 + engines: {node: '>=10'}
666 +
667 + chokidar@3.6.0:
668 + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
669 + engines: {node: '>= 8.10.0'}
670 +
671 + color-convert@2.0.1:
672 + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
673 + engines: {node: '>=7.0.0'}
674 +
675 + color-name@1.1.4:
676 + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
677 +
678 + commander@4.1.1:
679 + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
680 + engines: {node: '>= 6'}
681 +
682 + concat-map@0.0.1:
683 + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
684 +
685 + convert-source-map@2.0.0:
686 + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
687 +
688 + cross-spawn@7.0.6:
689 + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
690 + engines: {node: '>= 8'}
691 +
692 + cssesc@3.0.0:
693 + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
694 + engines: {node: '>=4'}
695 + hasBin: true
696 +
697 + csstype@3.1.3:
698 + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
699 +
700 + debug@4.4.0:
701 + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
702 + engines: {node: '>=6.0'}
703 + peerDependencies:
704 + supports-color: '*'
705 + peerDependenciesMeta:
706 + supports-color:
707 + optional: true
708 +
709 + deep-is@0.1.4:
710 + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
711 +
712 + didyoumean@1.2.2:
713 + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
714 +
715 + dlv@1.1.3:
716 + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
717 +
718 + eastasianwidth@0.2.0:
719 + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
720 +
721 + electron-to-chromium@1.5.137:
722 + resolution: {integrity: sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==}
723 +
724 + emoji-regex@8.0.0:
725 + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
726 +
727 + emoji-regex@9.2.2:
728 + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
729 +
730 + entities@4.5.0:
731 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
732 + engines: {node: '>=0.12'}
733 +
734 + esbuild@0.21.5:
735 + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
736 + engines: {node: '>=12'}
737 + hasBin: true
738 +
739 + escalade@3.2.0:
740 + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
741 + engines: {node: '>=6'}
742 +
743 + escape-string-regexp@4.0.0:
744 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
745 + engines: {node: '>=10'}
746 +
747 + eslint-plugin-vue@9.33.0:
748 + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==}
749 + engines: {node: ^14.17.0 || >=16.0.0}
750 + peerDependencies:
751 + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
752 +
753 + eslint-scope@7.2.2:
754 + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
755 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
756 +
757 + eslint-scope@8.3.0:
758 + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
759 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
760 +
761 + eslint-visitor-keys@3.4.3:
762 + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
763 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
764 +
765 + eslint-visitor-keys@4.2.0:
766 + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
767 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
768 +
769 + eslint@9.24.0:
770 + resolution: {integrity: sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==}
771 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
772 + hasBin: true
773 + peerDependencies:
774 + jiti: '*'
775 + peerDependenciesMeta:
776 + jiti:
777 + optional: true
778 +
779 + espree@10.3.0:
780 + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
781 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
782 +
783 + espree@9.6.1:
784 + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
785 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
786 +
787 + esquery@1.6.0:
788 + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
789 + engines: {node: '>=0.10'}
790 +
791 + esrecurse@4.3.0:
792 + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
793 + engines: {node: '>=4.0'}
794 +
795 + estraverse@5.3.0:
796 + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
797 + engines: {node: '>=4.0'}
798 +
799 + estree-walker@2.0.2:
800 + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
801 +
802 + esutils@2.0.3:
803 + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
804 + engines: {node: '>=0.10.0'}
805 +
806 + fast-deep-equal@3.1.3:
807 + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
808 +
809 + fast-glob@3.3.3:
810 + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
811 + engines: {node: '>=8.6.0'}
812 +
813 + fast-json-stable-stringify@2.1.0:
814 + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
815 +
816 + fast-levenshtein@2.0.6:
817 + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
818 +
819 + fastq@1.19.1:
820 + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
821 +
822 + file-entry-cache@8.0.0:
823 + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
824 + engines: {node: '>=16.0.0'}
825 +
826 + fill-range@7.1.1:
827 + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
828 + engines: {node: '>=8'}
829 +
830 + find-up@5.0.0:
831 + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
832 + engines: {node: '>=10'}
833 +
834 + flat-cache@4.0.1:
835 + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
836 + engines: {node: '>=16'}
837 +
838 + flatted@3.3.3:
839 + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
840 +
841 + foreground-child@3.3.1:
842 + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
843 + engines: {node: '>=14'}
844 +
845 + fraction.js@4.3.7:
846 + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
847 +
848 + fsevents@2.3.3:
849 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
850 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
851 + os: [darwin]
852 +
853 + function-bind@1.1.2:
854 + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
855 +
856 + gensync@1.0.0-beta.2:
857 + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
858 + engines: {node: '>=6.9.0'}
859 +
860 + glob-parent@5.1.2:
861 + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
862 + engines: {node: '>= 6'}
863 +
864 + glob-parent@6.0.2:
865 + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
866 + engines: {node: '>=10.13.0'}
867 +
868 + glob@10.4.5:
869 + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
870 + hasBin: true
871 +
872 + globals@11.12.0:
873 + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
874 + engines: {node: '>=4'}
875 +
876 + globals@13.24.0:
877 + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
878 + engines: {node: '>=8'}
879 +
880 + globals@14.0.0:
881 + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
882 + engines: {node: '>=18'}
883 +
884 + has-flag@4.0.0:
885 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
886 + engines: {node: '>=8'}
887 +
888 + hasown@2.0.2:
889 + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
890 + engines: {node: '>= 0.4'}
891 +
892 + ignore@5.3.2:
893 + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
894 + engines: {node: '>= 4'}
895 +
896 + import-fresh@3.3.1:
897 + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
898 + engines: {node: '>=6'}
899 +
900 + imurmurhash@0.1.4:
901 + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
902 + engines: {node: '>=0.8.19'}
903 +
904 + is-binary-path@2.1.0:
905 + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
906 + engines: {node: '>=8'}
907 +
908 + is-core-module@2.16.1:
909 + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
910 + engines: {node: '>= 0.4'}
911 +
912 + is-extglob@2.1.1:
913 + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
914 + engines: {node: '>=0.10.0'}
915 +
916 + is-fullwidth-code-point@3.0.0:
917 + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
918 + engines: {node: '>=8'}
919 +
920 + is-glob@4.0.3:
921 + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
922 + engines: {node: '>=0.10.0'}
923 +
924 + is-number@7.0.0:
925 + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
926 + engines: {node: '>=0.12.0'}
927 +
928 + isexe@2.0.0:
929 + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
930 +
931 + jackspeak@3.4.3:
932 + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
933 +
934 + jiti@1.21.7:
935 + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
936 + hasBin: true
937 +
938 + js-tokens@4.0.0:
939 + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
940 +
941 + js-yaml@4.1.0:
942 + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
943 + hasBin: true
944 +
945 + jsesc@3.1.0:
946 + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
947 + engines: {node: '>=6'}
948 + hasBin: true
949 +
950 + json-buffer@3.0.1:
951 + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
952 +
953 + json-schema-traverse@0.4.1:
954 + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
955 +
956 + json-stable-stringify-without-jsonify@1.0.1:
957 + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
958 +
959 + json5@2.2.3:
960 + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
961 + engines: {node: '>=6'}
962 + hasBin: true
963 +
964 + keyv@4.5.4:
965 + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
966 +
967 + levn@0.4.1:
968 + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
969 + engines: {node: '>= 0.8.0'}
970 +
971 + lilconfig@3.1.3:
972 + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
973 + engines: {node: '>=14'}
974 +
975 + lines-and-columns@1.2.4:
976 + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
977 +
978 + locate-path@6.0.0:
979 + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
980 + engines: {node: '>=10'}
981 +
982 + lodash.merge@4.6.2:
983 + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
984 +
985 + lodash@4.17.21:
986 + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
987 +
988 + lru-cache@10.4.3:
989 + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
990 +
991 + lru-cache@5.1.1:
992 + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
993 +
994 + magic-string@0.30.17:
995 + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
996 +
997 + merge2@1.4.1:
998 + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
999 + engines: {node: '>= 8'}
1000 +
1001 + micromatch@4.0.8:
1002 + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1003 + engines: {node: '>=8.6'}
1004 +
1005 + minimatch@3.1.2:
1006 + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
1007 +
1008 + minimatch@9.0.5:
1009 + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1010 + engines: {node: '>=16 || 14 >=14.17'}
1011 +
1012 + minipass@7.1.2:
1013 + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
1014 + engines: {node: '>=16 || 14 >=14.17'}
1015 +
1016 + ms@2.1.3:
1017 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1018 +
1019 + mz@2.7.0:
1020 + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
1021 +
1022 + nanoid@3.3.11:
1023 + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
1024 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1025 + hasBin: true
1026 +
1027 + natural-compare@1.4.0:
1028 + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
1029 +
1030 + node-releases@2.0.19:
1031 + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
1032 +
1033 + normalize-path@3.0.0:
1034 + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
1035 + engines: {node: '>=0.10.0'}
1036 +
1037 + normalize-range@0.1.2:
1038 + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
1039 + engines: {node: '>=0.10.0'}
1040 +
1041 + nth-check@2.1.1:
1042 + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
1043 +
1044 + object-assign@4.1.1:
1045 + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1046 + engines: {node: '>=0.10.0'}
1047 +
1048 + object-hash@3.0.0:
1049 + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
1050 + engines: {node: '>= 6'}
1051 +
1052 + optionator@0.9.4:
1053 + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
1054 + engines: {node: '>= 0.8.0'}
1055 +
1056 + p-limit@3.1.0:
1057 + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
1058 + engines: {node: '>=10'}
1059 +
1060 + p-locate@5.0.0:
1061 + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1062 + engines: {node: '>=10'}
1063 +
1064 + package-json-from-dist@1.0.1:
1065 + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
1066 +
1067 + parent-module@1.0.1:
1068 + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
1069 + engines: {node: '>=6'}
1070 +
1071 + path-exists@4.0.0:
1072 + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1073 + engines: {node: '>=8'}
1074 +
1075 + path-key@3.1.1:
1076 + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1077 + engines: {node: '>=8'}
1078 +
1079 + path-parse@1.0.7:
1080 + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1081 +
1082 + path-scurry@1.11.1:
1083 + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
1084 + engines: {node: '>=16 || 14 >=14.18'}
1085 +
1086 + picocolors@1.1.1:
1087 + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1088 +
1089 + picomatch@2.3.1:
1090 + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1091 + engines: {node: '>=8.6'}
1092 +
1093 + pify@2.3.0:
1094 + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
1095 + engines: {node: '>=0.10.0'}
1096 +
1097 + pinia@2.3.1:
1098 + resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
1099 + peerDependencies:
1100 + typescript: '>=4.4.4'
1101 + vue: ^2.7.0 || ^3.5.11
1102 + peerDependenciesMeta:
1103 + typescript:
1104 + optional: true
1105 +
1106 + pirates@4.0.7:
1107 + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
1108 + engines: {node: '>= 6'}
1109 +
1110 + postcss-import@15.1.0:
1111 + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
1112 + engines: {node: '>=14.0.0'}
1113 + peerDependencies:
1114 + postcss: ^8.0.0
1115 +
1116 + postcss-js@4.0.1:
1117 + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
1118 + engines: {node: ^12 || ^14 || >= 16}
1119 + peerDependencies:
1120 + postcss: ^8.4.21
1121 +
1122 + postcss-load-config@4.0.2:
1123 + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
1124 + engines: {node: '>= 14'}
1125 + peerDependencies:
1126 + postcss: '>=8.0.9'
1127 + ts-node: '>=9.0.0'
1128 + peerDependenciesMeta:
1129 + postcss:
1130 + optional: true
1131 + ts-node:
1132 + optional: true
1133 +
1134 + postcss-nested@6.2.0:
1135 + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
1136 + engines: {node: '>=12.0'}
1137 + peerDependencies:
1138 + postcss: ^8.2.14
1139 +
1140 + postcss-selector-parser@6.1.2:
1141 + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
1142 + engines: {node: '>=4'}
1143 +
1144 + postcss-value-parser@4.2.0:
1145 + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
1146 +
1147 + postcss@8.5.3:
1148 + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
1149 + engines: {node: ^10 || ^12 || >=14}
1150 +
1151 + prelude-ls@1.2.1:
1152 + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1153 + engines: {node: '>= 0.8.0'}
1154 +
1155 + punycode@2.3.1:
1156 + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1157 + engines: {node: '>=6'}
1158 +
1159 + queue-microtask@1.2.3:
1160 + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1161 +
1162 + read-cache@1.0.0:
1163 + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
1164 +
1165 + readdirp@3.6.0:
1166 + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
1167 + engines: {node: '>=8.10.0'}
1168 +
1169 + resolve-from@4.0.0:
1170 + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
1171 + engines: {node: '>=4'}
1172 +
1173 + resolve@1.22.10:
1174 + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
1175 + engines: {node: '>= 0.4'}
1176 + hasBin: true
1177 +
1178 + reusify@1.1.0:
1179 + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
1180 + engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1181 +
1182 + rollup@4.40.0:
1183 + resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==}
1184 + engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1185 + hasBin: true
1186 +
1187 + run-parallel@1.2.0:
1188 + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1189 +
1190 + semver@6.3.1:
1191 + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1192 + hasBin: true
1193 +
1194 + semver@7.7.1:
1195 + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
1196 + engines: {node: '>=10'}
1197 + hasBin: true
1198 +
1199 + shebang-command@2.0.0:
1200 + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1201 + engines: {node: '>=8'}
1202 +
1203 + shebang-regex@3.0.0:
1204 + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1205 + engines: {node: '>=8'}
1206 +
1207 + signal-exit@4.1.0:
1208 + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
1209 + engines: {node: '>=14'}
1210 +
1211 + source-map-js@1.2.1:
1212 + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
1213 + engines: {node: '>=0.10.0'}
1214 +
1215 + string-width@4.2.3:
1216 + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
1217 + engines: {node: '>=8'}
1218 +
1219 + string-width@5.1.2:
1220 + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
1221 + engines: {node: '>=12'}
1222 +
1223 + strip-ansi@6.0.1:
1224 + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1225 + engines: {node: '>=8'}
1226 +
1227 + strip-ansi@7.1.0:
1228 + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
1229 + engines: {node: '>=12'}
1230 +
1231 + strip-json-comments@3.1.1:
1232 + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
1233 + engines: {node: '>=8'}
1234 +
1235 + sucrase@3.35.0:
1236 + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
1237 + engines: {node: '>=16 || 14 >=14.17'}
1238 + hasBin: true
1239 +
1240 + supports-color@7.2.0:
1241 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
1242 + engines: {node: '>=8'}
1243 +
1244 + supports-preserve-symlinks-flag@1.0.0:
1245 + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1246 + engines: {node: '>= 0.4'}
1247 +
1248 + tailwindcss@3.4.17:
1249 + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
1250 + engines: {node: '>=14.0.0'}
1251 + hasBin: true
1252 +
1253 + thenify-all@1.6.0:
1254 + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
1255 + engines: {node: '>=0.8'}
1256 +
1257 + thenify@3.3.1:
1258 + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
1259 +
1260 + to-regex-range@5.0.1:
1261 + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1262 + engines: {node: '>=8.0'}
1263 +
1264 + ts-interface-checker@0.1.13:
1265 + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
1266 +
1267 + type-check@0.4.0:
1268 + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
1269 + engines: {node: '>= 0.8.0'}
1270 +
1271 + type-fest@0.20.2:
1272 + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
1273 + engines: {node: '>=10'}
1274 +
1275 + update-browserslist-db@1.1.3:
1276 + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
1277 + hasBin: true
1278 + peerDependencies:
1279 + browserslist: '>= 4.21.0'
1280 +
1281 + uri-js@4.4.1:
1282 + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1283 +
1284 + util-deprecate@1.0.2:
1285 + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
1286 +
1287 + vite@5.4.18:
1288 + resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==}
1289 + engines: {node: ^18.0.0 || >=20.0.0}
1290 + hasBin: true
1291 + peerDependencies:
1292 + '@types/node': ^18.0.0 || >=20.0.0
1293 + less: '*'
1294 + lightningcss: ^1.21.0
1295 + sass: '*'
1296 + sass-embedded: '*'
1297 + stylus: '*'
1298 + sugarss: '*'
1299 + terser: ^5.4.0
1300 + peerDependenciesMeta:
1301 + '@types/node':
1302 + optional: true
1303 + less:
1304 + optional: true
1305 + lightningcss:
1306 + optional: true
1307 + sass:
1308 + optional: true
1309 + sass-embedded:
1310 + optional: true
1311 + stylus:
1312 + optional: true
1313 + sugarss:
1314 + optional: true
1315 + terser:
1316 + optional: true
1317 +
1318 + vue-demi@0.14.10:
1319 + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
1320 + engines: {node: '>=12'}
1321 + hasBin: true
1322 + peerDependencies:
1323 + '@vue/composition-api': ^1.0.0-rc.1
1324 + vue: ^3.0.0-0 || ^2.6.0
1325 + peerDependenciesMeta:
1326 + '@vue/composition-api':
1327 + optional: true
1328 +
1329 + vue-eslint-parser@9.4.3:
1330 + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
1331 + engines: {node: ^14.17.0 || >=16.0.0}
1332 + peerDependencies:
1333 + eslint: '>=6.0.0'
1334 +
1335 + vue-router@4.5.0:
1336 + resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==}
1337 + peerDependencies:
1338 + vue: ^3.2.0
1339 +
1340 + vue@3.5.13:
1341 + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
1342 + peerDependencies:
1343 + typescript: '*'
1344 + peerDependenciesMeta:
1345 + typescript:
1346 + optional: true
1347 +
1348 + which@2.0.2:
1349 + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1350 + engines: {node: '>= 8'}
1351 + hasBin: true
1352 +
1353 + word-wrap@1.2.5:
1354 + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
1355 + engines: {node: '>=0.10.0'}
1356 +
1357 + wrap-ansi@7.0.0:
1358 + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
1359 + engines: {node: '>=10'}
1360 +
1361 + wrap-ansi@8.1.0:
1362 + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
1363 + engines: {node: '>=12'}
1364 +
1365 + xml-name-validator@4.0.0:
1366 + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
1367 + engines: {node: '>=12'}
1368 +
1369 + yallist@3.1.1:
1370 + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
1371 +
1372 + yaml@2.7.1:
1373 + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
1374 + engines: {node: '>= 14'}
1375 + hasBin: true
1376 +
1377 + yocto-queue@0.1.0:
1378 + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
1379 + engines: {node: '>=10'}
1380 +
1381 +snapshots:
1382 +
1383 + '@alloc/quick-lru@5.2.0': {}
1384 +
1385 + '@ampproject/remapping@2.3.0':
1386 + dependencies:
1387 + '@jridgewell/gen-mapping': 0.3.8
1388 + '@jridgewell/trace-mapping': 0.3.25
1389 +
1390 + '@babel/code-frame@7.26.2':
1391 + dependencies:
1392 + '@babel/helper-validator-identifier': 7.25.9
1393 + js-tokens: 4.0.0
1394 + picocolors: 1.1.1
1395 +
1396 + '@babel/compat-data@7.26.8': {}
1397 +
1398 + '@babel/core@7.26.10':
1399 + dependencies:
1400 + '@ampproject/remapping': 2.3.0
1401 + '@babel/code-frame': 7.26.2
1402 + '@babel/generator': 7.27.0
1403 + '@babel/helper-compilation-targets': 7.27.0
1404 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10)
1405 + '@babel/helpers': 7.27.0
1406 + '@babel/parser': 7.27.0
1407 + '@babel/template': 7.27.0
1408 + '@babel/traverse': 7.27.0
1409 + '@babel/types': 7.27.0
1410 + convert-source-map: 2.0.0
1411 + debug: 4.4.0
1412 + gensync: 1.0.0-beta.2
1413 + json5: 2.2.3
1414 + semver: 6.3.1
1415 + transitivePeerDependencies:
1416 + - supports-color
1417 +
1418 + '@babel/generator@7.27.0':
1419 + dependencies:
1420 + '@babel/parser': 7.27.0
1421 + '@babel/types': 7.27.0
1422 + '@jridgewell/gen-mapping': 0.3.8
1423 + '@jridgewell/trace-mapping': 0.3.25
1424 + jsesc: 3.1.0
1425 +
1426 + '@babel/helper-annotate-as-pure@7.25.9':
1427 + dependencies:
1428 + '@babel/types': 7.27.0
1429 +
1430 + '@babel/helper-compilation-targets@7.27.0':
1431 + dependencies:
1432 + '@babel/compat-data': 7.26.8
1433 + '@babel/helper-validator-option': 7.25.9
1434 + browserslist: 4.24.4
1435 + lru-cache: 5.1.1
1436 + semver: 6.3.1
1437 +
1438 + '@babel/helper-create-class-features-plugin@7.27.0(@babel/core@7.26.10)':
1439 + dependencies:
1440 + '@babel/core': 7.26.10
1441 + '@babel/helper-annotate-as-pure': 7.25.9
1442 + '@babel/helper-member-expression-to-functions': 7.25.9
1443 + '@babel/helper-optimise-call-expression': 7.25.9
1444 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.10)
1445 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
1446 + '@babel/traverse': 7.27.0
1447 + semver: 6.3.1
1448 + transitivePeerDependencies:
1449 + - supports-color
1450 +
1451 + '@babel/helper-member-expression-to-functions@7.25.9':
1452 + dependencies:
1453 + '@babel/traverse': 7.27.0
1454 + '@babel/types': 7.27.0
1455 + transitivePeerDependencies:
1456 + - supports-color
1457 +
1458 + '@babel/helper-module-imports@7.25.9':
1459 + dependencies:
1460 + '@babel/traverse': 7.27.0
1461 + '@babel/types': 7.27.0
1462 + transitivePeerDependencies:
1463 + - supports-color
1464 +
1465 + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)':
1466 + dependencies:
1467 + '@babel/core': 7.26.10
1468 + '@babel/helper-module-imports': 7.25.9
1469 + '@babel/helper-validator-identifier': 7.25.9
1470 + '@babel/traverse': 7.27.0
1471 + transitivePeerDependencies:
1472 + - supports-color
1473 +
1474 + '@babel/helper-optimise-call-expression@7.25.9':
1475 + dependencies:
1476 + '@babel/types': 7.27.0
1477 +
1478 + '@babel/helper-plugin-utils@7.26.5': {}
1479 +
1480 + '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.10)':
1481 + dependencies:
1482 + '@babel/core': 7.26.10
1483 + '@babel/helper-member-expression-to-functions': 7.25.9
1484 + '@babel/helper-optimise-call-expression': 7.25.9
1485 + '@babel/traverse': 7.27.0
1486 + transitivePeerDependencies:
1487 + - supports-color
1488 +
1489 + '@babel/helper-skip-transparent-expression-wrappers@7.25.9':
1490 + dependencies:
1491 + '@babel/traverse': 7.27.0
1492 + '@babel/types': 7.27.0
1493 + transitivePeerDependencies:
1494 + - supports-color
1495 +
1496 + '@babel/helper-string-parser@7.25.9': {}
1497 +
1498 + '@babel/helper-validator-identifier@7.25.9': {}
1499 +
1500 + '@babel/helper-validator-option@7.25.9': {}
1501 +
1502 + '@babel/helpers@7.27.0':
1503 + dependencies:
1504 + '@babel/template': 7.27.0
1505 + '@babel/types': 7.27.0
1506 +
1507 + '@babel/parser@7.27.0':
1508 + dependencies:
1509 + '@babel/types': 7.27.0
1510 +
1511 + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)':
1512 + dependencies:
1513 + '@babel/core': 7.26.10
1514 + '@babel/helper-plugin-utils': 7.26.5
1515 +
1516 + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)':
1517 + dependencies:
1518 + '@babel/core': 7.26.10
1519 + '@babel/helper-plugin-utils': 7.26.5
1520 +
1521 + '@babel/plugin-transform-typescript@7.27.0(@babel/core@7.26.10)':
1522 + dependencies:
1523 + '@babel/core': 7.26.10
1524 + '@babel/helper-annotate-as-pure': 7.25.9
1525 + '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.10)
1526 + '@babel/helper-plugin-utils': 7.26.5
1527 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
1528 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10)
1529 + transitivePeerDependencies:
1530 + - supports-color
1531 +
1532 + '@babel/template@7.27.0':
1533 + dependencies:
1534 + '@babel/code-frame': 7.26.2
1535 + '@babel/parser': 7.27.0
1536 + '@babel/types': 7.27.0
1537 +
1538 + '@babel/traverse@7.27.0':
1539 + dependencies:
1540 + '@babel/code-frame': 7.26.2
1541 + '@babel/generator': 7.27.0
1542 + '@babel/parser': 7.27.0
1543 + '@babel/template': 7.27.0
1544 + '@babel/types': 7.27.0
1545 + debug: 4.4.0
1546 + globals: 11.12.0
1547 + transitivePeerDependencies:
1548 + - supports-color
1549 +
1550 + '@babel/types@7.27.0':
1551 + dependencies:
1552 + '@babel/helper-string-parser': 7.25.9
1553 + '@babel/helper-validator-identifier': 7.25.9
1554 +
1555 + '@esbuild/aix-ppc64@0.21.5':
1556 + optional: true
1557 +
1558 + '@esbuild/android-arm64@0.21.5':
1559 + optional: true
1560 +
1561 + '@esbuild/android-arm@0.21.5':
1562 + optional: true
1563 +
1564 + '@esbuild/android-x64@0.21.5':
1565 + optional: true
1566 +
1567 + '@esbuild/darwin-arm64@0.21.5':
1568 + optional: true
1569 +
1570 + '@esbuild/darwin-x64@0.21.5':
1571 + optional: true
1572 +
1573 + '@esbuild/freebsd-arm64@0.21.5':
1574 + optional: true
1575 +
1576 + '@esbuild/freebsd-x64@0.21.5':
1577 + optional: true
1578 +
1579 + '@esbuild/linux-arm64@0.21.5':
1580 + optional: true
1581 +
1582 + '@esbuild/linux-arm@0.21.5':
1583 + optional: true
1584 +
1585 + '@esbuild/linux-ia32@0.21.5':
1586 + optional: true
1587 +
1588 + '@esbuild/linux-loong64@0.21.5':
1589 + optional: true
1590 +
1591 + '@esbuild/linux-mips64el@0.21.5':
1592 + optional: true
1593 +
1594 + '@esbuild/linux-ppc64@0.21.5':
1595 + optional: true
1596 +
1597 + '@esbuild/linux-riscv64@0.21.5':
1598 + optional: true
1599 +
1600 + '@esbuild/linux-s390x@0.21.5':
1601 + optional: true
1602 +
1603 + '@esbuild/linux-x64@0.21.5':
1604 + optional: true
1605 +
1606 + '@esbuild/netbsd-x64@0.21.5':
1607 + optional: true
1608 +
1609 + '@esbuild/openbsd-x64@0.21.5':
1610 + optional: true
1611 +
1612 + '@esbuild/sunos-x64@0.21.5':
1613 + optional: true
1614 +
1615 + '@esbuild/win32-arm64@0.21.5':
1616 + optional: true
1617 +
1618 + '@esbuild/win32-ia32@0.21.5':
1619 + optional: true
1620 +
1621 + '@esbuild/win32-x64@0.21.5':
1622 + optional: true
1623 +
1624 + '@eslint-community/eslint-utils@4.6.1(eslint@9.24.0(jiti@1.21.7))':
1625 + dependencies:
1626 + eslint: 9.24.0(jiti@1.21.7)
1627 + eslint-visitor-keys: 3.4.3
1628 +
1629 + '@eslint-community/regexpp@4.12.1': {}
1630 +
1631 + '@eslint/config-array@0.20.0':
1632 + dependencies:
1633 + '@eslint/object-schema': 2.1.6
1634 + debug: 4.4.0
1635 + minimatch: 3.1.2
1636 + transitivePeerDependencies:
1637 + - supports-color
1638 +
1639 + '@eslint/config-helpers@0.2.1': {}
1640 +
1641 + '@eslint/core@0.12.0':
1642 + dependencies:
1643 + '@types/json-schema': 7.0.15
1644 +
1645 + '@eslint/core@0.13.0':
1646 + dependencies:
1647 + '@types/json-schema': 7.0.15
1648 +
1649 + '@eslint/eslintrc@3.3.1':
1650 + dependencies:
1651 + ajv: 6.12.6
1652 + debug: 4.4.0
1653 + espree: 10.3.0
1654 + globals: 14.0.0
1655 + ignore: 5.3.2
1656 + import-fresh: 3.3.1
1657 + js-yaml: 4.1.0
1658 + minimatch: 3.1.2
1659 + strip-json-comments: 3.1.1
1660 + transitivePeerDependencies:
1661 + - supports-color
1662 +
1663 + '@eslint/js@9.24.0': {}
1664 +
1665 + '@eslint/object-schema@2.1.6': {}
1666 +
1667 + '@eslint/plugin-kit@0.2.8':
1668 + dependencies:
1669 + '@eslint/core': 0.13.0
1670 + levn: 0.4.1
1671 +
1672 + '@humanfs/core@0.19.1': {}
1673 +
1674 + '@humanfs/node@0.16.6':
1675 + dependencies:
1676 + '@humanfs/core': 0.19.1
1677 + '@humanwhocodes/retry': 0.3.1
1678 +
1679 + '@humanwhocodes/module-importer@1.0.1': {}
1680 +
1681 + '@humanwhocodes/retry@0.3.1': {}
1682 +
1683 + '@humanwhocodes/retry@0.4.2': {}
1684 +
1685 + '@isaacs/cliui@8.0.2':
1686 + dependencies:
1687 + string-width: 5.1.2
1688 + string-width-cjs: string-width@4.2.3
1689 + strip-ansi: 7.1.0
1690 + strip-ansi-cjs: strip-ansi@6.0.1
1691 + wrap-ansi: 8.1.0
1692 + wrap-ansi-cjs: wrap-ansi@7.0.0
1693 +
1694 + '@jridgewell/gen-mapping@0.3.8':
1695 + dependencies:
1696 + '@jridgewell/set-array': 1.2.1
1697 + '@jridgewell/sourcemap-codec': 1.5.0
1698 + '@jridgewell/trace-mapping': 0.3.25
1699 +
1700 + '@jridgewell/resolve-uri@3.1.2': {}
1701 +
1702 + '@jridgewell/set-array@1.2.1': {}
1703 +
1704 + '@jridgewell/sourcemap-codec@1.5.0': {}
1705 +
1706 + '@jridgewell/trace-mapping@0.3.25':
1707 + dependencies:
1708 + '@jridgewell/resolve-uri': 3.1.2
1709 + '@jridgewell/sourcemap-codec': 1.5.0
1710 +
1711 + '@nodelib/fs.scandir@2.1.5':
1712 + dependencies:
1713 + '@nodelib/fs.stat': 2.0.5
1714 + run-parallel: 1.2.0
1715 +
1716 + '@nodelib/fs.stat@2.0.5': {}
1717 +
1718 + '@nodelib/fs.walk@1.2.8':
1719 + dependencies:
1720 + '@nodelib/fs.scandir': 2.1.5
1721 + fastq: 1.19.1
1722 +
1723 + '@pkgjs/parseargs@0.11.0':
1724 + optional: true
1725 +
1726 + '@rollup/rollup-android-arm-eabi@4.40.0':
1727 + optional: true
1728 +
1729 + '@rollup/rollup-android-arm64@4.40.0':
1730 + optional: true
1731 +
1732 + '@rollup/rollup-darwin-arm64@4.40.0':
1733 + optional: true
1734 +
1735 + '@rollup/rollup-darwin-x64@4.40.0':
1736 + optional: true
1737 +
1738 + '@rollup/rollup-freebsd-arm64@4.40.0':
1739 + optional: true
1740 +
1741 + '@rollup/rollup-freebsd-x64@4.40.0':
1742 + optional: true
1743 +
1744 + '@rollup/rollup-linux-arm-gnueabihf@4.40.0':
1745 + optional: true
1746 +
1747 + '@rollup/rollup-linux-arm-musleabihf@4.40.0':
1748 + optional: true
1749 +
1750 + '@rollup/rollup-linux-arm64-gnu@4.40.0':
1751 + optional: true
1752 +
1753 + '@rollup/rollup-linux-arm64-musl@4.40.0':
1754 + optional: true
1755 +
1756 + '@rollup/rollup-linux-loongarch64-gnu@4.40.0':
1757 + optional: true
1758 +
1759 + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0':
1760 + optional: true
1761 +
1762 + '@rollup/rollup-linux-riscv64-gnu@4.40.0':
1763 + optional: true
1764 +
1765 + '@rollup/rollup-linux-riscv64-musl@4.40.0':
1766 + optional: true
1767 +
1768 + '@rollup/rollup-linux-s390x-gnu@4.40.0':
1769 + optional: true
1770 +
1771 + '@rollup/rollup-linux-x64-gnu@4.40.0':
1772 + optional: true
1773 +
1774 + '@rollup/rollup-linux-x64-musl@4.40.0':
1775 + optional: true
1776 +
1777 + '@rollup/rollup-win32-arm64-msvc@4.40.0':
1778 + optional: true
1779 +
1780 + '@rollup/rollup-win32-ia32-msvc@4.40.0':
1781 + optional: true
1782 +
1783 + '@rollup/rollup-win32-x64-msvc@4.40.0':
1784 + optional: true
1785 +
1786 + '@types/estree@1.0.7': {}
1787 +
1788 + '@types/json-schema@7.0.15': {}
1789 +
1790 + '@vitejs/plugin-vue-jsx@4.1.2(vite@5.4.18)(vue@3.5.13)':
1791 + dependencies:
1792 + '@babel/core': 7.26.10
1793 + '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.10)
1794 + '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.26.10)
1795 + vite: 5.4.18
1796 + vue: 3.5.13
1797 + transitivePeerDependencies:
1798 + - supports-color
1799 +
1800 + '@vitejs/plugin-vue@5.2.3(vite@5.4.18)(vue@3.5.13)':
1801 + dependencies:
1802 + vite: 5.4.18
1803 + vue: 3.5.13
1804 +
1805 + '@vue/babel-helper-vue-transform-on@1.4.0': {}
1806 +
1807 + '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.26.10)':
1808 + dependencies:
1809 + '@babel/helper-module-imports': 7.25.9
1810 + '@babel/helper-plugin-utils': 7.26.5
1811 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
1812 + '@babel/template': 7.27.0
1813 + '@babel/traverse': 7.27.0
1814 + '@babel/types': 7.27.0
1815 + '@vue/babel-helper-vue-transform-on': 1.4.0
1816 + '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.26.10)
1817 + '@vue/shared': 3.5.13
1818 + optionalDependencies:
1819 + '@babel/core': 7.26.10
1820 + transitivePeerDependencies:
1821 + - supports-color
1822 +
1823 + '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.26.10)':
1824 + dependencies:
1825 + '@babel/code-frame': 7.26.2
1826 + '@babel/core': 7.26.10
1827 + '@babel/helper-module-imports': 7.25.9
1828 + '@babel/helper-plugin-utils': 7.26.5
1829 + '@babel/parser': 7.27.0
1830 + '@vue/compiler-sfc': 3.5.13
1831 + transitivePeerDependencies:
1832 + - supports-color
1833 +
1834 + '@vue/compiler-core@3.5.13':
1835 + dependencies:
1836 + '@babel/parser': 7.27.0
1837 + '@vue/shared': 3.5.13
1838 + entities: 4.5.0
1839 + estree-walker: 2.0.2
1840 + source-map-js: 1.2.1
1841 +
1842 + '@vue/compiler-dom@3.5.13':
1843 + dependencies:
1844 + '@vue/compiler-core': 3.5.13
1845 + '@vue/shared': 3.5.13
1846 +
1847 + '@vue/compiler-sfc@3.5.13':
1848 + dependencies:
1849 + '@babel/parser': 7.27.0
1850 + '@vue/compiler-core': 3.5.13
1851 + '@vue/compiler-dom': 3.5.13
1852 + '@vue/compiler-ssr': 3.5.13
1853 + '@vue/shared': 3.5.13
1854 + estree-walker: 2.0.2
1855 + magic-string: 0.30.17
1856 + postcss: 8.5.3
1857 + source-map-js: 1.2.1
1858 +
1859 + '@vue/compiler-ssr@3.5.13':
1860 + dependencies:
1861 + '@vue/compiler-dom': 3.5.13
1862 + '@vue/shared': 3.5.13
1863 +
1864 + '@vue/devtools-api@6.6.4': {}
1865 +
1866 + '@vue/reactivity@3.5.13':
1867 + dependencies:
1868 + '@vue/shared': 3.5.13
1869 +
1870 + '@vue/runtime-core@3.5.13':
1871 + dependencies:
1872 + '@vue/reactivity': 3.5.13
1873 + '@vue/shared': 3.5.13
1874 +
1875 + '@vue/runtime-dom@3.5.13':
1876 + dependencies:
1877 + '@vue/reactivity': 3.5.13
1878 + '@vue/runtime-core': 3.5.13
1879 + '@vue/shared': 3.5.13
1880 + csstype: 3.1.3
1881 +
1882 + '@vue/server-renderer@3.5.13(vue@3.5.13)':
1883 + dependencies:
1884 + '@vue/compiler-ssr': 3.5.13
1885 + '@vue/shared': 3.5.13
1886 + vue: 3.5.13
1887 +
1888 + '@vue/shared@3.5.13': {}
1889 +
1890 + acorn-jsx@5.3.2(acorn@8.14.1):
1891 + dependencies:
1892 + acorn: 8.14.1
1893 +
1894 + acorn@8.14.1: {}
1895 +
1896 + ajv@6.12.6:
1897 + dependencies:
1898 + fast-deep-equal: 3.1.3
1899 + fast-json-stable-stringify: 2.1.0
1900 + json-schema-traverse: 0.4.1
1901 + uri-js: 4.4.1
1902 +
1903 + ansi-regex@5.0.1: {}
1904 +
1905 + ansi-regex@6.1.0: {}
1906 +
1907 + ansi-styles@4.3.0:
1908 + dependencies:
1909 + color-convert: 2.0.1
1910 +
1911 + ansi-styles@6.2.1: {}
1912 +
1913 + any-promise@1.3.0: {}
1914 +
1915 + anymatch@3.1.3:
1916 + dependencies:
1917 + normalize-path: 3.0.0
1918 + picomatch: 2.3.1
1919 +
1920 + arg@5.0.2: {}
1921 +
1922 + argparse@2.0.1: {}
1923 +
1924 + autoprefixer@10.4.21(postcss@8.5.3):
1925 + dependencies:
1926 + browserslist: 4.24.4
1927 + caniuse-lite: 1.0.30001714
1928 + fraction.js: 4.3.7
1929 + normalize-range: 0.1.2
1930 + picocolors: 1.1.1
1931 + postcss: 8.5.3
1932 + postcss-value-parser: 4.2.0
1933 +
1934 + balanced-match@1.0.2: {}
1935 +
1936 + binary-extensions@2.3.0: {}
1937 +
1938 + boolbase@1.0.0: {}
1939 +
1940 + brace-expansion@1.1.11:
1941 + dependencies:
1942 + balanced-match: 1.0.2
1943 + concat-map: 0.0.1
1944 +
1945 + brace-expansion@2.0.1:
1946 + dependencies:
1947 + balanced-match: 1.0.2
1948 +
1949 + braces@3.0.3:
1950 + dependencies:
1951 + fill-range: 7.1.1
1952 +
1953 + browserslist@4.24.4:
1954 + dependencies:
1955 + caniuse-lite: 1.0.30001714
1956 + electron-to-chromium: 1.5.137
1957 + node-releases: 2.0.19
1958 + update-browserslist-db: 1.1.3(browserslist@4.24.4)
1959 +
1960 + callsites@3.1.0: {}
1961 +
1962 + camelcase-css@2.0.1: {}
1963 +
1964 + caniuse-lite@1.0.30001714: {}
1965 +
1966 + chalk@4.1.2:
1967 + dependencies:
1968 + ansi-styles: 4.3.0
1969 + supports-color: 7.2.0
1970 +
1971 + chokidar@3.6.0:
1972 + dependencies:
1973 + anymatch: 3.1.3
1974 + braces: 3.0.3
1975 + glob-parent: 5.1.2
1976 + is-binary-path: 2.1.0
1977 + is-glob: 4.0.3
1978 + normalize-path: 3.0.0
1979 + readdirp: 3.6.0
1980 + optionalDependencies:
1981 + fsevents: 2.3.3
1982 +
1983 + color-convert@2.0.1:
1984 + dependencies:
1985 + color-name: 1.1.4
1986 +
1987 + color-name@1.1.4: {}
1988 +
1989 + commander@4.1.1: {}
1990 +
1991 + concat-map@0.0.1: {}
1992 +
1993 + convert-source-map@2.0.0: {}
1994 +
1995 + cross-spawn@7.0.6:
1996 + dependencies:
1997 + path-key: 3.1.1
1998 + shebang-command: 2.0.0
1999 + which: 2.0.2
2000 +
2001 + cssesc@3.0.0: {}
2002 +
2003 + csstype@3.1.3: {}
2004 +
2005 + debug@4.4.0:
2006 + dependencies:
2007 + ms: 2.1.3
2008 +
2009 + deep-is@0.1.4: {}
2010 +
2011 + didyoumean@1.2.2: {}
2012 +
2013 + dlv@1.1.3: {}
2014 +
2015 + eastasianwidth@0.2.0: {}
2016 +
2017 + electron-to-chromium@1.5.137: {}
2018 +
2019 + emoji-regex@8.0.0: {}
2020 +
2021 + emoji-regex@9.2.2: {}
2022 +
2023 + entities@4.5.0: {}
2024 +
2025 + esbuild@0.21.5:
2026 + optionalDependencies:
2027 + '@esbuild/aix-ppc64': 0.21.5
2028 + '@esbuild/android-arm': 0.21.5
2029 + '@esbuild/android-arm64': 0.21.5
2030 + '@esbuild/android-x64': 0.21.5
2031 + '@esbuild/darwin-arm64': 0.21.5
2032 + '@esbuild/darwin-x64': 0.21.5
2033 + '@esbuild/freebsd-arm64': 0.21.5
2034 + '@esbuild/freebsd-x64': 0.21.5
2035 + '@esbuild/linux-arm': 0.21.5
2036 + '@esbuild/linux-arm64': 0.21.5
2037 + '@esbuild/linux-ia32': 0.21.5
2038 + '@esbuild/linux-loong64': 0.21.5
2039 + '@esbuild/linux-mips64el': 0.21.5
2040 + '@esbuild/linux-ppc64': 0.21.5
2041 + '@esbuild/linux-riscv64': 0.21.5
2042 + '@esbuild/linux-s390x': 0.21.5
2043 + '@esbuild/linux-x64': 0.21.5
2044 + '@esbuild/netbsd-x64': 0.21.5
2045 + '@esbuild/openbsd-x64': 0.21.5
2046 + '@esbuild/sunos-x64': 0.21.5
2047 + '@esbuild/win32-arm64': 0.21.5
2048 + '@esbuild/win32-ia32': 0.21.5
2049 + '@esbuild/win32-x64': 0.21.5
2050 +
2051 + escalade@3.2.0: {}
2052 +
2053 + escape-string-regexp@4.0.0: {}
2054 +
2055 + eslint-plugin-vue@9.33.0(eslint@9.24.0(jiti@1.21.7)):
2056 + dependencies:
2057 + '@eslint-community/eslint-utils': 4.6.1(eslint@9.24.0(jiti@1.21.7))
2058 + eslint: 9.24.0(jiti@1.21.7)
2059 + globals: 13.24.0
2060 + natural-compare: 1.4.0
2061 + nth-check: 2.1.1
2062 + postcss-selector-parser: 6.1.2
2063 + semver: 7.7.1
2064 + vue-eslint-parser: 9.4.3(eslint@9.24.0(jiti@1.21.7))
2065 + xml-name-validator: 4.0.0
2066 + transitivePeerDependencies:
2067 + - supports-color
2068 +
2069 + eslint-scope@7.2.2:
2070 + dependencies:
2071 + esrecurse: 4.3.0
2072 + estraverse: 5.3.0
2073 +
2074 + eslint-scope@8.3.0:
2075 + dependencies:
2076 + esrecurse: 4.3.0
2077 + estraverse: 5.3.0
2078 +
2079 + eslint-visitor-keys@3.4.3: {}
2080 +
2081 + eslint-visitor-keys@4.2.0: {}
2082 +
2083 + eslint@9.24.0(jiti@1.21.7):
2084 + dependencies:
2085 + '@eslint-community/eslint-utils': 4.6.1(eslint@9.24.0(jiti@1.21.7))
2086 + '@eslint-community/regexpp': 4.12.1
2087 + '@eslint/config-array': 0.20.0
2088 + '@eslint/config-helpers': 0.2.1
2089 + '@eslint/core': 0.12.0
2090 + '@eslint/eslintrc': 3.3.1
2091 + '@eslint/js': 9.24.0
2092 + '@eslint/plugin-kit': 0.2.8
2093 + '@humanfs/node': 0.16.6
2094 + '@humanwhocodes/module-importer': 1.0.1
2095 + '@humanwhocodes/retry': 0.4.2
2096 + '@types/estree': 1.0.7
2097 + '@types/json-schema': 7.0.15
2098 + ajv: 6.12.6
2099 + chalk: 4.1.2
2100 + cross-spawn: 7.0.6
2101 + debug: 4.4.0
2102 + escape-string-regexp: 4.0.0
2103 + eslint-scope: 8.3.0
2104 + eslint-visitor-keys: 4.2.0
2105 + espree: 10.3.0
2106 + esquery: 1.6.0
2107 + esutils: 2.0.3
2108 + fast-deep-equal: 3.1.3
2109 + file-entry-cache: 8.0.0
2110 + find-up: 5.0.0
2111 + glob-parent: 6.0.2
2112 + ignore: 5.3.2
2113 + imurmurhash: 0.1.4
2114 + is-glob: 4.0.3
2115 + json-stable-stringify-without-jsonify: 1.0.1
2116 + lodash.merge: 4.6.2
2117 + minimatch: 3.1.2
2118 + natural-compare: 1.4.0
2119 + optionator: 0.9.4
2120 + optionalDependencies:
2121 + jiti: 1.21.7
2122 + transitivePeerDependencies:
2123 + - supports-color
2124 +
2125 + espree@10.3.0:
2126 + dependencies:
2127 + acorn: 8.14.1
2128 + acorn-jsx: 5.3.2(acorn@8.14.1)
2129 + eslint-visitor-keys: 4.2.0
2130 +
2131 + espree@9.6.1:
2132 + dependencies:
2133 + acorn: 8.14.1
2134 + acorn-jsx: 5.3.2(acorn@8.14.1)
2135 + eslint-visitor-keys: 3.4.3
2136 +
2137 + esquery@1.6.0:
2138 + dependencies:
2139 + estraverse: 5.3.0
2140 +
2141 + esrecurse@4.3.0:
2142 + dependencies:
2143 + estraverse: 5.3.0
2144 +
2145 + estraverse@5.3.0: {}
2146 +
2147 + estree-walker@2.0.2: {}
2148 +
2149 + esutils@2.0.3: {}
2150 +
2151 + fast-deep-equal@3.1.3: {}
2152 +
2153 + fast-glob@3.3.3:
2154 + dependencies:
2155 + '@nodelib/fs.stat': 2.0.5
2156 + '@nodelib/fs.walk': 1.2.8
2157 + glob-parent: 5.1.2
2158 + merge2: 1.4.1
2159 + micromatch: 4.0.8
2160 +
2161 + fast-json-stable-stringify@2.1.0: {}
2162 +
2163 + fast-levenshtein@2.0.6: {}
2164 +
2165 + fastq@1.19.1:
2166 + dependencies:
2167 + reusify: 1.1.0
2168 +
2169 + file-entry-cache@8.0.0:
2170 + dependencies:
2171 + flat-cache: 4.0.1
2172 +
2173 + fill-range@7.1.1:
2174 + dependencies:
2175 + to-regex-range: 5.0.1
2176 +
2177 + find-up@5.0.0:
2178 + dependencies:
2179 + locate-path: 6.0.0
2180 + path-exists: 4.0.0
2181 +
2182 + flat-cache@4.0.1:
2183 + dependencies:
2184 + flatted: 3.3.3
2185 + keyv: 4.5.4
2186 +
2187 + flatted@3.3.3: {}
2188 +
2189 + foreground-child@3.3.1:
2190 + dependencies:
2191 + cross-spawn: 7.0.6
2192 + signal-exit: 4.1.0
2193 +
2194 + fraction.js@4.3.7: {}
2195 +
2196 + fsevents@2.3.3:
2197 + optional: true
2198 +
2199 + function-bind@1.1.2: {}
2200 +
2201 + gensync@1.0.0-beta.2: {}
2202 +
2203 + glob-parent@5.1.2:
2204 + dependencies:
2205 + is-glob: 4.0.3
2206 +
2207 + glob-parent@6.0.2:
2208 + dependencies:
2209 + is-glob: 4.0.3
2210 +
2211 + glob@10.4.5:
2212 + dependencies:
2213 + foreground-child: 3.3.1
2214 + jackspeak: 3.4.3
2215 + minimatch: 9.0.5
2216 + minipass: 7.1.2
2217 + package-json-from-dist: 1.0.1
2218 + path-scurry: 1.11.1
2219 +
2220 + globals@11.12.0: {}
2221 +
2222 + globals@13.24.0:
2223 + dependencies:
2224 + type-fest: 0.20.2
2225 +
2226 + globals@14.0.0: {}
2227 +
2228 + has-flag@4.0.0: {}
2229 +
2230 + hasown@2.0.2:
2231 + dependencies:
2232 + function-bind: 1.1.2
2233 +
2234 + ignore@5.3.2: {}
2235 +
2236 + import-fresh@3.3.1:
2237 + dependencies:
2238 + parent-module: 1.0.1
2239 + resolve-from: 4.0.0
2240 +
2241 + imurmurhash@0.1.4: {}
2242 +
2243 + is-binary-path@2.1.0:
2244 + dependencies:
2245 + binary-extensions: 2.3.0
2246 +
2247 + is-core-module@2.16.1:
2248 + dependencies:
2249 + hasown: 2.0.2
2250 +
2251 + is-extglob@2.1.1: {}
2252 +
2253 + is-fullwidth-code-point@3.0.0: {}
2254 +
2255 + is-glob@4.0.3:
2256 + dependencies:
2257 + is-extglob: 2.1.1
2258 +
2259 + is-number@7.0.0: {}
2260 +
2261 + isexe@2.0.0: {}
2262 +
2263 + jackspeak@3.4.3:
2264 + dependencies:
2265 + '@isaacs/cliui': 8.0.2
2266 + optionalDependencies:
2267 + '@pkgjs/parseargs': 0.11.0
2268 +
2269 + jiti@1.21.7: {}
2270 +
2271 + js-tokens@4.0.0: {}
2272 +
2273 + js-yaml@4.1.0:
2274 + dependencies:
2275 + argparse: 2.0.1
2276 +
2277 + jsesc@3.1.0: {}
2278 +
2279 + json-buffer@3.0.1: {}
2280 +
2281 + json-schema-traverse@0.4.1: {}
2282 +
2283 + json-stable-stringify-without-jsonify@1.0.1: {}
2284 +
2285 + json5@2.2.3: {}
2286 +
2287 + keyv@4.5.4:
2288 + dependencies:
2289 + json-buffer: 3.0.1
2290 +
2291 + levn@0.4.1:
2292 + dependencies:
2293 + prelude-ls: 1.2.1
2294 + type-check: 0.4.0
2295 +
2296 + lilconfig@3.1.3: {}
2297 +
2298 + lines-and-columns@1.2.4: {}
2299 +
2300 + locate-path@6.0.0:
2301 + dependencies:
2302 + p-locate: 5.0.0
2303 +
2304 + lodash.merge@4.6.2: {}
2305 +
2306 + lodash@4.17.21: {}
2307 +
2308 + lru-cache@10.4.3: {}
2309 +
2310 + lru-cache@5.1.1:
2311 + dependencies:
2312 + yallist: 3.1.1
2313 +
2314 + magic-string@0.30.17:
2315 + dependencies:
2316 + '@jridgewell/sourcemap-codec': 1.5.0
2317 +
2318 + merge2@1.4.1: {}
2319 +
2320 + micromatch@4.0.8:
2321 + dependencies:
2322 + braces: 3.0.3
2323 + picomatch: 2.3.1
2324 +
2325 + minimatch@3.1.2:
2326 + dependencies:
2327 + brace-expansion: 1.1.11
2328 +
2329 + minimatch@9.0.5:
2330 + dependencies:
2331 + brace-expansion: 2.0.1
2332 +
2333 + minipass@7.1.2: {}
2334 +
2335 + ms@2.1.3: {}
2336 +
2337 + mz@2.7.0:
2338 + dependencies:
2339 + any-promise: 1.3.0
2340 + object-assign: 4.1.1
2341 + thenify-all: 1.6.0
2342 +
2343 + nanoid@3.3.11: {}
2344 +
2345 + natural-compare@1.4.0: {}
2346 +
2347 + node-releases@2.0.19: {}
2348 +
2349 + normalize-path@3.0.0: {}
2350 +
2351 + normalize-range@0.1.2: {}
2352 +
2353 + nth-check@2.1.1:
2354 + dependencies:
2355 + boolbase: 1.0.0
2356 +
2357 + object-assign@4.1.1: {}
2358 +
2359 + object-hash@3.0.0: {}
2360 +
2361 + optionator@0.9.4:
2362 + dependencies:
2363 + deep-is: 0.1.4
2364 + fast-levenshtein: 2.0.6
2365 + levn: 0.4.1
2366 + prelude-ls: 1.2.1
2367 + type-check: 0.4.0
2368 + word-wrap: 1.2.5
2369 +
2370 + p-limit@3.1.0:
2371 + dependencies:
2372 + yocto-queue: 0.1.0
2373 +
2374 + p-locate@5.0.0:
2375 + dependencies:
2376 + p-limit: 3.1.0
2377 +
2378 + package-json-from-dist@1.0.1: {}
2379 +
2380 + parent-module@1.0.1:
2381 + dependencies:
2382 + callsites: 3.1.0
2383 +
2384 + path-exists@4.0.0: {}
2385 +
2386 + path-key@3.1.1: {}
2387 +
2388 + path-parse@1.0.7: {}
2389 +
2390 + path-scurry@1.11.1:
2391 + dependencies:
2392 + lru-cache: 10.4.3
2393 + minipass: 7.1.2
2394 +
2395 + picocolors@1.1.1: {}
2396 +
2397 + picomatch@2.3.1: {}
2398 +
2399 + pify@2.3.0: {}
2400 +
2401 + pinia@2.3.1(vue@3.5.13):
2402 + dependencies:
2403 + '@vue/devtools-api': 6.6.4
2404 + vue: 3.5.13
2405 + vue-demi: 0.14.10(vue@3.5.13)
2406 + transitivePeerDependencies:
2407 + - '@vue/composition-api'
2408 +
2409 + pirates@4.0.7: {}
2410 +
2411 + postcss-import@15.1.0(postcss@8.5.3):
2412 + dependencies:
2413 + postcss: 8.5.3
2414 + postcss-value-parser: 4.2.0
2415 + read-cache: 1.0.0
2416 + resolve: 1.22.10
2417 +
2418 + postcss-js@4.0.1(postcss@8.5.3):
2419 + dependencies:
2420 + camelcase-css: 2.0.1
2421 + postcss: 8.5.3
2422 +
2423 + postcss-load-config@4.0.2(postcss@8.5.3):
2424 + dependencies:
2425 + lilconfig: 3.1.3
2426 + yaml: 2.7.1
2427 + optionalDependencies:
2428 + postcss: 8.5.3
2429 +
2430 + postcss-nested@6.2.0(postcss@8.5.3):
2431 + dependencies:
2432 + postcss: 8.5.3
2433 + postcss-selector-parser: 6.1.2
2434 +
2435 + postcss-selector-parser@6.1.2:
2436 + dependencies:
2437 + cssesc: 3.0.0
2438 + util-deprecate: 1.0.2
2439 +
2440 + postcss-value-parser@4.2.0: {}
2441 +
2442 + postcss@8.5.3:
2443 + dependencies:
2444 + nanoid: 3.3.11
2445 + picocolors: 1.1.1
2446 + source-map-js: 1.2.1
2447 +
2448 + prelude-ls@1.2.1: {}
2449 +
2450 + punycode@2.3.1: {}
2451 +
2452 + queue-microtask@1.2.3: {}
2453 +
2454 + read-cache@1.0.0:
2455 + dependencies:
2456 + pify: 2.3.0
2457 +
2458 + readdirp@3.6.0:
2459 + dependencies:
2460 + picomatch: 2.3.1
2461 +
2462 + resolve-from@4.0.0: {}
2463 +
2464 + resolve@1.22.10:
2465 + dependencies:
2466 + is-core-module: 2.16.1
2467 + path-parse: 1.0.7
2468 + supports-preserve-symlinks-flag: 1.0.0
2469 +
2470 + reusify@1.1.0: {}
2471 +
2472 + rollup@4.40.0:
2473 + dependencies:
2474 + '@types/estree': 1.0.7
2475 + optionalDependencies:
2476 + '@rollup/rollup-android-arm-eabi': 4.40.0
2477 + '@rollup/rollup-android-arm64': 4.40.0
2478 + '@rollup/rollup-darwin-arm64': 4.40.0
2479 + '@rollup/rollup-darwin-x64': 4.40.0
2480 + '@rollup/rollup-freebsd-arm64': 4.40.0
2481 + '@rollup/rollup-freebsd-x64': 4.40.0
2482 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.0
2483 + '@rollup/rollup-linux-arm-musleabihf': 4.40.0
2484 + '@rollup/rollup-linux-arm64-gnu': 4.40.0
2485 + '@rollup/rollup-linux-arm64-musl': 4.40.0
2486 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.0
2487 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0
2488 + '@rollup/rollup-linux-riscv64-gnu': 4.40.0
2489 + '@rollup/rollup-linux-riscv64-musl': 4.40.0
2490 + '@rollup/rollup-linux-s390x-gnu': 4.40.0
2491 + '@rollup/rollup-linux-x64-gnu': 4.40.0
2492 + '@rollup/rollup-linux-x64-musl': 4.40.0
2493 + '@rollup/rollup-win32-arm64-msvc': 4.40.0
2494 + '@rollup/rollup-win32-ia32-msvc': 4.40.0
2495 + '@rollup/rollup-win32-x64-msvc': 4.40.0
2496 + fsevents: 2.3.3
2497 +
2498 + run-parallel@1.2.0:
2499 + dependencies:
2500 + queue-microtask: 1.2.3
2501 +
2502 + semver@6.3.1: {}
2503 +
2504 + semver@7.7.1: {}
2505 +
2506 + shebang-command@2.0.0:
2507 + dependencies:
2508 + shebang-regex: 3.0.0
2509 +
2510 + shebang-regex@3.0.0: {}
2511 +
2512 + signal-exit@4.1.0: {}
2513 +
2514 + source-map-js@1.2.1: {}
2515 +
2516 + string-width@4.2.3:
2517 + dependencies:
2518 + emoji-regex: 8.0.0
2519 + is-fullwidth-code-point: 3.0.0
2520 + strip-ansi: 6.0.1
2521 +
2522 + string-width@5.1.2:
2523 + dependencies:
2524 + eastasianwidth: 0.2.0
2525 + emoji-regex: 9.2.2
2526 + strip-ansi: 7.1.0
2527 +
2528 + strip-ansi@6.0.1:
2529 + dependencies:
2530 + ansi-regex: 5.0.1
2531 +
2532 + strip-ansi@7.1.0:
2533 + dependencies:
2534 + ansi-regex: 6.1.0
2535 +
2536 + strip-json-comments@3.1.1: {}
2537 +
2538 + sucrase@3.35.0:
2539 + dependencies:
2540 + '@jridgewell/gen-mapping': 0.3.8
2541 + commander: 4.1.1
2542 + glob: 10.4.5
2543 + lines-and-columns: 1.2.4
2544 + mz: 2.7.0
2545 + pirates: 4.0.7
2546 + ts-interface-checker: 0.1.13
2547 +
2548 + supports-color@7.2.0:
2549 + dependencies:
2550 + has-flag: 4.0.0
2551 +
2552 + supports-preserve-symlinks-flag@1.0.0: {}
2553 +
2554 + tailwindcss@3.4.17:
2555 + dependencies:
2556 + '@alloc/quick-lru': 5.2.0
2557 + arg: 5.0.2
2558 + chokidar: 3.6.0
2559 + didyoumean: 1.2.2
2560 + dlv: 1.1.3
2561 + fast-glob: 3.3.3
2562 + glob-parent: 6.0.2
2563 + is-glob: 4.0.3
2564 + jiti: 1.21.7
2565 + lilconfig: 3.1.3
2566 + micromatch: 4.0.8
2567 + normalize-path: 3.0.0
2568 + object-hash: 3.0.0
2569 + picocolors: 1.1.1
2570 + postcss: 8.5.3
2571 + postcss-import: 15.1.0(postcss@8.5.3)
2572 + postcss-js: 4.0.1(postcss@8.5.3)
2573 + postcss-load-config: 4.0.2(postcss@8.5.3)
2574 + postcss-nested: 6.2.0(postcss@8.5.3)
2575 + postcss-selector-parser: 6.1.2
2576 + resolve: 1.22.10
2577 + sucrase: 3.35.0
2578 + transitivePeerDependencies:
2579 + - ts-node
2580 +
2581 + thenify-all@1.6.0:
2582 + dependencies:
2583 + thenify: 3.3.1
2584 +
2585 + thenify@3.3.1:
2586 + dependencies:
2587 + any-promise: 1.3.0
2588 +
2589 + to-regex-range@5.0.1:
2590 + dependencies:
2591 + is-number: 7.0.0
2592 +
2593 + ts-interface-checker@0.1.13: {}
2594 +
2595 + type-check@0.4.0:
2596 + dependencies:
2597 + prelude-ls: 1.2.1
2598 +
2599 + type-fest@0.20.2: {}
2600 +
2601 + update-browserslist-db@1.1.3(browserslist@4.24.4):
2602 + dependencies:
2603 + browserslist: 4.24.4
2604 + escalade: 3.2.0
2605 + picocolors: 1.1.1
2606 +
2607 + uri-js@4.4.1:
2608 + dependencies:
2609 + punycode: 2.3.1
2610 +
2611 + util-deprecate@1.0.2: {}
2612 +
2613 + vite@5.4.18:
2614 + dependencies:
2615 + esbuild: 0.21.5
2616 + postcss: 8.5.3
2617 + rollup: 4.40.0
2618 + optionalDependencies:
2619 + fsevents: 2.3.3
2620 +
2621 + vue-demi@0.14.10(vue@3.5.13):
2622 + dependencies:
2623 + vue: 3.5.13
2624 +
2625 + vue-eslint-parser@9.4.3(eslint@9.24.0(jiti@1.21.7)):
2626 + dependencies:
2627 + debug: 4.4.0
2628 + eslint: 9.24.0(jiti@1.21.7)
2629 + eslint-scope: 7.2.2
2630 + eslint-visitor-keys: 3.4.3
2631 + espree: 9.6.1
2632 + esquery: 1.6.0
2633 + lodash: 4.17.21
2634 + semver: 7.7.1
2635 + transitivePeerDependencies:
2636 + - supports-color
2637 +
2638 + vue-router@4.5.0(vue@3.5.13):
2639 + dependencies:
2640 + '@vue/devtools-api': 6.6.4
2641 + vue: 3.5.13
2642 +
2643 + vue@3.5.13:
2644 + dependencies:
2645 + '@vue/compiler-dom': 3.5.13
2646 + '@vue/compiler-sfc': 3.5.13
2647 + '@vue/runtime-dom': 3.5.13
2648 + '@vue/server-renderer': 3.5.13(vue@3.5.13)
2649 + '@vue/shared': 3.5.13
2650 +
2651 + which@2.0.2:
2652 + dependencies:
2653 + isexe: 2.0.0
2654 +
2655 + word-wrap@1.2.5: {}
2656 +
2657 + wrap-ansi@7.0.0:
2658 + dependencies:
2659 + ansi-styles: 4.3.0
2660 + string-width: 4.2.3
2661 + strip-ansi: 6.0.1
2662 +
2663 + wrap-ansi@8.1.0:
2664 + dependencies:
2665 + ansi-styles: 6.2.1
2666 + string-width: 5.1.2
2667 + strip-ansi: 7.1.0
2668 +
2669 + xml-name-validator@4.0.0: {}
2670 +
2671 + yallist@3.1.1: {}
2672 +
2673 + yaml@2.7.1: {}
2674 +
2675 + yocto-queue@0.1.0: {}
1 +export default {
2 + plugins: {
3 + tailwindcss: {},
4 + autoprefixer: {},
5 + },
6 +}
This file is too large to display.
1 +[
2 + {
3 + "id": "A0001",
4 + "title": "从《了不起的盖茨比》看世界变迁",
5 + "organizer_id": "O0006",
6 + "organizer_name": "百草园读书会",
7 + "description": "与志同道合的读者一起,深入解析《了不起的盖茨比》。\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n活动全程录制,无法准时参加的书友可在后续获取回放。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
8 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_1_online",
9 + "start_time": "2025-04-02 06:13:57",
10 + "end_time": "2025-04-02 07:13:57",
11 + "registration_start": "2025-03-25 05:13:57",
12 + "registration_end": "2025-04-02 05:13:57",
13 + "max_participants": 36,
14 + "participant_count": 30,
15 + "activity_type": "online",
16 + "location": null,
17 + "online_link": "飞书线上会议,会议ID将提前发送",
18 + "is_published": true,
19 + "is_public": true,
20 + "tags": [
21 + "音乐"
22 + ],
23 + "view_count": 515,
24 + "volunteer_positions": [
25 + "摄影记录",
26 + "签到助手"
27 + ],
28 + "created_at": "2025-03-23 05:13:57",
29 + "updated_at": "2025-03-25 21:13:57",
30 + "state": "past"
31 + },
32 + {
33 + "id": "A0002",
34 + "title": "与作者对话:《了不起的盖茨比》背后的故事",
35 + "organizer_id": "O0003",
36 + "organizer_name": "知行合一读书社",
37 + "description": "想要探索《了不起的盖茨比》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
38 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_2_offline",
39 + "start_time": "2025-03-24 06:13:57",
40 + "end_time": "2025-03-24 07:13:57",
41 + "registration_start": "2025-03-12 01:13:57",
42 + "registration_end": "2025-03-24 01:13:57",
43 + "max_participants": 56,
44 + "participant_count": 50,
45 + "activity_type": "offline",
46 + "location": {
47 + "name": "深蓝咖啡馆",
48 + "address": "深圳市南山区科技园南区科苑路15号",
49 + "city": "深圳"
50 + },
51 + "online_link": null,
52 + "is_published": true,
53 + "is_public": true,
54 + "tags": [
55 + "艺术",
56 + "政治"
57 + ],
58 + "view_count": 380,
59 + "volunteer_positions": [
60 + "设备维护",
61 + "资料分发",
62 + "场地布置",
63 + "讨论主持"
64 + ],
65 + "created_at": "2025-03-11 01:13:57",
66 + "updated_at": "2025-03-12 22:13:57",
67 + "state": "past"
68 + },
69 + {
70 + "id": "A0003",
71 + "title": "《瓦尔登湖》读书分享会",
72 + "organizer_id": "O0010",
73 + "organizer_name": "青禾读书会",
74 + "description": "本次读书会将围绕《瓦尔登湖》展开深入讨论。\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n活动全程录制,无法准时参加的书友可在后续获取回放。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
75 + "cover_image": "/assets/images/activity_3_online.jpg",
76 + "start_time": "2025-04-18 07:13:57",
77 + "end_time": "2025-04-18 10:13:57",
78 + "registration_start": "2025-03-25 09:13:57",
79 + "registration_end": "2025-04-17 09:13:57",
80 + "max_participants": 33,
81 + "participant_count": 6,
82 + "activity_type": "online",
83 + "location": null,
84 + "online_link": "腾讯会议线上会议,会议ID将提前发送",
85 + "is_published": true,
86 + "is_public": true,
87 + "tags": [
88 + "亲子阅读",
89 + "政治"
90 + ],
91 + "view_count": 97,
92 + "volunteer_positions": [
93 + "活动总结",
94 + "设备维护",
95 + "场地布置",
96 + "直播助理"
97 + ],
98 + "created_at": "2025-03-24 09:13:57",
99 + "updated_at": "2025-03-26 02:13:57",
100 + "state": "upcoming"
101 + },
102 + {
103 + "id": "A0004",
104 + "title": "从《围城》看世界变迁",
105 + "organizer_id": "O0009",
106 + "organizer_name": "四季阅读空间",
107 + "description": "想要探索《围城》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
108 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_4_offline",
109 + "start_time": "2025-06-22 07:13:57",
110 + "end_time": "2025-06-22 08:13:57",
111 + "registration_start": "2025-06-12 03:13:57",
112 + "registration_end": "2025-06-22 03:13:57",
113 + "max_participants": 60,
114 + "participant_count": 5,
115 + "activity_type": "offline",
116 + "location": {
117 + "name": "大雁塔文化空间",
118 + "address": "西安市雁塔区雁塔西路68号",
119 + "city": "西安"
120 + },
121 + "online_link": null,
122 + "is_published": true,
123 + "is_public": true,
124 + "tags": [
125 + "推理"
126 + ],
127 + "view_count": 68,
128 + "volunteer_positions": [
129 + "设备维护",
130 + "场地布置",
131 + "讨论主持"
132 + ],
133 + "created_at": "2025-06-09 03:13:57",
134 + "updated_at": "2025-06-12 18:13:57",
135 + "state": "upcoming"
136 + },
137 + {
138 + "id": "A0005",
139 + "title": "青年读书会:共读《月亮与六便士》",
140 + "organizer_id": "O0009",
141 + "organizer_name": "四季阅读空间",
142 + "description": "与志同道合的读者一起,深入解析《月亮与六便士》。\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n活动结束后有半小时自由交流时间,欢迎书友们互相认识。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
143 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_5_offline",
144 + "start_time": "2025-03-31 06:13:57",
145 + "end_time": "2025-03-31 07:13:57",
146 + "registration_start": "2025-03-21 23:13:57",
147 + "registration_end": "2025-03-30 23:13:57",
148 + "max_participants": 67,
149 + "participant_count": 62,
150 + "activity_type": "offline",
151 + "location": {
152 + "name": "大雁塔文化空间",
153 + "address": "西安市雁塔区雁塔西路68号",
154 + "city": "西安"
155 + },
156 + "online_link": null,
157 + "is_published": true,
158 + "is_public": false,
159 + "tags": [
160 + "健康生活",
161 + "经济"
162 + ],
163 + "view_count": 963,
164 + "volunteer_positions": [
165 + "设备维护"
166 + ],
167 + "created_at": "2025-03-18 23:13:57",
168 + "updated_at": "2025-03-22 03:13:57",
169 + "state": "past"
170 + },
171 + {
172 + "id": "A0006",
173 + "title": "《三体》的艺术赏析",
174 + "organizer_id": "O0008",
175 + "organizer_name": "墨香文化传媒",
176 + "description": "想要探索《三体》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n线上活动无地域限制,请提前调试好设备,确保网络流畅。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
177 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_6_online",
178 + "start_time": "2025-05-18 07:13:57",
179 + "end_time": "2025-05-18 10:13:57",
180 + "registration_start": "2025-05-01 15:13:57",
181 + "registration_end": "2025-05-17 15:13:57",
182 + "max_participants": 61,
183 + "participant_count": 9,
184 + "activity_type": "online",
185 + "location": null,
186 + "online_link": "微信视频线上会议,会议ID将提前发送",
187 + "is_published": true,
188 + "is_public": true,
189 + "tags": [
190 + "文学"
191 + ],
192 + "view_count": 77,
193 + "volunteer_positions": [
194 + "秩序维持",
195 + "场地布置",
196 + "直播助理",
197 + "活动总结",
198 + "资料分发"
199 + ],
200 + "created_at": "2025-04-28 15:13:57",
201 + "updated_at": "2025-05-02 09:13:57",
202 + "state": "upcoming"
203 + },
204 + {
205 + "id": "A0007",
206 + "title": "深度解读《活着》",
207 + "organizer_id": "O0006",
208 + "organizer_name": "百草园读书会",
209 + "description": "与志同道合的读者一起,深入解析《活着》。\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
210 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_7_offline",
211 + "start_time": "2025-04-03 05:13:57",
212 + "end_time": "2025-04-03 07:13:57",
213 + "registration_start": "2025-03-28 17:13:57",
214 + "registration_end": "2025-04-02 17:13:57",
215 + "max_participants": 30,
216 + "participant_count": 28,
217 + "activity_type": "offline",
218 + "location": {
219 + "name": "熊猫书房",
220 + "address": "成都市锦江区红星路三段99号",
221 + "city": "成都"
222 + },
223 + "online_link": null,
224 + "is_published": true,
225 + "is_public": true,
226 + "tags": [
227 + "科技",
228 + "经济"
229 + ],
230 + "view_count": 895,
231 + "volunteer_positions": [
232 + "秩序维持"
233 + ],
234 + "created_at": "2025-03-25 17:13:57",
235 + "updated_at": "2025-03-29 01:13:57",
236 + "state": "past"
237 + },
238 + {
239 + "id": "A0008",
240 + "title": "《月亮与六便士》的哲学思考",
241 + "organizer_id": "O0001",
242 + "organizer_name": "光合作用文化空间",
243 + "description": "本次读书会将围绕《月亮与六便士》展开深入讨论。\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
244 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_8_offline",
245 + "start_time": "2025-04-16 05:30:57",
246 + "end_time": "2025-04-16 07:30:57",
247 + "registration_start": "2025-04-11 05:30:57",
248 + "registration_end": "2025-04-16 05:30:57",
249 + "max_participants": 38,
250 + "participant_count": 18,
251 + "activity_type": "offline",
252 + "location": {
253 + "name": "紫金读书会",
254 + "address": "南京市鼓楼区中山路99号",
255 + "city": "南京"
256 + },
257 + "online_link": null,
258 + "is_published": true,
259 + "is_public": true,
260 + "tags": [
261 + "心灵成长"
262 + ],
263 + "view_count": 207,
264 + "volunteer_positions": [
265 + "直播助理"
266 + ],
267 + "created_at": "2025-04-10 05:30:57",
268 + "updated_at": "2025-04-12 00:30:57",
269 + "state": "ongoing"
270 + },
271 + {
272 + "id": "A0009",
273 + "title": "深度解读《瓦尔登湖》",
274 + "organizer_id": "O0001",
275 + "organizer_name": "光合作用文化空间",
276 + "description": "与志同道合的读者一起,深入解析《瓦尔登湖》。\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
277 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_9_offline",
278 + "start_time": "2025-05-30 07:13:57",
279 + "end_time": "2025-05-30 10:13:57",
280 + "registration_start": "2025-05-12 17:13:57",
281 + "registration_end": "2025-05-29 17:13:57",
282 + "max_participants": 52,
283 + "participant_count": 14,
284 + "activity_type": "offline",
285 + "location": {
286 + "name": "紫金读书会",
287 + "address": "南京市鼓楼区中山路99号",
288 + "city": "南京"
289 + },
290 + "online_link": null,
291 + "is_published": true,
292 + "is_public": false,
293 + "tags": [
294 + "哲学"
295 + ],
296 + "view_count": 69,
297 + "volunteer_positions": [
298 + "直播助理",
299 + "场地布置",
300 + "资料分发",
301 + "秩序维持",
302 + "签到助手"
303 + ],
304 + "created_at": "2025-05-07 17:13:57",
305 + "updated_at": "2025-05-13 07:13:57",
306 + "state": "upcoming"
307 + },
308 + {
309 + "id": "A0010",
310 + "title": "从《月亮与六便士》看世界变迁",
311 + "organizer_id": "O0002",
312 + "organizer_name": "四季阅读空间",
313 + "description": "与志同道合的读者一起,深入解析《月亮与六便士》。\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
314 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_10_offline",
315 + "start_time": "2025-04-30 07:13:57",
316 + "end_time": "2025-04-30 10:13:57",
317 + "registration_start": "2025-04-16 13:13:57",
318 + "registration_end": "2025-04-29 13:13:57",
319 + "max_participants": 76,
320 + "participant_count": 7,
321 + "activity_type": "offline",
322 + "location": {
323 + "name": "城市中心图书馆",
324 + "address": "北京市海淀区中关村南大街5号",
325 + "city": "北京"
326 + },
327 + "online_link": null,
328 + "is_published": true,
329 + "is_public": true,
330 + "tags": [
331 + "古典文学",
332 + "政治"
333 + ],
334 + "view_count": 59,
335 + "volunteer_positions": [
336 + "签到助手",
337 + "茶点准备",
338 + "设备维护",
339 + "资料分发"
340 + ],
341 + "created_at": "2025-04-14 13:13:57",
342 + "updated_at": "2025-04-17 06:13:57",
343 + "state": "upcoming"
344 + },
345 + {
346 + "id": "A0011",
347 + "title": "《人类简史》的艺术赏析",
348 + "organizer_id": "O0009",
349 + "organizer_name": "四季阅读空间",
350 + "description": "想要探索《人类简史》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
351 + "cover_image": "/assets/images/t2Sai-AqIpI_activity11.jpg",
352 + "start_time": "2025-04-17 07:13:57",
353 + "end_time": "2025-04-17 08:13:57",
354 + "registration_start": "2025-03-22 19:13:57",
355 + "registration_end": "2025-04-16 19:13:57",
356 + "max_participants": 47,
357 + "participant_count": 12,
358 + "activity_type": "offline",
359 + "location": {
360 + "name": "大雁塔文化空间",
361 + "address": "西安市雁塔区雁塔西路68号",
362 + "city": "西安"
363 + },
364 + "online_link": null,
365 + "is_published": true,
366 + "is_public": true,
367 + "tags": [
368 + "政治",
369 + "健康",
370 + "传记"
371 + ],
372 + "view_count": 61,
373 + "volunteer_positions": [],
374 + "created_at": "2025-03-21 19:13:57",
375 + "updated_at": "2025-03-23 05:13:57",
376 + "state": "upcoming"
377 + },
378 + {
379 + "id": "A0012",
380 + "title": "与作者对话:《小王子》背后的故事",
381 + "organizer_id": "O0001",
382 + "organizer_name": "光合作用文化空间",
383 + "description": "本次读书会将围绕《小王子》展开深入讨论。\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n活动全程录制,无法准时参加的书友可在后续获取回放。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
384 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_12_online",
385 + "start_time": "2025-04-04 05:13:57",
386 + "end_time": "2025-04-04 07:13:57",
387 + "registration_start": "2025-03-27 21:13:57",
388 + "registration_end": "2025-04-03 21:13:57",
389 + "max_participants": 93,
390 + "participant_count": 92,
391 + "activity_type": "online",
392 + "location": null,
393 + "online_link": "飞书线上会议,会议ID将提前发送",
394 + "is_published": true,
395 + "is_public": true,
396 + "tags": [
397 + "环保",
398 + "哲学"
399 + ],
400 + "view_count": 342,
401 + "volunteer_positions": [
402 + "秩序维持",
403 + "茶点准备",
404 + "签到助手",
405 + "摄影记录",
406 + "资料分发"
407 + ],
408 + "created_at": "2025-03-23 21:13:57",
409 + "updated_at": "2025-03-28 17:13:57",
410 + "state": "past"
411 + },
412 + {
413 + "id": "A0013",
414 + "title": "与作者对话:《红楼梦》背后的故事",
415 + "organizer_id": "O0003",
416 + "organizer_name": "知行合一读书社",
417 + "description": "本次读书会将围绕《红楼梦》展开深入讨论。\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
418 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_13_offline",
419 + "start_time": "2025-04-16 06:02:57",
420 + "end_time": "2025-04-16 08:02:57",
421 + "registration_start": "2025-04-10 06:02:57",
422 + "registration_end": "2025-04-16 06:02:57",
423 + "max_participants": 89,
424 + "participant_count": 55,
425 + "activity_type": "offline",
426 + "location": {
427 + "name": "深蓝咖啡馆",
428 + "address": "深圳市南山区科技园南区科苑路15号",
429 + "city": "深圳"
430 + },
431 + "online_link": null,
432 + "is_published": true,
433 + "is_public": true,
434 + "tags": [
435 + "管理",
436 + "哲学"
437 + ],
438 + "view_count": 248,
439 + "volunteer_positions": [],
440 + "created_at": "2025-04-06 06:02:57",
441 + "updated_at": "2025-04-10 15:02:57",
442 + "state": "ongoing"
443 + },
444 + {
445 + "id": "A0014",
446 + "title": "与作者对话:《白夜行》背后的故事",
447 + "organizer_id": "O0003",
448 + "organizer_name": "知行合一读书社",
449 + "description": "本次读书会将围绕《白夜行》展开深入讨论。\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n参与者将通过视频会议软件连接,链接将在活动开始前发送。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
450 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_14_online",
451 + "start_time": "2025-05-23 07:13:57",
452 + "end_time": "2025-05-23 08:13:57",
453 + "registration_start": "2025-05-09 20:13:57",
454 + "registration_end": "2025-05-22 20:13:57",
455 + "max_participants": 28,
456 + "participant_count": 5,
457 + "activity_type": "online",
458 + "location": null,
459 + "online_link": "微信视频线上会议,会议ID将提前发送",
460 + "is_published": true,
461 + "is_public": true,
462 + "tags": [
463 + "哲学",
464 + "外国文学"
465 + ],
466 + "view_count": 53,
467 + "volunteer_positions": [
468 + "设备维护",
469 + "秩序维持"
470 + ],
471 + "created_at": "2025-05-07 20:13:57",
472 + "updated_at": "2025-05-09 21:13:57",
473 + "state": "upcoming"
474 + },
475 + {
476 + "id": "A0015",
477 + "title": "《思考,快与慢》的哲学思考",
478 + "organizer_id": "O0003",
479 + "organizer_name": "知行合一读书社",
480 + "description": "本次读书会将围绕《思考,快与慢》展开深入讨论。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n参与者将通过视频会议软件连接,链接将在活动开始前发送。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
481 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_15_online",
482 + "start_time": "2025-04-16 06:02:57",
483 + "end_time": "2025-04-16 09:02:57",
484 + "registration_start": "2025-04-12 06:02:57",
485 + "registration_end": "2025-04-16 06:02:57",
486 + "max_participants": 21,
487 + "participant_count": 12,
488 + "activity_type": "online",
489 + "location": null,
490 + "online_link": "腾讯会议线上会议,会议ID将提前发送",
491 + "is_published": true,
492 + "is_public": true,
493 + "tags": [
494 + "心理学",
495 + "环保"
496 + ],
497 + "view_count": 103,
498 + "volunteer_positions": [],
499 + "created_at": "2025-04-07 06:02:57",
500 + "updated_at": "2025-04-12 13:02:57",
501 + "state": "ongoing"
502 + },
503 + {
504 + "id": "A0016",
505 + "title": "《自控力》的艺术赏析",
506 + "organizer_id": "O0002",
507 + "organizer_name": "四季阅读空间",
508 + "description": "本次读书会将围绕《自控力》展开深入讨论。\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
509 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_16_offline",
510 + "start_time": "2025-07-01 07:13:57",
511 + "end_time": "2025-07-01 08:13:57",
512 + "registration_start": "2025-06-15 13:13:57",
513 + "registration_end": "2025-06-30 13:13:57",
514 + "max_participants": 48,
515 + "participant_count": 2,
516 + "activity_type": "offline",
517 + "location": {
518 + "name": "城市中心图书馆",
519 + "address": "北京市海淀区中关村南大街5号",
520 + "city": "北京"
521 + },
522 + "online_link": null,
523 + "is_published": true,
524 + "is_public": true,
525 + "tags": [
526 + "历史文化"
527 + ],
528 + "view_count": 90,
529 + "volunteer_positions": [
530 + "茶点准备",
531 + "场地布置",
532 + "活动总结",
533 + "设备维护",
534 + "签到助手"
535 + ],
536 + "created_at": "2025-06-11 13:13:57",
537 + "updated_at": "2025-06-16 10:13:57",
538 + "state": "upcoming"
539 + },
540 + {
541 + "id": "A0017",
542 + "title": "经典重温:《解忧杂货店》",
543 + "organizer_id": "O0006",
544 + "organizer_name": "百草园读书会",
545 + "description": "诚挚邀请书友们参加《解忧杂货店》读书分享会。\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n线上活动无地域限制,请提前调试好设备,确保网络流畅。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
546 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_17_online",
547 + "start_time": "2025-06-15 07:13:57",
548 + "end_time": "2025-06-15 10:13:57",
549 + "registration_start": "2025-05-27 02:13:57",
550 + "registration_end": "2025-06-15 02:13:57",
551 + "max_participants": 75,
552 + "participant_count": 9,
553 + "activity_type": "online",
554 + "location": null,
555 + "online_link": "飞书线上会议,会议ID将提前发送",
556 + "is_published": true,
557 + "is_public": true,
558 + "tags": [
559 + "文学",
560 + "科技",
561 + "健康"
562 + ],
563 + "view_count": 34,
564 + "volunteer_positions": [],
565 + "created_at": "2025-05-26 02:13:57",
566 + "updated_at": "2025-05-27 15:13:57",
567 + "state": "upcoming"
568 + },
569 + {
570 + "id": "A0018",
571 + "title": "从《未来简史》看世界变迁",
572 + "organizer_id": "O0003",
573 + "organizer_name": "知行合一读书社",
574 + "description": "与志同道合的读者一起,深入解析《未来简史》。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n线上活动无地域限制,请提前调试好设备,确保网络流畅。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
575 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_18_online",
576 + "start_time": "2025-06-14 07:13:57",
577 + "end_time": "2025-06-14 09:13:57",
578 + "registration_start": "2025-06-04 02:13:57",
579 + "registration_end": "2025-06-14 02:13:57",
580 + "max_participants": 72,
581 + "participant_count": 12,
582 + "activity_type": "online",
583 + "location": null,
584 + "online_link": "飞书线上会议,会议ID将提前发送",
585 + "is_published": true,
586 + "is_public": true,
587 + "tags": [
588 + "诗歌"
589 + ],
590 + "view_count": 27,
591 + "volunteer_positions": [],
592 + "created_at": "2025-05-31 02:13:57",
593 + "updated_at": "2025-06-04 07:13:57",
594 + "state": "upcoming"
595 + },
596 + {
597 + "id": "A0019",
598 + "title": "与作者对话:《月亮与六便士》背后的故事",
599 + "organizer_id": "O0004",
600 + "organizer_name": "百草园读书会",
601 + "description": "诚挚邀请书友们参加《月亮与六便士》读书分享会。\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n线上活动无地域限制,请提前调试好设备,确保网络流畅。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
602 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_19_online",
603 + "start_time": "2025-04-16 05:34:57",
604 + "end_time": "2025-04-16 08:34:57",
605 + "registration_start": "2025-04-11 05:34:57",
606 + "registration_end": "2025-04-16 05:34:57",
607 + "max_participants": 29,
608 + "participant_count": 15,
609 + "activity_type": "online",
610 + "location": null,
611 + "online_link": "钉钉线上会议,会议ID将提前发送",
612 + "is_published": true,
613 + "is_public": false,
614 + "tags": [
615 + "健康",
616 + "科学普及"
617 + ],
618 + "view_count": 74,
619 + "volunteer_positions": [
620 + "摄影记录",
621 + "签到助手",
622 + "设备维护"
623 + ],
624 + "created_at": "2025-04-07 05:34:57",
625 + "updated_at": "2025-04-11 13:34:57",
626 + "state": "ongoing"
627 + },
628 + {
629 + "id": "A0020",
630 + "title": "《心理学与生活》的哲学思考",
631 + "organizer_id": "O0001",
632 + "organizer_name": "光合作用文化空间",
633 + "description": "与志同道合的读者一起,深入解析《心理学与生活》。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n参与者将通过视频会议软件连接,链接将在活动开始前发送。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
634 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_20_online",
635 + "start_time": "2025-06-17 07:13:57",
636 + "end_time": "2025-06-17 10:13:57",
637 + "registration_start": "2025-06-01 10:13:57",
638 + "registration_end": "2025-06-16 10:13:57",
639 + "max_participants": 38,
640 + "participant_count": 2,
641 + "activity_type": "online",
642 + "location": null,
643 + "online_link": "钉钉线上会议,会议ID将提前发送",
644 + "is_published": true,
645 + "is_public": true,
646 + "tags": [
647 + "历史",
648 + "文学",
649 + "管理"
650 + ],
651 + "view_count": 87,
652 + "volunteer_positions": [
653 + "摄影记录",
654 + "资料分发"
655 + ],
656 + "created_at": "2025-05-27 10:13:57",
657 + "updated_at": "2025-06-02 04:13:57",
658 + "state": "upcoming"
659 + },
660 + {
661 + "id": "A0021",
662 + "title": "深度解读《了不起的盖茨比》",
663 + "organizer_id": "O0007",
664 + "organizer_name": "百草园读书会",
665 + "description": "诚挚邀请书友们参加《了不起的盖茨比》读书分享会。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n参与者将通过视频会议软件连接,链接将在活动开始前发送。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
666 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_21_online",
667 + "start_time": "2025-05-04 07:13:57",
668 + "end_time": "2025-05-04 09:13:57",
669 + "registration_start": "2025-04-16 21:13:57",
670 + "registration_end": "2025-05-03 21:13:57",
671 + "max_participants": 75,
672 + "participant_count": 15,
673 + "activity_type": "online",
674 + "location": null,
675 + "online_link": "Zoom线上会议,会议ID将提前发送",
676 + "is_published": true,
677 + "is_public": false,
678 + "tags": [
679 + "历史",
680 + "心灵成长"
681 + ],
682 + "view_count": 49,
683 + "volunteer_positions": [],
684 + "created_at": "2025-04-12 21:13:57",
685 + "updated_at": "2025-04-17 08:13:57",
686 + "state": "upcoming"
687 + },
688 + {
689 + "id": "A0022",
690 + "title": "青年读书会:共读《未来简史》",
691 + "organizer_id": "O0005",
692 + "organizer_name": "鹿鸣书店",
693 + "description": "诚挚邀请书友们参加《未来简史》读书分享会。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
694 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_22_offline",
695 + "start_time": "2025-04-16 06:31:57",
696 + "end_time": "2025-04-16 07:31:57",
697 + "registration_start": "2025-04-02 06:31:57",
698 + "registration_end": "2025-04-16 06:31:57",
699 + "max_participants": 78,
700 + "participant_count": 46,
701 + "activity_type": "offline",
702 + "location": {
703 + "name": "大雁塔文化空间",
704 + "address": "西安市雁塔区雁塔西路68号",
705 + "city": "西安"
706 + },
707 + "online_link": null,
708 + "is_published": true,
709 + "is_public": true,
710 + "tags": [
711 + "管理"
712 + ],
713 + "view_count": 74,
714 + "volunteer_positions": [
715 + "设备维护",
716 + "直播助理",
717 + "资料分发"
718 + ],
719 + "created_at": "2025-04-01 06:31:57",
720 + "updated_at": "2025-04-02 23:31:57",
721 + "state": "ongoing"
722 + },
723 + {
724 + "id": "A0023",
725 + "title": "《万物简史》的哲学思考",
726 + "organizer_id": "O0007",
727 + "organizer_name": "百草园读书会",
728 + "description": "诚挚邀请书友们参加《万物简史》读书分享会。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n参与者将通过视频会议软件连接,链接将在活动开始前发送。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
729 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_23_online",
730 + "start_time": "2025-03-27 05:13:57",
731 + "end_time": "2025-03-27 07:13:57",
732 + "registration_start": "2025-03-22 02:13:57",
733 + "registration_end": "2025-03-27 02:13:57",
734 + "max_participants": 47,
735 + "participant_count": 38,
736 + "activity_type": "online",
737 + "location": null,
738 + "online_link": "飞书线上会议,会议ID将提前发送",
739 + "is_published": true,
740 + "is_public": true,
741 + "tags": [
742 + "外国文学",
743 + "历史",
744 + "历史文化"
745 + ],
746 + "view_count": 552,
747 + "volunteer_positions": [
748 + "设备维护",
749 + "秩序维持",
750 + "摄影记录",
751 + "活动总结"
752 + ],
753 + "created_at": "2025-03-19 02:13:57",
754 + "updated_at": "2025-03-23 00:13:57",
755 + "state": "past"
756 + },
757 + {
758 + "id": "A0024",
759 + "title": "跨界思考:《活着》与现代社会",
760 + "organizer_id": "O0003",
761 + "organizer_name": "知行合一读书社",
762 + "description": "想要探索《活着》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
763 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_24_offline",
764 + "start_time": "2025-05-21 07:13:57",
765 + "end_time": "2025-05-21 09:13:57",
766 + "registration_start": "2025-04-22 23:13:57",
767 + "registration_end": "2025-05-20 23:13:57",
768 + "max_participants": 77,
769 + "participant_count": 11,
770 + "activity_type": "offline",
771 + "location": {
772 + "name": "深蓝咖啡馆",
773 + "address": "深圳市南山区科技园南区科苑路15号",
774 + "city": "深圳"
775 + },
776 + "online_link": null,
777 + "is_published": true,
778 + "is_public": false,
779 + "tags": [
780 + "环保"
781 + ],
782 + "view_count": 61,
783 + "volunteer_positions": [
784 + "茶点准备",
785 + "秩序维持"
786 + ],
787 + "created_at": "2025-04-20 23:13:57",
788 + "updated_at": "2025-04-23 12:13:57",
789 + "state": "upcoming"
790 + },
791 + {
792 + "id": "A0025",
793 + "title": "经典重温:《活着》",
794 + "organizer_id": "O0009",
795 + "organizer_name": "四季阅读空间",
796 + "description": "与志同道合的读者一起,深入解析《活着》。\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n每一次阅读都是新的开始,每一次讨论都是思想的盛宴。",
797 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_25_offline",
798 + "start_time": "2025-05-08 07:13:57",
799 + "end_time": "2025-05-08 09:13:57",
800 + "registration_start": "2025-04-22 09:13:57",
801 + "registration_end": "2025-05-07 09:13:57",
802 + "max_participants": 83,
803 + "participant_count": 11,
804 + "activity_type": "offline",
805 + "location": {
806 + "name": "大雁塔文化空间",
807 + "address": "西安市雁塔区雁塔西路68号",
808 + "city": "西安"
809 + },
810 + "online_link": null,
811 + "is_published": true,
812 + "is_public": true,
813 + "tags": [
814 + "经济"
815 + ],
816 + "view_count": 30,
817 + "volunteer_positions": [
818 + "讨论主持",
819 + "资料分发",
820 + "摄影记录",
821 + "秩序维持",
822 + "场地布置"
823 + ],
824 + "created_at": "2025-04-20 09:13:57",
825 + "updated_at": "2025-04-23 06:13:57",
826 + "state": "upcoming"
827 + },
828 + {
829 + "id": "A0026",
830 + "title": "《未来简史》的哲学思考",
831 + "organizer_id": "O0007",
832 + "organizer_name": "百草园读书会",
833 + "description": "想要探索《未来简史》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n活动将邀请资深书评人进行导读,随后开展自由讨论与交流。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
834 + "cover_image": "/assets/images/XqXJJhK-c08.jpg",
835 + "start_time": "2025-04-29 07:13:57",
836 + "end_time": "2025-04-29 09:13:57",
837 + "registration_start": "2025-04-03 10:13:57",
838 + "registration_end": "2025-04-28 10:13:57",
839 + "max_participants": 90,
840 + "participant_count": 9,
841 + "activity_type": "offline",
842 + "location": {
843 + "name": "紫金读书会",
844 + "address": "南京市鼓楼区中山路99号",
845 + "city": "南京"
846 + },
847 + "online_link": null,
848 + "is_published": true,
849 + "is_public": true,
850 + "tags": [
851 + "健康生活"
852 + ],
853 + "view_count": 60,
854 + "volunteer_positions": [],
855 + "created_at": "2025-03-29 10:13:57",
856 + "updated_at": "2025-04-03 22:13:57",
857 + "state": "upcoming"
858 + },
859 + {
860 + "id": "A0027",
861 + "title": "跨界思考:《百年孤独》与现代社会",
862 + "organizer_id": "O0005",
863 + "organizer_name": "鹿鸣书店",
864 + "description": "诚挚邀请书友们参加《百年孤独》读书分享会。\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n场地座位有限,请提前报名,准时到场。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
865 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_27_offline",
866 + "start_time": "2025-04-16 05:21:57",
867 + "end_time": "2025-04-16 08:21:57",
868 + "registration_start": "2025-04-11 05:21:57",
869 + "registration_end": "2025-04-16 05:21:57",
870 + "max_participants": 23,
871 + "participant_count": 15,
872 + "activity_type": "offline",
873 + "location": {
874 + "name": "大雁塔文化空间",
875 + "address": "西安市雁塔区雁塔西路68号",
876 + "city": "西安"
877 + },
878 + "online_link": null,
879 + "is_published": true,
880 + "is_public": true,
881 + "tags": [
882 + "社会科学",
883 + "传记"
884 + ],
885 + "view_count": 248,
886 + "volunteer_positions": [],
887 + "created_at": "2025-04-10 05:21:57",
888 + "updated_at": "2025-04-11 06:21:57",
889 + "state": "ongoing"
890 + },
891 + {
892 + "id": "A0028",
893 + "title": "跨界思考:《追风筝的人》与现代社会",
894 + "organizer_id": "O0008",
895 + "organizer_name": "墨香文化传媒",
896 + "description": "想要探索《追风筝的人》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n除了对内容的讨论,我们还将探索作者创作背景与写作技巧。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n现场提供免费茶点,请携带纸质书籍或笔记参加。\n\n期待您的参与,共同度过一个充满思想碰撞的美好时光!",
897 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_28_offline",
898 + "start_time": "2025-05-27 07:13:57",
899 + "end_time": "2025-05-27 10:13:57",
900 + "registration_start": "2025-05-06 17:13:57",
901 + "registration_end": "2025-05-26 17:13:57",
902 + "max_participants": 86,
903 + "participant_count": 15,
904 + "activity_type": "offline",
905 + "location": {
906 + "name": "大雁塔文化空间",
907 + "address": "西安市雁塔区雁塔西路68号",
908 + "city": "西安"
909 + },
910 + "online_link": null,
911 + "is_published": true,
912 + "is_public": true,
913 + "tags": [
914 + "健康",
915 + "音乐"
916 + ],
917 + "view_count": 98,
918 + "volunteer_positions": [
919 + "场地布置"
920 + ],
921 + "created_at": "2025-05-04 17:13:57",
922 + "updated_at": "2025-05-06 18:13:57",
923 + "state": "upcoming"
924 + },
925 + {
926 + "id": "A0029",
927 + "title": "《了不起的盖茨比》的艺术赏析",
928 + "organizer_id": "O0005",
929 + "organizer_name": "鹿鸣书店",
930 + "description": "想要探索《了不起的盖茨比》的深层含义?欢迎加入我们的读书会!\n\n【活动内容】\n通过分组讨论、角色扮演等互动形式,全方位解读这部作品。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n线上活动无地域限制,请提前调试好设备,确保网络流畅。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
931 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_29_online",
932 + "start_time": "2025-03-18 05:13:57",
933 + "end_time": "2025-03-18 07:13:57",
934 + "registration_start": "2025-03-06 01:13:57",
935 + "registration_end": "2025-03-18 01:13:57",
936 + "max_participants": 42,
937 + "participant_count": 40,
938 + "activity_type": "online",
939 + "location": null,
940 + "online_link": "Zoom线上会议,会议ID将提前发送",
941 + "is_published": true,
942 + "is_public": false,
943 + "tags": [
944 + "古典文学",
945 + "哲学",
946 + "健康"
947 + ],
948 + "view_count": 772,
949 + "volunteer_positions": [],
950 + "created_at": "2025-03-05 01:13:57",
951 + "updated_at": "2025-03-06 07:13:57",
952 + "state": "past"
953 + },
954 + {
955 + "id": "A0030",
956 + "title": "从《活着》看世界变迁",
957 + "organizer_id": "O0005",
958 + "organizer_name": "鹿鸣书店",
959 + "description": "本次读书会将围绕《活着》展开深入讨论。\n\n【活动内容】\n我们将共同探讨书中的核心思想,分享阅读感悟,碰撞思维火花。\n\n【活动安排】\n1. 开场与活动介绍(15分钟)2. 作品导读(30分钟)3. 分组讨论(45分钟)4. 交流分享(30分钟)5. 问答与总结(15分钟)\n\n【参与须知】\n活动结束后有半小时自由交流时间,欢迎书友们互相认识。\n\n让我们在书香中相遇,在思想的海洋里遨游。",
960 + "cover_image": "https://via.placeholder.com/1600x900.png?text=Activity_30_offline",
961 + "start_time": "2025-03-24 06:13:57",
962 + "end_time": "2025-03-24 07:13:57",
963 + "registration_start": "2025-03-13 02:13:57",
964 + "registration_end": "2025-03-24 02:13:57",
965 + "max_participants": 64,
966 + "participant_count": 60,
967 + "activity_type": "offline",
968 + "location": {
969 + "name": "大雁塔文化空间",
970 + "address": "西安市雁塔区雁塔西路68号",
971 + "city": "西安"
972 + },
973 + "online_link": null,
974 + "is_published": true,
975 + "is_public": true,
976 + "tags": [
977 + "教育"
978 + ],
979 + "view_count": 881,
980 + "volunteer_positions": [
981 + "签到助手",
982 + "茶点准备",
983 + "场地布置",
984 + "活动总结"
985 + ],
986 + "created_at": "2025-03-11 02:13:57",
987 + "updated_at": "2025-03-13 12:13:57",
988 + "state": "past"
989 + }
990 +]
...\ No newline at end of file ...\ No newline at end of file
1 +[
2 + {
3 + "id": "C0001",
4 + "activity_id": "A0001",
5 + "user_id": "U0010",
6 + "registration_id": "R0001",
7 + "checkin_time": "2025-04-02 06:15:17",
8 + "checkin_type": "QR code",
9 + "location": null,
10 + "staff_id": null,
11 + "status": "successful",
12 + "notes": "",
13 + "is_late": false
14 + },
15 + {
16 + "id": "C0002",
17 + "activity_id": "A0001",
18 + "user_id": "U0020",
19 + "registration_id": "R0002",
20 + "checkin_time": "2025-04-02 05:59:08",
21 + "checkin_type": "QR code",
22 + "location": null,
23 + "staff_id": null,
24 + "status": "successful",
25 + "notes": "",
26 + "is_late": false
27 + },
28 + {
29 + "id": "C0003",
30 + "activity_id": "A0001",
31 + "user_id": "U0032",
32 + "registration_id": "R0004",
33 + "checkin_time": "2025-04-02 06:22:39",
34 + "checkin_type": "QR code",
35 + "location": null,
36 + "staff_id": null,
37 + "status": "successful",
38 + "notes": "",
39 + "is_late": true
40 + },
41 + {
42 + "id": "C0004",
43 + "activity_id": "A0001",
44 + "user_id": "U0025",
45 + "registration_id": "R0005",
46 + "checkin_time": "2025-04-02 06:27:22",
47 + "checkin_type": "QR code",
48 + "location": null,
49 + "staff_id": null,
50 + "status": "successful",
51 + "notes": "",
52 + "is_late": false
53 + },
54 + {
55 + "id": "C0005",
56 + "activity_id": "A0001",
57 + "user_id": "U0012",
58 + "registration_id": "R0006",
59 + "checkin_time": "2025-04-02 06:20:42",
60 + "checkin_type": "QR code",
61 + "location": null,
62 + "staff_id": null,
63 + "status": "successful",
64 + "notes": "",
65 + "is_late": true
66 + },
67 + {
68 + "id": "C0006",
69 + "activity_id": "A0001",
70 + "user_id": "U0034",
71 + "registration_id": "R0008",
72 + "checkin_time": "2025-04-02 06:31:33",
73 + "checkin_type": "QR code",
74 + "location": null,
75 + "staff_id": null,
76 + "status": "successful",
77 + "notes": "",
78 + "is_late": false
79 + },
80 + {
81 + "id": "C0007",
82 + "activity_id": "A0001",
83 + "user_id": "U0035",
84 + "registration_id": "R0010",
85 + "checkin_time": "2025-04-02 06:43:04",
86 + "checkin_type": "QR code",
87 + "location": null,
88 + "staff_id": null,
89 + "status": "successful",
90 + "notes": "",
91 + "is_late": false
92 + },
93 + {
94 + "id": "C0008",
95 + "activity_id": "A0001",
96 + "user_id": "U0008",
97 + "registration_id": "R0011",
98 + "checkin_time": "2025-04-02 06:24:37",
99 + "checkin_type": "QR code",
100 + "location": null,
101 + "staff_id": null,
102 + "status": "successful",
103 + "notes": "带了书本",
104 + "is_late": false
105 + },
106 + {
107 + "id": "C0009",
108 + "activity_id": "A0001",
109 + "user_id": "U0016",
110 + "registration_id": "R0012",
111 + "checkin_time": "2025-04-02 06:09:02",
112 + "checkin_type": "QR code",
113 + "location": null,
114 + "staff_id": null,
115 + "status": "successful",
116 + "notes": "",
117 + "is_late": false
118 + },
119 + {
120 + "id": "C0010",
121 + "activity_id": "A0001",
122 + "user_id": "U0009",
123 + "registration_id": "R0014",
124 + "checkin_time": "2025-04-02 06:40:18",
125 + "checkin_type": "QR code",
126 + "location": null,
127 + "staff_id": null,
128 + "status": "successful",
129 + "notes": "带了书本",
130 + "is_late": false
131 + },
132 + {
133 + "id": "C0011",
134 + "activity_id": "A0001",
135 + "user_id": "U0015",
136 + "registration_id": "R0015",
137 + "checkin_time": "2025-04-02 05:59:41",
138 + "checkin_type": "QR code",
139 + "location": null,
140 + "staff_id": null,
141 + "status": "successful",
142 + "notes": "",
143 + "is_late": false
144 + },
145 + {
146 + "id": "C0012",
147 + "activity_id": "A0001",
148 + "user_id": "U0019",
149 + "registration_id": "R0016",
150 + "checkin_time": "2025-04-02 06:11:07",
151 + "checkin_type": "QR code",
152 + "location": null,
153 + "staff_id": null,
154 + "status": "successful",
155 + "notes": "",
156 + "is_late": false
157 + },
158 + {
159 + "id": "C0013",
160 + "activity_id": "A0001",
161 + "user_id": "U0033",
162 + "registration_id": "R0017",
163 + "checkin_time": "2025-04-02 06:24:37",
164 + "checkin_type": "QR code",
165 + "location": null,
166 + "staff_id": null,
167 + "status": "successful",
168 + "notes": "",
169 + "is_late": false
170 + },
171 + {
172 + "id": "C0014",
173 + "activity_id": "A0001",
174 + "user_id": "U0001",
175 + "registration_id": "R0018",
176 + "checkin_time": "2025-04-02 05:59:19",
177 + "checkin_type": "QR code",
178 + "location": null,
179 + "staff_id": null,
180 + "status": "successful",
181 + "notes": "热情参与讨论",
182 + "is_late": false
183 + },
184 + {
185 + "id": "C0015",
186 + "activity_id": "A0001",
187 + "user_id": "U0036",
188 + "registration_id": "R0019",
189 + "checkin_time": "2025-04-02 06:06:21",
190 + "checkin_type": "QR code",
191 + "location": null,
192 + "staff_id": null,
193 + "status": "successful",
194 + "notes": "按时到达",
195 + "is_late": false
196 + },
197 + {
198 + "id": "C0016",
199 + "activity_id": "A0001",
200 + "user_id": "U0004",
201 + "registration_id": "R0020",
202 + "checkin_time": "2025-04-02 06:20:20",
203 + "checkin_type": "manual",
204 + "location": null,
205 + "staff_id": "O0006",
206 + "status": "failed",
207 + "notes": "网络连接不稳定",
208 + "is_late": false
209 + },
210 + {
211 + "id": "C0017",
212 + "activity_id": "A0001",
213 + "user_id": "U0029",
214 + "registration_id": "R0022",
215 + "checkin_time": "2025-04-02 06:26:13",
216 + "checkin_type": "QR code",
217 + "location": null,
218 + "staff_id": null,
219 + "status": "successful",
220 + "notes": "",
221 + "is_late": false
222 + },
223 + {
224 + "id": "C0018",
225 + "activity_id": "A0001",
226 + "user_id": "U0031",
227 + "registration_id": "R0023",
228 + "checkin_time": "2025-04-02 06:01:47",
229 + "checkin_type": "self-service",
230 + "location": null,
231 + "staff_id": null,
232 + "status": "successful",
233 + "notes": "",
234 + "is_late": false
235 + },
236 + {
237 + "id": "C0019",
238 + "activity_id": "A0001",
239 + "user_id": "U0011",
240 + "registration_id": "R0024",
241 + "checkin_time": "2025-04-02 06:01:25",
242 + "checkin_type": "self-service",
243 + "location": null,
244 + "staff_id": null,
245 + "status": "successful",
246 + "notes": "",
247 + "is_late": false
248 + },
249 + {
250 + "id": "C0020",
251 + "activity_id": "A0001",
252 + "user_id": "U0040",
253 + "registration_id": "R0027",
254 + "checkin_time": "2025-04-02 06:14:06",
255 + "checkin_type": "self-service",
256 + "location": null,
257 + "staff_id": null,
258 + "status": "successful",
259 + "notes": "",
260 + "is_late": false
261 + },
262 + {
263 + "id": "C0021",
264 + "activity_id": "A0001",
265 + "user_id": "U0003",
266 + "registration_id": "R0028",
267 + "checkin_time": "2025-04-02 05:59:24",
268 + "checkin_type": "self-service",
269 + "location": null,
270 + "staff_id": null,
271 + "status": "successful",
272 + "notes": "",
273 + "is_late": false
274 + },
275 + {
276 + "id": "C0022",
277 + "activity_id": "A0001",
278 + "user_id": "U0017",
279 + "registration_id": "R0030",
280 + "checkin_time": "2025-04-02 06:18:33",
281 + "checkin_type": "QR code",
282 + "location": null,
283 + "staff_id": null,
284 + "status": "successful",
285 + "notes": "",
286 + "is_late": false
287 + },
288 + {
289 + "id": "C0023",
290 + "activity_id": "A0001",
291 + "user_id": "U0038",
292 + "registration_id": "R0031",
293 + "checkin_time": "2025-04-02 06:10:30",
294 + "checkin_type": "manual",
295 + "location": null,
296 + "staff_id": "O0006",
297 + "status": "successful",
298 + "notes": "",
299 + "is_late": false
300 + },
301 + {
302 + "id": "C0024",
303 + "activity_id": "A0001",
304 + "user_id": "U0030",
305 + "registration_id": "R0032",
306 + "checkin_time": "2025-04-02 06:15:42",
307 + "checkin_type": "manual",
308 + "location": null,
309 + "staff_id": "O0006",
310 + "status": "successful",
311 + "notes": "热情参与讨论",
312 + "is_late": false
313 + },
314 + {
315 + "id": "C0025",
316 + "activity_id": "A0001",
317 + "user_id": "U0022",
318 + "registration_id": "R0033",
319 + "checkin_time": "2025-04-02 06:39:18",
320 + "checkin_type": "QR code",
321 + "location": null,
322 + "staff_id": null,
323 + "status": "successful",
324 + "notes": "带了书本",
325 + "is_late": false
326 + },
327 + {
328 + "id": "C0026",
329 + "activity_id": "A0001",
330 + "user_id": "U0014",
331 + "registration_id": "R0034",
332 + "checkin_time": "2025-04-02 06:36:11",
333 + "checkin_type": "self-service",
334 + "location": null,
335 + "staff_id": null,
336 + "status": "successful",
337 + "notes": "提前到达",
338 + "is_late": false
339 + },
340 + {
341 + "id": "C0027",
342 + "activity_id": "A0001",
343 + "user_id": "U0005",
344 + "registration_id": "R0035",
345 + "checkin_time": "2025-04-02 06:27:14",
346 + "checkin_type": "QR code",
347 + "location": null,
348 + "staff_id": null,
349 + "status": "successful",
350 + "notes": "提前到达",
351 + "is_late": false
352 + },
353 + {
354 + "id": "C0028",
355 + "activity_id": "A0001",
356 + "user_id": "U0039",
357 + "registration_id": "R0037",
358 + "checkin_time": "2025-04-02 06:43:29",
359 + "checkin_type": "QR code",
360 + "location": null,
361 + "staff_id": null,
362 + "status": "successful",
363 + "notes": "",
364 + "is_late": false
365 + },
366 + {
367 + "id": "C0029",
368 + "activity_id": "A0002",
369 + "user_id": "U0035",
370 + "registration_id": "R0038",
371 + "checkin_time": "2025-03-24 06:26:56",
372 + "checkin_type": "QR code",
373 + "location": {
374 + "name": "深蓝咖啡馆",
375 + "coordinates": "37.181720,114.507089"
376 + },
377 + "staff_id": null,
378 + "status": "successful",
379 + "notes": "",
380 + "is_late": false
381 + },
382 + {
383 + "id": "C0030",
384 + "activity_id": "A0002",
385 + "user_id": "U0031",
386 + "registration_id": "R0039",
387 + "checkin_time": "2025-03-24 06:40:17",
388 + "checkin_type": "QR code",
389 + "location": {
390 + "name": "深蓝咖啡馆",
391 + "coordinates": "38.496785,121.640291"
392 + },
393 + "staff_id": null,
394 + "status": "successful",
395 + "notes": "",
396 + "is_late": false
397 + },
398 + {
399 + "id": "C0031",
400 + "activity_id": "A0002",
401 + "user_id": "U0011",
402 + "registration_id": "R0040",
403 + "checkin_time": "2025-03-24 06:06:14",
404 + "checkin_type": "QR code",
405 + "location": {
406 + "name": "深蓝咖啡馆",
407 + "coordinates": "29.197808,115.186227"
408 + },
409 + "staff_id": null,
410 + "status": "failed",
411 + "notes": "网络连接不稳定",
412 + "is_late": false
413 + },
414 + {
415 + "id": "C0032",
416 + "activity_id": "A0002",
417 + "user_id": "U0032",
418 + "registration_id": "R0041",
419 + "checkin_time": "2025-03-24 06:17:34",
420 + "checkin_type": "QR code",
421 + "location": {
422 + "name": "深蓝咖啡馆",
423 + "coordinates": "36.369072,107.966159"
424 + },
425 + "staff_id": null,
426 + "status": "successful",
427 + "notes": "",
428 + "is_late": true
429 + },
430 + {
431 + "id": "C0033",
432 + "activity_id": "A0002",
433 + "user_id": "U0009",
434 + "registration_id": "R0043",
435 + "checkin_time": "2025-03-24 06:37:51",
436 + "checkin_type": "QR code",
437 + "location": {
438 + "name": "深蓝咖啡馆",
439 + "coordinates": "24.090606,121.419792"
440 + },
441 + "staff_id": null,
442 + "status": "successful",
443 + "notes": "",
444 + "is_late": true
445 + },
446 + {
447 + "id": "C0034",
448 + "activity_id": "A0002",
449 + "user_id": "U0018",
450 + "registration_id": "R0044",
451 + "checkin_time": "2025-03-24 06:37:28",
452 + "checkin_type": "QR code",
453 + "location": {
454 + "name": "深蓝咖啡馆",
455 + "coordinates": "24.317278,113.834895"
456 + },
457 + "staff_id": null,
458 + "status": "successful",
459 + "notes": "",
460 + "is_late": false
461 + },
462 + {
463 + "id": "C0035",
464 + "activity_id": "A0002",
465 + "user_id": "U0008",
466 + "registration_id": "R0045",
467 + "checkin_time": "2025-03-24 06:06:55",
468 + "checkin_type": "QR code",
469 + "location": {
470 + "name": "深蓝咖啡馆",
471 + "coordinates": "28.173906,120.708227"
472 + },
473 + "staff_id": null,
474 + "status": "successful",
475 + "notes": "",
476 + "is_late": false
477 + },
478 + {
479 + "id": "C0036",
480 + "activity_id": "A0002",
481 + "user_id": "U0025",
482 + "registration_id": "R0046",
483 + "checkin_time": "2025-03-24 06:26:03",
484 + "checkin_type": "manual",
485 + "location": {
486 + "name": "深蓝咖啡馆",
487 + "coordinates": "32.612174,103.031492"
488 + },
489 + "staff_id": "O0003",
490 + "status": "successful",
491 + "notes": "",
492 + "is_late": false
493 + },
494 + {
495 + "id": "C0037",
496 + "activity_id": "A0002",
497 + "user_id": "U0030",
498 + "registration_id": "R0047",
499 + "checkin_time": "2025-03-24 06:24:16",
500 + "checkin_type": "QR code",
501 + "location": {
502 + "name": "深蓝咖啡馆",
503 + "coordinates": "26.712645,111.630994"
504 + },
505 + "staff_id": null,
506 + "status": "successful",
507 + "notes": "按时到达",
508 + "is_late": false
509 + },
510 + {
511 + "id": "C0038",
512 + "activity_id": "A0002",
513 + "user_id": "U0022",
514 + "registration_id": "R0048",
515 + "checkin_time": "2025-03-24 06:12:56",
516 + "checkin_type": "QR code",
517 + "location": {
518 + "name": "深蓝咖啡馆",
519 + "coordinates": "24.159576,109.678518"
520 + },
521 + "staff_id": null,
522 + "status": "successful",
523 + "notes": "带了书本",
524 + "is_late": false
525 + },
526 + {
527 + "id": "C0039",
528 + "activity_id": "A0002",
529 + "user_id": "U0026",
530 + "registration_id": "R0049",
531 + "checkin_time": "2025-03-24 06:36:47",
532 + "checkin_type": "QR code",
533 + "location": {
534 + "name": "深蓝咖啡馆",
535 + "coordinates": "23.120311,112.843124"
536 + },
537 + "staff_id": null,
538 + "status": "successful",
539 + "notes": "",
540 + "is_late": true
541 + },
542 + {
543 + "id": "C0040",
544 + "activity_id": "A0002",
545 + "user_id": "U0016",
546 + "registration_id": "R0050",
547 + "checkin_time": "2025-03-24 06:08:26",
548 + "checkin_type": "manual",
549 + "location": {
550 + "name": "深蓝咖啡馆",
551 + "coordinates": "39.064207,125.051377"
552 + },
553 + "staff_id": "O0003",
554 + "status": "successful",
555 + "notes": "",
556 + "is_late": false
557 + },
558 + {
559 + "id": "C0041",
560 + "activity_id": "A0002",
561 + "user_id": "U0006",
562 + "registration_id": "R0051",
563 + "checkin_time": "2025-03-24 06:38:14",
564 + "checkin_type": "QR code",
565 + "location": {
566 + "name": "深蓝咖啡馆",
567 + "coordinates": "39.771853,103.340475"
568 + },
569 + "staff_id": null,
570 + "status": "successful",
571 + "notes": "",
572 + "is_late": false
573 + },
574 + {
575 + "id": "C0042",
576 + "activity_id": "A0002",
577 + "user_id": "U0017",
578 + "registration_id": "R0052",
579 + "checkin_time": "2025-03-24 06:38:41",
580 + "checkin_type": "QR code",
581 + "location": {
582 + "name": "深蓝咖啡馆",
583 + "coordinates": "28.109123,117.999368"
584 + },
585 + "staff_id": null,
586 + "status": "failed",
587 + "notes": "未携带所需材料",
588 + "is_late": false
589 + },
590 + {
591 + "id": "C0043",
592 + "activity_id": "A0002",
593 + "user_id": "U0023",
594 + "registration_id": "R0053",
595 + "checkin_time": "2025-03-24 06:28:37",
596 + "checkin_type": "QR code",
597 + "location": {
598 + "name": "深蓝咖啡馆",
599 + "coordinates": "26.399555,107.803150"
600 + },
601 + "staff_id": null,
602 + "status": "successful",
603 + "notes": "",
604 + "is_late": false
605 + },
606 + {
607 + "id": "C0044",
608 + "activity_id": "A0002",
609 + "user_id": "U0020",
610 + "registration_id": "R0054",
611 + "checkin_time": "2025-03-24 06:26:01",
612 + "checkin_type": "QR code",
613 + "location": {
614 + "name": "深蓝咖啡馆",
615 + "coordinates": "35.961689,105.194244"
616 + },
617 + "staff_id": null,
618 + "status": "successful",
619 + "notes": "",
620 + "is_late": false
621 + },
622 + {
623 + "id": "C0045",
624 + "activity_id": "A0002",
625 + "user_id": "U0002",
626 + "registration_id": "R0055",
627 + "checkin_time": "2025-03-24 06:17:24",
628 + "checkin_type": "QR code",
629 + "location": {
630 + "name": "深蓝咖啡馆",
631 + "coordinates": "38.805465,124.606647"
632 + },
633 + "staff_id": null,
634 + "status": "successful",
635 + "notes": "",
636 + "is_late": true
637 + },
638 + {
639 + "id": "C0046",
640 + "activity_id": "A0002",
641 + "user_id": "U0003",
642 + "registration_id": "R0057",
643 + "checkin_time": "2025-03-24 06:23:32",
644 + "checkin_type": "manual",
645 + "location": {
646 + "name": "深蓝咖啡馆",
647 + "coordinates": "26.842865,115.187406"
648 + },
649 + "staff_id": "O0003",
650 + "status": "successful",
651 + "notes": "",
652 + "is_late": true
653 + },
654 + {
655 + "id": "C0047",
656 + "activity_id": "A0002",
657 + "user_id": "U0021",
658 + "registration_id": "R0058",
659 + "checkin_time": "2025-03-24 06:01:19",
660 + "checkin_type": "QR code",
661 + "location": {
662 + "name": "深蓝咖啡馆",
663 + "coordinates": "31.048369,122.562894"
664 + },
665 + "staff_id": null,
666 + "status": "successful",
667 + "notes": "提前到达",
668 + "is_late": false
669 + },
670 + {
671 + "id": "C0048",
672 + "activity_id": "A0002",
673 + "user_id": "U0024",
674 + "registration_id": "R0059",
675 + "checkin_time": "2025-03-24 06:07:08",
676 + "checkin_type": "QR code",
677 + "location": {
678 + "name": "深蓝咖啡馆",
679 + "coordinates": "36.666997,102.016997"
680 + },
681 + "staff_id": null,
682 + "status": "successful",
683 + "notes": "",
684 + "is_late": false
685 + },
686 + {
687 + "id": "C0049",
688 + "activity_id": "A0002",
689 + "user_id": "U0037",
690 + "registration_id": "R0061",
691 + "checkin_time": "2025-03-24 06:39:21",
692 + "checkin_type": "QR code",
693 + "location": {
694 + "name": "深蓝咖啡馆",
695 + "coordinates": "26.657286,112.659843"
696 + },
697 + "staff_id": null,
698 + "status": "successful",
699 + "notes": "",
700 + "is_late": true
701 + },
702 + {
703 + "id": "C0050",
704 + "activity_id": "A0002",
705 + "user_id": "U0012",
706 + "registration_id": "R0063",
707 + "checkin_time": "2025-03-24 06:24:17",
708 + "checkin_type": "QR code",
709 + "location": {
710 + "name": "深蓝咖啡馆",
711 + "coordinates": "39.694284,109.014524"
712 + },
713 + "staff_id": null,
714 + "status": "successful",
715 + "notes": "提前到达",
716 + "is_late": true
717 + },
718 + {
719 + "id": "C0051",
720 + "activity_id": "A0002",
721 + "user_id": "U0014",
722 + "registration_id": "R0064",
723 + "checkin_time": "2025-03-24 06:19:52",
724 + "checkin_type": "manual",
725 + "location": {
726 + "name": "深蓝咖啡馆",
727 + "coordinates": "39.330172,105.342768"
728 + },
729 + "staff_id": "O0003",
730 + "status": "successful",
731 + "notes": "",
732 + "is_late": false
733 + },
734 + {
735 + "id": "C0052",
736 + "activity_id": "A0002",
737 + "user_id": "U0010",
738 + "registration_id": "R0065",
739 + "checkin_time": "2025-03-24 06:51:43",
740 + "checkin_type": "QR code",
741 + "location": {
742 + "name": "深蓝咖啡馆",
743 + "coordinates": "27.822307,110.880438"
744 + },
745 + "staff_id": null,
746 + "status": "successful",
747 + "notes": "",
748 + "is_late": true
749 + },
750 + {
751 + "id": "C0053",
752 + "activity_id": "A0002",
753 + "user_id": "U0034",
754 + "registration_id": "R0066",
755 + "checkin_time": "2025-03-24 06:24:07",
756 + "checkin_type": "QR code",
757 + "location": {
758 + "name": "深蓝咖啡馆",
759 + "coordinates": "37.010299,118.129043"
760 + },
761 + "staff_id": null,
762 + "status": "successful",
763 + "notes": "",
764 + "is_late": false
765 + },
766 + {
767 + "id": "C0054",
768 + "activity_id": "A0002",
769 + "user_id": "U0039",
770 + "registration_id": "R0067",
771 + "checkin_time": "2025-03-24 06:39:58",
772 + "checkin_type": "QR code",
773 + "location": {
774 + "name": "深蓝咖啡馆",
775 + "coordinates": "24.617110,119.003828"
776 + },
777 + "staff_id": null,
778 + "status": "successful",
779 + "notes": "热情参与讨论",
780 + "is_late": false
781 + },
782 + {
783 + "id": "C0055",
784 + "activity_id": "A0002",
785 + "user_id": "U0001",
786 + "registration_id": "R0068",
787 + "checkin_time": "2025-03-24 06:35:33",
788 + "checkin_type": "self-service",
789 + "location": {
790 + "name": "深蓝咖啡馆",
791 + "coordinates": "32.183967,110.892548"
792 + },
793 + "staff_id": null,
794 + "status": "successful",
795 + "notes": "准备充分",
796 + "is_late": true
797 + },
798 + {
799 + "id": "C0056",
800 + "activity_id": "A0002",
801 + "user_id": "U0028",
802 + "registration_id": "R0069",
803 + "checkin_time": "2025-03-24 06:22:47",
804 + "checkin_type": "self-service",
805 + "location": {
806 + "name": "深蓝咖啡馆",
807 + "coordinates": "37.102393,104.723842"
808 + },
809 + "staff_id": null,
810 + "status": "successful",
811 + "notes": "",
812 + "is_late": false
813 + },
814 + {
815 + "id": "C0057",
816 + "activity_id": "A0002",
817 + "user_id": "U0027",
818 + "registration_id": "R0070",
819 + "checkin_time": "2025-03-24 06:22:44",
820 + "checkin_type": "manual",
821 + "location": {
822 + "name": "深蓝咖啡馆",
823 + "coordinates": "35.022102,120.845595"
824 + },
825 + "staff_id": "O0003",
826 + "status": "successful",
827 + "notes": "",
828 + "is_late": true
829 + },
830 + {
831 + "id": "C0058",
832 + "activity_id": "A0002",
833 + "user_id": "U0013",
834 + "registration_id": "R0071",
835 + "checkin_time": "2025-03-24 06:29:54",
836 + "checkin_type": "QR code",
837 + "location": {
838 + "name": "深蓝咖啡馆",
839 + "coordinates": "27.520250,106.998565"
840 + },
841 + "staff_id": null,
842 + "status": "successful",
843 + "notes": "",
844 + "is_late": false
845 + },
846 + {
847 + "id": "C0059",
848 + "activity_id": "A0002",
849 + "user_id": "U0036",
850 + "registration_id": "R0072",
851 + "checkin_time": "2025-03-24 06:15:52",
852 + "checkin_type": "QR code",
853 + "location": {
854 + "name": "深蓝咖啡馆",
855 + "coordinates": "22.586642,106.806636"
856 + },
857 + "staff_id": null,
858 + "status": "successful",
859 + "notes": "提前到达",
860 + "is_late": false
861 + },
862 + {
863 + "id": "C0060",
864 + "activity_id": "A0002",
865 + "user_id": "U0004",
866 + "registration_id": "R0073",
867 + "checkin_time": "2025-03-24 06:14:19",
868 + "checkin_type": "QR code",
869 + "location": {
870 + "name": "深蓝咖啡馆",
871 + "coordinates": "34.054279,106.738680"
872 + },
873 + "staff_id": null,
874 + "status": "successful",
875 + "notes": "按时到达",
876 + "is_late": false
877 + },
878 + {
879 + "id": "C0061",
880 + "activity_id": "A0002",
881 + "user_id": "U0040",
882 + "registration_id": "R0074",
883 + "checkin_time": "2025-03-24 06:26:57",
884 + "checkin_type": "manual",
885 + "location": {
886 + "name": "深蓝咖啡馆",
887 + "coordinates": "35.010409,123.112389"
888 + },
889 + "staff_id": "O0003",
890 + "status": "successful",
891 + "notes": "",
892 + "is_late": false
893 + },
894 + {
895 + "id": "C0062",
896 + "activity_id": "A0002",
897 + "user_id": "U0005",
898 + "registration_id": "R0075",
899 + "checkin_time": "2025-03-24 06:34:07",
900 + "checkin_type": "QR code",
901 + "location": {
902 + "name": "深蓝咖啡馆",
903 + "coordinates": "29.606431,116.433917"
904 + },
905 + "staff_id": null,
906 + "status": "successful",
907 + "notes": "",
908 + "is_late": false
909 + },
910 + {
911 + "id": "C0063",
912 + "activity_id": "A0002",
913 + "user_id": "U0033",
914 + "registration_id": "R0077",
915 + "checkin_time": "2025-03-24 06:27:57",
916 + "checkin_type": "QR code",
917 + "location": {
918 + "name": "深蓝咖啡馆",
919 + "coordinates": "33.496844,120.165656"
920 + },
921 + "staff_id": null,
922 + "status": "successful",
923 + "notes": "",
924 + "is_late": false
925 + },
926 + {
927 + "id": "C0064",
928 + "activity_id": "A0007",
929 + "user_id": "U0031",
930 + "registration_id": "R0101",
931 + "checkin_time": "2025-04-03 05:12:17",
932 + "checkin_type": "QR code",
933 + "location": {
934 + "name": "熊猫书房",
935 + "coordinates": "37.519897,120.666728"
936 + },
937 + "staff_id": null,
938 + "status": "successful",
939 + "notes": "准备充分",
940 + "is_late": false
941 + },
942 + {
943 + "id": "C0065",
944 + "activity_id": "A0007",
945 + "user_id": "U0039",
946 + "registration_id": "R0102",
947 + "checkin_time": "2025-04-03 05:43:42",
948 + "checkin_type": "QR code",
949 + "location": {
950 + "name": "熊猫书房",
951 + "coordinates": "23.325053,124.389998"
952 + },
953 + "staff_id": null,
954 + "status": "successful",
955 + "notes": "",
956 + "is_late": false
957 + },
958 + {
959 + "id": "C0066",
960 + "activity_id": "A0007",
961 + "user_id": "U0040",
962 + "registration_id": "R0103",
963 + "checkin_time": "2025-04-03 05:36:39",
964 + "checkin_type": "manual",
965 + "location": {
966 + "name": "熊猫书房",
967 + "coordinates": "29.006041,121.944115"
968 + },
969 + "staff_id": "O0006",
970 + "status": "successful",
971 + "notes": "",
972 + "is_late": false
973 + },
974 + {
975 + "id": "C0067",
976 + "activity_id": "A0007",
977 + "user_id": "U0022",
978 + "registration_id": "R0104",
979 + "checkin_time": "2025-04-03 05:27:17",
980 + "checkin_type": "QR code",
981 + "location": {
982 + "name": "熊猫书房",
983 + "coordinates": "23.882683,105.402504"
984 + },
985 + "staff_id": null,
986 + "status": "successful",
987 + "notes": "准备充分",
988 + "is_late": false
989 + },
990 + {
991 + "id": "C0068",
992 + "activity_id": "A0007",
993 + "user_id": "U0015",
994 + "registration_id": "R0106",
995 + "checkin_time": "2025-04-03 05:27:01",
996 + "checkin_type": "QR code",
997 + "location": {
998 + "name": "熊猫书房",
999 + "coordinates": "37.561985,116.624272"
1000 + },
1001 + "staff_id": null,
1002 + "status": "successful",
1003 + "notes": "",
1004 + "is_late": false
1005 + },
1006 + {
1007 + "id": "C0069",
1008 + "activity_id": "A0007",
1009 + "user_id": "U0020",
1010 + "registration_id": "R0107",
1011 + "checkin_time": "2025-04-03 05:08:09",
1012 + "checkin_type": "QR code",
1013 + "location": {
1014 + "name": "熊猫书房",
1015 + "coordinates": "36.575256,121.636347"
1016 + },
1017 + "staff_id": null,
1018 + "status": "successful",
1019 + "notes": "",
1020 + "is_late": false
1021 + },
1022 + {
1023 + "id": "C0070",
1024 + "activity_id": "A0007",
1025 + "user_id": "U0038",
1026 + "registration_id": "R0108",
1027 + "checkin_time": "2025-04-03 05:21:29",
1028 + "checkin_type": "self-service",
1029 + "location": {
1030 + "name": "熊猫书房",
1031 + "coordinates": "27.958669,101.467384"
1032 + },
1033 + "staff_id": null,
1034 + "status": "successful",
1035 + "notes": "",
1036 + "is_late": false
1037 + },
1038 + {
1039 + "id": "C0071",
1040 + "activity_id": "A0007",
1041 + "user_id": "U0014",
1042 + "registration_id": "R0109",
1043 + "checkin_time": "2025-04-03 05:22:20",
1044 + "checkin_type": "self-service",
1045 + "location": {
1046 + "name": "熊猫书房",
1047 + "coordinates": "37.434346,114.067180"
1048 + },
1049 + "staff_id": null,
1050 + "status": "successful",
1051 + "notes": "",
1052 + "is_late": false
1053 + },
1054 + {
1055 + "id": "C0072",
1056 + "activity_id": "A0007",
1057 + "user_id": "U0011",
1058 + "registration_id": "R0110",
1059 + "checkin_time": "2025-04-03 05:08:27",
1060 + "checkin_type": "manual",
1061 + "location": {
1062 + "name": "熊猫书房",
1063 + "coordinates": "35.202228,124.219237"
1064 + },
1065 + "staff_id": "O0006",
1066 + "status": "successful",
1067 + "notes": "热情参与讨论",
1068 + "is_late": false
1069 + },
1070 + {
1071 + "id": "C0073",
1072 + "activity_id": "A0007",
1073 + "user_id": "U0034",
1074 + "registration_id": "R0111",
1075 + "checkin_time": "2025-04-03 05:35:07",
1076 + "checkin_type": "QR code",
1077 + "location": {
1078 + "name": "熊猫书房",
1079 + "coordinates": "30.140811,113.054793"
1080 + },
1081 + "staff_id": null,
1082 + "status": "successful",
1083 + "notes": "",
1084 + "is_late": false
1085 + },
1086 + {
1087 + "id": "C0074",
1088 + "activity_id": "A0007",
1089 + "user_id": "U0010",
1090 + "registration_id": "R0112",
1091 + "checkin_time": "2025-04-03 05:18:04",
1092 + "checkin_type": "QR code",
1093 + "location": {
1094 + "name": "熊猫书房",
1095 + "coordinates": "26.209453,123.344462"
1096 + },
1097 + "staff_id": null,
1098 + "status": "failed",
1099 + "notes": "未携带所需材料",
1100 + "is_late": false
1101 + },
1102 + {
1103 + "id": "C0075",
1104 + "activity_id": "A0007",
1105 + "user_id": "U0017",
1106 + "registration_id": "R0113",
1107 + "checkin_time": "2025-04-03 05:30:44",
1108 + "checkin_type": "QR code",
1109 + "location": {
1110 + "name": "熊猫书房",
1111 + "coordinates": "39.520586,111.230061"
1112 + },
1113 + "staff_id": null,
1114 + "status": "successful",
1115 + "notes": "",
1116 + "is_late": false
1117 + },
1118 + {
1119 + "id": "C0076",
1120 + "activity_id": "A0007",
1121 + "user_id": "U0006",
1122 + "registration_id": "R0115",
1123 + "checkin_time": "2025-04-03 05:35:32",
1124 + "checkin_type": "QR code",
1125 + "location": {
1126 + "name": "熊猫书房",
1127 + "coordinates": "22.854964,103.303204"
1128 + },
1129 + "staff_id": null,
1130 + "status": "successful",
1131 + "notes": "按时到达",
1132 + "is_late": false
1133 + },
1134 + {
1135 + "id": "C0077",
1136 + "activity_id": "A0007",
1137 + "user_id": "U0030",
1138 + "registration_id": "R0116",
1139 + "checkin_time": "2025-04-03 05:22:00",
1140 + "checkin_type": "self-service",
1141 + "location": {
1142 + "name": "熊猫书房",
1143 + "coordinates": "33.919211,117.039781"
1144 + },
1145 + "staff_id": null,
1146 + "status": "successful",
1147 + "notes": "",
1148 + "is_late": false
1149 + },
1150 + {
1151 + "id": "C0078",
1152 + "activity_id": "A0007",
1153 + "user_id": "U0036",
1154 + "registration_id": "R0117",
1155 + "checkin_time": "2025-04-03 05:14:00",
1156 + "checkin_type": "QR code",
1157 + "location": {
1158 + "name": "熊猫书房",
1159 + "coordinates": "22.389473,121.064134"
1160 + },
1161 + "staff_id": null,
1162 + "status": "successful",
1163 + "notes": "",
1164 + "is_late": false
1165 + },
1166 + {
1167 + "id": "C0079",
1168 + "activity_id": "A0007",
1169 + "user_id": "U0025",
1170 + "registration_id": "R0118",
1171 + "checkin_time": "2025-04-03 05:34:15",
1172 + "checkin_type": "manual",
1173 + "location": {
1174 + "name": "熊猫书房",
1175 + "coordinates": "23.279344,121.133817"
1176 + },
1177 + "staff_id": "O0006",
1178 + "status": "successful",
1179 + "notes": "准备充分",
1180 + "is_late": false
1181 + },
1182 + {
1183 + "id": "C0080",
1184 + "activity_id": "A0007",
1185 + "user_id": "U0008",
1186 + "registration_id": "R0120",
1187 + "checkin_time": "2025-04-03 05:29:55",
1188 + "checkin_type": "self-service",
1189 + "location": {
1190 + "name": "熊猫书房",
1191 + "coordinates": "26.897514,110.258579"
1192 + },
1193 + "staff_id": null,
1194 + "status": "failed",
1195 + "notes": "未携带所需材料",
1196 + "is_late": false
1197 + },
1198 + {
1199 + "id": "C0081",
1200 + "activity_id": "A0007",
1201 + "user_id": "U0009",
1202 + "registration_id": "R0122",
1203 + "checkin_time": "2025-04-03 05:19:05",
1204 + "checkin_type": "manual",
1205 + "location": {
1206 + "name": "熊猫书房",
1207 + "coordinates": "37.908705,105.332370"
1208 + },
1209 + "staff_id": "O0006",
1210 + "status": "successful",
1211 + "notes": "",
1212 + "is_late": false
1213 + },
1214 + {
1215 + "id": "C0082",
1216 + "activity_id": "A0007",
1217 + "user_id": "U0033",
1218 + "registration_id": "R0123",
1219 + "checkin_time": "2025-04-03 05:06:33",
1220 + "checkin_type": "QR code",
1221 + "location": {
1222 + "name": "熊猫书房",
1223 + "coordinates": "27.330438,106.622710"
1224 + },
1225 + "staff_id": null,
1226 + "status": "successful",
1227 + "notes": "",
1228 + "is_late": false
1229 + },
1230 + {
1231 + "id": "C0083",
1232 + "activity_id": "A0007",
1233 + "user_id": "U0028",
1234 + "registration_id": "R0124",
1235 + "checkin_time": "2025-04-03 05:06:45",
1236 + "checkin_type": "QR code",
1237 + "location": {
1238 + "name": "熊猫书房",
1239 + "coordinates": "22.012082,114.991471"
1240 + },
1241 + "staff_id": null,
1242 + "status": "successful",
1243 + "notes": "",
1244 + "is_late": false
1245 + },
1246 + {
1247 + "id": "C0084",
1248 + "activity_id": "A0007",
1249 + "user_id": "U0035",
1250 + "registration_id": "R0125",
1251 + "checkin_time": "2025-04-03 05:24:04",
1252 + "checkin_type": "QR code",
1253 + "location": {
1254 + "name": "熊猫书房",
1255 + "coordinates": "36.148959,100.254513"
1256 + },
1257 + "staff_id": null,
1258 + "status": "successful",
1259 + "notes": "提前到达",
1260 + "is_late": false
1261 + },
1262 + {
1263 + "id": "C0085",
1264 + "activity_id": "A0007",
1265 + "user_id": "U0032",
1266 + "registration_id": "R0127",
1267 + "checkin_time": "2025-04-03 05:25:34",
1268 + "checkin_type": "QR code",
1269 + "location": {
1270 + "name": "熊猫书房",
1271 + "coordinates": "26.064327,114.415219"
1272 + },
1273 + "staff_id": null,
1274 + "status": "successful",
1275 + "notes": "",
1276 + "is_late": false
1277 + },
1278 + {
1279 + "id": "C0086",
1280 + "activity_id": "A0007",
1281 + "user_id": "U0021",
1282 + "registration_id": "R0129",
1283 + "checkin_time": "2025-04-03 05:39:03",
1284 + "checkin_type": "manual",
1285 + "location": {
1286 + "name": "熊猫书房",
1287 + "coordinates": "32.406524,103.926679"
1288 + },
1289 + "staff_id": "O0006",
1290 + "status": "successful",
1291 + "notes": "",
1292 + "is_late": false
1293 + },
1294 + {
1295 + "id": "C0087",
1296 + "activity_id": "A0007",
1297 + "user_id": "U0002",
1298 + "registration_id": "R0130",
1299 + "checkin_time": "2025-04-03 05:16:57",
1300 + "checkin_type": "self-service",
1301 + "location": {
1302 + "name": "熊猫书房",
1303 + "coordinates": "34.129245,115.222974"
1304 + },
1305 + "staff_id": null,
1306 + "status": "successful",
1307 + "notes": "带了书本",
1308 + "is_late": false
1309 + },
1310 + {
1311 + "id": "C0088",
1312 + "activity_id": "A0007",
1313 + "user_id": "U0003",
1314 + "registration_id": "R0131",
1315 + "checkin_time": "2025-04-03 05:15:34",
1316 + "checkin_type": "QR code",
1317 + "location": {
1318 + "name": "熊猫书房",
1319 + "coordinates": "22.591087,120.597131"
1320 + },
1321 + "staff_id": null,
1322 + "status": "successful",
1323 + "notes": "",
1324 + "is_late": false
1325 + },
1326 + {
1327 + "id": "C0089",
1328 + "activity_id": "A0007",
1329 + "user_id": "U0019",
1330 + "registration_id": "R0132",
1331 + "checkin_time": "2025-04-03 05:50:38",
1332 + "checkin_type": "QR code",
1333 + "location": {
1334 + "name": "熊猫书房",
1335 + "coordinates": "36.712114,101.658613"
1336 + },
1337 + "staff_id": null,
1338 + "status": "successful",
1339 + "notes": "",
1340 + "is_late": true
1341 + },
1342 + {
1343 + "id": "C0090",
1344 + "activity_id": "A0008",
1345 + "user_id": "U0003",
1346 + "registration_id": "R0135",
1347 + "checkin_time": "2025-04-16 05:25:35",
1348 + "checkin_type": "self-service",
1349 + "location": {
1350 + "name": "紫金读书会",
1351 + "coordinates": "33.547392,111.581963"
1352 + },
1353 + "staff_id": null,
1354 + "status": "successful",
1355 + "notes": "",
1356 + "is_late": false
1357 + },
1358 + {
1359 + "id": "C0091",
1360 + "activity_id": "A0008",
1361 + "user_id": "U0031",
1362 + "registration_id": "R0136",
1363 + "checkin_time": "2025-04-16 05:32:36",
1364 + "checkin_type": "QR code",
1365 + "location": {
1366 + "name": "紫金读书会",
1367 + "coordinates": "26.534463,122.247619"
1368 + },
1369 + "staff_id": null,
1370 + "status": "successful",
1371 + "notes": "带了书本",
1372 + "is_late": false
1373 + },
1374 + {
1375 + "id": "C0092",
1376 + "activity_id": "A0008",
1377 + "user_id": "U0025",
1378 + "registration_id": "R0137",
1379 + "checkin_time": "2025-04-16 05:30:42",
1380 + "checkin_type": "QR code",
1381 + "location": {
1382 + "name": "紫金读书会",
1383 + "coordinates": "37.697400,124.784329"
1384 + },
1385 + "staff_id": null,
1386 + "status": "successful",
1387 + "notes": "",
1388 + "is_late": false
1389 + },
1390 + {
1391 + "id": "C0093",
1392 + "activity_id": "A0008",
1393 + "user_id": "U0010",
1394 + "registration_id": "R0139",
1395 + "checkin_time": "2025-04-16 05:52:05",
1396 + "checkin_type": "QR code",
1397 + "location": {
1398 + "name": "紫金读书会",
1399 + "coordinates": "37.840360,125.457487"
1400 + },
1401 + "staff_id": null,
1402 + "status": "successful",
1403 + "notes": "",
1404 + "is_late": false
1405 + },
1406 + {
1407 + "id": "C0094",
1408 + "activity_id": "A0008",
1409 + "user_id": "U0033",
1410 + "registration_id": "R0140",
1411 + "checkin_time": "2025-04-16 05:57:41",
1412 + "checkin_type": "self-service",
1413 + "location": {
1414 + "name": "紫金读书会",
1415 + "coordinates": "26.522240,113.936005"
1416 + },
1417 + "staff_id": null,
1418 + "status": "successful",
1419 + "notes": "",
1420 + "is_late": false
1421 + },
1422 + {
1423 + "id": "C0095",
1424 + "activity_id": "A0008",
1425 + "user_id": "U0038",
1426 + "registration_id": "R0143",
1427 + "checkin_time": "2025-04-16 05:34:49",
1428 + "checkin_type": "QR code",
1429 + "location": {
1430 + "name": "紫金读书会",
1431 + "coordinates": "32.237890,101.465566"
1432 + },
1433 + "staff_id": null,
1434 + "status": "successful",
1435 + "notes": "",
1436 + "is_late": false
1437 + },
1438 + {
1439 + "id": "C0096",
1440 + "activity_id": "A0008",
1441 + "user_id": "U0027",
1442 + "registration_id": "R0145",
1443 + "checkin_time": "2025-04-16 05:32:21",
1444 + "checkin_type": "QR code",
1445 + "location": {
1446 + "name": "紫金读书会",
1447 + "coordinates": "26.943107,117.949832"
1448 + },
1449 + "staff_id": null,
1450 + "status": "successful",
1451 + "notes": "提前到达",
1452 + "is_late": true
1453 + },
1454 + {
1455 + "id": "C0097",
1456 + "activity_id": "A0008",
1457 + "user_id": "U0030",
1458 + "registration_id": "R0146",
1459 + "checkin_time": "2025-04-16 05:24:26",
1460 + "checkin_type": "QR code",
1461 + "location": {
1462 + "name": "紫金读书会",
1463 + "coordinates": "26.658257,117.440716"
1464 + },
1465 + "staff_id": null,
1466 + "status": "successful",
1467 + "notes": "",
1468 + "is_late": false
1469 + },
1470 + {
1471 + "id": "C0098",
1472 + "activity_id": "A0008",
1473 + "user_id": "U0021",
1474 + "registration_id": "R0148",
1475 + "checkin_time": "2025-04-16 05:41:35",
1476 + "checkin_type": "QR code",
1477 + "location": {
1478 + "name": "紫金读书会",
1479 + "coordinates": "28.532310,111.835896"
1480 + },
1481 + "staff_id": null,
1482 + "status": "successful",
1483 + "notes": "",
1484 + "is_late": true
1485 + },
1486 + {
1487 + "id": "C0099",
1488 + "activity_id": "A0008",
1489 + "user_id": "U0004",
1490 + "registration_id": "R0149",
1491 + "checkin_time": "2025-04-16 05:16:49",
1492 + "checkin_type": "QR code",
1493 + "location": {
1494 + "name": "紫金读书会",
1495 + "coordinates": "24.072484,125.678064"
1496 + },
1497 + "staff_id": null,
1498 + "status": "successful",
1499 + "notes": "",
1500 + "is_late": false
1501 + },
1502 + {
1503 + "id": "C0100",
1504 + "activity_id": "A0012",
1505 + "user_id": "U0026",
1506 + "registration_id": "R0178",
1507 + "checkin_time": "2025-04-04 05:04:24",
1508 + "checkin_type": "QR code",
1509 + "location": null,
1510 + "staff_id": null,
1511 + "status": "successful",
1512 + "notes": "",
1513 + "is_late": false
1514 + },
1515 + {
1516 + "id": "C0101",
1517 + "activity_id": "A0012",
1518 + "user_id": "U0009",
1519 + "registration_id": "R0181",
1520 + "checkin_time": "2025-04-04 05:09:34",
1521 + "checkin_type": "QR code",
1522 + "location": null,
1523 + "staff_id": null,
1524 + "status": "successful",
1525 + "notes": "",
1526 + "is_late": false
1527 + },
1528 + {
1529 + "id": "C0102",
1530 + "activity_id": "A0012",
1531 + "user_id": "U0025",
1532 + "registration_id": "R0182",
1533 + "checkin_time": "2025-04-04 05:25:45",
1534 + "checkin_type": "self-service",
1535 + "location": null,
1536 + "staff_id": null,
1537 + "status": "successful",
1538 + "notes": "",
1539 + "is_late": true
1540 + },
1541 + {
1542 + "id": "C0103",
1543 + "activity_id": "A0012",
1544 + "user_id": "U0002",
1545 + "registration_id": "R0183",
1546 + "checkin_time": "2025-04-04 05:27:11",
1547 + "checkin_type": "QR code",
1548 + "location": null,
1549 + "staff_id": null,
1550 + "status": "successful",
1551 + "notes": "",
1552 + "is_late": false
1553 + },
1554 + {
1555 + "id": "C0104",
1556 + "activity_id": "A0012",
1557 + "user_id": "U0031",
1558 + "registration_id": "R0184",
1559 + "checkin_time": "2025-04-04 05:31:22",
1560 + "checkin_type": "manual",
1561 + "location": null,
1562 + "staff_id": "O0001",
1563 + "status": "successful",
1564 + "notes": "",
1565 + "is_late": false
1566 + },
1567 + {
1568 + "id": "C0105",
1569 + "activity_id": "A0012",
1570 + "user_id": "U0015",
1571 + "registration_id": "R0185",
1572 + "checkin_time": "2025-04-04 05:36:53",
1573 + "checkin_type": "QR code",
1574 + "location": null,
1575 + "staff_id": null,
1576 + "status": "successful",
1577 + "notes": "带了书本",
1578 + "is_late": false
1579 + },
1580 + {
1581 + "id": "C0106",
1582 + "activity_id": "A0012",
1583 + "user_id": "U0039",
1584 + "registration_id": "R0187",
1585 + "checkin_time": "2025-04-04 05:48:01",
1586 + "checkin_type": "QR code",
1587 + "location": null,
1588 + "staff_id": null,
1589 + "status": "successful",
1590 + "notes": "",
1591 + "is_late": true
1592 + },
1593 + {
1594 + "id": "C0107",
1595 + "activity_id": "A0012",
1596 + "user_id": "U0006",
1597 + "registration_id": "R0188",
1598 + "checkin_time": "2025-04-04 05:18:54",
1599 + "checkin_type": "QR code",
1600 + "location": null,
1601 + "staff_id": null,
1602 + "status": "successful",
1603 + "notes": "带了书本",
1604 + "is_late": false
1605 + },
1606 + {
1607 + "id": "C0108",
1608 + "activity_id": "A0012",
1609 + "user_id": "U0020",
1610 + "registration_id": "R0189",
1611 + "checkin_time": "2025-04-04 05:55:49",
1612 + "checkin_type": "QR code",
1613 + "location": null,
1614 + "staff_id": null,
1615 + "status": "successful",
1616 + "notes": "准备充分",
1617 + "is_late": true
1618 + },
1619 + {
1620 + "id": "C0109",
1621 + "activity_id": "A0012",
1622 + "user_id": "U0034",
1623 + "registration_id": "R0190",
1624 + "checkin_time": "2025-04-04 05:08:51",
1625 + "checkin_type": "QR code",
1626 + "location": null,
1627 + "staff_id": null,
1628 + "status": "successful",
1629 + "notes": "提前到达",
1630 + "is_late": false
1631 + },
1632 + {
1633 + "id": "C0110",
1634 + "activity_id": "A0012",
1635 + "user_id": "U0010",
1636 + "registration_id": "R0191",
1637 + "checkin_time": "2025-04-04 05:48:49",
1638 + "checkin_type": "QR code",
1639 + "location": null,
1640 + "staff_id": null,
1641 + "status": "successful",
1642 + "notes": "",
1643 + "is_late": true
1644 + },
1645 + {
1646 + "id": "C0111",
1647 + "activity_id": "A0012",
1648 + "user_id": "U0005",
1649 + "registration_id": "R0192",
1650 + "checkin_time": "2025-04-04 05:09:19",
1651 + "checkin_type": "QR code",
1652 + "location": null,
1653 + "staff_id": null,
1654 + "status": "successful",
1655 + "notes": "准备充分",
1656 + "is_late": false
1657 + },
1658 + {
1659 + "id": "C0112",
1660 + "activity_id": "A0012",
1661 + "user_id": "U0032",
1662 + "registration_id": "R0193",
1663 + "checkin_time": "2025-04-04 05:00:54",
1664 + "checkin_type": "QR code",
1665 + "location": null,
1666 + "staff_id": null,
1667 + "status": "successful",
1668 + "notes": "",
1669 + "is_late": false
1670 + },
1671 + {
1672 + "id": "C0113",
1673 + "activity_id": "A0012",
1674 + "user_id": "U0012",
1675 + "registration_id": "R0194",
1676 + "checkin_time": "2025-04-04 05:44:20",
1677 + "checkin_type": "QR code",
1678 + "location": null,
1679 + "staff_id": null,
1680 + "status": "successful",
1681 + "notes": "",
1682 + "is_late": true
1683 + },
1684 + {
1685 + "id": "C0114",
1686 + "activity_id": "A0012",
1687 + "user_id": "U0023",
1688 + "registration_id": "R0195",
1689 + "checkin_time": "2025-04-04 05:24:39",
1690 + "checkin_type": "QR code",
1691 + "location": null,
1692 + "staff_id": null,
1693 + "status": "successful",
1694 + "notes": "",
1695 + "is_late": false
1696 + },
1697 + {
1698 + "id": "C0115",
1699 + "activity_id": "A0012",
1700 + "user_id": "U0021",
1701 + "registration_id": "R0196",
1702 + "checkin_time": "2025-04-04 05:00:48",
1703 + "checkin_type": "QR code",
1704 + "location": null,
1705 + "staff_id": null,
1706 + "status": "successful",
1707 + "notes": "",
1708 + "is_late": false
1709 + },
1710 + {
1711 + "id": "C0116",
1712 + "activity_id": "A0012",
1713 + "user_id": "U0004",
1714 + "registration_id": "R0199",
1715 + "checkin_time": "2025-04-04 05:47:11",
1716 + "checkin_type": "manual",
1717 + "location": null,
1718 + "staff_id": "O0001",
1719 + "status": "successful",
1720 + "notes": "",
1721 + "is_late": true
1722 + },
1723 + {
1724 + "id": "C0117",
1725 + "activity_id": "A0012",
1726 + "user_id": "U0033",
1727 + "registration_id": "R0200",
1728 + "checkin_time": "2025-04-04 05:26:28",
1729 + "checkin_type": "manual",
1730 + "location": null,
1731 + "staff_id": "O0001",
1732 + "status": "successful",
1733 + "notes": "准备充分",
1734 + "is_late": false
1735 + },
1736 + {
1737 + "id": "C0118",
1738 + "activity_id": "A0012",
1739 + "user_id": "U0018",
1740 + "registration_id": "R0201",
1741 + "checkin_time": "2025-04-04 05:27:24",
1742 + "checkin_type": "QR code",
1743 + "location": null,
1744 + "staff_id": null,
1745 + "status": "successful",
1746 + "notes": "",
1747 + "is_late": false
1748 + },
1749 + {
1750 + "id": "C0119",
1751 + "activity_id": "A0012",
1752 + "user_id": "U0017",
1753 + "registration_id": "R0202",
1754 + "checkin_time": "2025-04-04 05:15:29",
1755 + "checkin_type": "QR code",
1756 + "location": null,
1757 + "staff_id": null,
1758 + "status": "successful",
1759 + "notes": "",
1760 + "is_late": false
1761 + },
1762 + {
1763 + "id": "C0120",
1764 + "activity_id": "A0012",
1765 + "user_id": "U0030",
1766 + "registration_id": "R0203",
1767 + "checkin_time": "2025-04-04 06:00:53",
1768 + "checkin_type": "manual",
1769 + "location": null,
1770 + "staff_id": "O0001",
1771 + "status": "successful",
1772 + "notes": "",
1773 + "is_late": true
1774 + },
1775 + {
1776 + "id": "C0121",
1777 + "activity_id": "A0012",
1778 + "user_id": "U0040",
1779 + "registration_id": "R0204",
1780 + "checkin_time": "2025-04-04 05:39:19",
1781 + "checkin_type": "self-service",
1782 + "location": null,
1783 + "staff_id": null,
1784 + "status": "successful",
1785 + "notes": "",
1786 + "is_late": false
1787 + },
1788 + {
1789 + "id": "C0122",
1790 + "activity_id": "A0012",
1791 + "user_id": "U0035",
1792 + "registration_id": "R0205",
1793 + "checkin_time": "2025-04-04 04:59:03",
1794 + "checkin_type": "manual",
1795 + "location": null,
1796 + "staff_id": "O0001",
1797 + "status": "successful",
1798 + "notes": "",
1799 + "is_late": false
1800 + },
1801 + {
1802 + "id": "C0123",
1803 + "activity_id": "A0012",
1804 + "user_id": "U0008",
1805 + "registration_id": "R0206",
1806 + "checkin_time": "2025-04-04 05:18:37",
1807 + "checkin_type": "QR code",
1808 + "location": null,
1809 + "staff_id": null,
1810 + "status": "successful",
1811 + "notes": "",
1812 + "is_late": false
1813 + },
1814 + {
1815 + "id": "C0124",
1816 + "activity_id": "A0012",
1817 + "user_id": "U0029",
1818 + "registration_id": "R0207",
1819 + "checkin_time": "2025-04-04 05:05:57",
1820 + "checkin_type": "manual",
1821 + "location": null,
1822 + "staff_id": "O0001",
1823 + "status": "successful",
1824 + "notes": "",
1825 + "is_late": false
1826 + },
1827 + {
1828 + "id": "C0125",
1829 + "activity_id": "A0012",
1830 + "user_id": "U0028",
1831 + "registration_id": "R0208",
1832 + "checkin_time": "2025-04-04 05:12:11",
1833 + "checkin_type": "manual",
1834 + "location": null,
1835 + "staff_id": "O0001",
1836 + "status": "successful",
1837 + "notes": "",
1838 + "is_late": false
1839 + },
1840 + {
1841 + "id": "C0126",
1842 + "activity_id": "A0012",
1843 + "user_id": "U0027",
1844 + "registration_id": "R0209",
1845 + "checkin_time": "2025-04-04 05:25:32",
1846 + "checkin_type": "QR code",
1847 + "location": null,
1848 + "staff_id": null,
1849 + "status": "successful",
1850 + "notes": "带了书本",
1851 + "is_late": true
1852 + },
1853 + {
1854 + "id": "C0127",
1855 + "activity_id": "A0012",
1856 + "user_id": "U0022",
1857 + "registration_id": "R0211",
1858 + "checkin_time": "2025-04-04 05:37:40",
1859 + "checkin_type": "QR code",
1860 + "location": null,
1861 + "staff_id": null,
1862 + "status": "successful",
1863 + "notes": "热情参与讨论",
1864 + "is_late": true
1865 + },
1866 + {
1867 + "id": "C0128",
1868 + "activity_id": "A0012",
1869 + "user_id": "U0016",
1870 + "registration_id": "R0213",
1871 + "checkin_time": "2025-04-04 05:02:22",
1872 + "checkin_type": "self-service",
1873 + "location": null,
1874 + "staff_id": null,
1875 + "status": "successful",
1876 + "notes": "热情参与讨论",
1877 + "is_late": false
1878 + },
1879 + {
1880 + "id": "C0129",
1881 + "activity_id": "A0012",
1882 + "user_id": "U0011",
1883 + "registration_id": "R0214",
1884 + "checkin_time": "2025-04-04 05:29:01",
1885 + "checkin_type": "self-service",
1886 + "location": null,
1887 + "staff_id": null,
1888 + "status": "successful",
1889 + "notes": "按时到达",
1890 + "is_late": false
1891 + },
1892 + {
1893 + "id": "C0130",
1894 + "activity_id": "A0012",
1895 + "user_id": "U0036",
1896 + "registration_id": "R0215",
1897 + "checkin_time": "2025-04-04 05:42:57",
1898 + "checkin_type": "QR code",
1899 + "location": null,
1900 + "staff_id": null,
1901 + "status": "successful",
1902 + "notes": "",
1903 + "is_late": false
1904 + },
1905 + {
1906 + "id": "C0131",
1907 + "activity_id": "A0012",
1908 + "user_id": "U0019",
1909 + "registration_id": "R0216",
1910 + "checkin_time": "2025-04-04 05:12:01",
1911 + "checkin_type": "QR code",
1912 + "location": null,
1913 + "staff_id": null,
1914 + "status": "successful",
1915 + "notes": "",
1916 + "is_late": false
1917 + },
1918 + {
1919 + "id": "C0132",
1920 + "activity_id": "A0012",
1921 + "user_id": "U0037",
1922 + "registration_id": "R0217",
1923 + "checkin_time": "2025-04-04 05:43:15",
1924 + "checkin_type": "QR code",
1925 + "location": null,
1926 + "staff_id": null,
1927 + "status": "successful",
1928 + "notes": "准备充分",
1929 + "is_late": false
1930 + },
1931 + {
1932 + "id": "C0133",
1933 + "activity_id": "A0013",
1934 + "user_id": "U0021",
1935 + "registration_id": "R0219",
1936 + "checkin_time": "2025-04-16 06:30:48",
1937 + "checkin_type": "QR code",
1938 + "location": {
1939 + "name": "深蓝咖啡馆",
1940 + "coordinates": "31.314481,112.706910"
1941 + },
1942 + "staff_id": null,
1943 + "status": "successful",
1944 + "notes": "",
1945 + "is_late": false
1946 + },
1947 + {
1948 + "id": "C0134",
1949 + "activity_id": "A0013",
1950 + "user_id": "U0034",
1951 + "registration_id": "R0220",
1952 + "checkin_time": "2025-04-16 06:28:10",
1953 + "checkin_type": "QR code",
1954 + "location": {
1955 + "name": "深蓝咖啡馆",
1956 + "coordinates": "27.233821,118.026442"
1957 + },
1958 + "staff_id": null,
1959 + "status": "successful",
1960 + "notes": "",
1961 + "is_late": false
1962 + },
1963 + {
1964 + "id": "C0135",
1965 + "activity_id": "A0013",
1966 + "user_id": "U0024",
1967 + "registration_id": "R0222",
1968 + "checkin_time": "2025-04-16 06:19:07",
1969 + "checkin_type": "QR code",
1970 + "location": {
1971 + "name": "深蓝咖啡馆",
1972 + "coordinates": "26.304584,117.374957"
1973 + },
1974 + "staff_id": null,
1975 + "status": "successful",
1976 + "notes": "",
1977 + "is_late": false
1978 + },
1979 + {
1980 + "id": "C0136",
1981 + "activity_id": "A0013",
1982 + "user_id": "U0006",
1983 + "registration_id": "R0226",
1984 + "checkin_time": "2025-04-16 06:47:15",
1985 + "checkin_type": "QR code",
1986 + "location": {
1987 + "name": "深蓝咖啡馆",
1988 + "coordinates": "26.656372,107.313507"
1989 + },
1990 + "staff_id": null,
1991 + "status": "successful",
1992 + "notes": "带了书本",
1993 + "is_late": true
1994 + },
1995 + {
1996 + "id": "C0137",
1997 + "activity_id": "A0013",
1998 + "user_id": "U0016",
1999 + "registration_id": "R0228",
2000 + "checkin_time": "2025-04-16 06:02:25",
2001 + "checkin_type": "manual",
2002 + "location": {
2003 + "name": "深蓝咖啡馆",
2004 + "coordinates": "22.619492,111.165082"
2005 + },
2006 + "staff_id": "O0003",
2007 + "status": "successful",
2008 + "notes": "带了书本",
2009 + "is_late": false
2010 + },
2011 + {
2012 + "id": "C0138",
2013 + "activity_id": "A0013",
2014 + "user_id": "U0039",
2015 + "registration_id": "R0230",
2016 + "checkin_time": "2025-04-16 06:19:57",
2017 + "checkin_type": "QR code",
2018 + "location": {
2019 + "name": "深蓝咖啡馆",
2020 + "coordinates": "28.583453,100.061289"
2021 + },
2022 + "staff_id": null,
2023 + "status": "successful",
2024 + "notes": "",
2025 + "is_late": false
2026 + },
2027 + {
2028 + "id": "C0139",
2029 + "activity_id": "A0013",
2030 + "user_id": "U0033",
2031 + "registration_id": "R0231",
2032 + "checkin_time": "2025-04-16 05:48:13",
2033 + "checkin_type": "QR code",
2034 + "location": {
2035 + "name": "深蓝咖啡馆",
2036 + "coordinates": "39.928726,103.865093"
2037 + },
2038 + "staff_id": null,
2039 + "status": "successful",
2040 + "notes": "",
2041 + "is_late": false
2042 + },
2043 + {
2044 + "id": "C0140",
2045 + "activity_id": "A0013",
2046 + "user_id": "U0013",
2047 + "registration_id": "R0232",
2048 + "checkin_time": "2025-04-16 06:22:37",
2049 + "checkin_type": "manual",
2050 + "location": {
2051 + "name": "深蓝咖啡馆",
2052 + "coordinates": "30.206263,116.407620"
2053 + },
2054 + "staff_id": "O0003",
2055 + "status": "successful",
2056 + "notes": "",
2057 + "is_late": false
2058 + },
2059 + {
2060 + "id": "C0141",
2061 + "activity_id": "A0013",
2062 + "user_id": "U0017",
2063 + "registration_id": "R0237",
2064 + "checkin_time": "2025-04-16 05:51:41",
2065 + "checkin_type": "QR code",
2066 + "location": {
2067 + "name": "深蓝咖啡馆",
2068 + "coordinates": "26.353654,112.348033"
2069 + },
2070 + "staff_id": null,
2071 + "status": "successful",
2072 + "notes": "",
2073 + "is_late": false
2074 + },
2075 + {
2076 + "id": "C0142",
2077 + "activity_id": "A0013",
2078 + "user_id": "U0040",
2079 + "registration_id": "R0238",
2080 + "checkin_time": "2025-04-16 05:59:42",
2081 + "checkin_type": "manual",
2082 + "location": {
2083 + "name": "深蓝咖啡馆",
2084 + "coordinates": "29.536559,121.926857"
2085 + },
2086 + "staff_id": "O0003",
2087 + "status": "successful",
2088 + "notes": "按时到达",
2089 + "is_late": false
2090 + },
2091 + {
2092 + "id": "C0143",
2093 + "activity_id": "A0013",
2094 + "user_id": "U0028",
2095 + "registration_id": "R0239",
2096 + "checkin_time": "2025-04-16 06:03:43",
2097 + "checkin_type": "QR code",
2098 + "location": {
2099 + "name": "深蓝咖啡馆",
2100 + "coordinates": "38.445453,111.849643"
2101 + },
2102 + "staff_id": null,
2103 + "status": "successful",
2104 + "notes": "热情参与讨论",
2105 + "is_late": false
2106 + },
2107 + {
2108 + "id": "C0144",
2109 + "activity_id": "A0013",
2110 + "user_id": "U0015",
2111 + "registration_id": "R0240",
2112 + "checkin_time": "2025-04-16 06:15:58",
2113 + "checkin_type": "QR code",
2114 + "location": {
2115 + "name": "深蓝咖啡馆",
2116 + "coordinates": "38.987819,109.828606"
2117 + },
2118 + "staff_id": null,
2119 + "status": "failed",
2120 + "notes": "网络连接不稳定",
2121 + "is_late": false
2122 + },
2123 + {
2124 + "id": "C0145",
2125 + "activity_id": "A0013",
2126 + "user_id": "U0023",
2127 + "registration_id": "R0242",
2128 + "checkin_time": "2025-04-16 06:21:50",
2129 + "checkin_type": "manual",
2130 + "location": {
2131 + "name": "深蓝咖啡馆",
2132 + "coordinates": "38.259928,108.439322"
2133 + },
2134 + "staff_id": "O0003",
2135 + "status": "successful",
2136 + "notes": "",
2137 + "is_late": false
2138 + },
2139 + {
2140 + "id": "C0146",
2141 + "activity_id": "A0013",
2142 + "user_id": "U0012",
2143 + "registration_id": "R0243",
2144 + "checkin_time": "2025-04-16 06:22:16",
2145 + "checkin_type": "QR code",
2146 + "location": {
2147 + "name": "深蓝咖啡馆",
2148 + "coordinates": "30.354202,113.504246"
2149 + },
2150 + "staff_id": null,
2151 + "status": "successful",
2152 + "notes": "准备充分",
2153 + "is_late": false
2154 + },
2155 + {
2156 + "id": "C0147",
2157 + "activity_id": "A0013",
2158 + "user_id": "U0020",
2159 + "registration_id": "R0244",
2160 + "checkin_time": "2025-04-16 06:14:18",
2161 + "checkin_type": "QR code",
2162 + "location": {
2163 + "name": "深蓝咖啡馆",
2164 + "coordinates": "38.643924,120.520059"
2165 + },
2166 + "staff_id": null,
2167 + "status": "successful",
2168 + "notes": "",
2169 + "is_late": false
2170 + },
2171 + {
2172 + "id": "C0148",
2173 + "activity_id": "A0013",
2174 + "user_id": "U0022",
2175 + "registration_id": "R0246",
2176 + "checkin_time": "2025-04-16 06:03:06",
2177 + "checkin_type": "QR code",
2178 + "location": {
2179 + "name": "深蓝咖啡馆",
2180 + "coordinates": "31.559993,119.096261"
2181 + },
2182 + "staff_id": null,
2183 + "status": "successful",
2184 + "notes": "",
2185 + "is_late": false
2186 + },
2187 + {
2188 + "id": "C0149",
2189 + "activity_id": "A0013",
2190 + "user_id": "U0003",
2191 + "registration_id": "R0247",
2192 + "checkin_time": "2025-04-16 05:52:52",
2193 + "checkin_type": "self-service",
2194 + "location": {
2195 + "name": "深蓝咖啡馆",
2196 + "coordinates": "39.656725,112.033980"
2197 + },
2198 + "staff_id": null,
2199 + "status": "successful",
2200 + "notes": "",
2201 + "is_late": false
2202 + },
2203 + {
2204 + "id": "C0150",
2205 + "activity_id": "A0013",
2206 + "user_id": "U0001",
2207 + "registration_id": "R0249",
2208 + "checkin_time": "2025-04-16 05:53:42",
2209 + "checkin_type": "QR code",
2210 + "location": {
2211 + "name": "深蓝咖啡馆",
2212 + "coordinates": "35.419700,117.373079"
2213 + },
2214 + "staff_id": null,
2215 + "status": "successful",
2216 + "notes": "",
2217 + "is_late": false
2218 + },
2219 + {
2220 + "id": "C0151",
2221 + "activity_id": "A0013",
2222 + "user_id": "U0036",
2223 + "registration_id": "R0250",
2224 + "checkin_time": "2025-04-16 06:22:22",
2225 + "checkin_type": "self-service",
2226 + "location": {
2227 + "name": "深蓝咖啡馆",
2228 + "coordinates": "22.718087,103.176777"
2229 + },
2230 + "staff_id": null,
2231 + "status": "successful",
2232 + "notes": "",
2233 + "is_late": true
2234 + },
2235 + {
2236 + "id": "C0152",
2237 + "activity_id": "A0013",
2238 + "user_id": "U0037",
2239 + "registration_id": "R0251",
2240 + "checkin_time": "2025-04-16 06:04:16",
2241 + "checkin_type": "QR code",
2242 + "location": {
2243 + "name": "深蓝咖啡馆",
2244 + "coordinates": "36.437377,116.319798"
2245 + },
2246 + "staff_id": null,
2247 + "status": "successful",
2248 + "notes": "",
2249 + "is_late": false
2250 + },
2251 + {
2252 + "id": "C0153",
2253 + "activity_id": "A0013",
2254 + "user_id": "U0010",
2255 + "registration_id": "R0252",
2256 + "checkin_time": "2025-04-16 06:06:45",
2257 + "checkin_type": "QR code",
2258 + "location": {
2259 + "name": "深蓝咖啡馆",
2260 + "coordinates": "25.502640,110.555487"
2261 + },
2262 + "staff_id": null,
2263 + "status": "failed",
2264 + "notes": "设备问题",
2265 + "is_late": false
2266 + },
2267 + {
2268 + "id": "C0154",
2269 + "activity_id": "A0013",
2270 + "user_id": "U0004",
2271 + "registration_id": "R0254",
2272 + "checkin_time": "2025-04-16 06:00:23",
2273 + "checkin_type": "QR code",
2274 + "location": {
2275 + "name": "深蓝咖啡馆",
2276 + "coordinates": "38.473438,101.763058"
2277 + },
2278 + "staff_id": null,
2279 + "status": "successful",
2280 + "notes": "",
2281 + "is_late": false
2282 + },
2283 + {
2284 + "id": "C0155",
2285 + "activity_id": "A0013",
2286 + "user_id": "U0005",
2287 + "registration_id": "R0255",
2288 + "checkin_time": "2025-04-16 06:50:21",
2289 + "checkin_type": "QR code",
2290 + "location": {
2291 + "name": "深蓝咖啡馆",
2292 + "coordinates": "25.433843,111.547821"
2293 + },
2294 + "staff_id": null,
2295 + "status": "successful",
2296 + "notes": "",
2297 + "is_late": true
2298 + },
2299 + {
2300 + "id": "C0156",
2301 + "activity_id": "A0013",
2302 + "user_id": "U0025",
2303 + "registration_id": "R0256",
2304 + "checkin_time": "2025-04-16 06:06:05",
2305 + "checkin_type": "manual",
2306 + "location": {
2307 + "name": "深蓝咖啡馆",
2308 + "coordinates": "33.246812,102.422719"
2309 + },
2310 + "staff_id": "O0003",
2311 + "status": "successful",
2312 + "notes": "",
2313 + "is_late": false
2314 + },
2315 + {
2316 + "id": "C0157",
2317 + "activity_id": "A0015",
2318 + "user_id": "U0035",
2319 + "registration_id": "R0263",
2320 + "checkin_time": "2025-04-16 06:21:26",
2321 + "checkin_type": "QR code",
2322 + "location": null,
2323 + "staff_id": null,
2324 + "status": "successful",
2325 + "notes": "",
2326 + "is_late": false
2327 + },
2328 + {
2329 + "id": "C0158",
2330 + "activity_id": "A0015",
2331 + "user_id": "U0010",
2332 + "registration_id": "R0264",
2333 + "checkin_time": "2025-04-16 06:35:32",
2334 + "checkin_type": "QR code",
2335 + "location": null,
2336 + "staff_id": null,
2337 + "status": "successful",
2338 + "notes": "",
2339 + "is_late": true
2340 + },
2341 + {
2342 + "id": "C0159",
2343 + "activity_id": "A0015",
2344 + "user_id": "U0015",
2345 + "registration_id": "R0265",
2346 + "checkin_time": "2025-04-16 05:52:07",
2347 + "checkin_type": "QR code",
2348 + "location": null,
2349 + "staff_id": null,
2350 + "status": "successful",
2351 + "notes": "",
2352 + "is_late": false
2353 + },
2354 + {
2355 + "id": "C0160",
2356 + "activity_id": "A0015",
2357 + "user_id": "U0005",
2358 + "registration_id": "R0266",
2359 + "checkin_time": "2025-04-16 06:18:17",
2360 + "checkin_type": "QR code",
2361 + "location": null,
2362 + "staff_id": null,
2363 + "status": "successful",
2364 + "notes": "",
2365 + "is_late": false
2366 + },
2367 + {
2368 + "id": "C0161",
2369 + "activity_id": "A0015",
2370 + "user_id": "U0001",
2371 + "registration_id": "R0267",
2372 + "checkin_time": "2025-04-16 06:05:44",
2373 + "checkin_type": "self-service",
2374 + "location": null,
2375 + "staff_id": null,
2376 + "status": "successful",
2377 + "notes": "准备充分",
2378 + "is_late": true
2379 + },
2380 + {
2381 + "id": "C0162",
2382 + "activity_id": "A0015",
2383 + "user_id": "U0025",
2384 + "registration_id": "R0270",
2385 + "checkin_time": "2025-04-16 06:32:13",
2386 + "checkin_type": "QR code",
2387 + "location": null,
2388 + "staff_id": null,
2389 + "status": "successful",
2390 + "notes": "提前到达",
2391 + "is_late": false
2392 + },
2393 + {
2394 + "id": "C0163",
2395 + "activity_id": "A0015",
2396 + "user_id": "U0018",
2397 + "registration_id": "R0274",
2398 + "checkin_time": "2025-04-16 06:25:01",
2399 + "checkin_type": "QR code",
2400 + "location": null,
2401 + "staff_id": null,
2402 + "status": "successful",
2403 + "notes": "提前到达",
2404 + "is_late": true
2405 + },
2406 + {
2407 + "id": "C0164",
2408 + "activity_id": "A0015",
2409 + "user_id": "U0019",
2410 + "registration_id": "R0275",
2411 + "checkin_time": "2025-04-16 06:13:07",
2412 + "checkin_type": "QR code",
2413 + "location": null,
2414 + "staff_id": null,
2415 + "status": "successful",
2416 + "notes": "",
2417 + "is_late": false
2418 + },
2419 + {
2420 + "id": "C0165",
2421 + "activity_id": "A0022",
2422 + "user_id": "U0026",
2423 + "registration_id": "R0304",
2424 + "checkin_time": "2025-04-16 07:15:52",
2425 + "checkin_type": "manual",
2426 + "location": {
2427 + "name": "大雁塔文化空间",
2428 + "coordinates": "26.580414,125.344607"
2429 + },
2430 + "staff_id": "O0005",
2431 + "status": "successful",
2432 + "notes": "",
2433 + "is_late": true
2434 + },
2435 + {
2436 + "id": "C0166",
2437 + "activity_id": "A0022",
2438 + "user_id": "U0016",
2439 + "registration_id": "R0307",
2440 + "checkin_time": "2025-04-16 06:48:22",
2441 + "checkin_type": "QR code",
2442 + "location": {
2443 + "name": "大雁塔文化空间",
2444 + "coordinates": "24.470878,121.132080"
2445 + },
2446 + "staff_id": null,
2447 + "status": "successful",
2448 + "notes": "",
2449 + "is_late": false
2450 + },
2451 + {
2452 + "id": "C0167",
2453 + "activity_id": "A0022",
2454 + "user_id": "U0001",
2455 + "registration_id": "R0310",
2456 + "checkin_time": "2025-04-16 06:28:54",
2457 + "checkin_type": "QR code",
2458 + "location": {
2459 + "name": "大雁塔文化空间",
2460 + "coordinates": "28.449496,109.032109"
2461 + },
2462 + "staff_id": null,
2463 + "status": "successful",
2464 + "notes": "按时到达",
2465 + "is_late": false
2466 + },
2467 + {
2468 + "id": "C0168",
2469 + "activity_id": "A0022",
2470 + "user_id": "U0008",
2471 + "registration_id": "R0312",
2472 + "checkin_time": "2025-04-16 06:55:34",
2473 + "checkin_type": "QR code",
2474 + "location": {
2475 + "name": "大雁塔文化空间",
2476 + "coordinates": "36.733507,110.478652"
2477 + },
2478 + "staff_id": null,
2479 + "status": "successful",
2480 + "notes": "",
2481 + "is_late": false
2482 + },
2483 + {
2484 + "id": "C0169",
2485 + "activity_id": "A0022",
2486 + "user_id": "U0021",
2487 + "registration_id": "R0313",
2488 + "checkin_time": "2025-04-16 06:27:02",
2489 + "checkin_type": "self-service",
2490 + "location": {
2491 + "name": "大雁塔文化空间",
2492 + "coordinates": "38.460924,120.561687"
2493 + },
2494 + "staff_id": null,
2495 + "status": "successful",
2496 + "notes": "",
2497 + "is_late": false
2498 + },
2499 + {
2500 + "id": "C0170",
2501 + "activity_id": "A0022",
2502 + "user_id": "U0035",
2503 + "registration_id": "R0314",
2504 + "checkin_time": "2025-04-16 06:31:10",
2505 + "checkin_type": "QR code",
2506 + "location": {
2507 + "name": "大雁塔文化空间",
2508 + "coordinates": "26.603106,100.136011"
2509 + },
2510 + "staff_id": null,
2511 + "status": "successful",
2512 + "notes": "",
2513 + "is_late": false
2514 + },
2515 + {
2516 + "id": "C0171",
2517 + "activity_id": "A0022",
2518 + "user_id": "U0009",
2519 + "registration_id": "R0316",
2520 + "checkin_time": "2025-04-16 06:23:40",
2521 + "checkin_type": "manual",
2522 + "location": {
2523 + "name": "大雁塔文化空间",
2524 + "coordinates": "34.533322,104.144383"
2525 + },
2526 + "staff_id": "O0005",
2527 + "status": "successful",
2528 + "notes": "",
2529 + "is_late": false
2530 + },
2531 + {
2532 + "id": "C0172",
2533 + "activity_id": "A0022",
2534 + "user_id": "U0015",
2535 + "registration_id": "R0317",
2536 + "checkin_time": "2025-04-16 06:55:20",
2537 + "checkin_type": "QR code",
2538 + "location": {
2539 + "name": "大雁塔文化空间",
2540 + "coordinates": "23.284204,106.163339"
2541 + },
2542 + "staff_id": null,
2543 + "status": "successful",
2544 + "notes": "热情参与讨论",
2545 + "is_late": false
2546 + },
2547 + {
2548 + "id": "C0173",
2549 + "activity_id": "A0022",
2550 + "user_id": "U0036",
2551 + "registration_id": "R0318",
2552 + "checkin_time": "2025-04-16 06:47:27",
2553 + "checkin_type": "QR code",
2554 + "location": {
2555 + "name": "大雁塔文化空间",
2556 + "coordinates": "32.940263,122.500088"
2557 + },
2558 + "staff_id": null,
2559 + "status": "successful",
2560 + "notes": "",
2561 + "is_late": true
2562 + },
2563 + {
2564 + "id": "C0174",
2565 + "activity_id": "A0022",
2566 + "user_id": "U0012",
2567 + "registration_id": "R0319",
2568 + "checkin_time": "2025-04-16 06:35:18",
2569 + "checkin_type": "QR code",
2570 + "location": {
2571 + "name": "大雁塔文化空间",
2572 + "coordinates": "23.633884,125.935592"
2573 + },
2574 + "staff_id": null,
2575 + "status": "successful",
2576 + "notes": "",
2577 + "is_late": false
2578 + },
2579 + {
2580 + "id": "C0175",
2581 + "activity_id": "A0022",
2582 + "user_id": "U0020",
2583 + "registration_id": "R0320",
2584 + "checkin_time": "2025-04-16 06:44:24",
2585 + "checkin_type": "manual",
2586 + "location": {
2587 + "name": "大雁塔文化空间",
2588 + "coordinates": "30.891166,113.054445"
2589 + },
2590 + "staff_id": "O0005",
2591 + "status": "successful",
2592 + "notes": "",
2593 + "is_late": false
2594 + },
2595 + {
2596 + "id": "C0176",
2597 + "activity_id": "A0022",
2598 + "user_id": "U0017",
2599 + "registration_id": "R0321",
2600 + "checkin_time": "2025-04-16 06:18:29",
2601 + "checkin_type": "QR code",
2602 + "location": {
2603 + "name": "大雁塔文化空间",
2604 + "coordinates": "38.287921,119.065611"
2605 + },
2606 + "staff_id": null,
2607 + "status": "successful",
2608 + "notes": "",
2609 + "is_late": false
2610 + },
2611 + {
2612 + "id": "C0177",
2613 + "activity_id": "A0022",
2614 + "user_id": "U0004",
2615 + "registration_id": "R0324",
2616 + "checkin_time": "2025-04-16 06:48:23",
2617 + "checkin_type": "QR code",
2618 + "location": {
2619 + "name": "大雁塔文化空间",
2620 + "coordinates": "30.466273,121.305050"
2621 + },
2622 + "staff_id": null,
2623 + "status": "successful",
2624 + "notes": "提前到达",
2625 + "is_late": false
2626 + },
2627 + {
2628 + "id": "C0178",
2629 + "activity_id": "A0022",
2630 + "user_id": "U0031",
2631 + "registration_id": "R0327",
2632 + "checkin_time": "2025-04-16 06:22:20",
2633 + "checkin_type": "QR code",
2634 + "location": {
2635 + "name": "大雁塔文化空间",
2636 + "coordinates": "30.379818,116.809350"
2637 + },
2638 + "staff_id": null,
2639 + "status": "successful",
2640 + "notes": "",
2641 + "is_late": false
2642 + },
2643 + {
2644 + "id": "C0179",
2645 + "activity_id": "A0022",
2646 + "user_id": "U0037",
2647 + "registration_id": "R0328",
2648 + "checkin_time": "2025-04-16 06:28:39",
2649 + "checkin_type": "QR code",
2650 + "location": {
2651 + "name": "大雁塔文化空间",
2652 + "coordinates": "26.477816,122.575710"
2653 + },
2654 + "staff_id": null,
2655 + "status": "successful",
2656 + "notes": "",
2657 + "is_late": false
2658 + },
2659 + {
2660 + "id": "C0180",
2661 + "activity_id": "A0022",
2662 + "user_id": "U0007",
2663 + "registration_id": "R0329",
2664 + "checkin_time": "2025-04-16 06:37:26",
2665 + "checkin_type": "QR code",
2666 + "location": {
2667 + "name": "大雁塔文化空间",
2668 + "coordinates": "25.742254,108.270197"
2669 + },
2670 + "staff_id": null,
2671 + "status": "successful",
2672 + "notes": "",
2673 + "is_late": false
2674 + },
2675 + {
2676 + "id": "C0181",
2677 + "activity_id": "A0022",
2678 + "user_id": "U0018",
2679 + "registration_id": "R0331",
2680 + "checkin_time": "2025-04-16 06:59:00",
2681 + "checkin_type": "QR code",
2682 + "location": {
2683 + "name": "大雁塔文化空间",
2684 + "coordinates": "27.938128,105.012614"
2685 + },
2686 + "staff_id": null,
2687 + "status": "successful",
2688 + "notes": "",
2689 + "is_late": false
2690 + },
2691 + {
2692 + "id": "C0182",
2693 + "activity_id": "A0022",
2694 + "user_id": "U0030",
2695 + "registration_id": "R0332",
2696 + "checkin_time": "2025-04-16 07:01:36",
2697 + "checkin_type": "manual",
2698 + "location": {
2699 + "name": "大雁塔文化空间",
2700 + "coordinates": "39.677261,117.105251"
2701 + },
2702 + "staff_id": "O0005",
2703 + "status": "successful",
2704 + "notes": "",
2705 + "is_late": true
2706 + },
2707 + {
2708 + "id": "C0183",
2709 + "activity_id": "A0022",
2710 + "user_id": "U0006",
2711 + "registration_id": "R0333",
2712 + "checkin_time": "2025-04-16 06:51:37",
2713 + "checkin_type": "QR code",
2714 + "location": {
2715 + "name": "大雁塔文化空间",
2716 + "coordinates": "26.249063,116.245331"
2717 + },
2718 + "staff_id": null,
2719 + "status": "successful",
2720 + "notes": "",
2721 + "is_late": false
2722 + },
2723 + {
2724 + "id": "C0184",
2725 + "activity_id": "A0022",
2726 + "user_id": "U0014",
2727 + "registration_id": "R0336",
2728 + "checkin_time": "2025-04-16 06:33:41",
2729 + "checkin_type": "QR code",
2730 + "location": {
2731 + "name": "大雁塔文化空间",
2732 + "coordinates": "32.162403,115.813070"
2733 + },
2734 + "staff_id": null,
2735 + "status": "successful",
2736 + "notes": "",
2737 + "is_late": false
2738 + },
2739 + {
2740 + "id": "C0185",
2741 + "activity_id": "A0022",
2742 + "user_id": "U0024",
2743 + "registration_id": "R0338",
2744 + "checkin_time": "2025-04-16 06:54:14",
2745 + "checkin_type": "QR code",
2746 + "location": {
2747 + "name": "大雁塔文化空间",
2748 + "coordinates": "25.927681,123.295939"
2749 + },
2750 + "staff_id": null,
2751 + "status": "successful",
2752 + "notes": "",
2753 + "is_late": false
2754 + },
2755 + {
2756 + "id": "C0186",
2757 + "activity_id": "A0022",
2758 + "user_id": "U0003",
2759 + "registration_id": "R0339",
2760 + "checkin_time": "2025-04-16 06:37:10",
2761 + "checkin_type": "QR code",
2762 + "location": {
2763 + "name": "大雁塔文化空间",
2764 + "coordinates": "24.967249,100.833877"
2765 + },
2766 + "staff_id": null,
2767 + "status": "successful",
2768 + "notes": "",
2769 + "is_late": false
2770 + },
2771 + {
2772 + "id": "C0187",
2773 + "activity_id": "A0022",
2774 + "user_id": "U0033",
2775 + "registration_id": "R0340",
2776 + "checkin_time": "2025-04-16 06:40:31",
2777 + "checkin_type": "QR code",
2778 + "location": {
2779 + "name": "大雁塔文化空间",
2780 + "coordinates": "34.843743,113.933664"
2781 + },
2782 + "staff_id": null,
2783 + "status": "successful",
2784 + "notes": "",
2785 + "is_late": false
2786 + },
2787 + {
2788 + "id": "C0188",
2789 + "activity_id": "A0022",
2790 + "user_id": "U0011",
2791 + "registration_id": "R0342",
2792 + "checkin_time": "2025-04-16 06:44:31",
2793 + "checkin_type": "QR code",
2794 + "location": {
2795 + "name": "大雁塔文化空间",
2796 + "coordinates": "38.968901,123.655330"
2797 + },
2798 + "staff_id": null,
2799 + "status": "successful",
2800 + "notes": "",
2801 + "is_late": true
2802 + },
2803 + {
2804 + "id": "C0189",
2805 + "activity_id": "A0022",
2806 + "user_id": "U0005",
2807 + "registration_id": "R0343",
2808 + "checkin_time": "2025-04-16 06:18:06",
2809 + "checkin_type": "self-service",
2810 + "location": {
2811 + "name": "大雁塔文化空间",
2812 + "coordinates": "22.517659,116.229674"
2813 + },
2814 + "staff_id": null,
2815 + "status": "successful",
2816 + "notes": "",
2817 + "is_late": false
2818 + },
2819 + {
2820 + "id": "C0190",
2821 + "activity_id": "A0023",
2822 + "user_id": "U0014",
2823 + "registration_id": "R0344",
2824 + "checkin_time": "2025-03-27 05:16:40",
2825 + "checkin_type": "QR code",
2826 + "location": null,
2827 + "staff_id": null,
2828 + "status": "successful",
2829 + "notes": "提前到达",
2830 + "is_late": false
2831 + },
2832 + {
2833 + "id": "C0191",
2834 + "activity_id": "A0023",
2835 + "user_id": "U0006",
2836 + "registration_id": "R0346",
2837 + "checkin_time": "2025-03-27 05:35:36",
2838 + "checkin_type": "QR code",
2839 + "location": null,
2840 + "staff_id": null,
2841 + "status": "successful",
2842 + "notes": "提前到达",
2843 + "is_late": false
2844 + },
2845 + {
2846 + "id": "C0192",
2847 + "activity_id": "A0023",
2848 + "user_id": "U0030",
2849 + "registration_id": "R0347",
2850 + "checkin_time": "2025-03-27 05:23:26",
2851 + "checkin_type": "QR code",
2852 + "location": null,
2853 + "staff_id": null,
2854 + "status": "successful",
2855 + "notes": "",
2856 + "is_late": false
2857 + },
2858 + {
2859 + "id": "C0193",
2860 + "activity_id": "A0023",
2861 + "user_id": "U0035",
2862 + "registration_id": "R0348",
2863 + "checkin_time": "2025-03-27 05:11:26",
2864 + "checkin_type": "QR code",
2865 + "location": null,
2866 + "staff_id": null,
2867 + "status": "successful",
2868 + "notes": "",
2869 + "is_late": false
2870 + },
2871 + {
2872 + "id": "C0194",
2873 + "activity_id": "A0023",
2874 + "user_id": "U0007",
2875 + "registration_id": "R0350",
2876 + "checkin_time": "2025-03-27 05:05:40",
2877 + "checkin_type": "QR code",
2878 + "location": null,
2879 + "staff_id": null,
2880 + "status": "failed",
2881 + "notes": "签到后临时离开",
2882 + "is_late": false
2883 + },
2884 + {
2885 + "id": "C0195",
2886 + "activity_id": "A0023",
2887 + "user_id": "U0016",
2888 + "registration_id": "R0351",
2889 + "checkin_time": "2025-03-27 05:30:46",
2890 + "checkin_type": "QR code",
2891 + "location": null,
2892 + "staff_id": null,
2893 + "status": "successful",
2894 + "notes": "",
2895 + "is_late": true
2896 + },
2897 + {
2898 + "id": "C0196",
2899 + "activity_id": "A0023",
2900 + "user_id": "U0011",
2901 + "registration_id": "R0352",
2902 + "checkin_time": "2025-03-27 05:32:44",
2903 + "checkin_type": "manual",
2904 + "location": null,
2905 + "staff_id": "O0007",
2906 + "status": "successful",
2907 + "notes": "",
2908 + "is_late": false
2909 + },
2910 + {
2911 + "id": "C0197",
2912 + "activity_id": "A0023",
2913 + "user_id": "U0003",
2914 + "registration_id": "R0353",
2915 + "checkin_time": "2025-03-27 05:31:42",
2916 + "checkin_type": "QR code",
2917 + "location": null,
2918 + "staff_id": null,
2919 + "status": "successful",
2920 + "notes": "提前到达",
2921 + "is_late": false
2922 + },
2923 + {
2924 + "id": "C0198",
2925 + "activity_id": "A0023",
2926 + "user_id": "U0029",
2927 + "registration_id": "R0354",
2928 + "checkin_time": "2025-03-27 04:59:34",
2929 + "checkin_type": "QR code",
2930 + "location": null,
2931 + "staff_id": null,
2932 + "status": "successful",
2933 + "notes": "",
2934 + "is_late": false
2935 + },
2936 + {
2937 + "id": "C0199",
2938 + "activity_id": "A0023",
2939 + "user_id": "U0034",
2940 + "registration_id": "R0355",
2941 + "checkin_time": "2025-03-27 05:32:02",
2942 + "checkin_type": "manual",
2943 + "location": null,
2944 + "staff_id": "O0007",
2945 + "status": "successful",
2946 + "notes": "",
2947 + "is_late": true
2948 + },
2949 + {
2950 + "id": "C0200",
2951 + "activity_id": "A0023",
2952 + "user_id": "U0002",
2953 + "registration_id": "R0356",
2954 + "checkin_time": "2025-03-27 05:26:35",
2955 + "checkin_type": "QR code",
2956 + "location": null,
2957 + "staff_id": null,
2958 + "status": "successful",
2959 + "notes": "",
2960 + "is_late": false
2961 + },
2962 + {
2963 + "id": "C0201",
2964 + "activity_id": "A0023",
2965 + "user_id": "U0026",
2966 + "registration_id": "R0357",
2967 + "checkin_time": "2025-03-27 05:05:43",
2968 + "checkin_type": "manual",
2969 + "location": null,
2970 + "staff_id": "O0007",
2971 + "status": "successful",
2972 + "notes": "按时到达",
2973 + "is_late": false
2974 + },
2975 + {
2976 + "id": "C0202",
2977 + "activity_id": "A0023",
2978 + "user_id": "U0008",
2979 + "registration_id": "R0358",
2980 + "checkin_time": "2025-03-27 05:26:39",
2981 + "checkin_type": "manual",
2982 + "location": null,
2983 + "staff_id": "O0007",
2984 + "status": "successful",
2985 + "notes": "",
2986 + "is_late": false
2987 + },
2988 + {
2989 + "id": "C0203",
2990 + "activity_id": "A0023",
2991 + "user_id": "U0020",
2992 + "registration_id": "R0360",
2993 + "checkin_time": "2025-03-27 05:57:08",
2994 + "checkin_type": "manual",
2995 + "location": null,
2996 + "staff_id": "O0007",
2997 + "status": "successful",
2998 + "notes": "",
2999 + "is_late": true
3000 + },
3001 + {
3002 + "id": "C0204",
3003 + "activity_id": "A0023",
3004 + "user_id": "U0038",
3005 + "registration_id": "R0361",
3006 + "checkin_time": "2025-03-27 05:23:01",
3007 + "checkin_type": "QR code",
3008 + "location": null,
3009 + "staff_id": null,
3010 + "status": "successful",
3011 + "notes": "热情参与讨论",
3012 + "is_late": false
3013 + },
3014 + {
3015 + "id": "C0205",
3016 + "activity_id": "A0023",
3017 + "user_id": "U0024",
3018 + "registration_id": "R0362",
3019 + "checkin_time": "2025-03-27 05:33:56",
3020 + "checkin_type": "QR code",
3021 + "location": null,
3022 + "staff_id": null,
3023 + "status": "successful",
3024 + "notes": "",
3025 + "is_late": false
3026 + },
3027 + {
3028 + "id": "C0206",
3029 + "activity_id": "A0023",
3030 + "user_id": "U0025",
3031 + "registration_id": "R0363",
3032 + "checkin_time": "2025-03-27 05:23:06",
3033 + "checkin_type": "self-service",
3034 + "location": null,
3035 + "staff_id": null,
3036 + "status": "successful",
3037 + "notes": "",
3038 + "is_late": false
3039 + },
3040 + {
3041 + "id": "C0207",
3042 + "activity_id": "A0023",
3043 + "user_id": "U0001",
3044 + "registration_id": "R0365",
3045 + "checkin_time": "2025-03-27 05:18:27",
3046 + "checkin_type": "QR code",
3047 + "location": null,
3048 + "staff_id": null,
3049 + "status": "successful",
3050 + "notes": "",
3051 + "is_late": true
3052 + },
3053 + {
3054 + "id": "C0208",
3055 + "activity_id": "A0023",
3056 + "user_id": "U0019",
3057 + "registration_id": "R0366",
3058 + "checkin_time": "2025-03-27 05:34:53",
3059 + "checkin_type": "QR code",
3060 + "location": null,
3061 + "staff_id": null,
3062 + "status": "successful",
3063 + "notes": "",
3064 + "is_late": false
3065 + },
3066 + {
3067 + "id": "C0209",
3068 + "activity_id": "A0023",
3069 + "user_id": "U0027",
3070 + "registration_id": "R0370",
3071 + "checkin_time": "2025-03-27 05:33:23",
3072 + "checkin_type": "QR code",
3073 + "location": null,
3074 + "staff_id": null,
3075 + "status": "successful",
3076 + "notes": "提前到达",
3077 + "is_late": true
3078 + },
3079 + {
3080 + "id": "C0210",
3081 + "activity_id": "A0023",
3082 + "user_id": "U0036",
3083 + "registration_id": "R0371",
3084 + "checkin_time": "2025-03-27 05:22:50",
3085 + "checkin_type": "manual",
3086 + "location": null,
3087 + "staff_id": "O0007",
3088 + "status": "successful",
3089 + "notes": "",
3090 + "is_late": false
3091 + },
3092 + {
3093 + "id": "C0211",
3094 + "activity_id": "A0023",
3095 + "user_id": "U0031",
3096 + "registration_id": "R0373",
3097 + "checkin_time": "2025-03-27 05:34:33",
3098 + "checkin_type": "manual",
3099 + "location": null,
3100 + "staff_id": "O0007",
3101 + "status": "successful",
3102 + "notes": "提前到达",
3103 + "is_late": false
3104 + },
3105 + {
3106 + "id": "C0212",
3107 + "activity_id": "A0023",
3108 + "user_id": "U0037",
3109 + "registration_id": "R0374",
3110 + "checkin_time": "2025-03-27 05:27:34",
3111 + "checkin_type": "QR code",
3112 + "location": null,
3113 + "staff_id": null,
3114 + "status": "successful",
3115 + "notes": "热情参与讨论",
3116 + "is_late": false
3117 + },
3118 + {
3119 + "id": "C0213",
3120 + "activity_id": "A0023",
3121 + "user_id": "U0013",
3122 + "registration_id": "R0375",
3123 + "checkin_time": "2025-03-27 05:23:16",
3124 + "checkin_type": "QR code",
3125 + "location": null,
3126 + "staff_id": null,
3127 + "status": "successful",
3128 + "notes": "带了书本",
3129 + "is_late": false
3130 + },
3131 + {
3132 + "id": "C0214",
3133 + "activity_id": "A0023",
3134 + "user_id": "U0005",
3135 + "registration_id": "R0376",
3136 + "checkin_time": "2025-03-27 05:11:53",
3137 + "checkin_type": "QR code",
3138 + "location": null,
3139 + "staff_id": null,
3140 + "status": "failed",
3141 + "notes": "网络连接不稳定",
3142 + "is_late": false
3143 + },
3144 + {
3145 + "id": "C0215",
3146 + "activity_id": "A0023",
3147 + "user_id": "U0039",
3148 + "registration_id": "R0377",
3149 + "checkin_time": "2025-03-27 05:01:36",
3150 + "checkin_type": "manual",
3151 + "location": null,
3152 + "staff_id": "O0007",
3153 + "status": "successful",
3154 + "notes": "",
3155 + "is_late": false
3156 + },
3157 + {
3158 + "id": "C0216",
3159 + "activity_id": "A0023",
3160 + "user_id": "U0017",
3161 + "registration_id": "R0379",
3162 + "checkin_time": "2025-03-27 05:31:07",
3163 + "checkin_type": "QR code",
3164 + "location": null,
3165 + "staff_id": null,
3166 + "status": "successful",
3167 + "notes": "",
3168 + "is_late": false
3169 + },
3170 + {
3171 + "id": "C0217",
3172 + "activity_id": "A0023",
3173 + "user_id": "U0004",
3174 + "registration_id": "R0380",
3175 + "checkin_time": "2025-03-27 05:42:59",
3176 + "checkin_type": "manual",
3177 + "location": null,
3178 + "staff_id": "O0007",
3179 + "status": "successful",
3180 + "notes": "准备充分",
3181 + "is_late": false
3182 + },
3183 + {
3184 + "id": "C0218",
3185 + "activity_id": "A0023",
3186 + "user_id": "U0015",
3187 + "registration_id": "R0381",
3188 + "checkin_time": "2025-03-27 05:45:54",
3189 + "checkin_type": "self-service",
3190 + "location": null,
3191 + "staff_id": null,
3192 + "status": "successful",
3193 + "notes": "",
3194 + "is_late": true
3195 + },
3196 + {
3197 + "id": "C0219",
3198 + "activity_id": "A0023",
3199 + "user_id": "U0009",
3200 + "registration_id": "R0382",
3201 + "checkin_time": "2025-03-27 05:26:18",
3202 + "checkin_type": "manual",
3203 + "location": null,
3204 + "staff_id": "O0007",
3205 + "status": "successful",
3206 + "notes": "带了书本",
3207 + "is_late": true
3208 + },
3209 + {
3210 + "id": "C0220",
3211 + "activity_id": "A0023",
3212 + "user_id": "U0040",
3213 + "registration_id": "R0383",
3214 + "checkin_time": "2025-03-27 05:30:24",
3215 + "checkin_type": "QR code",
3216 + "location": null,
3217 + "staff_id": null,
3218 + "status": "successful",
3219 + "notes": "",
3220 + "is_late": false
3221 + },
3222 + {
3223 + "id": "C0221",
3224 + "activity_id": "A0027",
3225 + "user_id": "U0017",
3226 + "registration_id": "R0407",
3227 + "checkin_time": "2025-04-16 05:32:53",
3228 + "checkin_type": "QR code",
3229 + "location": {
3230 + "name": "大雁塔文化空间",
3231 + "coordinates": "38.833814,114.514317"
3232 + },
3233 + "staff_id": null,
3234 + "status": "successful",
3235 + "notes": "提前到达",
3236 + "is_late": true
3237 + },
3238 + {
3239 + "id": "C0222",
3240 + "activity_id": "A0027",
3241 + "user_id": "U0031",
3242 + "registration_id": "R0408",
3243 + "checkin_time": "2025-04-16 05:46:08",
3244 + "checkin_type": "self-service",
3245 + "location": {
3246 + "name": "大雁塔文化空间",
3247 + "coordinates": "27.781089,102.822173"
3248 + },
3249 + "staff_id": null,
3250 + "status": "successful",
3251 + "notes": "",
3252 + "is_late": false
3253 + },
3254 + {
3255 + "id": "C0223",
3256 + "activity_id": "A0027",
3257 + "user_id": "U0029",
3258 + "registration_id": "R0410",
3259 + "checkin_time": "2025-04-16 05:47:53",
3260 + "checkin_type": "QR code",
3261 + "location": {
3262 + "name": "大雁塔文化空间",
3263 + "coordinates": "27.308599,114.704434"
3264 + },
3265 + "staff_id": null,
3266 + "status": "successful",
3267 + "notes": "",
3268 + "is_late": false
3269 + },
3270 + {
3271 + "id": "C0224",
3272 + "activity_id": "A0027",
3273 + "user_id": "U0007",
3274 + "registration_id": "R0411",
3275 + "checkin_time": "2025-04-16 05:40:07",
3276 + "checkin_type": "QR code",
3277 + "location": {
3278 + "name": "大雁塔文化空间",
3279 + "coordinates": "38.156809,110.024987"
3280 + },
3281 + "staff_id": null,
3282 + "status": "successful",
3283 + "notes": "",
3284 + "is_late": false
3285 + },
3286 + {
3287 + "id": "C0225",
3288 + "activity_id": "A0027",
3289 + "user_id": "U0022",
3290 + "registration_id": "R0414",
3291 + "checkin_time": "2025-04-16 05:55:30",
3292 + "checkin_type": "QR code",
3293 + "location": {
3294 + "name": "大雁塔文化空间",
3295 + "coordinates": "30.231849,102.497485"
3296 + },
3297 + "staff_id": null,
3298 + "status": "successful",
3299 + "notes": "",
3300 + "is_late": true
3301 + },
3302 + {
3303 + "id": "C0226",
3304 + "activity_id": "A0027",
3305 + "user_id": "U0027",
3306 + "registration_id": "R0417",
3307 + "checkin_time": "2025-04-16 05:54:23",
3308 + "checkin_type": "QR code",
3309 + "location": {
3310 + "name": "大雁塔文化空间",
3311 + "coordinates": "30.564601,104.408038"
3312 + },
3313 + "staff_id": null,
3314 + "status": "successful",
3315 + "notes": "",
3316 + "is_late": true
3317 + },
3318 + {
3319 + "id": "C0227",
3320 + "activity_id": "A0027",
3321 + "user_id": "U0005",
3322 + "registration_id": "R0418",
3323 + "checkin_time": "2025-04-16 05:47:45",
3324 + "checkin_type": "QR code",
3325 + "location": {
3326 + "name": "大雁塔文化空间",
3327 + "coordinates": "24.122715,109.827973"
3328 + },
3329 + "staff_id": null,
3330 + "status": "successful",
3331 + "notes": "",
3332 + "is_late": false
3333 + },
3334 + {
3335 + "id": "C0228",
3336 + "activity_id": "A0027",
3337 + "user_id": "U0013",
3338 + "registration_id": "R0419",
3339 + "checkin_time": "2025-04-16 05:27:24",
3340 + "checkin_type": "QR code",
3341 + "location": {
3342 + "name": "大雁塔文化空间",
3343 + "coordinates": "25.549267,111.522543"
3344 + },
3345 + "staff_id": null,
3346 + "status": "successful",
3347 + "notes": "",
3348 + "is_late": true
3349 + },
3350 + {
3351 + "id": "C0229",
3352 + "activity_id": "A0027",
3353 + "user_id": "U0037",
3354 + "registration_id": "R0421",
3355 + "checkin_time": "2025-04-16 05:22:07",
3356 + "checkin_type": "QR code",
3357 + "location": {
3358 + "name": "大雁塔文化空间",
3359 + "coordinates": "26.680011,113.731917"
3360 + },
3361 + "staff_id": null,
3362 + "status": "successful",
3363 + "notes": "准备充分",
3364 + "is_late": true
3365 + },
3366 + {
3367 + "id": "C0230",
3368 + "activity_id": "A0027",
3369 + "user_id": "U0012",
3370 + "registration_id": "R0422",
3371 + "checkin_time": "2025-04-16 05:08:30",
3372 + "checkin_type": "QR code",
3373 + "location": {
3374 + "name": "大雁塔文化空间",
3375 + "coordinates": "30.777704,110.012542"
3376 + },
3377 + "staff_id": null,
3378 + "status": "successful",
3379 + "notes": "按时到达",
3380 + "is_late": false
3381 + },
3382 + {
3383 + "id": "C0231",
3384 + "activity_id": "A0027",
3385 + "user_id": "U0024",
3386 + "registration_id": "R0423",
3387 + "checkin_time": "2025-04-16 05:24:15",
3388 + "checkin_type": "QR code",
3389 + "location": {
3390 + "name": "大雁塔文化空间",
3391 + "coordinates": "30.010222,103.940968"
3392 + },
3393 + "staff_id": null,
3394 + "status": "successful",
3395 + "notes": "提前到达",
3396 + "is_late": true
3397 + },
3398 + {
3399 + "id": "C0232",
3400 + "activity_id": "A0027",
3401 + "user_id": "U0006",
3402 + "registration_id": "R0424",
3403 + "checkin_time": "2025-04-16 05:08:05",
3404 + "checkin_type": "QR code",
3405 + "location": {
3406 + "name": "大雁塔文化空间",
3407 + "coordinates": "33.278353,101.387988"
3408 + },
3409 + "staff_id": null,
3410 + "status": "successful",
3411 + "notes": "",
3412 + "is_late": false
3413 + },
3414 + {
3415 + "id": "C0233",
3416 + "activity_id": "A0030",
3417 + "user_id": "U0022",
3418 + "registration_id": "R0441",
3419 + "checkin_time": "2025-03-24 06:13:41",
3420 + "checkin_type": "QR code",
3421 + "location": {
3422 + "name": "大雁塔文化空间",
3423 + "coordinates": "28.787895,119.435720"
3424 + },
3425 + "staff_id": null,
3426 + "status": "successful",
3427 + "notes": "按时到达",
3428 + "is_late": false
3429 + },
3430 + {
3431 + "id": "C0234",
3432 + "activity_id": "A0030",
3433 + "user_id": "U0031",
3434 + "registration_id": "R0442",
3435 + "checkin_time": "2025-03-24 06:14:40",
3436 + "checkin_type": "manual",
3437 + "location": {
3438 + "name": "大雁塔文化空间",
3439 + "coordinates": "38.127912,104.147554"
3440 + },
3441 + "staff_id": "O0005",
3442 + "status": "successful",
3443 + "notes": "",
3444 + "is_late": false
3445 + },
3446 + {
3447 + "id": "C0235",
3448 + "activity_id": "A0030",
3449 + "user_id": "U0003",
3450 + "registration_id": "R0443",
3451 + "checkin_time": "2025-03-24 06:12:05",
3452 + "checkin_type": "QR code",
3453 + "location": {
3454 + "name": "大雁塔文化空间",
3455 + "coordinates": "36.552893,123.319240"
3456 + },
3457 + "staff_id": null,
3458 + "status": "successful",
3459 + "notes": "",
3460 + "is_late": false
3461 + },
3462 + {
3463 + "id": "C0236",
3464 + "activity_id": "A0030",
3465 + "user_id": "U0030",
3466 + "registration_id": "R0444",
3467 + "checkin_time": "2025-03-24 06:35:57",
3468 + "checkin_type": "QR code",
3469 + "location": {
3470 + "name": "大雁塔文化空间",
3471 + "coordinates": "37.606920,110.862144"
3472 + },
3473 + "staff_id": null,
3474 + "status": "successful",
3475 + "notes": "",
3476 + "is_late": false
3477 + },
3478 + {
3479 + "id": "C0237",
3480 + "activity_id": "A0030",
3481 + "user_id": "U0028",
3482 + "registration_id": "R0445",
3483 + "checkin_time": "2025-03-24 06:21:00",
3484 + "checkin_type": "manual",
3485 + "location": {
3486 + "name": "大雁塔文化空间",
3487 + "coordinates": "36.680083,106.083682"
3488 + },
3489 + "staff_id": "O0005",
3490 + "status": "successful",
3491 + "notes": "",
3492 + "is_late": true
3493 + },
3494 + {
3495 + "id": "C0238",
3496 + "activity_id": "A0030",
3497 + "user_id": "U0007",
3498 + "registration_id": "R0446",
3499 + "checkin_time": "2025-03-24 06:14:08",
3500 + "checkin_type": "QR code",
3501 + "location": {
3502 + "name": "大雁塔文化空间",
3503 + "coordinates": "39.916319,120.798035"
3504 + },
3505 + "staff_id": null,
3506 + "status": "failed",
3507 + "notes": "未携带所需材料",
3508 + "is_late": false
3509 + },
3510 + {
3511 + "id": "C0239",
3512 + "activity_id": "A0030",
3513 + "user_id": "U0034",
3514 + "registration_id": "R0447",
3515 + "checkin_time": "2025-03-24 06:21:39",
3516 + "checkin_type": "manual",
3517 + "location": {
3518 + "name": "大雁塔文化空间",
3519 + "coordinates": "35.329186,107.028931"
3520 + },
3521 + "staff_id": "O0005",
3522 + "status": "successful",
3523 + "notes": "",
3524 + "is_late": false
3525 + },
3526 + {
3527 + "id": "C0240",
3528 + "activity_id": "A0030",
3529 + "user_id": "U0032",
3530 + "registration_id": "R0449",
3531 + "checkin_time": "2025-03-24 06:19:22",
3532 + "checkin_type": "QR code",
3533 + "location": {
3534 + "name": "大雁塔文化空间",
3535 + "coordinates": "30.944494,124.919253"
3536 + },
3537 + "staff_id": null,
3538 + "status": "failed",
3539 + "notes": "迟到超过30分钟",
3540 + "is_late": false
3541 + },
3542 + {
3543 + "id": "C0241",
3544 + "activity_id": "A0030",
3545 + "user_id": "U0016",
3546 + "registration_id": "R0450",
3547 + "checkin_time": "2025-03-24 06:31:18",
3548 + "checkin_type": "self-service",
3549 + "location": {
3550 + "name": "大雁塔文化空间",
3551 + "coordinates": "30.156234,121.488007"
3552 + },
3553 + "staff_id": null,
3554 + "status": "failed",
3555 + "notes": "网络连接不稳定",
3556 + "is_late": false
3557 + },
3558 + {
3559 + "id": "C0242",
3560 + "activity_id": "A0030",
3561 + "user_id": "U0002",
3562 + "registration_id": "R0451",
3563 + "checkin_time": "2025-03-24 06:32:07",
3564 + "checkin_type": "QR code",
3565 + "location": {
3566 + "name": "大雁塔文化空间",
3567 + "coordinates": "36.425545,115.307617"
3568 + },
3569 + "staff_id": null,
3570 + "status": "successful",
3571 + "notes": "",
3572 + "is_late": false
3573 + },
3574 + {
3575 + "id": "C0243",
3576 + "activity_id": "A0030",
3577 + "user_id": "U0029",
3578 + "registration_id": "R0453",
3579 + "checkin_time": "2025-03-24 06:04:37",
3580 + "checkin_type": "self-service",
3581 + "location": {
3582 + "name": "大雁塔文化空间",
3583 + "coordinates": "30.531905,106.376309"
3584 + },
3585 + "staff_id": null,
3586 + "status": "successful",
3587 + "notes": "",
3588 + "is_late": false
3589 + },
3590 + {
3591 + "id": "C0244",
3592 + "activity_id": "A0030",
3593 + "user_id": "U0038",
3594 + "registration_id": "R0454",
3595 + "checkin_time": "2025-03-24 06:16:33",
3596 + "checkin_type": "self-service",
3597 + "location": {
3598 + "name": "大雁塔文化空间",
3599 + "coordinates": "30.017986,114.083724"
3600 + },
3601 + "staff_id": null,
3602 + "status": "successful",
3603 + "notes": "",
3604 + "is_late": true
3605 + },
3606 + {
3607 + "id": "C0245",
3608 + "activity_id": "A0030",
3609 + "user_id": "U0021",
3610 + "registration_id": "R0455",
3611 + "checkin_time": "2025-03-24 06:11:23",
3612 + "checkin_type": "QR code",
3613 + "location": {
3614 + "name": "大雁塔文化空间",
3615 + "coordinates": "27.571855,119.718238"
3616 + },
3617 + "staff_id": null,
3618 + "status": "successful",
3619 + "notes": "",
3620 + "is_late": false
3621 + },
3622 + {
3623 + "id": "C0246",
3624 + "activity_id": "A0030",
3625 + "user_id": "U0027",
3626 + "registration_id": "R0456",
3627 + "checkin_time": "2025-03-24 06:24:43",
3628 + "checkin_type": "QR code",
3629 + "location": {
3630 + "name": "大雁塔文化空间",
3631 + "coordinates": "38.706085,113.113853"
3632 + },
3633 + "staff_id": null,
3634 + "status": "successful",
3635 + "notes": "",
3636 + "is_late": false
3637 + },
3638 + {
3639 + "id": "C0247",
3640 + "activity_id": "A0030",
3641 + "user_id": "U0012",
3642 + "registration_id": "R0457",
3643 + "checkin_time": "2025-03-24 06:31:03",
3644 + "checkin_type": "manual",
3645 + "location": {
3646 + "name": "大雁塔文化空间",
3647 + "coordinates": "36.627228,124.621240"
3648 + },
3649 + "staff_id": "O0005",
3650 + "status": "successful",
3651 + "notes": "",
3652 + "is_late": false
3653 + },
3654 + {
3655 + "id": "C0248",
3656 + "activity_id": "A0030",
3657 + "user_id": "U0009",
3658 + "registration_id": "R0458",
3659 + "checkin_time": "2025-03-24 06:24:40",
3660 + "checkin_type": "QR code",
3661 + "location": {
3662 + "name": "大雁塔文化空间",
3663 + "coordinates": "28.284668,120.174935"
3664 + },
3665 + "staff_id": null,
3666 + "status": "successful",
3667 + "notes": "",
3668 + "is_late": false
3669 + },
3670 + {
3671 + "id": "C0249",
3672 + "activity_id": "A0030",
3673 + "user_id": "U0035",
3674 + "registration_id": "R0459",
3675 + "checkin_time": "2025-03-24 06:22:22",
3676 + "checkin_type": "QR code",
3677 + "location": {
3678 + "name": "大雁塔文化空间",
3679 + "coordinates": "25.338185,120.506832"
3680 + },
3681 + "staff_id": null,
3682 + "status": "successful",
3683 + "notes": "提前到达",
3684 + "is_late": true
3685 + },
3686 + {
3687 + "id": "C0250",
3688 + "activity_id": "A0030",
3689 + "user_id": "U0036",
3690 + "registration_id": "R0460",
3691 + "checkin_time": "2025-03-24 06:10:36",
3692 + "checkin_type": "QR code",
3693 + "location": {
3694 + "name": "大雁塔文化空间",
3695 + "coordinates": "39.576489,123.708963"
3696 + },
3697 + "staff_id": null,
3698 + "status": "successful",
3699 + "notes": "",
3700 + "is_late": false
3701 + },
3702 + {
3703 + "id": "C0251",
3704 + "activity_id": "A0030",
3705 + "user_id": "U0010",
3706 + "registration_id": "R0461",
3707 + "checkin_time": "2025-03-24 06:30:17",
3708 + "checkin_type": "QR code",
3709 + "location": {
3710 + "name": "大雁塔文化空间",
3711 + "coordinates": "25.560215,113.066388"
3712 + },
3713 + "staff_id": null,
3714 + "status": "successful",
3715 + "notes": "",
3716 + "is_late": false
3717 + },
3718 + {
3719 + "id": "C0252",
3720 + "activity_id": "A0030",
3721 + "user_id": "U0011",
3722 + "registration_id": "R0462",
3723 + "checkin_time": "2025-03-24 06:53:26",
3724 + "checkin_type": "QR code",
3725 + "location": {
3726 + "name": "大雁塔文化空间",
3727 + "coordinates": "24.201619,102.148571"
3728 + },
3729 + "staff_id": null,
3730 + "status": "successful",
3731 + "notes": "热情参与讨论",
3732 + "is_late": true
3733 + },
3734 + {
3735 + "id": "C0253",
3736 + "activity_id": "A0030",
3737 + "user_id": "U0040",
3738 + "registration_id": "R0464",
3739 + "checkin_time": "2025-03-24 06:28:18",
3740 + "checkin_type": "manual",
3741 + "location": {
3742 + "name": "大雁塔文化空间",
3743 + "coordinates": "27.166876,110.472657"
3744 + },
3745 + "staff_id": "O0005",
3746 + "status": "successful",
3747 + "notes": "准备充分",
3748 + "is_late": false
3749 + },
3750 + {
3751 + "id": "C0254",
3752 + "activity_id": "A0030",
3753 + "user_id": "U0005",
3754 + "registration_id": "R0465",
3755 + "checkin_time": "2025-03-24 06:16:32",
3756 + "checkin_type": "QR code",
3757 + "location": {
3758 + "name": "大雁塔文化空间",
3759 + "coordinates": "39.765965,102.449461"
3760 + },
3761 + "staff_id": null,
3762 + "status": "successful",
3763 + "notes": "准备充分",
3764 + "is_late": false
3765 + },
3766 + {
3767 + "id": "C0255",
3768 + "activity_id": "A0030",
3769 + "user_id": "U0017",
3770 + "registration_id": "R0466",
3771 + "checkin_time": "2025-03-24 06:26:07",
3772 + "checkin_type": "manual",
3773 + "location": {
3774 + "name": "大雁塔文化空间",
3775 + "coordinates": "35.381179,100.310767"
3776 + },
3777 + "staff_id": "O0005",
3778 + "status": "successful",
3779 + "notes": "",
3780 + "is_late": true
3781 + },
3782 + {
3783 + "id": "C0256",
3784 + "activity_id": "A0030",
3785 + "user_id": "U0006",
3786 + "registration_id": "R0467",
3787 + "checkin_time": "2025-03-24 06:08:21",
3788 + "checkin_type": "QR code",
3789 + "location": {
3790 + "name": "大雁塔文化空间",
3791 + "coordinates": "30.564468,110.716545"
3792 + },
3793 + "staff_id": null,
3794 + "status": "successful",
3795 + "notes": "",
3796 + "is_late": false
3797 + },
3798 + {
3799 + "id": "C0257",
3800 + "activity_id": "A0030",
3801 + "user_id": "U0013",
3802 + "registration_id": "R0468",
3803 + "checkin_time": "2025-03-24 06:34:50",
3804 + "checkin_type": "QR code",
3805 + "location": {
3806 + "name": "大雁塔文化空间",
3807 + "coordinates": "38.552870,102.806985"
3808 + },
3809 + "staff_id": null,
3810 + "status": "successful",
3811 + "notes": "",
3812 + "is_late": false
3813 + },
3814 + {
3815 + "id": "C0258",
3816 + "activity_id": "A0030",
3817 + "user_id": "U0014",
3818 + "registration_id": "R0469",
3819 + "checkin_time": "2025-03-24 06:11:44",
3820 + "checkin_type": "manual",
3821 + "location": {
3822 + "name": "大雁塔文化空间",
3823 + "coordinates": "28.615959,124.683004"
3824 + },
3825 + "staff_id": "O0005",
3826 + "status": "successful",
3827 + "notes": "",
3828 + "is_late": false
3829 + },
3830 + {
3831 + "id": "C0259",
3832 + "activity_id": "A0030",
3833 + "user_id": "U0024",
3834 + "registration_id": "R0470",
3835 + "checkin_time": "2025-03-24 06:21:59",
3836 + "checkin_type": "QR code",
3837 + "location": {
3838 + "name": "大雁塔文化空间",
3839 + "coordinates": "32.650324,122.567416"
3840 + },
3841 + "staff_id": null,
3842 + "status": "successful",
3843 + "notes": "",
3844 + "is_late": false
3845 + },
3846 + {
3847 + "id": "C0260",
3848 + "activity_id": "A0030",
3849 + "user_id": "U0023",
3850 + "registration_id": "R0471",
3851 + "checkin_time": "2025-03-24 06:19:00",
3852 + "checkin_type": "QR code",
3853 + "location": {
3854 + "name": "大雁塔文化空间",
3855 + "coordinates": "33.643347,118.704871"
3856 + },
3857 + "staff_id": null,
3858 + "status": "successful",
3859 + "notes": "",
3860 + "is_late": false
3861 + },
3862 + {
3863 + "id": "C0261",
3864 + "activity_id": "A0030",
3865 + "user_id": "U0018",
3866 + "registration_id": "R0472",
3867 + "checkin_time": "2025-03-24 06:11:01",
3868 + "checkin_type": "QR code",
3869 + "location": {
3870 + "name": "大雁塔文化空间",
3871 + "coordinates": "29.846041,119.659244"
3872 + },
3873 + "staff_id": null,
3874 + "status": "successful",
3875 + "notes": "热情参与讨论",
3876 + "is_late": false
3877 + },
3878 + {
3879 + "id": "C0262",
3880 + "activity_id": "A0030",
3881 + "user_id": "U0020",
3882 + "registration_id": "R0474",
3883 + "checkin_time": "2025-03-24 06:35:07",
3884 + "checkin_type": "QR code",
3885 + "location": {
3886 + "name": "大雁塔文化空间",
3887 + "coordinates": "25.709511,106.061221"
3888 + },
3889 + "staff_id": null,
3890 + "status": "successful",
3891 + "notes": "",
3892 + "is_late": true
3893 + },
3894 + {
3895 + "id": "C0263",
3896 + "activity_id": "A0030",
3897 + "user_id": "U0008",
3898 + "registration_id": "R0475",
3899 + "checkin_time": "2025-03-24 06:39:17",
3900 + "checkin_type": "manual",
3901 + "location": {
3902 + "name": "大雁塔文化空间",
3903 + "coordinates": "32.647541,101.108562"
3904 + },
3905 + "staff_id": "O0005",
3906 + "status": "successful",
3907 + "notes": "",
3908 + "is_late": false
3909 + },
3910 + {
3911 + "id": "C0264",
3912 + "activity_id": "A0030",
3913 + "user_id": "U0039",
3914 + "registration_id": "R0476",
3915 + "checkin_time": "2025-03-24 06:08:49",
3916 + "checkin_type": "QR code",
3917 + "location": {
3918 + "name": "大雁塔文化空间",
3919 + "coordinates": "23.752600,113.568037"
3920 + },
3921 + "staff_id": null,
3922 + "status": "successful",
3923 + "notes": "准备充分",
3924 + "is_late": false
3925 + },
3926 + {
3927 + "id": "C0265",
3928 + "activity_id": "A0030",
3929 + "user_id": "U0025",
3930 + "registration_id": "R0477",
3931 + "checkin_time": "2025-03-24 06:14:21",
3932 + "checkin_type": "QR code",
3933 + "location": {
3934 + "name": "大雁塔文化空间",
3935 + "coordinates": "27.951622,124.171448"
3936 + },
3937 + "staff_id": null,
3938 + "status": "successful",
3939 + "notes": "",
3940 + "is_late": false
3941 + },
3942 + {
3943 + "id": "C0266",
3944 + "activity_id": "A0030",
3945 + "user_id": "U0004",
3946 + "registration_id": "R0478",
3947 + "checkin_time": "2025-03-24 06:28:35",
3948 + "checkin_type": "QR code",
3949 + "location": {
3950 + "name": "大雁塔文化空间",
3951 + "coordinates": "39.931468,121.020079"
3952 + },
3953 + "staff_id": null,
3954 + "status": "successful",
3955 + "notes": "准备充分",
3956 + "is_late": false
3957 + },
3958 + {
3959 + "id": "C0267",
3960 + "activity_id": "A0030",
3961 + "user_id": "U0037",
3962 + "registration_id": "R0479",
3963 + "checkin_time": "2025-03-24 06:07:58",
3964 + "checkin_type": "manual",
3965 + "location": {
3966 + "name": "大雁塔文化空间",
3967 + "coordinates": "34.700797,103.276532"
3968 + },
3969 + "staff_id": "O0005",
3970 + "status": "successful",
3971 + "notes": "",
3972 + "is_late": false
3973 + },
3974 + {
3975 + "id": "C0268",
3976 + "activity_id": "A0030",
3977 + "user_id": "U0001",
3978 + "registration_id": "R0480",
3979 + "checkin_time": "2025-03-24 06:28:46",
3980 + "checkin_type": "QR code",
3981 + "location": {
3982 + "name": "大雁塔文化空间",
3983 + "coordinates": "39.529239,117.178983"
3984 + },
3985 + "staff_id": null,
3986 + "status": "successful",
3987 + "notes": "带了书本",
3988 + "is_late": false
3989 + }
3990 +]
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "data": [
3 + {
4 + "title": "Travel Blog",
5 + "description": "Here is json data for travel blog"
6 + }
7 + ]
8 +}
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 +[
2 + {
3 + "id": "U0001",
4 + "username": "philosopher915",
5 + "name": "陈静",
6 + "avatar": "https://randomuser.me/api/portraits/women/33.jpg",
7 + "phone": "15242619085",
8 + "email": "philosopher915@outlook.com",
9 + "gender": "男",
10 + "age_group": "31-35岁",
11 + "education": "高中",
12 + "registration_date": "2024-10-15 07:11:49",
13 + "location": "南京",
14 + "bio": "观点,交流,生活,发现,认知,情感,情感,交流,兴趣",
15 + "interests": [
16 + "历史",
17 + "古典文学",
18 + "教育",
19 + "社会科学",
20 + "政治"
21 + ],
22 + "is_organizer": false,
23 + "organization": null,
24 + "activity_count": 10,
25 + "follow_count": 7,
26 + "followers_count": 13,
27 + "is_new_user": false
28 + },
29 + {
30 + "id": "U0002",
31 + "username": "bibliophile888",
32 + "name": "林杰",
33 + "avatar": "https://randomuser.me/api/portraits/men/71.jpg",
34 + "phone": "18639598797",
35 + "email": "bibliophile888@foxmail.com",
36 + "gender": "女",
37 + "age_group": "31-35岁",
38 + "education": "本科",
39 + "registration_date": "2023-11-12 07:11:49",
40 + "location": "西安",
41 + "bio": "生活,未来,读书,思考,知识,思考,感悟,视野,思想,坚持,热爱,工作,沟通,坚持,知识,生活,理解,坚持",
42 + "interests": [
43 + "推理",
44 + "诗歌",
45 + "音乐",
46 + "政治",
47 + "旅行"
48 + ],
49 + "is_organizer": false,
50 + "organization": null,
51 + "activity_count": 9,
52 + "follow_count": 37,
53 + "followers_count": 14,
54 + "is_new_user": false
55 + },
56 + {
57 + "id": "U0003",
58 + "username": "sage560",
59 + "name": "胡秀英",
60 + "avatar": "https://randomuser.me/api/portraits/men/25.jpg",
61 + "phone": "13039456917",
62 + "email": "sage560@gmail.com",
63 + "gender": "女",
64 + "age_group": "41-50岁",
65 + "education": "硕士",
66 + "registration_date": "2024-03-12 07:11:49",
67 + "location": "北京",
68 + "bio": "思想,学习,沟通,坚持,启发,认知,经验,创造,理解,观点,精神,认知,思想,视野,探索,知识,文化,",
69 + "interests": [
70 + "艺术",
71 + "哲学",
72 + "旅行",
73 + "政治",
74 + "教育"
75 + ],
76 + "is_organizer": false,
77 + "organization": null,
78 + "activity_count": 5,
79 + "follow_count": 13,
80 + "followers_count": 15,
81 + "is_new_user": false
82 + },
83 + {
84 + "id": "U0004",
85 + "username": "reader777",
86 + "name": "陈勇",
87 + "avatar": "https://randomuser.me/api/portraits/men/92.jpg",
88 + "phone": "13678802250",
89 + "email": "reader777@163.com",
90 + "gender": "女",
91 + "age_group": "36-40岁",
92 + "education": "硕士",
93 + "registration_date": "2023-05-28 07:11:49",
94 + "location": "重庆",
95 + "bio": "生活,教育,分享,交流,启发,观点,沟通,情感,观点,发现,认知,价值,视野",
96 + "interests": [
97 + "旅行",
98 + "外国文学",
99 + "古典文学",
100 + "推理",
101 + "经济"
102 + ],
103 + "is_organizer": false,
104 + "organization": null,
105 + "activity_count": 9,
106 + "follow_count": 18,
107 + "followers_count": 22,
108 + "is_new_user": false
109 + },
110 + {
111 + "id": "U0005",
112 + "username": "scholar823",
113 + "name": "李霞",
114 + "avatar": "https://randomuser.me/api/portraits/women/24.jpg",
115 + "phone": "15765356756",
116 + "email": "scholar823@outlook.com",
117 + "gender": "女",
118 + "age_group": "41-50岁",
119 + "education": "高中",
120 + "registration_date": "2023-07-05 07:11:49",
121 + "location": "北京",
122 + "bio": "思考,兴趣,观点,兴趣,观点,经验,观点,观点,文化,智慧",
123 + "interests": [
124 + "艺术",
125 + "旅行",
126 + "古典文学",
127 + "音乐",
128 + "外国文学"
129 + ],
130 + "is_organizer": false,
131 + "organization": null,
132 + "activity_count": 3,
133 + "follow_count": 2,
134 + "followers_count": 27,
135 + "is_new_user": false
136 + },
137 + {
138 + "id": "U0006",
139 + "username": "thinker926",
140 + "name": "胡超",
141 + "avatar": "https://randomuser.me/api/portraits/men/40.jpg",
142 + "phone": "15926853615",
143 + "email": "thinker926@126.com",
144 + "gender": "女",
145 + "age_group": "36-40岁",
146 + "education": "博士",
147 + "registration_date": "2025-04-10 07:11:49",
148 + "location": "北京",
149 + "bio": "情感,未来,发现,创造,灵感,灵感,读书,灵感,灵感",
150 + "interests": [
151 + "心理学",
152 + "哲学",
153 + "管理",
154 + "科技"
155 + ],
156 + "is_organizer": false,
157 + "organization": null,
158 + "activity_count": 15,
159 + "follow_count": 10,
160 + "followers_count": 21,
161 + "is_new_user": true
162 + },
163 + {
164 + "id": "U0007",
165 + "username": "reader206",
166 + "name": "何秀英",
167 + "avatar": "https://randomuser.me/api/portraits/men/33.jpg",
168 + "phone": "13028502918",
169 + "email": "reader206@hotmail.com",
170 + "gender": "女",
171 + "age_group": "18-24岁",
172 + "education": "大专",
173 + "registration_date": "2024-05-18 07:11:49",
174 + "location": "深圳",
175 + "bio": "坚持,文化,教育,智慧,专注,情感,探索,成长,知识,沟通,生活,认知",
176 + "interests": [
177 + "外国文学",
178 + "古典文学",
179 + "传记",
180 + "科技",
181 + "社会科学"
182 + ],
183 + "is_organizer": false,
184 + "organization": null,
185 + "activity_count": 13,
186 + "follow_count": 1,
187 + "followers_count": 25,
188 + "is_new_user": false
189 + },
190 + {
191 + "id": "U0008",
192 + "username": "reader520",
193 + "name": "周霞",
194 + "avatar": "https://randomuser.me/api/portraits/women/77.jpg",
195 + "phone": "13954151629",
196 + "email": "reader520@126.com",
197 + "gender": "男",
198 + "age_group": "31-35岁",
199 + "education": "本科",
200 + "registration_date": "2024-08-24 07:11:49",
201 + "location": "北京",
202 + "bio": "读书,专注,观点,兴趣,知识,探索,智慧,情感,理解,创造,心灵,思想,观点,创造,视野,认知,学习,文化,探索,情感,知识,视野,工作",
203 + "interests": [
204 + "健康",
205 + "推理"
206 + ],
207 + "is_organizer": false,
208 + "organization": null,
209 + "activity_count": 15,
210 + "follow_count": 43,
211 + "followers_count": 16,
212 + "is_new_user": false
213 + },
214 + {
215 + "id": "U0009",
216 + "username": "literati391",
217 + "name": "何敏",
218 + "avatar": "https://randomuser.me/api/portraits/women/26.jpg",
219 + "phone": "18778721591",
220 + "email": "literati391@gmail.com",
221 + "gender": "女",
222 + "age_group": "41-50岁",
223 + "education": "博士",
224 + "registration_date": "2024-08-06 07:11:49",
225 + "location": "重庆",
226 + "bio": "成长,工作,启发,文化,认知,观点,创造,感悟,学习,情感,坚持,成长,视野,思考,生活,启发,热爱,热爱",
227 + "interests": [
228 + "健康",
229 + "经济",
230 + "科幻",
231 + "外国文学",
232 + "社会科学"
233 + ],
234 + "is_organizer": false,
235 + "organization": null,
236 + "activity_count": 5,
237 + "follow_count": 14,
238 + "followers_count": 5,
239 + "is_new_user": false
240 + },
241 + {
242 + "id": "U0010",
243 + "username": "scholar137",
244 + "name": "黄伟",
245 + "avatar": "https://randomuser.me/api/portraits/women/46.jpg",
246 + "phone": "15554784272",
247 + "email": "scholar137@qq.com",
248 + "gender": "女",
249 + "age_group": "50岁以上",
250 + "education": "博士",
251 + "registration_date": "2023-04-21 07:11:49",
252 + "location": "深圳",
253 + "bio": "未来,心灵,知识,沟通,交流,感悟,未来,情感,知识,表达,理解,沟通,表达,观点,价值,工作,交流,教育,知识,发现,发现,读书,学习,认知,启发,工作,价值,智慧,沟通,启发,表达,感悟,",
254 + "interests": [
255 + "政治",
256 + "经济",
257 + "推理"
258 + ],
259 + "is_organizer": false,
260 + "organization": null,
261 + "activity_count": 5,
262 + "follow_count": 44,
263 + "followers_count": 10,
264 + "is_new_user": false
265 + },
266 + {
267 + "id": "U0011",
268 + "username": "thinker327",
269 + "name": "陈洋",
270 + "avatar": "https://randomuser.me/api/portraits/men/44.jpg",
271 + "phone": "13252122058",
272 + "email": "thinker327@gmail.com",
273 + "gender": "女",
274 + "age_group": "41-50岁",
275 + "education": "大专",
276 + "registration_date": "2024-06-27 07:11:49",
277 + "location": "北京",
278 + "bio": "工作,启发,工作,表达,发现,观点,读书,经验,沟通,探索,智慧,理解,文化,探索,兴趣,兴趣,思考,灵感,",
279 + "interests": [
280 + "健康",
281 + "历史",
282 + "文学",
283 + "经济",
284 + "心理学"
285 + ],
286 + "is_organizer": false,
287 + "organization": null,
288 + "activity_count": 3,
289 + "follow_count": 15,
290 + "followers_count": 11,
291 + "is_new_user": false
292 + },
293 + {
294 + "id": "U0012",
295 + "username": "scholar730",
296 + "name": "陈超",
297 + "avatar": "https://randomuser.me/api/portraits/men/67.jpg",
298 + "phone": "13152364083",
299 + "email": "scholar730@126.com",
300 + "gender": "男",
301 + "age_group": "41-50岁",
302 + "education": "博士",
303 + "registration_date": "2023-10-07 07:11:49",
304 + "location": "北京",
305 + "bio": "交流,探索,灵感,发现,未来,分享,专注,读书,工作,未来,思考,观点",
306 + "interests": [
307 + "传记"
308 + ],
309 + "is_organizer": false,
310 + "organization": null,
311 + "activity_count": 2,
312 + "follow_count": 34,
313 + "followers_count": 5,
314 + "is_new_user": false
315 + },
316 + {
317 + "id": "U0013",
318 + "username": "booklover923",
319 + "name": "黄超",
320 + "avatar": "https://randomuser.me/api/portraits/men/48.jpg",
321 + "phone": "15670614516",
322 + "email": "booklover923@126.com",
323 + "gender": "女",
324 + "age_group": "36-40岁",
325 + "education": "硕士",
326 + "registration_date": "2024-10-25 07:11:49",
327 + "location": "重庆",
328 + "bio": "思考,精神,经验,价值,经验,精神,知识,启发,表达,学习,思考,生活,探索,经验,坚持,创造,思想,情感,专注,精神,知识,工作,探索,知识,思想",
329 + "interests": [
330 + "社会科学",
331 + "科技",
332 + "哲学",
333 + "推理",
334 + "心理学"
335 + ],
336 + "is_organizer": false,
337 + "organization": null,
338 + "activity_count": 3,
339 + "follow_count": 15,
340 + "followers_count": 10,
341 + "is_new_user": false
342 + },
343 + {
344 + "id": "U0014",
345 + "username": "reader948",
346 + "name": "李敏",
347 + "avatar": "https://randomuser.me/api/portraits/men/85.jpg",
348 + "phone": "18128797547",
349 + "email": "reader948@qq.com",
350 + "gender": "女",
351 + "age_group": "18-24岁",
352 + "education": "高中",
353 + "registration_date": "2023-07-12 07:11:49",
354 + "location": "广州",
355 + "bio": "价值,坚持,认知,读书,灵感,探索,知识,坚持,专注,灵感,未来,文化,探索,教育,未来,教育,认知,学习,探索,教育",
356 + "interests": [
357 + "科技",
358 + "政治",
359 + "科幻"
360 + ],
361 + "is_organizer": false,
362 + "organization": null,
363 + "activity_count": 9,
364 + "follow_count": 50,
365 + "followers_count": 9,
366 + "is_new_user": false
367 + },
368 + {
369 + "id": "U0015",
370 + "username": "literati987",
371 + "name": "徐磊",
372 + "avatar": "https://randomuser.me/api/portraits/women/29.jpg",
373 + "phone": "15585365059",
374 + "email": "literati987@yahoo.com",
375 + "gender": "女",
376 + "age_group": "25-30岁",
377 + "education": "博士",
378 + "registration_date": "2024-06-18 07:11:49",
379 + "location": "上海",
380 + "bio": "理解,心灵,表达,感悟,启发,读书,文化,交流,兴趣,思想,文化,未来,分享,兴趣,启发,交流,生活,知识,专注,文化,情感,兴趣",
381 + "interests": [
382 + "教育",
383 + "社会科学",
384 + "哲学",
385 + "管理",
386 + "古典文学"
387 + ],
388 + "is_organizer": false,
389 + "organization": null,
390 + "activity_count": 4,
391 + "follow_count": 40,
392 + "followers_count": 18,
393 + "is_new_user": false
394 + },
395 + {
396 + "id": "U0016",
397 + "username": "literati913",
398 + "name": "刘磊",
399 + "avatar": "https://randomuser.me/api/portraits/men/41.jpg",
400 + "phone": "15126570482",
401 + "email": "literati913@qq.com",
402 + "gender": "女",
403 + "age_group": "36-40岁",
404 + "education": "本科",
405 + "registration_date": "2023-09-06 07:11:49",
406 + "location": "南京",
407 + "bio": "思考,未来,感悟,探索,坚持,发现,感悟,思想,学习,沟通,坚持,文化,探索,未来",
408 + "interests": [
409 + "经济",
410 + "管理"
411 + ],
412 + "is_organizer": false,
413 + "organization": null,
414 + "activity_count": 10,
415 + "follow_count": 19,
416 + "followers_count": 26,
417 + "is_new_user": false
418 + },
419 + {
420 + "id": "U0017",
421 + "username": "bibliophile286",
422 + "name": "林杰",
423 + "avatar": "https://randomuser.me/api/portraits/men/12.jpg",
424 + "phone": "18598074151",
425 + "email": "bibliophile286@foxmail.com",
426 + "gender": "男",
427 + "age_group": "36-40岁",
428 + "education": "大专",
429 + "registration_date": "2023-09-02 07:11:49",
430 + "location": "杭州",
431 + "bio": "兴趣,启发,坚持,观点,情感,沟通,生活,学习,交流,发现,精神",
432 + "interests": [
433 + "文学",
434 + "音乐"
435 + ],
436 + "is_organizer": false,
437 + "organization": null,
438 + "activity_count": 4,
439 + "follow_count": 32,
440 + "followers_count": 12,
441 + "is_new_user": false
442 + },
443 + {
444 + "id": "U0018",
445 + "username": "sage162",
446 + "name": "高明",
447 + "avatar": "https://randomuser.me/api/portraits/women/73.jpg",
448 + "phone": "18339690960",
449 + "email": "sage162@126.com",
450 + "gender": "女",
451 + "age_group": "25-30岁",
452 + "education": "本科",
453 + "registration_date": "2024-06-14 07:11:49",
454 + "location": "北京",
455 + "bio": "热爱,思想,教育,学习,启发,教育,启发,生活,专注,知识,成长,热爱,热爱,学习,思想,视野,启发,思考,认知,精神,专注,坚持,读书,教育,思想,学习,读书,价值,理解,工作",
456 + "interests": [
457 + "社会科学",
458 + "历史",
459 + "音乐",
460 + "心理学"
461 + ],
462 + "is_organizer": false,
463 + "organization": null,
464 + "activity_count": 5,
465 + "follow_count": 37,
466 + "followers_count": 1,
467 + "is_new_user": false
468 + },
469 + {
470 + "id": "U0019",
471 + "username": "thinker676",
472 + "name": "张芳",
473 + "avatar": "https://randomuser.me/api/portraits/men/31.jpg",
474 + "phone": "15710859650",
475 + "email": "thinker676@qq.com",
476 + "gender": "男",
477 + "age_group": "36-40岁",
478 + "education": "本科",
479 + "registration_date": "2025-03-20 07:11:49",
480 + "location": "上海",
481 + "bio": "坚持,成长,思想,情感,工作,心灵,经验,探索,分享,知识,创造,兴趣,表达,思考,分享,发现,情感,视野,思考,创造,文化,思想,",
482 + "interests": [
483 + "教育",
484 + "旅行"
485 + ],
486 + "is_organizer": false,
487 + "organization": null,
488 + "activity_count": 3,
489 + "follow_count": 12,
490 + "followers_count": 8,
491 + "is_new_user": true
492 + },
493 + {
494 + "id": "U0020",
495 + "username": "bibliophile942",
496 + "name": "杨勇",
497 + "avatar": "https://randomuser.me/api/portraits/men/9.jpg",
498 + "phone": "13970324817",
499 + "email": "bibliophile942@foxmail.com",
500 + "gender": "男",
501 + "age_group": "41-50岁",
502 + "education": "硕士",
503 + "registration_date": "2025-02-25 07:11:49",
504 + "location": "西安",
505 + "bio": "发现,成长,知识,智慧,理解,观点,坚持,创造,学习",
506 + "interests": [
507 + "推理",
508 + "科幻",
509 + "诗歌",
510 + "管理",
511 + "外国文学"
512 + ],
513 + "is_organizer": false,
514 + "organization": null,
515 + "activity_count": 9,
516 + "follow_count": 49,
517 + "followers_count": 29,
518 + "is_new_user": false
519 + },
520 + {
521 + "id": "U0021",
522 + "username": "bibliophile998",
523 + "name": "刘秀英",
524 + "avatar": "https://randomuser.me/api/portraits/women/17.jpg",
525 + "phone": "15844639209",
526 + "email": "bibliophile998@126.com",
527 + "gender": "女",
528 + "age_group": "31-35岁",
529 + "education": "硕士",
530 + "registration_date": "2023-12-06 07:11:49",
531 + "location": "武汉",
532 + "bio": "精神,发现,心灵,交流,视野,情感,思考,情感,坚持,发现,心灵,教育,感悟,",
533 + "interests": [
534 + "外国文学",
535 + "艺术",
536 + "健康",
537 + "政治",
538 + "心理学"
539 + ],
540 + "is_organizer": false,
541 + "organization": null,
542 + "activity_count": 6,
543 + "follow_count": 17,
544 + "followers_count": 25,
545 + "is_new_user": false
546 + },
547 + {
548 + "id": "U0022",
549 + "username": "bibliophile730",
550 + "name": "徐娜",
551 + "avatar": "https://randomuser.me/api/portraits/women/5.jpg",
552 + "phone": "18820345218",
553 + "email": "bibliophile730@foxmail.com",
554 + "gender": "男",
555 + "age_group": "36-40岁",
556 + "education": "硕士",
557 + "registration_date": "2024-09-23 07:11:49",
558 + "location": "北京",
559 + "bio": "思考,创造,感悟,思想,学习,成长,情感,生活,启发,创造,学习,理解,感悟,生活,分享,沟通,智慧,探索,学习,智慧,交流,成长,",
560 + "interests": [
561 + "健康",
562 + "诗歌",
563 + "科幻",
564 + "心理学"
565 + ],
566 + "is_organizer": false,
567 + "organization": null,
568 + "activity_count": 3,
569 + "follow_count": 11,
570 + "followers_count": 3,
571 + "is_new_user": false
572 + },
573 + {
574 + "id": "U0023",
575 + "username": "booklover552",
576 + "name": "徐娜",
577 + "avatar": "https://randomuser.me/api/portraits/women/54.jpg",
578 + "phone": "18161956034",
579 + "email": "booklover552@foxmail.com",
580 + "gender": "女",
581 + "age_group": "31-35岁",
582 + "education": "高中",
583 + "registration_date": "2023-12-19 07:11:49",
584 + "location": "成都",
585 + "bio": "沟通,思考,价值,教育,分享,心灵,交流,生活,经验,启发,专注,情感",
586 + "interests": [
587 + "社会科学",
588 + "音乐",
589 + "古典文学"
590 + ],
591 + "is_organizer": false,
592 + "organization": null,
593 + "activity_count": 5,
594 + "follow_count": 39,
595 + "followers_count": 14,
596 + "is_new_user": false
597 + },
598 + {
599 + "id": "U0024",
600 + "username": "bookworm707",
601 + "name": "胡敏",
602 + "avatar": "https://randomuser.me/api/portraits/men/80.jpg",
603 + "phone": "13799073465",
604 + "email": "bookworm707@yahoo.com",
605 + "gender": "女",
606 + "age_group": "31-35岁",
607 + "education": "博士",
608 + "registration_date": "2024-03-23 07:11:49",
609 + "location": "武汉",
610 + "bio": "沟通,情感,思考,未来,知识,生活,知识,工作,经验,教育,文化,专注,专注,情感,认知,未来,经验,精神,教育,认知,坚持,视野,精神,情感,交流,观点,表达,专注,分享,经验,视野,知识,认知",
611 + "interests": [
612 + "外国文学",
613 + "音乐",
614 + "管理",
615 + "艺术"
616 + ],
617 + "is_organizer": false,
618 + "organization": null,
619 + "activity_count": 4,
620 + "follow_count": 17,
621 + "followers_count": 6,
622 + "is_new_user": false
623 + },
624 + {
625 + "id": "U0025",
626 + "username": "philosopher564",
627 + "name": "朱洋",
628 + "avatar": "https://randomuser.me/api/portraits/women/71.jpg",
629 + "phone": "18654998138",
630 + "email": "philosopher564@foxmail.com",
631 + "gender": "男",
632 + "age_group": "50岁以上",
633 + "education": "硕士",
634 + "registration_date": "2023-05-23 07:11:49",
635 + "location": "深圳",
636 + "bio": "思考,经验,观点,学习,理解,智慧,交流,表达,专注,",
637 + "interests": [
638 + "传记",
639 + "社会科学",
640 + "经济",
641 + "音乐"
642 + ],
643 + "is_organizer": false,
644 + "organization": null,
645 + "activity_count": 3,
646 + "follow_count": 20,
647 + "followers_count": 27,
648 + "is_new_user": false
649 + },
650 + {
651 + "id": "U0026",
652 + "username": "sage490",
653 + "name": "吴洋",
654 + "avatar": "https://randomuser.me/api/portraits/men/71.jpg",
655 + "phone": "13415330923",
656 + "email": "sage490@163.com",
657 + "gender": "女",
658 + "age_group": "36-40岁",
659 + "education": "大专",
660 + "registration_date": "2025-01-10 07:11:49",
661 + "location": "广州",
662 + "bio": "成长,心灵,精神,交流,分享,观点,认知,坚持,交流,创造,思考,经验,兴趣",
663 + "interests": [
664 + "音乐",
665 + "健康"
666 + ],
667 + "is_organizer": false,
668 + "organization": null,
669 + "activity_count": 13,
670 + "follow_count": 49,
671 + "followers_count": 20,
672 + "is_new_user": false
673 + },
674 + {
675 + "id": "U0027",
676 + "username": "bookworm606",
677 + "name": "王勇",
678 + "avatar": "https://randomuser.me/api/portraits/women/55.jpg",
679 + "phone": "18785082868",
680 + "email": "bookworm606@126.com",
681 + "gender": "女",
682 + "age_group": "41-50岁",
683 + "education": "高中",
684 + "registration_date": "2024-12-05 07:11:49",
685 + "location": "武汉",
686 + "bio": "坚持,价值,思考,视野,观点,精神,感悟,分享,理解,启发,思考,文化,精神,思想,思考,热爱,",
687 + "interests": [
688 + "历史",
689 + "健康",
690 + "管理",
691 + "古典文学"
692 + ],
693 + "is_organizer": false,
694 + "organization": null,
695 + "activity_count": 3,
696 + "follow_count": 29,
697 + "followers_count": 24,
698 + "is_new_user": false
699 + },
700 + {
701 + "id": "U0028",
702 + "username": "sage811",
703 + "name": "罗强",
704 + "avatar": "https://randomuser.me/api/portraits/men/30.jpg",
705 + "phone": "15294749899",
706 + "email": "sage811@hotmail.com",
707 + "gender": "男",
708 + "age_group": "25-30岁",
709 + "education": "大专",
710 + "registration_date": "2024-08-05 07:11:49",
711 + "location": "北京",
712 + "bio": "成长,视野,视野,读书,思想,思想,沟通,知识,启发,精神,成长,感悟,认知,探索,表达,感悟,启发,热爱,文化,专注,思想,感悟,专注,情感,思想,发现,兴趣",
713 + "interests": [
714 + "艺术",
715 + "文学",
716 + "管理"
717 + ],
718 + "is_organizer": false,
719 + "organization": null,
720 + "activity_count": 15,
721 + "follow_count": 13,
722 + "followers_count": 17,
723 + "is_new_user": false
724 + },
725 + {
726 + "id": "U0029",
727 + "username": "scholar879",
728 + "name": "朱强",
729 + "avatar": "https://randomuser.me/api/portraits/men/24.jpg",
730 + "phone": "15552442116",
731 + "email": "scholar879@yahoo.com",
732 + "gender": "女",
733 + "age_group": "31-35岁",
734 + "education": "本科",
735 + "registration_date": "2024-04-26 07:11:49",
736 + "location": "西安",
737 + "bio": "生活,发现,沟通,感悟,情感,探索,生活,精神,教育,知识,思考,交流,成长,心灵,价值,教育,工作,心灵,发现,思想,发现,观点,经验,经验,成长,表达,表达,视野,知识,生活,教育",
738 + "interests": [
739 + "推理",
740 + "管理"
741 + ],
742 + "is_organizer": false,
743 + "organization": null,
744 + "activity_count": 3,
745 + "follow_count": 4,
746 + "followers_count": 14,
747 + "is_new_user": false
748 + },
749 + {
750 + "id": "U0030",
751 + "username": "booklover385",
752 + "name": "张涛",
753 + "avatar": "https://randomuser.me/api/portraits/women/38.jpg",
754 + "phone": "15189425224",
755 + "email": "booklover385@gmail.com",
756 + "gender": "女",
757 + "age_group": "41-50岁",
758 + "education": "高中",
759 + "registration_date": "2024-05-14 07:11:49",
760 + "location": "成都",
761 + "bio": "认知,经验,心灵,经验,观点,读书,热爱,思考,思考",
762 + "interests": [
763 + "哲学",
764 + "文学",
765 + "科幻",
766 + "社会科学",
767 + "外国文学"
768 + ],
769 + "is_organizer": false,
770 + "organization": null,
771 + "activity_count": 8,
772 + "follow_count": 16,
773 + "followers_count": 19,
774 + "is_new_user": false
775 + },
776 + {
777 + "id": "U0031",
778 + "username": "bibliophile693",
779 + "name": "李芳",
780 + "avatar": "https://randomuser.me/api/portraits/women/21.jpg",
781 + "phone": "18658709476",
782 + "email": "bibliophile693@hotmail.com",
783 + "gender": "男",
784 + "age_group": "36-40岁",
785 + "education": "高中",
786 + "registration_date": "2023-05-29 07:11:49",
787 + "location": "杭州",
788 + "bio": "观点,交流,启发,思想,兴趣,生活,精神,思想,交流",
789 + "interests": [
790 + "外国文学",
791 + "哲学",
792 + "经济",
793 + "科技",
794 + "历史"
795 + ],
796 + "is_organizer": false,
797 + "organization": null,
798 + "activity_count": 7,
799 + "follow_count": 21,
800 + "followers_count": 9,
801 + "is_new_user": false
802 + },
803 + {
804 + "id": "U0032",
805 + "username": "scholar444",
806 + "name": "王敏",
807 + "avatar": "https://randomuser.me/api/portraits/men/69.jpg",
808 + "phone": "18353657373",
809 + "email": "scholar444@163.com",
810 + "gender": "女",
811 + "age_group": "25-30岁",
812 + "education": "高中",
813 + "registration_date": "2024-01-10 07:11:49",
814 + "location": "重庆",
815 + "bio": "读书,创造,价值,发现,工作,探索,未来,理解,视野,经验,成长,思考,思想,价值,文化,思想,观点,教育,沟通,坚持,表达,生活,经验,分享,心灵,探索,智慧,智慧,知识,",
816 + "interests": [
817 + "推理",
818 + "健康",
819 + "社会科学"
820 + ],
821 + "is_organizer": false,
822 + "organization": null,
823 + "activity_count": 9,
824 + "follow_count": 18,
825 + "followers_count": 28,
826 + "is_new_user": false
827 + },
828 + {
829 + "id": "U0033",
830 + "username": "bibliophile775",
831 + "name": "林秀英",
832 + "avatar": "https://randomuser.me/api/portraits/men/39.jpg",
833 + "phone": "13786169274",
834 + "email": "bibliophile775@outlook.com",
835 + "gender": "女",
836 + "age_group": "25-30岁",
837 + "education": "高中",
838 + "registration_date": "2025-03-20 07:11:49",
839 + "location": "西安",
840 + "bio": "知识,认知,坚持,精神,文化,读书,未来,分享,智慧,专注,分享,经验,热爱,专注,未来,兴趣,探索,智慧",
841 + "interests": [
842 + "科幻"
843 + ],
844 + "is_organizer": false,
845 + "organization": null,
846 + "activity_count": 14,
847 + "follow_count": 27,
848 + "followers_count": 1,
849 + "is_new_user": true
850 + },
851 + {
852 + "id": "U0034",
853 + "username": "philosopher472",
854 + "name": "何平",
855 + "avatar": "https://randomuser.me/api/portraits/women/22.jpg",
856 + "phone": "13967679665",
857 + "email": "philosopher472@gmail.com",
858 + "gender": "女",
859 + "age_group": "25-30岁",
860 + "education": "本科",
861 + "registration_date": "2023-11-02 07:11:49",
862 + "location": "广州",
863 + "bio": "知识,思考,坚持,学习,专注,学习,发现,分享,智慧,经验,心灵,工作,专注,生活,未来,成长,价值,精神,热爱,精神,工作,专注,智慧,心灵,智慧,感悟",
864 + "interests": [
865 + "心理学",
866 + "哲学",
867 + "古典文学",
868 + "音乐"
869 + ],
870 + "is_organizer": false,
871 + "organization": null,
872 + "activity_count": 10,
873 + "follow_count": 4,
874 + "followers_count": 27,
875 + "is_new_user": false
876 + },
877 + {
878 + "id": "U0035",
879 + "username": "thinker448",
880 + "name": "罗芳",
881 + "avatar": "https://randomuser.me/api/portraits/men/51.jpg",
882 + "phone": "18474979217",
883 + "email": "thinker448@hotmail.com",
884 + "gender": "女",
885 + "age_group": "36-40岁",
886 + "education": "本科",
887 + "registration_date": "2024-03-12 07:11:49",
888 + "location": "武汉",
889 + "bio": "沟通,发现,表达,表达,读书,热爱,热爱,观点,理解,创造,观点,感悟,观点,交流,探索,心灵,表达,思想,专注,思想,探索,学习,理解,坚持,理解,坚持,热爱,探索,价值,感悟,文化,探索,经验,成",
890 + "interests": [
891 + "科幻"
892 + ],
893 + "is_organizer": false,
894 + "organization": null,
895 + "activity_count": 15,
896 + "follow_count": 16,
897 + "followers_count": 29,
898 + "is_new_user": false
899 + },
900 + {
901 + "id": "U0036",
902 + "username": "scholar345",
903 + "name": "刘杰",
904 + "avatar": "https://randomuser.me/api/portraits/women/49.jpg",
905 + "phone": "18492551516",
906 + "email": "scholar345@qq.com",
907 + "gender": "女",
908 + "age_group": "36-40岁",
909 + "education": "博士",
910 + "registration_date": "2024-10-14 07:11:49",
911 + "location": "上海",
912 + "bio": "文化,工作,表达,理解,未来,沟通,启发,坚持,工作,认知,学习,未来,热爱,专注,价值,兴趣,工作",
913 + "interests": [
914 + "历史",
915 + "心理学"
916 + ],
917 + "is_organizer": false,
918 + "organization": null,
919 + "activity_count": 3,
920 + "follow_count": 0,
921 + "followers_count": 20,
922 + "is_new_user": false
923 + },
924 + {
925 + "id": "U0037",
926 + "username": "bookworm118",
927 + "name": "黄静",
928 + "avatar": "https://randomuser.me/api/portraits/women/25.jpg",
929 + "phone": "18027065768",
930 + "email": "bookworm118@126.com",
931 + "gender": "女",
932 + "age_group": "41-50岁",
933 + "education": "硕士",
934 + "registration_date": "2024-06-03 07:11:49",
935 + "location": "深圳",
936 + "bio": "启发,学习,分享,探索,工作,思想,经验,创造,认知,理解,理解,专注,智慧,兴趣,思想,灵感",
937 + "interests": [
938 + "诗歌",
939 + "社会科学",
940 + "哲学",
941 + "艺术",
942 + "政治"
943 + ],
944 + "is_organizer": false,
945 + "organization": null,
946 + "activity_count": 15,
947 + "follow_count": 33,
948 + "followers_count": 21,
949 + "is_new_user": false
950 + },
951 + {
952 + "id": "U0038",
953 + "username": "sage197",
954 + "name": "杨伟",
955 + "avatar": "https://randomuser.me/api/portraits/men/33.jpg",
956 + "phone": "15524305177",
957 + "email": "sage197@outlook.com",
958 + "gender": "女",
959 + "age_group": "50岁以上",
960 + "education": "高中",
961 + "registration_date": "2024-12-24 07:11:49",
962 + "location": "深圳",
963 + "bio": "心灵,学习,文化,读书,思考,文化,表达,教育,灵感,价值,表达,启发,创造,专注,表达,感悟,文化,兴趣,探索,价值,灵感,价值,探索,精神,价值,经验,坚持,表达,",
964 + "interests": [
965 + "外国文学"
966 + ],
967 + "is_organizer": false,
968 + "organization": null,
969 + "activity_count": 15,
970 + "follow_count": 13,
971 + "followers_count": 1,
972 + "is_new_user": false
973 + },
974 + {
975 + "id": "U0039",
976 + "username": "reader235",
977 + "name": "罗芳",
978 + "avatar": "https://randomuser.me/api/portraits/men/35.jpg",
979 + "phone": "13189382815",
980 + "email": "reader235@126.com",
981 + "gender": "女",
982 + "age_group": "25-30岁",
983 + "education": "大专",
984 + "registration_date": "2024-06-30 07:11:49",
985 + "location": "南京",
986 + "bio": "视野,兴趣,知识,认知,思考,学习,灵感,教育,读书,专注,智慧,文化,发现,成长,学习,灵感,生活,思想,工作,读书,热爱,价值,感悟,视野,智慧,精神,教育,交流,学习,坚持",
987 + "interests": [
988 + "科幻",
989 + "经济"
990 + ],
991 + "is_organizer": false,
992 + "organization": null,
993 + "activity_count": 13,
994 + "follow_count": 39,
995 + "followers_count": 12,
996 + "is_new_user": false
997 + },
998 + {
999 + "id": "U0040",
1000 + "username": "scholar946",
1001 + "name": "孙敏",
1002 + "avatar": "https://randomuser.me/api/portraits/men/96.jpg",
1003 + "phone": "13191009314",
1004 + "email": "scholar946@gmail.com",
1005 + "gender": "女",
1006 + "age_group": "18-24岁",
1007 + "education": "博士",
1008 + "registration_date": "2024-10-30 07:11:49",
1009 + "location": "深圳",
1010 + "bio": "认知,坚持,启发,情感,情感,生活,思想,感悟,坚持,发现,读书,工作,成长,视野,灵感,经验,探索,启发,表达,发现,观点,成长",
1011 + "interests": [
1012 + "古典文学",
1013 + "历史"
1014 + ],
1015 + "is_organizer": false,
1016 + "organization": null,
1017 + "activity_count": 14,
1018 + "follow_count": 13,
1019 + "followers_count": 12,
1020 + "is_new_user": false
1021 + },
1022 + {
1023 + "id": "O0001",
1024 + "username": "sage841",
1025 + "name": "王超",
1026 + "avatar": "https://randomuser.me/api/portraits/men/49.jpg",
1027 + "phone": "13039676712",
1028 + "email": "sage841@hotmail.com",
1029 + "gender": "女",
1030 + "age_group": "31-35岁",
1031 + "education": "博士",
1032 + "registration_date": "2022-11-07 07:11:49",
1033 + "location": "南京",
1034 + "bio": "学习,认知,理解,成长,兴趣,视野,兴趣,情感,交流,智慧,交流,智慧,交流,启发,生活,启发,知识,启发,智慧,学习,理解,创造,价值,生活,思想,学习,生活,读书,坚持,热爱,思考,感悟,读书,交流,思想,成长,学习,工作,视野,专注,认知,心灵,心灵,探索,文化",
1035 + "interests": [
1036 + "艺术",
1037 + "科幻",
1038 + "推理",
1039 + "历史"
1040 + ],
1041 + "is_organizer": true,
1042 + "organization": "光合作用文化空间",
1043 + "activity_count": 31,
1044 + "follow_count": 40,
1045 + "followers_count": 50,
1046 + "is_new_user": false
1047 + },
1048 + {
1049 + "id": "O0002",
1050 + "username": "bibliophile236",
1051 + "name": "罗芳",
1052 + "avatar": "https://randomuser.me/api/portraits/women/83.jpg",
1053 + "phone": "15760214355",
1054 + "email": "bibliophile236@163.com",
1055 + "gender": "女",
1056 + "age_group": "41-50岁",
1057 + "education": "本科",
1058 + "registration_date": "2023-01-08 07:11:49",
1059 + "location": "北京",
1060 + "bio": "学习,观点,经验,观点,创造,精神,沟通,心灵,启发,兴趣,创造,理解,思考,经验,文化,坚持,热爱,沟通,教育,认知,成长,分享,理解,思想,智慧,探索,表达,教育,观点,观点,教育,思想,发现,生活,视野,思想,情感,探索,成长,交流,观点,成长,文化,探索,感悟,文化,智慧,学习,思考",
1061 + "interests": [
1062 + "文学",
1063 + "古典文学",
1064 + "诗歌"
1065 + ],
1066 + "is_organizer": true,
1067 + "organization": "四季阅读空间",
1068 + "activity_count": 16,
1069 + "follow_count": 161,
1070 + "followers_count": 235,
1071 + "is_new_user": false
1072 + },
1073 + {
1074 + "id": "O0003",
1075 + "username": "booklover483",
1076 + "name": "李秀英",
1077 + "avatar": "https://randomuser.me/api/portraits/women/77.jpg",
1078 + "phone": "15043716500",
1079 + "email": "booklover483@hotmail.com",
1080 + "gender": "女",
1081 + "age_group": "31-35岁",
1082 + "education": "硕士",
1083 + "registration_date": "2023-05-17 07:11:49",
1084 + "location": "深圳",
1085 + "bio": "精神,教育,视野,心灵,交流,未来,认知,文化,热爱,学习,经验,工作,启发,价值,学习,成长,交流,成长,工作,生活,分享,创造,文化,工作,表达,视野,工作",
1086 + "interests": [
1087 + "古典文学",
1088 + "健康",
1089 + "历史",
1090 + "诗歌",
1091 + "艺术",
1092 + "教育"
1093 + ],
1094 + "is_organizer": true,
1095 + "organization": "知行合一读书社",
1096 + "activity_count": 31,
1097 + "follow_count": 37,
1098 + "followers_count": 490,
1099 + "is_new_user": false
1100 + },
1101 + {
1102 + "id": "O0004",
1103 + "username": "sage327",
1104 + "name": "赵磊",
1105 + "avatar": "https://randomuser.me/api/portraits/women/32.jpg",
1106 + "phone": "18591156244",
1107 + "email": "sage327@163.com",
1108 + "gender": "女",
1109 + "age_group": "41-50岁",
1110 + "education": "硕士",
1111 + "registration_date": "2023-03-12 07:11:49",
1112 + "location": "武汉",
1113 + "bio": "经验,读书,创造,思考,工作,价值,视野,交流,表达,教育,分享,价值,分享,创造,知识,热爱,经验,工作",
1114 + "interests": [
1115 + "经济",
1116 + "古典文学",
1117 + "哲学",
1118 + "教育",
1119 + "艺术",
1120 + "音乐",
1121 + "社会科学",
1122 + "旅行"
1123 + ],
1124 + "is_organizer": true,
1125 + "organization": "百草园读书会",
1126 + "activity_count": 44,
1127 + "follow_count": 21,
1128 + "followers_count": 150,
1129 + "is_new_user": false
1130 + },
1131 + {
1132 + "id": "O0005",
1133 + "username": "bookworm479",
1134 + "name": "朱明",
1135 + "avatar": "https://randomuser.me/api/portraits/men/45.jpg",
1136 + "phone": "13581054570",
1137 + "email": "bookworm479@163.com",
1138 + "gender": "男",
1139 + "age_group": "50岁以上",
1140 + "education": "硕士",
1141 + "registration_date": "2022-09-04 07:11:49",
1142 + "location": "西安",
1143 + "bio": "智慧,认知,心灵,价值,经验,智慧,视野,认知,专注,启发,智慧,心灵,沟通,经验,交流,智慧,交流,坚持,精神,智慧,教育,发现,探索,精神,坚持,经验,知识,探索,经验,认知,专注,",
1144 + "interests": [
1145 + "哲学",
1146 + "心理学",
1147 + "科幻",
1148 + "传记",
1149 + "经济",
1150 + "政治",
1151 + "科技"
1152 + ],
1153 + "is_organizer": true,
1154 + "organization": "鹿鸣书店",
1155 + "activity_count": 26,
1156 + "follow_count": 96,
1157 + "followers_count": 454,
1158 + "is_new_user": false
1159 + },
1160 + {
1161 + "id": "O0006",
1162 + "username": "booklover785",
1163 + "name": "李敏",
1164 + "avatar": "https://randomuser.me/api/portraits/women/51.jpg",
1165 + "phone": "15828129990",
1166 + "email": "booklover785@foxmail.com",
1167 + "gender": "女",
1168 + "age_group": "50岁以上",
1169 + "education": "硕士",
1170 + "registration_date": "2024-07-16 07:11:49",
1171 + "location": "成都",
1172 + "bio": "分享,灵感,分享,视野,心灵,分享,视野,创造,认知,读书,专注,理解,视野,探索,视野,感悟,发现,精神,工作,精神,情感,表达,交流,生活,沟通,观点,启发,热爱",
1173 + "interests": [
1174 + "文学",
1175 + "心理学",
1176 + "科技",
1177 + "历史"
1178 + ],
1179 + "is_organizer": true,
1180 + "organization": "百草园读书会",
1181 + "activity_count": 37,
1182 + "follow_count": 133,
1183 + "followers_count": 425,
1184 + "is_new_user": false
1185 + },
1186 + {
1187 + "id": "O0007",
1188 + "username": "bookworm995",
1189 + "name": "刘明",
1190 + "avatar": "https://randomuser.me/api/portraits/men/35.jpg",
1191 + "phone": "18484363974",
1192 + "email": "bookworm995@163.com",
1193 + "gender": "女",
1194 + "age_group": "31-35岁",
1195 + "education": "博士",
1196 + "registration_date": "2024-03-02 07:11:49",
1197 + "location": "南京",
1198 + "bio": "专注,情感,未来,专注,文化,思考,生活,心灵,精神,启发,分享,工作,感悟,思想,创造,理解,思想,工作,思考,热爱,认知,启发,精神,交流,启发,知识,坚持,认知,知识,读书,认知,学习,坚持,精神,兴趣,智慧,理解,发现",
1199 + "interests": [
1200 + "教育",
1201 + "经济",
1202 + "文学",
1203 + "健康",
1204 + "传记",
1205 + "科技",
1206 + "心理学",
1207 + "政治"
1208 + ],
1209 + "is_organizer": true,
1210 + "organization": "百草园读书会",
1211 + "activity_count": 28,
1212 + "follow_count": 73,
1213 + "followers_count": 319,
1214 + "is_new_user": false
1215 + },
1216 + {
1217 + "id": "O0008",
1218 + "username": "literati792",
1219 + "name": "吴军",
1220 + "avatar": "https://randomuser.me/api/portraits/women/25.jpg",
1221 + "phone": "18764703997",
1222 + "email": "literati792@hotmail.com",
1223 + "gender": "女",
1224 + "age_group": "31-35岁",
1225 + "education": "本科",
1226 + "registration_date": "2023-09-21 07:11:49",
1227 + "location": "西安",
1228 + "bio": "认知,未来,沟通,灵感,思想,坚持,启发,思想,思考,生活,思考,未来,成长,学习,表达,精神,理解,未来,认知,坚持,表达,价值,沟通,精神,思想,分享,观点,视野,知识,灵感,生活,文化,学习,思想,观点,智慧,创造,兴趣,学习,坚持,精神,成长",
1229 + "interests": [
1230 + "哲学",
1231 + "经济",
1232 + "古典文学"
1233 + ],
1234 + "is_organizer": true,
1235 + "organization": "墨香文化传媒",
1236 + "activity_count": 41,
1237 + "follow_count": 175,
1238 + "followers_count": 306,
1239 + "is_new_user": false
1240 + },
1241 + {
1242 + "id": "O0009",
1243 + "username": "reader617",
1244 + "name": "陈艳",
1245 + "avatar": "https://randomuser.me/api/portraits/women/25.jpg",
1246 + "phone": "15658916066",
1247 + "email": "reader617@foxmail.com",
1248 + "gender": "女",
1249 + "age_group": "41-50岁",
1250 + "education": "本科",
1251 + "registration_date": "2024-06-26 07:11:49",
1252 + "location": "西安",
1253 + "bio": "思考,读书,精神,创造,热爱,情感,读书,创造,工作,灵感,热爱,创造,认知,沟通,读书,精神,生活,心灵,学习,热爱,交流,发现,知识,经验,感悟,创造,读书,学习,探索,文化,沟通,发现",
1254 + "interests": [
1255 + "科技",
1256 + "诗歌",
1257 + "教育"
1258 + ],
1259 + "is_organizer": true,
1260 + "organization": "四季阅读空间",
1261 + "activity_count": 24,
1262 + "follow_count": 35,
1263 + "followers_count": 84,
1264 + "is_new_user": false
1265 + },
1266 + {
1267 + "id": "O0010",
1268 + "username": "bibliophile948",
1269 + "name": "李伟",
1270 + "avatar": "https://randomuser.me/api/portraits/women/58.jpg",
1271 + "phone": "18696049013",
1272 + "email": "bibliophile948@hotmail.com",
1273 + "gender": "男",
1274 + "age_group": "50岁以上",
1275 + "education": "硕士",
1276 + "registration_date": "2024-02-21 07:11:49",
1277 + "location": "南京",
1278 + "bio": "工作,教育,视野,教育,认知,创造,生活,价值,学习,教育,专注,情感,价值,沟通,热爱,未来,知识,知识,思想,表达,知识,创造,专注,交流,视野,思考,感悟,",
1279 + "interests": [
1280 + "社会科学",
1281 + "音乐",
1282 + "教育",
1283 + "旅行"
1284 + ],
1285 + "is_organizer": true,
1286 + "organization": "青禾读书会",
1287 + "activity_count": 17,
1288 + "follow_count": 135,
1289 + "followers_count": 269,
1290 + "is_new_user": false
1291 + }
1292 +]
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
2 + * @Date: 2025-04-17 08:14:12
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 13:15:27
5 + * @FilePath: /reading-club-app/src/App.vue
6 + * @Description: 文件描述
7 +-->
8 +/*
9 + * @Date: 2025-04-17 08:14:12
10 + * @LastEditors: hookehuyr hookehuyr@gmail.com
11 + * @LastEditTime: 2025-04-17 13:15:05
12 + * @FilePath: /reading-club-app/src/App.jsx
13 + * @Description: 文件描述
14 + */
15 +<template>
16 + <div class="flex flex-col min-h-screen">
17 + <Header />
18 + <main class="flex-grow">
19 + <router-view></router-view>
20 + </main>
21 + <Footer />
22 + </div>
23 +</template>
24 +
25 +<script setup>
26 +import { onMounted } from 'vue'
27 +import Header from './components/layout/Header.vue'
28 +import Footer from './components/layout/Footer.vue'
29 +
30 +// 更新文档标题
31 +onMounted(() => {
32 + document.title = '读书会 - 连接爱读书的人'
33 +})
34 +</script>
1 +<!--
2 + * @Date: 2025-04-17 13:22:07
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 13:22:09
5 + * @FilePath: /reading-club-app/src/components/layout/Footer.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <footer class="mt-8 py-6 bg-gradient-to-r from-green-50 to-blue-50">
10 + <div class="container mx-auto px-4">
11 + <div class="flex flex-col md:flex-row justify-between items-center">
12 + <div class="mb-4 md:mb-0">
13 + <h3 class="text-xl font-bold bg-gradient-to-r from-green-500 to-blue-400 bg-clip-text text-transparent">读书会</h3>
14 + <p class="text-gray-600 mt-2">连接爱读书的人,共享知识的力量</p>
15 + </div>
16 +
17 + <div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-8">
18 + <div>
19 + <h4 class="font-semibold text-gray-800 mb-3">关于我们</h4>
20 + <ul class="space-y-2">
21 + <li><a href="#" class="text-gray-600 hover:text-green-500">平台介绍</a></li>
22 + <li><a href="#" class="text-gray-600 hover:text-green-500">使用指南</a></li>
23 + <li><a href="#" class="text-gray-600 hover:text-green-500">联系我们</a></li>
24 + </ul>
25 + </div>
26 +
27 + <div>
28 + <h4 class="font-semibold text-gray-800 mb-3">热门分类</h4>
29 + <ul class="space-y-2">
30 + <li><a href="#" class="text-gray-600 hover:text-green-500">文学小说</a></li>
31 + <li><a href="#" class="text-gray-600 hover:text-green-500">社科人文</a></li>
32 + <li><a href="#" class="text-gray-600 hover:text-green-500">商业财经</a></li>
33 + </ul>
34 + </div>
35 +
36 + <div>
37 + <h4 class="font-semibold text-gray-800 mb-3">关注我们</h4>
38 + <div class="flex space-x-4">
39 + <a href="#" class="text-gray-600 hover:text-green-500">
40 + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
41 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 8l-8 8m0-8l8 8" />
42 + </svg>
43 + </a>
44 + <a href="#" class="text-gray-600 hover:text-green-500">
45 + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
46 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
47 + </svg>
48 + </a>
49 + <a href="#" class="text-gray-600 hover:text-green-500">
50 + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
51 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
52 + </svg>
53 + </a>
54 + </div>
55 + </div>
56 + </div>
57 + </div>
58 +
59 + <div class="border-t border-gray-200 mt-8 pt-6 text-center">
60 + <p class="text-gray-500 text-sm">© 2024 读书会平台 All Rights Reserved</p>
61 + </div>
62 + </div>
63 + </footer>
64 +</template>
65 +
66 +<script setup>
67 +// Footer组件不需要任何响应式数据或方法
68 +</script>
1 +<template>
2 + <header class="sticky top-0 z-50">
3 + <!-- Frosted glass effect background -->
4 + <div class="backdrop-blur-xl bg-white/70 shadow-sm">
5 + <div class="container mx-auto px-4">
6 + <div class="flex items-center justify-between h-16">
7 + <!-- Logo and Title -->
8 + <div class="flex items-center">
9 + <router-link to="/" class="flex items-center">
10 + <span class="text-2xl font-bold bg-gradient-to-r from-green-500 to-blue-400 bg-clip-text text-transparent">
11 + 读书会
12 + </span>
13 + </router-link>
14 + </div>
15 +
16 + <!-- Navigation - Desktop -->
17 + <nav class="hidden md:flex items-center space-x-6">
18 + <router-link to="/" class="text-gray-600 hover:text-green-500 font-medium">
19 + 首页
20 + </router-link>
21 + <router-link to="/activities" class="text-gray-600 hover:text-green-500 font-medium">
22 + 全部活动
23 + </router-link>
24 + <router-link to="/create-activity" class="text-gray-600 hover:text-green-500 font-medium">
25 + 创建活动
26 + </router-link>
27 + </nav>
28 +
29 + <!-- User Profile and Actions -->
30 + <div class="flex items-center">
31 + <!-- Messages Icon -->
32 + <router-link to="/messages" class="relative p-2 mr-4 text-gray-600 hover:text-green-500">
33 + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
34 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" />
35 + </svg>
36 + <span class="absolute top-1 right-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-white transform translate-x-1/2 -translate-y-1/2 bg-red-500 rounded-full">
37 + 2
38 + </span>
39 + </router-link>
40 +
41 + <!-- User Profile -->
42 + <div class="relative">
43 + <button @click="toggleProfileDropdown" class="flex items-center space-x-2">
44 + <div class="h-8 w-8 rounded-full overflow-hidden border-2 border-green-500">
45 + <img :src="currentUser?.avatar || '/assets/images/avatars/default_avatar.png'" alt="User Avatar" class="h-full w-full object-cover" />
46 + </div>
47 + <span class="hidden lg:block text-sm font-medium text-gray-700">{{ currentUser?.name || 'User' }}</span>
48 + <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
49 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
50 + </svg>
51 + </button>
52 +
53 + <!-- Profile Dropdown -->
54 + <div v-if="isProfileDropdownOpen" class="absolute right-0 mt-2 w-48 py-2 bg-white rounded-md shadow-lg backdrop-blur-xl bg-white/90 z-20">
55 + <router-link to="/profile" class="block px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-green-500">
56 + 个人资料
57 + </router-link>
58 + <router-link to="/my-activities" class="block px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-green-500">
59 + 我的活动
60 + </router-link>
61 + <router-link to="/settings" class="block px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-green-500">
62 + 设置
63 + </router-link>
64 + <button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-green-500">
65 + 退出登录
66 + </button>
67 + </div>
68 + </div>
69 +
70 + <!-- Mobile Menu Button -->
71 + <div class="ml-4 md:hidden">
72 + <button @click="toggleMenu" type="button" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-green-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-green-500">
73 + <span class="sr-only">Open main menu</span>
74 + <svg :class="{ 'hidden': isMenuOpen, 'block': !isMenuOpen }" class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
75 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
76 + </svg>
77 + <svg :class="{ 'block': isMenuOpen, 'hidden': !isMenuOpen }" class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
78 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
79 + </svg>
80 + </button>
81 + </div>
82 + </div>
83 + </div>
84 +
85 + <!-- Mobile Menu -->
86 + <div :class="{ 'block': isMenuOpen, 'hidden': !isMenuOpen }" class="md:hidden">
87 + <div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">
88 + <router-link to="/" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-green-500 hover:bg-green-50">
89 + 首页
90 + </router-link>
91 + <router-link to="/activities" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-green-500 hover:bg-green-50">
92 + 全部活动
93 + </router-link>
94 + <router-link to="/create-activity" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-green-500 hover:bg-green-50">
95 + 创建活动
96 + </router-link>
97 + <router-link to="/profile" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-green-500 hover:bg-green-50">
98 + 个人资料
99 + </router-link>
100 + <router-link to="/messages" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-green-500 hover:bg-green-50">
101 + 消息通知
102 + </router-link>
103 + </div>
104 + </div>
105 + </div>
106 + </div>
107 + </header>
108 +</template>
109 +
110 +<script setup>
111 +import { ref } from 'vue'
112 +import { useAppStore } from '../../stores/app'
113 +
114 +const appStore = useAppStore()
115 +const currentUser = appStore.currentUser
116 +
117 +const isMenuOpen = ref(false)
118 +const isProfileDropdownOpen = ref(false)
119 +
120 +const toggleMenu = () => {
121 + isMenuOpen.value = !isMenuOpen.value
122 +}
123 +
124 +const toggleProfileDropdown = () => {
125 + isProfileDropdownOpen.value = !isProfileDropdownOpen.value
126 +}
127 +</script>
1 +<!--
2 + * @Date: 2025-04-17 13:16:20
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 13:16:22
5 + * @FilePath: /reading-club-app/src/components/shared/ActivityCard.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <router-link
10 + :to="`/activity/${activity.id}`"
11 + class="block bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-200"
12 + >
13 + <div class="relative pb-48 overflow-hidden">
14 + <img
15 + :src="activity.cover_image"
16 + :alt="activity.title"
17 + class="absolute inset-0 h-full w-full object-cover"
18 + />
19 + <div
20 + v-if="activity.tags && activity.tags.length"
21 + class="absolute top-0 left-0 p-4 flex flex-wrap gap-2"
22 + >
23 + <span
24 + v-for="tag in activity.tags"
25 + :key="tag"
26 + class="px-3 py-1 text-sm bg-green-500 text-white rounded-full"
27 + >
28 + {{ tag }}
29 + </span>
30 + </div>
31 + </div>
32 +
33 + <div class="p-6">
34 + <h3 class="text-xl font-semibold text-gray-800 mb-2">{{ activity.title }}</h3>
35 + <p class="text-gray-600 text-sm mb-4 line-clamp-2">{{ activity.description }}</p>
36 +
37 + <div class="flex items-center text-sm text-gray-500 mb-4">
38 + <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
39 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
40 + </svg>
41 + <span>{{ formatDateTime(activity.start_time) }}</span>
42 + </div>
43 +
44 + <div class="flex items-center text-sm text-gray-500 mb-4">
45 + <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
46 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
47 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
48 + </svg>
49 + <span>{{ activity.location }}</span>
50 + </div>
51 +
52 + <div class="flex items-center justify-between">
53 + <div class="flex items-center">
54 + <img
55 + :src="activity.organizer_avatar"
56 + :alt="activity.organizer_name"
57 + class="w-8 h-8 rounded-full mr-2"
58 + />
59 + <span class="text-sm text-gray-600">{{ activity.organizer_name }}</span>
60 + </div>
61 + <span class="text-sm font-medium text-green-500">
62 + {{ activity.participant_count }}人参与
63 + </span>
64 + </div>
65 + </div>
66 + </router-link>
67 +</template>
68 +
69 +<script setup>
70 +import { defineProps } from 'vue'
71 +
72 +const props = defineProps({
73 + activity: {
74 + type: Object,
75 + required: true
76 + }
77 +})
78 +
79 +// 格式化日期时间
80 +const formatDateTime = (dateTimeStr) => {
81 + const date = new Date(dateTimeStr.replace(' ', 'T'))
82 + return new Intl.DateTimeFormat('zh-CN', {
83 + year: 'numeric',
84 + month: 'long',
85 + day: 'numeric',
86 + hour: '2-digit',
87 + minute: '2-digit'
88 + }).format(date)
89 +}
90 +</script>
1 +<template>
2 + <button
3 + :type="type"
4 + :class="[baseClasses, variantClass, sizeClass, roundedClass, block ? buttonBlock : '', className]"
5 + :disabled="disabled"
6 + @click="$emit('click', $event)"
7 + >
8 + <span v-if="leftIcon" class="mr-2">
9 + <slot name="leftIcon">
10 + {{ leftIcon }}
11 + </slot>
12 + </span>
13 + <slot></slot>
14 + <span v-if="rightIcon" class="ml-2">
15 + <slot name="rightIcon">
16 + {{ rightIcon }}
17 + </slot>
18 + </span>
19 + </button>
20 +</template>
21 +
22 +<script setup>
23 +import { computed } from 'vue'
24 +
25 +const buttonVariants = {
26 + primary: 'bg-gradient-to-r from-green-500 to-blue-500 hover:from-green-600 hover:to-blue-600 text-white border-transparent',
27 + secondary: 'bg-white hover:bg-gray-50 text-gray-700 border-gray-300',
28 + success: 'bg-green-600 hover:bg-green-700 text-white border-transparent',
29 + danger: 'bg-red-600 hover:bg-red-700 text-white border-transparent',
30 + warning: 'bg-yellow-500 hover:bg-yellow-600 text-white border-transparent',
31 + info: 'bg-blue-500 hover:bg-blue-600 text-white border-transparent',
32 + ghost: 'bg-transparent hover:bg-gray-100 text-gray-700 hover:text-gray-900 border-transparent'
33 +}
34 +
35 +const buttonSizes = {
36 + xs: 'px-2 py-1 text-xs',
37 + sm: 'px-2 py-1 text-sm',
38 + md: 'px-4 py-2',
39 + lg: 'px-6 py-3 text-lg',
40 + xl: 'px-8 py-4 text-xl'
41 +}
42 +
43 +const buttonRounded = {
44 + none: 'rounded-none',
45 + sm: 'rounded-sm',
46 + md: 'rounded-md',
47 + lg: 'rounded-lg',
48 + full: 'rounded-full'
49 +}
50 +
51 +const buttonBlock = 'w-full flex justify-center'
52 +
53 +const props = defineProps({
54 + variant: {
55 + type: String,
56 + default: 'primary',
57 + validator: (value) => Object.keys(buttonVariants).includes(value)
58 + },
59 + size: {
60 + type: String,
61 + default: 'md',
62 + validator: (value) => Object.keys(buttonSizes).includes(value)
63 + },
64 + rounded: {
65 + type: String,
66 + default: 'md',
67 + validator: (value) => Object.keys(buttonRounded).includes(value)
68 + },
69 + block: {
70 + type: Boolean,
71 + default: false
72 + },
73 + disabled: {
74 + type: Boolean,
75 + default: false
76 + },
77 + className: {
78 + type: String,
79 + default: ''
80 + },
81 + leftIcon: {
82 + type: [String, Object],
83 + default: null
84 + },
85 + rightIcon: {
86 + type: [String, Object],
87 + default: null
88 + },
89 + type: {
90 + type: String,
91 + default: 'button'
92 + }
93 +})
94 +
95 +defineEmits(['click'])
96 +
97 +const baseClasses = 'flex items-center justify-center font-medium border shadow-sm transition duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed'
98 +
99 +const variantClass = computed(() => buttonVariants[props.variant] || buttonVariants.primary)
100 +const sizeClass = computed(() => buttonSizes[props.size] || buttonSizes.md)
101 +const roundedClass = computed(() => buttonRounded[props.rounded] || buttonRounded.md)
102 +</script>
1 +<template>
2 + <div :class="['w-full', className]">
3 + <label v-if="label" :for="inputId" class="block text-sm font-medium text-gray-700 mb-1">
4 + {{ label }}
5 + <span v-if="required" class="text-red-500 ml-1">*</span>
6 + </label>
7 +
8 + <div class="relative rounded-md shadow-sm">
9 + <div v-if="leftIcon" class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
10 + <span class="text-gray-500 sm:text-sm">
11 + <slot name="leftIcon">
12 + {{ leftIcon }}
13 + </slot>
14 + </span>
15 + </div>
16 +
17 + <input
18 + :id="inputId"
19 + :type="type"
20 + :name="name"
21 + :placeholder="placeholder"
22 + :value="modelValue"
23 + :required="required"
24 + :disabled="disabled"
25 + @input="$emit('update:modelValue', $event.target.value)"
26 + @blur="$emit('blur', $event)"
27 + :class="[
28 + 'block w-full border-gray-300 rounded-md shadow-sm',
29 + 'focus:ring-green-500 focus:border-green-500 sm:text-sm',
30 + 'disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed',
31 + sizeClass,
32 + error ? 'border-red-300 text-red-900 placeholder-red-300 focus:ring-red-500 focus:border-red-500' : '',
33 + leftIcon ? 'pl-10' : '',
34 + rightIcon ? 'pr-10' : ''
35 + ]"
36 + v-bind="$attrs"
37 + />
38 +
39 + <div v-if="rightIcon" class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
40 + <span class="text-gray-500 sm:text-sm">
41 + <slot name="rightIcon">
42 + {{ rightIcon }}
43 + </slot>
44 + </span>
45 + </div>
46 + </div>
47 +
48 + <p v-if="error || helperText" :class="['mt-1 text-sm', error ? 'text-red-600' : 'text-gray-500']">
49 + {{ error || helperText }}
50 + </p>
51 + </div>
52 +</template>
53 +
54 +<script setup>
55 +import { computed } from 'vue'
56 +
57 +const inputSizes = {
58 + sm: 'px-2 py-1 text-sm',
59 + md: 'px-3 py-2',
60 + lg: 'px-4 py-3 text-lg'
61 +}
62 +
63 +const props = defineProps({
64 + modelValue: {
65 + type: [String, Number],
66 + default: ''
67 + },
68 + label: {
69 + type: String,
70 + default: ''
71 + },
72 + name: {
73 + type: String,
74 + required: true
75 + },
76 + type: {
77 + type: String,
78 + default: 'text'
79 + },
80 + placeholder: {
81 + type: String,
82 + default: ''
83 + },
84 + error: {
85 + type: String,
86 + default: ''
87 + },
88 + size: {
89 + type: String,
90 + default: 'md',
91 + validator: (value) => Object.keys(inputSizes).includes(value)
92 + },
93 + required: {
94 + type: Boolean,
95 + default: false
96 + },
97 + disabled: {
98 + type: Boolean,
99 + default: false
100 + },
101 + className: {
102 + type: String,
103 + default: ''
104 + },
105 + helperText: {
106 + type: String,
107 + default: ''
108 + },
109 + leftIcon: {
110 + type: [String, Object],
111 + default: null
112 + },
113 + rightIcon: {
114 + type: [String, Object],
115 + default: null
116 + }
117 +})
118 +
119 +defineEmits(['update:modelValue', 'blur'])
120 +
121 +const inputId = computed(() => `input-${props.name}`)
122 +const sizeClass = computed(() => inputSizes[props.size] || inputSizes.md)
123 +</script>
1 +<script setup>
2 +import { ref, onMounted, onUnmounted, watch } from 'vue';
3 +
4 +const props = defineProps({
5 + isOpen: {
6 + type: Boolean,
7 + required: true
8 + },
9 + title: {
10 + type: String,
11 + required: true
12 + },
13 + size: {
14 + type: String,
15 + default: 'md'
16 + },
17 + closeOnClickOutside: {
18 + type: Boolean,
19 + default: true
20 + },
21 + showCloseButton: {
22 + type: Boolean,
23 + default: true
24 + },
25 + contentClassName: {
26 + type: String,
27 + default: ''
28 + }
29 +});
30 +
31 +const emit = defineEmits(['close']);
32 +
33 +const modalRef = ref(null);
34 +const isMounted = ref(false);
35 +
36 +// Set sizes based on the size prop
37 +const sizeClasses = {
38 + sm: 'max-w-md',
39 + md: 'max-w-lg',
40 + lg: 'max-w-2xl',
41 + xl: 'max-w-4xl',
42 + full: 'max-w-full mx-4'
43 +};
44 +
45 +const modalSize = computed(() => sizeClasses[props.size] || sizeClasses.md);
46 +
47 +// Handle ESC key press
48 +const handleKeyDown = (event) => {
49 + if (event.key === 'Escape' && props.isOpen) {
50 + emit('close');
51 + }
52 +};
53 +
54 +// Handle click outside
55 +const handleClickOutside = (event) => {
56 + if (modalRef.value && !modalRef.value.contains(event.target) && props.closeOnClickOutside) {
57 + emit('close');
58 + }
59 +};
60 +
61 +// Handle mounting/unmounting and event listeners
62 +onMounted(() => {
63 + isMounted.value = true;
64 + document.addEventListener('keydown', handleKeyDown);
65 +});
66 +
67 +onUnmounted(() => {
68 + document.removeEventListener('keydown', handleKeyDown);
69 + document.removeEventListener('mousedown', handleClickOutside);
70 + document.body.style.overflow = 'auto';
71 +});
72 +
73 +// Watch isOpen changes
74 +watch(() => props.isOpen, (newValue) => {
75 + if (newValue) {
76 + document.body.style.overflow = 'hidden';
77 + document.addEventListener('mousedown', handleClickOutside);
78 + } else {
79 + document.body.style.overflow = 'auto';
80 + document.removeEventListener('mousedown', handleClickOutside);
81 + }
82 +});
83 +</script>
84 +
85 +<template>
86 + <Teleport to="body">
87 + <div v-if="isOpen && isMounted" class="fixed inset-0 z-50 overflow-y-auto">
88 + <!-- Backdrop with semi-transparent background -->
89 + <div class="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm transition-opacity"></div>
90 +
91 + <!-- Modal container -->
92 + <div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center">
93 + <div
94 + :class="['relative inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle w-full', modalSize]"
95 + ref="modalRef"
96 + >
97 + <!-- Modal header -->
98 + <div class="bg-white px-4 py-4 border-b border-gray-200 sm:px-6">
99 + <div class="flex items-center justify-between">
100 + <h3 class="text-lg leading-6 font-medium text-gray-900">
101 + {{ title }}
102 + </h3>
103 + <button
104 + v-if="showCloseButton"
105 + type="button"
106 + class="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none"
107 + @click="emit('close')"
108 + >
109 + <span class="sr-only">关闭</span>
110 + <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
111 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
112 + </svg>
113 + </button>
114 + </div>
115 + </div>
116 +
117 + <!-- Modal content -->
118 + <div :class="['bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4', contentClassName]">
119 + <slot></slot>
120 + </div>
121 +
122 + <!-- Modal footer -->
123 + <div v-if="$slots.footer" class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
124 + <slot name="footer"></slot>
125 + </div>
126 + </div>
127 + </div>
128 + </div>
129 + </Teleport>
130 +</template>
1 +<template>
2 + <div :class="className">
3 + <!-- Tab headers -->
4 + <div :class="getContainerClasses()">
5 + <button
6 + v-for="(tab, index) in tabs"
7 + :key="index"
8 + :class="getTabHeaderClasses(index)"
9 + @click="handleTabChange(index)"
10 + role="tab"
11 + :aria-selected="currentTab === index"
12 + >
13 + {{ tab.label }}
14 + </button>
15 + </div>
16 +
17 + <!-- Tab content -->
18 + <div class="pt-4">
19 + <component :is="tabs[currentTab]?.content" />
20 + </div>
21 + </div>
22 +</template>
23 +
24 +<script setup>
25 +import { ref, computed } from 'vue';
26 +
27 +const props = defineProps({
28 + tabs: {
29 + type: Array,
30 + required: true
31 + },
32 + activeTab: {
33 + type: Number,
34 + default: 0
35 + },
36 + className: {
37 + type: String,
38 + default: ''
39 + },
40 + variant: {
41 + type: String,
42 + default: 'underline', // 'underline', 'pills', 'bordered'
43 + validator: (value) => ['underline', 'pills', 'bordered'].includes(value)
44 + },
45 + size: {
46 + type: String,
47 + default: 'md', // 'sm', 'md', 'lg'
48 + validator: (value) => ['sm', 'md', 'lg'].includes(value)
49 + }
50 +});
51 +
52 +const emit = defineEmits(['change']);
53 +
54 +const currentTab = ref(props.activeTab);
55 +
56 +const handleTabChange = (index) => {
57 + currentTab.value = index;
58 + emit('change', index);
59 +};
60 +
61 +const getSizeClasses = () => {
62 + switch (props.size) {
63 + case 'sm': return 'text-sm py-1 px-2';
64 + case 'lg': return 'text-lg py-3 px-5';
65 + case 'md':
66 + default: return 'text-base py-2 px-4';
67 + }
68 +};
69 +
70 +const getTabHeaderClasses = (index) => {
71 + const isActive = index === currentTab.value;
72 + const sizeClasses = getSizeClasses();
73 +
74 + const baseClasses = 'font-medium transition-all duration-200 focus:outline-none';
75 +
76 + switch (props.variant) {
77 + case 'pills':
78 + return `${baseClasses} ${sizeClasses} rounded-md ${
79 + isActive
80 + ? 'bg-green-500 text-white'
81 + : 'text-gray-700 hover:text-green-500 hover:bg-green-50'
82 + }`;
83 +
84 + case 'bordered':
85 + return `${baseClasses} ${sizeClasses} border-b-2 ${
86 + isActive
87 + ? 'border-green-500 text-green-600'
88 + : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
89 + }`;
90 +
91 + case 'underline':
92 + default:
93 + return `${baseClasses} ${sizeClasses} border-b-2 ${
94 + isActive
95 + ? 'border-green-500 text-green-600'
96 + : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
97 + }`;
98 + }
99 +};
100 +
101 +const getContainerClasses = () => {
102 + switch (props.variant) {
103 + case 'pills':
104 + return 'flex p-1 space-x-1 bg-gray-100 rounded-lg';
105 + case 'bordered':
106 + return 'flex border-b border-gray-200';
107 + case 'underline':
108 + default:
109 + return 'flex border-b border-gray-200';
110 + }
111 +};
112 +</script>
1 +import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
2 +
3 +const AppContext = createContext();
4 +
5 +export const AppProvider = ({ children }) => {
6 + const [currentUser, setCurrentUser] = useState(null);
7 + const [activities, setActivities] = useState([]);
8 + const [loading, setLoading] = useState(true);
9 + const [error, setError] = useState(null);
10 + const [registrations, setRegistrations] = useState([]);
11 + const [userMessages, setUserMessages] = useState([]);
12 +
13 + // Initialize app data
14 + useEffect(() => {
15 + const fetchInitialData = async () => {
16 + try {
17 + setLoading(true);
18 +
19 + // Fetch users data - In a real app, this would be a real API call
20 + const usersResponse = await fetch('/data/users.json');
21 + const usersData = await usersResponse.json();
22 +
23 + // For demo purposes, set the first user as current user
24 + setCurrentUser(usersData[0]);
25 +
26 + // Fetch activities
27 + const activitiesResponse = await fetch('/data/activities.json');
28 + const activitiesData = await activitiesResponse.json();
29 + setActivities(activitiesData);
30 +
31 + // Fetch registrations
32 + const registrationsResponse = await fetch('/data/registrations.json');
33 + const registrationsData = await registrationsResponse.json();
34 + setRegistrations(registrationsData);
35 +
36 + // Fetch messages
37 + const messagesResponse = await fetch('/data/messages.json');
38 + const messagesData = await messagesResponse.json();
39 + setUserMessages(messagesData);
40 +
41 + setLoading(false);
42 + } catch (err) {
43 + console.error('Failed to fetch initial data:', err);
44 + setError('Failed to load application data. Please try again later.');
45 + setLoading(false);
46 + }
47 + };
48 +
49 + fetchInitialData();
50 + }, []);
51 +
52 + // Get activity by ID
53 + const getActivityById = useCallback((activityId) => {
54 + return activities.find(activity => activity.id === activityId) || null;
55 + }, [activities]);
56 +
57 + // Get user's registrations
58 + const getUserRegistrations = useCallback(() => {
59 + if (!currentUser) return [];
60 + return registrations.filter(reg => reg.user_id === currentUser.id);
61 + }, [currentUser, registrations]);
62 +
63 + // Register for an activity
64 + const registerForActivity = useCallback((activityId, formData) => {
65 + if (!currentUser) return { success: false, error: 'User not logged in' };
66 +
67 + // In a real app, this would make an API call
68 + const newRegistration = {
69 + id: `R${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
70 + activity_id: activityId,
71 + user_id: currentUser.id,
72 + registration_time: new Date().toISOString().replace('T', ' ').substring(0, 19),
73 + status: 'pending',
74 + custom_fields: formData.fields,
75 + custom_answers: formData.answers
76 + };
77 +
78 + setRegistrations(prev => [...prev, newRegistration]);
79 + return { success: true, registrationId: newRegistration.id };
80 + }, [currentUser]);
81 +
82 + // Create a new activity
83 + const createActivity = useCallback((activityData) => {
84 + if (!currentUser) return { success: false, error: 'User not logged in' };
85 +
86 + // In a real app, this would make an API call
87 + const newActivity = {
88 + id: `A${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
89 + organizer_id: currentUser.id,
90 + organizer_name: currentUser.name,
91 + ...activityData,
92 + participant_count: 0,
93 + is_published: activityData.is_published || true,
94 + is_public: activityData.is_public || true,
95 + };
96 +
97 + setActivities(prev => [...prev, newActivity]);
98 + return { success: true, activityId: newActivity.id };
99 + }, [currentUser]);
100 +
101 + // Check in for an activity
102 + const checkInForActivity = useCallback((activityId, registrationId, method = 'manual') => {
103 + if (!currentUser) return { success: false, error: 'User not logged in' };
104 +
105 + // In a real app, this would make an API call
106 + const checkinData = {
107 + id: `C${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
108 + activity_id: activityId,
109 + user_id: currentUser.id,
110 + registration_id: registrationId,
111 + checkin_time: new Date().toISOString().replace('T', ' ').substring(0, 19),
112 + checkin_type: method,
113 + status: 'successful',
114 + is_late: false
115 + };
116 +
117 + // In a real app, we would update the backend
118 + return { success: true, checkin: checkinData };
119 + }, [currentUser]);
120 +
121 + // Get user messages
122 + const getUserMessages = useCallback(() => {
123 + if (!currentUser) return [];
124 + return userMessages.filter(msg => msg.recipient_id === currentUser.id);
125 + }, [currentUser, userMessages]);
126 +
127 + // Toggle read status of a message
128 + const toggleMessageReadStatus = useCallback((messageId) => {
129 + setUserMessages(prev =>
130 + prev.map(msg =>
131 + msg.id === messageId
132 + ? { ...msg, read_status: !msg.read_status }
133 + : msg
134 + )
135 + );
136 + }, []);
137 +
138 + const contextValue = {
139 + currentUser,
140 + activities,
141 + loading,
142 + error,
143 + registrations,
144 + userMessages,
145 + getActivityById,
146 + getUserRegistrations,
147 + registerForActivity,
148 + createActivity,
149 + checkInForActivity,
150 + getUserMessages,
151 + toggleMessageReadStatus
152 + };
153 +
154 + return (
155 + <AppContext.Provider value={contextValue}>
156 + {children}
157 + </AppContext.Provider>
158 + );
159 +};
160 +
161 +export const useApp = () => {
162 + const context = useContext(AppContext);
163 + if (!context) {
164 + throw new Error('useApp must be used within an AppProvider');
165 + }
166 + return context;
167 +};
168 +
169 +export default AppContext;
...\ No newline at end of file ...\ No newline at end of file
1 +import { useState, useEffect } from 'react';
2 +import { fetchUsers } from '../utils/api';
3 +
4 +// A custom hook to manage authentication state
5 +const useAuth = () => {
6 + const [user, setUser] = useState(null);
7 + const [loading, setLoading] = useState(true);
8 + const [error, setError] = useState(null);
9 +
10 + useEffect(() => {
11 + // In a real app, this would check if the user is logged in via JWT, cookies, etc.
12 + // For this demo, we'll just load the first user from the mock data
13 + const checkAuth = async () => {
14 + try {
15 + const users = await fetchUsers();
16 + // For demo purposes, use the first user as the logged-in user
17 + if (users && users.length > 0) {
18 + // Check if there's a stored user ID in local storage (for persistence)
19 + const storedUserId = localStorage.getItem('currentUserId');
20 + if (storedUserId) {
21 + const storedUser = users.find(u => u.id === storedUserId);
22 + if (storedUser) {
23 + setUser(storedUser);
24 + } else {
25 + setUser(users[0]); // Fallback to first user
26 + }
27 + } else {
28 + setUser(users[0]);
29 + }
30 + }
31 + setLoading(false);
32 + } catch (err) {
33 + console.error('Authentication error:', err);
34 + setError('Failed to authenticate. Please try again.');
35 + setLoading(false);
36 + }
37 + };
38 +
39 + checkAuth();
40 + }, []);
41 +
42 + // Login function - in a real app, this would validate credentials
43 + const login = async (userId) => {
44 + try {
45 + setLoading(true);
46 + const users = await fetchUsers();
47 + const foundUser = users.find(u => u.id === userId);
48 +
49 + if (foundUser) {
50 + setUser(foundUser);
51 + localStorage.setItem('currentUserId', userId);
52 + setLoading(false);
53 + return { success: true };
54 + } else {
55 + setError('User not found');
56 + setLoading(false);
57 + return { success: false, error: 'User not found' };
58 + }
59 + } catch (err) {
60 + console.error('Login error:', err);
61 + setError('Failed to log in. Please try again.');
62 + setLoading(false);
63 + return { success: false, error: err.message };
64 + }
65 + };
66 +
67 + // Logout function
68 + const logout = () => {
69 + setUser(null);
70 + localStorage.removeItem('currentUserId');
71 + };
72 +
73 + // Switch user (for demo purposes)
74 + const switchUser = async (userId) => {
75 + return await login(userId);
76 + };
77 +
78 + return {
79 + user,
80 + loading,
81 + error,
82 + login,
83 + logout,
84 + switchUser,
85 + isAuthenticated: !!user
86 + };
87 +};
88 +
89 +export default useAuth;
...\ No newline at end of file ...\ No newline at end of file
1 +@tailwind base;
2 +@tailwind components;
3 +@tailwind utilities;
4 +
5 +::-webkit-scrollbar {
6 + width: 5px;
7 + height: 5px;
8 +}
9 +
10 +::-webkit-scrollbar-track {
11 + background-color: transparent;
12 +}
13 +
14 +::-webkit-scrollbar-thumb {
15 + border-radius: 25px;
16 + transition: all 0.3s;
17 + background-color: rgba(106, 115, 125, 0.2);
18 + &:hover {
19 + background-color: rgba(106, 115, 125, 0.27);
20 + }
21 +}
22 +
23 +::-webkit-scrollbar-corner {
24 + display: none;
25 +}
1 +import { createApp } from 'vue'
2 +import { createPinia } from 'pinia'
3 +import { createRouter, createWebHistory } from 'vue-router'
4 +import App from './App.vue'
5 +import './index.css'
6 +
7 +// 创建Vue应用实例
8 +const app = createApp(App)
9 +
10 +// 创建Pinia状态管理实例
11 +const pinia = createPinia()
12 +app.use(pinia)
13 +
14 +// 创建路由实例
15 +const router = createRouter({
16 + history: createWebHistory(),
17 + routes: [
18 + { path: '/', component: () => import('./pages/HomePage.vue') },
19 + { path: '/activity/:activityId', component: () => import('./pages/ActivityDetail.vue') },
20 + { path: '/create-activity', component: () => import('./pages/CreateActivity.vue') },
21 + { path: '/profile', component: () => import('./pages/UserProfile.vue') },
22 + { path: '/registration/:activityId', component: () => import('./pages/Registration.vue') },
23 + { path: '/check-in/:activityId', component: () => import('./pages/CheckIn.vue') },
24 + { path: '/messages/:activityId', component: () => import('./pages/Messages.vue') },
25 + { path: '/:pathMatch(.*)*', redirect: '/' }
26 + ]
27 +})
28 +app.use(router)
29 +
30 +// 挂载应用
31 +app.mount('#root')
1 +<template>
2 + <div class="min-h-screen bg-gray-50">
3 + <!-- Activity Header -->
4 + <div class="bg-white shadow-sm">
5 + <div class="container mx-auto px-4 py-4">
6 + <button @click="goBack" class="flex items-center text-gray-600 hover:text-green-500">
7 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20"
8 + fill="currentColor">
9 + <path fill-rule="evenodd"
10 + d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z"
11 + clip-rule="evenodd" />
12 + </svg>
13 + 返回
14 + </button>
15 + </div>
16 + </div>
17 +
18 + <!-- Loading State -->
19 + <div v-if="loading" class="min-h-screen flex justify-center items-center">
20 + <div class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-green-500"></div>
21 + </div>
22 +
23 + <!-- Error State -->
24 + <div v-else-if="error || !activity"
25 + class="min-h-screen flex flex-col justify-center items-center bg-gray-50 px-4">
26 + <div class="text-red-500 text-6xl mb-4">
27 + <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24"
28 + stroke="currentColor">
29 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
30 + d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
31 + </svg>
32 + </div>
33 + <h1 class="text-2xl font-bold mb-2">活动不存在</h1>
34 + <p class="text-gray-600 mb-6">{{ error || '该活动可能已被删除或不存在' }}</p>
35 + <Button @click="goBack" variant="primary">返回上一页</Button>
36 + </div>
37 +
38 + <!-- Activity Content -->
39 + <template v-else>
40 + <!-- Activity Cover -->
41 + <div class="relative h-64 md:h-80 bg-gray-300 overflow-hidden">
42 + <img :src="getActivityImage(activity.id)" :alt="activity.title" class="w-full h-full object-cover" />
43 + <div class="absolute inset-0 bg-black bg-opacity-40"></div>
44 + <div class="absolute bottom-0 left-0 right-0 p-6">
45 + <div class="container mx-auto">
46 + <span
47 + :class="['inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium mb-2', activityStatus.class]">
48 + {{ activityStatus.text }}
49 + </span>
50 + <h1 class="text-2xl md:text-3xl font-bold text-white">{{ activity.title }}</h1>
51 + </div>
52 + </div>
53 + </div>
54 +
55 + <!-- Activity Content -->
56 + <div class="container mx-auto px-4 py-8">
57 + <div class="flex flex-col md:flex-row gap-8">
58 + <!-- Main content -->
59 + <div class="w-full md:w-2/3">
60 + <!-- Organizer -->
61 + <div class="mb-8 flex items-center">
62 + <div class="flex items-center">
63 + <div class="h-12 w-12 rounded-full overflow-hidden bg-gray-200 mr-3">
64 + <svg xmlns="http://www.w3.org/2000/svg" class="h-full w-full text-gray-400"
65 + fill="none" viewBox="0 0 24 24" stroke="currentColor">
66 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1"
67 + d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
68 + </svg>
69 + </div>
70 + <div>
71 + <div class="font-medium">主办方:{{ activity.organizer_name }}</div>
72 + <div class="text-sm text-gray-500">活动组织者</div>
73 + </div>
74 + </div>
75 + </div>
76 +
77 + <!-- Activity Tabs -->
78 + <Tabs :tabs="tabs" />
79 + </div>
80 +
81 + <!-- Sidebar -->
82 + <div class="w-full md:w-1/3 mt-8 md:mt-0">
83 + <div class="bg-white rounded-lg shadow-sm p-6 mb-6">
84 + <h3 class="text-lg font-medium text-gray-900 mb-4">活动信息</h3>
85 +
86 + <div class="space-y-4">
87 + <!-- Date and Time -->
88 + <div class="flex">
89 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mt-0.5 mr-3"
90 + fill="none" viewBox="0 0 24 24" stroke="currentColor">
91 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
92 + d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
93 + </svg>
94 + <div>
95 + <div class="font-medium">活动时间</div>
96 + <div class="text-gray-600">{{ formatDate(activity.start_time) }}</div>
97 + </div>
98 + </div>
99 +
100 + <!-- Location -->
101 + <div class="flex">
102 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mt-0.5 mr-3"
103 + fill="none" viewBox="0 0 24 24" stroke="currentColor">
104 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
105 + d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
106 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
107 + d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
108 + </svg>
109 + <div>
110 + <div class="font-medium">活动地点</div>
111 + <div class="text-gray-600">
112 + {{ activity.activity_type === 'online'
113 + ? '线上活动' + (activity.online_link ? `(${activity.online_link})` : '')
114 + : (activity.location ? (typeof activity.location === 'object' ?
115 + activity.location.name : activity.location) : '地点未设置')
116 + }}
117 + </div>
118 + </div>
119 + </div>
120 +
121 + <!-- Registration -->
122 + <div class="flex">
123 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mt-0.5 mr-3"
124 + fill="none" viewBox="0 0 24 24" stroke="currentColor">
125 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
126 + d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
127 + </svg>
128 + <div>
129 + <div class="font-medium">报名时间</div>
130 + <div class="text-gray-600">{{ formatDate(activity.registration_start) }} - {{
131 + formatDate(activity.registration_end) }}</div>
132 + </div>
133 + </div>
134 +
135 + <!-- Participants -->
136 + <div class="flex">
137 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mt-0.5 mr-3"
138 + fill="none" viewBox="0 0 24 24" stroke="currentColor">
139 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
140 + d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
141 + </svg>
142 + <div>
143 + <div class="font-medium">参与人数</div>
144 + <div class="text-gray-600">{{ activity.participant_count }}/{{
145 + activity.max_participants }}</div>
146 + </div>
147 + </div>
148 +
149 + <!-- Registration progress -->
150 + <div class="mt-2">
151 + <div class="flex items-center justify-between text-sm mb-1">
152 + <span>报名进度</span>
153 + <span>{{ Math.round((activity.participant_count / activity.max_participants) *
154 + 100) }}%</span>
155 + </div>
156 + <div class="w-full bg-gray-200 rounded-full h-2">
157 + <div class="bg-gradient-to-r from-green-400 to-blue-500 h-2 rounded-full"
158 + :style="{ width: `${Math.min((activity.participant_count / activity.max_participants) * 100, 100)}%` }">
159 + </div>
160 + </div>
161 + </div>
162 +
163 + <!-- Registration Status -->
164 + <div v-if="hasRegistered"
165 + class="mt-2 bg-green-50 border border-green-100 rounded-md p-4">
166 + <div class="flex items-center">
167 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2"
168 + viewBox="0 0 20 20" fill="currentColor">
169 + <path fill-rule="evenodd"
170 + d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
171 + clip-rule="evenodd" />
172 + </svg>
173 + <span class="font-medium text-green-800">您已报名此活动</span>
174 + </div>
175 + <div class="mt-2 flex items-center">
176 + <span class="text-sm text-green-700 mr-2">报名状态:</span>
177 + <span :class="registrationStatusBadge.class">{{ registrationStatusBadge.text
178 + }}</span>
179 + </div>
180 + </div>
181 +
182 + <!-- Registration button or message -->
183 + <template v-if="!hasRegistered">
184 + <Button variant="primary" size="lg" block
185 + :disabled="!isRegistrationOpen || activity.participant_count >= activity.max_participants"
186 + @click="handleRegistration" class="mt-4">
187 + {{ isRegistrationOpen
188 + ? activity.participant_count >= activity.max_participants
189 + ? '名额已满'
190 + : '立即报名'
191 + : '报名已截止' }}
192 + </Button>
193 + </template>
194 +
195 + <RouterLink v-if="hasRegistered" :to="`/check-in/${activity.id}`">
196 + <Button variant="secondary" size="md" block class="mt-2">
197 + 活动签到
198 + </Button>
199 + </RouterLink>
200 + </div>
201 + </div>
202 +
203 + <!-- Similar Activities -->
204 + <div v-if="similarActivities.length > 0" class="bg-white rounded-lg shadow-sm p-6">
205 + <h3 class="text-lg font-medium text-gray-900 mb-4">相似活动推荐</h3>
206 + <div class="space-y-4">
207 + <ActivityCard v-for="activity in similarActivities" :key="activity.id"
208 + :activity="activity" variant="compact" />
209 + </div>
210 + </div>
211 + </div>
212 + </div>
213 + </div>
214 + </template>
215 +
216 + <!-- Register Modal -->
217 + <Modal v-if="showRegisterModal" @close="showRegisterModal = false">
218 + <template #title>活动报名</template>
219 + <template #content>
220 + <form @submit.prevent="submitRegistration">
221 + <!-- Registration form fields -->
222 + </form>
223 + </template>
224 + </Modal>
225 + </div>
226 +</template>
227 +
228 +<script setup>
229 +import { ref, computed, onMounted } from 'vue'
230 +import { useRoute, useRouter } from 'vue-router'
231 +import { useAppStore } from '../stores/app'
232 +import Button from '../components/shared/Button.vue'
233 +import Modal from '../components/shared/Modal.vue'
234 +import Tabs from '../components/shared/Tabs.vue'
235 +import ActivityCard from '../components/shared/ActivityCard.vue'
236 +
237 +const route = useRoute()
238 +const router = useRouter()
239 +const store = useAppStore()
240 +
241 +// State
242 +const activity = ref(null)
243 +const loading = ref(true)
244 +const error = ref(null)
245 +const showRegisterModal = ref(false)
246 +const similarActivities = ref([])
247 +const hasRegistered = ref(false)
248 +const registrationStatus = ref(null)
249 +
250 +// Computed
251 +const activityStatus = computed(() => {
252 + if (!activity.value) return {}
253 +
254 + const now = new Date()
255 + const startTime = new Date(activity.value.start_time.replace(' ', 'T'))
256 + const endTime = new Date(activity.value.end_time.replace(' ', 'T'))
257 + const registrationEnd = new Date(activity.value.registration_end.replace(' ', 'T'))
258 +
259 + if (now > endTime) {
260 + return {
261 + text: '已结束',
262 + class: 'bg-gray-100 text-gray-800'
263 + }
264 + } else if (now >= startTime && now <= endTime) {
265 + return {
266 + text: '进行中',
267 + class: 'bg-green-100 text-green-800'
268 + }
269 + } else if (now <= registrationEnd) {
270 + return {
271 + text: '报名中',
272 + class: 'bg-blue-100 text-blue-800'
273 + }
274 + } else {
275 + return {
276 + text: '即将开始',
277 + class: 'bg-yellow-100 text-yellow-800'
278 + }
279 + }
280 +})
281 +
282 +const isRegistrationOpen = computed(() => {
283 + if (!activity.value) return false
284 + const now = new Date()
285 + return now >= new Date(activity.value.registration_start.replace(' ', 'T')) &&
286 + now <= new Date(activity.value.registration_end.replace(' ', 'T'))
287 +})
288 +
289 +const registrationStatusBadge = computed(() => {
290 + if (!registrationStatus.value) return null
291 +
292 + const statusMap = {
293 + pending: { text: '审核中', class: 'bg-yellow-100 text-yellow-800' },
294 + approved: { text: '已通过', class: 'bg-green-100 text-green-800' },
295 + rejected: { text: '已拒绝', class: 'bg-red-100 text-red-800' },
296 + waitlist: { text: '候补', class: 'bg-purple-100 text-purple-800' }
297 + }
298 +
299 + return statusMap[registrationStatus.value] || {
300 + text: registrationStatus.value,
301 + class: 'bg-gray-100 text-gray-800'
302 + }
303 +})
304 +
305 +const tabs = computed(() => [
306 + {
307 + label: '活动详情',
308 + content: activity.value?.description.split('\n').map((paragraph, idx) => (
309 + `<p key="${idx}" class="mb-4">${paragraph}</p>`
310 + )).join('')
311 + },
312 + {
313 + label: '参与须知',
314 + content: `
315 + <h3 class="font-medium text-lg mb-4">参与须知</h3>
316 + <ul class="list-disc pl-5 space-y-2">
317 + <li>请准时到达活动地点或登录线上会议</li>
318 + <li>请提前阅读相关书籍或材料</li>
319 + <li>活动开始后,请将手机调至静音模式</li>
320 + <li>尊重他人发言,不打断他人</li>
321 + <li>可携带笔记本进行记录</li>
322 + <li>如需取消参与,请提前24小时通知主办方</li>
323 + </ul>
324 + `
325 + },
326 + {
327 + label: '常见问题',
328 + content: `
329 + <div className="py-4">
330 + <div className="space-y-6">
331 + <div>
332 + <h4 className="font-medium text-gray-900">如何取消报名?</h4>
333 + <p className="mt-2 text-gray-600">您可以在"我的活动"页面找到已报名的活动,点击"取消报名"按钮即可。请注意,活动开始前24小时内取消将无法获得退款。</p>
334 + </div>
335 + <div>
336 + <h4 className="font-medium text-gray-900">活动材料如何获取?</h4>
337 + <p className="mt-2 text-gray-600">报名成功后,您将在"我的活动"页面看到活动详情,相关材料可在页面底部下载或通过邮件接收。</p>
338 + </div>
339 + <div>
340 + <h4 className="font-medium text-gray-900">线上活动如何参加?</h4>
341 + <p className="mt-2 text-gray-600">线上活动将在活动开始前30分钟发送会议链接到您的邮箱和手机短信,您也可以在"我的活动"页面找到入口链接。</p>
342 + </div>
343 + </div>
344 + </div>
345 + `}
346 +])
347 +
348 +// Functions
349 +const goBack = () => {
350 + router.back()
351 +}
352 +
353 +const getActivityImage = (activityId) => {
354 + return `/ assets / images / activities / ${activityId}.jpg`
355 +}
356 +
357 +const formatDate = (dateString) => {
358 + if (!dateString) return ''
359 + const date = new Date(dateString.replace(' ', 'T'))
360 + return date.toLocaleString('zh-CN', {
361 + year: 'numeric',
362 + month: '2-digit',
363 + day: '2-digit',
364 + hour: '2-digit',
365 + minute: '2-digit'
366 + })
367 +}
368 +
369 +const handleRegistration = () => {
370 + showRegisterModal.value = true
371 +}
372 +
373 +const submitRegistration = async () => {
374 + // TODO: Implement registration submission
375 + showRegisterModal.value = false
376 +}
377 +
378 +// Fetch activity data
379 +onMounted(async () => {
380 + try {
381 + const activityId = route.params.id
382 + const response = await store.fetchActivity(activityId)
383 + activity.value = response
384 + loading.value = false
385 +
386 + // Check registration status
387 + const registration = await store.checkRegistration(activityId)
388 + hasRegistered.value = registration !== null
389 + registrationStatus.value = registration?.status
390 +
391 + // Fetch similar activities
392 + similarActivities.value = await store.fetchSimilarActivities(activityId)
393 + } catch (err) {
394 + error.value = err.message
395 + loading.value = false
396 + }
397 +})
398 +</script>
1 +<template>
2 + <div class="min-h-screen bg-gray-50">
3 + <!-- Loading State -->
4 + <div v-if="loading" class="min-h-screen flex justify-center items-center bg-gray-50">
5 + <div class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-green-500"></div>
6 + </div>
7 +
8 + <!-- Error State -->
9 + <div v-else-if="error" class="min-h-screen flex flex-col justify-center items-center bg-gray-50 px-4">
10 + <div class="text-red-500 text-6xl mb-4">
11 + <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24"
12 + stroke="currentColor">
13 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
14 + d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
15 + </svg>
16 + </div>
17 + <h1 class="text-2xl font-bold mb-2">无法签到</h1>
18 + <p class="text-gray-600 mb-6">{{ error }}</p>
19 + <Button @click="goBack" variant="primary">返回上一页</Button>
20 + </div>
21 +
22 + <!-- Already Checked In -->
23 + <div v-else-if="userCheckIn" class="py-8">
24 + <div class="container mx-auto px-4">
25 + <div class="max-w-md mx-auto bg-white rounded-lg shadow-sm overflow-hidden">
26 + <div class="bg-gradient-to-r from-green-500 to-blue-500 px-6 py-4">
27 + <h1 class="text-2xl font-bold text-white">签到成功</h1>
28 + </div>
29 +
30 + <div class="p-6 text-center">
31 + <div class="flex justify-center">
32 + <div class="rounded-full bg-green-100 p-4">
33 + <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-green-500" fill="none"
34 + viewBox="0 0 24 24" stroke="currentColor">
35 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
36 + d="M5 13l4 4L19 7" />
37 + </svg>
38 + </div>
39 + </div>
40 +
41 + <h2 class="text-2xl font-bold text-gray-800 mt-6">您已完成签到</h2>
42 + <p class="text-gray-600 mt-2">签到时间:{{ formatDate(userCheckIn.check_in_time) }}</p>
43 + <p class="text-gray-600">祝您阅读愉快!</p>
44 +
45 + <div class="mt-8">
46 + <h3 class="font-semibold text-gray-800 mb-2">活动信息</h3>
47 + <div class="bg-gray-50 rounded-lg p-4 text-left">
48 + <p class="font-medium text-gray-900">{{ activity.title }}</p>
49 + <p class="text-gray-600 text-sm mt-1">
50 + 时间:{{ formatDate(activity.start_time) }} - {{ formatDate(activity.end_time) }}
51 + </p>
52 + <p class="text-gray-600 text-sm mt-1">
53 + 地点:{{ activity.activity_type === 'online' ? '线上活动' : (activity.location ? (typeof
54 + activity.location === 'object' ? activity.location.name : activity.location) :
55 + '地点未设置') }}
56 + </p>
57 + </div>
58 + </div>
59 +
60 + <Button @click="goToActivityDetail" variant="primary" class="mt-8">
61 + 返回活动详情
62 + </Button>
63 + </div>
64 + </div>
65 + </div>
66 + </div>
67 +
68 + <!-- Activity Not Now -->
69 + <div v-else-if="!isActivityNow()" class="py-8">
70 + <div class="container mx-auto px-4">
71 + <div class="max-w-md mx-auto bg-white rounded-lg shadow-sm overflow-hidden">
72 + <div class="bg-gradient-to-r from-green-500 to-blue-500 px-6 py-4">
73 + <h1 class="text-2xl font-bold text-white">暂不可签到</h1>
74 + </div>
75 +
76 + <div class="p-6">
77 + <div class="text-center">
78 + <div class="inline-block rounded-full bg-blue-100 p-4">
79 + <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-blue-500" fill="none"
80 + viewBox="0 0 24 24" stroke="currentColor">
81 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
82 + d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
83 + </svg>
84 + </div>
85 +
86 + <h2 class="text-xl font-bold text-gray-800 mt-6">签到尚未开始</h2>
87 + <p class="text-gray-600 mt-2">
88 + {{ new Date() < new Date(activity.start_time.replace(' ', ' T')) ? '活动签到将于活动开始前30分钟开放'
89 + : '活动已结束,签到已关闭' }} </p>
90 +
91 + <div class="mt-8">
92 + <h3 class="font-semibold text-gray-800 mb-2">活动信息</h3>
93 + <div class="bg-gray-50 rounded-lg p-4 text-left">
94 + <p class="font-medium text-gray-900">{{ activity.title }}</p>
95 + <p class="text-gray-600 text-sm mt-1">
96 + 时间:{{ formatDate(activity.start_time) }} - {{
97 + formatDate(activity.end_time) }}
98 + </p>
99 + <p class="text-gray-600 text-sm mt-1">
100 + 地点:{{ activity.activity_type === 'online' ? '线上活动' : (activity.location
101 + ? (typeof activity.location === 'object' ? activity.location.name :
102 + activity.location) : '地点未设置') }}
103 + </p>
104 + </div>
105 + </div>
106 +
107 + <Button @click="goToActivityDetail" variant="primary" class="mt-6">
108 + 返回活动详情
109 + </Button>
110 + </div>
111 + </div>
112 + </div>
113 + </div>
114 + </div>
115 +
116 + <!-- Main Check-in Flow -->
117 + <div v-else class="py-8">
118 + <div class="container mx-auto px-4">
119 + <div class="max-w-md mx-auto bg-white rounded-lg shadow-sm overflow-hidden">
120 + <div class="bg-gradient-to-r from-green-500 to-blue-500 px-6 py-4">
121 + <h1 class="text-2xl font-bold text-white">活动签到</h1>
122 + </div>
123 +
124 + <div class="p-6">
125 + <!-- Activity Info -->
126 + <div class="mb-6">
127 + <h2 class="text-xl font-medium text-gray-900 mb-2">{{ activity.title }}</h2>
128 + <p class="text-gray-600 text-sm">
129 + 时间:{{ formatDate(activity.start_time) }} - {{ formatDate(activity.end_time) }}
130 + </p>
131 + <p class="text-gray-600 text-sm mt-1">
132 + 地点:{{ activity.activity_type === 'online' ? '线上活动' : (activity.location ? (typeof
133 + activity.location === 'object' ? activity.location.name : activity.location) : '地点未设置')
134 + }}
135 + </p>
136 +
137 + <div v-if="userRegistration.status === 'approved'"
138 + class="bg-green-50 border border-green-200 rounded-md p-3 mt-4">
139 + <div class="flex">
140 + <svg class="h-5 w-5 text-green-500 mr-2" xmlns="http://www.w3.org/2000/svg"
141 + viewBox="0 0 20 20" fill="currentColor">
142 + <path fill-rule="evenodd"
143 + d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
144 + clip-rule="evenodd" />
145 + </svg>
146 + <span class="text-green-800 font-medium">您的报名已通过审核</span>
147 + </div>
148 + </div>
149 + </div>
150 +
151 + <!-- Check-in Steps -->
152 + <div v-if="checkInStep === 'initial'" class="text-center">
153 + <div class="inline-block bg-blue-100 rounded-full p-4 mb-6">
154 + <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-blue-600" fill="none"
155 + viewBox="0 0 24 24" stroke="currentColor">
156 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
157 + d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
158 + </svg>
159 + </div>
160 +
161 + <h3 class="text-lg font-medium text-gray-900 mb-2">活动签到</h3>
162 + <p class="text-gray-600 mb-6">
163 + 点击下方按钮生成签到码,请向工作人员出示
164 + </p>
165 +
166 + <Button @click="generateCheckInCode" variant="primary" size="lg" block>
167 + 生成签到码
168 + </Button>
169 + </div>
170 +
171 + <div v-else-if="checkInStep === 'code'" class="text-center">
172 + <h3 class="text-lg font-medium text-gray-900 mb-2">您的签到码</h3>
173 + <p class="text-gray-600 mb-4">请向工作人员出示以下签到码</p>
174 +
175 + <div class="border-2 border-gray-300 rounded-lg p-4 mb-6">
176 + <div class="mb-4">
177 + <div class="w-48 h-48 mx-auto bg-gray-100 flex items-center justify-center">
178 + <div class="text-center">
179 + <div class="text-xl font-bold mb-2">签到码</div>
180 + <div class="text-3xl font-bold">{{ checkInCode }}</div>
181 + </div>
182 + </div>
183 + </div>
184 +
185 + <div class="flex justify-center">
186 + <div
187 + class="text-3xl font-bold tracking-wide text-gray-800 py-2 px-4 border-2 border-gray-300 rounded-lg">
188 + {{ checkInCode }}
189 + </div>
190 + </div>
191 + </div>
192 +
193 + <p class="text-sm text-gray-600">
194 + {{ countdown > 0 ? `签到码有效期: ${countdown} 秒` : '签到码已过期,请重新生成' }}
195 + </p>
196 +
197 + <div class="mt-6 text-sm">
198 + <p class="text-gray-700">或输入工作人员提供的活动码</p>
199 + <div class="flex mt-2">
200 + <input type="text" v-model="inputCode" placeholder="请输入6位签到码"
201 + class="flex-1 block border-gray-300 rounded-l-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm" />
202 + <Button @click="handleSubmitCode" variant="primary" class="rounded-l-none"
203 + :disabled="isSubmitting || !inputCode">
204 + 验证
205 + </Button>
206 + </div>
207 + </div>
208 +
209 + <Button @click="setCheckInStep('initial')" variant="secondary" size="md" class="mt-6">
210 + 返回
211 + </Button>
212 + </div>
213 +
214 + <div v-else-if="checkInStep === 'success'" class="text-center">
215 + <div class="flex justify-center">
216 + <div class="rounded-full bg-green-100 p-4">
217 + <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-green-500" fill="none"
218 + viewBox="0 0 24 24" stroke="currentColor">
219 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
220 + d="M5 13l4 4L19 7" />
221 + </svg>
222 + </div>
223 + </div>
224 +
225 + <h2 class="text-2xl font-bold text-gray-800 mt-6">签到成功</h2>
226 + <p class="text-gray-600 mt-2">祝您阅读愉快!</p>
227 +
228 + <Button @click="goToActivityDetail" variant="primary" class="mt-8">
229 + 返回活动详情
230 + </Button>
231 + </div>
232 +
233 + <div v-else-if="checkInStep === 'error'" class="text-center">
234 + <div class="flex justify-center">
235 + <div class="rounded-full bg-red-100 p-4">
236 + <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-red-500" fill="none"
237 + viewBox="0 0 24 24" stroke="currentColor">
238 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
239 + d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
240 + </svg>
241 + </div>
242 + </div>
243 +
244 + <h2 class="text-xl font-bold text-gray-800 mt-6">签到失败</h2>
245 + <p class="text-red-500 mt-2">{{ error }}</p>
246 +
247 + <Button @click="setCheckInStep('initial')" variant="primary" class="mt-6">
248 + 重新签到
249 + </Button>
250 + </div>
251 + </div>
252 + </div>
253 + </div>
254 + </div>
255 + </div>
256 +</template>
257 +
258 +<script setup>
259 +import { ref, onMounted, watch } from 'vue'
260 +import { useRoute, useRouter } from 'vue-router'
261 +import { useAppStore } from '../stores/app'
262 +import Button from '../components/shared/Button.vue'
263 +
264 +const route = useRoute()
265 +const router = useRouter()
266 +const appStore = useAppStore()
267 +
268 +const activityId = route.params.activityId
269 +const activity = ref(null)
270 +const loading = ref(true)
271 +const error = ref(null)
272 +const userRegistration = ref(null)
273 +const userCheckIn = ref(null)
274 +const checkInStep = ref('initial')
275 +const checkInCode = ref('')
276 +const countdown = ref(0)
277 +const inputCode = ref('')
278 +const isSubmitting = ref(false)
279 +
280 +// Fetch activity details and user registration
281 +const fetchData = async () => {
282 + try {
283 + if (!appStore.currentUser) {
284 + error.value = '请先登录'
285 + loading.value = false
286 + return
287 + }
288 +
289 + if (appStore.activities.length > 0) {
290 + const foundActivity = appStore.getActivityById(activityId)
291 +
292 + if (foundActivity) {
293 + activity.value = foundActivity
294 +
295 + // Find user registration for this activity
296 + const registration = appStore.registrations.find(
297 + reg => reg.activity_id === activityId && reg.user_id === appStore.currentUser.id
298 + )
299 +
300 + if (registration) {
301 + userRegistration.value = registration
302 +
303 + // Check if user already checked in
304 + const checkIn = appStore.checkIns.find(
305 + check => check.activity_id === activityId && check.user_id === appStore.currentUser.id
306 + )
307 +
308 + if (checkIn) {
309 + userCheckIn.value = checkIn
310 + }
311 + } else {
312 + error.value = '您尚未报名该活动'
313 + }
314 + } else {
315 + error.value = '未找到活动信息'
316 + }
317 + }
318 + loading.value = false
319 + } catch (err) {
320 + console.error('Failed to fetch data:', err)
321 + error.value = '加载数据失败'
322 + loading.value = false
323 + }
324 +}
325 +
326 +// Generate a random check-in code
327 +const generateCheckInCode = () => {
328 + checkInCode.value = Math.floor(100000 + Math.random() * 900000).toString()
329 + checkInStep.value = 'code'
330 + countdown.value = 60
331 +}
332 +
333 +// Handle check-in code submission
334 +const handleSubmitCode = async () => {
335 + if (!inputCode.value.trim()) return
336 +
337 + isSubmitting.value = true
338 +
339 + try {
340 + if (inputCode.value === checkInCode.value) {
341 + const now = new Date().toISOString().replace('T', ' ').substring(0, 19)
342 + const checkInResult = await appStore.addCheckIn({
343 + user_id: appStore.currentUser.id,
344 + activity_id: activityId,
345 + check_in_time: now,
346 + status: 'checked_in'
347 + })
348 +
349 + if (checkInResult.success) {
350 + userCheckIn.value = {
351 + user_id: appStore.currentUser.id,
352 + activity_id: activityId,
353 + check_in_time: now,
354 + status: 'checked_in'
355 + }
356 + checkInStep.value = 'success'
357 + } else {
358 + error.value = '签到失败,请稍后重试'
359 + checkInStep.value = 'error'
360 + }
361 + } else {
362 + error.value = '签到码不正确,请重试'
363 + checkInStep.value = 'error'
364 + }
365 + } catch (err) {
366 + console.error('Check-in error:', err)
367 + error.value = '签到失败,请稍后重试'
368 + checkInStep.value = 'error'
369 + } finally {
370 + isSubmitting.value = false
371 + }
372 +}
373 +
374 +// Format date for display
375 +const formatDate = (dateString) => {
376 + if (!dateString) return ''
377 + const date = new Date(dateString.replace(' ', 'T'))
378 + return new Intl.DateTimeFormat('zh-CN', {
379 + year: 'numeric',
380 + month: 'long',
381 + day: 'numeric',
382 + hour: '2-digit',
383 + minute: '2-digit',
384 + hour12: false
385 + }).format(date)
386 +}
387 +
388 +// Check if the activity is happening now
389 +const isActivityNow = () => {
390 + if (!activity.value) return false
391 +
392 + const now = new Date()
393 + const startTime = new Date(activity.value.start_time.replace(' ', 'T'))
394 + const endTime = new Date(activity.value.end_time.replace(' ', 'T'))
395 +
396 + // Allow check-in from 30 minutes before start until end time
397 + const checkInStartTime = new Date(startTime.getTime() - 30 * 60 * 1000)
398 +
399 + return now >= checkInStartTime && now <= endTime
400 +}
401 +
402 +// Navigation
403 +const goBack = () => router.back()
404 +const goToActivityDetail = () => router.push(`/activity/${activityId}`)
405 +
406 +// Watch countdown
407 +watch(countdown, (newValue) => {
408 + if (newValue > 0 && checkInStep.value === 'code') {
409 + setTimeout(() => {
410 + countdown.value--
411 + }, 1000)
412 + } else if (newValue === 0 && checkInStep.value === 'code') {
413 + checkInStep.value = 'initial'
414 + }
415 +})
416 +
417 +// Initial data fetch
418 +onMounted(fetchData)
419 +</script>
1 +<template>
2 + <div class="min-h-screen bg-gray-50 py-8">
3 + <div class="container mx-auto px-4">
4 + <div class="max-w-4xl mx-auto bg-white rounded-lg shadow-sm overflow-hidden">
5 + <!-- Header -->
6 + <div class="bg-gradient-to-r from-green-500 to-blue-500 px-6 py-4">
7 + <h1 class="text-2xl font-bold text-white">创建读书会活动</h1>
8 + </div>
9 +
10 + <!-- Form -->
11 + <form class="p-6" @submit.prevent="handlePreview">
12 + <div class="space-y-8">
13 + <!-- Basic Information Section -->
14 + <div>
15 + <h2 class="text-xl font-medium text-gray-900 border-b pb-2">基本信息</h2>
16 + <div class="mt-4 space-y-6">
17 + <!-- Title -->
18 + <Input id="title" label="活动标题" name="title" v-model="formState.title"
19 + placeholder="例如:《百年孤独》读书分享会" :error="formErrors.title" required />
20 +
21 + <!-- Description -->
22 + <div>
23 + <label for="description" class="block text-sm font-medium text-gray-700 mb-1">
24 + 活动描述 <span class="text-red-500">*</span>
25 + </label>
26 + <textarea id="description" name="description" rows="8"
27 + v-model="formState.description" :class="[
28 + 'block w-full border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm',
29 + formErrors.description ? 'border-red-300' : ''
30 + ]" placeholder="请描述活动内容、流程安排、参与须知等信息..."></textarea>
31 + <p v-if="formErrors.description" class="mt-1 text-sm text-red-600">{{
32 + formErrors.description }}</p>
33 + </div>
34 +
35 + <!-- Cover Image -->
36 + <div>
37 + <label for="cover_image" class="block text-sm font-medium text-gray-700 mb-1">
38 + 封面图片
39 + </label>
40 + <div class="mt-1 flex items-center space-x-4">
41 + <div class="w-48 h-32 bg-gray-100 rounded-md overflow-hidden">
42 + <img :src="formState.cover_image" alt="活动封面预览"
43 + class="w-full h-full object-cover" />
44 + </div>
45 + <Input id="cover_image" name="cover_image" v-model="formState.cover_image"
46 + placeholder="输入图片URL或上传图片" />
47 + </div>
48 + <p class="mt-1 text-sm text-gray-500">
49 + 建议尺寸:1200 x 675 像素,格式:JPG、PNG
50 + </p>
51 + </div>
52 +
53 + <!-- Tags -->
54 + <div>
55 + <label for="tagInput" class="block text-sm font-medium text-gray-700 mb-1">
56 + 活动标签
57 + </label>
58 + <div class="flex flex-wrap items-center gap-2 mb-2">
59 + <div v-for="(tag, index) in formState.tags" :key="index"
60 + class="bg-green-50 text-green-700 rounded-full px-3 py-1 text-sm flex items-center">
61 + <span>{{ tag }}</span>
62 + <button type="button" @click="removeTag(tag)"
63 + class="ml-1 text-green-500 hover:text-green-700 focus:outline-none">
64 + &times;
65 + </button>
66 + </div>
67 + <Input id="tagInput" name="tagInput" v-model="formState.tagInput"
68 + @keydown="handleTagKeyDown" placeholder="输入标签并按回车" class="flex-1" />
69 + </div>
70 + <p class="text-sm text-gray-500">
71 + 添加相关标签,使活动更容易被发现,如:小说、科普、心理学等
72 + </p>
73 + </div>
74 + </div>
75 + </div>
76 +
77 + <!-- Time and Location Section -->
78 + <div>
79 + <h2 class="text-xl font-medium text-gray-900 border-b pb-2">时间与地点</h2>
80 + <div class="mt-4 space-y-6">
81 + <!-- Activity Time -->
82 + <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
83 + <Input id="start_time" type="datetime-local" label="活动开始时间" name="start_time"
84 + v-model="formState.start_time" :error="formErrors.start_time" required />
85 +
86 + <Input id="end_time" type="datetime-local" label="活动结束时间" name="end_time"
87 + v-model="formState.end_time" :error="formErrors.end_time" required />
88 + </div>
89 +
90 + <!-- Registration Time -->
91 + <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
92 + <Input id="registration_start" type="datetime-local" label="报名开始时间"
93 + name="registration_start" v-model="formState.registration_start"
94 + :error="formErrors.registration_start" required />
95 +
96 + <Input id="registration_end" type="datetime-local" label="报名结束时间"
97 + name="registration_end" v-model="formState.registration_end"
98 + :error="formErrors.registration_end" required />
99 + </div>
100 +
101 + <!-- Activity Type -->
102 + <div>
103 + <label class="block text-sm font-medium text-gray-700 mb-1">
104 + 活动形式 <span class="text-red-500">*</span>
105 + </label>
106 + <div class="flex space-x-4">
107 + <label class="inline-flex items-center">
108 + <input type="radio" name="activity_type" value="offline"
109 + v-model="formState.activity_type"
110 + class="h-4 w-4 text-green-600 border-gray-300 focus:ring-green-500" />
111 + <span class="ml-2">线下活动</span>
112 + </label>
113 + <label class="inline-flex items-center">
114 + <input type="radio" name="activity_type" value="online"
115 + v-model="formState.activity_type"
116 + class="h-4 w-4 text-green-600 border-gray-300 focus:ring-green-500" />
117 + <span class="ml-2">线上活动</span>
118 + </label>
119 + </div>
120 + </div>
121 +
122 + <!-- Location or Online Link -->
123 + <Input v-if="formState.activity_type === 'offline'" id="location" label="活动地点"
124 + name="location" v-model="formState.location" placeholder="例如:北京市海淀区中关村图书馆三层读书区"
125 + :error="formErrors.location" required />
126 + <Input v-else id="online_link" label="线上会议信息" name="online_link"
127 + v-model="formState.online_link" placeholder="例如:Zoom会议ID或飞书会议链接"
128 + :error="formErrors.online_link" required />
129 + </div>
130 + </div>
131 +
132 + <!-- Settings Section -->
133 + <div>
134 + <h2 class="text-xl font-medium text-gray-900 border-b pb-2">活动设置</h2>
135 + <div class="mt-4 space-y-6">
136 + <!-- Maximum Participants -->
137 + <div class="max-w-xs">
138 + <Input id="max_participants" type="number" label="最大参与人数" name="max_participants"
139 + v-model="formState.max_participants" min="1" max="500"
140 + :error="formErrors.max_participants" />
141 + </div>
142 +
143 + <!-- Visibility -->
144 + <div>
145 + <label class="block text-sm font-medium text-gray-700 mb-1">
146 + 活动可见性
147 + </label>
148 + <label class="inline-flex items-center">
149 + <input type="checkbox" name="is_public" v-model="formState.is_public"
150 + class="h-4 w-4 text-green-600 border-gray-300 rounded focus:ring-green-500" />
151 + <span class="ml-2">公开活动(所有人可见)</span>
152 + </label>
153 + <p class="mt-1 text-sm text-gray-500">
154 + {{ formState.is_public ? '活动将在首页和搜索结果中显示' : '活动仅对邀请的用户可见' }}
155 + </p>
156 + </div>
157 + </div>
158 + </div>
159 +
160 + <!-- Submit Buttons -->
161 + <div class="flex justify-end space-x-4 pt-6">
162 + <Button type="button" variant="secondary" @click="goBack">
163 + 取消
164 + </Button>
165 + <Button type="submit" variant="primary" :disabled="isLoading">
166 + 预览活动
167 + </Button>
168 + </div>
169 + </div>
170 + </form>
171 + </div>
172 + </div>
173 +
174 + <!-- Preview Modal -->
175 + <Modal v-model:isOpen="showPreviewModal" title="活动预览" size="lg">
176 + <template #default>
177 + <div class="py-4">
178 + <div class="space-y-6">
179 + <!-- Preview Cover -->
180 + <div class="relative h-48 rounded-lg overflow-hidden bg-gray-200">
181 + <img :src="formState.cover_image" :alt="formState.title"
182 + class="w-full h-full object-cover" />
183 + </div>
184 +
185 + <!-- Preview Title -->
186 + <h2 class="text-2xl font-bold text-gray-900">{{ formState.title }}</h2>
187 +
188 + <!-- Preview Tags -->
189 + <div v-if="formState.tags.length > 0" class="flex flex-wrap gap-2">
190 + <span v-for="(tag, index) in formState.tags" :key="index"
191 + class="bg-green-50 text-green-700 rounded-full px-3 py-1 text-sm">
192 + {{ tag }}
193 + </span>
194 + </div>
195 +
196 + <!-- Preview Description -->
197 + <div class="prose max-w-none text-gray-700">
198 + <p v-for="(paragraph, idx) in formState.description.split('\n')" :key="idx" class="mb-2">
199 + {{ paragraph }}
200 + </p>
201 + </div>
202 + </div>
203 + </div>
204 + </template>
205 + <template #footer>
206 + <Button @click="showPreviewModal = false" variant="secondary" class="mr-3">
207 + 返回编辑
208 + </Button>
209 + <Button @click="handleSubmit" variant="primary" :disabled="isLoading">
210 + {{ isLoading ? '创建中...' : '创建活动' }}
211 + </Button>
212 + </template>
213 + </Modal>
214 + </div>
215 +</template>
216 +
217 +<script setup>
218 +import { ref, reactive, onMounted } from 'vue'
219 +import { useRouter } from 'vue-router'
220 +import { useAppStore } from '../stores/app'
221 +import Button from '../components/shared/Button.vue'
222 +import Input from '../components/shared/Input.vue'
223 +import Modal from '../components/shared/Modal.vue'
224 +
225 +const router = useRouter()
226 +const appStore = useAppStore()
227 +const { currentUser } = appStore
228 +
229 +const isLoading = ref(false)
230 +const showPreviewModal = ref(false)
231 +const formErrors = reactive({})
232 +
233 +// Form state
234 +const formState = reactive({
235 + title: '',
236 + description: '',
237 + cover_image: '/assets/images/LEVRaos2Ero.jpg',
238 + start_time: '',
239 + end_time: '',
240 + registration_start: '',
241 + registration_end: '',
242 + activity_type: 'offline', // 'online' or 'offline'
243 + location: '',
244 + online_link: '',
245 + max_participants: 20,
246 + is_public: true,
247 + tags: [],
248 + tagInput: '' // For managing tag input
249 +})
250 +
251 +// Check if user is logged in
252 +onMounted(() => {
253 + if (!currentUser) {
254 + // In a real app, redirect to login
255 + alert('请先登录再创建活动')
256 + router.push('/')
257 + }
258 +})
259 +
260 +// Handle tag input
261 +const handleTagKeyDown = (e) => {
262 + if (e.key === 'Enter') {
263 + e.preventDefault()
264 + addTag()
265 + }
266 +}
267 +
268 +// Add tag to the list
269 +const addTag = () => {
270 + const tag = formState.tagInput.trim()
271 + if (tag && !formState.tags.includes(tag)) {
272 + formState.tags.push(tag)
273 + formState.tagInput = ''
274 + }
275 +}
276 +
277 +// Remove tag from the list
278 +const removeTag = (tagToRemove) => {
279 + formState.tags = formState.tags.filter(tag => tag !== tagToRemove)
280 +}
281 +
282 +// Validate form before submission
283 +const validateForm = () => {
284 + const errors = {}
285 +
286 + if (!formState.title.trim()) {
287 + errors.title = '请输入活动标题'
288 + }
289 +
290 + if (!formState.description.trim()) {
291 + errors.description = '请输入活动描述'
292 + }
293 +
294 + if (!formState.start_time) {
295 + errors.start_time = '请选择活动开始时间'
296 + }
297 +
298 + if (!formState.end_time) {
299 + errors.end_time = '请选择活动结束时间'
300 + } else if (new Date(formState.end_time) <= new Date(formState.start_time)) {
301 + errors.end_time = '结束时间必须晚于开始时间'
302 + }
303 +
304 + if (!formState.registration_start) {
305 + errors.registration_start = '请选择报名开始时间'
306 + }
307 +
308 + if (!formState.registration_end) {
309 + errors.registration_end = '请选择报名结束时间'
310 + } else if (new Date(formState.registration_end) <= new Date(formState.registration_start)) {
311 + errors.registration_end = '报名结束时间必须晚于开始时间'
312 + }
313 +
314 + if (formState.activity_type === 'offline' && !formState.location.trim()) {
315 + errors.location = '请输入活动地点'
316 + }
317 +
318 + if (formState.activity_type === 'online' && !formState.online_link.trim()) {
319 + errors.online_link = '请输入线上会议链接'
320 + }
321 +
322 + if (formState.max_participants <= 0) {
323 + errors.max_participants = '参与人数必须大于0'
324 + }
325 +
326 + Object.assign(formErrors, errors)
327 + return Object.keys(errors).length === 0
328 +}
329 +
330 +// Preview activity before submission
331 +const handlePreview = (e) => {
332 + e.preventDefault()
333 + if (validateForm()) {
334 + showPreviewModal.value = true
335 + } else {
336 + // Scroll to the first error
337 + const firstErrorField = Object.keys(formErrors)[0]
338 + const element = document.getElementById(firstErrorField)
339 + if (element) {
340 + element.scrollIntoView({ behavior: 'smooth', block: 'center' })
341 + }
342 + }
343 +}
344 +
345 +// Submit form to create activity
346 +const handleSubmit = async () => {
347 + if (validateForm()) {
348 + isLoading.value = true
349 + try {
350 + // Create form data for submission
351 + const activityData = { ...formState }
352 + delete activityData.tagInput
353 + activityData.participant_count = 0
354 + activityData.is_published = true
355 +
356 + const result = await appStore.createActivity(activityData)
357 +
358 + if (result.success) {
359 + alert('活动创建成功!')
360 + router.push(`/activity/${result.activityId}`)
361 + } else {
362 + alert(`创建失败: ${result.error || '未知错误'}`)
363 + isLoading.value = false
364 + }
365 + } catch (error) {
366 + console.error('Failed to create activity:', error)
367 + alert('创建活动失败,请稍后再试')
368 + isLoading.value = false
369 + }
370 + }
371 +}
372 +
373 +// Format datetime for preview
374 +const formatDatetime = (datetimeStr) => {
375 + if (!datetimeStr) return ''
376 + const date = new Date(datetimeStr)
377 + return new Intl.DateTimeFormat('zh-CN', {
378 + year: 'numeric',
379 + month: 'long',
380 + day: 'numeric',
381 + hour: '2-digit',
382 + minute: '2-digit',
383 + hour12: false
384 + }).format(date)
385 +}
386 +
387 +// Navigation
388 +const goBack = () => {
389 + router.back()
390 +}
391 +</script>
1 +<template>
2 + <div class="min-h-screen">
3 + <!-- 加载状态 -->
4 + <div v-if="loading" class="min-h-screen flex justify-center items-center">
5 + <div class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-green-500"></div>
6 + </div>
7 +
8 + <template v-else>
9 + <!-- Hero Section -->
10 + <section class="relative bg-gradient-to-br from-green-50 to-blue-50 py-16">
11 + <div class="container mx-auto px-4">
12 + <div class="max-w-3xl mx-auto text-center">
13 + <h1 class="text-4xl md:text-5xl font-bold mb-6 text-gray-800">
14 + 连接爱读书的人,<span
15 + class="bg-gradient-to-r from-green-500 to-blue-400 bg-clip-text text-transparent">共享知识的力量</span>
16 + </h1>
17 + <p class="text-xl text-gray-600 mb-8">
18 + 发现丰富的读书活动,结识志同道合的朋友,让阅读成为一种享受
19 + </p>
20 +
21 + <!-- 搜索表单 -->
22 + <form @submit.prevent="handleSearch" class="relative max-w-xl mx-auto mb-8">
23 + <div class="flex rounded-full overflow-hidden shadow-lg">
24 + <input type="text" v-model="searchTerm" class="flex-grow px-6 py-4 focus:outline-none"
25 + placeholder="搜索读书会活动、主题或城市..." />
26 + <button type="submit"
27 + class="px-8 py-4 bg-green-500 text-white font-semibold hover:bg-green-600 transition duration-200">
28 + 搜索
29 + </button>
30 + </div>
31 + </form>
32 + </div>
33 + </div>
34 + </section>
35 +
36 + <!-- 搜索结果 -->
37 + <section v-if="isSearching" class="container mx-auto px-4 py-12">
38 + <div class="flex justify-between items-center mb-8">
39 + <h2 class="text-2xl font-bold text-gray-800">搜索结果</h2>
40 + <button @click="clearSearch" class="text-gray-600 hover:text-gray-800 transition duration-200">
41 + 清除搜索
42 + </button>
43 + </div>
44 + <div v-if="searchResults.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
45 + <ActivityCard v-for="activity in searchResults" :key="activity.id" :activity="activity" />
46 + </div>
47 + <div v-else class="text-center py-12 text-gray-600">
48 + 未找到相关活动
49 + </div>
50 + </section>
51 +
52 + <!-- 活动列表 -->
53 + <template v-else>
54 + <!-- 进行中的活动 -->
55 + <section v-if="ongoingActivities.length > 0" class="container mx-auto px-4 py-12">
56 + <h2 class="text-2xl font-bold text-gray-800 mb-8">正在进行的活动</h2>
57 + <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
58 + <ActivityCard v-for="activity in ongoingActivities" :key="activity.id" :activity="activity" />
59 + </div>
60 + </section>
61 +
62 + <!-- 即将开始的活动 -->
63 + <section class="container mx-auto px-4 py-12">
64 + <div class="flex justify-between items-center mb-8">
65 + <h2 class="text-2xl font-bold text-gray-800">即将开始的活动</h2>
66 + <router-link to="/create-activity"
67 + class="px-6 py-2 bg-green-500 text-white rounded-full font-semibold hover:bg-green-600 transition duration-200">
68 + 创建活动
69 + </router-link>
70 + </div>
71 + <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
72 + <ActivityCard v-for="activity in upcomingActivities" :key="activity.id" :activity="activity" />
73 + </div>
74 + </section>
75 +
76 + <!-- 往期活动 -->
77 + <section class="container mx-auto px-4 py-12">
78 + <h2 class="text-2xl font-bold text-gray-800 mb-8">往期活动</h2>
79 + <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
80 + <ActivityCard v-for="activity in pastActivities" :key="activity.id" :activity="activity" />
81 + </div>
82 + </section>
83 + </template>
84 + </template>
85 + </div>
86 +</template>
87 +
88 +<script setup>
89 +import { ref, computed, watchEffect } from 'vue'
90 +import { useAppStore } from '../stores/app'
91 +import ActivityCard from '../components/shared/ActivityCard.vue'
92 +
93 +const store = useAppStore()
94 +const { activities, loading } = store
95 +
96 +const upcomingActivities = ref([])
97 +const ongoingActivities = ref([])
98 +const pastActivities = ref([])
99 +const searchTerm = ref('')
100 +const searchResults = ref([])
101 +const isSearching = ref(false)
102 +
103 +// 监听活动数据变化并分类
104 +watchEffect(() => {
105 + if (activities.value.length > 0) {
106 + const now = new Date()
107 +
108 + // 筛选即将开始的活动
109 + upcomingActivities.value = activities.value
110 + .filter(activity => {
111 + const startTime = new Date(activity.start_time.replace(' ', 'T'))
112 + return startTime > now
113 + })
114 + .sort((a, b) => {
115 + return new Date(a.start_time.replace(' ', 'T')) - new Date(b.start_time.replace(' ', 'T'))
116 + })
117 + .slice(0, 6)
118 +
119 + // 筛选进行中的活动
120 + ongoingActivities.value = activities.value
121 + .filter(activity => {
122 + const startTime = new Date(activity.start_time.replace(' ', 'T'))
123 + const endTime = new Date(activity.end_time.replace(' ', 'T'))
124 + return startTime <= now && endTime >= now
125 + })
126 +
127 + // 筛选往期活动
128 + pastActivities.value = activities.value
129 + .filter(activity => {
130 + const endTime = new Date(activity.end_time.replace(' ', 'T'))
131 + return endTime < now
132 + })
133 + .sort((a, b) => {
134 + return new Date(b.end_time.replace(' ', 'T')) - new Date(a.end_time.replace(' ', 'T'))
135 + })
136 + .slice(0, 6)
137 + }
138 +})
139 +
140 +// 搜索处理
141 +const handleSearch = () => {
142 + if (!searchTerm.value.trim()) return
143 +
144 + isSearching.value = true
145 + searchResults.value = activities.value.filter(activity =>
146 + activity.title.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
147 + activity.description.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
148 + (activity.tags && activity.tags.some(tag => tag.toLowerCase().includes(searchTerm.value.toLowerCase())))
149 + )
150 +}
151 +
152 +// 清除搜索
153 +const clearSearch = () => {
154 + searchTerm.value = ''
155 + isSearching.value = false
156 +}
157 +</script>
1 +<template>
2 + <div class="min-h-screen bg-gray-50">
3 + <!-- Header -->
4 + <div class="bg-white shadow-sm sticky top-0 z-10">
5 + <div class="container mx-auto px-4 py-3">
6 + <div class="flex items-center justify-between">
7 + <div class="flex items-center">
8 + <button @click="goBack" class="mr-3 text-gray-600 hover:text-gray-900">
9 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
10 + fill="currentColor">
11 + <path fill-rule="evenodd"
12 + d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
13 + clip-rule="evenodd" />
14 + </svg>
15 + </button>
16 + <div>
17 + <h1 class="text-lg font-medium text-gray-900 truncate max-w-xs">{{ activity?.title }}</h1>
18 + <p class="text-sm text-gray-500">消息交流</p>
19 + </div>
20 + </div>
21 + <router-link :to="`/activity/${activityId}`"
22 + class="text-sm text-green-600 hover:text-green-700 font-medium">
23 + 活动详情
24 + </router-link>
25 + </div>
26 + </div>
27 + </div>
28 +
29 + <!-- Message Tabs -->
30 + <div class="bg-white shadow-sm">
31 + <div class="container mx-auto px-4">
32 + <div class="flex overflow-x-auto hide-scrollbar">
33 + <button v-for="tab in tabs" :key="tab.value" :class="[
34 + 'py-3 px-4 text-sm font-medium border-b-2 whitespace-nowrap',
35 + activeTab === tab.value
36 + ? 'border-green-500 text-green-600'
37 + : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
38 + ]" @click="activeTab = tab.value">
39 + {{ tab.label }}
40 + </button>
41 + </div>
42 + </div>
43 + </div>
44 +
45 + <!-- Messages Container -->
46 + <div class="container mx-auto px-4 py-4 flex flex-col h-[calc(100vh-13rem)]">
47 + <!-- Message List -->
48 + <div class="flex-grow overflow-y-auto mb-4 pr-1">
49 + <div v-if="filteredMessages.length === 0" class="flex flex-col items-center justify-center h-full">
50 + <div class="text-gray-400">
51 + <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16" fill="none" viewBox="0 0 24 24"
52 + stroke="currentColor">
53 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1"
54 + d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
55 + </svg>
56 + </div>
57 + <p class="text-gray-500 mt-2">暂无消息</p>
58 + <button v-if="activeTab !== 'all'" @click="activeTab = 'all'"
59 + class="mt-4 text-green-600 hover:text-green-700 font-medium text-sm">
60 + 查看全部消息
61 + </button>
62 + </div>
63 + <div v-else>
64 + <div v-for="date in sortedDates" :key="date">
65 + <div class="flex items-center justify-center my-4">
66 + <div class="border-t border-gray-200 flex-grow"></div>
67 + <span class="mx-4 text-xs text-gray-500">{{ formatDate(date) }}</span>
68 + <div class="border-t border-gray-200 flex-grow"></div>
69 + </div>
70 + <div v-for="message in groupedMessages[date]" :key="message.id">
71 + <component :is="messageComponent(message)" :message="message" />
72 + </div>
73 + </div>
74 + <div ref="messageEndRef" />
75 + </div>
76 + </div>
77 +
78 + <!-- Message Input -->
79 + <div class="bg-white border border-gray-200 rounded-lg overflow-hidden">
80 + <div v-if="isOrganizer" class="p-2 bg-gray-50 border-b border-gray-200">
81 + <div class="flex items-center">
82 + <span
83 + class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">
84 + 组织者
85 + </span>
86 + <span class="text-xs text-gray-500 ml-2">
87 + 您可以发送公告消息
88 + </span>
89 + </div>
90 + </div>
91 + <form @submit.prevent="handleSendMessage" class="flex p-2">
92 + <input type="text" v-model="messageInput" placeholder="输入消息..."
93 + class="flex-grow border-none focus:ring-0 focus:outline-none" :disabled="isSubmitting" />
94 + <Button type="submit" variant="primary" size="sm" :disabled="!messageInput.trim() || isSubmitting">
95 + 发送
96 + </Button>
97 + </form>
98 + </div>
99 + </div>
100 + </div>
101 +</template>
102 +
103 +<script setup>
104 +import { ref, computed, onMounted, watch } from 'vue'
105 +import { useRoute, useRouter } from 'vue-router'
106 +import { useAppStore } from '../stores/app'
107 +import Button from '../components/shared/Button.vue'
108 +
109 +// Route and store setup
110 +const route = useRoute()
111 +const router = useRouter()
112 +const store = useAppStore()
113 +
114 +// Props and refs
115 +const activityId = route.params.activityId
116 +const messageEndRef = ref(null)
117 +const messageInput = ref('')
118 +const activeTab = ref('all')
119 +const isSubmitting = ref(false)
120 +const activity = ref(null)
121 +const filteredMessages = ref([])
122 +const loading = ref(true)
123 +const error = ref(null)
124 +
125 +// Computed properties
126 +const isOrganizer = computed(() => {
127 + return store.currentUser && store.currentUser.id === activity.value?.organizer_id
128 +})
129 +
130 +// Tabs configuration
131 +const tabs = [
132 + { label: '全部消息', value: 'all' },
133 + { label: '活动公告', value: 'announcements' },
134 + { label: '问答交流', value: 'questions' },
135 + { label: '我的消息', value: 'personal' }
136 +]
137 +
138 +// Methods
139 +const goBack = () => router.back()
140 +
141 +const formatTime = (dateString) => {
142 + const date = new Date(dateString.replace(' ', 'T'))
143 + return new Intl.DateTimeFormat('zh-CN', {
144 + month: 'short',
145 + day: 'numeric',
146 + hour: '2-digit',
147 + minute: '2-digit',
148 + hour12: false
149 + }).format(date)
150 +}
151 +
152 +const formatDate = (dateString) => {
153 + return new Date(dateString).toLocaleDateString('zh-CN')
154 +}
155 +
156 +const handleSendMessage = async () => {
157 + if (!messageInput.value.trim()) return
158 +
159 + if (!store.currentUser) {
160 + alert('请先登录再发送消息')
161 + return
162 + }
163 +
164 + isSubmitting.value = true
165 +
166 + try {
167 + const now = new Date().toISOString().replace('T', ' ').substring(0, 19)
168 +
169 + const newMessage = {
170 + activity_id: activityId,
171 + user_id: store.currentUser.id,
172 + type: 'question',
173 + content: messageInput.value,
174 + timestamp: now,
175 + user_name: store.currentUser.name,
176 + user_avatar: store.currentUser.avatar
177 + }
178 +
179 + const result = await store.addMessage(newMessage)
180 +
181 + if (result.success) {
182 + messageInput.value = ''
183 + } else {
184 + alert(`发送失败: ${result.error}`)
185 + }
186 + } catch (error) {
187 + console.error('Failed to send message:', error)
188 + alert('发送消息失败,请稍后重试')
189 + } finally {
190 + isSubmitting.value = false
191 + }
192 +}
193 +
194 +// Message components
195 +const AnnouncementMessage = {
196 + props: ['message'],
197 + template: `
198 + <div class="bg-blue-50 border border-blue-100 rounded-lg p-4 mb-4">
199 + <div class="flex items-center mb-2">
200 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-blue-500 mr-2" viewBox="0 0 20 20" fill="currentColor">
201 + <path fill-rule="evenodd" d="M18 3a1 1 0 00-1.447-.894L8.763 6H5a3 3 0 000 6h.28l1.771 5.316A1 1 0 008 18h1a1 1 0 001-1v-4.382l6.553 3.276A1 1 0 0018 15V3z" clip-rule="evenodd" />
202 + </svg>
203 + <span class="font-semibold text-blue-800">活动公告</span>
204 + </div>
205 + <p class="text-gray-700">{{ message.content }}</p>
206 + <div class="mt-2 text-xs text-gray-500 flex items-center justify-between">
207 + <span>{{ message.user_name }}</span>
208 + <span>{{ formatTime(message.timestamp) }}</span>
209 + </div>
210 + </div>
211 + `
212 +}
213 +
214 +const AnswerMessage = {
215 + props: ['message'],
216 + template: `
217 + <div class="bg-green-50 border border-green-100 rounded-lg p-4 mb-4">
218 + <div class="flex items-center mb-2">
219 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500 mr-2" viewBox="0 0 20 20" fill="currentColor">
220 + <path fill-rule="evenodd" d="M18 13V5a2 2 0 00-2-2H4a2 2 0 00-2 2v8a2 2 0 002 2h3l3 3 3-3h3a2 2 0 002-2zM5 7a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1zm1 3a1 1 0 100 2h3a1 1 0 100-2H6z" clip-rule="evenodd" />
221 + </svg>
222 + <span class="font-semibold text-green-800">回复</span>
223 + </div>
224 + <div v-if="message.reply_to" class="bg-white rounded p-2 mb-2 text-sm text-gray-600 border-l-2 border-gray-300">
225 + <p class="truncate">{{ message.reply_to }}</p>
226 + </div>
227 + <p class="text-gray-700">{{ message.content }}</p>
228 + <div class="mt-2 text-xs text-gray-500 flex items-center justify-between">
229 + <span>{{ message.user_name }}</span>
230 + <span>{{ formatTime(message.timestamp) }}</span>
231 + </div>
232 + </div>
233 + `
234 +}
235 +
236 +const DefaultMessage = {
237 + props: ['message'],
238 + setup(props) {
239 + const store = useAppStore()
240 + const isCurrentUser = computed(() => props.message.user_id === store.currentUser?.id)
241 + return { isCurrentUser, formatTime }
242 + },
243 + template: `
244 + <div :class="['flex', isCurrentUser ? 'justify-end' : 'justify-start', 'mb-4']">
245 + <div :class="['max-w-[80%]', isCurrentUser ? 'order-1' : 'order-2']">
246 + <div :class="['flex items-center', isCurrentUser ? 'justify-end' : 'justify-start', 'mb-1']">
247 + <span class="text-xs text-gray-500 mr-2">{{ message.user_name }}</span>
248 + <div class="h-6 w-6 rounded-full overflow-hidden bg-gray-200">
249 + <img v-if="message.user_avatar" :src="message.user_avatar" :alt="message.user_name" class="h-full w-full object-cover" />
250 + <svg v-else xmlns="http://www.w3.org/2000/svg" class="h-full w-full text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
251 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
252 + </svg>
253 + </div>
254 + </div>
255 + <div :class="['rounded-lg p-3', isCurrentUser ? 'bg-green-100' : 'bg-white border border-gray-200']">
256 + <p class="text-gray-700">{{ message.content }}</p>
257 + </div>
258 + <div :class="['text-xs text-gray-500 mt-1', isCurrentUser ? 'text-right' : 'text-left']">
259 + {{ formatTime(message.timestamp) }}
260 + </div>
261 + </div>
262 + </div>
263 + `
264 +}
265 +
266 +const messageComponent = (message) => {
267 + switch (message.type) {
268 + case 'announcement':
269 + return AnnouncementMessage
270 + case 'answer':
271 + return AnswerMessage
272 + default:
273 + return DefaultMessage
274 + }
275 +}
276 +
277 +// Computed properties for message grouping
278 +const groupedMessages = computed(() => {
279 + return filteredMessages.value.reduce((groups, message) => {
280 + const date = message.timestamp.split(' ')[0]
281 + if (!groups[date]) {
282 + groups[date] = []
283 + }
284 + groups[date].push(message)
285 + return groups
286 + }, {})
287 +})
288 +
289 +const sortedDates = computed(() => {
290 + return Object.keys(groupedMessages.value).sort()
291 +})
292 +
293 +// Watch for changes in messages and scroll to bottom
294 +watch(filteredMessages, () => {
295 + if (messageEndRef.value) {
296 + messageEndRef.value.scrollIntoView({ behavior: 'smooth' })
297 + }
298 +})
299 +
300 +// Filter messages based on active tab
301 +watch([() => store.messages, activeTab], () => {
302 + if (!store.messages) return
303 +
304 + let filtered = store.messages.filter(msg => msg.activity_id === activityId)
305 +
306 + switch (activeTab.value) {
307 + case 'announcements':
308 + filtered = filtered.filter(msg => msg.type === 'announcement')
309 + break
310 + case 'questions':
311 + filtered = filtered.filter(msg => msg.type === 'question' || msg.type === 'answer')
312 + break
313 + case 'personal':
314 + filtered = filtered.filter(msg =>
315 + msg.user_id === store.currentUser?.id || msg.to_user_id === store.currentUser?.id
316 + )
317 + break
318 + }
319 +
320 + filteredMessages.value = filtered.sort((a, b) => {
321 + return new Date(a.timestamp.replace(' ', 'T')) - new Date(b.timestamp.replace(' ', 'T'))
322 + })
323 +})
324 +
325 +// Initial data fetch
326 +onMounted(async () => {
327 + try {
328 + if (!store.currentUser) {
329 + error.value = '请先登录'
330 + loading.value = false
331 + return
332 + }
333 +
334 + if (store.activities.length > 0) {
335 + const foundActivity = store.getActivityById(activityId)
336 +
337 + if (foundActivity) {
338 + activity.value = foundActivity
339 +
340 + // Filter messages for this activity
341 + const activityMessages = store.messages.filter(
342 + msg => msg.activity_id === activityId
343 + ).sort((a, b) => {
344 + return new Date(a.timestamp.replace(' ', 'T')) - new Date(b.timestamp.replace(' ', 'T'))
345 + })
346 +
347 + filteredMessages.value = activityMessages
348 + } else {
349 + error.value = '未找到活动信息'
350 + }
351 + }
352 + loading.value = false
353 + } catch (err) {
354 + console.error('Failed to fetch data:', err)
355 + error.value = '加载数据失败'
356 + loading.value = false
357 + }
358 +})
359 +</script>
360 +
361 +<style scoped>
362 +.hide-scrollbar {
363 + -ms-overflow-style: none;
364 + scrollbar-width: none;
365 +}
366 +
367 +.hide-scrollbar::-webkit-scrollbar {
368 + display: none;
369 +}
370 +</style>
1 +<template>
2 + <div class="min-h-screen bg-gray-50">
3 + <!-- Loading State -->
4 + <div v-if="loading" class="min-h-screen flex justify-center items-center bg-gray-50">
5 + <div class="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-green-500"></div>
6 + </div>
7 +
8 + <!-- Error State -->
9 + <div v-else-if="error || !activity"
10 + class="min-h-screen flex flex-col justify-center items-center bg-gray-50 px-4">
11 + <div class="text-red-500 text-6xl mb-4">
12 + <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24"
13 + stroke="currentColor">
14 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
15 + d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
16 + </svg>
17 + </div>
18 + <h1 class="text-2xl font-bold mb-2">出错了</h1>
19 + <p class="text-gray-600 mb-6">{{ error || '无法加载活动信息' }}</p>
20 + <Button @click="$router.push('/')" variant="primary">返回首页</Button>
21 + </div>
22 +
23 + <!-- Registration Closed State -->
24 + <div v-else-if="!isRegistrationOpen"
25 + class="min-h-screen flex flex-col justify-center items-center bg-gray-50 px-4">
26 + <div class="text-yellow-500 text-6xl mb-4">
27 + <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24"
28 + stroke="currentColor">
29 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
30 + d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
31 + </svg>
32 + </div>
33 + <h1 class="text-2xl font-bold mb-2">报名已截止</h1>
34 + <p class="text-gray-600 mb-6">该活动的报名时间为 {{ formatDate(activity.registration_start) }} 至 {{
35 + formatDate(activity.registration_end) }}</p>
36 + <Button @click="$router.push(`/activity/${activityId}`)" variant="primary">查看活动详情</Button>
37 + </div>
38 +
39 + <!-- Full State -->
40 + <div v-else-if="isFull" class="min-h-screen flex flex-col justify-center items-center bg-gray-50 px-4">
41 + <div class="text-blue-500 text-6xl mb-4">
42 + <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24"
43 + stroke="currentColor">
44 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
45 + d="M13 10V3L4 14h7v7l9-11h-7z" />
46 + </svg>
47 + </div>
48 + <h1 class="text-2xl font-bold mb-2">名额已满</h1>
49 + <p class="text-gray-600 mb-6">该活动名额已满,请关注其他精彩活动</p>
50 + <div class="flex space-x-4">
51 + <Button @click="$router.push(`/activity/${activityId}`)" variant="secondary">查看活动详情</Button>
52 + <Button @click="$router.push('/')" variant="primary">浏览更多活动</Button>
53 + </div>
54 + </div>
55 +
56 + <!-- Registration Form -->
57 + <div v-else class="py-8">
58 + <div class="container mx-auto px-4">
59 + <div class="max-w-3xl mx-auto bg-white rounded-lg shadow-sm overflow-hidden">
60 + <!-- Header -->
61 + <div class="bg-gradient-to-r from-green-500 to-blue-500 px-6 py-4">
62 + <h1 class="text-2xl font-bold text-white">活动报名</h1>
63 + </div>
64 +
65 + <!-- Activity Info -->
66 + <div class="border-b border-gray-200 bg-gray-50 px-6 py-4">
67 + <h2 class="text-xl font-semibold text-gray-800 mb-2">{{ activity.title }}</h2>
68 + <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-600">
69 + <div class="flex items-center">
70 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mr-2" fill="none"
71 + viewBox="0 0 24 24" stroke="currentColor">
72 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
73 + d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
74 + </svg>
75 + <span>活动时间:{{ formatDate(activity.start_time) }}</span>
76 + </div>
77 + <div class="flex items-center">
78 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mr-2" fill="none"
79 + viewBox="0 0 24 24" stroke="currentColor">
80 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
81 + d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
82 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
83 + d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
84 + </svg>
85 + <span>{{ activity.activity_type === 'online' ? '线上活动' : `活动地点:${activity.location}`
86 + }}</span>
87 + </div>
88 + <div class="flex items-center">
89 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mr-2" fill="none"
90 + viewBox="0 0 24 24" stroke="currentColor">
91 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
92 + d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
93 + </svg>
94 + <span>已报名:{{ activity.participant_count }}/{{ activity.max_participants }}</span>
95 + </div>
96 + <div class="flex items-center">
97 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 mr-2" fill="none"
98 + viewBox="0 0 24 24" stroke="currentColor">
99 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
100 + d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
101 + </svg>
102 + <span>报名截止:{{ formatDate(activity.registration_end) }}</span>
103 + </div>
104 + </div>
105 + </div>
106 +
107 + <!-- Registration Form -->
108 + <form class="p-6" @submit.prevent="handleSubmit">
109 + <h3 class="text-lg font-medium text-gray-900 mb-4">填写报名信息</h3>
110 +
111 + <div class="space-y-6">
112 + <Input id="name" label="姓名" v-model="formState.name" :error="formErrors.name"
113 + placeholder="请输入您的姓名" required />
114 +
115 + <Input id="phone" label="手机号码" v-model="formState.phone" :error="formErrors.phone"
116 + placeholder="请输入您的手机号码" required />
117 +
118 + <Input id="email" label="电子邮箱" type="email" v-model="formState.email"
119 + :error="formErrors.email" placeholder="请输入您的电子邮箱" required />
120 +
121 + <div>
122 + <label for="reason" class="block text-sm font-medium text-gray-700 mb-1">
123 + 参加原因 <span class="text-red-500">*</span>
124 + </label>
125 + <textarea id="reason" v-model="formState.reason" rows="4" :class="[
126 + 'block w-full border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm',
127 + formErrors.reason ? 'border-red-300' : ''
128 + ]" placeholder="请简要说明您参加本次活动的原因或期望..."></textarea>
129 + <p v-if="formErrors.reason" class="mt-1 text-sm text-red-600">{{ formErrors.reason }}
130 + </p>
131 + </div>
132 +
133 + <div class="flex items-start">
134 + <div class="flex items-center h-5">
135 + <input id="agreeTerms" type="checkbox" v-model="formState.agreeTerms"
136 + class="focus:ring-green-500 h-4 w-4 text-green-600 border-gray-300 rounded" />
137 + </div>
138 + <div class="ml-3 text-sm">
139 + <label for="agreeTerms"
140 + :class="['font-medium', formErrors.agreeTerms ? 'text-red-600' : 'text-gray-700']">
141 + 我同意活动条款和隐私政策
142 + </label>
143 + <p class="text-gray-500">
144 + 注册即表示您同意我们的
145 + <a href="#" class="text-green-600 hover:text-green-500">服务条款</a>和
146 + <a href="#" class="text-green-600 hover:text-green-500">隐私政策</a>。
147 + </p>
148 + <p v-if="formErrors.agreeTerms" class="mt-1 text-sm text-red-600">{{
149 + formErrors.agreeTerms }}</p>
150 + </div>
151 + </div>
152 + </div>
153 +
154 + <div class="mt-8 flex justify-between">
155 + <Button type="button" variant="secondary" @click="$router.push(`/activity/${activityId}`)">
156 + 返回活动详情
157 + </Button>
158 + <Button type="submit" variant="primary" :disabled="isSubmitting">
159 + {{ isSubmitting ? '提交中...' : '确认报名' }}
160 + </Button>
161 + </div>
162 + </form>
163 + </div>
164 + </div>
165 + </div>
166 + </div>
167 +</template>
168 +
169 +<script setup>
170 +import { ref, computed, onMounted } from 'vue'
171 +import { useRoute, useRouter } from 'vue-router'
172 +import { useAppStore } from '../stores/app'
173 +import Button from '../components/shared/Button.vue'
174 +import Input from '../components/shared/Input.vue'
175 +
176 +const route = useRoute()
177 +const router = useRouter()
178 +const store = useAppStore()
179 +
180 +const activityId = route.params.activityId
181 +const activity = ref(null)
182 +const loading = ref(true)
183 +const error = ref(null)
184 +const isSubmitting = ref(false)
185 +
186 +const formState = ref({
187 + name: '',
188 + phone: '',
189 + email: '',
190 + reason: '',
191 + agreeTerms: false
192 +})
193 +
194 +const formErrors = ref({})
195 +
196 +// Fetch activity details
197 +onMounted(async () => {
198 + try {
199 + if (store.activities.length > 0) {
200 + const foundActivity = store.getActivityById(activityId)
201 +
202 + if (foundActivity) {
203 + activity.value = foundActivity
204 +
205 + // Pre-fill form with user data if available
206 + if (store.currentUser) {
207 + formState.value = {
208 + ...formState.value,
209 + name: store.currentUser.name || '',
210 + phone: store.currentUser.phone || '',
211 + email: store.currentUser.email || ''
212 + }
213 + }
214 + } else {
215 + error.value = '未找到活动信息'
216 + }
217 + }
218 + loading.value = false
219 + } catch (err) {
220 + console.error('Failed to fetch activity details:', err)
221 + error.value = '加载活动详情失败'
222 + loading.value = false
223 + }
224 +})
225 +
226 +// Validate form
227 +const validateForm = () => {
228 + const errors = {}
229 +
230 + if (!formState.value.name.trim()) {
231 + errors.name = '请输入您的姓名'
232 + }
233 +
234 + if (!formState.value.phone.trim()) {
235 + errors.phone = '请输入您的手机号码'
236 + } else if (!/^1[3-9]\d{9}$/.test(formState.value.phone)) {
237 + errors.phone = '请输入有效的手机号码'
238 + }
239 +
240 + if (!formState.value.email.trim()) {
241 + errors.email = '请输入您的电子邮箱'
242 + } else if (!/\S+@\S+\.\S+/.test(formState.value.email)) {
243 + errors.email = '请输入有效的电子邮箱'
244 + }
245 +
246 + if (!formState.value.reason.trim()) {
247 + errors.reason = '请简要说明参加原因'
248 + }
249 +
250 + if (!formState.value.agreeTerms) {
251 + errors.agreeTerms = '请同意活动条款和隐私政策'
252 + }
253 +
254 + formErrors.value = errors
255 + return Object.keys(errors).length === 0
256 +}
257 +
258 +// Handle form submission
259 +const handleSubmit = async () => {
260 + if (!store.currentUser) {
261 + alert('请先登录再进行报名')
262 + // Redirect to login page in a real app
263 + return
264 + }
265 +
266 + if (!validateForm()) {
267 + // Focus on first error field
268 + const firstErrorField = Object.keys(formErrors.value)[0]
269 + const element = document.getElementById(firstErrorField)
270 + if (element) {
271 + element.scrollIntoView({ behavior: 'smooth', block: 'center' })
272 + }
273 + return
274 + }
275 +
276 + isSubmitting.value = true
277 +
278 + try {
279 + const registrationData = {
280 + fields: {
281 + 姓名: true,
282 + 手机号: true,
283 + 邮箱: true,
284 + 参加原因: true
285 + },
286 + answers: {
287 + 姓名: formState.value.name,
288 + 手机号: formState.value.phone,
289 + 邮箱: formState.value.email,
290 + 参加原因: formState.value.reason
291 + }
292 + }
293 +
294 + const result = await store.registerForActivity(activityId, registrationData)
295 +
296 + if (result.success) {
297 + alert('报名成功,请等待审核')
298 + router.push(`/activity/${activityId}`)
299 + } else {
300 + alert(`报名失败: ${result.error}`)
301 + isSubmitting.value = false
302 + }
303 + } catch (error) {
304 + console.error('Registration error:', error)
305 + alert('报名失败,请稍后再试')
306 + isSubmitting.value = false
307 + }
308 +}
309 +
310 +// Format date for display
311 +const formatDate = (dateString) => {
312 + if (!dateString) return ''
313 + const date = new Date(dateString.replace(' ', 'T'))
314 + return new Intl.DateTimeFormat('zh-CN', {
315 + year: 'numeric',
316 + month: 'long',
317 + day: 'numeric',
318 + hour: '2-digit',
319 + minute: '2-digit',
320 + hour12: false
321 + }).format(date)
322 +}
323 +
324 +// Check if registration is still open
325 +const isRegistrationOpen = computed(() => {
326 + if (!activity.value) return false
327 + return (
328 + new Date() >= new Date(activity.value.registration_start.replace(' ', 'T')) &&
329 + new Date() <= new Date(activity.value.registration_end.replace(' ', 'T'))
330 + )
331 +})
332 +
333 +// Check if activity is full
334 +const isFull = computed(() => {
335 + if (!activity.value) return false
336 + return activity.value.participant_count >= activity.value.max_participants
337 +})
338 +</script>
1 +<template>
2 + <div class="min-h-screen bg-gray-50">
3 + <!-- Loading UI if no current user -->
4 + <div v-if="!currentUser" class="min-h-screen flex justify-center items-center bg-gray-50">
5 + <div class="text-center">
6 + <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-green-500 mx-auto"></div>
7 + <p class="mt-4 text-gray-600">加载用户资料...</p>
8 + </div>
9 + </div>
10 +
11 + <template v-else>
12 + <!-- Profile Header -->
13 + <div class="bg-gradient-to-r from-green-500 to-blue-500 py-12 px-4">
14 + <div class="container mx-auto">
15 + <div class="flex flex-col md:flex-row items-center justify-between">
16 + <!-- User Info -->
17 + <div class="flex flex-col md:flex-row items-center">
18 + <div
19 + class="h-24 w-24 md:h-32 md:w-32 rounded-full overflow-hidden border-4 border-white mb-4 md:mb-0 md:mr-6">
20 + <img :src="currentUser.avatar || '/assets/images/avatars/default_avatar.png'"
21 + :alt="currentUser.name" class="h-full w-full object-cover" />
22 + </div>
23 + <div class="text-center md:text-left">
24 + <h1 class="text-2xl md:text-3xl font-bold text-white">{{ currentUser.name }}</h1>
25 + <p class="text-white mt-2 opacity-90">@{{ currentUser.username }}</p>
26 + <div class="flex flex-wrap justify-center md:justify-start mt-3 gap-2">
27 + <span v-for="(interest, index) in displayedInterests" :key="index"
28 + class="bg-white bg-opacity-20 text-white px-3 py-1 rounded-full text-sm">
29 + {{ interest }}
30 + </span>
31 + <span v-if="currentUser.interests && currentUser.interests.length > 3"
32 + class="bg-white bg-opacity-20 text-white px-3 py-1 rounded-full text-sm">
33 + +{{ currentUser.interests.length - 3 }}
34 + </span>
35 + </div>
36 + </div>
37 + </div>
38 +
39 + <!-- Actions -->
40 + <div class="mt-6 md:mt-0 flex gap-2">
41 + <Button @click="isEditModalOpen = true" variant="secondary">
42 + <template #leftIcon>
43 + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
44 + fill="currentColor">
45 + <path
46 + d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
47 + </svg>
48 + </template>
49 + 编辑资料
50 + </Button>
51 + </div>
52 + </div>
53 + </div>
54 + </div>
55 +
56 + <!-- Profile Content -->
57 + <div class="container mx-auto py-8 px-4">
58 + <div class="bg-white rounded-lg shadow-sm overflow-hidden">
59 + <Tabs :tabs="profileTabs" />
60 + </div>
61 + </div>
62 +
63 + <!-- Edit Profile Modal -->
64 + <Modal v-model:show="isEditModalOpen" title="编辑个人资料">
65 + <div class="space-y-4">
66 + <Input v-model="editForm.name" label="姓名" placeholder="请输入姓名" />
67 + <Input v-model="editForm.email" label="电子邮箱" type="email" placeholder="请输入电子邮箱" />
68 + <Input v-model="editForm.phone" label="手机号码" placeholder="请输入手机号码" />
69 + <Input v-model="editForm.location" label="所在地" placeholder="请输入所在地" />
70 + <div>
71 + <label class="block text-sm font-medium text-gray-700 mb-1">个人简介</label>
72 + <textarea v-model="editForm.bio" rows="4"
73 + class="w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
74 + placeholder="请输入个人简介"></textarea>
75 + </div>
76 + <div>
77 + <label class="block text-sm font-medium text-gray-700 mb-1">兴趣标签</label>
78 + <div class="flex flex-wrap gap-2 mb-2">
79 + <span v-for="interest in editForm.interests" :key="interest"
80 + class="bg-green-50 text-green-700 px-3 py-1 rounded-full text-sm flex items-center">
81 + {{ interest }}
82 + <button @click="handleRemoveInterest(interest)"
83 + class="ml-2 text-green-500 hover:text-green-700">
84 + ×
85 + </button>
86 + </span>
87 + </div>
88 + <div class="flex gap-2">
89 + <Input v-model="interestInput" placeholder="添加兴趣标签" @keyup.enter="handleAddInterest" />
90 + <Button @click="handleAddInterest" variant="secondary">添加</Button>
91 + </div>
92 + </div>
93 + </div>
94 + <template #footer>
95 + <div class="flex justify-end gap-2">
96 + <Button @click="isEditModalOpen = false" variant="ghost">取消</Button>
97 + <Button @click="handleSubmitEdit" variant="primary">保存</Button>
98 + </div>
99 + </template>
100 + </Modal>
101 + </template>
102 + </div>
103 +</template>
104 +
105 +<script setup>
106 +import { ref, computed, onMounted } from 'vue'
107 +import { useAppStore } from '../stores/app'
108 +import Button from '../components/shared/Button.vue'
109 +import Input from '../components/shared/Input.vue'
110 +import Tabs from '../components/shared/Tabs.vue'
111 +import ActivityCard from '../components/shared/ActivityCard.vue'
112 +import Modal from '../components/shared/Modal.vue'
113 +
114 +const store = useAppStore()
115 +const { currentUser, activities, registrations } = store
116 +
117 +const userRegistrations = ref([])
118 +const registeredActivities = ref([])
119 +const pastActivities = ref([])
120 +const upcomingActivities = ref([])
121 +const isEditModalOpen = ref(false)
122 +const editForm = ref({
123 + name: '',
124 + bio: '',
125 + email: '',
126 + phone: '',
127 + location: '',
128 + interests: []
129 +})
130 +const interestInput = ref('')
131 +
132 +// Computed properties
133 +const displayedInterests = computed(() => {
134 + return currentUser.value?.interests?.slice(0, 3) || []
135 +})
136 +
137 +// Methods
138 +const handleInputChange = (e) => {
139 + const { name, value } = e.target
140 + editForm.value[name] = value
141 +}
142 +
143 +const handleAddInterest = () => {
144 + const interest = interestInput.value.trim()
145 + if (interest && !editForm.value.interests.includes(interest)) {
146 + editForm.value.interests.push(interest)
147 + interestInput.value = ''
148 + }
149 +}
150 +
151 +const handleRemoveInterest = (interestToRemove) => {
152 + editForm.value.interests = editForm.value.interests.filter(
153 + interest => interest !== interestToRemove
154 + )
155 +}
156 +
157 +const handleSubmitEdit = () => {
158 + // In a real app, this would make an API call to update the user profile
159 + console.log('Profile update submitted:', editForm.value)
160 + isEditModalOpen.value = false
161 + // Update would be handled through context in a real app
162 +}
163 +
164 +const formatRegistrationDate = (dateString) => {
165 + const date = new Date(dateString.replace(' ', 'T'))
166 + return new Intl.DateTimeFormat('zh-CN', {
167 + year: 'numeric',
168 + month: 'short',
169 + day: 'numeric',
170 + hour: '2-digit',
171 + minute: '2-digit',
172 + hour12: false
173 + }).format(date)
174 +}
175 +
176 +// Profile tabs configuration
177 +const profileTabs = computed(() => [
178 + {
179 + label: "个人资料",
180 + content: {
181 + component: 'div',
182 + class: 'p-6',
183 + children: [
184 + // Basic Info section
185 + {
186 + component: 'div',
187 + class: 'grid grid-cols-1 md:grid-cols-2 gap-8',
188 + children: [
189 + // Left column
190 + {
191 + component: 'div',
192 + children: [
193 + {
194 + component: 'h3',
195 + class: 'text-lg font-medium text-gray-900 mb-4',
196 + text: '基本信息'
197 + },
198 + // User info fields
199 + {
200 + component: 'div',
201 + class: 'space-y-4',
202 + children: [
203 + // Name
204 + {
205 + component: 'div',
206 + children: [
207 + {
208 + component: 'h4',
209 + class: 'text-sm font-medium text-gray-500',
210 + text: '姓名'
211 + },
212 + {
213 + component: 'p',
214 + class: 'mt-1 text-gray-900',
215 + text: currentUser.value?.name
216 + }
217 + ]
218 + },
219 + // Location
220 + {
221 + component: 'div',
222 + children: [
223 + {
224 + component: 'h4',
225 + class: 'text-sm font-medium text-gray-500',
226 + text: '所在地'
227 + },
228 + {
229 + component: 'p',
230 + class: 'mt-1 text-gray-900',
231 + text: currentUser.value?.location || '未设置'
232 + }
233 + ]
234 + }
235 + ]
236 + }
237 + ]
238 + },
239 + // Right column
240 + {
241 + component: 'div',
242 + children: [
243 + {
244 + component: 'h3',
245 + class: 'text-lg font-medium text-gray-900 mb-4',
246 + text: '联系方式'
247 + },
248 + // Contact info
249 + {
250 + component: 'div',
251 + class: 'space-y-4',
252 + children: [
253 + // Email
254 + {
255 + component: 'div',
256 + children: [
257 + {
258 + component: 'h4',
259 + class: 'text-sm font-medium text-gray-500',
260 + text: '电子邮箱'
261 + },
262 + {
263 + component: 'p',
264 + class: 'mt-1 text-gray-900',
265 + text: currentUser.value?.email
266 + }
267 + ]
268 + },
269 + // Phone
270 + {
271 + component: 'div',
272 + children: [
273 + {
274 + component: 'h4',
275 + class: 'text-sm font-medium text-gray-500',
276 + text: '手机号码'
277 + },
278 + {
279 + component: 'p',
280 + class: 'mt-1 text-gray-900',
281 + text: currentUser.value?.phone
282 + }
283 + ]
284 + }
285 + ]
286 + }
287 + ]
288 + }
289 + ]
290 + }
291 + ]
292 + }
293 + },
294 + {
295 + label: "我的活动",
296 + content: {
297 + component: 'div',
298 + class: 'p-6',
299 + children: [
300 + // Upcoming activities section
301 + {
302 + component: 'div',
303 + class: 'mb-8',
304 + children: [
305 + {
306 + component: 'h3',
307 + class: 'text-lg font-medium text-gray-900 mb-4',
308 + children: [
309 + {
310 + component: 'span',
311 + class: 'inline-block w-3 h-3 bg-green-500 rounded-full mr-2'
312 + },
313 + {
314 + component: 'span',
315 + text: '即将参加的活动'
316 + }
317 + ]
318 + },
319 + // Activity cards
320 + {
321 + component: 'div',
322 + class: 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6',
323 + children: upcomingActivities.value.map(activity => ({
324 + component: ActivityCard,
325 + props: { activity }
326 + }))
327 + }
328 + ]
329 + }
330 + ]
331 + }
332 + },
333 + {
334 + label: "报名记录",
335 + content: {
336 + component: 'div',
337 + class: 'p-6',
338 + children: [
339 + {
340 + component: 'div',
341 + class: 'overflow-x-auto',
342 + children: [
343 + // Registration table
344 + {
345 + component: 'table',
346 + class: 'min-w-full divide-y divide-gray-200',
347 + children: [
348 + // Table header
349 + {
350 + component: 'thead',
351 + class: 'bg-gray-50',
352 + children: [
353 + {
354 + component: 'tr',
355 + children: [
356 + {
357 + component: 'th',
358 + class: 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider',
359 + text: '活动信息'
360 + },
361 + {
362 + component: 'th',
363 + class: 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider',
364 + text: '报名时间'
365 + },
366 + {
367 + component: 'th',
368 + class: 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider',
369 + text: '状态'
370 + }
371 + ]
372 + }
373 + ]
374 + },
375 + // Table body
376 + {
377 + component: 'tbody',
378 + class: 'bg-white divide-y divide-gray-200',
379 + children: userRegistrations.value.map(registration => ({
380 + component: 'tr',
381 + key: registration.id,
382 + children: [
383 + // Activity info
384 + {
385 + component: 'td',
386 + class: 'px-6 py-4 whitespace-nowrap',
387 + children: [
388 + {
389 + component: 'div',
390 + class: 'flex items-center',
391 + children: [
392 + {
393 + component: 'div',
394 + class: 'text-sm font-medium text-gray-900',
395 + text: activities.value.find(a => a.id === registration.activity_id)?.title || '未知活动'
396 + }
397 + ]
398 + }
399 + ]
400 + },
401 + // Registration time
402 + {
403 + component: 'td',
404 + class: 'px-6 py-4 whitespace-nowrap',
405 + children: [
406 + {
407 + component: 'div',
408 + class: 'text-sm text-gray-900',
409 + text: formatRegistrationDate(registration.registration_time)
410 + }
411 + ]
412 + },
413 + // Status
414 + {
415 + component: 'td',
416 + class: 'px-6 py-4 whitespace-nowrap',
417 + children: [
418 + {
419 + component: 'span',
420 + class: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${registration.status === 'approved'
421 + ? 'bg-green-100 text-green-800'
422 + : registration.status === 'pending'
423 + ? 'bg-yellow-100 text-yellow-800'
424 + : 'bg-gray-100 text-gray-800'
425 + }`,
426 + text: registration.status === 'approved'
427 + ? '已通过'
428 + : registration.status === 'pending'
429 + ? '审核中'
430 + : registration.status
431 + }
432 + ]
433 + }
434 + ]
435 + }))
436 + }
437 + ]
438 + }
439 + ]
440 + }
441 + ]
442 + }
443 + }
444 +])
445 +
446 +// Lifecycle hooks
447 +onMounted(() => {
448 + if (currentUser.value) {
449 + editForm.value = {
450 + name: currentUser.value.name || '',
451 + bio: currentUser.value.bio || '',
452 + email: currentUser.value.email || '',
453 + phone: currentUser.value.phone || '',
454 + location: currentUser.value.location || '',
455 + interests: currentUser.value.interests || []
456 + }
457 + }
458 +
459 + if (currentUser.value && registrations.value && activities.value) {
460 + // Filter user registrations
461 + const userRegs = registrations.value.filter(reg => reg.user_id === currentUser.value.id)
462 + userRegistrations.value = userRegs
463 +
464 + // Get activities the user has registered for
465 + const regActivityIds = userRegs.map(reg => reg.activity_id)
466 + const regActivities = activities.value.filter(act => regActivityIds.includes(act.id))
467 + registeredActivities.value = regActivities
468 +
469 + const now = new Date()
470 +
471 + // Split activities into past and upcoming
472 + pastActivities.value = regActivities.filter(act => {
473 + return new Date(act.end_time.replace(' ', 'T')) < now
474 + })
475 +
476 + upcomingActivities.value = regActivities.filter(act => {
477 + return new Date(act.start_time.replace(' ', 'T')) > now
478 + })
479 + }
480 +})
481 +</script>
1 +<!--
2 + * @Date: 2025-04-17 13:41:17
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 13:41:18
5 + * @FilePath: /reading-club-app/src/providers/AppProvider.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <slot></slot>
10 +</template>
11 +
12 +<script setup>
13 +import { onMounted } from 'vue'
14 +import { useAppStore } from '../stores/app'
15 +
16 +const store = useAppStore()
17 +
18 +onMounted(() => {
19 + store.fetchInitialData()
20 +})
21 +</script>
1 +import { defineStore } from 'pinia'
2 +import { ref, computed } from 'vue'
3 +
4 +export const useAppStore = defineStore('app', () => {
5 + const currentUser = ref(null)
6 + const activities = ref([])
7 + const loading = ref(true)
8 + const error = ref(null)
9 + const registrations = ref([])
10 + const userMessages = ref([])
11 +
12 + // 初始化应用数据
13 + async function fetchInitialData() {
14 + try {
15 + loading.value = true
16 +
17 + // 获取用户数据
18 + const usersResponse = await fetch('/data/users.json')
19 + const usersData = await usersResponse.json()
20 + currentUser.value = usersData[0]
21 +
22 + // 获取活动数据
23 + const activitiesResponse = await fetch('/data/activities.json')
24 + const activitiesData = await activitiesResponse.json()
25 + activities.value = activitiesData
26 +
27 + // 获取报名数据
28 + const registrationsResponse = await fetch('/data/registrations.json')
29 + const registrationsData = await registrationsResponse.json()
30 + registrations.value = registrationsData
31 +
32 + // 获取消息数据
33 + const messagesResponse = await fetch('/data/messages.json')
34 + const messagesData = await messagesResponse.json()
35 + userMessages.value = messagesData
36 +
37 + loading.value = false
38 + } catch (err) {
39 + console.error('Failed to fetch initial data:', err)
40 + error.value = '加载应用数据失败,请稍后重试。'
41 + loading.value = false
42 + }
43 + }
44 +
45 + // 根据ID获取活动
46 + const getActivityById = computed(() => {
47 + return (activityId) => activities.value.find(activity => activity.id === activityId) || null
48 + })
49 +
50 + // 获取用户的报名记录
51 + const getUserRegistrations = computed(() => {
52 + return () => currentUser.value ? registrations.value.filter(reg => reg.user_id === currentUser.value.id) : []
53 + })
54 +
55 + // 报名活动
56 + function registerForActivity(activityId, formData) {
57 + if (!currentUser.value) return { success: false, error: '用户未登录' }
58 +
59 + const newRegistration = {
60 + id: `R${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
61 + activity_id: activityId,
62 + user_id: currentUser.value.id,
63 + registration_time: new Date().toISOString().replace('T', ' ').substring(0, 19),
64 + status: 'pending',
65 + custom_fields: formData.fields,
66 + custom_answers: formData.answers
67 + }
68 +
69 + registrations.value.push(newRegistration)
70 + return { success: true, registrationId: newRegistration.id }
71 + }
72 +
73 + // 创建新活动
74 + function createActivity(activityData) {
75 + if (!currentUser.value) return { success: false, error: '用户未登录' }
76 +
77 + const newActivity = {
78 + id: `A${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
79 + organizer_id: currentUser.value.id,
80 + organizer_name: currentUser.value.name,
81 + ...activityData,
82 + participant_count: 0,
83 + is_published: activityData.is_published || true,
84 + is_public: activityData.is_public || true,
85 + }
86 +
87 + activities.value.push(newActivity)
88 + return { success: true, activityId: newActivity.id }
89 + }
90 +
91 + // 活动签到
92 + function checkInForActivity(activityId, registrationId, method = 'manual') {
93 + if (!currentUser.value) return { success: false, error: '用户未登录' }
94 +
95 + const checkinData = {
96 + id: `C${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
97 + activity_id: activityId,
98 + user_id: currentUser.value.id,
99 + registration_id: registrationId,
100 + checkin_time: new Date().toISOString().replace('T', ' ').substring(0, 19),
101 + checkin_type: method,
102 + status: 'successful',
103 + is_late: false
104 + }
105 +
106 + return { success: true, checkin: checkinData }
107 + }
108 +
109 + // 获取用户消息
110 + const getUserMessages = computed(() => {
111 + return () => currentUser.value ? userMessages.value.filter(msg => msg.recipient_id === currentUser.value.id) : []
112 + })
113 +
114 + // 切换消息已读状态
115 + function toggleMessageReadStatus(messageId) {
116 + userMessages.value = userMessages.value.map(msg =>
117 + msg.id === messageId
118 + ? { ...msg, read_status: !msg.read_status }
119 + : msg
120 + )
121 + }
122 +
123 + return {
124 + currentUser,
125 + activities,
126 + loading,
127 + error,
128 + registrations,
129 + userMessages,
130 + fetchInitialData,
131 + getActivityById,
132 + getUserRegistrations,
133 + registerForActivity,
134 + createActivity,
135 + checkInForActivity,
136 + getUserMessages,
137 + toggleMessageReadStatus
138 + }
139 +})
1 +// Mock API functions to fetch data from JSON files
2 +
3 +// Fetch activities from mock data
4 +export const fetchActivities = async () => {
5 + try {
6 + const response = await fetch('/data/activities.json');
7 + if (!response.ok) throw new Error('Failed to fetch activities');
8 + const data = await response.json();
9 + return data;
10 + } catch (error) {
11 + console.error('Error fetching activities:', error);
12 + throw error;
13 + }
14 +};
15 +
16 +// Fetch a single activity by ID
17 +export const fetchActivityById = async (activityId) => {
18 + try {
19 + const response = await fetch('/data/activities.json');
20 + if (!response.ok) throw new Error('Failed to fetch activity');
21 + const activities = await response.json();
22 + const activity = activities.find(act => act.id === activityId);
23 +
24 + if (!activity) throw new Error('Activity not found');
25 + return activity;
26 + } catch (error) {
27 + console.error(`Error fetching activity ${activityId}:`, error);
28 + throw error;
29 + }
30 +};
31 +
32 +// Fetch users data
33 +export const fetchUsers = async () => {
34 + try {
35 + const response = await fetch('/data/users.json');
36 + if (!response.ok) throw new Error('Failed to fetch users');
37 + const data = await response.json();
38 + return data;
39 + } catch (error) {
40 + console.error('Error fetching users:', error);
41 + throw error;
42 + }
43 +};
44 +
45 +// Fetch a single user by ID
46 +export const fetchUserById = async (userId) => {
47 + try {
48 + const response = await fetch('/data/users.json');
49 + if (!response.ok) throw new Error('Failed to fetch user');
50 + const users = await response.json();
51 + const user = users.find(u => u.id === userId);
52 +
53 + if (!user) throw new Error('User not found');
54 + return user;
55 + } catch (error) {
56 + console.error(`Error fetching user ${userId}:`, error);
57 + throw error;
58 + }
59 +};
60 +
61 +// Fetch registrations data
62 +export const fetchRegistrations = async (activityId = null, userId = null) => {
63 + try {
64 + const response = await fetch('/data/registrations.json');
65 + if (!response.ok) throw new Error('Failed to fetch registrations');
66 + let registrations = await response.json();
67 +
68 + // Filter by activity ID if provided
69 + if (activityId) {
70 + registrations = registrations.filter(reg => reg.activity_id === activityId);
71 + }
72 +
73 + // Filter by user ID if provided
74 + if (userId) {
75 + registrations = registrations.filter(reg => reg.user_id === userId);
76 + }
77 +
78 + return registrations;
79 + } catch (error) {
80 + console.error('Error fetching registrations:', error);
81 + throw error;
82 + }
83 +};
84 +
85 +// Fetch check-in data
86 +export const fetchCheckins = async (activityId = null, userId = null) => {
87 + try {
88 + const response = await fetch('/data/checkins.json');
89 + if (!response.ok) throw new Error('Failed to fetch check-ins');
90 + let checkins = await response.json();
91 +
92 + // Filter by activity ID if provided
93 + if (activityId) {
94 + checkins = checkins.filter(check => check.activity_id === activityId);
95 + }
96 +
97 + // Filter by user ID if provided
98 + if (userId) {
99 + checkins = checkins.filter(check => check.user_id === userId);
100 + }
101 +
102 + return checkins;
103 + } catch (error) {
104 + console.error('Error fetching check-ins:', error);
105 + throw error;
106 + }
107 +};
108 +
109 +// Fetch messages data
110 +export const fetchMessages = async (recipientId = null) => {
111 + try {
112 + const response = await fetch('/data/messages.json');
113 + if (!response.ok) throw new Error('Failed to fetch messages');
114 + let messages = await response.json();
115 +
116 + // Filter by recipient ID if provided
117 + if (recipientId) {
118 + messages = messages.filter(msg => msg.recipient_id === recipientId);
119 + }
120 +
121 + return messages;
122 + } catch (error) {
123 + console.error('Error fetching messages:', error);
124 + throw error;
125 + }
126 +};
127 +
128 +// Mock function for creating an activity (would be an API POST in a real app)
129 +export const createActivity = async (activityData) => {
130 + // In a real app, this would send a POST request
131 + console.log('Creating activity with data:', activityData);
132 + return {
133 + success: true,
134 + id: `A${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
135 + ...activityData
136 + };
137 +};
138 +
139 +// Mock function for registering to an activity
140 +export const registerForActivity = async (activityId, userData) => {
141 + // In a real app, this would send a POST request
142 + console.log(`Registering for activity ${activityId} with data:`, userData);
143 + return {
144 + success: true,
145 + id: `R${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
146 + activity_id: activityId,
147 + ...userData
148 + };
149 +};
150 +
151 +// Mock function for checking in to an activity
152 +export const checkInForActivity = async (registrationId, checkinData) => {
153 + // In a real app, this would send a POST request
154 + console.log(`Checking in for registration ${registrationId} with data:`, checkinData);
155 + return {
156 + success: true,
157 + id: `C${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`,
158 + registration_id: registrationId,
159 + ...checkinData
160 + };
161 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * @Date: 2025-04-16 22:22:18
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 14:09:27
5 + * @FilePath: /reading-club-app/tailwind.config.js
6 + * @Description: 文件描述
7 + */
8 +/** @type {import('tailwindcss').Config} */
9 +export default {
10 + content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
11 + theme: {
12 + extend: {
13 + colors: {
14 + primary: '#4caf50',
15 + secondary: '#2196f3',
16 + 'background-start': '#f0f9ef',
17 + 'background-end': '#e6f4ff',
18 + 'text-base': '#333333',
19 + 'text-light': '#6e6e6e'
20 + }
21 + },
22 + },
23 + plugins: [],
24 +};
1 +{
2 + "description": " - This is a template for general web app, game, common application,client,tool,component development. It is based on React framework with Tailwind CSS. The template includes the basic structure of a React project, including an index.html file and a src directory with an App.jsx file.\n - Modify index.html to change web title, create new jsx files under src if needed, and rewrite src/App.jsx to meet the user's requirements. You should NOT modify src/main.jsx and src/index.css. Modify vite.config.js only if extra config is absolutely necessary.\n - Style your elements with Tailwind CSS classes directly in the jsx files.\n - If the user's requirements do not specify a technology stack, development language, development tools, components, etc., this template will be used by default. For example, developing an XXX application, XXX client, etc. all use this template.",
3 + "required_fields": [],
4 + "required_files": [
5 + "index.html",
6 + "src/main.jsx",
7 + "src/App.jsx",
8 + "src/index.css",
9 + "vite.config.js"
10 + ],
11 + "lang": "JavaScript",
12 + "framework": "React",
13 + "style": "react_template",
14 + "scene": "default_web_project"
15 +}
1 +/*
2 + * @Date: 2025-04-16 22:22:18
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-04-17 14:11:13
5 + * @FilePath: /reading-club-app/vite.config.js
6 + * @Description: 文件描述
7 + */
8 +import { defineConfig } from 'vite'
9 +import vue from '@vitejs/plugin-vue'
10 +import vueJsx from '@vitejs/plugin-vue-jsx'
11 +
12 +// https://vitejs.dev/config/
13 +export default defineConfig({
14 + plugins: [
15 + vue(),
16 + vueJsx(),
17 + ],
18 +})