Browse Source

refactor: 重构用户信息修改页面及优化相关逻辑

将用户信息修改页面从 `novel/Modifyinformation.vue` 移动到 `auth/Modifyinformation.vue`,并优化了表单提交逻辑。新增了手机号绑定功能,提升了用户体验。同时,修复了页面样式和交互问题,确保功能更加完善。

- 移动并重构用户信息修改页面
- 新增手机号绑定功能
- 优化表单提交逻辑
- 修复页面样式和交互问题
master
前端-胡立永 5 months ago
parent
commit
44c8ced505
25 changed files with 1347 additions and 1340 deletions
  1. +1
    -1
      App.vue
  2. +1
    -1
      api/http.js
  3. +3
    -2
      api/model/bookshelf.js
  4. +1
    -1
      api/model/login.js
  5. +7
    -6
      common.scss
  6. +0
    -4
      components/base/tabbar.vue
  7. +6
    -13
      components/novel/novelItem.vue
  8. +10
    -17
      components/novel/workItem.vue
  9. +1
    -1
      mixins/configList.js
  10. +1
    -1
      pages.json
  11. +32
    -126
      pages/index/bookshelf.vue
  12. +14
    -37
      pages/index/center.vue
  13. +3
    -6
      pages/index/index.vue
  14. +19
    -24
      pages_order/announcement/announcementDetail.vue
  15. +6
    -39
      pages_order/announcement/announcements.vue
  16. +248
    -0
      pages_order/auth/Modifyinformation.vue
  17. +37
    -19
      pages_order/auth/wxLogin.vue
  18. +91
    -14
      pages_order/auth/wxUserInfo.vue
  19. +173
    -196
      pages_order/author/editor.vue
  20. +237
    -159
      pages_order/novel/Giftbox.vue
  21. +0
    -185
      pages_order/novel/Modifyinformation.vue
  22. +13
    -29
      pages_order/novel/chapterList.vue
  23. +321
    -349
      pages_order/novel/createNovel.vue
  24. +92
    -68
      pages_order/novel/novelDetail.vue
  25. +30
    -42
      store/store.js

+ 1
- 1
App.vue View File

@ -3,7 +3,7 @@
onLaunch: function() {
},
onShow: function() {
// this.$store.commit('initConfig')
this.$store.commit('initConfig')
},
onHide: function() {
}


+ 1
- 1
api/http.js View File

@ -35,7 +35,7 @@ function http(uri, data, callback, method = 'GET', showLoading, title) {
if(res.statusCode == 401 ||
res.data.message == '操作失败,token非法无效!' ||
res.data.message == '操作失败,用户不存在!'){
store.commit('logout')
store.commit('logout', '登录过期了,你可以停留在此页面或去重新登录')
console.error('登录过期');
utils.toLogin()
}


+ 3
- 2
api/model/bookshelf.js View File

@ -30,14 +30,15 @@ const api = {
url: '/all_book/saveOrUpdateBook',
method: 'POST',
auth: true,
limit: 500,
limit: 800,
showLoading: true,
},
// 增加或修改作品章节
saveOrUpdateCatalog: {
url: '/all_book/saveOrUpdateCatalog',
method: 'GET',
method: 'POST',
auth: true,
limit: 800,
},
}

+ 1
- 1
api/model/login.js View File

