小说小程序前端代码仓库(小程序)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
8.9 KiB

  1. <template>
  2. <!-- 新建作品页面 -->
  3. <view class="create-novel">
  4. <navbar :title="id ? '设置作品' : '新建作品'" leftClick @leftClick="$utils.navigateBack" />
  5. <view class="form-container">
  6. <!-- 封面信息 -->
  7. <view class="section">
  8. <view class="section-title">封面信息</view>
  9. <view class="upload-cover">
  10. <view class="sub-title">上传封面</view>
  11. <view class="cover-box" @click="chooseCover">
  12. <image v-if="formData.image" :src="formData.image" mode="aspectFill" class="cover-image">
  13. </image>
  14. <view v-else class="upload-placeholder">
  15. <text class="plus">+</text>
  16. </view>
  17. </view>
  18. </view>
  19. </view>
  20. <!-- 作品信息 -->
  21. <view class="section">
  22. <view class="section-title">作品信息</view>
  23. <view class="form-item">
  24. <view style="display: flex;">
  25. <text class="required">*</text>
  26. <text class="label">作品名称</text>
  27. </view>
  28. <input shopClass="text" v-model="formData.name" placeholder="请输入"
  29. placeholder-class="input-placeholder" />
  30. </view>
  31. <view class="form-item">
  32. <view style="display: flex;">
  33. <text class="required">*</text>
  34. <text class="label">作品分类</text>
  35. </view>
  36. <view class="">
  37. <!-- 原有的picker方案 -->
  38. <!-- <view class="category-select" @click="showCategoryPicker = true">
  39. <text v-if="formData.shopClass" class="selected-category">{{selectedCategoryName}}</text>
  40. <text v-else class="placeholder">请选择</text>
  41. </view>
  42. <uv-picker
  43. :show="showCategoryPicker"
  44. :columns="[categoryList]"
  45. @confirm="confirmCategory"
  46. @cancel="showCategoryPicker = false"
  47. ></uv-picker> -->
  48. <!-- 新的标签选择方案 -->
  49. <view class="category-tags">
  50. <view
  51. v-for="(item, index) in categoryList"
  52. :key="index"
  53. class="category-tag"
  54. :class="{ active: classList.includes(item.id) }"
  55. @click="selectCategory(item)"
  56. >
  57. {{item.title}}
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. <view class="form-item">
  63. <view style="display: flex;">
  64. <text class="required">*</text>
  65. <text class="label">作品简介</text>
  66. </view>
  67. <textarea v-model="formData.details" placeholder="请输入" placeholder-class="input-placeholder"
  68. :maxlength="500"></textarea>
  69. </view>
  70. <view class="form-item">
  71. <view style="display: flex;">
  72. <text class="required">*</text>
  73. <text class="label">作品状态</text>
  74. </view>
  75. <view class="status-options">
  76. <view class="status-item" :class="{ active: formData.status == '0' }"
  77. @click="formData.status = '0'">
  78. <view class="radio-dot" :class="{ checked: formData.status == '0' }"></view>
  79. <text>连载</text>
  80. </view>
  81. <view class="status-item" :class="{ active: formData.status == '1' }"
  82. @click="formData.status = '1'">
  83. <view class="radio-dot" :class="{ checked: formData.status == '1' }"></view>
  84. <text>完结</text>
  85. </view>
  86. </view>
  87. </view>
  88. </view>
  89. <!-- 书籍信息 -->
  90. <view class="section">
  91. <view class="section-title">书籍信息</view>
  92. <view class="form-item">
  93. <text class="label">书号</text>
  94. <text class="value">{{ formData.id || '_' }}</text>
  95. </view>
  96. <view class="form-item">
  97. <text class="label">总字数</text>
  98. <text class="value">{{ formData.bookSum || '_' }}</text>
  99. </view>
  100. </view>
  101. <!-- 提交按钮 -->
  102. <view class="submit-btn" @click="submitForm">{{ id ? '保存' : "提交申请" }}</view>
  103. </view>
  104. </view>
  105. </template>
  106. <script>
  107. export default {
  108. data() {
  109. return {
  110. formData: {
  111. image: '',
  112. name: '',
  113. shopClass: '',
  114. details: '',
  115. status: '0' // 默认连载
  116. },
  117. classList : [],
  118. id: 0,
  119. // showCategoryPicker: false,
  120. categoryList: [],
  121. // selectedCategoryName: ''
  122. }
  123. },
  124. onLoad(options) {
  125. if (options.id) {
  126. this.id = options.id
  127. this.getBookInfo()
  128. }
  129. },
  130. onShow() {
  131. this.getCategoryList()
  132. },
  133. methods: {
  134. getBookInfo() {
  135. this.$fetch('getBookDetail', {
  136. id : this.id
  137. }).then(res => {
  138. this.formData = res
  139. this.classList = res.shopClass ? res.shopClass.split(',') : []
  140. })
  141. },
  142. // 选择封面
  143. chooseCover() {
  144. uni.chooseImage({
  145. count: 1,
  146. success: (res) => {
  147. this.$Oss.ossUpload(res.tempFilePaths[0])
  148. .then(url => {
  149. this.formData.image = url
  150. })
  151. }
  152. })
  153. },
  154. // 获取分类列表
  155. async getCategoryList() {
  156. const data = await this.$fetch('getCategoryList')
  157. this.categoryList = data
  158. },
  159. // 选择分类
  160. selectCategory(item) {
  161. let index = this.classList.indexOf(item.id)
  162. if(index != -1){
  163. this.classList.splice(index, 1)
  164. }else{
  165. this.classList.push(item.id)
  166. }
  167. },
  168. // 提交表单
  169. async submitForm() {
  170. if (!this.formData.name) {
  171. uni.showToast({
  172. title: '请输入作品名称',
  173. icon: 'none'
  174. })
  175. return
  176. }
  177. if (!this.formData.shopClass) {
  178. uni.showToast({
  179. title: '请选择作品分类',
  180. icon: 'none'
  181. })
  182. return
  183. }
  184. if (!this.formData.details) {
  185. uni.showToast({
  186. title: '请输入作品简介',
  187. icon: 'none'
  188. })
  189. return
  190. }
  191. // 构建作品数据
  192. const workData = {
  193. name: this.formData.name,
  194. image: this.formData.image,
  195. shopClass: this.classList.join(','),
  196. details: this.formData.details,
  197. status: this.formData.status || 0,
  198. }
  199. if(this.id){
  200. workData.id = this.id
  201. }
  202. await this.$fetch('saveOrUpdateBook', workData)
  203. // 延迟返回上一页
  204. setTimeout(() => {
  205. // 先保存当前要显示的标签
  206. uni.setStorageSync('activeBookshelfTab', 'work')
  207. // 返回上一页
  208. uni.navigateBack({
  209. delta: 1
  210. })
  211. }, 500)
  212. }
  213. }
  214. }
  215. </script>
  216. <style lang="scss">
  217. .create-novel {
  218. min-height: 100vh;
  219. background-color: #F8F8F8;
  220. .form-container {
  221. padding: 20rpx;
  222. .section {
  223. background-color: #FFFFFF;
  224. border-radius: 12rpx;
  225. padding: 30rpx;
  226. margin-bottom: 20rpx;
  227. .section-title {
  228. font-size: 32rpx;
  229. font-weight: bold;
  230. color: #333;
  231. margin-bottom: 30rpx;
  232. }
  233. .upload-cover {
  234. .sub-title {
  235. font-size: 28rpx;
  236. color: #666;
  237. margin-bottom: 20rpx;
  238. }
  239. .cover-box {
  240. width: 200rpx;
  241. height: 266rpx;
  242. background-color: #F5F5F5;
  243. border-radius: 8rpx;
  244. display: flex;
  245. align-items: center;
  246. justify-content: center;
  247. overflow: hidden;
  248. .cover-image {
  249. width: 100%;
  250. height: 100%;
  251. }
  252. .upload-placeholder {
  253. .plus {
  254. font-size: 60rpx;
  255. color: #999;
  256. }
  257. }
  258. }
  259. }
  260. .form-item {
  261. margin-bottom: 30rpx;
  262. position: relative;
  263. border-bottom: 1rpx solid #00000012;
  264. padding-bottom: 10rpx;
  265. &:last-child {
  266. margin-bottom: 0;
  267. }
  268. .required {
  269. color: #FF0000;
  270. margin-right: 4rpx;
  271. }
  272. .label {
  273. font-size: 28rpx;
  274. color: #333;
  275. margin-bottom: 16rpx;
  276. display: block;
  277. }
  278. input,
  279. textarea {
  280. width: 100%;
  281. border-radius: 8rpx;
  282. padding: 20rpx;
  283. font-size: 28rpx;
  284. color: #333;
  285. position: relative;
  286. z-index: 1;
  287. }
  288. textarea {
  289. height: 200rpx;
  290. }
  291. .input-placeholder {
  292. color: #999;
  293. }
  294. .status-options {
  295. display: flex;
  296. gap: 40rpx;
  297. .status-item {
  298. display: flex;
  299. align-items: center;
  300. gap: 10rpx;
  301. .radio-dot {
  302. width: 32rpx;
  303. height: 32rpx;
  304. border: 2rpx solid #999;
  305. border-radius: 50%;
  306. position: relative;
  307. &.checked {
  308. border-color: #001351;
  309. &::after {
  310. content: '';
  311. position: absolute;
  312. width: 20rpx;
  313. height: 20rpx;
  314. background-color: #001351;
  315. border-radius: 50%;
  316. left: 50%;
  317. top: 50%;
  318. transform: translate(-50%, -50%);
  319. }
  320. }
  321. }
  322. text {
  323. font-size: 28rpx;
  324. color: #333;
  325. }
  326. }
  327. }
  328. .value {
  329. font-size: 28rpx;
  330. color: #999;
  331. }
  332. .category-tags {
  333. display: flex;
  334. gap: 10rpx;
  335. .category-tag {
  336. padding: 8rpx 16rpx;
  337. background-color: #F5F5F5;
  338. border-radius: 8rpx;
  339. &.active {
  340. background-color: #001351;
  341. color: #FFFFFF;
  342. }
  343. }
  344. }
  345. }
  346. }
  347. .submit-btn {
  348. background-color: #001351;
  349. color: #FFFFFF;
  350. font-size: 32rpx;
  351. text-align: center;
  352. padding: 24rpx 0;
  353. border-radius: 12rpx;
  354. margin-top: 40rpx;
  355. &:active {
  356. opacity: 0.9;
  357. }
  358. }
  359. }
  360. }
  361. </style>