Browse Source

refactor(导航栏): 统一使用自定义navbar组件替换uv-navbar

将多个页面中的uv-navbar替换为自定义的navbar组件,以保持导航栏风格一致。同时,添加了暗色主题支持,优化了主题切换功能,并更新了相关文档和样式文件。

主要变更:
- 在多个页面中使用navbar组件替换uv-navbar
- 添加暗色主题支持,优化主题切换功能
- 更新uni.scss和store.js,增加主题相关变量和状态管理
- 新增ThemeProvider组件和themeMode混入器
- 更新README.md和dark-mode-guide.md文档
master
前端-胡立永 4 weeks ago
parent
commit
cfe59d50bf
72 changed files with 1319 additions and 523 deletions
  1. +28
    -0
      .cursor/rules/a.mdc
  2. +156
    -0
      components/theme/DarkModeStyles.vue
  3. +32
    -0
      components/theme/ThemeProvider.vue
  4. +159
    -0
      doc/dark-mode-guide.md
  5. BIN
      doc/布丁笔录小程序-切图x3/.DS_Store
  6. BIN
      doc/布丁笔录小程序-切图x3/1.png
  7. BIN
      doc/布丁笔录小程序-切图x3/2.png
  8. BIN
      doc/布丁笔录小程序-切图x3/3.png
  9. BIN
      doc/布丁笔录小程序-切图x3/Group 2681.png
  10. BIN
      doc/布丁笔录小程序-切图x3/Group 2878-1.png
  11. BIN
      doc/布丁笔录小程序-切图x3/Group 2878-2.png
  12. BIN
      doc/布丁笔录小程序-切图x3/Group 2878-3.png
  13. BIN
      doc/布丁笔录小程序-切图x3/Group 2878.png
  14. BIN
      doc/布丁笔录小程序-切图x3/image-1.png
  15. BIN
      doc/布丁笔录小程序-切图x3/image-2.png
  16. BIN
      doc/布丁笔录小程序-切图x3/image-3.png
  17. BIN
      doc/布丁笔录小程序-切图x3/image-4.png
  18. BIN
      doc/布丁笔录小程序-切图x3/image-5.png
  19. BIN
      doc/布丁笔录小程序-切图x3/image-6.png
  20. BIN
      doc/布丁笔录小程序-切图x3/image-7.png
  21. BIN
      doc/布丁笔录小程序-切图x3/image.png
  22. BIN
      doc/布丁笔录小程序-切图x3/下.png
  23. BIN
      doc/布丁笔录小程序-切图x3/书城-1.png
  24. BIN
      doc/布丁笔录小程序-切图x3/书城-2.png
  25. BIN
      doc/布丁笔录小程序-切图x3/书城.png
  26. BIN
      doc/布丁笔录小程序-切图x3/书架.png
  27. BIN
      doc/布丁笔录小程序-切图x3/任务中心.png
  28. BIN
      doc/布丁笔录小程序-切图x3/修改信息.png
  29. BIN
      doc/布丁笔录小程序-切图x3/全部公告.png
  30. BIN
      doc/布丁笔录小程序-切图x3/加.png
  31. BIN
      doc/布丁笔录小程序-切图x3/加载.png
  32. BIN
      doc/布丁笔录小程序-切图x3/取消删除.png
  33. BIN
      doc/布丁笔录小程序-切图x3/右.png
  34. BIN
      doc/布丁笔录小程序-切图x3/失败.png
  35. BIN
      doc/布丁笔录小程序-切图x3/布丁小说logo.png
  36. BIN
      doc/布丁笔录小程序-切图x3/成功.png
  37. BIN
      doc/布丁笔录小程序-切图x3/我的.png
  38. BIN
      doc/布丁笔录小程序-切图x3/我的等级.png
  39. BIN
      doc/布丁笔录小程序-切图x3/我的评论.png
  40. BIN
      doc/布丁笔录小程序-切图x3/投推荐票.png
  41. BIN
      doc/布丁笔录小程序-切图x3/推荐票.png
  42. BIN
      doc/布丁笔录小程序-切图x3/申请成为创作者.png
  43. BIN
      doc/布丁笔录小程序-切图x3/礼物盒.png
  44. BIN
      doc/布丁笔录小程序-切图x3/移除书架.png
  45. BIN
      doc/布丁笔录小程序-切图x3/移除全部.png
  46. BIN
      doc/布丁笔录小程序-切图x3/联系客服.png
  47. BIN
      doc/布丁笔录小程序-切图x3/读者榜单.png
  48. BIN
      doc/布丁笔录小程序-切图x3/退出登录.png
  49. BIN
      doc/布丁笔录小程序-切图x3/钱包流水.png
  50. BIN
      doc/布丁笔录小程序-切图x3/首页.png
  51. BIN
      doc/布丁笔录小程序-切图x3/默认头像.png
  52. +7
    -1
      main.js
  53. +18
    -0
      mixins/themeMode.js
  54. +141
    -0
      pages_order/components/novel/README.md
  55. +159
    -106
      pages_order/components/novel/chapterPopup.vue
  56. +159
    -0
      pages_order/components/novel/subscriptionPopup.vue
  57. +2
    -7
      pages_order/novel/Giftbox.vue
  58. +1
    -1
      pages_order/novel/Giftpurchases.vue
  59. +2
    -2
      pages_order/novel/Modifyinformation.vue
  60. +1
    -1
      pages_order/novel/ReaderAchievement.vue
  61. +1
    -2
      pages_order/novel/Respondcomments.vue
  62. +1
    -1
      pages_order/novel/Review.vue
  63. +1
    -2
      pages_order/novel/SubscriptionInformation.vue
  64. +1
    -1
      pages_order/novel/Tipping.vue
  65. +2
    -6
      pages_order/novel/Translation.vue
  66. +1
    -6
      pages_order/novel/Walletflow.vue
  67. +187
    -169
      pages_order/novel/comments.vue
  68. +1
    -8
      pages_order/novel/createNovel.vue
  69. +99
    -94
      pages_order/novel/creator.vue
  70. +100
    -116
      pages_order/novel/readnovels.vue
  71. +31
    -0
      store/store.js
  72. +29
    -0
      uni.scss

+ 28
- 0
.cursor/rules/a.mdc View File

@ -0,0 +1,28 @@
---
description:
globs:
alwaysApply: true
---
当前项目是uniapp开发微信小程序
每次编写代码时,请你先查看我的整个目录,提供的哪些文件、组件、功能、API接口,了解我的代码风格,写出和我风格一致的代码
## 项目规则
1、将列表以及列表中的元素封装成组件,并使用组件的方式来组织项目。使他可以复用
2、项目中使用到的功能优先去查看uni_modules里面的组件列表,若没有则自己封装。
### 项目结构
- components 放主包组件
- pages 只放主页面
- pages_order 放其他页面
- components 放分包组件
css语法按照scss的嵌套写法
白天/黑夜模式:白天模式保持当前的样式,黑夜模式颜色变暗
主题色:#0A2463

+ 156
- 0
components/theme/DarkModeStyles.vue View File