@ -21,7 +21,7 @@ const api = {
auth: true,
},
// 更新用户信息
updateUserInfo: {
updateInfo: {
url: '/all_login/updateUserInfo',
method: 'POST',
auth: true,


+ 7
- 6
common.scss View File

@ -24,12 +24,13 @@
.share{
padding: 0;
margin: 0;
background-color: #fff;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
font-size: 26rpx;
background-color: unset;
display: unset !important;
flex-direction: unset !important;
justify-content: unset !important;
align-items: unset !important;
font-size: unset;
line-height: unset;
}
.share::after{
border: none;


+ 0
- 4
components/base/tabbar.vue View File

@ -17,14 +17,10 @@
</template>
<script>
import {
mapGetters
} from 'vuex'
export default {
name: "tabbar",
props: ['select'],
computed: {
...mapGetters(['userShop']),
},
data() {
return {


+ 6
- 13
components/novel/novelItem.vue View File

@ -1,8 +1,9 @@
<template>
<view class="book-item" :class="{'horizontal': horizontal}" @click="$emit('click')">
<image class="book-cover" src="https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp" mode="aspectFill"></image>
<image class="book-cover" :src="book.image
&& book.image.split(',')[0]" mode="aspectFill"></image>
<view class="book-info">
<view class="book-title">{{book.title}}</view>
<view class="book-title">{{book.name}}</view>
<view class="book-author" v-if="book.author && !horizontal">{{book.author}}</view>
<view class="book-desc" v-if="!horizontal">{{book.desc || '暂无简介'}}</view>
@ -14,11 +15,11 @@
<view class="content-row" v-if="!horizontal">
<view class="book-status" v-if="book.status">
<view class="book-status">
<text>{{book.status}}</text>
</view>
<view class="book-text">
大家都在读
{{ item.service || '大家都在读' }}
</view>
</view>
</view>
@ -30,15 +31,7 @@
props: {
book: {
type: Object,
default: () => ({
id: '1',
title: '我是半妖',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '东方不败',
desc: '这是一部关于半妖少年成长的玄幻小说,讲述了主角在人妖两界的冒险故事。',
tags: ['玄幻', '冒险', '热血'],
status: '连载中'
})
default: {}
},
//
horizontal: {


+ 10
- 17
components/novel/workItem.vue View File

@ -1,15 +1,15 @@
<template>
<view class="work-item" @click="handleClick">
<view class="cover-wrapper">
<image class="cover" :src="work.cover || 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain'" mode="aspectFill"></image>
<image class="cover" :src="work.image && work.image.split(',')[0]" mode="aspectFill"></image>
<view class="tag original" v-if="work.isOriginal">原创</view>
</view>
<view class="info">
<text class="title">{{work.title || '兽王进化:从被小萝莉召唤开始'}}</text>
<text class="title">{{work.name}}</text>
<view class="readers">
<text class="readers-count">达成成就人{{work.readers || '8721'}}</text>
<text class="readers-count">达成成就人{{work.readers || 0}}</text>
</view>
<!-- 状态标签 -->
@ -36,14 +36,7 @@
props: {
work: {
type: Object,
default: () => ({
title: '兽王进化:从被小萝莉召唤开始',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
readers: '8721',
status: 'ongoing',
publishStatus: '发布审核中',
isOriginal: true
})
default: {},
},
isManaging: {
type: Boolean,
@ -54,16 +47,16 @@
statusClass() {
const statusMap = {
'draft': 'new',
'ongoing': 'ongoing',
'completed': 'completed'
'0': 'ongoing',
'1': 'completed'
};
return statusMap[this.work.status] || 'ongoing';
},
statusText() {
const textMap = {
'draft': '新建',
'ongoing': '连载中',
'completed': '已完结'
// '0': '',
'0': '连载中',
'1': '已完结'
};
return textMap[this.work.status] || '连载中';
}
@ -76,7 +69,7 @@
}
//
uni.navigateTo({
url: '/pages_order/novel/chapterList'
url: '/pages_order/novel/chapterList?id=' + this.work.id
});
},
handleDelete() {


+ 1
- 1
mixins/configList.js View File

@ -13,7 +13,7 @@ export default {
}
},
computed: {
...mapState(['configList', 'userInfo', 'riceInfo']),
...mapState(['configList', 'userInfo', 'isLogin', 'themeMode']),
},
// 定义全局分享
// 1.发送给朋友


+ 1
- 1
pages.json View File

@ -107,7 +107,7 @@
"path": "novel/Translation"
},
{
"path": "novel/Modifyinformation"
"path": "auth/Modifyinformation"
}
]
}],


+ 32
- 126
pages/index/bookshelf.vue View File

@ -2,7 +2,7 @@
<!-- 书架页面 -->
<view class="page">
<!-- 头部标签切换 -->
<view class="header" :style="{ paddingTop: `${statusBarHeight}px` }">
<view class="header">
<view class="header-content">
<view class="tab-container">
<view class="tab" :class="{'active': activeTab === 'read'}" @click="switchTab('read')">阅读</view>
@ -15,7 +15,7 @@
<view class="novel-grid" v-if="activeTab === 'read' && !isEditMode">
<view class="novel-row" v-for="(row, rowIndex) in novelRows" :key="rowIndex">
<view class="novel-item"
v-for="(novel, index) in row"
v-for="(novel, index) in list"
:key="novel.id"
@click="toNovelDetail(novel.id)"
@longpress="enterEditMode">
@ -43,7 +43,7 @@
<!-- 作品列表 -->
<view class="works-content">
<work-item
v-for="work in worksList"
v-for="work in list"
:key="work.id"
:work="work"
@click="toWorkDetail(work.id)"
@ -51,7 +51,7 @@
/>
<!-- 空状态提示 -->
<view class="empty-works" v-if="worksList.length === 0">
<view class="empty-works" v-if="list.length === 0">
<text class="empty-text">你还没有创建作品</text>
<text class="empty-tips">点击左上角"+"创建你的第一部作品吧</text>
</view>
@ -62,7 +62,7 @@
<view class="novel-grid edit-mode" v-if="activeTab === 'read' && isEditMode">
<view class="novel-row" v-for="(row, rowIndex) in novelRows" :key="rowIndex">
<view class="novel-item"
v-for="(novel, index) in row"
v-for="(novel, index) in list"
:key="novel.id"
@click="toggleSelect(novel, 'novel')">
<view class="item-checkbox" v-if="selectedItems.includes(novel.id)">
@ -94,7 +94,7 @@
<view class="works-content">
<view
class="work-item-wrapper"
v-for="work in worksList"
v-for="work in list"
:key="work.id"
@click="toggleSelect(work, 'work')"
>
@ -137,8 +137,9 @@
import novelItem from '@/components/novel/novelItem.vue'
import workItem from '@/components/novel/workItem.vue'
import newWorkItem from '@/components/novel/newWorkItem.vue'
import { mapGetters } from 'vuex'
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
components : {
tabber,
novelItem,
@ -146,14 +147,13 @@
newWorkItem
},
computed : {
...mapGetters(['userShop']),
// 3
novelRows() {
const rows = [];
const itemsPerRow = 3;
for (let i = 0; i < this.novels.length; i += itemsPerRow) {
rows.push(this.novels.slice(i, i + itemsPerRow));
for (let i = 0; i < this.list.length; i += itemsPerRow) {
rows.push(this.list.slice(i, i + itemsPerRow));
}
return rows;
@ -166,115 +166,21 @@
activeTab: 'read',
isEditMode: false,
selectedItems: [], //
novels: [
{
id: '1',
title: '我是半妖',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '炎兰',
desc: '都市玄幻小说,主角获得半妖化能力,通过吸收妖气不断变强...',
tags: ['玄幻', '都市', '热血'],
status: '连载中'
},
{
id: '2',
title: '兽王进化:从被小萝莉召唤开始',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '九灵',
desc: '一场意外让主角获得兽王血脉,开始了进化之路...',
tags: ['奇幻', '冒险'],
isOriginal: true,
status: '连载中'
},
{
id: '3',
title: '魔法少女纯爷们',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '烟火',
desc: '一个普通男孩意外获得魔法少女的力量,开始了奇妙冒险...',
tags: ['搞笑', '奇幻'],
status: '已完结'
},
{
id: '4',
title: '我是一条小青龙',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '东升',
desc: '重生为一条小青龙,主角在修仙世界中成长的故事...',
tags: ['仙侠', '修真'],
tag: '独家',
status: '连载中'
},
{
id: '5',
title: '女帝:别闹,朕怀孕了!',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '君临',
desc: '一代女帝意外穿越成了皇帝,却发现自己怀孕了...',
tags: ['宫廷', '穿越'],
isOriginal: true,
status: '连载中'
},
{
id: '6',
title: '中国式应酬——应酬是门技术活',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '商业顾问',
desc: '一本教你如何在商业场合应对各种应酬的实用指南...',
tags: ['商业', '实用'],
status: '已完结'
},
{
id: '7',
title: '苏世民:我的经验与教训',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '苏世民',
desc: '黑石集团创始人苏世民的商业回忆录...',
tags: ['传记', '商业'],
status: '已完结'
},
{
id: '8',
title: '认知觉醒:开启自我改变的原动力',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: '周岭',
desc: '帮助你打破思维局限,重塑认知结构的心理学著作...',
tags: ['心理', '自助'],
status: '已完结'
},
{
id: '9',
title: '纳瓦尔宝典',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: 'Naval',
desc: '硅谷天使投资人纳瓦尔·拉维坎特的人生智慧...',
tags: ['哲学', '投资'],
status: '已完结'
}
],
//
worksList: [
{
id: '9',
title: '纳瓦尔宝典',
cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
author: 'Naval',
desc: '硅谷天使投资人纳瓦尔·拉维坎特的人生智慧...',
tags: ['哲学', '投资'],
status: '已完结'
}
] //
mixinsListApi : 'getReadBookPage',
apiMap : {
read : 'getReadBookPage',
work : 'getMyBookPage',
// work : 'getMyBookPage',
},
}
},
onLoad() {
//
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight;
//
const activeTab = uni.getStorageSync('activeBookshelfTab')
if (activeTab === 'work') {
this.activeTab = 'work'
this.mixinsListApi = this.apiMap[tab]
uni.removeStorageSync('activeBookshelfTab')
}
@ -283,14 +189,8 @@
this.activeTab = 'work'
})
// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
const navBarHeight = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height + systemInfo.statusBarHeight;
this.navBarHeight = navBarHeight;
// #endif
},
onShow() {
//
const pages = getCurrentPages();
const current = pages[pages.length - 1];
@ -315,6 +215,12 @@
switchTab(tab) {
this.activeTab = tab;
this.mixinsListApi = this.apiMap[tab]
this.list = []
this.getData()
// 退
this.exitEditMode();
},
@ -328,6 +234,7 @@
//
toWorkDetail(id) {
console.log(id);
uni.navigateTo({
url: '/pages/work/detail?id=' + id
})
@ -381,11 +288,11 @@
}
} else {
//
if (this.selectedItems.length === this.worksList.length) {
if (this.selectedItems.length === this.list.length) {
this.selectedItems = [];
} else {
//
this.selectedItems = this.worksList.map(work => work.id);
this.selectedItems = this.list.map(work => work.id);
}
}
},
@ -419,9 +326,9 @@
});
} else {
//
this.worksList = this.worksList.filter(work => !this.selectedItems.includes(work.id));
this.list = this.list.filter(work => !this.selectedItems.includes(work.id));
//
uni.setStorageSync('worksList', this.worksList)
uni.setStorageSync('list', this.list)
uni.showToast({
title: '删除成功',
icon: 'success'
@ -432,7 +339,7 @@
// 退
if ((this.activeTab === 'read' && this.novels.length === 0) ||
(this.activeTab === 'work' && this.worksList.length === 0)) {
(this.activeTab === 'work' && this.list.length === 0)) {
this.exitEditMode();
}
}
@ -441,8 +348,8 @@
},
//
loadWorksList() {
const savedWorks = uni.getStorageSync('worksList') || []
this.worksList = savedWorks
const savedWorks = uni.getStorageSync('list') || []
this.list = savedWorks
}
}
}
@ -468,8 +375,7 @@
box-sizing: border-box;
width: 100%;
border-bottom: 1rpx solid #f5f5f5;
padding-top: constant(safe-area-inset-top); /* iOS 11.0 */
padding-top: env(safe-area-inset-top); /* iOS 11.2+ */
padding-top: calc(var(--status-bar-height) + 20rpx);
.header-content {
display: flex;


+ 14
- 37
pages/index/center.vue View File

@ -6,16 +6,15 @@
<view class=""
style="height: 120rpx;padding-top: calc(var(--status-bar-height) + 20rpx);">
</view>
<!-- 用户信息区域 -->
<view class="user-info">
<image class="avatar" src="https://img95.699pic.com/photo/50058/1378.jpg_wh860.jpg" mode=""></image>
<image class="avatar" :src="userInfo.headImage" mode="aspectFill"></image>
<view class="info">
<view class="name">战斗世界 <text class="id">(ID: 50523541)</text></view>
<view class="name">{{ userInfo.nickName }} <text class="id"> (ID: {{ userInfo.id }})</text></view>
<view class="desc">世界达人小说控</view>
<view class="phone">手机号19898474531</view>
<view class="phone">手机号{{ userInfo.phone }}</view>
</view>
<view class="more">
<uv-icon name="more-dot-fill" size="46rpx" color="#999"></uv-icon>
@ -81,18 +80,17 @@
<view class="section-item-left">
<uv-icon name="star" size="40rpx" color="#333"></uv-icon>
<text>联系客服</text>
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="$utils.navigateTo('/pages_order/novel/Modifyinformation')">
<view class="section-item" @click="$utils.navigateTo('/pages_order/auth/Modifyinformation')">
<view class="section-item-left">
<uv-icon name="edit-pen" size="40rpx" color="#333"></uv-icon>
<text>修改信息</text>
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="logout">
<view class="section-item" @click="$store.commit('logout')">
<view class="section-item-left">
<uv-icon name="star" size="40rpx" color="#333"></uv-icon>
<text>退出登录</text>
@ -103,14 +101,13 @@
</view>
</view>
<tabber select="center" />
</view>
<view v-else-if="!isLogin && !showLogoutModal" class="nologin-page">
<view v-else-if="!isLogin" class="nologin-page">
<view class="nologin-top-bg">
<view class="nologin-header">
<image class="nologin-avatar" src="https://cdn.uviewui.com/uview/album/1.jpg" mode="aspectFill" @click="toLogin" />
<image class="nologin-avatar" mode="aspectFill" @click="toLogin" />
<view class="nologin-text" @click="toLogin">点击登录</view>
</view>
<view class="nologin-header-right">
@ -119,20 +116,10 @@
</view>
<view class="nologin-content-center">
<button class="nologin-btn" @click="toLogin">立即登录</button>
<view class="nologin-tip">暂未登录 请先登录</view>
</view>
<tabber select="center" />
</view>
<view v-if="showLogoutModal" class="modal-mask">
<view class="modal-box">
<view class="modal-title">退出登录</view>
<view class="modal-content">确认要退出登录吗</view>
<view class="modal-actions">
<view class="modal-btn" @click="cancelLogout">取消</view>
<view class="modal-btn confirm" @click="confirmLogout">确认</view>
</view>
</view>
</view>
<tabber select="center" />
</view>
</template>
@ -146,24 +133,14 @@
},
data() {
return {
showLogoutModal: false,
isLogin: true,
}
},
methods: {
logout() {
this.showLogoutModal = true;
},
cancelLogout() {
this.showLogoutModal = false;
},
confirmLogout() {
this.showLogoutModal = false;
this.isLogin = false;
},
toLogin() {
uni.showToast({ title: '跳转登录页', icon: 'none' })
onLoad() {
if(this.isLogin){
this.$store.commit('getUserInfo')
}
},
methods: {
}
}
</script>


+ 3
- 6
pages/index/index.vue View File

@ -97,7 +97,7 @@
<text class="section-more" @click="$utils.navigateTo('/pages/index/category')">查看更多 ></text>
</view>
<view class="novel-list">
<novel-item v-for="(item, index) in novelList"
<novel-item v-for="(item, index) in list"
@click="navigateToDetail(item.id)"
:key="index" :book="item" />
</view>
@ -115,16 +115,13 @@
import tabber from '@/components/base/tabbar.vue'
import novelItem from '@/components/novel/novelItem.vue'
import mixinsList from '@/mixins/list.js'
import {
mapGetters
} from 'vuex'
export default {
mixins: [mixinsList],
components: {
tabber,
PrivacyAgreementPoup,
novelItem,
},
mixins: [mixinsList],
data() {
return {
//
@ -183,7 +180,7 @@
//
async getNewList() {
const data = await this.$fetch('getNewList')
this.novelList = data
this.novelList = data.records
},
}
}


+ 19
- 24
pages_order/announcement/announcementDetail.vue View File

@ -4,7 +4,10 @@
<navbar title="公告详情" leftClick @leftClick="$utils.navigateBack" />
<view class="award-section">
<view class="award-item">
<uv-parse :content="detail.details"></uv-parse>
<!-- <view class="award-item">
<text class="award-title">长篇网文</text>
<view class="award-content">
<view class="sub-award">
@ -39,7 +42,7 @@
<text class="link">2月完结专题奖励名单</text>
</view>
</view>
</view>
</view> -->
</view>
</view>
</template>
@ -47,41 +50,33 @@
<script>
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue')
},
data() {
return {}
return {
id : 0,
detail : {},
}
},
onLoad({id}) {
this.id = id
this.getNoticeDetail()
},
methods: {
//
async getNoticeDetail(){
this.detail = await this.$fetch('getNoticeById', {
id : this.id
});
},
}
}
</script>
<style lang="scss">
.announcement-container {
padding: 30rpx;
background: #fff;
.header {
margin-bottom: 40rpx;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.date {
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
}
.award-section {
padding: 20rpx;
.award-item {
margin-bottom: 40rpx;


+ 6
- 39
pages_order/announcement/announcements.vue View File

@ -3,11 +3,11 @@
<navbar title="全部公告" leftClick @leftClick="$utils.navigateBack" />
<view class="announcements-list">
<view class="announcement-item" v-for="(item, index) in announcements" :key="index"
<view class="announcement-item" v-for="(item, index) in list" :key="index"
@click="$utils.navigateTo(`/pages_order/announcement/announcementDetail?id=${item.id}`)">
<view class="item-left">
<text class="tag">通讯</text>
<text class="title">{{ item.title }}</text>
<text class="tag">{{ item.title }}</text>
<text class="title">{{ item.titleText }}</text>
</view>
<view class="item-right">
<uv-icon name="arrow-right" color="#C8C8C8" size="28rpx"></uv-icon>
@ -18,45 +18,12 @@
</template>
<script>
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
data() {
return {
announcements: [{
id: 1,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 2,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 3,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 4,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 5,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 6,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
},
{
id: 7,
title: '2025年2月平台福利活动拟获奖作品公示',
type: '通讯'
}
]
mixinsListApi : 'getNoticePage',
}
},
methods: {


+ 248
- 0
pages_order/auth/Modifyinformation.vue View File

@ -0,0 +1,248 @@
<template>
<view class="modify-info-page">
<navbar title="修改信息" leftClick @leftClick="$utils.navigateBack" />
<view class="header">
<button open-type="chooseAvatar"
class="share"
@chooseavatar="onChooseAvatar">
<view class="avatar-upload">
<uv-avatar :src="userInfoForm.headImage" size="88" shape="circle" class="avatar-main" />
<view class="avatar-upload-btn">
<uv-icon name="camera-fill" size="28" color="#fff" />
</view>
</view>
</button>
</view>
<view class="card">
<view class="card-title">个人信息</view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 昵称</view>
<view class="form-value">
<input type="nickname" placeholder="请输入昵称" id="nickName"
v-model="userInfoForm.nickName" />
</view>
</view>
<view class="divider"></view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 个性签名</view>
<view class="form-value">
<input placeholder="请输入个性签名" v-model="userInfoForm.signature" />
</view>
</view>
<view class="divider"></view>
</view>
<button
class="share"
open-type="getPhoneNumber" @getphonenumber="getPhone">
<view class="card">
<view class="card-title">手机号</view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 手机号</view>
<view class="form-value">{{ userInfoForm.phone }}</view>
</view>
<view class="divider"></view>
</view>
</button>
<view class="footer">
<uv-button type="primary" text="确认" shape="circle" size="large" @click="submit"
customStyle="width:100%;height:44px;font-size:18px;background:#0a226b;" />
</view>
</view>
</template>
<script>
export default {
components: {},
data() {
return {
avatarUrl: '',
fileList: [],
showUpload: false,
userInfoForm: {
headImage: '',
nickName: '',
phone: '',
},
}
},
onLoad() {
this.userInfoForm.phone = this.userInfo.phone || ''
this.userInfoForm.nickName = this.userInfo.nickName || ''
this.userInfoForm.headImage = this.userInfo.headImage || ''
},
methods: {
onChooseAvatar(res) {
this.$Oss.ossUpload(res.target.avatarUrl)
.then(url => {
this.userInfoForm.headImage = url
})
},
getPhone(e) {
this.$api('bindPhone', {
phoneCode: e.detail.code
}, res => {
if (res.code == 200) {
let phoneObj = JSON.parse(res.result)
if (phoneObj.errmsg == 'ok') {
this.userInfoForm.phone = phoneObj.phone_info.phoneNumber
} else {
uni.showModal({
title: phoneObj.errmsg
})
}
}
})
},
submit() {
let self = this
uni.createSelectorQuery().in(this)
.select("#nickName")
.fields({
properties: ["value"],
})
.exec((res) => {
const nickName = res?.[0]?.value
self.userInfoForm.nickName = nickName
if (self.$utils.verificationAll(self.userInfoForm, {
headImage: '请选择头像',
nickName: '请填写昵称',
phone: '请填写手机号',
})) {
return
}
self.$api('updateInfo', {
avatarUrl: self.userInfoForm.headImage,
nickName: self.userInfoForm.nickName,
phone: self.userInfoForm.phone,
}, res => {
if (res.code == 200) {
uni.reLaunch({
url: '/pages/index/index'
})
}
})
})
},
}
}
</script>
<style scoped>
.modify-info-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.back-arrow-fix {
position: absolute;
top: 60rpx;
left: 32rpx;
z-index: 1000;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 120rpx;
margin-bottom: 24rpx;
position: relative;
}
.avatar-upload {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
width: 120rpx;
height: 120rpx;
}
.avatar-main {
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}
.avatar-upload-btn {
position: absolute;
bottom: 0;
right: 0;
width: 48rpx;
height: 48rpx;
background: #0a226b;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
border: 2rpx solid #fff;
}
.card {
background: #fff;
border-radius: 24rpx;
margin: 0 24rpx 32rpx 24rpx;
padding: 32rpx 28rpx 8rpx 28rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.02);
text-align: left;
}
.card-title {
font-size: 28rpx;
font-weight: bold;
color: #222;
margin-bottom: 18rpx;
margin-top: 18rpx;
}
.form-item {
margin-bottom: 8rpx;
margin-top: 18rpx;
}
.form-label {
font-size: 22rpx;
color: #888;
margin-bottom: 4rpx;
display: flex;
align-items: center;
}
.star {
color: #f44;
font-size: 22rpx;
margin-right: 4rpx;
margin-top: 18rpx;
}
.form-value {
font-size: 26rpx;
color: #222;
font-weight: 500;
margin-bottom: 2rpx;
}
.divider {
height: 1px;
background: #f0f0f0;
margin: 8rpx 0 8rpx 0;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
margin: 0 24rpx;
}
</style>

+ 37
- 19
pages_order/auth/wxLogin.vue View File

@ -1,30 +1,28 @@
<template>
<view class="login">
<view class="logo">
<!-- <image src="/static/image/login/logo.png" mode=""></image> -->
<image :src="configList.logo_image" mode=""></image>
</view>
<view class="title">
欢迎使用酒店桌布租赁平台
欢迎使用{{ configList.logo_name }}
</view>
<view class="btn mt"
@click="wxLogin">
<view class="icon">
<!-- <view class="icon">
<image src="../static/auth/wx.png" mode=""></image>
</view>
</view> -->
<view class="">
微信授权登录
手机号授权登录
</view>
</view>
<!-- <view class="btn b2">
使用短信验证登录
</view> -->
<view class="btn b2"
@click="qux">
取消登录
</view>
<view class="config">
<uv-checkbox-group
v-model="checkboxValue"
@ -32,17 +30,16 @@
<view class="content">
<view
style="display: flex;">
<uv-checkbox
size="40rpx"
icon-size="30rpx"
activeColor="#FD5100"
activeColor="#E3441A"
:name="1"
></uv-checkbox>
阅读并同意我们的<text @click="$refs.popup.open('getPrivacyPolicy')">服务协议与隐私条款</text>
阅读并同意我们的<text @click="$refs.popup.open('user_xy')">用户协议</text>
</view>
<view class="">
以及<text @click="$refs.popup.open('getUserAgreement')">个人信息保护指引</text>
以及<text @click="$refs.popup.open('user_ys')">隐私政策</text>
</view>
</view>
</uv-checkbox-group>
@ -58,18 +55,36 @@
name : 'Login',
data() {
return {
checkboxValue : []
checkboxValue : [],
config : {},
}
},
onLoad(query) {
if (query.shareId) {
uni.setStorageSync('shareId', query.shareId)
}
if(query.path){
this.config = query
}
},
methods: {
wxLogin(){
getPhoneNumber(e){
console.log(e, e.detail.code);
this.wxLogin(phoneCode)
},
wxLogin(phoneCode){
if(!this.checkboxValue.length){
return uni.showToast({
title: '请先同意隐私协议',
icon:'none'
})
}
this.$store.commit('login')
this.$store.commit('login', this.config)
},
//
openConfigDetail(key){
this.$refs.popup.open(key)
},
qux(){
uni.reLaunch({
@ -91,8 +106,6 @@
.logo{
height: 140rpx;
width: 140rpx;
background-color: #ddd;
border-radius: 30rpx;
image{
height: 140rpx;
width: 140rpx;
@ -116,6 +129,7 @@
}
}
.btn{
all: unset;
width: 80%;
height: 100rpx;
background-color: $uni-color;
@ -125,6 +139,10 @@
align-items: center;
margin: 20rpx 0;
border-radius: 20rpx;
border: none;
&::after{
}
.icon{
margin-right: 10rpx;
image{


+ 91
- 14
pages_order/auth/wxUserInfo.vue View File

@ -1,7 +1,10 @@
<template>
<view class="login">
<!-- <view class="logo">
<image :src="configList.logo_image" mode=""></image>
</view> -->
<view class="title">
酒店桌布租赁平台
{{ configList.logo_name }}
</view>
<view class="title">
申请获取你的头像昵称
@ -13,7 +16,7 @@
头像
</view>
<view class="">
<image :src="userInfo.headImage" v-if="userInfo.headImage" style="width: 60rpx;height: 60rpx;"
<image :src="userInfoForm.headImage" v-if="userInfoForm.headImage" style="width: 60rpx;height: 60rpx;"
mode=""></image>
<image src="../static/auth/headImage.png" v-else style="width: 50rpx;height: 50rpx;" mode=""></image>
@ -26,9 +29,34 @@
</view>
<view class="">
<input type="nickname" placeholder="请输入昵称" style="text-align: right;" id="nickName"
v-model="userInfo.nickName" />
v-model="userInfoForm.nickName" />
</view>
</view>
<view class="line">
<view class="">
手机号
</view>
<view class=""
v-if="userInfoForm.phone">
<input placeholder="请输入手机号" style="text-align: right;"
disabled
v-model="userInfoForm.phone" />
</view>
<view class=""
v-else>
<button
class="getPhoneNumber"
open-type="getPhoneNumber"
@getphonenumber="getPhone">
获取电话号码
</button>
</view>
</view>
<view class="btn" @click="submit">
确认
</view>
@ -39,21 +67,42 @@
export default {
data() {
return {
userInfo: {
userInfoForm: {
headImage: '',
nickName: '',
}
phone : '',
},
};
},
onShow() {},
onLoad() {
this.userInfoForm.phone = this.userInfo.phone || ''
this.userInfoForm.nickName = this.userInfo.nickName || ''
this.userInfoForm.headImage = this.userInfo.headImage || ''
},
computed: {},
methods: {
onChooseAvatar(res) {
let self = this
self.$Oss.ossUpload(res.target.avatarUrl)
.then(url => {
self.userInfo.headImage = url
})
this.$Oss.ossUpload(res.target.avatarUrl)
.then(url => {
this.userInfoForm.headImage = url
})
},
getPhone(e){
this.$api('bindPhone', {
phoneCode : e.detail.code
}, res => {
if(res.code == 200){
let phoneObj = JSON.parse(res.result)
if(phoneObj.errmsg == 'ok'){
this.userInfoForm.phone = phoneObj.phone_info.phoneNumber
}else{
uni.showModal({
title: phoneObj.errmsg
})
}
}
})
},
submit() {
let self = this
@ -65,18 +114,23 @@
})
.exec((res) => {
const nickName = res?.[0]?.value
self.userInfo.nickName = nickName
self.userInfoForm.nickName = nickName
if (self.$utils.verificationAll(self.userInfo, {
if (self.$utils.verificationAll(self.userInfoForm, {
headImage: '请选择头像',
nickName: '请填写昵称',
phone: '请填写手机号',
})) {
return
}
self.$api('updateInfo', self.userInfo, res => {
self.$api('updateInfo', {
avatarUrl : self.userInfoForm.headImage,
nickName : self.userInfoForm.nickName,
phone : self.userInfoForm.phone,
}, res => {
if (res.code == 200) {
uni.switchTab({
uni.reLaunch({
url:'/pages/index/index'
})
}
@ -95,6 +149,16 @@
justify-content: center;
align-items: center;
height: 80vh;
.logo{
height: 140rpx;
width: 140rpx;
image{
height: 140rpx;
width: 140rpx;
border-radius: 30rpx;
}
margin-bottom: 20rpx;
}
.title {
line-height: 45rpx;
@ -129,5 +193,18 @@
border-radius: 15rpx;
margin-top: 10vh;
}
.getPhoneNumber{
// all: unset;
display: flex;
justify-content: center;
align-items: center;
// background: $uni-linear-gradient-btn-color;
background: $uni-color;
color: #fff;
width: 200rpx;
height: 60rpx;
border-radius: 30rpx;
font-size: 24rpx;
}
}
</style>

+ 173
- 196
pages_order/author/editor.vue View File

@ -5,17 +5,34 @@
<view class="form-item">
<text class="required">*</text>
<text class="label">章节名称</text>
<input class="input" type="text" placeholder='请输入章节号与章节名。例如:"第十章天降奇缘"' v-model="form.title" />
<input class="input"
type="text"
placeholder='请输入章节号与章节名。例如:"第十章天降奇缘"'
v-model="form.title" />
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">章节内容</text>
<textarea class="textarea" placeholder="请输入章节内容" v-model="form.content" />
<view class="textarea-container">
<!-- <textarea class="textarea"
placeholder="请输入章节内容"
:maxlength="100000"
v-model="form.details" /> -->
<!-- <view class="toolbar">
<view class="indent-btn" @tap="addIndent">缩进</view>
</view> -->
<uv-textarea v-model="form.details"
:maxlength="-1"
autoHeight
class="textarea"
placeholder="请输入章节内容"></uv-textarea>
</view>
</view>
</view>
<view class="footer-btns">
<button class="btn save-btn" @click="onSave">保存</button>
<button class="btn publish-btn" @click="onPublish">发布</button>
<button class="btn save-btn" @click="onPublish(0)">保存</button>
<button class="btn publish-btn" @click="onPublish(1)">发布</button>
</view>
</view>
</template>
@ -24,209 +41,169 @@
export default {
data() {
return {
formats: {},
form: {
title: '',
content: '',
details: '',
},
id : 0,
cid : 0,
}
},
onLoad() {
onLoad({id, cid}) {
this.id = id
this.cid = cid || 0
},
methods: {
onInput(e){
console.log(e.detail);
},
onEditorReady() {
uni.createSelectorQuery().select('#editor').context((res) => {
this.editorCtx = res.context
}).exec()
},
undo() {
this.editorCtx.undo()
},
redo() {
this.editorCtx.redo()
},
format(e) {
let {
name,
value
} = e.target.dataset
if (!name) return
// console.log('format', name, value)
this.editorCtx.format(name, value)
},
onStatusChange(e) {
const formats = e.detail
this.formats = formats
},
insertDivider() {
this.editorCtx.insertDivider({
success: function() {
console.log('insert divider success')
}
})
},
clear() {
uni.showModal({
title: '清空编辑器',
content: '确定清空编辑器全部内容?',
success: res => {
if (res.confirm) {
this.editorCtx.clear({
success: function(res) {
console.log("clear success")
}
})
}
}
})
},
removeFormat() {
this.editorCtx.removeFormat()
},
insertDate() {
const date = new Date()
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
this.editorCtx.insertText({
text: formatDate
async onPublish(status) {
let data = {
bookId : this.id,
num : this.form.details.length,
title : this.form.title,
details : this.form.details,
}
if(this.cid){
data.id = this.id
}
if(status == 1){
data.status = status
}
await this.$fetch('saveOrUpdateCatalog', data)
uni.showToast({
title: status ? '发布成功' : '保存成功'
})
},
insertImage() {
uni.chooseImage({
count: 1,
success: (res) => {
this.editorCtx.insertImage({
src: res.tempFilePaths[0],
alt: '图像',
success: function() {
console.log('insert image success')
}
})
}
})
},
onSave() {
//
const newChapter = {
title: this.form.title,
content: this.form.content
};
//
let chapters = uni.getStorageSync('chapters') || [];
chapters.push(newChapter);
uni.setStorageSync('chapters', chapters);
//
uni.redirectTo({
url: '/pages_order/novel/chapterList?fromSave=1'
});
},
onPublish() {
//
const newChapter = {
title: this.form.title,
content: this.form.content,
time: Date.now()
};
//
let publishedChapters = uni.getStorageSync('publishedChapters') || [];
publishedChapters.push(newChapter);
uni.setStorageSync('publishedChapters', publishedChapters);
// bookshelftab
uni.redirectTo({
url: '/pages/index/bookshelf?fromPublish=1'
});
setTimeout(uni.navigateBack, 800, -1)
},
}
}
</script>
<style scoped lang="scss">
.page {
margin-top: -920rpx;
margin-left: 30rpx;
margin-right: 30rpx;
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-box {
background: #fff;
margin: 0;
padding: 0 32rpx 0 32rpx;
border-radius: 0;
position: relative;
}
.top-fixed {
margin-top: 0;
}
.form-item {
display: flex;
flex-direction: column;
margin-top: 32rpx;
position: relative;
}
.label {
font-size: 28rpx;
color: #222;
margin-bottom: 12rpx;
margin-left: 32rpx;
}
.required {
color: #f44;
position: absolute;
left: 0;
top: 0;
font-size: 32rpx;
}
.input {
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
}
.textarea {
min-height: 180rpx;
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
resize: none;
}
.footer-btns {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
background: #fff;
display: flex;
justify-content: space-between;
padding: 24rpx 48rpx 32rpx 48rpx;
box-sizing: border-box;
z-index: 10;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
}
.btn {
flex: 1;
height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
margin: 0 12rpx;
font-weight: 500;
}
.save-btn {
background: #fff;
color: #0a225f;
border: 2rpx solid #0a225f;
}
.publish-btn {
background: #0a225f;
color: #fff;
border: none;
}
</style>
.page {
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
}
.form-box {
background: #fff;
margin: 0;
padding: 0 32rpx;
border-radius: 0;
position: relative;
}
.top-fixed {
margin-top: 0;
}
.form-item {
display: flex;
flex-direction: column;
margin-top: 32rpx;
position: relative;
}
.label {
font-size: 28rpx;
color: #222;
margin-bottom: 12rpx;
margin-left: 32rpx;
}
.required {
color: #f44;
position: absolute;
left: 0;
top: 0;
font-size: 32rpx;
}
.input {
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
}
.textarea-container {
position: relative;
line-height: 50rpx;
/deep/ .uv-textarea{
min-height: 70vh;
border: none !important;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
resize: none;
width: 100%;
}
}
.textarea {
min-height: 70vh;
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
resize: none;
width: 100%;
}
.toolbar {
display: flex;
padding: 10rpx 0;
margin-top: 10rpx;
}
.indent-btn {
background: #eaeaea;
color: #333;
padding: 8rpx 20rpx;
border-radius: 6rpx;
font-size: 26rpx;
}
.footer-btns {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
background: #fff;
display: flex;
justify-content: space-between;
padding: 24rpx 48rpx 32rpx 48rpx;
box-sizing: border-box;
z-index: 10;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.03);
}
.btn {
flex: 1;
height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
margin: 0 12rpx;
font-weight: 500;
}
.save-btn {
background: #fff;
color: #0a225f;
border: 2rpx solid #0a225f;
}
.publish-btn {
background: #0a225f;
color: #fff;
border: none;
}
</style>

+ 237
- 159
pages_order/novel/Giftbox.vue View File

@ -1,166 +1,244 @@
<template>
<!-- 礼物盒页面 -->
<view class="giftbox-container">
<navbar title="礼物盒" leftClick @leftClick="$utils.navigateBack" />
<!-- 礼物列表 -->
<view class="gift-list">
<view
v-for="(gift, idx) in gifts"
:key="gift.id"
:class="['gift-item', { selected: idx === selectedIndex }]"
@click="selectGift(idx)"
>
<view class="gift-img">
<text class="gift-emoji">{{ gift.emoji }}</text>
</view>
<view class="gift-name">{{ gift.name }}</view>
<view class="gift-info">
<text class="gift-count">x{{ gift.count }}</text>
<text class="gift-bean">{{ gift.price }}豆豆</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="giftbox-bottom">
<view class="giftbox-left">剩余{{ gifts[selectedIndex].count }} </view>
<view class="giftbox-buy">
<button class="buy-btn" @click="buyGift">购买</button>
<uv-number-box v-model="buyCount" :min="1" :max="gifts[selectedIndex].count" />
</view>
</view>
</view>
<!-- 礼物盒页面 -->
<view class="giftbox-container">
<navbar title="礼物盒" leftClick @leftClick="$utils.navigateBack" />
<!-- 礼物列表 -->
<view class="gift-list">
<view v-for="(gift, idx) in gifts" :key="gift.id"
:class="['gift-item', { selected: idx === selectedIndex }]" @click="selectGift(idx)">
<view class="gift-img">
<text class="gift-emoji">{{ gift.emoji }}</text>
</view>
<view class="gift-name">{{ gift.name }}</view>
<view class="gift-info">
<text class="gift-count">x{{ gift.count }}</text>
<text class="gift-bean">{{ gift.price }}豆豆</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="giftbox-bottom">
<view class="giftbox-left">剩余{{ gifts[selectedIndex].count }} </view>
<view class="giftbox-buy">
<button class="buy-btn" @click="buyGift">购买</button>
<uv-number-box v-model="buyCount" :min="1" :max="gifts[selectedIndex].count" />
</view>
</view>
</view>
</template>
<script>
import BackArrow from './components/BackArrow.vue';
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue'),
'uv-number-box': () => import('@/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue'),
BackArrow
},
data() {
return {
gifts: [
{ id: 1, name: '小星星', emoji: '🌟', count: 6, price: 1 },
{ id: 2, name: '爱你哟', emoji: '🧡', count: 5, price: 1 },
{ id: 3, name: '加油鸭', emoji: '🦆', count: 5, price: 1 },
{ id: 4, name: '蓝色烟花', emoji: '🎆', count: 5, price: 1 },
{ id: 5, name: '沙滩椅', emoji: '🏖️', count: 5, price: 1 },
{ id: 6, name: '菠萝', emoji: '🍍', count: 5, price: 1 },
{ id: 7, name: '咖啡', emoji: '☕', count: 5, price: 1 },
{ id: 8, name: '早餐', emoji: '🥐', count: 5, price: 1 },
{ id: 9, name: '花花', emoji: '🌷', count: 5, price: 1 },
{ id: 10, name: '玫瑰', emoji: '🌹', count: 5, price: 1 },
{ id: 11, name: '玫瑰花', emoji: '🥀', count: 5, price: 1 },
{ id: 12, name: '跑车', emoji: '🏎️', count: 5, price: 1 },
],
selectedIndex: 0,
buyCount: 1,
}
},
methods: {
selectGift(idx) {
this.selectedIndex = idx
this.buyCount = 1
},
buyGift() {
//
const gift = this.gifts[this.selectedIndex]
uni.navigateTo({
url: `/pages_order/novel/Giftpurchases?name=${encodeURIComponent(gift.name)}&price=${gift.price}&count=${this.buyCount}`
})
},
},
}
import BackArrow from './components/BackArrow.vue';
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue'),
'uv-number-box': () => import('@/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue'),
BackArrow
},
data() {
return {
gifts: [{
id: 1,
name: '小星星',
emoji: '🌟',
count: 6,
price: 1
},
{
id: 2,
name: '爱你哟',
emoji: '🧡',
count: 5,
price: 1
},
{
id: 3,
name: '加油鸭',
emoji: '🦆',
count: 5,
price: 1
},
{
id: 4,
name: '蓝色烟花',
emoji: '🎆',
count: 5,
price: 1
},
{
id: 5,
name: '沙滩椅',
emoji: '🏖️',
count: 5,
price: 1
},
{
id: 6,
name: '菠萝',
emoji: '🍍',
count: 5,
price: 1
},
{
id: 7,
name: '咖啡',
emoji: '☕',
count: 5,
price: 1
},
{
id: 8,
name: '早餐',
emoji: '🥐',
count: 5,
price: 1
},
{
id: 9,
name: '花花',
emoji: '🌷',
count: 5,
price: 1
},
{
id: 10,
name: '玫瑰',
emoji: '🌹',
count: 5,
price: 1
},
{
id: 11,
name: '玫瑰花',
emoji: '🥀',
count: 5,
price: 1
},
{
id: 12,
name: '跑车',
emoji: '🏎️',
count: 5,
price: 1
},
],
selectedIndex: 0,
buyCount: 1,
}
},
methods: {
selectGift(idx) {
this.selectedIndex = idx
this.buyCount = 1
},
buyGift() {
//
const gift = this.gifts[this.selectedIndex]
uni.navigateTo({
url: `/pages_order/novel/Giftpurchases?name=${encodeURIComponent(gift.name)}&price=${gift.price}&count=${this.buyCount}`
})
},
},
}
</script>
<style scoped lang="scss">
.giftbox-container {
min-height: 100vh;
background: #f8f8f8;
padding-bottom: 120rpx;
}
.gift-list {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
padding: 32rpx 16rpx 0 16rpx;
}
.gift-item {
width: 30%;
background: #fff;
border-radius: 18rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 0 16rpx 0;
border: 2rpx solid transparent;
transition: border 0.2s;
}
.gift-item.selected {
border: 2rpx solid #223a7a;
box-shadow: 0 4rpx 16rpx 0 rgba(34,58,122,0.08);
}
.gift-img {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
.gift-emoji {
font-size: 56rpx;
}
.gift-name {
font-size: 28rpx;
color: #222;
margin-bottom: 4rpx;
}
.gift-info {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 22rpx;
color: #888;
}
.giftbox-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
padding: 0 32rpx;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
z-index: 10;
}
.giftbox-left {
font-size: 28rpx;
color: #666;
}
.giftbox-buy {
display: flex;
align-items: center;
gap: 18rpx;
}
.buy-btn {
background: #223a7a;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
padding: 0 36rpx;
height: 68rpx;
line-height: 68rpx;
border: none;
}
</style>
.giftbox-container {
min-height: 100vh;
background: #f8f8f8;
padding-bottom: 120rpx;
}
.gift-list {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
padding: 32rpx 16rpx 0 16rpx;
}
.gift-item {
width: 30%;
background: #fff;
border-radius: 18rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 0 16rpx 0;
border: 2rpx solid transparent;
transition: border 0.2s;
}
.gift-item.selected {
border: 2rpx solid #223a7a;
box-shadow: 0 4rpx 16rpx 0 rgba(34, 58, 122, 0.08);
}
.gift-img {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
.gift-emoji {
font-size: 56rpx;
}
.gift-name {
font-size: 28rpx;
color: #222;
margin-bottom: 4rpx;
}
.gift-info {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 22rpx;
color: #888;
}
.giftbox-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
padding: 0 32rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
z-index: 10;
}
.giftbox-left {
font-size: 28rpx;
color: #666;
}
.giftbox-buy {
display: flex;
align-items: center;
gap: 18rpx;
}
.buy-btn {
background: #223a7a;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
padding: 0 36rpx;
height: 68rpx;
line-height: 68rpx;
border: none;
}
</style>

+ 0
- 185
pages_order/novel/Modifyinformation.vue View File

@ -1,185 +0,0 @@
<template>
<view class="modify-info-page">
<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" />
<view class="avatar-upload-btn" @click="showUpload = true">
<uv-icon name="camera-fill" size="28" color="#fff" />
</view>
<uv-upload
v-if="showUpload"
:fileList="fileList"
:maxCount="1"
accept="image"
:previewImage="false"
:deletable="false"
:showUploadList="false"
@afterRead="onAvatarChange"
@delete="onAvatarDelete"
/>
</view>
</view>
<view class="card">
<view class="card-title">个人信息</view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 昵称</view>
<view class="form-value">{{ form.nickname }}</view>
</view>
<view class="divider"></view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 个性签名</view>
<view class="form-value">{{ form.signature }}</view>
</view>
<view class="divider"></view>
</view>
<view class="card">
<view class="card-title">手机号</view>
<view class="form-item">
<view class="form-label"><text class="star">*</text> 手机号</view>
<view class="form-value">{{ form.phone }}</view>
</view>
<view class="divider"></view>
</view>
<view class="footer">
<uv-button type="primary" text="确认" shape="circle" size="large" @click="onSubmit" customStyle="width:100%;height:44px;font-size:18px;background:#0a226b;" />
</view>
</view>
</template>
<script>
import BackArrow from './components/BackArrow.vue'
export default {
components: { BackArrow },
data() {
return {
avatarUrl: '',
fileList: [],
showUpload: false,
form: {
nickname: '战斗世界',
signature: '世界这么美,小说好精彩~',
phone: '19989674531'
}
}
},
methods: {
goBack() {
uni.navigateBack()
},
onAvatarChange(file) {
this.avatarUrl = file.file.url
this.fileList = [file.file]
this.showUpload = false
},
onAvatarDelete() {
this.avatarUrl = ''
this.fileList = []
},
onSubmit() {
uni.showToast({ title: '提交成功', icon: 'success' })
}
}
}
</script>
<style scoped>
.modify-info-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.back-arrow-fix {
position: absolute;
top: 60rpx;
left: 32rpx;
z-index: 1000;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 120rpx;
margin-bottom: 24rpx;
position: relative;
}
.avatar-upload {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
width: 120rpx;
height: 120rpx;
}
.avatar-main {
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
}
.avatar-upload-btn {
position: absolute;
bottom: 0;
right: 0;
width: 48rpx;
height: 48rpx;
background: #0a226b;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.08);
border: 2rpx solid #fff;
}
.card {
background: #fff;
border-radius: 24rpx;
margin: 0 24rpx 32rpx 24rpx;
padding: 32rpx 28rpx 8rpx 28rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
}
.card-title {
font-size: 28rpx;
font-weight: bold;
color: #222;
margin-bottom: 18rpx;
margin-top: 18rpx;
}
.form-item {
margin-bottom: 8rpx;
margin-top: 18rpx;
}
.form-label {
font-size: 22rpx;
color: #888;
margin-bottom: 4rpx;
display: flex;
align-items: center;
}
.star {
color: #f44;
font-size: 22rpx;
margin-right: 4rpx;
margin-top: 18rpx;
}
.form-value {
font-size: 26rpx;
color: #222;
font-weight: 500;
margin-bottom: 2rpx;
}
.divider {
height: 1px;
background: #f0f0f0;
margin: 8rpx 0 8rpx 0;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
margin: 0 24rpx;
}
</style>

+ 13
- 29
pages_order/novel/chapterList.vue View File

@ -33,11 +33,10 @@
</template>
<script>
import BackArrow from './components/BackArrow.vue';
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
components: {
BackArrow,
},
data() {
return {
@ -50,7 +49,8 @@
}
],
activeTab: 0,
chapters: [] //
mixinsListApi : 'getBookCatalogList',
id : 0,
}
},
onLoad(options) {
@ -58,43 +58,27 @@
if (options.activeTab) {
this.activeTab = options.activeTab;
}
this.loadChapters();
//
if (options && options.fromSave === '1') {
uni.showToast({
title: '保存成功',
icon: 'success'
});
}
if (options && options.fromPublish === '1') {
uni.showToast({
title: '发布成功',
icon: 'success'
});
if(options.id){
this.queryParams.bookId = options.id
this.id = options.id
this.queryParams.status = this.activeTab
}
},
methods: {
loadChapters() {
if (this.activeTab === 'published') {
this.chapters = uni.getStorageSync('publishedChapters') || [];
} else {
this.chapters = uni.getStorageSync('chapters') || [];
}
},
goBack() {
uni.navigateBack()
},
clickTabs(tab) {
this.activeTab = tab.index;
this.queryParams.status = this.activeTab
this.getData()
},
addNewChapter() {
uni.navigateTo({
url: '/pages_order/author/editor'
url: '/pages_order/author/editor?id=' + this.id
})
},
editChapter(chapter) {
uni.navigateTo({
url: '/pages_order/author/editor?id=' + chapter.id
url: '/pages_order/author/editor?cid=' + chapter.id + '&id=' + this.id
})
},
handleSettings() {


+ 321
- 349
pages_order/novel/createNovel.vue View File

@ -1,356 +1,328 @@
<template>
<!-- 新建作品页面 -->
<view class="create-novel">
<navbar title="新建作品" leftClick @leftClick="$utils.navigateBack" />
<view class="form-container">
<!-- 封面信息 -->
<view class="section">
<view class="section-title">封面信息</view>
<view class="upload-cover">
<view class="sub-title">上传封面</view>
<view class="cover-box" @click="chooseCover">
<image v-if="formData.cover" :src="formData.cover" mode="aspectFill" class="cover-image"></image>
<view v-else class="upload-placeholder">
<text class="plus">+</text>
</view>
</view>
</view>
</view>
<!-- 作品信息 -->
<view class="section">
<view class="section-title">作品信息</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">作品名称</text>
<input
type="text"
v-model="formData.title"
placeholder="请输入"
placeholder-class="input-placeholder"
/>
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">作品类型</text>
<input
type="text"
v-model="formData.type"
placeholder="请输入"
placeholder-class="input-placeholder"
/>
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">作品简介</text>
<textarea
v-model="formData.description"
placeholder="请输入"
placeholder-class="input-placeholder"
:maxlength="500"
></textarea>
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">作品状态</text>
<view class="status-options">
<view
class="status-item"
:class="{ active: formData.status === 'serial' }"
@click="formData.status = 'serial'"
>
<view class="radio-dot" :class="{ checked: formData.status === 'serial' }"></view>
<text>连载</text>
</view>
<view
class="status-item"
:class="{ active: formData.status === 'completed' }"
@click="formData.status = 'completed'"
>
<view class="radio-dot" :class="{ checked: formData.status === 'completed' }"></view>
<text>完结</text>
</view>
</view>
</view>
</view>
<!-- 书籍信息 -->
<view class="section">
<view class="section-title">书籍信息</view>
<view class="form-item">
<text class="label">书号</text>
<text class="value">9999993339393</text>
</view>
<view class="form-item">
<text class="label">总字数</text>
<text class="value">99999999</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-btn" @click="submitForm">提交申请</view>
</view>
</view>
<!-- 新建作品页面 -->
<view class="create-novel">
<navbar title="新建作品" leftClick @leftClick="$utils.navigateBack" />
<view class="form-container">
<!-- 封面信息 -->
<view class="section">
<view class="section-title">封面信息</view>
<view class="upload-cover">
<view class="sub-title">上传封面</view>
<view class="cover-box" @click="chooseCover">
<image v-if="formData.Image" :src="formData.Image" mode="aspectFill" class="cover-image">
</image>
<view v-else class="upload-placeholder">
<text class="plus">+</text>
</view>
</view>
</view>
</view>
<!-- 作品信息 -->
<view class="section">
<view class="section-title">作品信息</view>
<view class="form-item">
<view style="display: flex;">
<text class="required">*</text>
<text class="label">作品名称</text>
</view>
<input type="text" v-model="formData.name" placeholder="请输入"
placeholder-class="input-placeholder" />
</view>
<view class="form-item">
<view style="display: flex;">
<text class="required">*</text>
<text class="label">作品类型</text>
</view>
<input type="text"
disabled
v-model="formData.type" placeholder="请输入"
placeholder-class="input-placeholder" />
</view>
<view class="form-item">
<view style="display: flex;">
<text class="required">*</text>
<text class="label">作品简介</text>
</view>
<textarea v-model="formData.details" placeholder="请输入" placeholder-class="input-placeholder"
:maxlength="500"></textarea>
</view>
<view class="form-item">
<view style="display: flex;">
<text class="required">*</text>
<text class="label">作品状态</text>
</view>
<view class="status-options">
<view class="status-item" :class="{ active: formData.status == '0' }"
@click="formData.status = '0'">
<view class="radio-dot" :class="{ checked: formData.status == '0' }"></view>
<text>连载</text>
</view>
<view class="status-item" :class="{ active: formData.status == '1' }"
@click="formData.status = '1'">
<view class="radio-dot" :class="{ checked: formData.status == '1' }"></view>
<text>完结</text>
</view>
</view>
</view>
</view>
<!-- 书籍信息 -->
<view class="section" v-if="formData.id">
<view class="section-title">书籍信息</view>
<view class="form-item">
<text class="label">书号</text>
<text class="value">9999993339393</text>
</view>
<view class="form-item">
<text class="label">总字数</text>
<text class="value">99999999</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-btn" @click="submitForm">提交申请</view>
</view>
</view>
</template>
<script>
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue')
},
data() {
return {
formData: {
cover: '',
title: '',
type: '',
description: '',
status: 'serial' //
}
}
},
methods: {
//
chooseCover() {
uni.chooseImage({
count: 1,
success: (res) => {
this.formData.cover = res.tempFilePaths[0]
}
})
},
//
submitForm() {
if (!this.formData.title) {
uni.showToast({
title: '请输入作品名称',
icon: 'none'
})
return
}
if (!this.formData.type) {
uni.showToast({
title: '请输入作品类型',
icon: 'none'
})
return
}
if (!this.formData.description) {
uni.showToast({
title: '请输入作品简介',
icon: 'none'
})
return
}
//
const workData = {
id: Date.now().toString(),
title: this.formData.title,
cover: this.formData.cover || 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
type: this.formData.type,
description: this.formData.description,
status: this.formData.status,
readers: '0',
publishStatus: '发布审核中',
isOriginal: true,
chapters: [],
author: '当前用户', //
tags: [this.formData.type], //
updateTime: new Date().getTime() //
}
//
let worksList = uni.getStorageSync('worksList') || []
//
worksList.unshift(workData) // 使unshift
//
uni.setStorageSync('worksList', worksList)
//
getApp().globalData.submittedWork = workData
//
getApp().globalData.showSubmitSuccess = true
//
uni.showToast({
title: '创建成功',
icon: 'success'
})
//
setTimeout(() => {
//
uni.setStorageSync('activeBookshelfTab', 'work')
//
uni.navigateBack({
delta: 1
})
}, 500)
}
}
}
export default {
data() {
return {
formData: {
Image: '',
title: '',
type: '1',
details: '',
status: '0' //
}
}
},
methods: {
//
chooseCover() {
uni.chooseImage({
count: 1,
success: (res) => {
this.$Oss.ossUpload(res.tempFilePaths[0])
.then(url => {
this.formData.Image = url
})
}
})
},
//
async submitForm() {
if (!this.formData.name) {
uni.showToast({
title: '请输入作品名称',
icon: 'none'
})
return
}
if (!this.formData.type) {
uni.showToast({
title: '请输入作品类型',
icon: 'none'
})
return
}
if (!this.formData.details) {
uni.showToast({
title: '请输入作品简介',
icon: 'none'
})
return
}
//
const workData = {
name: this.formData.name,
Image: this.formData.Image,
type: this.formData.type,
details: this.formData.details,
status: this.formData.status,
}
await this.$fetch('saveOrUpdateBook', workData)
//
setTimeout(() => {
//
uni.setStorageSync('activeBookshelfTab', 'work')
//
uni.navigateBack({
delta: 1
})
}, 500)
}
}
}
</script>
<style lang="scss">
.create-novel {
min-height: 100vh;
background-color: #F8F8F8;
.form-container {
padding: 20rpx;
.section {
background-color: #FFFFFF;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.upload-cover {
.sub-title {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
}
.cover-box {
width: 200rpx;
height: 266rpx;
background-color: #F5F5F5;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.cover-image {
width: 100%;
height: 100%;
}
.upload-placeholder {
.plus {
font-size: 60rpx;
color: #999;
}
}
}
}
.form-item {
margin-bottom: 30rpx;
position: relative;
&:last-child {
margin-bottom: 0;
}
.required {
color: #FF0000;
margin-right: 4rpx;
}
.label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
display: block;
}
input, textarea {
width: 100%;
height: 80rpx;
background-color: #F5F5F5;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
position: relative;
z-index: 1;
}
textarea {
height: 200rpx;
}
.input-placeholder {
color: #999;
}
.status-options {
display: flex;
gap: 40rpx;
.status-item {
display: flex;
align-items: center;
gap: 10rpx;
.radio-dot {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #999;
border-radius: 50%;
position: relative;
&.checked {
border-color: #001351;
&::after {
content: '';
position: absolute;
width: 20rpx;
height: 20rpx;
background-color: #001351;
border-radius: 50%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
text {
font-size: 28rpx;
color: #333;
}
}
}
.value {
font-size: 28rpx;
color: #999;
}
}
}
.submit-btn {
background-color: #001351;
color: #FFFFFF;
font-size: 32rpx;
text-align: center;
padding: 24rpx 0;
border-radius: 12rpx;
margin-top: 40rpx;
&:active {
opacity: 0.9;
}
}
}
}
</style>
.create-novel {
min-height: 100vh;
background-color: #F8F8F8;
.form-container {
padding: 20rpx;
.section {
background-color: #FFFFFF;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.upload-cover {
.sub-title {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
}
.cover-box {
width: 200rpx;
height: 266rpx;
background-color: #F5F5F5;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.cover-image {
width: 100%;
height: 100%;
}
.upload-placeholder {
.plus {
font-size: 60rpx;
color: #999;
}
}
}
}
.form-item {
margin-bottom: 30rpx;
position: relative;
&:last-child {
margin-bottom: 0;
}
.required {
color: #FF0000;
margin-right: 4rpx;
}
.label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
display: block;
}
input,
textarea {
width: 100%;
height: 80rpx;
background-color: #F5F5F5;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
position: relative;
z-index: 1;
}
textarea {
height: 200rpx;
}
.input-placeholder {
color: #999;
}
.status-options {
display: flex;
gap: 40rpx;
.status-item {
display: flex;
align-items: center;
gap: 10rpx;
.radio-dot {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #999;
border-radius: 50%;
position: relative;
&.checked {
border-color: #001351;
&::after {
content: '';
position: absolute;
width: 20rpx;
height: 20rpx;
background-color: #001351;
border-radius: 50%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
text {
font-size: 28rpx;
color: #333;
}
}
}
.value {
font-size: 28rpx;
color: #999;
}
}
}
.submit-btn {
background-color: #001351;
color: #FFFFFF;
font-size: 32rpx;
text-align: center;
padding: 24rpx 0;
border-radius: 12rpx;
margin-top: 40rpx;
&:active {
opacity: 0.9;
}
}
}
}
</style>

+ 92
- 68
pages_order/novel/novelDetail.vue View File

@ -6,20 +6,31 @@
<!-- 小说基本信息 -->
<view class="novel-info">
<view class="novel-cover">
<image :src="novelData.coverUrl" mode="aspectFill"></image>
<image :src="novelData.image &&
novelData.image.split(',')[0]" mode="aspectFill"></image>
</view>
<view class="novel-basic">
<text class="title">{{ novelData.title }}</text>
<text class="title">{{ novelData.name }}</text>
<view class="author-line">
<text class="label">作者</text>
<text class="author">{{ novelData.author }}</text>
</view>
<view class="status-line">
<!-- <view class="status-line">
<text class="label">完结</text>
<text class="status">{{ novelData.status }}</text>
</view> -->
<view class="content-row">
<view class="book-status">
<text>{{novelData.status}}</text>
</view>
<view class="book-text">
{{ novelData.service }}
</view>
</view>
<view class="score-line">
<text class="score">{{ novelData.score || 2814}}</text>
<text class="score">{{ novelData.qmNum || 0}}</text>
<text class="score-label">作者累计亲密度值</text>
</view>
</view>
@ -28,7 +39,7 @@
<!-- 推荐票数显示 -->
<view class="recommendation-section">
<view class="rec-left">
<text class="rec-count">2814</text>
<text class="rec-count">{{ novelData.tuiNum }}</text>
<text class="rec-label">推荐票数</text>
</view>
<view class="rec-divider"></view>
@ -92,10 +103,7 @@
<text>简介</text>
</view>
<view class="intro-content">
<text>这游戏也太真实了吧</text>
<text>搬砖跑腿捡垃圾送快递......公司最多能让你体会到996的艰辛在这里你能体会到超级加倍的007</text>
<text>好了不废话了伟大的管理者大人嘛就去搬砖了那位大人说了只要我们努力献上自己的肝下个月他又能换一套全新的动力甲到时候候带我们开全新的地图去广阔的废土捡更多的垃圾</text>
<text>穿越到废土世界的基光发现自己解锁了游戏所系统能够从平行世界召唤名为"玩家"的生物</text>
{{ novelData.details }}
</view>
</view>
@ -114,10 +122,6 @@
</view>
<!-- 目录弹窗完整内容内联 -->
<!-- 书评区域 -->
<view class="comments-section">
<view class="comments-header">
@ -138,20 +142,33 @@
<view class="novel-bottom">
<view class="bottom-left">
<view class="action-btn" @click="addToBookshelf">
<text class="btn-icon">📚</text>
<view class="btn-icon">
<uv-icon
name="grid"
color="#999"
size="60rpx"
></uv-icon>
</view>
<text>加入书架</text>
</view>
<view class="action-btn" @click="goToGiftbox">
<!-- <view class="action-btn" @click="goToGiftbox">
<text class="btn-icon">🎁</text>
<text>礼物盒</text>
</view>
<view class="action-btn" @click="toggleInteractive">
<text class="btn-icon">🎯</text>
</view> -->
<view class="action-btn"
@click="$utils.navigateTo(`/pages_order/novel/Giftbox?id=${novelData.id}`)">
<view class="btn-icon">
<uv-icon
name="gift"
color="#999"
size="60rpx"
></uv-icon>
</view>
<text>互动打赏</text>
</view>
</view>
<view class="bottom-right">
<button class="read-now-btn" @click="startReading">立即阅读</button>
<button class="read-now-btn" @click="">立即阅读</button>
</view>
</view>
@ -166,7 +183,9 @@
import chapterPopup from '../components/novel/chapterPopup.vue'
import commentItem from '../components/comment/commentItem.vue'
import novelVotePopup from '../components/novel/novelVotePopup.vue'
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
components: {
catalogpopup,
chapterPopup,
@ -175,23 +194,13 @@
},
data() {
return {
novelData: {
id: '1',
title: '这游戏也太真实了',
author: '大宝鉴在在',
category: '玄幻',
wordCount: '2814',
coverUrl: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
introduction: '这游戏也太真实了!\n模拟,模拟,模拟,这游戏……公空居然把自己做成了游戏的NPC?这是什么新型的套路啊!好了,不要慌了,作为的管理者人员都在忙碌着,都在工作人员中,只要我们努力就自己的事,下个月他又给一套全新的剧情开始,如何将模拟的开关全新的世界,去广阔的世界去探索吧!'
},
novelData: {},
isCollected: false,
comments: [{
username: '方寒锋',
avatar: '/static/images/user1.jpg',
content: '我是全书中年龄最大的人,这是一本很好的文章!满满心头热,只要对你"安全"文案有兴趣的,都可以不虚,不害怕,不要想太多就去尝试阅读吧!',
time: '2024-07-09'
}],
comments: [],
currentIndex: 0,
id : 0,
bookLevel : {},
mixinsListApi : 'getBookCommentList',
}
},
computed: {
@ -202,33 +211,37 @@
}
}
},
onLoad({id}) {
this.id = id
this.queryParams.id = id
this.getDateil()
this.getAchievement()
},
methods: {
goBack() {
uni.navigateBack()
getDateil(){
this.$fetch('getBookDetail', {
id : this.id
}).then(res => {
this.novelData = res
})
},
startReading() {
uni.navigateTo({
url: `/pages_order/novel/readnovels?id=${this.novelData.id}`
getAchievement(){
this.$fetch('getAchievement', {
id : this.id
}).then(res => {
this.bookLevel = res
})
},
getBookCatalogList(){
this.$fetch('getBookCatalogList', {
id : this.id
}).then(res => {
})
},
toggleCollect() {
this.isCollected = !this.isCollected
// TODO:
},
recommendNovel() {
if (!this.novelData.id) {
uni.showToast({
title: '无效的小说ID',
icon: 'none'
})
return
}
uni.navigateTo({
url: `/pages_order/novel/vote?id=${this.novelData.id}`
})
},
goToWriteReview() {
//
uni.navigateTo({
url: `/pages_order/novel/Review?title=${encodeURIComponent(this.novelData.title)}`
})
@ -241,25 +254,15 @@
})
},
toggleInteractive() {
//
uni.navigateTo({
url: `/pages_order/novel/Tipping?id=${this.novelData.id}`
})
},
goToGiftbox() {
uni.navigateTo({
url: '/pages_order/novel/Giftbox'
url: `/pages_order/novel/Giftbox?id=${this.novelData.id}`
})
},
handleShowCatalog() {
console.log('点击目录区域');
this.showCatalog = true;
},
selectChapter(idx) {
this.currentIndex = idx
this.showCatalog = false
// TODO:
},
goToCommentReply() {
uni.navigateTo({
url: '/pages_order/novel/comments'
@ -326,6 +329,26 @@
color: #666;
}
.content-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
.book-status {
flex-shrink: 0;
text {
font-size: 20rpx;
color: #67C23A;
background-color: rgba(103, 194, 58, 0.1);
border-radius: 20rpx;
padding: 4rpx 12rpx;
}
}
.book-text{
font-size: 20rpx;
}
}
.label {
color: #999;
margin-right: 8rpx;
@ -692,11 +715,11 @@
display: flex;
align-items: center;
padding: 0 30rpx;
padding-top: 15rpx;
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
gap: 40rpx;
.bottom-left {
flex: 1;
display: flex;
gap: 40rpx;
@ -719,9 +742,10 @@
}
.bottom-right {
flex-shrink: 0;
flex: 1;
display: flex;
.read-now-btn {
flex: 1;
background: #1a237e;
color: #fff;
font-size: 32rpx;


+ 30
- 42
store/store.js View File

@ -9,15 +9,11 @@ import api from '@/api/api.js'
const store = new Vuex.Store({
state: {
configList: {}, //配置列表
shop : false,
userInfo : {}, //用户信息
userInfo: {}, //用户信息
themeMode: uni.getStorageSync('themeMode') || 'light', // 主题模式:light(白天)或dark(黑夜)
isLogin : !!uni.getStorageSync('token')
},
getters: {
// 角色 true为水洗店 false为酒店
userShop(state){
return state.shop
},
// 当前主题模式
currentTheme(state) {
return state.themeMode
@ -29,24 +25,15 @@ const store = new Vuex.Store({
},
mutations: {
// 初始化配置
initConfig(state){
// api('getConfig', res => {
// if(res.code == 200){
// state.configList = res.result
// res.result.forEach(n => {
// state.configList[n.keyName] = n.keyContent
// })
// }
// })
let config = ['getPrivacyPolicy', 'getUserAgreement']
config.forEach(k => {
api(k, res => {
if (res.code == 200) {
state.configList[k] = res.result
}
})
initConfig(state) {
api('getConfig', res => {
if (res.code == 200) {
// state.configList = res.result
res.result.forEach(n => {
state.configList[n.keyName] = n.keyContent
})
}
})
},
// 切换主题模式
@ -71,55 +58,56 @@ const store = new Vuex.Store({
state.themeMode = savedMode
}
},
login(state){
login(state) {
uni.showLoading({
title: '登录中...'
})
uni.login({
success(res) {
if(res.errMsg != "login:ok"){
if (res.errMsg != "login:ok") {
return
}
api('wxLogin', {
code : res.code
code: res.code
}, res => {
uni.hideLoading()
if(res.code != 200){
if (res.code != 200) {
return
}
state.userInfo = res.result.userInfo
uni.setStorageSync('token', res.result.token)
if(!state.userInfo.nickName || !state.userInfo.headImage){
state.isLogin = true
if (!state.userInfo.nickName || !state.userInfo.headImage) {
uni.navigateTo({
url: '/pages_order/auth/wxUserInfo'
})
}else{
} else {
uni.navigateBack(-1)
}
})
}
})
},
getUserInfo(state){
api('getInfo', res => {
if(res.code == 200){
getUserInfo(state) {
api('getUserInfo', res => {
if (res.code == 200) {
state.userInfo = res.result
}
})
},
// 退出登录
logout(state){
logout(state, title = '确认退出登录吗') {
uni.showModal({
title: '确认退出登录吗',
title,
success(r) {
if(r.confirm){
if (r.confirm) {
state.userInfo = {}
state.role = false
state.isLogin = false
uni.removeStorageSync('token')
uni.reLaunch({
url: '/pages/index/index'


Loading…
Cancel
Save