Browse Source

feat: 新增书籍列表页面及多项功能优化

- 新增书籍列表页面 `/pages_order/novel/bookList.vue`
- 优化书籍项组件 `novelItem.vue` 的样式和逻辑
- 新增删除作品章节和批量删除作品的功能接口
- 调整登录页面样式和交互
- 修复书籍管理页面中的删除逻辑和样式问题
master
前端-胡立永 2 weeks ago
parent
commit
e5e7d345b7
17 changed files with 618 additions and 314 deletions
  1. +12
    -0
      api/model/my_book.js
  2. +211
    -0
      components/novel/bookshelfItem.vue
  3. +14
    -14
      components/novel/novelItem.vue
  4. +5
    -3
      components/novel/workItem.vue
  5. +3
    -0
      pages.json
  6. +0
    -160
      pages/chapter/index.vue
  7. +71
    -14
      pages/index/bookshelf.vue
  8. +28
    -36
      pages/index/category.vue
  9. +19
    -13
      pages/index/center.vue
  10. +12
    -16
      pages/index/index.vue
  11. +14
    -5
      pages_order/auth/Modifyinformation.vue
  12. +5
    -5
      pages_order/auth/wxLogin.vue
  13. +5
    -5
      pages_order/auth/wxUserInfo.vue
  14. +78
    -19
      pages_order/author/chapterList.vue
  15. +83
    -24
      pages_order/author/createNovel.vue
  16. +58
    -0
      pages_order/novel/bookList.vue
  17. BIN
      static/image/center/headImage.png

+ 12
- 0
api/model/my_book.js View File

@ -28,6 +28,18 @@ const api = {
method: 'POST',
auth: true,
},
// 删除作品章节
deleteMyNovel : {
url: '/my_book/deleteMyNovel',
method: 'POST',
auth: true,
},
// 多选删除我的作品
deleteMyShopList : {
url: '/my_book/deleteMyShopList',
method: 'POST',
auth: true,
},
}
export default api

+ 211
- 0
components/novel/bookshelfItem.vue View File

@ -0,0 +1,211 @@
<template>
<view class="book-item" :class="{'horizontal': horizontal}" @click="$emit('click')">
<image class="book-cover" :src="book.image
&& book.image.split(',')[0]" mode="aspectFill"></image>
<view class="book-info">
<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.details || '暂无简介'}}</view>
<view class="content-row" v-if="!horizontal">
<view class="book-tags" v-if="book.tags && book.tags.length">
<text class="tag" v-for="(tag, index) in book.tags" :key="index">{{tag}}</text>
</view>
</view>
<view class="content-row" v-if="!horizontal">
<view class="book-status">
<text>{{ statusText }}</text>
</view>
<view class="book-text">
{{ item.service || '大家都在读' }}
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
book: {
type: Object,
default: {}
},
//
horizontal: {
type: Boolean,
default: false
},
},
computed: {
statusClass() {
const statusMap = {
'draft': 'new',
'0': 'ongoing',
'1': 'completed'
};
return statusMap[this.book.status] || 'ongoing';
},
statusText() {
const textMap = {
// '0': '',
'0': '连载中',
'1': '已完结'
};
return textMap[this.book.status] || '连载中';
},
},
data() {
return {}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.book-item {
display: flex;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
&:last-child {
border-bottom: none;
}
.book-cover {
width: 160rpx;
height: 210rpx;
border-radius: 16rpx;
margin-right: 20rpx;
box-shadow: 0 4rpx 8rpx rgba(0,0,0,0.1);
}
.book-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.book-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
margin-right: 10rpx;
}
.book-author {
font-size: 24rpx;
color: #666;
margin-bottom: 10rpx;
}
.book-desc {
font-size: 24rpx;
color: #999;
line-height: 1.5;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
margin-bottom: 10rpx;
}
.content-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.book-tags {
display: flex;
flex-wrap: wrap;
.tag {
font-size: 20rpx;
color: #666;
background-color: #f5f5f5;
padding: 4rpx 12rpx;
border-radius: 20rpx;
margin-right: 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;
}
}
}
/* 水平布局样式 - 用于网格展示 */
.book-item.horizontal {
flex-direction: column;
width: 220rpx;
padding: 10rpx;
border: none;
.book-cover {
width: 100%;
height: 300rpx;
margin-right: 0;
margin-bottom: 10rpx;
}
.book-info {
.title-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.book-title {
font-size: 26rpx;
white-space: wrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
margin-right: 5rpx;
}
.book-author {
font-size: 22rpx;
}
.book-desc {
display: none;
}
.book-tags {
display: none;
}
.book-status {
flex-shrink: 0;
text {
font-size: 18rpx;
}
}
}
}
</style>