@ -0,0 +1,156 @@
<template>
<!-- 空模板仅提供样式 -->
</template>
<script>
export default {
name: 'DarkModeStyles'
}
</script>
<style lang="scss">
/* 通用暗色模式样式 */
.dark-mode {
/* 文本容器 */
.text-container {
color: $dark-text-color-secondary;
.title {
color: $dark-text-color-primary;
}
.subtitle {
color: $dark-text-color-secondary;
}
.description, .content {
color: $dark-text-color-secondary;
}
.hint, .caption {
color: $dark-text-color-tertiary;
}
}
/* 卡片组件 */
.card {
background-color: $dark-bg-color-secondary;
box-shadow: $dark-shadow;
border-color: $dark-border-color;
.card-title {
color: $dark-text-color-primary;
}
.card-content {
color: $dark-text-color-secondary;
}
.card-footer {
border-top-color: $dark-border-color;
}
}
/* 按钮组件 */
.btn {
&.primary {
background-color: $dark-accent-color;
}
&.secondary {
background-color: $dark-bg-color-tertiary;
color: $dark-text-color-primary;
}
&.outline {
background-color: transparent;
color: $dark-accent-color;
border-color: $dark-accent-color;
}
}
/* 导航栏 */
.navbar {
background-color: $dark-bg-color-secondary;
box-shadow: $dark-shadow;
.navbar-title {
color: $dark-text-color-primary;
}
.navbar-item {
color: $dark-text-color-secondary;
&.active {
color: $dark-accent-color;
}
}
}
/* 表单元素 */
.form-item {
.label {
color: $dark-text-color-secondary;
}
.input {
background-color: $dark-bg-color-tertiary;
color: $dark-text-color-primary;
border-color: $dark-border-color;
&::placeholder {
color: $dark-text-color-tertiary;
}
}
.hint {
color: $dark-text-color-tertiary;
}
}
/* 列表组件 */
.list {
.list-item {
border-bottom-color: $dark-border-color;
.list-title {
color: $dark-text-color-primary;
}
.list-desc {
color: $dark-text-color-secondary;
}
}
}
/* 分割线 */
.divider {
background-color: $dark-border-color;
}
/* 标签 */
.tag {
background-color: $dark-bg-color-tertiary;
color: $dark-text-color-secondary;
&.primary {
background-color: rgba($dark-accent-color, 0.3);
color: $dark-accent-color;
}
}
/* 底部操作栏 */
.bottom-action-bar {
background-color: $dark-bg-color-secondary;
box-shadow: $dark-shadow;
.action-btn {
color: $dark-text-color-secondary;
&.active {
color: $dark-accent-color;
}
}
}
}
</style>

+ 32
- 0
components/theme/ThemeProvider.vue View File

@ -0,0 +1,32 @@
<template>
<view :class="{'dark-mode': isDarkMode}" class="theme-provider">
<slot></slot>
</view>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
name: 'ThemeProvider',
computed: {
...mapGetters(['isDarkMode', 'currentTheme'])
},
methods: {
...mapMutations(['toggleThemeMode', 'setThemeMode'])
}
}
</script>
<style lang="scss">
.theme-provider {
/* 确保占满容器,但不影响布局 */
width: 100%;
height: 100%;
/* 适配应用到深色模式的样式 */
&.dark-mode {
/* 这里不添加具体样式,由子元素通过对应的类应用样式 */
}
}
</style>

+ 159
- 0
doc/dark-mode-guide.md View File

@ -0,0 +1,159 @@
# 暗色主题使用指南
本文档介绍如何在项目中使用暗色主题功能。
## 概述
我们的应用支持白天/黑夜两种主题模式,用户可以根据自己的偏好切换。主题的状态保存在Vuex中,并且会持久化保存在本地存储中,以便下次打开应用时恢复用户的偏好设置。
## 使用方法
### 1. 在组件中使用主题混入器
我们提供了一个方便的混入器,可以轻松地在任何组件中使用主题相关的功能:
```js
// 1. 导入主题混入器
import themeMixin from '@/mixins/themeMode.js'
export default {
// 2. 在组件中注册混入器
mixins: [themeMixin],
methods: {
someMethod() {
// 3. 直接使用混入器提供的属性和方法
console.log(this.isDarkMode) // 是否为暗色模式
console.log(this.currentTheme) // 当前主题模式:'light'或'dark'
// 切换主题
this.toggleThemeMode()
// 设置为特定主题
this.setThemeMode('dark') // 或 'light'
}
}
}
```
### 2. 为组件添加暗色主题样式
我们在`uni.scss`中定义了一系列暗色主题相关的变量,您可以在组件的样式中使用这些变量:
```scss
/* 示例组件的样式 */
.my-component {
background-color: #fff;
color: #333;
/* 添加主题过渡效果 */
@extend .theme-transition;
/* 定义暗色主题下的样式 */
.dark-mode & {
background-color: $dark-bg-color;
color: $dark-text-color-primary;
}
}
```
### 3. 使用主题组件
我们还提供了两个便捷的组件:
#### ThemeProvider
ThemeProvider是一个包装组件,它会自动传递当前的主题模式,并提供暗色主题的根容器:
```html
<template>
<ThemeProvider>
<!-- 您的组件内容 -->
<view class="my-content">
这里的内容会自动响应主题变化
</view>
</ThemeProvider>
</template>
<script>
import ThemeProvider from '@/components/theme/ThemeProvider.vue'
export default {
components: {
ThemeProvider
}
}
</script>
```
#### DarkModeStyles
DarkModeStyles是一个提供通用暗色主题样式的无渲染组件,可以导入到您的页面中使用:
```html
<template>
<view>
<!-- 这里不会渲染任何内容,但会应用样式 -->
<DarkModeStyles />
<!-- 您的组件内容 -->
<view class="card">
<view class="card-title">标题</view>
<view class="card-content">内容</view>
</view>
</view>
</template>
<script>
import DarkModeStyles from '@/components/theme/DarkModeStyles.vue'
export default {
components: {
DarkModeStyles
}
}
</script>
```
## 可用的主题变量
以下是在`uni.scss`中定义的暗色主题相关变量,您可以在组件样式中使用:
```scss
/* 背景色 */
$dark-bg-color: #1a1a1a; // 主背景色
$dark-bg-color-secondary: #222; // 次要背景色
$dark-bg-color-tertiary: #333; // 第三级背景色
/* 文本颜色 */
$dark-text-color-primary: #eee; // 主要文本颜色
$dark-text-color-secondary: #bbb; // 次要文本颜色
$dark-text-color-tertiary: #999; // 第三级文本颜色
/* 边框和阴影 */
$dark-border-color: #444; // 边框颜色
$dark-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2); // 阴影
/* 强调色 */
$dark-accent-color: #4a90e2; // 强调色
/* 过渡动画时间 */
$theme-transition-duration: 0.3s; // 主题切换过渡时间
```
## 最佳实践
1. 总是为可能受主题影响的元素添加`theme-transition`类,以确保主题切换时有平滑的过渡效果
2. 使用预定义的变量而不是硬编码颜色值,以保持整个应用的视觉一致性
3. 在设计组件时考虑两种主题模式,确保在暗色模式下内容仍然清晰可见
4. 对于特定于组件的变量,可以基于全局主题变量派生:
```scss
$my-component-bg: $dark-bg-color;
$my-component-text: $dark-text-color-primary;
```
## 注意事项
- 当用户切换主题时,会立即应用新的主题并保存到本地存储中
- 主题状态在整个应用中共享,任何组件都可以访问和修改它
- 对于复杂的组件,考虑分别定义亮色和暗色两套图标或图片资源

BIN
doc/布丁笔录小程序-切图x3/.DS_Store View File


BIN
doc/布丁笔录小程序-切图x3/1.png View File

Before After
Width: 355  |  Height: 354  |  Size: 130 KiB

BIN
doc/布丁笔录小程序-切图x3/2.png View File

Before After
Width: 355  |  Height: 354  |  Size: 129 KiB

BIN
doc/布丁笔录小程序-切图x3/3.png View File

Before After
Width: 355  |  Height: 354  |  Size: 130 KiB

BIN
doc/布丁笔录小程序-切图x3/Group 2681.png View File

Before After
Width: 85  |  Height: 84  |  Size: 2.4 KiB

BIN
doc/布丁笔录小程序-切图x3/Group 2878-1.png View File

Before After
Width: 78  |  Height: 78  |  Size: 1.5 KiB

BIN
doc/布丁笔录小程序-切图x3/Group 2878-2.png View File

Before After
Width: 78  |  Height: 78  |  Size: 1.3 KiB

BIN
doc/布丁笔录小程序-切图x3/Group 2878-3.png View File

