小说网站前端代码仓库
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.

302 lines
9.9 KiB

  1. <template>
  2. <div class="create-work-container">
  3. <div class="create-work-content">
  4. <h2 class="page-title">创建新作品</h2>
  5. <div class="form-container">
  6. <el-form :model="workForm" :rules="rules" ref="workFormRef" label-position="top">
  7. <el-form-item label="作品名称" prop="title">
  8. <el-input v-model="workForm.title" placeholder="请输入作品名称"></el-input>
  9. </el-form-item>
  10. <el-form-item label="作品分类" prop="category">
  11. <el-select v-model="workForm.category" placeholder="请选择分类">
  12. <el-option v-for="item in categories" :key="item.id" :label="item.name" :value="item.id"></el-option>
  13. </el-select>
  14. </el-form-item>
  15. <el-form-item label="作品标签" prop="tags">
  16. <el-select
  17. v-model="workForm.tags"
  18. multiple
  19. placeholder="请选择标签(最多选择3个)"
  20. :multiple-limit="3"
  21. >
  22. <el-option v-for="item in tags" :key="item.id" :label="item.name" :value="item.id"></el-option>
  23. </el-select>
  24. </el-form-item>
  25. <el-form-item label="简介" prop="description">
  26. <el-input
  27. v-model="workForm.description"
  28. type="textarea"
  29. :rows="5"
  30. placeholder="请输入作品简介"
  31. maxlength="500"
  32. show-word-limit
  33. ></el-input>
  34. </el-form-item>
  35. <el-form-item label="封面图片">
  36. <el-upload
  37. class="cover-uploader"
  38. action="#"
  39. :http-request="uploadCover"
  40. :show-file-list="false"
  41. :before-upload="beforeUpload"
  42. >
  43. <img v-if="workForm.cover" :src="workForm.cover" class="cover-image" />
  44. <div v-else class="cover-placeholder">
  45. <el-icon><Plus /></el-icon>
  46. <div class="upload-text">点击上传封面</div>
  47. </div>
  48. </el-upload>
  49. <div class="upload-tip">建议尺寸300x400像素JPG/PNG格式</div>
  50. </el-form-item>
  51. <div class="form-actions">
  52. <el-button @click="goBack">取消</el-button>
  53. <el-button type="primary" @click="submitForm" :loading="loading">创建作品</el-button>
  54. </div>
  55. </el-form>
  56. </div>
  57. </div>
  58. </div>
  59. </template>
  60. <script>
  61. import { defineComponent, ref, reactive } from 'vue';
  62. import { useRouter } from 'vue-router';
  63. import { ElMessage } from 'element-plus';
  64. import { Plus } from '@element-plus/icons-vue';
  65. export default defineComponent({
  66. name: 'CreateWork',
  67. components: {
  68. Plus
  69. },
  70. setup() {
  71. const router = useRouter();
  72. const workFormRef = ref(null);
  73. const loading = ref(false);
  74. // 表单数据
  75. const workForm = reactive({
  76. title: '',
  77. category: '',
  78. tags: [],
  79. description: '',
  80. cover: ''
  81. });
  82. // 表单验证规则
  83. const rules = {
  84. title: [
  85. { required: true, message: '请输入作品名称', trigger: 'blur' },
  86. { min: 2, max: 30, message: '长度应为2-30个字符', trigger: 'blur' }
  87. ],
  88. category: [
  89. { required: true, message: '请选择作品分类', trigger: 'change' }
  90. ],
  91. description: [
  92. { required: true, message: '请输入作品简介', trigger: 'blur' },
  93. { min: 10, max: 500, message: '简介长度应为10-500个字符', trigger: 'blur' }
  94. ]
  95. };
  96. // 分类和标签数据,实际应用中应该从API获取
  97. const categories = ref([
  98. { id: 1, name: '玄幻' },
  99. { id: 2, name: '奇幻' },
  100. { id: 3, name: '武侠' },
  101. { id: 4, name: '仙侠' },
  102. { id: 5, name: '都市' },
  103. { id: 6, name: '历史' },
  104. { id: 7, name: '军事' },
  105. { id: 8, name: '科幻' }
  106. ]);
  107. const tags = ref([
  108. { id: 1, name: '热血' },
  109. { id: 2, name: '爽文' },
  110. { id: 3, name: '冒险' },
  111. { id: 4, name: '复仇' },
  112. { id: 5, name: '种田' },
  113. { id: 6, name: '经商' },
  114. { id: 7, name: '争霸' },
  115. { id: 8, name: '升级' },
  116. { id: 9, name: '穿越' },
  117. { id: 10, name: '重生' }
  118. ]);
  119. // 上传前校验
  120. const beforeUpload = (file) => {
  121. const isImage = file.type === 'image/jpeg' || file.type === 'image/png';
  122. const isLt2M = file.size / 1024 / 1024 < 2;
  123. if (!isImage) {
  124. ElMessage.error('封面图片只能是JPG或PNG格式!');
  125. return false;
  126. }
  127. if (!isLt2M) {
  128. ElMessage.error('封面图片大小不能超过2MB!');
  129. return false;
  130. }
  131. return true;
  132. };
  133. // 上传封面
  134. const uploadCover = (options) => {
  135. const { file } = options;
  136. // 实际应用中应该上传到服务器,这里模拟上传成功
  137. const reader = new FileReader();
  138. reader.readAsDataURL(file);
  139. reader.onload = (e) => {
  140. workForm.cover = e.target.result;
  141. };
  142. };
  143. // 提交表单
  144. const submitForm = () => {
  145. workFormRef.value.validate(async (valid) => {
  146. if (valid) {
  147. try {
  148. loading.value = true;
  149. // 模拟API调用,实际项目中应调用真实的后端API
  150. await new Promise(resolve => setTimeout(resolve, 1000));
  151. ElMessage.success('作品创建成功!');
  152. router.push({ name: 'authorCenter' });
  153. } catch (error) {
  154. ElMessage.error('创建失败,请稍后重试');
  155. } finally {
  156. loading.value = false;
  157. }
  158. } else {
  159. ElMessage.warning('请正确填写表单信息');
  160. return false;
  161. }
  162. });
  163. };
  164. // 返回上一页
  165. const goBack = () => {
  166. router.back();
  167. };
  168. return {
  169. workFormRef,
  170. workForm,
  171. rules,
  172. categories,
  173. tags,
  174. loading,
  175. beforeUpload,
  176. uploadCover,
  177. submitForm,
  178. goBack
  179. };
  180. }
  181. });
  182. </script>
  183. <style lang="scss" scoped>
  184. .create-work-container {
  185. background-color: #f5f5f9;
  186. min-height: calc(100vh - 60px);
  187. padding: 30px 0;
  188. .create-work-content {
  189. max-width: 800px;
  190. margin: 0 auto;
  191. padding: 30px;
  192. background-color: #fff;
  193. border-radius: 8px;
  194. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
  195. .page-title {
  196. font-size: 24px;
  197. color: #333;
  198. margin: 0 0 30px;
  199. padding-bottom: 15px;
  200. border-bottom: 1px solid #eee;
  201. }
  202. .form-container {
  203. :deep(.el-form-item__label) {
  204. font-weight: 500;
  205. color: #333;
  206. }
  207. :deep(.el-input__wrapper) {
  208. padding: 0 15px;
  209. height: 44px;
  210. }
  211. .cover-uploader {
  212. .cover-image {
  213. width: 150px;
  214. height: 200px;
  215. object-fit: cover;
  216. border-radius: 4px;
  217. }
  218. .cover-placeholder {
  219. width: 150px;
  220. height: 200px;
  221. background-color: #f7f9fc;
  222. border: 1px dashed #d9d9d9;
  223. border-radius: 4px;
  224. display: flex;
  225. flex-direction: column;
  226. justify-content: center;
  227. align-items: center;
  228. cursor: pointer;
  229. &:hover {
  230. border-color: #0A2463;
  231. color: #0A2463;
  232. }
  233. .el-icon {
  234. font-size: 28px;
  235. color: #8c939d;
  236. margin-bottom: 8px;
  237. }
  238. .upload-text {
  239. font-size: 14px;
  240. color: #8c939d;
  241. }
  242. }
  243. }
  244. .upload-tip {
  245. font-size: 12px;
  246. color: #aaa;
  247. margin-top: 8px;
  248. }
  249. .form-actions {
  250. margin-top: 40px;
  251. display: flex;
  252. justify-content: center;
  253. gap: 20px;
  254. .el-button {
  255. width: 120px;
  256. }
  257. .el-button--primary {
  258. background-color: #0A2463;
  259. border-color: #0A2463;
  260. &:hover, &:focus {
  261. background-color: #1a3473;
  262. border-color: #1a3473;
  263. }
  264. }
  265. }
  266. }
  267. }
  268. }
  269. </style>