Dev (#151)
* feat: 更新依赖,解决启动报错 * feat: 依赖升级后的样式修改 * refactor: 首页提取样式至单独的文件 * refactor: 重构首页代码,提取处理函数 * feat: 使用web worker来进行文件导入计算 * refactor: 提取组件,优化代码 * refactor: 人员列表代码结构重组 * feat: ✨ 修复问题 * feat: 添加Loading效果 * feat: indexdb存储人员名单,使用dexie * feat(config): 重构界面配置项布局与样式 将原有的线性表单布局调整为分组的 fieldset 布局,提升视觉层次和可维护性。 新增文本设置、布局设置、主题设置、图案设置和其他设置等分类区域。 优化部分输入控件的结构和样式类名,增强用户体验和代码可读性。 * feat(data): 调整默认图案列表顺序 2025改为2026 feat(ui): 添加配置项注释说明 为 FaceConfig.vue 中的各个设置字段添加了 HTML 注释,便于识别和维护不同功能区域。包括: - 文本设置(主标题、语言、文字大小) - 布局设置(列数、卡片宽度、卡片高度) - 主题设置(主题、背景图片) - 图案设置 - 其他设置(是否常显奖项列表、是否显示头像) * feat(PageHeader): 新增 PageHeader 组件并应用于多个配置页面 新增了 PageHeader 通用组件,用于统一页面标题和操作按钮区域的布局。 该组件包含 title 属性以及 buttons 和 alerts 两个具名插槽,便于复用和维护。 已在以下页面中集成使用: - 图片管理页(ImageConfig.vue) - 音乐管理页(MusicConfig.vue) - 人员管理页(PersonAll/index.vue) - 中奖者管理页(PersonAlready.vue) - 奖品管理页(PrizeConfig.vue) * fix(style): 调整 markdown 样式以支持主题变量 移除 media 查询,统一使用 CSS 变量定义颜色;修复组件中 i18n 导入位置问题。 * ``` fix(Config): 注释掉设置卡片颜色的逻辑 将设置卡片颜色的相关代码注释掉,完全使用用户使用的颜色,避免在主题切换时出现异常。 ``` * ``` refactor(FaceConfig): 移除未使用的颜色验证工具函数 从 FaceConfig 组件中移除了不再需要的 isHex 和 isRgbOrRgba 工具函数导入, 因为这些函数当前并未在组件中使用。这有助于减少不必要的代码依赖并提高 组件的简洁性。 ``` * fix(MusicConfig): 优化本地音乐列表项的显示样式 移除列表项中的 divide-y 类,为音乐名称添加 title 属性以显示完整名称, 并将音乐名称渲染为可点击的链接样式。 * build(workflow): 更新Node.js版本到22.x 将GitHub Actions工作流中的Node.js版本从20.x升级到22.x, 以使用最新的稳定版Node.js环境进行构建和测试。 * feat(router、config): 更新配置页面组件路径 将 FaceConfig、ImageConfig 和 MusicConfig 组件的导入路径从直接引用 `.vue` 文件改为引用 `index.vue`。 * build(eslint): 更新 ESLint 配置以忽略更多文件并添加警告规则 在原有忽略文件列表中新增了 '*.config.js' 文件类型,同时添加了 'no-console' 和 'no-debugger' 规则,并将其设置为警告级别,以提高 代码质量和一致性。 * feat(components): 添加图片上传组件及对话框功能 新增 `ImageUpload` 组件用于文件选择与预览,并集成到 `UploadDialog` 中实现图片上传逻辑。 更新了 `Dialog` 组件以支持可选属性和 model 绑定,增强其灵活性和可用性。 引入 `lucide-vue-next` 图标库支持图标渲染。 重构图片配置页面,移除旧上传逻辑,使用新的弹窗方式进行图片上传操作。 * feat(imgConfig): 优化图片存储结构 新增uuid依赖用于生成唯一ID,改进图片上传功能中的本地存储结构, 使用uuid替代时间戳作为键名以避免冲突,并调整从indexedDB读取数据的方式。 * feat: 音乐管理功能优化 * fix(MusicConfig): 移除上传对话框中的图片上传提示功能 移除了 UploadDialog 组件中不再使用的图片上传状态提示逻辑, 包括相关的响应式变量 imgUploadToast 及其对应的模板代码。 此变更简化了组件结构并移除了未使用的错误提示功能。 * fix(Config): 添加图片上传提示信息 在全局人脸配置页面添加了图片上传提示文本,提醒用户需要先上传图片到图片管理模块。 * feat(layout): 重构页面布局与音乐播放组件 将 PlayMusic 组件迁移至 layout/RightButton 并重构成通用右下角按钮组件, 提取音乐播放逻辑到独立 hook `usePlayMusic`,优化模态框提示逻辑并统一滚动行为。 * feat(prize-config): 引入拖拽功能并优化奖品配置界面 - 添加 `vue-draggable-plus` 和 `lodash-es` 依赖以支持列表拖拽排序 - 使用 `cloneDeep` 深拷贝奖品列表,避免直接修改原始数据 - 移除原有的手动排序逻辑(上下移动按钮),改用可视化拖拽方式 - 调整 UI 布局和样式,增强用户体验与可操作性 - 在 Demo 页面中添加 draggable 示例用于验证功能实现 * feat(Config): 动态显示当前年份并更新版权信息 将页脚中的版权年份从硬编码的 2024 改为动态获取的当前年份,并将作者名称添加跳转到 GitHub 主页的链接。 * fix(ui): 优化图片列表下拉选择器及表单项的样式与布局 * feat(person): 导入人员时添加uuid唯一标识字段 #91 * feat(utils): 添加安全洗牌与随机抽样函数 Feature #91 * test(random): 添加随机元素抽取函数的全面测试用例 Test #91 - 新增 Random.test.ts 文件,对 getRandomElements 函数进行详尽测试 - 包括基础功能、边界情况(count 为 0、负数、超出数组长度) - 支持空数组、单元素数组、字符串数组、对象数组等多种数据类型 - 增加概率性测试,验证多次调用结果不完全一致 - 20万次循环验证各元素被抽中概率接近理论值,确保算法公平性 * feat(home): 重构抽奖逻辑并优化初始化流程 Feature #91 * feat(Home): 添加初始化完成状态控制头部标题显示 Fix #91 * feat(HeaderTitle): 添加加载状态显示 #91 * feat(store): 调整默认人员列表类型定义 * feat(deps): 添加tauri打包(遗留pnpm build:file打包好的文件跨域无法访问) #94 * feat(icons): 添加tauri多种平台的应用图标资源 * fix(tauri): 更新 Cargo.toml 配置格式 #94 * build(workflow): 更新 GitHub Actions 工作流配置 #94 * build(workflow): 更新发布流程并移除旧的构建文件 * chore(release): 调整 GitHub Actions 工作流中的缩进格式 * build(deps): 固定 three.js 相关依赖版本 * build(workflows): 添加 fast-glob 依赖到 GitHub 工作流 * build(release): 添加发布名称和文件名以包含版本信息 * build(github): 升级下载构件操作到v4版本 * ci(release): 上传web构建产物作为GitHub Actions工件 * feat(Config): 支持多语言 README 文档展示 * feat(Global/FaceConfig): 添加跳转到图片管理页面的链接 #96 * chore: 格式化 * feat(i18n): 更新抽奖配置相关国际化文案 #96 * build: git钩子 * build: pre-psuh * feat(components): 添加 shadcn-vue 组件配置文件及配套组件库 #96 * feat(font): 添加本地字体选择功能(not done) #96 * feat: 更换字体v,以及页面加载之前就设置字体 #96 * chore: 删除v console.log #96 * build: pre-push * feat(globalConfig): 设置默认字体为微软雅黑 #96 * feat: 设置标题字体与全局字体同步 #96 * build(.gitignore): 添加 husky 目录到忽略文件 * docs(readme): 格式 * docs(readme): 移除关于build:file命令的过时文档 * build: 🏗️ 使用低版本node会报错,强制限制使用node 22.x版本 可在.npmrc里面修改为不强制使用 * feat: ✨ 上传文件时校验是否模板文件,否则提示 #96 * feat(layout): 添加全屏切换功能 #96 * refactor: ♻️ 界面设置页面重构,抽离逻辑 #96 * fix(background): 修复从本地存储获取图片数据的问题 #96 * refactor: ♻️ 重构奖项配置模块 #96 * feat(personalready): ✨ 已中奖的员页面修改switch组件 #96 * refactor(personalready): ♻️ 已中奖的界面代码重构 #96 * style: 💄 删除shadcn-vue的样式预设,只使用daisyui的样式 #96 * feat: ✨ 标题样式跟随主题,设置文本颜色会覆盖主题的标题颜色 #96 * fix(Global/FaceConfig): 调整主题设置中文本颜色按钮的高度样式 #96 * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 (#119) 卸载路由时清除requestAnimationFrame * 96 UI optimization (#122) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * Confilct dev date 12 22 (#131) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、 * 96 UI optimization (#132) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、 * fix(Config): 更新备案信息链接样式 将备案信息的段落标签替换为可点击的链接标签,使用户能够直接跳转到工信部备案查询页面。同时添加了悬停效果样式,提升用户体验。 * 96 UI optimization (#136) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、 * fix(Config): 更新备案信息链接样式 将备案信息的段落标签替换为可点击的链接标签,使用户能够直接跳转到工信部备案查询页面。同时添加了悬停效果样式,提升用户体验。 * feat: ✨ 首页奖项列表样式修改 not done #96 * chore(deps): ✏️ 更新依赖版本 * chore: ✏️ gsap list demo * build: 🏗️ docker构建优化 * chore: ✏️ gsap scroll demo * style: 💄 gsap demno * feat: ✨ demo smooth scroll gsap scrolltrigger * feat(Demo): 添加更多颜色选项并注释GSAP动画 * refactor(PrizeList): 重构奖品列表组件结构 * feat(PrizeList): 重构奖品列表组件并添加滚动动画 * feat: ✨ 增加定时抽取功能 #96 * feat: ✨ 添加定时抽取功能的说明 * feat: ✨ 优化gsap #96 项数不多时不触发gsap * style: 💄 文本修改 * feat: ✨ 优化 * feat: ✨ 优化奖项列表 * fix(Home): 修复奖品列表滚动检测逻辑 * fix(home): 修复抽奖停止逻辑避免重复执行;调整卡片垂直居中位置计算 * feat: ✨ 播放中奖音频 #96 * 96 UI optimization (#137) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、 * fix(Config): 更新备案信息链接样式 将备案信息的段落标签替换为可点击的链接标签,使用户能够直接跳转到工信部备案查询页面。同时添加了悬停效果样式,提升用户体验。 * feat: ✨ 首页奖项列表样式修改 not done #96 * chore(deps): ✏️ 更新依赖版本 * chore: ✏️ gsap list demo * build: 🏗️ docker构建优化 * chore: ✏️ gsap scroll demo * style: 💄 gsap demno * feat: ✨ demo smooth scroll gsap scrolltrigger * feat(Demo): 添加更多颜色选项并注释GSAP动画 * refactor(PrizeList): 重构奖品列表组件结构 * feat(PrizeList): 重构奖品列表组件并添加滚动动画 * feat: ✨ 增加定时抽取功能 #96 * feat: ✨ 添加定时抽取功能的说明 * feat: ✨ 优化gsap #96 项数不多时不触发gsap * style: 💄 文本修改 * feat: ✨ 优化 * feat: ✨ 优化奖项列表 * fix(Home): 修复奖品列表滚动检测逻辑 * fix(home): 修复抽奖停止逻辑避免重复执行;调整卡片垂直居中位置计算 * feat: ✨ 播放中奖音频 #96 * style: 💄 下载模板成功后进行提示 * 96 UI optimization (#141) * fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、 * fix(Config): 更新备案信息链接样式 将备案信息的段落标签替换为可点击的链接标签,使用户能够直接跳转到工信部备案查询页面。同时添加了悬停效果样式,提升用户体验。 * feat: ✨ 首页奖项列表样式修改 not done #96 * chore(deps): ✏️ 更新依赖版本 * chore: ✏️ gsap list demo * build: 🏗️ docker构建优化 * chore: ✏️ gsap scroll demo * style: 💄 gsap demno * feat: ✨ demo smooth scroll gsap scrolltrigger * feat(Demo): 添加更多颜色选项并注释GSAP动画 * refactor(PrizeList): 重构奖品列表组件结构 * feat(PrizeList): 重构奖品列表组件并添加滚动动画 * feat: ✨ 增加定时抽取功能 #96 * feat: ✨ 添加定时抽取功能的说明 * feat: ✨ 优化gsap #96 项数不多时不触发gsap * style: 💄 文本修改 * feat: ✨ 优化 * feat: ✨ 优化奖项列表 * fix(Home): 修复奖品列表滚动检测逻辑 * fix(home): 修复抽奖停止逻辑避免重复执行;调整卡片垂直居中位置计算 * feat: ✨ 播放中奖音频 #96 * style: 💄 下载模板成功后进行提示 * docs: 📝 readme更新 * ci: 👷 git action触发改为推送release版本时执行 * Feature action (#143) * ci: 👷 整合github action配置文件 * docs: 📝 贡献文档修改 * Feature action (#144) * ci: 👷 整合github action配置文件 * docs: 📝 贡献文档修改 * style: 💄 更新版本 * style: 💄 cargo.lock版本更新 * Feature action (#149) * ci: 👷 整合github action配置文件 * docs: 📝 贡献文档修改 * style: 💄 更新版本 * style: 💄 cargo.lock版本更新 * feat(husky): 增强Git标签版本校验脚本 添加了对Git标签指向提交与release分支一致性的校验功能。 脚本现在会检查tag指向的提交是否与当前或任何release分支的最新提交一致, 确保发布流程的准确性。如果当前在release分支上,直接比较分支HEAD与tag指向的提交; 如果不在release分支上,则遍历所有release分支查找匹配的提交。 * feat: ✨ 国际化 * Feature action (#150) * ci: 👷 整合github action配置文件 * docs: 📝 贡献文档修改 * style: 💄 更新版本 * style: 💄 cargo.lock版本更新 * feat(husky): 增强Git标签版本校验脚本 添加了对Git标签指向提交与release分支一致性的校验功能。 脚本现在会检查tag指向的提交是否与当前或任何release分支的最新提交一致, 确保发布流程的准确性。如果当前在release分支上,直接比较分支HEAD与tag指向的提交; 如果不在release分支上,则遍历所有release分支查找匹配的提交。 * feat: ✨ 国际化 * fix: 🐛 修复国际化问题;修复字体大小未生效问题 * fix: 🐛 修复部分问题 * docs: 📝 update readme
23
.github/CONTRIBUTING.md
vendored
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
## PR检查表
|
## PR检查表
|
||||||
|
|
||||||
- 该项目主要的工作分支有`main`和`dev`
|
- 该项目主要的工作分支有`main`、`release`和`dev`三个分支。
|
||||||
- `main`分支包含的是已经发布的功能,请勿提交PR到`main`分支。
|
- `main`分支用于生产环境,如果是修改文档、注释、代码格式化等不影响主要功能的修改,可以直接发起PR到`main`分支。
|
||||||
- 如要贡献代码,请从`dev`分支拉取代码,该分支一般包含最新待上线的功能。
|
- `release`分支用于发布新版本,为当前网站上运行的正式版本的代码。
|
||||||
- 你的代码提交(添加新功能、修复bug、优化性能等)需要发起PR到`dev`分支
|
- 如要贡献代码,请从`release`或者`main`分支拉取代码。
|
||||||
- 请尽可能地在您的PR请求中描述清楚添加的功能或者修复的问题
|
- 你的代码提交(添加新功能、修复bug、优化性能等)尽量发起PR到`dev`分支。
|
||||||
|
- 请尽可能地在您的PR请求中描述清楚添加的功能或者修复的问题。
|
||||||
- 在一个PR中有多个小提交是没问题的,但请确保每个提交都包含一个清晰的提交信息。
|
- 在一个PR中有多个小提交是没问题的,但请确保每个提交都包含一个清晰的提交信息。
|
||||||
- 请确保您的提交信息遵循[Conventional Commits](https://www.conventionalcommits.org/)规范。
|
- 请确保您的提交信息遵循[Conventional Commits](https://www.conventionalcommits.org/)规范。
|
||||||
|
|
||||||
@@ -32,6 +33,12 @@ pnpm install # 安装依赖
|
|||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
启动tauri的开发服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
### `pnpm build`
|
### `pnpm build`
|
||||||
|
|
||||||
构建项目
|
构建项目
|
||||||
@@ -40,12 +47,10 @@ pnpm dev
|
|||||||
pnpm build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
### `pnpm build:file`
|
打包tauri安装包
|
||||||
|
|
||||||
打包后的项目可以直接通过html文件的形式直接在浏览器打开
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm build:file
|
pnpm tauri build
|
||||||
```
|
```
|
||||||
|
|
||||||
### `pnpm lint:fix`
|
### `pnpm lint:fix`
|
||||||
|
|||||||
44
.github/workflows/node.js.yml
vendored
@@ -1,44 +0,0 @@
|
|||||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
||||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
||||||
|
|
||||||
name: Node.js CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [20.x]
|
|
||||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: pnpm/action-setup@v2
|
|
||||||
with:
|
|
||||||
version: 8
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
cache: 'pnpm'
|
|
||||||
- run: pnpm install
|
|
||||||
- run: pnpm build
|
|
||||||
- name: Copy and Rename index.html to dist
|
|
||||||
run: cp dist/index.html dist/404.html
|
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
|
||||||
uses: crazy-max/ghaction-github-pages@v2
|
|
||||||
with:
|
|
||||||
target_branch: gh-pages
|
|
||||||
build_dir: dist
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
|
|
||||||
132
.github/workflows/release.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-release:
|
build-web:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -32,31 +32,139 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Install dependency fast-glob
|
||||||
|
run: pnpm add fast-glob -D
|
||||||
|
|
||||||
- name: Build for production
|
- name: Build for production
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Build for file protocol
|
|
||||||
run: pnpm build:file
|
|
||||||
|
|
||||||
- name: Prepare directories
|
- name: Prepare directories
|
||||||
run: |
|
run: |
|
||||||
mv dist dist-prod
|
mv dist dist-prod
|
||||||
mv dist-prod/index.html dist-prod/404.html
|
|
||||||
|
|
||||||
- name: Create production build archive
|
- name: Create production build archive
|
||||||
run: |
|
run: |
|
||||||
tar -czf dist.tar.gz dist-prod
|
zip -r dist.zip dist-prod
|
||||||
|
|
||||||
- name: Create file protocol build archive
|
- name: Upload web build artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: web-build
|
||||||
|
path: dist.zip
|
||||||
|
|
||||||
|
- name: Prepare GitHub Pages artifact
|
||||||
run: |
|
run: |
|
||||||
tar -czf dist-file.tar.gz dist-file
|
cp -r dist-prod dist-gh-pages
|
||||||
|
|
||||||
- name: Release
|
- name: Upload GitHub Pages artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: github-pages
|
||||||
|
path: dist-gh-pages
|
||||||
|
|
||||||
|
- name: Prepare GitHub Pages artifact
|
||||||
|
run: |
|
||||||
|
cp -r dist-prod dist-gh-pages
|
||||||
|
|
||||||
|
- name: Upload GitHub Pages artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: github-pages
|
||||||
|
path: dist-gh-pages
|
||||||
|
|
||||||
|
publish-tauri:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: ./src-tauri -> target
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 22.x
|
||||||
|
cache: pnpm
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Install dependency fast-glob
|
||||||
|
run: pnpm add fast-glob -D
|
||||||
|
|
||||||
|
- name: Build the app
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tagName: ${{ github.ref_name }}
|
||||||
|
releaseName: 'Release ${{ github.ref_name }}'
|
||||||
|
releaseBody: See the assets to download this version and install.
|
||||||
|
releaseDraft: true
|
||||||
|
prerelease: false
|
||||||
|
run: pnpm tauri build
|
||||||
|
|
||||||
|
deploy-gh-pages:
|
||||||
|
needs: build-web
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download GitHub Pages artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: github-pages
|
||||||
|
path: dist-gh-pages
|
||||||
|
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v3
|
||||||
|
|
||||||
|
- name: Upload to GitHub Pages
|
||||||
|
uses: actions/deploy-pages@v2
|
||||||
|
id: deployment
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./dist-gh-pages
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: [build-web, publish-tauri, deploy-gh-pages]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Download web build artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: web-build
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist.tar.gz
|
dist.zip
|
||||||
dist-file.tar.gz
|
name: 'Release ${{ github.ref_name }}'
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
8
.gitignore
vendored
@@ -35,13 +35,15 @@ bower_components
|
|||||||
# node-waf configuration
|
# node-waf configuration
|
||||||
.lock-wscript
|
.lock-wscript
|
||||||
|
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
build/Release
|
build/Release
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
|
.husky/_
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
web_modules/
|
web_modules/
|
||||||
|
|
||||||
@@ -79,6 +81,9 @@ web_modules/
|
|||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
|
**/components.d.ts
|
||||||
|
**/auto-imports.d.ts
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
.cache
|
.cache
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
@@ -90,6 +95,7 @@ out
|
|||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
dist
|
||||||
|
dist-file
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
|
|||||||
2
.husky/_/applypatch-msg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/commit-msg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
22
.husky/_/h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
[ "$HUSKY" = "2" ] && set -x
|
||||||
|
n=$(basename "$0")
|
||||||
|
s=$(dirname "$(dirname "$0")")/$n
|
||||||
|
|
||||||
|
[ ! -f "$s" ] && exit 0
|
||||||
|
|
||||||
|
if [ -f "$HOME/.huskyrc" ]; then
|
||||||
|
echo "husky - '~/.huskyrc' is DEPRECATED, please move your code to ~/.config/husky/init.sh"
|
||||||
|
fi
|
||||||
|
i="${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh"
|
||||||
|
[ -f "$i" ] && . "$i"
|
||||||
|
|
||||||
|
[ "${HUSKY-}" = "0" ] && exit 0
|
||||||
|
|
||||||
|
export PATH="node_modules/.bin:$PATH"
|
||||||
|
sh -e "$s" "$@"
|
||||||
|
c=$?
|
||||||
|
|
||||||
|
[ $c != 0 ] && echo "husky - $n script failed (code $c)"
|
||||||
|
[ $c = 127 ] && echo "husky - command not found in PATH=$PATH"
|
||||||
|
exit $c
|
||||||
9
.husky/_/husky.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
echo "husky - DEPRECATED
|
||||||
|
|
||||||
|
Please remove the following two lines from $0:
|
||||||
|
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
. \"\$(dirname -- \"\$0\")/_/husky.sh\"
|
||||||
|
|
||||||
|
They WILL FAIL in v10.0.0
|
||||||
|
"
|
||||||
2
.husky/_/post-applypatch
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/post-checkout
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/post-commit
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/post-merge
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/post-rewrite
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-applypatch
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-auto-gc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-commit
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-merge-commit
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-push
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/pre-rebase
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
2
.husky/_/prepare-commit-msg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname "$0")/h"
|
||||||
38
.husky/pre-push
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# 读取 stdin 中的所有内容
|
||||||
|
while read local_ref local_sha remote_ref remote_sha; do
|
||||||
|
# 检查是否是 tag 推送
|
||||||
|
case "$remote_ref" in
|
||||||
|
refs/tags/*)
|
||||||
|
# 提取 tag 名称
|
||||||
|
TAG="${remote_ref#refs/tags/}"
|
||||||
|
|
||||||
|
# 只校验以 "v" 开头的 tag
|
||||||
|
if [[ $TAG == v* ]]; then
|
||||||
|
echo "🏷️ 检查推送的 tag: $TAG"
|
||||||
|
node .husky/scripts/verifyTagVersion.js "$TAG"
|
||||||
|
else
|
||||||
|
echo "ℹ️ 非版本 tag ($TAG),跳过校验"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# 如果 stdin 为空(没有推送任何引用),也检查最新 tag
|
||||||
|
if [ -t 0 ]; then
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
# 只校验以 "v" 开头的 tag
|
||||||
|
if [[ $LATEST_TAG == v* ]]; then
|
||||||
|
echo "🏷️ 检查最新的 tag: $LATEST_TAG"
|
||||||
|
node .husky/scripts/verifyTagVersion.js "$LATEST_TAG"
|
||||||
|
else
|
||||||
|
echo "ℹ️ 最新 tag ($LATEST_TAG) 不是版本 tag,跳过校验"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⚠️ 没有找到任何 tag,跳过版本校验"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -3,18 +3,18 @@ import fs from "fs";
|
|||||||
const msg = fs.readFileSync(".git/COMMIT_EDITMSG", "utf-8").trim();
|
const msg = fs.readFileSync(".git/COMMIT_EDITMSG", "utf-8").trim();
|
||||||
|
|
||||||
const commitRE =
|
const commitRE =
|
||||||
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?: .{1,50}/;
|
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?: .{1,50}/;
|
||||||
const mergeRe = /^(Merge pull request|Merge branch)/;
|
const mergeRe = /^(Merge pull request|Merge branch)/;
|
||||||
if (!commitRE.test(msg)) {
|
if (!commitRE.test(msg)) {
|
||||||
if (!mergeRe.test(msg)) {
|
if (!mergeRe.test(msg)) {
|
||||||
console.log("git commit信息校验不通过");
|
console.log("git commit信息校验不通过");
|
||||||
|
|
||||||
console.error(`git commit的信息格式不对, 需要使用 title(scope): desc的格式
|
console.error(`git commit的信息格式不对, 需要使用 title(scope): desc的格式
|
||||||
比如 fix: xxbug
|
比如 fix: xxbug
|
||||||
feat(test): add new
|
feat(test): add new
|
||||||
`);
|
`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("git commit信息校验通过");
|
console.log("git commit信息校验通过");
|
||||||
}
|
}
|
||||||
|
|||||||
106
.husky/scripts/verifyTagVersion.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 读取 package.json 中的版本号
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
||||||
|
const packageVersion = packageJson.version;
|
||||||
|
|
||||||
|
// 获取命令行参数中的 tag 名称
|
||||||
|
const tag = process.argv[2];
|
||||||
|
|
||||||
|
if (!tag) {
|
||||||
|
console.log('⚠️ 未提供 tag 参数,跳过版本校验');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 tag 是否与 package.json 版本匹配
|
||||||
|
const validTags = [`v${packageVersion}`, packageVersion];
|
||||||
|
|
||||||
|
if (!validTags.includes(tag)) {
|
||||||
|
console.log('🏷️ Git tag 版本校验不通过');
|
||||||
|
console.error(`当前 package.json 版本为: ${packageVersion}`);
|
||||||
|
console.error(`提供的 tag 为: ${tag}`);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
console.log('✅ Git tag 版本校验通过');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 tag 指向的提交哈希
|
||||||
|
const tagCommit = execSync(`git rev-list -1 ${tag}`, { encoding: 'utf-8' }).trim();
|
||||||
|
|
||||||
|
// 获取当前分支名称
|
||||||
|
let currentBranch;
|
||||||
|
try {
|
||||||
|
currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('无法获取当前分支名称');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前分支是否为 release 分支
|
||||||
|
if (currentBranch.startsWith('release/')) {
|
||||||
|
// 如果当前在 release 分支上,检查当前分支的 HEAD 提交是否与 tag 指向的提交一致
|
||||||
|
const currentBranchCommit = execSync(`git rev-parse ${currentBranch}`, { encoding: 'utf-8' }).trim();
|
||||||
|
|
||||||
|
if (tagCommit !== currentBranchCommit) {
|
||||||
|
console.log('🏷️ Git tag 指向的提交与当前 release 分支的最新提交不一致');
|
||||||
|
console.error(`当前 release 分支 "${currentBranch}" 的最新提交为: ${currentBranchCommit}`);
|
||||||
|
console.error(`Tag "${tag}" 指向的提交为: ${tagCommit}`);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
console.log('✅ Git tag 指向的提交与当前 release 分支的最新提交一致');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果当前不在 release 分支上,查找所有 release 分支并检查是否有分支的 HEAD 与 tag 指向的提交一致
|
||||||
|
console.log(`🔍 当前在 "${currentBranch}" 分支,检查 tag 指向的提交是否与任何 release 分支的最新提交一致`);
|
||||||
|
|
||||||
|
// 获取所有本地分支
|
||||||
|
const localBranchesOutput = execSync('git branch --format="%(refname:short)"', { encoding: 'utf-8' });
|
||||||
|
const localBranches = localBranchesOutput.split('\n').map(b => b.trim()).filter(b => b);
|
||||||
|
|
||||||
|
// 过滤出 release 分支
|
||||||
|
const releaseBranches = localBranches.filter(branch => branch.startsWith('release/'));
|
||||||
|
|
||||||
|
if (releaseBranches.length === 0) {
|
||||||
|
console.log('⚠️ 未找到 release 分支');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundMatchingBranch = false;
|
||||||
|
let matchingBranchName = '';
|
||||||
|
|
||||||
|
for (const branch of releaseBranches) {
|
||||||
|
try {
|
||||||
|
// 获取 release 分支的最新提交
|
||||||
|
const releaseBranchCommit = execSync(`git rev-parse ${branch}`, { encoding: 'utf-8' }).trim();
|
||||||
|
|
||||||
|
// 检查是否与 tag 指向的提交一致
|
||||||
|
if (tagCommit === releaseBranchCommit) {
|
||||||
|
foundMatchingBranch = true;
|
||||||
|
matchingBranchName = branch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 如果无法获取分支信息,继续尝试下一个分支
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundMatchingBranch) {
|
||||||
|
console.log('🏷️ Git tag 指向的提交与任何 release 分支的最新提交都不一致');
|
||||||
|
console.error(`提供的 tag 为: ${tag}`);
|
||||||
|
console.error(`tag 指向的提交为: ${tagCommit}`);
|
||||||
|
console.log(`检查了以下 release 分支:`, releaseBranches);
|
||||||
|
console.log('可能的原因:');
|
||||||
|
console.log('1. release 分支不是最新的');
|
||||||
|
console.log('2. tag 指向的提交不在 release 分支上');
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
console.log(`✅ Git tag 指向的提交与 release 分支 "${matchingBranchName}" 的最新提交一致`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 校验过程中发生错误:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# 使用官方的 Node 镜像作为基础镜像
|
# 使用更小的 Node 镜像作为构建基础镜像
|
||||||
FROM node:22
|
FROM node:22-alpine as builder
|
||||||
|
|
||||||
# 设置工作目录
|
# 设置工作目录
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|||||||
98
README.md
@@ -17,20 +17,21 @@ log-lottery是一个可配置可定制化的抽奖应用,炫酷3D球体,可
|
|||||||
|
|
||||||
> 如果进入网站遇到图片无法显示或有报错的情况,请先到【全局配置】-【界面配置】菜单中点击【重置所有数据】按钮清除数据后进行更新。
|
> 如果进入网站遇到图片无法显示或有报错的情况,请先到【全局配置】-【界面配置】菜单中点击【重置所有数据】按钮清除数据后进行更新。
|
||||||
|
|
||||||
> 该项目将在近期进行**内部代码重构**及**开发新功能**,预计元旦节前三天上线新版本。
|
|
||||||
|
|
||||||
## 要求
|
## 要求
|
||||||
|
|
||||||
使用PC端最新版Chrome或Edge浏览器。
|
使用PC端最新版Chrome或Edge浏览器。
|
||||||
|
|
||||||
访问地址:
|
访问地址:
|
||||||
|
|
||||||
<https://to2026.xyz/log-lottery>
|
<https://lottery.to2026.xyz/log-lottery>
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
<https://log1997.github.io/log-lottery/>
|
<https://log1997.github.io/log-lottery/>
|
||||||
|
|
||||||
|
开发仓促,若以上网站内容存在bug还请宽容。
|
||||||
|
如果想要访问2025年12月31日前的版本,请前往:<https://lottery.to2026.xyz/log-lottery>
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [x] 🕍 炫酷3D球体,年会抽奖必备,开箱即用
|
- [x] 🕍 炫酷3D球体,年会抽奖必备,开箱即用
|
||||||
@@ -70,33 +71,27 @@ or
|
|||||||
## 预览
|
## 预览
|
||||||
|
|
||||||
首页
|
首页
|
||||||
|
<div align="center">
|
||||||

|
<img src="./static/images/home.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||
|
<img src="./static//images/home_prizelist.png" alt="img2-2" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||

|
</div>
|
||||||
|
|
||||||
抽奖
|
抽奖
|
||||||
|
<div align="center">
|
||||||

|
<img src="./static/images/lottery-enter.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||
|
<img src="./static/images/lottery-done.png" alt="img2-2" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||

|
</div>
|
||||||
|
|
||||||
配置
|
配置
|
||||||
|
<div align="center">
|
||||||

|
<img src="./static/images/config_personall.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||
|
<img src="./static/images/config_prize.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||

|
<img src="./static/images/config-view.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||
|
<img src="./static/images/config_pattern.png" alt="img2-1" width="400" style="border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin: 8px;">
|
||||||

|
</div>
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
图片音乐配置
|
图片音乐配置
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 技术
|
## 技术
|
||||||
|
|
||||||
- vue3
|
- vue3
|
||||||
@@ -131,31 +126,54 @@ or
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
若想直接以打开html文件的方式运行,请执行以下命令进行打包。打包完成后在dist目录中直接打开index.html即可。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm build:file
|
|
||||||
or
|
|
||||||
npm run build:file
|
|
||||||
```
|
|
||||||
|
|
||||||
> 项目思路来源于 <https://github.com/moshang-xc/lottery>
|
> 项目思路来源于 <https://github.com/moshang-xc/lottery>
|
||||||
|
|
||||||
## Docker支持
|
## Docker支持
|
||||||
|
|
||||||
构建镜像
|
以下任意方式选一种即可
|
||||||
|
|
||||||
```bash
|
1. 拉取镜像,从Docker Hub拉取镜像[log-lottery](https://hub.docker.com/r/log1997/log-lottery)
|
||||||
docker build -t log-lottery .
|
|
||||||
```
|
|
||||||
|
|
||||||
运行容器
|
```bash
|
||||||
|
docker pull log1997/log-lottery:latest
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
运行容器
|
||||||
docker run -d -p 9279:80 log-lottery
|
|
||||||
```
|
|
||||||
|
|
||||||
容器运行成功后即可在本地通过<http://localhost:9279/log-lottery/>访问
|
```bash
|
||||||
|
docker run -d --name log-lottery -p 9279:80 log1997/log-lottery:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 手动构建镜像
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t log-lottery .
|
||||||
|
```
|
||||||
|
|
||||||
|
运行容器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d -p 9279:80 log-lottery
|
||||||
|
```
|
||||||
|
|
||||||
|
容器运行成功后即可在本地通过<http://localhost:9279/log-lottery/>访问
|
||||||
|
|
||||||
|
3. 软件安装包
|
||||||
|
|
||||||
|
可前往[Releases](https://github.com/LOG1997/log-lottery/releases)下载。
|
||||||
|
目前只支持windows平台使用,跨平台安装包暂不支持,如有需要请自行编译,参照[贡献文档](https://github.com/LOG1997/log-lottery/blob/dev/.github/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## 支持项目
|
||||||
|
|
||||||
|
<h3>💝 赞助支持</h3>
|
||||||
|
|
||||||
|
<p><em>如果您觉得 log-lottery 对您有帮助,欢迎赞助支持,您的支持是我们不断前进的动力!</em></p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="./static/images/ZanShang.png" height="240" alt="WeChat Code">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import Button from '@/components/Button/index.vue'
|
|
||||||
|
|
||||||
import { shallowMount } from '@vue/test-utils'
|
|
||||||
import { describe, expect, test } from 'vitest'
|
|
||||||
// 测试分组
|
|
||||||
describe('Button', () => {
|
|
||||||
// mount
|
|
||||||
test('Buttons slot text', () => {
|
|
||||||
// @vue/test-utils
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
slots: {
|
|
||||||
default: 'Button',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// 断言
|
|
||||||
expect(wrapper.text()).toBe('Button')
|
|
||||||
})
|
|
||||||
test('Button click', () => {
|
|
||||||
const wrapper = shallowMount(Button)
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeTruthy()
|
|
||||||
})
|
|
||||||
test('Button disabled', () => {
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
props: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeFalsy()
|
|
||||||
})
|
|
||||||
test('Button not disabled', () => {
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
props: {
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeTruthy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import Button from '@/components/Button/index.vue'
|
|
||||||
|
|
||||||
import { shallowMount } from '@vue/test-utils'
|
|
||||||
import { describe, expect, test } from 'vitest'
|
|
||||||
// 测试分组
|
|
||||||
describe('Button', () => {
|
|
||||||
// mount
|
|
||||||
test('Buttons slot text', () => {
|
|
||||||
// @vue/test-utils
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
slots: {
|
|
||||||
default: 'Button',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// 断言
|
|
||||||
expect(wrapper.text()).toBe('Button')
|
|
||||||
})
|
|
||||||
test('Button click', () => {
|
|
||||||
const wrapper = shallowMount(Button)
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeTruthy()
|
|
||||||
})
|
|
||||||
test('Button disabled', () => {
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
props: {
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeFalsy()
|
|
||||||
})
|
|
||||||
test('Button not disabled', () => {
|
|
||||||
const wrapper = shallowMount(Button, {
|
|
||||||
props: {
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
wrapper.trigger('click')
|
|
||||||
expect(wrapper.emitted('click')).toBeTruthy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
124
__test__/Random.test.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { getRandomElements } from '@/views/Home/utils/random'
|
||||||
|
|
||||||
|
describe('getRandomElements', () => {
|
||||||
|
// 测试基本功能:从数组中获取指定数量的元素
|
||||||
|
it('should return specified number of elements', () => {
|
||||||
|
const sourceArray = [1, 2, 3, 4, 5]
|
||||||
|
const result = getRandomElements(sourceArray, 3)
|
||||||
|
|
||||||
|
expect(result).toHaveLength(3)
|
||||||
|
result.forEach((element) => {
|
||||||
|
expect(sourceArray).toContain(element)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试边界情况:count为0
|
||||||
|
it('should return empty array when count is 0', () => {
|
||||||
|
const sourceArray = [1, 2, 3]
|
||||||
|
const result = getRandomElements(sourceArray, 0)
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试边界情况:count为负数
|
||||||
|
it('should return empty array when count is negative', () => {
|
||||||
|
const sourceArray = [1, 2, 3]
|
||||||
|
const result = getRandomElements(sourceArray, -1)
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试边界情况:count大于等于数组长度
|
||||||
|
it('should return shuffled array when count equals or exceeds array length', () => {
|
||||||
|
const sourceArray = [1, 2, 3]
|
||||||
|
const result1 = getRandomElements(sourceArray, 3)
|
||||||
|
const result2 = getRandomElements(sourceArray, 5)
|
||||||
|
|
||||||
|
expect(result1).toHaveLength(3)
|
||||||
|
expect(result2).toHaveLength(3)
|
||||||
|
|
||||||
|
// 验证返回的元素与原数组相同
|
||||||
|
expect(result1.sort()).toEqual(sourceArray.sort())
|
||||||
|
expect(result2.sort()).toEqual(sourceArray.sort())
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试空数组情况
|
||||||
|
it('should return empty array when source array is empty', () => {
|
||||||
|
const sourceArray: number[] = []
|
||||||
|
const result = getRandomElements(sourceArray, 3)
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试单元素数组
|
||||||
|
it('should handle single element array', () => {
|
||||||
|
const sourceArray = [42]
|
||||||
|
const result = getRandomElements(sourceArray, 1)
|
||||||
|
|
||||||
|
expect(result).toEqual([42])
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试字符串数组
|
||||||
|
it('should work with string arrays', () => {
|
||||||
|
const sourceArray = ['a', 'b', 'c', 'd', 'e']
|
||||||
|
const result = getRandomElements(sourceArray, 2)
|
||||||
|
|
||||||
|
expect(result).toHaveLength(2)
|
||||||
|
result.forEach((element) => {
|
||||||
|
expect(sourceArray).toContain(element)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试对象数组
|
||||||
|
it('should work with object arrays', () => {
|
||||||
|
const sourceArray = [
|
||||||
|
{ id: 1, name: 'Alice' },
|
||||||
|
{ id: 2, name: 'Bob' },
|
||||||
|
{ id: 3, name: 'Charlie' },
|
||||||
|
]
|
||||||
|
const result = getRandomElements(sourceArray, 2)
|
||||||
|
|
||||||
|
expect(result).toHaveLength(2)
|
||||||
|
result.forEach((element) => {
|
||||||
|
expect(sourceArray).toContain(element)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试多次调用应产生不同结果(概率性测试)
|
||||||
|
it('should produce different results on multiple calls', () => {
|
||||||
|
const sourceArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||||
|
const results = new Set()
|
||||||
|
|
||||||
|
// 多次调用并收集结果
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const result = getRandomElements(sourceArray, 5).sort().join(',')
|
||||||
|
results.add(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 虽然有极小概率会相同,但大多数情况下应该有不同的结果
|
||||||
|
expect(results.size).toBeGreaterThan(1)
|
||||||
|
})
|
||||||
|
// 多次调用,每个元素抽中的概率基本上相等
|
||||||
|
it('should have approximately equal probabilities for each element', () => {
|
||||||
|
const sourceArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||||
|
const times = 200000 // 次数
|
||||||
|
const count = 5 // 抽奖个数
|
||||||
|
const expectedProbability = count / sourceArray.length
|
||||||
|
const elementCounts = new Map()
|
||||||
|
|
||||||
|
// 多次调用并统计元素出现的次数
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
const result = getRandomElements(sourceArray, count)
|
||||||
|
result.forEach((element) => {
|
||||||
|
const count = elementCounts.get(element) || 0
|
||||||
|
elementCounts.set(element, count + 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
elementCounts.forEach((value) => {
|
||||||
|
// 验证每个元素出现的概率接近相等
|
||||||
|
const probability = value / times
|
||||||
|
expect(probability).toBeCloseTo(expectedProbability, 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// test axios request
|
|
||||||
import Request from '@/api/request';
|
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
|
||||||
import { mount, flushPromises } from '@vue/test-utils';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const fn = vi.fn();
|
|
||||||
const mockRes = {
|
|
||||||
data: {
|
|
||||||
code: 200,
|
|
||||||
success: true,
|
|
||||||
message: 'success',
|
|
||||||
data: {
|
|
||||||
name: 'test',
|
|
||||||
age: 18,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
fn(mockRes);
|
|
||||||
fn.mock.calls[0] === [mockRes];
|
|
||||||
|
|
||||||
describe('Request', () => {
|
|
||||||
it('should return data when request success', async () => {
|
|
||||||
const request = new Request();
|
|
||||||
const res = await request({
|
|
||||||
url: '/test',
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
expect(res).toEqual(mockRes.data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
22
build/syncVersion.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// 读取 package.json 版本号
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
||||||
|
const version = packageJson.version;
|
||||||
|
|
||||||
|
// 读取并更新 tauri.conf.json
|
||||||
|
const tauriConfPath = path.join(__dirname, '..', 'src-tauri', 'tauri.conf.json');
|
||||||
|
const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf8'));
|
||||||
|
|
||||||
|
// 更新版本号
|
||||||
|
tauriConf.version = version;
|
||||||
|
|
||||||
|
// 写回 tauri.conf.json
|
||||||
|
fs.writeFileSync(tauriConfPath, JSON.stringify(tauriConf, null, 2));
|
||||||
|
|
||||||
|
console.log(`Tauri 配置版本号已同步至: ${version}`);
|
||||||
21
components.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://shadcn-vue.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"typescript": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/style.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"composables": "@/composables"
|
||||||
|
},
|
||||||
|
"registries": {}
|
||||||
|
}
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import antfu from '@antfu/eslint-config'
|
import antfu from '@antfu/eslint-config'
|
||||||
|
|
||||||
export default antfu(
|
export default antfu(
|
||||||
{
|
{
|
||||||
ignores: ['**/node_modules', '**/public', '**/dist', '**/package.json', '**/*.yaml', '**/.gitignore', '**/.env*', '**/tsconfig*'],
|
ignores: ['**/node_modules', '**/build', '**/.husky', '**/public', '**/dist', '**/package.json', '**/*.yaml', '**/.gitignore', '**/.env*', '**/tsconfig*', '**/*.config.js'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'no-console': 'warn',
|
||||||
|
'no-debugger': 'warn',
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
133
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "log-lottery",
|
"name": "log-lottery",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.4",
|
"version": "0.5.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -9,78 +9,105 @@
|
|||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
"build:pre": "vue-tsc --noEmit && vite build --mode prebuild",
|
"build:pre": "vue-tsc --noEmit && vite build --mode prebuild",
|
||||||
"build:file": "vue-tsc --noEmit && vite build --mode file",
|
"build:file": "vue-tsc --noEmit && vite build --mode file",
|
||||||
|
"tauri": "node ./build/syncVersion.js && tauri",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint ./src",
|
"lint": "eslint ./src",
|
||||||
"lint:fix": "eslint ./src --fix"
|
"lint:fix": "eslint ./src --fix",
|
||||||
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tweenjs/tween.js": "^23.1.2",
|
"@tweenjs/tween.js": "23.1.2",
|
||||||
"@vueuse/core": "^11.3.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.13.2",
|
||||||
"canvas-confetti": "^1.9.3",
|
"canvas-confetti": "^1.9.4",
|
||||||
"dayjs": "^1.11.13",
|
"class-variance-authority": "^0.7.1",
|
||||||
"github-markdown-css": "^5.8.0",
|
"clsx": "^2.1.1",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
|
"dexie": "^4.2.1",
|
||||||
|
"github-markdown-css": "^5.8.1",
|
||||||
|
"gsap": "^3.14.2",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
|
"lodash-es": "^4.17.22",
|
||||||
|
"lucide-vue-next": "^0.562.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"pinia": "^2.2.6",
|
"masonry-layout": "^4.2.2",
|
||||||
|
"pinia": "^3.0.4",
|
||||||
"pinia-plugin-persist": "^1.0.0",
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
"reka-ui": "^2.7.0",
|
||||||
"sparticles": "^1.3.1",
|
"sparticles": "^1.3.1",
|
||||||
"three": "^0.166.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"three-css3d": "^1.0.6",
|
"three": "0.166.0",
|
||||||
"vue": "^3.5.13",
|
"three-css3d": "1.0.6",
|
||||||
"vue-dompurify-html": "^5.2.0",
|
"uuid": "^13.0.0",
|
||||||
"vue-i18n": "^10.0.4",
|
"vue": "^3.5.26",
|
||||||
"vue-router": "^4.5.0",
|
"vue-dompurify-html": "^5.3.0",
|
||||||
|
"vue-draggable-plus": "^0.6.0",
|
||||||
|
"vue-i18n": "^11.2.7",
|
||||||
|
"vue-router": "^4.6.4",
|
||||||
|
"vue-sonner": "^2.0.9",
|
||||||
"vue-toast-notification": "^3",
|
"vue-toast-notification": "^3",
|
||||||
"vue3-colorpicker": "^2.3.0",
|
"vue3-colorpicker": "^2.3.0",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^3.23.8"
|
"zod": "^4.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^3.9.2",
|
"@antfu/eslint-config": "^6.7.3",
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.3.3",
|
||||||
"@eslint/js": "^9.15.0",
|
"@eslint/js": "^9.39.2",
|
||||||
"@iconify-json/ep": "^1.2.1",
|
"@iconify-json/ep": "^1.2.3",
|
||||||
"@iconify-json/fluent": "^1.2.8",
|
"@iconify-json/fluent": "^1.2.36",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
|
"@tauri-apps/cli": "^2.9.6",
|
||||||
"@testing-library/vue": "^8.1.0",
|
"@testing-library/vue": "^8.1.0",
|
||||||
"@types/canvas-confetti": "^1.6.4",
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/node": "^22.9.4",
|
"@types/masonry-layout": "^4.2.8",
|
||||||
"@types/three": "^0.166.0",
|
"@types/node": "^25.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
"@types/three": "0.166.0",
|
||||||
"@typescript-eslint/parser": "^8.16.0",
|
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
||||||
"@vitejs/plugin-legacy": "^6.0.0",
|
"@typescript-eslint/parser": "^8.50.1",
|
||||||
"@vitejs/plugin-vue": "^5.2.0",
|
"@vitejs/plugin-legacy": "^7.2.1",
|
||||||
"@vitest/ui": "^2.1.5",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
|
"@vitest/ui": "^4.0.16",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"autoprefixer": "^10.4.20",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"daisyui": "^4.12.14",
|
"autoprefixer": "^10.4.23",
|
||||||
"eslint": "^9.15.0",
|
"baseline-browser-mapping": "^2.9.11",
|
||||||
"eslint-plugin-vue": "^9.31.0",
|
"child_process": "^1.0.2",
|
||||||
"globals": "^15.12.0",
|
"daisyui": "^5.5.14",
|
||||||
"happy-dom": "^15.11.6",
|
"eslint": "^9.39.2",
|
||||||
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
|
"fast-glob": "^3.3.3",
|
||||||
|
"globals": "^16.5.0",
|
||||||
|
"happy-dom": "^20.0.11",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^27.3.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.5.6",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^6.0.5",
|
||||||
"sass": "^1.81.0",
|
"sass": "^1.97.1",
|
||||||
"sass-loader": "^16.0.3",
|
"sass-loader": "^16.0.6",
|
||||||
"tailwindcss": "^3.4.15",
|
"tailwindcss": "^4.1.18",
|
||||||
"terser": "^5.36.0",
|
"terser": "^5.44.1",
|
||||||
"typescript": "5.5.3",
|
"tw-animate-css": "^1.4.0",
|
||||||
"unplugin-auto-import": "^0.18.5",
|
"typescript": "~5.9.3",
|
||||||
"unplugin-icons": "^0.20.1",
|
"unplugin-auto-import": "^20.3.0",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-icons": "^22.5.0",
|
||||||
"vite": "^5.4.11",
|
"unplugin-vue-components": "^30.0.0",
|
||||||
|
"vite": "^7.3.0",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-inspect": "^0.8.8",
|
"vite-plugin-inspect": "^11.3.3",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vite-plugin-vue-devtools": "^7.6.4",
|
"vite-plugin-vue-devtools": "^8.0.5",
|
||||||
"vitest": "^2.1.5",
|
"vitest": "^4.0.16",
|
||||||
"vue-tsc": "^2.1.10"
|
"vue-tsc": "^3.2.1"
|
||||||
}
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.x"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.26.1"
|
||||||
}
|
}
|
||||||
9641
pnpm-lock.yaml
generated
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
/gen/schemas
|
||||||
5005
src-tauri/Cargo.lock
generated
Normal file
25
src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "app"
|
||||||
|
version = "0.5.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
license = ""
|
||||||
|
repository = ""
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.77.2"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "app_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2.5.3", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
log = "0.4"
|
||||||
|
tauri = { version = "2.9.4", features = [] }
|
||||||
|
tauri-plugin-log = "2"
|
||||||
3
src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
11
src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "enables the default permissions",
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"core:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src-tauri/icons/64x64.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
</adaptive-icon>
|
||||||
BIN
src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 106 KiB |
BIN
src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#fff</color>
|
||||||
|
</resources>
|
||||||
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
src-tauri/icons/ios/AppIcon-20x20@1x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src-tauri/icons/ios/AppIcon-20x20@2x-1.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src-tauri/icons/ios/AppIcon-20x20@2x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src-tauri/icons/ios/AppIcon-20x20@3x.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
src-tauri/icons/ios/AppIcon-29x29@1x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src-tauri/icons/ios/AppIcon-29x29@2x-1.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src-tauri/icons/ios/AppIcon-29x29@2x.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src-tauri/icons/ios/AppIcon-29x29@3x.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
src-tauri/icons/ios/AppIcon-40x40@1x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src-tauri/icons/ios/AppIcon-40x40@2x-1.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
src-tauri/icons/ios/AppIcon-40x40@2x.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
src-tauri/icons/ios/AppIcon-40x40@3x.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src-tauri/icons/ios/AppIcon-512@2x.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
src-tauri/icons/ios/AppIcon-60x60@2x.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src-tauri/icons/ios/AppIcon-60x60@3x.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src-tauri/icons/ios/AppIcon-76x76@1x.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
src-tauri/icons/ios/AppIcon-76x76@2x.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
16
src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
pub fn run() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.setup(|app| {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
app.handle().plugin(
|
||||||
|
tauri_plugin_log::Builder::default()
|
||||||
|
.level(log::LevelFilter::Info)
|
||||||
|
.build(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
6
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
app_lib::run();
|
||||||
|
}
|
||||||
37
src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
|
"productName": "log-lottery",
|
||||||
|
"version": "0.5.0",
|
||||||
|
"identifier": "to2026.xyz",
|
||||||
|
"build": {
|
||||||
|
"frontendDist": "../dist",
|
||||||
|
"devUrl": "http://localhost:6719/log-lottery",
|
||||||
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
"beforeBuildCommand": "pnpm build"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "log-lottery",
|
||||||
|
"width": 800,
|
||||||
|
"height": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/App.vue
@@ -1,86 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PlayMusic from '@/components/PlayMusic/index.vue'
|
import { provide } from 'vue'
|
||||||
import useStore from '@/store'
|
import { loadingKey, loadingState } from '@/components/Loading'
|
||||||
import { themeChange } from '@/utils'
|
// import PlayMusic from '@/components/PlayMusic/index.vue'
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
provide(loadingKey, loadingState)
|
||||||
const globalConfig = useStore().globalConfig
|
|
||||||
const prizeConfig = useStore().prizeConfig
|
|
||||||
const system = useStore().system
|
|
||||||
const { getTheme: localTheme } = storeToRefs(globalConfig)
|
|
||||||
const { getPrizeConfig: prizeList } = storeToRefs(prizeConfig)
|
|
||||||
|
|
||||||
const tipDialog = ref()
|
|
||||||
|
|
||||||
// 设置当前奖列表
|
|
||||||
function setCurrentPrize() {
|
|
||||||
if (prizeList.value.length <= 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (let i = 0; i < prizeList.value.length; i++) {
|
|
||||||
if (!prizeList.value[i].isUsed) {
|
|
||||||
prizeConfig.setCurrentPrize(prizeList.value[i])
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 判断是否手机端访问
|
|
||||||
function judgeMobile() {
|
|
||||||
const ua = navigator.userAgent
|
|
||||||
const isAndroid = ua.includes('Android') || ua.includes('Adr')
|
|
||||||
const isIOS = !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
|
|
||||||
|
|
||||||
system.setIsMobile(isAndroid || isIOS)
|
|
||||||
|
|
||||||
return isAndroid || isIOS
|
|
||||||
}
|
|
||||||
// 判断是否chrome或者edge访问
|
|
||||||
function judgeChromeOrEdge() {
|
|
||||||
const ua = navigator.userAgent
|
|
||||||
const isChrome = ua.includes('Chrome')
|
|
||||||
const isEdge = ua.includes('Edg')
|
|
||||||
|
|
||||||
system.setIsChrome(isChrome)
|
|
||||||
|
|
||||||
return isChrome || isEdge
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
themeChange(localTheme.value.name)
|
|
||||||
setCurrentPrize()
|
|
||||||
if (judgeMobile() || !judgeChromeOrEdge()) {
|
|
||||||
tipDialog.value.showModal()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<dialog id="my_modal_1" ref="tipDialog" class="border-none modal">
|
|
||||||
<div class="modal-box">
|
|
||||||
<h3 class="text-lg font-bold">
|
|
||||||
{{ t('dialog.titleTip') }}
|
|
||||||
</h3>
|
|
||||||
<p v-if="judgeMobile()" class="py-4">
|
|
||||||
{{ t('dialog.dialogPCWeb') }}
|
|
||||||
</p>
|
|
||||||
<p v-if=" !judgeChromeOrEdge()" class="py-4">
|
|
||||||
{{ t('dialog.dialogLatestBrowser') }}
|
|
||||||
</p>
|
|
||||||
<div class="modal-action">
|
|
||||||
<form method="dialog" class="flex justify-start w-full gap-3">
|
|
||||||
<!-- if there is a button in form, it will close the modal -->
|
|
||||||
<button class="btn">
|
|
||||||
{{ t('button.confirm') }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</dialog>
|
|
||||||
<router-view />
|
<router-view />
|
||||||
<PlayMusic class="absolute right-0 bottom-1/2" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||