Before After
Width: 78  |  Height: 78  |  Size: 1.3 KiB

BIN
doc/布丁笔录小程序-切图x3/Group 2878.png View File

Before After
Width: 78  |  Height: 78  |  Size: 1.3 KiB

BIN
doc/布丁笔录小程序-切图x3/image-1.png View File

Before After
Width: 55  |  Height: 55  |  Size: 5.1 KiB

BIN
doc/布丁笔录小程序-切图x3/image-2.png View File

Before After
Width: 55  |  Height: 55  |  Size: 4.8 KiB

BIN
doc/布丁笔录小程序-切图x3/image-3.png View File

Before After
Width: 55  |  Height: 55  |  Size: 4.1 KiB

BIN
doc/布丁笔录小程序-切图x3/image-4.png View File

Before After
Width: 55  |  Height: 55  |  Size: 4.4 KiB

BIN
doc/布丁笔录小程序-切图x3/image-5.png View File

Before After
Width: 55  |  Height: 55  |  Size: 3.2 KiB

BIN
doc/布丁笔录小程序-切图x3/image-6.png View File

Before After
Width: 55  |  Height: 55  |  Size: 3.6 KiB

BIN
doc/布丁笔录小程序-切图x3/image-7.png View File

Before After
Width: 55  |  Height: 55  |  Size: 4.4 KiB

BIN
doc/布丁笔录小程序-切图x3/image.png View File

Before After
Width: 55  |  Height: 55  |  Size: 4.9 KiB

BIN
doc/布丁笔录小程序-切图x3/下.png View File

Before After
Width: 84  |  Height: 84  |  Size: 572 B

BIN
doc/布丁笔录小程序-切图x3/书城-1.png View File

Before After
Width: 85  |  Height: 84  |  Size: 1.4 KiB

BIN
doc/布丁笔录小程序-切图x3/书城-2.png View File

Before After
Width: 139  |  Height: 138  |  Size: 7.7 KiB

BIN
doc/布丁笔录小程序-切图x3/书城.png View File

Before After
Width: 85  |  Height: 84  |  Size: 1.0 KiB

BIN
doc/布丁笔录小程序-切图x3/书架.png View File

Before After
Width: 139  |  Height: 138  |  Size: 7.4 KiB

BIN
doc/布丁笔录小程序-切图x3/任务中心.png View File

Before After
Width: 60  |  Height: 60  |  Size: 954 B

BIN
doc/布丁笔录小程序-切图x3/修改信息.png View File

Before After
Width: 60  |  Height: 60  |  Size: 1.2 KiB

BIN
doc/布丁笔录小程序-切图x3/全部公告.png View File

Before After
Width: 85  |  Height: 84  |  Size: 8.9 KiB

BIN
doc/布丁笔录小程序-切图x3/加.png View File

Before After
Width: 96  |  Height: 96  |  Size: 536 B

BIN
doc/布丁笔录小程序-切图x3/加载.png View File

Before After
Width: 73  |  Height: 72  |  Size: 982 B

BIN
doc/布丁笔录小程序-切图x3/取消删除.png View File

Before After
Width: 61  |  Height: 60  |  Size: 1.2 KiB

BIN
doc/布丁笔录小程序-切图x3/右.png View File

Before After
Width: 48  |  Height: 48  |  Size: 385 B

BIN
doc/布丁笔录小程序-切图x3/失败.png View File

Before After
Width: 72  |  Height: 72  |  Size: 1.0 KiB

BIN
doc/布丁笔录小程序-切图x3/布丁小说logo.png View File

Before After
Width: 373  |  Height: 372  |  Size: 50 KiB

BIN
doc/布丁笔录小程序-切图x3/成功.png View File

Before After
Width: 72  |  Height: 72  |  Size: 1.0 KiB

BIN
doc/布丁笔录小程序-切图x3/我的.png View File

Before After
Width: 139  |  Height: 138  |  Size: 8.5 KiB

BIN
doc/布丁笔录小程序-切图x3/我的等级.png View File

Before After
Width: 48  |  Height: 48  |  Size: 4.6 KiB

BIN
doc/布丁笔录小程序-切图x3/我的评论.png View File

Before After
Width: 60  |  Height: 60  |  Size: 604 B

BIN
doc/布丁笔录小程序-切图x3/投推荐票.png View File

Before After
Width: 48  |  Height: 48  |  Size: 663 B

BIN
doc/布丁笔录小程序-切图x3/推荐票.png View File

Before After
Width: 129  |  Height: 129  |  Size: 18 KiB

BIN
doc/布丁笔录小程序-切图x3/申请成为创作者.png View File

Before After
Width: 60  |  Height: 60  |  Size: 1.4 KiB

BIN
doc/布丁笔录小程序-切图x3/礼物盒.png View File

Before After
Width: 60  |  Height: 60  |  Size: 679 B

BIN
doc/布丁笔录小程序-切图x3/移除书架.png View File

Before After
Width: 61  |  Height: 60  |  Size: 1.1 KiB

BIN
doc/布丁笔录小程序-切图x3/移除全部.png View File

Before After
Width: 61  |  Height: 60  |  Size: 648 B

BIN
doc/布丁笔录小程序-切图x3/联系客服.png View File

Before After
Width: 60  |  Height: 60  |  Size: 1.3 KiB

BIN
doc/布丁笔录小程序-切图x3/读者榜单.png View File

Before After
Width: 366  |  Height: 115  |  Size: 59 KiB

BIN
doc/布丁笔录小程序-切图x3/退出登录.png View File

Before After
Width: 60  |  Height: 60  |  Size: 672 B

BIN
doc/布丁笔录小程序-切图x3/钱包流水.png View File

Before After
Width: 60  |  Height: 60  |  Size: 905 B

BIN
doc/布丁笔录小程序-切图x3/首页.png View File

Before After
Width: 139  |  Height: 138  |  Size: 7.3 KiB

BIN
doc/布丁笔录小程序-切图x3/默认头像.png View File

Before After
Width: 132  |  Height: 132  |  Size: 8.3 KiB

+ 7
- 1
main.js View File

