|
|
@@ -1,1097 +0,0 @@
|
|
|
-<template>
|
|
|
- <view class="container">
|
|
|
- <!-- H5环境自定义导航栏 -->
|
|
|
- <view class="h5-custom-navbar" v-if="isH5">
|
|
|
- <view class="h5-navbar-left" @click="goBack">
|
|
|
- <view class="h5-back-icon">
|
|
|
- <view class="h5-arrow-left"></view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <text class="h5-navbar-title">编辑文章</text>
|
|
|
- <view class="h5-navbar-right"></view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 表单内容区 -->
|
|
|
- <view class="article-container" :style="isH5 ? 'margin-top: 90rpx;' : ''">
|
|
|
- <!-- 加载状态 -->
|
|
|
- <view class="loading-container" v-if="loading">
|
|
|
- <view class="loading-spinner"></view>
|
|
|
- <text class="loading-text">加载中...</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 编辑区域 -->
|
|
|
- <block v-else>
|
|
|
- <!-- 基本信息区域 -->
|
|
|
- <view class="form-section">
|
|
|
- <view class="form-title">基本信息</view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">文章标题</text>
|
|
|
- <input class="form-input" v-model="articleInfo.title" placeholder="请输入文章标题" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">文章来源</text>
|
|
|
- <input class="form-input" v-model="articleInfo.source" placeholder="请输入文章来源" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">文章类型</text>
|
|
|
- <radio-group class="type-group" @change="handleTypeChange">
|
|
|
- <label class="type-item">
|
|
|
- <radio value="tech" :checked="articleInfo.category === 'tech'" />
|
|
|
- <text>农技知识</text>
|
|
|
- </label>
|
|
|
- <label class="type-item">
|
|
|
- <radio value="policy" :checked="articleInfo.category === 'policy'" />
|
|
|
- <text>政策解读</text>
|
|
|
- </label>
|
|
|
- </radio-group>
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">内容类型</text>
|
|
|
- <radio-group class="type-group" @change="handleContentTypeChange">
|
|
|
- <label class="type-item">
|
|
|
- <radio value="article" :checked="articleInfo.contentType === 'article'" />
|
|
|
- <text>文章</text>
|
|
|
- </label>
|
|
|
- <label class="type-item">
|
|
|
- <radio value="video" :checked="articleInfo.contentType === 'video'" />
|
|
|
- <text>视频</text>
|
|
|
- </label>
|
|
|
- </radio-group>
|
|
|
- </view>
|
|
|
- <view class="form-item" v-if="articleInfo.contentType === 'video'">
|
|
|
- <text class="form-label">视频链接</text>
|
|
|
- <input class="form-input" v-model="articleInfo.videoUrl" placeholder="请输入视频链接" />
|
|
|
- </view>
|
|
|
- <view class="form-item" v-if="articleInfo.contentType === 'video'">
|
|
|
- <text class="form-label">视频时长</text>
|
|
|
- <input class="form-input" v-model="articleInfo.videoDuration" placeholder="请输入视频时长,如12:30" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">文章简介</text>
|
|
|
- <textarea class="form-textarea" v-model="articleInfo.description" placeholder="请输入文章简介" />
|
|
|
- </view>
|
|
|
- <!-- <view class="form-item">
|
|
|
- <text class="form-label">缩略图</text>
|
|
|
- <view class="upload-box" @click="chooseThumbnail">
|
|
|
- <image v-if="articleInfo.thumbnail" :src="articleInfo.thumbnail" mode="aspectFill" class="thumbnail-preview"></image>
|
|
|
- <view v-else class="upload-placeholder">
|
|
|
- <text class="upload-icon">+</text>
|
|
|
- <text>上传缩略图</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view> -->
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 文章内容编辑区 -->
|
|
|
- <view class="form-section">
|
|
|
- <view class="form-title">文章内容</view>
|
|
|
-
|
|
|
- <!-- 文章内容构建器 -->
|
|
|
- <view class="content-builder">
|
|
|
- <view class="section-controls">
|
|
|
- <button class="control-btn" @click="addSection('paragraph')">添加段落</button>
|
|
|
- <button class="control-btn" @click="addSection('title')">添加标题</button>
|
|
|
- <button class="control-btn" @click="addSection('image')">添加图片</button>
|
|
|
- <button class="control-btn" @click="addSection('note')">添加提示框</button>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 内容块 -->
|
|
|
- <view class="content-sections">
|
|
|
- <view v-for="(section, index) in contentSections" :key="index" class="content-section-item">
|
|
|
- <!-- 段落 -->
|
|
|
- <view v-if="section.type === 'paragraph'" class="section-edit-item">
|
|
|
- <textarea class="section-textarea" v-model="section.content" placeholder="请输入段落内容"></textarea>
|
|
|
- <view class="section-controls">
|
|
|
- <text class="delete-btn" @click="deleteSection(index)">删除</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 标题 -->
|
|
|
- <view v-if="section.type === 'title'" class="section-edit-item">
|
|
|
- <input class="section-title-input" v-model="section.content" placeholder="请输入标题"></input>
|
|
|
- <view class="section-controls">
|
|
|
- <text class="delete-btn" @click="deleteSection(index)">删除</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 图片 -->
|
|
|
- <view v-if="section.type === 'image'" class="section-edit-item">
|
|
|
- <view class="image-upload-container">
|
|
|
- <view class="upload-box" @click="chooseImage(index)">
|
|
|
- <image v-if="section.imageUrl" :src="section.imageUrl" mode="aspectFill" class="section-image-preview"></image>
|
|
|
- <view v-else class="upload-placeholder">
|
|
|
- <text class="upload-icon">+</text>
|
|
|
- <text>上传图片</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <input class="image-caption-input" v-model="section.caption" placeholder="图片说明文字"></input>
|
|
|
- <input class="color-input" v-model="section.color" placeholder="颜色代码,例如: #8BC34A"></input>
|
|
|
- </view>
|
|
|
- <view class="section-controls">
|
|
|
- <text class="delete-btn" @click="deleteSection(index)">删除</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 提示框 -->
|
|
|
- <view v-if="section.type === 'note'" class="section-edit-item">
|
|
|
- <view class="note-box-edit">
|
|
|
- <input class="note-title-input" v-model="section.title" placeholder="提示框标题,例如:注意事项"></input>
|
|
|
- <textarea class="note-content-input" v-model="section.content" placeholder="提示框内容"></textarea>
|
|
|
- </view>
|
|
|
- <view class="section-controls">
|
|
|
- <text class="delete-btn" @click="deleteSection(index)">删除</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, -1)" v-if="index > 0">上移</text>
|
|
|
- <text class="move-btn" @click="moveSection(index, 1)" v-if="index < contentSections.length - 1">下移</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 预览区 -->
|
|
|
- <view class="preview-section">
|
|
|
- <view class="form-title">内容预览</view>
|
|
|
- <view class="article-content">
|
|
|
- <rich-text :nodes="generatedHtml"></rich-text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 提交按钮 -->
|
|
|
- <view class="submit-section">
|
|
|
- <button class="submit-btn" @click="saveArticle">保存文章</button>
|
|
|
- </view>
|
|
|
- </block>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 返回顶部 -->
|
|
|
- <view class="back-to-top" @click="scrollToTop">
|
|
|
- <view class="top-arrow"></view>
|
|
|
- <text class="top-text">顶部</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import { createArticle, updateArticle, getArticleDetail, uploadFile } from '@/api/services/knowledge.js';
|
|
|
-
|
|
|
-export default {
|
|
|
- data() {
|
|
|
- return {
|
|
|
- title: "编辑文章",
|
|
|
- isH5: false,
|
|
|
- loading: false,
|
|
|
- isEdit: false, // 是否为编辑模式
|
|
|
- id: null, // 文章ID
|
|
|
-
|
|
|
- // 文章基本信息
|
|
|
- articleInfo: {
|
|
|
- id: '',
|
|
|
- title: '',
|
|
|
- description: '',
|
|
|
- source: '农业技术研究院',
|
|
|
- category: 'tech', // 默认为农技知识
|
|
|
- contentType: 'article', // 默认为文章类型
|
|
|
- thumbnail: '', // 缩略图
|
|
|
- videoUrl: '', // 视频链接
|
|
|
- videoDuration: '', // 视频时长
|
|
|
- isRecommend: 0,
|
|
|
- status: 1
|
|
|
- },
|
|
|
-
|
|
|
- // 内容块
|
|
|
- contentSections: [],
|
|
|
-
|
|
|
- // 生成的HTML内容
|
|
|
- generatedHtml: ''
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- onLoad(options) {
|
|
|
- // 检测是否在H5环境中运行
|
|
|
- // #ifdef H5
|
|
|
- this.isH5 = true;
|
|
|
- // #endif
|
|
|
-
|
|
|
- // 设置导航栏标题
|
|
|
- uni.setNavigationBarTitle({
|
|
|
- title: '编辑文章'
|
|
|
- });
|
|
|
-
|
|
|
- // 如果有ID参数,则为编辑模式
|
|
|
- if (options && options.id) {
|
|
|
- this.id = options.id;
|
|
|
- this.isEdit = true;
|
|
|
- this.loadArticleData();
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- watch: {
|
|
|
- // 监听内容区块变化,实时生成HTML
|
|
|
- contentSections: {
|
|
|
- handler: function() {
|
|
|
- console.log("this.generateHtml()",this.generateHtml());
|
|
|
-
|
|
|
- this.generateHtml();
|
|
|
- },
|
|
|
- deep: true
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- methods: {
|
|
|
- // 加载文章数据(编辑模式)
|
|
|
- async loadArticleData() {
|
|
|
- if (!this.id) return;
|
|
|
-
|
|
|
- try {
|
|
|
- this.loading = true;
|
|
|
- const result = await getArticleDetail(this.id);
|
|
|
-
|
|
|
- if (result.data.code === 200 && result.data.data) {
|
|
|
- const articleData = result.data.data;
|
|
|
-
|
|
|
- // 填充基本信息
|
|
|
- this.articleInfo = {
|
|
|
- id: articleData.id,
|
|
|
- title: articleData.title,
|
|
|
- description: articleData.description,
|
|
|
- source: articleData.source,
|
|
|
- category: articleData.type,
|
|
|
- contentType: articleData.contentType,
|
|
|
- thumbnail: articleData.image,
|
|
|
- videoUrl: articleData.videoUrl,
|
|
|
- videoDuration: articleData.duration,
|
|
|
- isRecommend: 0,
|
|
|
- status: 1
|
|
|
- };
|
|
|
-
|
|
|
- // 解析HTML内容为内容块
|
|
|
- this.parseHtmlToSections(articleData.content);
|
|
|
- } else {
|
|
|
- uni.showToast({
|
|
|
- title: result.data.msg || '获取文章详情失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('获取文章详情失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '网络异常,请稍后重试',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- } finally {
|
|
|
- this.loading = false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 解析HTML为内容块
|
|
|
- parseHtmlToSections(html) {
|
|
|
- if (!html) {
|
|
|
- this.contentSections = [];
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 创建一个临时的DOM元素来解析HTML
|
|
|
- const parser = new DOMParser();
|
|
|
- const doc = parser.parseFromString(html, 'text/html');
|
|
|
- const divs = doc.querySelectorAll('div.content-section');
|
|
|
-
|
|
|
- const sections = [];
|
|
|
-
|
|
|
- divs.forEach(div => {
|
|
|
- // 处理段落
|
|
|
- const paragraph = div.querySelector('p.paragraph');
|
|
|
- if (paragraph) {
|
|
|
- sections.push({
|
|
|
- type: 'paragraph',
|
|
|
- content: paragraph.textContent
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 处理标题
|
|
|
- const title = div.querySelector('h3.section-title');
|
|
|
- if (title) {
|
|
|
- sections.push({
|
|
|
- type: 'title',
|
|
|
- content: title.textContent
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 处理图片
|
|
|
- const imageDiv = div.querySelector('div.section-image');
|
|
|
- if (imageDiv) {
|
|
|
- const colorImage = imageDiv.querySelector('div.color-image');
|
|
|
- const imageLabel = colorImage ? colorImage.querySelector('div.image-label') : null;
|
|
|
-
|
|
|
- sections.push({
|
|
|
- type: 'image',
|
|
|
- imageUrl: '', // 需要后端提供真实URL
|
|
|
- caption: imageLabel ? imageLabel.textContent : '',
|
|
|
- color: colorImage ? colorImage.style.backgroundColor : '#8BC34A'
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 处理提示框
|
|
|
- const noteBox = div.querySelector('div.note-box');
|
|
|
- if (noteBox) {
|
|
|
- const noteTitle = noteBox.querySelector('div.note-title');
|
|
|
- const noteContent = noteBox.querySelector('div.note-content');
|
|
|
-
|
|
|
- sections.push({
|
|
|
- type: 'note',
|
|
|
- title: noteTitle ? noteTitle.textContent : '【注意事项】',
|
|
|
- content: noteContent ? noteContent.textContent : ''
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- this.contentSections = sections;
|
|
|
- },
|
|
|
-
|
|
|
- // 类型切换处理
|
|
|
- handleTypeChange(e) {
|
|
|
- this.articleInfo.category = e.detail.value;
|
|
|
- },
|
|
|
-
|
|
|
- // 内容类型切换处理
|
|
|
- handleContentTypeChange(e) {
|
|
|
- this.articleInfo.contentType = e.detail.value;
|
|
|
- },
|
|
|
-
|
|
|
- // 添加内容区块
|
|
|
- addSection(type) {
|
|
|
- switch(type) {
|
|
|
- case 'paragraph':
|
|
|
- this.contentSections.push({
|
|
|
- type: 'paragraph',
|
|
|
- content: ''
|
|
|
- });
|
|
|
- break;
|
|
|
- case 'title':
|
|
|
- this.contentSections.push({
|
|
|
- type: 'title',
|
|
|
- content: ''
|
|
|
- });
|
|
|
- break;
|
|
|
- case 'image':
|
|
|
- this.contentSections.push({
|
|
|
- type: 'image',
|
|
|
- imageUrl: '',
|
|
|
- caption: '',
|
|
|
- color: '#8BC34A' // 默认颜色
|
|
|
- });
|
|
|
- break;
|
|
|
- case 'note':
|
|
|
- this.contentSections.push({
|
|
|
- type: 'note',
|
|
|
- title: '【注意事项】',
|
|
|
- content: ''
|
|
|
- });
|
|
|
- break;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 删除区块
|
|
|
- deleteSection(index) {
|
|
|
- this.contentSections.splice(index, 1);
|
|
|
- },
|
|
|
-
|
|
|
- // 移动区块
|
|
|
- moveSection(index, direction) {
|
|
|
- const newIndex = index + direction;
|
|
|
-
|
|
|
- if (newIndex >= 0 && newIndex < this.contentSections.length) {
|
|
|
- const item = this.contentSections[index];
|
|
|
- this.contentSections.splice(index, 1);
|
|
|
- this.contentSections.splice(newIndex, 0, item);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 选择缩略图
|
|
|
- chooseThumbnail() {
|
|
|
- uni.chooseImage({
|
|
|
- count: 1,
|
|
|
- success: async (res) => {
|
|
|
- const tempFilePath = res.tempFilePaths[0];
|
|
|
- console.log("res",res);
|
|
|
-
|
|
|
- console.log("tempFilePath",tempFilePath);
|
|
|
- try {
|
|
|
- uni.showLoading({ title: '上传中...' });
|
|
|
- // 调用上传API
|
|
|
- const uploadResult = await this.uploadImage(tempFilePath);
|
|
|
-
|
|
|
- if (uploadResult && uploadResult.url) {
|
|
|
- this.articleInfo.thumbnail = ""+ uploadResult.url;
|
|
|
- } else {
|
|
|
- throw new Error('上传失败');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('上传图片失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '上传图片失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- } finally {
|
|
|
- uni.hideLoading();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 选择内容图片
|
|
|
- chooseImage(index) {
|
|
|
- uni.chooseImage({
|
|
|
- count: 1,
|
|
|
- success: async (res) => {
|
|
|
- const tempFilePath = res.tempFilePaths[0];
|
|
|
-
|
|
|
- try {
|
|
|
- uni.showLoading({ title: '上传中...' });
|
|
|
- // 调用上传API
|
|
|
- const uploadResult = await this.uploadImage(tempFilePath);
|
|
|
-
|
|
|
- if (uploadResult && uploadResult.url) {
|
|
|
- this.$set(this.contentSections[index], 'imageUrl', uploadResult.url);
|
|
|
- } else {
|
|
|
- throw new Error('上传失败');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('上传图片失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '上传图片失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- } finally {
|
|
|
- uni.hideLoading();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 上传图片
|
|
|
- async uploadImage(filePath) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.uploadFile({
|
|
|
- url: 'http://localhost:9203/file/upload', // 替换为实际的上传API
|
|
|
- filePath: filePath,
|
|
|
- name: 'file',
|
|
|
- success: (uploadRes) => {
|
|
|
- const data = JSON.parse(uploadRes.data);
|
|
|
- if (data.code === 200 && data.data) {
|
|
|
- resolve(data.data);
|
|
|
- } else {
|
|
|
- reject(new Error(data.msg || '上传失败'));
|
|
|
- }
|
|
|
- },
|
|
|
- fail: (error) => {
|
|
|
- reject(error);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 生成HTML内容
|
|
|
- generateHtml() {
|
|
|
- let html = '';
|
|
|
-
|
|
|
- this.contentSections.forEach(section => {
|
|
|
- switch(section.type) {
|
|
|
- case 'paragraph':
|
|
|
- html += `<div class="content-section">
|
|
|
- <p class="paragraph">${section.content || ''}</p>
|
|
|
- </div>`;
|
|
|
- break;
|
|
|
-
|
|
|
- case 'title':
|
|
|
- html += `<div class="content-section">
|
|
|
- <h3 class="section-title">${section.content || ''}</h3>
|
|
|
- </div>`;
|
|
|
- break;
|
|
|
-
|
|
|
- case 'image':
|
|
|
- html += `<div class="content-section">
|
|
|
- <div class="section-image">
|
|
|
- <div class="color-image" style="background-color: ${section.color || '#8BC34A'};">
|
|
|
- <div class="image-label">${section.caption || ''}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>`;
|
|
|
- break;
|
|
|
-
|
|
|
- case 'note':
|
|
|
- html += `<div class="content-section">
|
|
|
- <div class="note-box">
|
|
|
- <div class="note-title">${section.title || '【注意事项】'}</div>
|
|
|
- <div class="note-content">${section.content || ''}</div>
|
|
|
- </div>
|
|
|
- </div>`;
|
|
|
- break;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- this.generatedHtml = html;
|
|
|
- return html;
|
|
|
- },
|
|
|
-
|
|
|
- // 验证表单
|
|
|
- validateForm() {
|
|
|
- if (!this.articleInfo.title) {
|
|
|
- uni.showToast({
|
|
|
- title: '请输入文章标题',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!this.articleInfo.source) {
|
|
|
- uni.showToast({
|
|
|
- title: '请输入文章来源',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!this.articleInfo.description) {
|
|
|
- uni.showToast({
|
|
|
- title: '请输入文章简介',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // if (!this.articleInfo.thumbnail) {
|
|
|
- // uni.showToast({
|
|
|
- // title: '请上传文章缩略图',
|
|
|
- // icon: 'none'
|
|
|
- // });
|
|
|
- // return false;
|
|
|
- // }
|
|
|
-
|
|
|
- if (this.articleInfo.contentType === 'video') {
|
|
|
- if (!this.articleInfo.videoUrl) {
|
|
|
- uni.showToast({
|
|
|
- title: '请输入视频链接',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- return false;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 文章类型时,需要检查内容是否为空
|
|
|
- if (this.contentSections.length === 0) {
|
|
|
- uni.showToast({
|
|
|
- title: '请添加文章内容',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
- // 保存文章
|
|
|
- async saveArticle() {
|
|
|
- if (!this.validateForm()) return;
|
|
|
-
|
|
|
- try {
|
|
|
- this.loading = true;
|
|
|
-
|
|
|
- // 生成最终的HTML内容
|
|
|
- const contentHtml = this.generateHtml();
|
|
|
- console.log("contentHtml",contentHtml);
|
|
|
- // 构建提交数据
|
|
|
- const articleData = {
|
|
|
- id: this.isEdit ? this.id : null,
|
|
|
- title: this.articleInfo.title,
|
|
|
- description: this.articleInfo.description,
|
|
|
- content: contentHtml, // 直接传递原始HTML内容,不进行编码或转义
|
|
|
- category: this.articleInfo.category,
|
|
|
- thumbnail: this.articleInfo.thumbnail,
|
|
|
- contentType: this.articleInfo.contentType,
|
|
|
- videoUrl: this.articleInfo.videoUrl,
|
|
|
- videoDuration: this.articleInfo.videoDuration,
|
|
|
- source: this.articleInfo.source,
|
|
|
- isRecommend: this.articleInfo.isRecommend,
|
|
|
- status: this.articleInfo.status
|
|
|
- };
|
|
|
-
|
|
|
- let result;
|
|
|
-
|
|
|
- if (this.isEdit) {
|
|
|
- // 编辑模式
|
|
|
- result = await updateArticle(articleData);
|
|
|
- } else {
|
|
|
- // 新建模式
|
|
|
- result = await createArticle(articleData);
|
|
|
- }
|
|
|
-
|
|
|
- if (result.data.code === 200) {
|
|
|
- uni.showToast({
|
|
|
- title: this.isEdit ? '更新成功' : '发布成功',
|
|
|
- icon: 'success'
|
|
|
- });
|
|
|
-
|
|
|
- // 成功后返回上一页
|
|
|
- setTimeout(() => {
|
|
|
- uni.navigateBack();
|
|
|
- }, 1500);
|
|
|
- } else {
|
|
|
- uni.showToast({
|
|
|
- title: result.data.msg || '保存失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('保存文章失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '网络异常,请稍后重试',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- } finally {
|
|
|
- this.loading = false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 返回
|
|
|
- goBack() {
|
|
|
- uni.navigateBack();
|
|
|
- },
|
|
|
-
|
|
|
- // 滚动到顶部
|
|
|
- scrollToTop() {
|
|
|
- uni.pageScrollTo({
|
|
|
- scrollTop: 0,
|
|
|
- duration: 300
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-</script>
|
|
|
-
|
|
|
-<style>
|
|
|
-/* 引入原有样式 */
|
|
|
-@font-face {
|
|
|
- font-family: 'iconfont';
|
|
|
- src: url('https://at.alicdn.com/t/font_3442238_cosd6rj55jg.ttf') format('truetype');
|
|
|
-}
|
|
|
-
|
|
|
-@font-face {
|
|
|
- font-family: 'Material Icons';
|
|
|
- font-style: normal;
|
|
|
- font-weight: 400;
|
|
|
- src: url(https://fonts.gstatic.com/s/materialicons/v139/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
|
|
|
-}
|
|
|
-
|
|
|
-.icon, .view-icon, .play-icon, .action-icon, .top-icon {
|
|
|
- font-family: 'iconfont';
|
|
|
-}
|
|
|
-
|
|
|
-.material-icon {
|
|
|
- font-family: 'Material Icons';
|
|
|
- font-weight: normal;
|
|
|
- font-style: normal;
|
|
|
- font-size: 50rpx;
|
|
|
- line-height: 1;
|
|
|
- letter-spacing: normal;
|
|
|
- text-transform: none;
|
|
|
- display: inline-block;
|
|
|
- white-space: nowrap;
|
|
|
- word-wrap: normal;
|
|
|
- direction: ltr;
|
|
|
- -webkit-font-smoothing: antialiased;
|
|
|
- color: #4CAF50;
|
|
|
-}
|
|
|
-
|
|
|
-/* 容器样式 */
|
|
|
-.container {
|
|
|
- background-color: #f8f8f8;
|
|
|
- min-height: 100vh;
|
|
|
- position: relative;
|
|
|
- padding-bottom: 120rpx;
|
|
|
-}
|
|
|
-
|
|
|
-/* 文章容器 */
|
|
|
-.article-container {
|
|
|
- background-color: #fff;
|
|
|
- border-radius: 0;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-/* H5导航栏样式 */
|
|
|
-.h5-custom-navbar {
|
|
|
- position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- height: 90rpx;
|
|
|
- background-color: #fff;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 0 30rpx;
|
|
|
- z-index: 100;
|
|
|
- box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
-}
|
|
|
-
|
|
|
-.h5-navbar-left {
|
|
|
- width: 80rpx;
|
|
|
- height: 80rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.h5-back-icon {
|
|
|
- font-size: 40rpx;
|
|
|
- color: #333;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.h5-navbar-title {
|
|
|
- flex: 1;
|
|
|
- text-align: center;
|
|
|
- font-size: 32rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.h5-navbar-right {
|
|
|
- width: 60rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.h5-arrow-left {
|
|
|
- width: 24rpx;
|
|
|
- height: 24rpx;
|
|
|
- border-top: 4rpx solid #333;
|
|
|
- border-left: 4rpx solid #333;
|
|
|
- transform: rotate(-45deg);
|
|
|
-}
|
|
|
-
|
|
|
-/* 表单样式 */
|
|
|
-.form-section {
|
|
|
- padding: 30rpx;
|
|
|
- margin-bottom: 30rpx;
|
|
|
- background-color: #fff;
|
|
|
- border-bottom: 1rpx solid #eee;
|
|
|
-}
|
|
|
-
|
|
|
-.form-title {
|
|
|
- font-size: 34rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 30rpx;
|
|
|
- padding-left: 16rpx;
|
|
|
- border-left: 8rpx solid #4CAF50;
|
|
|
-}
|
|
|
-
|
|
|
-.form-item {
|
|
|
- margin-bottom: 30rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-label {
|
|
|
- display: block;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #666;
|
|
|
- margin-bottom: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-input {
|
|
|
- width: 100%;
|
|
|
- height: 80rpx;
|
|
|
- border: 1rpx solid #ddd;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.form-textarea {
|
|
|
- width: 100%;
|
|
|
- height: 180rpx;
|
|
|
- border: 1rpx solid #ddd;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.type-group {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
-}
|
|
|
-
|
|
|
-.type-item {
|
|
|
- margin-right: 60rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-/* 上传框样式 */
|
|
|
-.upload-box {
|
|
|
- width: 200rpx;
|
|
|
- height: 200rpx;
|
|
|
- border: 2rpx dashed #ddd;
|
|
|
- border-radius: 8rpx;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-placeholder {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- color: #999;
|
|
|
- font-size: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.upload-icon {
|
|
|
- font-size: 60rpx;
|
|
|
- margin-bottom: 10rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.thumbnail-preview {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-/* 内容构建器样式 */
|
|
|
-.content-builder {
|
|
|
- margin-top: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-controls {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- margin-bottom: 30rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.control-btn {
|
|
|
- margin-right: 20rpx;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- padding: 10rpx 24rpx;
|
|
|
- background-color: #f5f5f5;
|
|
|
- color: #333;
|
|
|
- font-size: 26rpx;
|
|
|
- border-radius: 30rpx;
|
|
|
- border: 1rpx solid #e0e0e0;
|
|
|
-}
|
|
|
-
|
|
|
-.content-section-item {
|
|
|
- margin-bottom: 30rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-edit-item {
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.section-textarea {
|
|
|
- width: 100%;
|
|
|
- min-height: 150rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.section-title-input {
|
|
|
- width: 100%;
|
|
|
- height: 80rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 30rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.section-controls {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
-}
|
|
|
-
|
|
|
-.delete-btn, .move-btn {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
- padding: 6rpx 16rpx;
|
|
|
- margin-left: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.delete-btn {
|
|
|
- color: #ff5252;
|
|
|
-}
|
|
|
-
|
|
|
-/* 图片上传区域 */
|
|
|
-.image-upload-container {
|
|
|
- width: 100%;
|
|
|
- margin-bottom: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-image-preview {
|
|
|
- width: 100%;
|
|
|
- height: 300rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.image-caption-input {
|
|
|
- width: 100%;
|
|
|
- height: 70rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
- margin-top: 20rpx;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-.color-input {
|
|
|
- width: 100%;
|
|
|
- height: 70rpx;
|
|
|
- border: 1rpx solid #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
- margin-top: 20rpx;
|
|
|
- background-color: #f9f9f9;
|
|
|
-}
|
|
|
-
|
|
|
-/* 提示框编辑区域 */
|
|
|
-.note-box-edit {
|
|
|
- border-left: 8rpx solid #8bc34a;
|
|
|
- padding: 20rpx;
|
|
|
- background-color: #f1f8e9;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.note-title-input {
|
|
|
- width: 100%;
|
|
|
- height: 70rpx;
|
|
|
- border: 1rpx solid #e0e0e0;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #558b2f;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- background-color: rgba(255,255,255,0.5);
|
|
|
-}
|
|
|
-
|
|
|
-.note-content-input {
|
|
|
- width: 100%;
|
|
|
- height: 150rpx;
|
|
|
- border: 1rpx solid #e0e0e0;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 20rpx;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #689f38;
|
|
|
- background-color: rgba(255,255,255,0.5);
|
|
|
-}
|
|
|
-
|
|
|
-/* 预览区域 */
|
|
|
-.preview-section {
|
|
|
- padding: 30rpx;
|
|
|
- margin-bottom: 30rpx;
|
|
|
- background-color: #fff;
|
|
|
- border-top: 1rpx solid #eee;
|
|
|
-}
|
|
|
-
|
|
|
-.article-content {
|
|
|
- margin-top: 20rpx;
|
|
|
- border: 1rpx dashed #ddd;
|
|
|
- padding: 20rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-/* 提交按钮区域 */
|
|
|
-.submit-section {
|
|
|
- padding: 20rpx 30rpx 50rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.submit-btn {
|
|
|
- width: 100%;
|
|
|
- height: 90rpx;
|
|
|
- line-height: 90rpx;
|
|
|
- background: linear-gradient(135deg, #4CAF50, #388E3C);
|
|
|
- color: #fff;
|
|
|
- font-size: 32rpx;
|
|
|
- font-weight: bold;
|
|
|
- border-radius: 45rpx;
|
|
|
- text-align: center;
|
|
|
- box-shadow: 0 6rpx 16rpx rgba(76, 175, 80, 0.3);
|
|
|
-}
|
|
|
-
|
|
|
-/* 内容预览样式 */
|
|
|
-.content-section {
|
|
|
- margin-bottom: 40rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-title {
|
|
|
- font-size: 34rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #2e7d32;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- display: block;
|
|
|
-}
|
|
|
-
|
|
|
-.paragraph {
|
|
|
- font-size: 30rpx;
|
|
|
- line-height: 1.8;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- display: block;
|
|
|
-}
|
|
|
-
|
|
|
-.section-image {
|
|
|
- margin: 20rpx 0;
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.color-image {
|
|
|
- width: 100%;
|
|
|
- height: 300rpx;
|
|
|
- border-radius: 0;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- position: relative;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.image-label {
|
|
|
- color: white;
|
|
|
- font-size: 32rpx;
|
|
|
- font-weight: bold;
|
|
|
- text-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
|
|
- z-index: 5;
|
|
|
-}
|
|
|
-
|
|
|
-.note-box {
|
|
|
- background-color: #f1f8e9;
|
|
|
- border-left: 8rpx solid #8bc34a;
|
|
|
- padding: 20
|
|
|
-}
|
|
|
-</style>
|