+ 14
- 14
components/novel/novelItem.vue View File

@ -4,14 +4,14 @@
&& book.image.split(',')[0]" mode="aspectFill"></image>
<view class="book-info">
<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>
<view class="book-author" v-if="!horizontal">{{book.author || '暂无作者昵称'}}</view>
<view class="book-desc" v-if="!horizontal">{{book.details || '暂无简介'}}</view>
<view class="content-row" v-if="!horizontal">
<!-- <view class="content-row" v-if="!horizontal">
<view class="book-tags" v-if="book.tags && book.tags.length">
<text class="tag" v-for="(tag, index) in book.tags" :key="index">{{tag}}</text>
</view>
</view>
</view> -->
<view class="content-row" v-if="!horizontal">
@ -37,7 +37,7 @@
horizontal: {
type: Boolean,
default: false
}
},
},
computed: {
statusClass() {
@ -76,9 +76,9 @@
}
.book-cover {
width: 160rpx;
height: 210rpx;
border-radius: 8rpx;
width: 150rpx;
height: 196rpx;
border-radius: 16rpx;
margin-right: 20rpx;
box-shadow: 0 4rpx 8rpx rgba(0,0,0,0.1);
}
@ -96,14 +96,14 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
margin-right: 10rpx;
margin-bottom: 6rpx;
}
.book-author {
font-size: 24rpx;
color: #666;
margin-bottom: 10rpx;
color: #999;
margin-bottom: 6rpx;
flex: 1;
}
.book-desc {
@ -114,13 +114,13 @@
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
margin-bottom: 10rpx;
margin-bottom: 6rpx;
}
.content-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
margin-bottom: 6rpx;
}
.book-tags {


+ 5
- 3
components/novel/workItem.vue View File

@ -1,5 +1,5 @@
<template>
<view class="work-item" @click="handleClick">
<view class="work-item" @click.stop="handleClick">
<view class="cover-wrapper">
<image class="cover" :src="work.image && work.image.split(',')[0]" mode="aspectFill"></image>
<view class="tag original" v-if="work.isOriginal">原创</view>
@ -63,7 +63,6 @@
},
statusText() {
const textMap = {
// '0': '',
'0': '连载中',
'1': '已完结'
};
@ -104,11 +103,14 @@
},
methods: {
handleClick() {
console.log('handleClick called, isManaging:', this.isManaging);
//
if (this.isManaging) {
this.$emit('click');
return;
}
//
//
uni.navigateTo({
url: '/pages_order/author/chapterList?id=' + this.work.id
});


+ 3
- 0
pages.json View File

@ -109,6 +109,9 @@
{
"path": "novel/Translation"
},
{
"path": "novel/bookList"
},
{
"path": "auth/Modifyinformation"
}


+ 0
- 160
pages/chapter/index.vue View File

@ -1,160 +0,0 @@
<template>
<view class="chapter-container">
<view class="header">
<view class="nav-bar">
<view class="back" @click="goBack">
<text class="iconfont icon-back">&#xe60e;</text>
</view>
<view class="tabs">
<view class="tab active">草稿箱</view>
<view class="tab">已发布</view>
</view>
<view class="more">
<text class="iconfont">&#xe612;</text>
</view>
</view>
</view>
<view class="chapter-list">
<view class="chapter-item" v-for="(chapter, index) in chapters" :key="index">
<view class="chapter-info">
<text class="chapter-title">章节名</text>
<text class="chapter-number">{{index + 1}}</text>
</view>
<text class="iconfont icon-arrow">&#xe65f;</text>
</view>
</view>
<view class="bottom-actions">
<button class="btn-settings">设置作品</button>
<button class="btn-new" @click="addNewChapter">新建章节</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
chapters: Array(8).fill({}) // 8
}
},
methods: {
goBack() {
uni.navigateBack()
},
addNewChapter() {
//
uni.showToast({
title: '新建章节',
icon: 'none'
})
}
}
}
</script>
<style lang="scss" scoped>
.chapter-container {
min-height: 100vh;
background-color: #fff;
.header {
background: #fff;
padding-top: var(--status-bar-height);
.nav-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
.back {
padding: 5px;
.icon-back {
font-size: 20px;
}
}
.tabs {
display: flex;
.tab {
padding: 0 15px;
font-size: 16px;
color: #666;
&.active {
color: #000;
font-weight: bold;
}
}
}
.more {
padding: 5px;
}
}
}
.chapter-list {
padding: 0 15px;
.chapter-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #eee;
.chapter-info {
.chapter-title {
font-size: 14px;
color: #999;
margin-bottom: 5px;
}
.chapter-number {
font-size: 16px;
color: #333;
}
}
.icon-arrow {
color: #999;
font-size: 16px;
}
}
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
padding: 10px 15px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0,0,0,0.05);
button {
flex: 1;
height: 40px;
border-radius: 20px;
font-size: 16px;
margin: 0 5px;
&.btn-settings {
background: #fff;
border: 1px solid #ddd;
color: #333;
}
&.btn-new {
background: #2b4acb;
color: #fff;
border: none;
}
}
}
}
</style>