@ -9,7 +9,7 @@ Vue.config.productionTip = false
App.mpType = 'app'
import store from '@/store/store'
import store from '@/store/store.js'
import './config'
import './utils/index.js'
@ -25,6 +25,12 @@ import navbar from '@/components/base/navbar.vue'
Vue.component('configPopup',configPopup)
Vue.component('navbar',navbar)
// 注册全局store
Vue.prototype.$store = store
// 初始化主题模式
store.commit('initThemeMode')
const app = new Vue({
...App,
store,


+ 18
- 0
mixins/themeMode.js View File

@ -0,0 +1,18 @@
import { mapGetters, mapMutations } from 'vuex'
/**
* 主题模式混合器
* 提供了isDarkMode和currentTheme计算属性以及toggleThemeMode和setThemeMode方法
* 使用方法
* 1. 在组件中导入import themeMixin from '@/mixins/themeMode.js'
* 2. 在组件中注册mixins: [themeMixin]
* 3. 然后可以使用this.isDarkMode this.toggleThemeMode()
*/
export default {
computed: {
...mapGetters(['isDarkMode', 'currentTheme'])
},
methods: {
...mapMutations(['toggleThemeMode', 'setThemeMode'])
}
}

+ 141
- 0
pages_order/components/novel/README.md View File

@ -0,0 +1,141 @@
# 小说阅读相关组件
本目录包含小说阅读页面使用的各种公共组件,所有组件都支持暗色主题模式。
## 组件列表
### 1. 订阅弹窗 (subscriptionPopup.vue)
中心弹出的订阅章节提示弹窗。
#### 使用方法
```html
<template>
<subscriptionPopup ref="subscriptionPopup" @subscribe="handleSubscribe"/>
</template>
<script>
import subscriptionPopup from 'pages_order/components/novel/subscriptionPopup.vue'
export default {
components: {
subscriptionPopup
},
methods: {
showSubscriptionPopup() {
this.$refs.subscriptionPopup.open()
},
handleSubscribe() {
// 处理订阅事件
}
}
}
</script>
```
### 2. 支付弹窗 (paymentPopup.vue)
从底部弹出的支付方式选择弹窗。
#### 使用方法
```html
<template>
<paymentPopup ref="paymentPopup" @subscribe="handleSubscribe" @close="handleClose"/>
</template>
<script>
import paymentPopup from 'pages_order/components/novel/paymentPopup.vue'
export default {
components: {
paymentPopup
},
methods: {
showPaymentPopup() {
this.$refs.paymentPopup.open()
},
handleSubscribe() {
// 处理订阅事件
},
handleClose() {
// 处理关闭事件
}
}
}
</script>
```
### 3. 章节目录弹窗 (chapterPopup.vue)
显示小说章节列表的弹窗。
#### 使用方法
```html
<template>
<chapterPopup ref="chapterPopup"/>
</template>
<script>
import chapterPopup from 'pages_order/components/novel/chapterPopup.vue'
export default {
components: {
chapterPopup
},
methods: {
showChapterList() {
this.$refs.chapterPopup.open()
}
}
}
</script>
```
### 4. 评分弹窗 (novelVotePopup.vue)
用于评价小说的弹窗组件。
## 暗色主题支持
所有组件都支持暗色主题模式,当系统切换到暗色模式或用户手动切换时,组件会自动应用暗色样式。
### 主题切换
主题的状态保存在Vuex中,可以通过以下方式切换:
```js
// 切换主题模式
this.$store.commit('toggleThemeMode')
// 设置为特定模式
this.$store.commit('setThemeMode', 'dark') // 或 'light'
// 获取当前主题模式
const isDarkMode = this.$store.getters.isDarkMode
```
### 使用主题混入器
为了方便在组件中使用主题相关功能,可以引入主题混入器:
```js
import themeMixin from '@/mixins/themeMode.js'
export default {
mixins: [themeMixin],
// 然后可以直接使用以下属性和方法
created() {
console.log(this.isDarkMode) // 是否为暗色模式
console.log(this.currentTheme) // 'light' 或 'dark'
// 切换主题
this.toggleThemeMode()
}
}
```
详细的文档请参考 `doc/dark-mode-guide.md`

+ 159
- 106
pages_order/components/novel/chapterPopup.vue View File

@ -1,29 +1,34 @@
<template>
<uv-popup ref="popup" :round="30" :customStyle="{height: '70vh'}">
<view class="catalog-popup-fullscreen">
<view class="catalog-header">
<view class="header-left" @click.stop="close">
<uv-icon name="arrow-down" size="46" color="#333"/>
</view>
<view class="header-title">目录</view>
<view class="header-right" @click.stop="orderAsc = !orderAsc">倒序</view>
</view>
<scroll-view scroll-y class="catalog-list">
<view v-for="(item, idx) in (orderAsc ? chapterList : [...chapterList].reverse())" :key="item.id"
@click="selectChapter(orderAsc ? idx : chapterList.length - 1 - idx)"
:class="['catalog-item', {active: (orderAsc ? idx : chapterList.length - 1 - idx) === currentIndex}]">
<view class="item-main">
<text class="item-title">{{ item.title }}</text>
<text v-if="item.vip" class="vip-tag">付费</text>
<view class="chapter-popup-container" :class="{'dark-mode': isDarkMode}">
<uv-popup ref="popup" :round="30" :customStyle="popupStyle">
<view class="catalog-popup-fullscreen theme-transition">
<view class="catalog-header theme-transition">
<view class="header-left" @click.stop="close">
<uv-icon name="arrow-down" size="46" :color="isDarkMode ? '#ccc' : '#333'"/>
</view>
<view class="header-title theme-transition">目录</view>
<view class="header-right theme-transition" @click.stop="orderAsc = !orderAsc">倒序</view>
</view>
</scroll-view>
</view>
</uv-popup>
<scroll-view scroll-y class="catalog-list">
<view v-for="(item, idx) in (orderAsc ? chapterList : [...chapterList].reverse())" :key="item.id"
@click="selectChapter(orderAsc ? idx : chapterList.length - 1 - idx)"
:class="['catalog-item theme-transition', {active: (orderAsc ? idx : chapterList.length - 1 - idx) === currentIndex}]">
<view class="item-main">
<text class="item-title theme-transition">{{ item.title }}</text>
<text v-if="item.vip" class="vip-tag theme-transition">付费</text>
</view>
</view>
</scroll-view>
</view>
</uv-popup>
</view>
</template>
<script>
import themeMixin from '@/mixins/themeMode.js'
export default {
mixins: [themeMixin],
data() {
return {
chapterCount: 2814,
@ -122,15 +127,20 @@
]
}
},
computed: {
popupStyle() {
return {
height: '70vh',
background: this.isDarkMode ? '#222' : '#fff'
}
}
},
methods: {
open() {
this.$refs.popup.open('bottom')
},
close() {
this.$refs.popup.close()
},
selectChapter(){
},
selectChapter(idx) {
this.currentIndex = idx
@ -142,94 +152,137 @@
</script>
<style scoped lang="scss">
.catalog-popup-fullscreen {
position: relative;
height: 100%;
width: 100vw;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
.catalog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
height: 96rpx;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
.chapter-popup-container {
.catalog-popup-fullscreen {
position: relative;
height: 100%;
width: 100vw;
background: #fff;
z-index: 2;
}
.header-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
}
.header-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #222;
}
.header-right {
color: #223a7a;
font-size: 28rpx;
font-weight: 500;
min-width: 80rpx;
text-align: right;
}
.catalog-list {
height: calc(100% - 180rpx);
overflow: auto;
padding-bottom: 40rpx;
}
.catalog-item {
padding: 0 32rpx;
min-height: 80rpx;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 1px solid #f5f5f5;
background: #fff;
color: #222;
font-size: 30rpx;
position: relative;
}
.catalog-item.active {
color: #ff5a5f;
background: #fff7f7;
}
.item-main {
display: flex;
align-items: center;
gap: 16rpx;
}
.item-title {
font-size: 30rpx;
overflow: hidden;
.catalog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
height: 96rpx;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
background: #fff;
z-index: 2;
}
.header-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
}
.header-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #222;
}
.header-right {
color: #223a7a;
font-size: 28rpx;
font-weight: 500;
min-width: 80rpx;
text-align: right;
}
.catalog-list {
height: calc(100% - 180rpx);
overflow: auto;
padding-bottom: 40rpx;
}
.catalog-item {
padding: 0 32rpx;
min-height: 80rpx;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 1px solid #f5f5f5;
background: #fff;
color: #222;
font-size: 30rpx;
position: relative;
}
.catalog-item.active {
color: #ff5a5f;
background: #fff7f7;
}
.item-main {
display: flex;
align-items: center;
gap: 16rpx;
}
.item-title {
font-size: 30rpx;
}
.vip-tag {
background: #ffe1b2;
color: #ff9900;
border-radius: 20rpx;
font-size: 24rpx;
padding: 2rpx 18rpx;
margin-left: 16rpx;
}
}
.vip-tag {
background: #ffe1b2;
color: #ff9900;
border-radius: 20rpx;
font-size: 24rpx;
padding: 2rpx 18rpx;
margin-left: 16rpx;
}
/* 暗色主题样式 */
.chapter-popup-container.dark-mode {
.catalog-popup-fullscreen {
background: $dark-bg-color-secondary;
.catalog-header {
background: $dark-bg-color-secondary;
border-bottom: 1px solid $dark-border-color;
.header-title {
color: $dark-text-color-primary;
}
.header-right {
color: $dark-accent-color;
}
}
.catalog-list {
.catalog-item {
background: $dark-bg-color-secondary;
color: $dark-text-color-secondary;
border-bottom: 1px solid $dark-border-color;
&.active {
color: #ff5a5f;
background: rgba(255, 90, 95, 0.1);
}
.item-title {
color: $dark-text-color-secondary;
}
.vip-tag {
background: rgba(255, 153, 0, 0.2);
color: #ff9900;
}
}
}
}
}
</style>

+ 159
- 0
pages_order/components/novel/subscriptionPopup.vue View File

@ -0,0 +1,159 @@
<template>
<view class="subscription-popup" :class="{'dark-mode': isDarkMode}">
<uv-popup ref="popup" mode="center" :closeOnClickOverlay="true" :customStyle="popupStyle">
<view class="content theme-transition">
<view class="popup-title theme-transition">{{ title }}</view>
<view class="popup-desc theme-transition">{{ description }}</view>
<view class="popup-btns">
<button class="popup-btn theme-transition" @click="handleSubscribe">订阅本章</button>
<button class="popup-btn popup-btn-video theme-transition">观看视频解锁</button>
<button class="popup-btn popup-btn-batch theme-transition">批量订阅</button>
</view>
</view>
</uv-popup>
</view>
</template>
<script>
import themeMixin from '@/mixins/themeMode.js'
export default {
name: 'subscriptionPopup',
mixins: [themeMixin],
props: {
title: {
type: String,
default: '这是付费章节 需要订阅后才能阅读'
},
description: {
type: String,
default: '订阅后可继续阅读本章内容'
}
},
data() {
return {
popupShown: false, //
}
},
computed: {
popupStyle() {
return {
'background': this.isDarkMode ? '#232323' : '#fff',
'padding': '48rpx 32rpx',
'text-align': 'center',
'border-radius': '24rpx',
'min-width': '500rpx'
}
}
},
methods: {
//
open() {
if (!this.popupShown) {
this.$refs.popup.open();
this.popupShown = true;
}
},
//
close() {
this.$refs.popup.close();
},
//
reset() {
this.popupShown = false;
},
//
handleSubscribe() {
this.$emit('subscribe');
this.close();
}
}
}
</script>
<style lang="scss" scoped>
.subscription-popup {
.content {
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #222;
margin-bottom: 24rpx;
word-wrap: break-word;
white-space: normal;
}
.popup-desc {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
word-wrap: break-word;
white-space: normal;
}
.popup-btns {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 24rpx;
.popup-btn {
background: #ff9800;
color: #fff;
border-radius: 32rpx;
font-size: 28rpx;
padding: 0 32rpx;
border: none;
margin-bottom: 16rpx;
word-break: keep-all;
&.popup-btn-video {
background: #fff3e0;
color: #ff9800;
border: 1px solid #ff9800;
}
&.popup-btn-batch {
background: #fff;
color: #ff9800;
border: 1px solid #ff9800;
}
}
}
}
&.dark-mode {
.content {
.popup-title {
color: $dark-text-color-primary;
}
.popup-desc {
color: $dark-text-color-tertiary;
}
.popup-btns {
.popup-btn {
background: #ff9800;
color: #fff;
&.popup-btn-video {
background: rgba(255, 152, 0, 0.1);
color: #ff9800;
border: 1px solid #ff9800;
}
&.popup-btn-batch {
background: $dark-bg-color-secondary;
color: #ff9800;
border: 1px solid #ff9800;
}
}
}
}
}
}
</style>

