小说小程序前端代码仓库(小程序)
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.

402 lines
8.7 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: formData.shopClass == 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. id: 0,
  118. // showCategoryPicker: false,
  119. categoryList: [],
  120. // selectedCategoryName: ''
  121. }
  122. },
  123. onLoad(options) {
  124. if (options.id) {
  125. this.id = options.id
  126. this.getBookInfo()
  127. }
  128. },
  129. onShow() {
  130. this.getCategoryList()
  131. },
  132. methods: {
  133. getBookInfo() {
  134. this.$fetch('getBookDetail', {
  135. id : this.id
  136. }).then(res => {
  137. this.formData = res
  138. })
  139. },
  140. // 选择封面
  141. chooseCover() {
  142. uni.chooseImage({
  143. count: 1,
  144. success: (res) => {
  145. this.$Oss.ossUpload(res.tempFilePaths[0])
  146. .then(url => {
  147. this.formData.image = url
  148. })
  149. }
  150. })
  151. },
  152. // 获取分类列表
  153. async getCategoryList() {
  154. const data = await this.$fetch('getCategoryList')
  155. this.categoryList = data
  156. },
  157. // 选择分类
  158. selectCategory(item) {
  159. this.formData.shopClass = item.id
  160. },
  161. // 提交表单
  162. async submitForm() {
  163. if (!this.formData.name) {
  164. uni.showToast({
  165. title: '请输入作品名称',
  166. icon: 'none'
  167. })
  168. return
  169. }
  170. if (!this.formData.shopClass) {
  171. uni.showToast({
  172. title: '请选择作品分类',
  173. icon: 'none'
  174. })
  175. return
  176. }
  177. if (!this.formData.details) {
  178. uni.showToast({
  179. title: '请输入作品简介',
  180. icon: 'none'
  181. })
  182. return
  183. }
  184. // 构建作品数据
  185. const workData = {
  186. name: this.formData.name,
  187. image: this.formData.image,
  188. shopClass: this.formData.shopClass,
  189. details: this.formData.details,
  190. status: this.formData.status || 0,
  191. }
  192. if(this.id){
  193. workData.id = this.id
  194. }
  195. await this.$fetch('saveOrUpdateBook', workData)
  196. // 延迟返回上一页
  197. setTimeout(() => {
  198. // 先保存当前要显示的标签
  199. uni.setStorageSync('activeBookshelfTab', 'work')
  200. // 返回上一页
  201. uni.navigateBack({
  202. delta: 1
  203. })
  204. }, 500)
  205. }
  206. }
  207. }
  208. </script>
  209. <style lang="scss">
  210. .create-novel {
  211. min-height: 100vh;
  212. background-color: #F8F8F8;
  213. .form-container {
  214. padding: 20rpx;
  215. .section {
  216. background-color: #FFFFFF;
  217. border-radius: 12rpx;
  218. padding: 30rpx;
  219. margin-bottom: 20rpx;
  220. .section-title {
  221. font-size: 32rpx;
  222. font-weight: bold;
  223. color: #333;
  224. margin-bottom: 30rpx;
  225. }
  226. .upload-cover {
  227. .sub-title {
  228. font-size: 28rpx;
  229. color: #666;
  230. margin-bottom: 20rpx;
  231. }
  232. .cover-box {
  233. width: 200rpx;
  234. height: 266rpx;
  235. background-color: #F5F5F5;
  236. border-radius: 8rpx;
  237. display: flex;
  238. align-items: center;
  239. justify-content: center;
  240. overflow: hidden;
  241. .cover-image {
  242. width: 100%;
  243. height: 100%;
  244. }
  245. .upload-placeholder {
  246. .plus {
  247. font-size: 60rpx;
  248. color: #999;
  249. }
  250. }
  251. }
  252. }
  253. .form-item {
  254. margin-bottom: 30rpx;
  255. position: relative;
  256. border-bottom: 1rpx solid #00000012;
  257. padding-bottom: 10rpx;
  258. &:last-child {
  259. margin-bottom: 0;
  260. }
  261. .required {
  262. color: #FF0000;
  263. margin-right: 4rpx;
  264. }
  265. .label {
  266. font-size: 28rpx;
  267. color: #333;
  268. margin-bottom: 16rpx;
  269. display: block;
  270. }
  271. input,
  272. textarea {
  273. width: 100%;
  274. border-radius: 8rpx;
  275. padding: 20rpx;
  276. font-size: 28rpx;
  277. color: #333;
  278. position: relative;
  279. z-index: 1;
  280. }
  281. textarea {
  282. height: 200rpx;
  283. }
  284. .input-placeholder {
  285. color: #999;
  286. }
  287. .status-options {
  288. display: flex;
  289. gap: 40rpx;
  290. .status-item {
  291. display: flex;
  292. align-items: center;
  293. gap: 10rpx;
  294. .radio-dot {
  295. width: 32rpx;
  296. height: 32rpx;
  297. border: 2rpx solid #999;
  298. border-radius: 50%;
  299. position: relative;
  300. &.checked {
  301. border-color: #001351;
  302. &::after {
  303. content: '';
  304. position: absolute;
  305. width: 20rpx;
  306. height: 20rpx;
  307. background-color: #001351;
  308. border-radius: 50%;
  309. left: 50%;
  310. top: 50%;
  311. transform: translate(-50%, -50%);
  312. }
  313. }
  314. }
  315. text {
  316. font-size: 28rpx;
  317. color: #333;
  318. }
  319. }
  320. }
  321. .value {
  322. font-size: 28rpx;
  323. color: #999;
  324. }
  325. .category-tags {
  326. display: flex;
  327. gap: 10rpx;
  328. .category-tag {
  329. padding: 8rpx 16rpx;
  330. background-color: #F5F5F5;
  331. border-radius: 8rpx;
  332. &.active {
  333. background-color: #001351;
  334. color: #FFFFFF;
  335. }
  336. }
  337. }
  338. }
  339. }
  340. .submit-btn {
  341. background-color: #001351;
  342. color: #FFFFFF;
  343. font-size: 32rpx;
  344. text-align: center;
  345. padding: 24rpx 0;
  346. border-radius: 12rpx;
  347. margin-top: 40rpx;
  348. &:active {
  349. opacity: 0.9;
  350. }
  351. }
  352. }
  353. }
  354. </style>