+ 71
- 14
pages/index/bookshelf.vue View File

@ -51,7 +51,8 @@
v-for="work in list"
:key="work.id"
:work="work"
@click="toWorkDetail(work.id)"
:isManaging="isEditMode"
@click="toggleSelect(work, 'work')"
@longpress="enterEditMode"
/>
@ -101,10 +102,22 @@
class="work-item-wrapper"
v-for="work in list"
:key="work.id"
@click="toggleSelect(work, 'work')"
>
<!-- 选中框 -->
<view class="item-checkbox-work" v-if="selectedItems.includes(work.id)">
<view class="checkbox-inner">
<uv-icon name="checkmark" size="28" color="#ffffff"></uv-icon>
</view>
</view>
<view class="item-checkbox-work" v-else>
<view class="checkbox-inner-no">
</view>
</view>
<work-item
:work="work"
:isManaging="true"
@click="toggleSelect(work)"
:style="{ opacity: selectedItems.includes(work.id) ? '0.8' : '1' }"
/>
</view>
@ -139,7 +152,7 @@
<script>
import tabber from '@/components/base/tabbar.vue'
import novelItem from '@/components/novel/novelItem.vue'
import novelItem from '@/components/novel/bookshelfItem.vue'
import workItem from '@/components/novel/workItem.vue'
import newWorkItem from '@/components/novel/newWorkItem.vue'
import mixinsList from '@/mixins/list.js'
@ -194,12 +207,15 @@
// this.activeTab = 'work'
// })
if(this.isLogin){
this.mixinsListApi = this.apiMap[this.activeTab]
}
},
onShow() {
// this.isEditMode = false;
// this.selectedItems = [];
if(this.isLogin){
this.mixinsListApi = this.apiMap[tab]
this.mixinsListApi = this.apiMap[this.activeTab]
}
},
onUnload() {
@ -265,7 +281,7 @@
},
//
toggleSelect(item, type) {
toggleSelect(item) {
const index = this.selectedItems.indexOf(item.id);
if (index === -1) {
this.selectedItems.push(item.id);
@ -317,8 +333,6 @@
if (res.confirm) {
if (this.activeTab === 'read') {
//
// this.novels = this.novels.filter(novel => !this.selectedItems.includes(novel.id));
await this.$fetch('batchRemoveReadBook', {
bookIds : this.selectedItems.join(',')
})
@ -331,13 +345,17 @@
this.getData()
} else {
//
this.list = this.list.filter(work => !this.selectedItems.includes(work.id));
//
uni.setStorageSync('list', this.list)
await this.$fetch('deleteMyShopList', {
bookIds: this.selectedItems.join(',')
})
uni.showToast({
title: '删除成功',
icon: 'success'
});
//
this.getData()
}
this.selectedItems = [];
@ -467,7 +485,7 @@
margin-bottom: 40rpx;
.novel-item {
width: 31%;
width: 33%;
position: relative;
.novel-tag {
@ -556,8 +574,8 @@
.edit-mode {
.item-checkbox {
position: absolute;
top: 10rpx;
left: 10rpx;
right: 20rpx;
top: 230rpx;
z-index: 10;
background-color: rgba(255,255,255,0.8);
border-radius: 50%;
@ -570,7 +588,7 @@
.checkbox-inner {
width: 40rpx;
height: 40rpx;
background-color: #1989fa;
background-color: $uni-color;
border-radius: 50%;
display: flex;
align-items: center;
@ -587,5 +605,44 @@
justify-content: center;
}
}
.item-checkbox-work {
position: absolute;
right: 20rpx;
top: 20rpx;
z-index: 10;
background-color: rgba(255,255,255,0.8);
border-radius: 50%;
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
.checkbox-inner {
width: 40rpx;
height: 40rpx;
background-color: $uni-color;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox-inner-no {
width: 40rpx;
height: 40rpx;
background-color: #fff;
border: 2rpx solid #ddd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
}
.work-item-wrapper {
position: relative;
}
}
</style>

+ 28
- 36
pages/index/category.vue View File

@ -13,9 +13,9 @@
<view class="tabs">
<uv-tabs
:list="category"
:activeStyle="{color : '#0A2463', fontWeight : 600}"
lineColor="#0A2463"
:inactiveStyle="{color: 'rgba(0,0,0,.8)'}"
:activeStyle="{color : '#000', fontWeight : 600}"
lineColor="#000"
:inactiveStyle="{color: '#000'}"
lineHeight="8rpx"
lineWidth="50rpx"
:current="current"
@ -66,7 +66,7 @@
},
data() {
return {
mixinsListApi: 'getRecommendList',
mixinsListApi: '',
current : 0,
currentChildren : 0,
categoryList: [],
@ -83,30 +83,6 @@
this.queryParams.title = search
}
//
if (this.category.length > 0) {
if (cid) {
// ID
let foundCategory = false
this.category.forEach((n, i) => {
if (n.id == cid) {
this.current = i
foundCategory = true
}
})
if (foundCategory) {
this.updateBookList(this.category[this.current].children[0].id)
} else {
//
this.updateBookList(this.category[0].children[0].id)
}
} else {
// ID
this.updateBookList(this.category[0].children[0].id)
}
}
// this.$store.commit('getCategoryList')
// if(this.category.length > 0 && cid){
// this.category.forEach((n, i) => {
@ -121,27 +97,43 @@
},
onShow() {
this.getCategoryList()
this.getBookAreaList()
// this.getBookAreaList()
},
methods: {
change(e) {
// this.queryParams.classId = this.category[e].id
this.currentChildren = e
//
this.getData(this.category[this.current].children[e].id)
this.getData()
},
clickTabs({index}){
this.current = index
this.currentChildren = 0
//
this.getData(this.category[index].children[0].id)
this.getData()
},
search(){
this.getData()
},
beforeGetData(){
return {
shopCion : this.categoryList[this.currentChildren].id,
shopClass : this.category[this.current].id,
}
},
async getCategoryList() {
const data = await this.$fetch('getCategoryList')
this.category = data
// const data = await this.$fetch('getCategoryList')
// this.category = data
let [data1, data2] = await Promise.all([this.$fetch('getCategoryList'), this.$fetch('getBookAreaList')])
this.category = data1
this.categoryList = data2
this.mixinsListApi = 'getRecommendList'
this.queryParams.pageSize = 999999
this.getData()
},
async getBookAreaList() {
const data = await this.$fetch('getBookAreaList')
@ -154,15 +146,15 @@
<style scoped lang="scss">
.page {
/deep/ .uv-vtabs {
height: calc(100vh - 600rpx) !important;
height: calc(100vh - 460rpx) !important;
}
/deep/ .uv-vtabs__bar {
height: calc(100vh - 600rpx) !important;
height: calc(100vh - 460rpx) !important;
}
/deep/ .uv-vtabs__content {
height: calc(100vh - 600rpx) !important;
height: calc(100vh - 460rpx) !important;
}
.search {


+ 19
- 13
pages/index/center.vue View File

@ -76,13 +76,17 @@
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="$utils.navigateTo('/pages/customer-service')">
<view class="section-item-left">
<uv-icon name="star" size="40rpx" color="#333"></uv-icon>
<text>联系客服</text>
<button open-type="contact" class="share">
<view class="section-item">
<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>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
</button>
<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>
@ -107,7 +111,7 @@
<view v-else-if="!isLogin" class="nologin-page">
<view class="nologin-top-bg">
<view class="nologin-header">
<image class="nologin-avatar" mode="aspectFill" @click="$utils.toLogin" />
<image class="nologin-avatar" src="/static/image/center/headImage.png" mode="aspectFill" @click="$utils.toLogin" />
<view class="nologin-text" @click="$utils.toLogin">点击登录</view>
</view>
<view class="nologin-header-right">
@ -116,6 +120,7 @@
</view>
<view class="nologin-content-center">
<button class="nologin-btn" @click="$utils.toLogin">立即登录</button>
<view style="font-size: 24rpx;color: #999;">暂未登录请先登录</view>
</view>
</view>
@ -263,8 +268,8 @@
}
.nologin-top-bg {
width: 100vw;
height: 220rpx;
background: linear-gradient(180deg, #f3eafe 0%, #fff 100%);
height: 580rpx;
background: linear-gradient(180deg, #f3eafe 0%, #f5f5f5 100%);
position: relative;
display: flex;
flex-direction: row;
@ -288,7 +293,8 @@
}
.nologin-text {
font-size: 28rpx;
color: #333;
color: #000;
font-weight: 900;
}
.nologin-header-right {
position: absolute;
@ -307,12 +313,12 @@
z-index: 2;
}
.nologin-btn {
width: 320rpx;
height: 64rpx;
width: 380rpx;
height: 74rpx;
background: #132a57;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
border-radius: 38rpx;
margin-bottom: 18rpx;
font-weight: bold;
display: flex;


+ 12
- 16
pages/index/index.vue View File

@ -2,9 +2,10 @@
<view class="page">
<!-- 自定义导航栏 -->
<view class="custom-navbar">
<view class="navbar-left">
<view class="navbar-left"
@click="$utils.navigateTo('/pages_order/novel/Translation')">
<view class="calendar-icon">
<uv-icon name="calendar" color="#ffffff" size="44rpx"></uv-icon>
<uv-icon name="calendar" color="#ffffff" size="54rpx"></uv-icon>
</view>
</view>
<view class="navbar-center" @click="toSearch">
@ -14,12 +15,7 @@
</view>
</view>
<view class="navbar-right">
<view class="function-btn">
<uv-icon name="more-dot-fill" color="#ffffff" size="44rpx"></uv-icon>
</view>
<view class="function-btn">
<uv-icon name="chat-fill" color="#ffffff" size="44rpx"></uv-icon>
</view>
</view>
</view>
@ -32,8 +28,8 @@
:autoplay="true"
:circular="true"
:interval="3000"
radius="10"
:height="400"
radius="30rpx"
height="250rpx"
indicatorActiveColor="#ffffff"
indicatorInactiveColor="rgba(255, 255, 255, 0.5)"
@click="clickBanner"
@ -143,7 +139,7 @@
methods: {
//
toSearch() {
this.$utils.navigateTo('/pages/search/index')
this.$utils.navigateTo('/pages_order/novel/bookList')
},
//
clickBanner(index) {
@ -196,14 +192,13 @@
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx 20rpx 20rpx;
padding: 0 20rpx 30rpx 20rpx;
padding-top: calc(var(--status-bar-height) + 50rpx);
background-color: #001351; /* 深蓝色背景 */
height: 160rpx;
box-sizing: border-box;
position: sticky;
top: 0;
z-index: 99;
padding-top: calc(var(--status-bar-height) + 20rpx);
.navbar-left {
width: 80rpx;
@ -223,12 +218,13 @@
margin: 0 20rpx;
.search-box {
width: 420rpx;
display: flex;
align-items: center;
background-color: rgba(255, 255, 255, 0.95);
background-color: rgba(255, 255, 255, 0.2);
border-radius: 40rpx;
padding: 0 30rpx;
height: 70rpx;
height: 60rpx;
box-sizing: border-box;
.search-placeholder {


+ 14
- 5
pages_order/auth/Modifyinformation.vue View File

@ -89,9 +89,9 @@
if (phoneObj.errmsg == 'ok') {
this.userInfoForm.phone = phoneObj.phone_info.phoneNumber
} else {
uni.showModal({
title: phoneObj.errmsg
})
// uni.showModal({
// title: phoneObj.errmsg
// })
}
}
})
@ -122,9 +122,18 @@
phone: self.userInfoForm.phone,
}, res => {
if (res.code == 200) {
uni.reLaunch({
url: '/pages/index/index'
// uni.reLaunch({
// url: '/pages/index/index'
// })
uni.showToast({
title: '修改成功',
icon: 'none',
success() {
uni.navigateBack(-1)
}
})
}
})
})


+ 5
- 5
pages_order/auth/wxLogin.vue View File

@ -33,7 +33,7 @@
<uv-checkbox
size="40rpx"
icon-size="30rpx"
activeColor="#E3441A"
activeColor="#0A2463"
:name="1"
></uv-checkbox>
阅读并同意我们的<text @click="$refs.popup.open('user_xy')">用户协议</text>
@ -100,9 +100,10 @@
display: flex;
justify-content: center;
align-items: center;
height: 80vh;
flex-direction: column;
position: relative;
background-color: #e5eaf9;
min-height: 100vh;
.logo{
height: 140rpx;
width: 140rpx;
@ -138,7 +139,7 @@
justify-content: center;
align-items: center;
margin: 20rpx 0;
border-radius: 20rpx;
border-radius: 60rpx;
border: none;
&::after{
@ -160,8 +161,7 @@
}
.config{
position: absolute;
bottom: 0;
margin-top: 30rpx;
font-size: 24rpx;
text-align: center;
line-height: 40rpx;


+ 5
- 5
pages_order/auth/wxUserInfo.vue View File

@ -1,8 +1,8 @@
<template>
<view class="login">
<!-- <view class="logo">
<view class="logo">
<image :src="configList.logo_image" mode=""></image>
</view> -->
</view>
<view class="title">
{{ configList.logo_name }}
</view>
@ -97,9 +97,9 @@
if(phoneObj.errmsg == 'ok'){
this.userInfoForm.phone = phoneObj.phone_info.phoneNumber
}else{
uni.showModal({
title: phoneObj.errmsg
})
// uni.showModal({
// title: ''
// })
}
}
})


+ 78
- 19
pages_order/author/chapterList.vue View File

@ -16,22 +16,46 @@
</view>
<view class="box">
<view class="chapter-list" >
<view class="draft-header">
<text class="draft-title">{{ activeTab === 0 ? '草稿箱章节' : '已发布章节' }}</text>
<text class="delete-btn" @click="reverseList">{{ queryParams.reverse ? '正序' : '倒序' }}</text>
</view>
<view
class="chapter-item"
v-for="(chapter, index) in list"
:key="chapter.id"
@click="editChapter(chapter)">
<view class="chapter-info">
<text class="chapter-title">章节名</text>
<text class="chapter-number">{{ chapter.title }}</text>
</view>
<uv-icon name="arrow-right" color="#999" size="28"></uv-icon>
</view>
<view class="draft-header">
<text class="draft-title">{{ activeTab === 0 ? '草稿箱章节' : '已发布章节' }}</text>
<text class="delete-btn" @click="reverseList">{{ queryParams.reverse ? '正序' : '倒序' }}</text>
</view>
<view class="chapter-list">
<!-- 章节列表 -->
<uv-swipe-action>
<uv-swipe-action-item
v-for="(chapter, index) in list"
:key="chapter.id"
:options="swipeOptions"
@click="deleteChapter(chapter, index)"
>
<view
class="chapter-item"
@click="editChapter(chapter)"
>
<view class="chapter-info">
<text class="chapter-title">章节名</text>
<text class="chapter-number">{{ chapter.title }}</text>
</view>
<uv-icon name="arrow-right" color="#999" size="28"></uv-icon>
</view>
</uv-swipe-action-item>
</uv-swipe-action>
<!-- 空状态 -->
<view style="padding-bottom: 60rpx;"
v-if="list.length === 0" >
<uv-empty
:text="activeTab === 0 ? '还没有草稿章节' : '还没有发布章节'"
:icon="activeTab === 0 ? 'edit-pen' : 'list'"
iconSize="60"
iconColor="#ccc"
textColor="#999"
textSize="28"
mode="data"
/>
</view>
</view>
</view>
@ -63,6 +87,12 @@
activeTab: 0,
mixinsListApi : 'getMyShopNovelPage',
id : 0,
swipeOptions: [{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}]
}
},
onLoad(options) {
@ -103,7 +133,36 @@
},
handleSettings() {
uni.navigateTo({
url: '/pages_order/novel/createNovel?id=' + this.id
url: '/pages_order/author/createNovel?id=' + this.id
})
},
//
deleteChapter(chapter, index) {
uni.showModal({
title: '提示',
content: `确定要删除章节"${chapter.title}"吗?`,
success: async (res) => {
if (res.confirm) {
try {
await this.$fetch('deleteMyNovel', {
id: chapter.id
})
uni.showToast({
title: '删除成功',
icon: 'success'
})
//
this.getData()
} catch (error) {
uni.showToast({
title: '删除失败',
icon: 'none'
})
}
}
}
})
},
deleteAll() {
@ -138,12 +197,12 @@
}
.box{
padding: 20rpx 40rpx;
background-color: #fff;
margin: 50rpx 40rpx;
border-radius: 20rpx;
.draft-header {
padding: 20rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
@ -165,10 +224,10 @@
.chapter-list {
.chapter-item {
padding: 20rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #eee;
.chapter-info {


+ 83
- 24
pages_order/author/createNovel.vue View File

@ -1,7 +1,7 @@
<template>
<!-- 新建作品页面 -->
<view class="create-novel">
<navbar title="新建作品" leftClick @leftClick="$utils.navigateBack" />
<navbar :title="id ? '设置作品' : '新建作品'" leftClick @leftClick="$utils.navigateBack" />
<view class="form-container">
<!-- 封面信息 -->
@ -10,7 +10,7 @@
<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 v-if="formData.image" :src="formData.image" mode="aspectFill" class="cover-image">
</image>
<view v-else class="upload-placeholder">
<text class="plus">+</text>
@ -27,19 +27,42 @@
<text class="required">*</text>
<text class="label">作品名称</text>
</view>
<input type="text" v-model="formData.name" placeholder="请输入"
<input shopClass="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>
<text class="label">作品分类</text>
</view>
<view class="">
<!-- 原有的picker方案 -->
<!-- <view class="category-select" @click="showCategoryPicker = true">
<text v-if="formData.shopClass" class="selected-category">{{selectedCategoryName}}</text>
<text v-else class="placeholder">请选择</text>
</view>
<uv-picker
:show="showCategoryPicker"
:columns="[categoryList]"
@confirm="confirmCategory"
@cancel="showCategoryPicker = false"
></uv-picker> -->
<!-- 新的标签选择方案 -->
<view class="category-tags">
<view
v-for="(item, index) in categoryList"
:key="index"
class="category-tag"
:class="{ active: formData.shopClass == item.id }"
@click="selectCategory(item)"
>
{{item.title}}
</view>
</view>
</view>
<input type="text"
disabled
v-model="formData.type" placeholder="请输入"
placeholder-class="input-placeholder" />
</view>
<view class="form-item">
@ -72,20 +95,20 @@
</view>
<!-- 书籍信息 -->
<view class="section" v-if="formData.id">
<view class="section">
<view class="section-title">书籍信息</view>
<view class="form-item">
<text class="label">书号</text>
<text class="value">9999993339393</text>
<text class="value">{{ formData.id || '_' }}</text>
</view>
<view class="form-item">
<text class="label">总字数</text>
<text class="value">99999999</text>
<text class="value">{{ formData.num || '_' }}</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-btn" @click="submitForm">提交申请</view>
<view class="submit-btn" @click="submitForm">{{ id ? '保存' : "提交申请" }}</view>
</view>
</view>
</template>
@ -95,13 +118,16 @@
data() {
return {
formData: {
Image: '',
title: '',
type: '1',
image: '',
name: '',
shopClass: '',
details: '',
status: '0' //
},
id: 0
id: 0,
// showCategoryPicker: false,
categoryList: [],
// selectedCategoryName: ''
}
},
onLoad(options) {
@ -109,6 +135,9 @@
this.id = options.id
this.getBookInfo()
}
},
onShow() {
this.getCategoryList()
},
methods: {
@ -128,13 +157,24 @@
this.$Oss.ossUpload(res.tempFilePaths[0])
.then(url => {
this.formData.Image = url
this.formData.image = url
})
}
})
},
//
async getCategoryList() {
const data = await this.$fetch('getCategoryList')
this.categoryList = data
},
//
selectCategory(item) {
this.formData.shopClass = item.id
},
//
async submitForm() {
if (!this.formData.name) {
@ -144,9 +184,9 @@
})
return
}
if (!this.formData.type) {
if (!this.formData.shopClass) {
uni.showToast({
title: '请输入作品类型',
title: '请选择作品分类',
icon: 'none'
})
return
@ -162,12 +202,16 @@
//
const workData = {
name: this.formData.name,
Image: this.formData.Image,
type: this.formData.type,
image: this.formData.image,
shopClass: this.formData.shopClass,
details: this.formData.details,
status: this.formData.status || 0,
}
if(this.id){
workData.id = this.id
}
await this.$fetch('saveOrUpdateBook', workData)
//
@ -240,6 +284,8 @@
.form-item {
margin-bottom: 30rpx;
position: relative;
border-bottom: 1rpx solid #00000012;
padding-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
@ -260,13 +306,10 @@
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;
}
@ -323,6 +366,22 @@
font-size: 28rpx;
color: #999;
}
.category-tags {
display: flex;
gap: 10rpx;
.category-tag {
padding: 8rpx 16rpx;
background-color: #F5F5F5;
border-radius: 8rpx;
&.active {
background-color: #001351;
color: #FFFFFF;
}
}
}
}
}


+ 58
- 0
pages_order/novel/bookList.vue View File

@ -0,0 +1,58 @@
<template>
<view class="page">
<navbar title="搜素" leftClick @leftClick="$utils.navigateBack" />
<view class="search">
<uv-search bgColor="#ffffff"
placeholder="搜索"
inputAlign="left"
@search="handleSearch"
v-model="keyword"
@change="searchChange"
showAction="false"></uv-search>
</view>
<view class="novel-list">
<novel-item v-for="(item, index) in list"
@click="navigateToDetail(item.id)"
:key="index" :book="item" />
</view>
</view>
</template>
<script>
import novelItem from '@/components/novel/novelItem.vue'
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
components: {
novelItem,
},
data() {
return {
mixinsListApi : 'getRecommendList',
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.search {
background-color: #fff;
border-radius: 40rpx;
display: flex;
padding: 20rpx;
margin: 20rpx;
:deep(.uv-search__content) {
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.1);
}
}
.novel-list{
padding: 20rpx;
}
</style>

BIN
static/image/center/headImage.png View File

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

Loading…
Cancel
Save