+ 2
- 7
pages_order/novel/Giftbox.vue View File

@ -1,13 +1,8 @@
<template>
<!-- 礼物盒页面 -->
<view class="giftbox-container">
<!-- 顶部标题栏 -->
<uv-navbar title="礼物盒" :autoBack="true" fixed placeholder rightIcon="more-dot-fill">
<template #left>
<BackArrow :size="56" color="#333" />
</template>
</uv-navbar>
<navbar title="礼物盒" leftClick @leftClick="$utils.navigateBack" />
<!-- 礼物列表 -->
<view class="gift-list">
<view


+ 1
- 1
pages_order/novel/Giftpurchases.vue View File

@ -2,7 +2,7 @@
<!-- 礼物购买页面 -->
<view class="gift-purchase-page">
<!-- 顶部导航栏 -->
<uv-navbar title="礼物购买" :autoBack="true" fixed placeholder />
<navbar title="礼物购买" leftClick @leftClick="$utils.navigateBack" />
<!-- 礼物购买卡片 -->
<view class="card gift-card">


+ 2
- 2
pages_order/novel/Modifyinformation.vue View File

@ -1,7 +1,7 @@
<template>
<view class="modify-info-page">
<navbar title="修改信息" />
<BackArrow class="back-arrow-fix" @back="goBack" />
<navbar title="修改信息" leftClick @leftClick="$utils.navigateBack" />
<view class="header">
<view class="avatar-upload">
<uv-avatar :src="avatarUrl" size="88" shape="circle" @click="showUpload = true" class="avatar-main" />


+ 1
- 1
pages_order/novel/ReaderAchievement.vue View File

@ -1,6 +1,6 @@
<template>
<view class="achievement-page">
<uv-navbar title="读者成就设置" fixed placeholder></uv-navbar>
<navbar title="读者成就设置" leftClick @leftClick="$utils.navigateBack" />
<view class="achievement-card">
<view class="card-title-row">
<view class="card-title">阅读成就设置</view>


+ 1
- 2
pages_order/novel/Respondcomments.vue View File

@ -1,9 +1,8 @@
<template>
<view class="respond-comments-page">
<!-- 顶部导航栏 -->
<navbar title="回复评论" :leftClick="true" @leftClick="goBack">
<navbar title="回复评论" leftClick @leftClick="$utils.navigateBack" />
</navbar>
<!-- 原评论展示 -->
<view class="origin-comment-card">
<view class="comment-header">


+ 1
- 1
pages_order/novel/Review.vue View File

@ -1,7 +1,7 @@
<template>
<view class="review-page">
<!-- 顶部导航栏 -->
<navbar title="写书评" :leftClick="true" @leftClick="goBack" />
<navbar title="写书评" leftClick @leftClick="$utils.navigateBack" />
<view class="review-content">
<view class="book-title-label">书本名称</view>
<view class="book-title">{{ bookTitle }}</view>


+ 1
- 2
pages_order/novel/SubscriptionInformation.vue View File

@ -1,7 +1,6 @@
<template>
<view class="subscription-info-container">
<uv-navbar title="订阅信息" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;"
:border="false" leftIconSize="46" />
<navbar title="订阅信息" leftClick @leftClick="$utils.navigateBack" />
<view class="content-area">
<view class="card">
<view class="section-title">订阅章节</view>


+ 1
- 1
pages_order/novel/Tipping.vue View File

@ -1,7 +1,7 @@
<template>
<view class="tipping-page">
<!-- 顶部导航栏 -->
<uv-navbar title="读者亲密值榜单" fixed placeholder></uv-navbar>
<navbar title="读者亲密值榜单" leftClick @leftClick="$utils.navigateBack" />
<!-- 榜单前三名 -->
<view class="top-three">


+ 2
- 6
pages_order/novel/Translation.vue View File

@ -1,12 +1,8 @@
<template>
<view class="page-container">
<view class="task-center">
<uv-navbar title="任务中心" :autoBack="true" fixed placeholder titleStyle="color: #222; font-weight: 500;"
:border="false" :bgColor="'#fff9e2'">
<template #left>
<BackArrow :size="56" color="#222" @back="goBack" />
</template>
</uv-navbar>
<navbar title="回复评论" leftClick @leftClick="$utils.navigateBack" />
<view class="navbar-placeholder"></view>
<!-- 账户剩余 -->
<view class="account-balance">


+ 1
- 6
pages_order/novel/Walletflow.vue View File

@ -2,12 +2,7 @@
<template>
<view class="walletflow-page">
<!-- 顶部导航栏 -->
<uv-navbar title="钱包流水" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;"
:border="false">
<template #left>
<BackArrow :size="56" color="#333" />
</template>
</uv-navbar>
<navbar title="钱包流水" leftClick @leftClick="$utils.navigateBack" />
<!-- 账户余额卡片 -->
<view class="balance-card">


+ 187
- 169
pages_order/novel/comments.vue View File

@ -1,232 +1,250 @@
<template>
<view class="comments-page">
<!-- 顶部导航栏 -->
<navbar title="评论详情" :leftClick="true" @leftClick="goBack" />
<!-- 书本名称 -->
<view class="book-title-area">
<view class="book-title-label">书本名称</view>
<view class="book-title">{{ bookTitle }}</view>
</view>
<!-- 主评论卡片 -->
<view class="comment-card">
<view class="comment-header">
<image class="avatar" :src="comment.avatar" mode="aspectFill" />
<view class="user-info">
<text class="username">{{ comment.username }}</text>
<view class="comments-page">
<navbar title="评论详情" leftClick @leftClick="$utils.navigateBack" />
<!-- 书本名称 -->
<view class="book-title-area">
<view class="book-title-label">书本名称</view>
<view class="book-title">{{ bookTitle }}</view>
</view>
</view>
<view class="comment-content">{{ comment.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ comment.time }}</text>
</view>
</view>
<!-- 全部评论列表 -->
<view class="all-reply-area">
<view class="all-reply-header">全部评论·{{ comment.replyCount }}</view>
<view class="reply-list">
<view class="reply-item" v-for="(item, idx) in replies" :key="idx">
<image class="reply-avatar" :src="item.avatar" mode="aspectFill" />
<view class="reply-main">
<view class="reply-username">{{ item.username }}</view>
<view class="reply-content">{{ item.content }}</view>
<view class="reply-time">{{ item.time }}</view>
</view>
<!-- 主评论卡片 -->
<view class="comment-card">
<view class="comment-header">
<image class="avatar" :src="comment.avatar" mode="aspectFill" />
<view class="user-info">
<text class="username">{{ comment.username }}</text>
</view>
</view>
<view class="comment-content">{{ comment.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ comment.time }}</text>
</view>
</view>
<!-- 全部评论列表 -->
<view class="all-reply-area">
<view class="all-reply-header">全部评论·{{ comment.replyCount }}</view>
<view class="reply-list">
<view class="reply-item" v-for="(item, idx) in replies" :key="idx">
<image class="reply-avatar" :src="item.avatar" mode="aspectFill" />
<view class="reply-main">
<view class="reply-username">{{ item.username }}</view>
<view class="reply-content">{{ item.content }}</view>
<view class="reply-time">{{ item.time }}</view>
</view>
</view>
</view>
</view>
<!-- 底部回复按钮 -->
<view class="reply-footer">
<button class="submit-btn" @click="goToRespond">回复评论</button>
</view>
</view>
</view>
<!-- 底部回复按钮 -->
<view class="reply-footer">
<button class="submit-btn" @click="goToRespond">回复评论</button>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
components: { navbar },
data() {
return {
bookTitle: '这游戏也太真实了',
comment: {
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09',
replyCount: 17
},
replies: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09'
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '战斗世界',
content: '这本书打破了我看甜文套路之前的观念女主真的太可爱太有趣和以往看过的一个NPC介绍有很大不同',
time: '2024.07.09'
components: { navbar },
data() {
return {
bookTitle: '这游戏也太真实了',
comment: {
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09',
replyCount: 17
},
replies: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09'
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '战斗世界',
content: '这本书打破了我看甜文套路之前的观念女主真的太可爱太有趣和以往看过的一个NPC介绍有很大不同',
time: '2024.07.09'
}
]
}
]
}
},
methods: {
goBack() {
uni.navigateBack()
},
goToRespond() {
uni.navigateTo({ url: '/pages_order/novel/Respondcomments' })
},
submitReply() {
uni.showToast({ title: '功能开发中', icon: 'none' })
methods: {
goToRespond() {
uni.navigateTo({ url: '/pages_order/novel/Respondcomments' })
},
submitReply() {
uni.showToast({ title: '功能开发中', icon: 'none' })
}
}
}
}
</script>
<style scoped lang="scss">
.comments-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.book-title-area {
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
}
.book-title-label {
color: #bdbdbd;
font-size: 24rpx;
margin-bottom: 4rpx;
color: #bdbdbd;
font-size: 24rpx;
margin-bottom: 4rpx;
}
.book-title {
font-size: 28rpx;
color: #222;
margin-bottom: 16rpx;
border-bottom: 1px solid #ededed;
padding-bottom: 8rpx;
font-size: 28rpx;
color: #222;
margin-bottom: 16rpx;
border-bottom: 1px solid #ededed;
padding-bottom: 8rpx;
}
.comment-card {
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
margin-bottom: 0;
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
margin-bottom: 0;
}
.comment-header {
display: flex;
align-items: center;
margin-bottom: 8rpx;
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.avatar {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 16rpx;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.user-info {
display: flex;
flex-direction: column;
display: flex;
flex-direction: column;
}
.username {
font-size: 26rpx;
color: #222;
font-weight: 500;
font-size: 26rpx;
color: #222;
font-weight: 500;
}
.comment-content {
font-size: 26rpx;
color: #333;
margin-bottom: 12rpx;
font-size: 26rpx;
color: #333;
margin-bottom: 12rpx;
}
.comment-footer {
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
}
.comment-time {
color: #bdbdbd;
margin-top: 18rpx;
color: #bdbdbd;
margin-top: 18rpx;
}
.all-reply-area {
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 16rpx 24rpx;
margin-top: 24rpx;
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 16rpx 24rpx;
margin-top: 24rpx;
}
.all-reply-header {
color: #222;
font-size: 28rpx;
font-weight: 500;
margin-bottom: 16rpx;
color: #222;
font-size: 28rpx;
font-weight: 500;
margin-bottom: 16rpx;
}
.reply-list {
margin-top: 40rpx;
display: flex;
flex-direction: column;
gap: 50rpx;
margin-top: 40rpx;
display: flex;
flex-direction: column;
gap: 50rpx;
}
.reply-item {
display: flex;
align-items: flex-start;
display: flex;
align-items: flex-start;
}
.reply-avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
margin-right: 16rpx;
flex-shrink: 0;
width: 44rpx;
height: 44rpx;
border-radius: 50%;
margin-right: 16rpx;
flex-shrink: 0;
}
.reply-main {
flex: 1;
display: flex;
flex-direction: column;
flex: 1;
display: flex;
flex-direction: column;
}
.reply-username {
font-size: 24rpx;
color: #222;
font-weight: 500;
margin-bottom: 4rpx;
font-size: 24rpx;
color: #222;
font-weight: 500;
margin-bottom: 4rpx;
}
.reply-content {
font-size: 24rpx;
color: #333;
margin-bottom: 6rpx;
word-break: break-all;
font-size: 24rpx;
color: #333;
margin-bottom: 6rpx;
word-break: break-all;
}
.reply-time {
font-size: 20rpx;
color: #bdbdbd;
font-size: 20rpx;
color: #bdbdbd;
}
.reply-footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
z-index: 10;
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.03);
z-index: 10;
}
.submit-btn {
width: 100%;
height: 80rpx;
background: #0a225f;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
font-weight: 500;
letter-spacing: 2rpx;
width: 100%;
height: 80rpx;
background: #0a225f;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
font-weight: 500;
letter-spacing: 2rpx;
}
</style>

+ 1
- 8
pages_order/novel/createNovel.vue View File

@ -1,14 +1,7 @@
<template>
<!-- 新建作品页面 -->
<view class="create-novel">
<uv-navbar
title="新建作品"
:autoBack="true"
fixed
placeholder
titleStyle="color: #333; font-weight: 500;"
:border="false"
></uv-navbar>
<navbar title="新建作品" leftClick @leftClick="$utils.navigateBack" />
<view class="form-container">
<!-- 封面信息 -->


+ 99
- 94
pages_order/novel/creator.vue View File

@ -1,126 +1,131 @@
<template>
<!-- 申请成为作者页面 -->
<view class="creator-page">
<uv-navbar title="请成为创作者" fixed placeholder>
<template #left>
<BackArrow :size="56" color="#333" />
</template>
</uv-navbar>
<view class="form-card">
<view class="form-item">
<text class="required">*</text>
<text class="label">笔名</text>
<input v-model="penName" placeholder="请输入" class="input" placeholder-class="input-placeholder" />
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">简介</text>
<textarea v-model="intro" placeholder="请输入" class="textarea" placeholder-class="input-placeholder" />
</view>
</view>
<view class="footer-btn-area">
<button class="submit-btn" @click="submit">成为创作者</button>
<!-- 申请成为作者页面 -->
<view class="creator-page">
<navbar title="请成为创作者" leftClick @leftClick="$utils.navigateBack" />
<view class="form-card">
<view class="form-item">
<text class="required">*</text>
<text class="label">笔名</text>
<input v-model="penName" placeholder="请输入" class="input" placeholder-class="input-placeholder" />
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">简介</text>
<textarea v-model="intro" placeholder="请输入" class="textarea" placeholder-class="input-placeholder" />
</view>
</view>
<view class="footer-btn-area">
<button class="submit-btn" @click="submit">成为创作者</button>
</view>
</view>
</view>
</template>
<script>
import BackArrow from './components/BackArrow.vue';
export default {
components: {
BackArrow,
},
data() {
return {
penName: '',
intro: ''
components: {
},
data() {
return {
penName: '',
intro: ''
}
},
methods: {
submit() {
if (!this.penName) {
uni.showToast({ title: '请输入笔名', icon: 'none' })
return
}
if (!this.intro) {
uni.showToast({ title: '请输入简介', icon: 'none' })
return
}
//
uni.showToast({ title: '申请成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 800)
}
}
},
methods: {
submit() {
if (!this.penName) {
uni.showToast({ title: '请输入笔名', icon: 'none' })
return
}
if (!this.intro) {
uni.showToast({ title: '请输入简介', icon: 'none' })
return
}
//
uni.showToast({ title: '申请成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 800)
}
}
}
</script>
<style scoped lang="scss">
.creator-page {
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
}
.form-card {
background: #fff;
border-radius: 20rpx;
margin: 40rpx 32rpx 0 32rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0,0,0,0.04);
background: #fff;
border-radius: 20rpx;
margin: 40rpx 32rpx 0 32rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0, 0, 0, 0.04);
}
.form-item {
display: flex;
flex-direction: column;
margin-bottom: 32rpx;
position: relative;
display: flex;
flex-direction: column;
margin-bottom: 32rpx;
position: relative;
}
.required {
color: #e23d3d;
font-size: 28rpx;
margin-right: 4rpx;
color: #e23d3d;
font-size: 28rpx;
margin-right: 4rpx;
}
.label {
font-size: 28rpx;
color: #222;
font-weight: bold;
margin-bottom: 12rpx;
font-size: 28rpx;
color: #222;
font-weight: bold;
margin-bottom: 12rpx;
}
.input, .textarea {
width: 100%;
height: 80rpx;
border: none;
border-bottom: 1.5rpx solid #ececec;
font-size: 28rpx;
background: transparent;
padding: 18rpx 0 12rpx 0;
margin-bottom: 2rpx;
.input,
.textarea {
width: 100%;
height: 80rpx;
border: none;
border-bottom: 1.5rpx solid #ececec;
font-size: 28rpx;
background: transparent;
padding: 18rpx 0 12rpx 0;
margin-bottom: 2rpx;
}
.input-placeholder {
color: #d2d2d2;
font-size: 26rpx;
color: #d2d2d2;
font-size: 26rpx;
}
.textarea {
min-height: 120rpx;
resize: none;
min-height: 120rpx;
resize: none;
}
.footer-btn-area {
margin-top: auto;
padding: 48rpx 32rpx 32rpx 32rpx;
background: transparent;
margin-bottom: 80rpx;
margin-top: auto;
padding: 48rpx 32rpx 32rpx 32rpx;
background: transparent;
margin-bottom: 80rpx;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #0a2e6d;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
font-weight: bold;
letter-spacing: 2rpx;
transition: background 0.2s;
width: 100%;
height: 88rpx;
background: #0a2e6d;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
font-weight: bold;
letter-spacing: 2rpx;
transition: background 0.2s;
}
</style>

+ 100
- 116
pages_order/novel/readnovels.vue View File

@ -1,10 +1,10 @@
<template>
<!-- 小说文本页面 -->
<view class="reader-container">
<view class="reader-container" :class="{'dark-mode': isDarkMode}">
<view class="top-controls" :class="{'top-controls-hidden': isFullScreen}">
<view class="controls-inner">
<view class="left">
<uv-icon name="arrow-left" @click="$utils.navigateBack" color="#333" size="46rpx"></uv-icon>
<uv-icon name="arrow-left" @click="$utils.navigateBack" :color="isDarkMode ? '#ccc' : '#333'" size="46rpx"></uv-icon>
</view>
<view class="center">
<text class="title">{{ novelTitle }}</text>
@ -39,9 +39,11 @@
<view class="bar-icon"> <uv-icon name="plus"></uv-icon> </view>
<text class="bar-label">加入书架</text>
</view>
<view class="bar-item">
<view class="bar-icon"> <uv-icon name="eye-fill"></uv-icon> </view>
<text class="bar-label">夜间</text>
<view class="bar-item" @click="toggleThemeMode">
<view class="bar-icon">
<uv-icon :name="isDarkMode ? 'eye' : 'eye-fill'"></uv-icon>
</view>
<text class="bar-label">{{ isDarkMode ? '白天' : '夜间' }}</text>
</view>
</view>
<view class="bottom-right">
@ -51,35 +53,8 @@
</view>
</view>
<uv-popup v-model="showPopup" mode="center" :closeOnClickOverlay="true">
<view
style="padding: 48rpx 32rpx; text-align: center; background: #fff; border-radius: 24rpx; min-width: 500rpx;">
<view style="font-size: 32rpx; font-weight: bold; color: #222; margin-bottom: 24rpx;">这是付费章节 需要订阅后才能阅读
</view>
<view style="font-size: 26rpx; color: #999; margin-bottom: 40rpx;">订阅后可继续阅读本章内容</view>
<view style="display: flex; justify-content: center; gap: 24rpx;">
<button
style="background: #ff9800; color: #fff; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: none;"
@click="goToSubscription">订阅本章</button>
<button
style="background: #fff3e0; color: #ff9800; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: 1px solid #ff9800;">观看视频解锁</button>
<button
style="background: #fff; color: #ff9800; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: 1px solid #ff9800;">批量订阅</button>
</view>
</view>
</uv-popup>
<view v-if="showPayPopup" class="pay-popup-mask" @click="showPayPopup = false">
<view class="pay-popup" @click.stop>
<view class="pay-title">这是付费章节 需要订阅后才能阅读</view>
<view class="pay-desc">订阅后可继续阅读本章内容</view>
<view class="pay-btns">
<button class="pay-btn" @click="goToSubscription">订阅本章</button>
<button class="pay-btn pay-btn-video">观看视频解锁</button>
<button class="pay-btn pay-btn-batch">批量订阅</button>
</view>
</view>
</view>
<!-- 使用封装的订阅弹窗组件 -->
<subscriptionPopup ref="subscriptionPopup" @subscribe="goToSubscription"/>
<novelVotePopup ref="novelVotePopup"/>
@ -90,17 +65,20 @@
<script>
import chapterPopup from '../components/novel/chapterPopup.vue'
import novelVotePopup from '../components/novel/novelVotePopup.vue'
import subscriptionPopup from '../components/novel/subscriptionPopup.vue'
import themeMixin from '@/mixins/themeMode.js' //
export default {
components: {
chapterPopup,
novelVotePopup,
subscriptionPopup,
},
mixins: [themeMixin], // 使
data() {
return {
isFullScreen: false,
showPopup: false,
popupShown: false, //
showPayPopup: false,
novelTitle: "这游戏也太真实了",
currentChapter: "第1章 重回2004",
readProgress: 15, //
@ -144,9 +122,9 @@
//
const scrollTop = e.detail.scrollTop;
//
//
if (scrollTop > 50 && !this.popupShown) {
this.showPopup = true;
this.$refs.subscriptionPopup.open();
this.popupShown = true;
}
},
@ -157,7 +135,7 @@
uni.navigateTo({
url: '/pages_order/novel/SubscriptionInformation'
})
}
},
},
mounted() {
//
@ -168,7 +146,7 @@
window.onscroll = () => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop > 50 && !this.popupShown) {
this.showPopup = true;
this.$refs.subscriptionPopup.open();
this.popupShown = true;
}
};
@ -197,6 +175,77 @@
position: relative;
overflow: hidden;
&.dark-mode {
background: #1a1a1a;
.top-controls {
background: rgba(34, 34, 34, 0.98);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2);
.controls-inner {
.center {
.title {
color: #eee;
}
.chapter {
color: #bbb;
}
}
}
.progress-bar {
background: #333;
.progress-inner {
background: #4a90e2;
}
}
}
.chapter-content {
color: #ccc;
.chapter-content-item {
.chapter-title {
color: #eee;
}
.paragraph-content {
.paragraph {
color: #bbb;
}
}
}
}
.bottom-bar {
background: #222;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.2);
.bottom-left {
.bar-item {
.bar-label {
color: #999;
}
}
}
.bottom-right {
.outline-btn {
background: #222;
color: #4a90e2;
border: 2rpx solid #4a90e2;
.btn-text {
color: #4a90e2;
border-bottom: 2rpx solid #4a90e2;
}
}
}
}
}
.top-controls {
position: fixed;
top: 0;
@ -206,7 +255,7 @@
padding-top: calc(var(--status-bar-height) + 10rpx);
z-index: 100;
transform: translateY(0);
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out, background-color 0.3s ease;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.controls-inner {
@ -289,6 +338,7 @@
width: 100%;
box-sizing: border-box;
overflow-x: hidden;
transition: color 0.3s ease, background-color 0.3s ease;
.chapter-content-item {
width: 100%;
@ -300,6 +350,7 @@
text-align: center;
word-break: break-word;
white-space: normal;
transition: color 0.3s ease;
}
.paragraph-content {
@ -314,6 +365,7 @@
word-wrap: break-word;
word-break: normal;
white-space: normal;
transition: color 0.3s ease;
}
}
}
@ -337,7 +389,7 @@
z-index: 10;
padding: 0 40rpx 10rpx 40rpx;
transform: translateY(0);
transition: transform 0.3s ease-in-out;
transition: transform 0.3s ease-in-out, background-color 0.3s ease;
&.bottom-bar-hidden {
transform: translateY(100%);
@ -359,12 +411,16 @@
height: 48rpx;
margin-bottom: 4rpx;
margin-right: 1rpx;
display: flex;
align-items: center;
justify-content: center;
}
.bar-label {
font-size: 22rpx;
color: #b3b3b3;
margin-top: 2rpx;
transition: color 0.3s ease;
}
}
}
@ -390,6 +446,7 @@
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
.btn-text {
font-weight: bold;
@ -397,80 +454,7 @@
font-size: 28rpx;
border-bottom: 2rpx solid #223a7a;
padding-bottom: 2rpx;
}
}
}
}
}
.pay-popup-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 10000;
.pay-popup {
position: absolute;
left: 0;
right: 0;
bottom: 0;
background: #232323;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
min-width: 500rpx;
width: 100%;
padding: 48rpx 32rpx 32rpx 32rpx;
text-align: center;
color: #fff;
box-sizing: border-box;
.pay-title {
font-size: 32rpx;
font-weight: bold;
color: #fff;
margin-bottom: 24rpx;
margin-right: 50rpx;
word-wrap: break-word;
white-space: normal;
}
.pay-desc {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
margin-right: 50rpx;
word-wrap: break-word;
white-space: normal;
}
.pay-btns {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 24rpx;
margin-right: 50rpx;
.pay-btn {
background: #ff9800;
color: #fff;
border-radius: 32rpx;
font-size: 28rpx;
padding: 0 32rpx;
border: none;
margin-bottom: 16rpx;
word-break: keep-all;
&.pay-btn-video,
&.pay-btn-batch {
background: #ff9800;
color: #fff;
border-radius: 32rpx;
font-size: 28rpx;
padding: 0 32rpx;
border: none;
transition: color 0.3s ease, border-color 0.3s ease;
}
}
}


+ 31
- 0
store/store.js View File

@ -11,11 +11,20 @@ const store = new Vuex.Store({
configList: {}, //配置列表
shop : false,
userInfo : {}, //用户信息
themeMode: uni.getStorageSync('themeMode') || 'light', // 主题模式:light(白天)或dark(黑夜)
},
getters: {
// 角色 true为水洗店 false为酒店
userShop(state){
return state.shop
},
// 当前主题模式
currentTheme(state) {
return state.themeMode
},
// 是否为黑夜模式
isDarkMode(state) {
return state.themeMode === 'dark'
}
},
mutations: {
@ -40,6 +49,28 @@ const store = new Vuex.Store({
})
})
},
// 切换主题模式
toggleThemeMode(state) {
state.themeMode = state.themeMode === 'light' ? 'dark' : 'light'
// 保存到本地存储,以便下次打开时恢复
uni.setStorageSync('themeMode', state.themeMode)
},
// 设置为指定主题模式
setThemeMode(state, mode) {
if (mode === 'light' || mode === 'dark') {
state.themeMode = mode
// 保存到本地存储,以便下次打开时恢复
uni.setStorageSync('themeMode', state.themeMode)
}
},
// 初始化主题模式
initThemeMode(state) {
// 从本地存储中读取主题设置
const savedMode = uni.getStorageSync('themeMode')
if (savedMode) {
state.themeMode = savedMode
}
},
login(state){
uni.showLoading({
title: '登录中...'


+ 29
- 0
uni.scss View File

@ -75,6 +75,27 @@ $uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
/* 暗色主题相关变量 */
// 背景色
$dark-bg-color: #1a1a1a; // 主背景色
$dark-bg-color-secondary: #222; // 次要背景色如底部栏顶部栏
$dark-bg-color-tertiary: #333; // 第三级背景色如卡片背景
// 文本颜色
$dark-text-color-primary: #eee; // 主要文本颜色
$dark-text-color-secondary: #bbb; // 次要文本颜色
$dark-text-color-tertiary: #999; // 第三级文本颜色如说明文字
// 边框和阴影
$dark-border-color: #444; // 边框颜色
$dark-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2); // 阴影
// 强调色
$dark-accent-color: #4a90e2; // 强调色如进度条按钮
// 过渡动画时间
$theme-transition-duration: 0.3s; // 主题切换过渡时间
.uni-color-btn{
border-radius: 40rpx;
@ -93,4 +114,12 @@ $uni-font-size-paragraph:15px;
color: $uni-color;
text-align: center;
font-size: 28rpx;
}
/* 通用黑暗模式过渡 */
.theme-transition {
transition: color $theme-transition-duration ease,
background-color $theme-transition-duration ease,
border-color $theme-transition-duration ease,
box-shadow $theme-transition-duration ease;
}

Loading…
Cancel
Save