Lijy 6 månader sedan
förälder
incheckning
0989d739b0

+ 0 - 1
fhKeeper/formulahousekeeper/articleBackend/src/components/backend/TinymceEditor.vue

@@ -100,7 +100,6 @@ const myTinyInit = reactive({
   //图片上传方法(注意!!:要使用 promise 对象中的 resolve 返回图片路径,否则会报错)
   images_upload_handler: (blobInfo) => new Promise((resolve, reject) => {
       let formData = new FormData();
-      console.log(blobInfo, '<==== blobInfo')
       formData.append('multipartFile', blobInfo.blob(), blobInfo.filename());
       axios.post(`/api/common/uploadFile`, formData, {
           headers: ({

+ 120 - 117
fhKeeper/formulahousekeeper/articleBackend/src/views/article/ArticleManage.vue

@@ -19,20 +19,20 @@ import { ref } from 'vue'
 const categorys = ref([])
 
 //用户搜索时选中的分类id
-const categoryId=ref('')
+const categoryId = ref('')
 
 //用户搜索时选中的发布状态
-const state=ref('')
+const state = ref('')
 
 //文章列表数据模型
 const articles = ref([])
 
 const productList = ref([
-    { id: '1', label: '工时管家' }, 
-    { id: '2', label: '随访管家' }, 
-    { id: '3', label: '项目管家' }, 
-    { id: '4', label: '客户管家' }, 
-    { id: '5', label: '生产车间管家' }, 
+    { id: '1', label: '工时管家' },
+    { id: '2', label: '随访管家' },
+    { id: '3', label: '项目管家' },
+    { id: '4', label: '客户管家' },
+    { id: '5', label: '生产车间管家' },
 ])
 const quilleditorKey = ref(1)
 
@@ -52,32 +52,32 @@ const onCurrentChange = (num) => {
     articleList();
 }
 //回显文章标签
-import {articleCategoryListService, articleListService,articleAddService, articleDeleteService,uploadFileService} from '@/api/article.js'
-const articleCategoryList=async()=>{
+import { articleCategoryListService, articleListService, articleAddService, articleDeleteService, uploadFileService } from '@/api/article.js'
+const articleCategoryList = async () => {
     let result = await articleCategoryListService();
-    categorys.value=result.data;
+    categorys.value = result.data;
 }
 articleCategoryList();
 
 //获取文章列表数据
-const articleList=async()=>{
-    let params={
-        pageNum:pageNum.value,
-        pageSize:pageSize.value,
-        categoryId:categoryId.value?categoryId.value:null,
-        state:state.value?state.value:null
+const articleList = async () => {
+    let params = {
+        pageNum: pageNum.value,
+        pageSize: pageSize.value,
+        categoryId: categoryId.value ? categoryId.value : null,
+        state: state.value ? state.value : null
     }
-    let result =await articleListService(params);
+    let result = await articleListService(params);
     //渲染视图
-    total.value=result.data.total;
-    articles.value=result.data.items;
+    total.value = result.data.total;
+    articles.value = result.data.items;
 
     //处理数据,给数据模型扩展一个属性 categoryName ,分类名称
-    for(let i=0;i<articles.value.length;i++){
-        let article=articles.value[i];
-        for(let j=0;j<categorys.value.length;j++){
-            if(article.categoryId==categorys.value[j].id){
-                article.categoryName=categorys.value[j].categoryName
+    for (let i = 0; i < articles.value.length; i++) {
+        let article = articles.value[i];
+        for (let j = 0; j < categorys.value.length; j++) {
+            if (article.categoryId == categorys.value[j].id) {
+                article.categoryName = categorys.value[j].categoryName
             }
         }
     }
@@ -85,7 +85,7 @@ const articleList=async()=>{
 articleList()
 
 // 添加文章功能
-import {Plus} from '@element-plus/icons-vue'
+import { Plus } from '@element-plus/icons-vue'
 //控制抽屉是否显示
 const visibleDrawer = ref(false)
 //添加表单数据模型
@@ -93,53 +93,53 @@ const articleModel = ref({
     title: '',
     categoryId: '',
     coverImg: '',
-    content:'',
-    state:'',
+    content: '',
+    state: '',
     profile: '',
     productId: ''
 })
 const fileList = ref([])
 const options = ref({
-  theme: "snow",
-  bounds: document.body,
-  debug: "warn",
-  modules: {
-    // 工具栏配置
-    toolbar: [
-      ["bold", "italic", "underline", "strike"],      // 加粗 斜体 下划线 删除线
-      ["blockquote", "code-block"],                   // 引用  代码块
-      [{ list: "ordered" }, { list: "bullet" }],      // 有序、无序列表
-      [{ indent: "-1" }, { indent: "+1" }],           // 缩进
-      [{ size: ["small", false, "large", "huge"] }],  // 字体大小
-      [{ header: [1, 2, 3, 4, 5, 6, false] }],        // 标题
-      [{ color: [] }, { background: [] }],            // 字体颜色、字体背景颜色
-      [{ align: [] }],                                // 对齐方式
-      ["clean"],                                      // 清除文本格式
-      ["link", "image"]                      // 链接、图片、视频
-    ],
-    // ImageDrop: true,  // PS:因为QuillEditor自带可拖拽此配置可以不开,开启拖动会复制图片
-    // todo 富文本导入图片是否需要缩放拖拽 
-    // imageResize: {
-    //   displayStyles: {
-    //     backgroundColor: 'black',
-    //     border: 'none',
-    //     color: 'white',
-    //   },
-    //   modules: ['Resize', 'DisplaySize', 'Toolbar'],
-    // },
-  },
-  placeholder: "请输入内容",
-//   readOnly: props.readOnly
+    theme: "snow",
+    bounds: document.body,
+    debug: "warn",
+    modules: {
+        // 工具栏配置
+        toolbar: [
+            ["bold", "italic", "underline", "strike"],      // 加粗 斜体 下划线 删除线
+            ["blockquote", "code-block"],                   // 引用  代码块
+            [{ list: "ordered" }, { list: "bullet" }],      // 有序、无序列表
+            [{ indent: "-1" }, { indent: "+1" }],           // 缩进
+            [{ size: ["small", false, "large", "huge"] }],  // 字体大小
+            [{ header: [1, 2, 3, 4, 5, 6, false] }],        // 标题
+            [{ color: [] }, { background: [] }],            // 字体颜色、字体背景颜色
+            [{ align: [] }],                                // 对齐方式
+            ["clean"],                                      // 清除文本格式
+            ["link", "image"]                      // 链接、图片、视频
+        ],
+        // ImageDrop: true,  // PS:因为QuillEditor自带可拖拽此配置可以不开,开启拖动会复制图片
+        // todo 富文本导入图片是否需要缩放拖拽 
+        // imageResize: {
+        //   displayStyles: {
+        //     backgroundColor: 'black',
+        //     border: 'none',
+        //     color: 'white',
+        //   },
+        //   modules: ['Resize', 'DisplaySize', 'Toolbar'],
+        // },
+    },
+    placeholder: "请输入内容",
+    //   readOnly: props.readOnly
 });
 
 //导入token,方便编辑完文章后保存至服务器
-import {userTokenStore} from '@/stores/token.js'
-const tokenStore=userTokenStore();
+import { userTokenStore } from '@/stores/token.js'
+const tokenStore = userTokenStore();
 
 //上传成功的回调函数
-const uploadSuccess=(result)=>{
+const uploadSuccess = (result) => {
     //将服务器响应的图片地址赋值给 articleModel 的 coverImg
-    articleModel.value.coverImg=result.data;
+    articleModel.value.coverImg = result.data;
     console.log(result.data)
 }
 
@@ -148,39 +148,45 @@ const fileExceedsLimit = () => {
 }
 
 // 添加文章
-import {ElMessage, ElMessageBox} from 'element-plus'
-const addArticle=async(clickState)=>{
+import { ElMessage, ElMessageBox } from 'element-plus'
+const addArticle = async (clickState) => {
     // 把发布文章yes或草稿no赋值给数据模型
-    articleModel.value.state=clickState;
+    articleModel.value.state = clickState;
     const { content, title, profile, categoryId, productId } = articleModel.value
     let str = ''
-    if(!content) {
+    if (!content) {
         str += '文章内容不能为空,'
     }
-    if(!title) {
+    if (!title) {
         str += '文章标题不能为空,'
     }
-    if(!profile) {
+    if (!profile) {
         str += '文章封面不能为空,'
     }
-    if(!categoryId || categoryId.length <= 0) {
+    if (!categoryId || categoryId.length <= 0) {
         str += '文章标签不能为空,'
     }
-    if(fileList.value.length <= 0) {
+    if (fileList.value.length <= 0) {
         str += '文章封面不能为空,'
     }
-    if(!productId || productId == '0') {
+    if (!productId || productId == '0') {
         str += '文章所属产品不能为空,'
     }
-    if(str) {
+    if (str) {
         ElMessage.warning(str)
         return
     }
-    const formVla = { ...articleModel.value, categoryIds: (articleModel.value.categoryId || []).join(',') }
+    const formVla = {
+        ...articleModel.value, categoryIds: (articleModel.value.categoryId || []).join(','), content: content.replace(/<img\s+[^>]*src=["']([^"']+)["'][^>]*>/g, (match, src) => {
+            // 对 src 进行处理,只保留 "/upload/..." 部分
+            let newSrc = src.replace(/^(?:\.{2}\/)?(upload\/.*)/, '/\$1');
+            return match.replace(src, newSrc);
+        })
+    }
     delete formVla.categoryId
     const formData = new FormData()
     for (const key in formVla) {
-        if(key == 'coverImg') {
+        if (key == 'coverImg') {
             let file = fileList.value[0].raw
             // if(!isFile(file)) {
             //     file = base64ToFile(file, 'image.png')
@@ -192,8 +198,8 @@ const addArticle=async(clickState)=>{
     }
     //调用接口
     // let result=await articleAddService(articleModel.value)
-    let result=await articleAddService(formData)
-    ElMessage.success(result.msg?result.msg:"添加文章成功!")
+    let result = await articleAddService(formData)
+    ElMessage.success(result.msg ? result.msg : "添加文章成功!")
 
     //让添加文章的抽屉消失
     visibleDrawer.value = false;
@@ -218,17 +224,17 @@ const fileUploadArticle = async (row) => {
     }]
 }
 const base64ToFile = (base64String, fileName) => {
-  const arr = base64String.split(',');
-  const mime = arr[0].match(/:(.*?);/)[1];
-  const bstr = atob(arr[1]);
-  let n = bstr.length;
-  const u8arr = new Uint8Array(n);
-
-  while (n--) {
-    u8arr[n] = bstr.charCodeAt(n);
-  }
+    const arr = base64String.split(',');
+    const mime = arr[0].match(/:(.*?);/)[1];
+    const bstr = atob(arr[1]);
+    let n = bstr.length;
+    const u8arr = new Uint8Array(n);
+
+    while (n--) {
+        u8arr[n] = bstr.charCodeAt(n);
+    }
 
-  return new File([u8arr], fileName, { type: mime });
+    return new File([u8arr], fileName, { type: mime });
 }
 
 const addArticleAdministration = () => {
@@ -256,7 +262,7 @@ const editArticle = (row) => {
         coverImg: '',
         productId: productId
     }
-    if(coverImgUrl) {
+    if (coverImgUrl) {
         fileList.value = [{
             name: '图片',
             // url: `data:image/jpeg;base64, ${coverImg}`,
@@ -267,7 +273,7 @@ const editArticle = (row) => {
     } else {
         fileList.value = []
     }
-    
+
 
     visibleDrawer.value = true
 }
@@ -282,8 +288,8 @@ const deleteArticle = async (row) => {
             type: 'warning',
         }
     ).then(async () => {
-        let result=await articleDeleteService({ id })
-        ElMessage.success(result.message?result.message:"删除文章成功!")
+        let result = await articleDeleteService({ id })
+        ElMessage.success(result.message ? result.message : "删除文章成功!")
         articleList()
     })
 }
@@ -302,11 +308,7 @@ const deleteArticle = async (row) => {
         <el-form inline>
             <el-form-item label="文章标签:">
                 <el-select placeholder="请选择" v-model="categoryId">
-                    <el-option 
-                        v-for="c in categorys" 
-                        :key="c.id" 
-                        :label="c.categoryName"
-                        :value="c.id">
+                    <el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id">
                     </el-option>
                 </el-select>
             </el-form-item>
@@ -319,7 +321,7 @@ const deleteArticle = async (row) => {
             </el-form-item>
             <el-form-item>
                 <el-button type="primary" @click="articleList">搜索</el-button>
-                <el-button @click="categoryId='';state=''">重置</el-button>
+                <el-button @click="categoryId = ''; state = ''">重置</el-button>
             </el-form-item>
         </el-form>
         <!-- 文章列表 -->
@@ -340,20 +342,20 @@ const deleteArticle = async (row) => {
             </template>
         </el-table>
         <!-- 分页条 -->
-        <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[5, 10 ,20, 50, 100]"
+        <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[5, 10, 20, 50, 100]"
             layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"
             @current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" />
 
-            <!-- 抽屉 -->
+        <!-- 抽屉 -->
         <el-drawer v-model="visibleDrawer" :title="`${articleModel.id ? '编辑文章' : '添加文章'}`" direction="rtl" size="100%">
             <!-- 添加文章表单 -->
-            <el-form :model="articleModel" label-width="100px" >
-                <el-form-item label="文章标题" >
+            <el-form :model="articleModel" label-width="100px">
+                <el-form-item label="文章标题">
                     <el-input v-model="articleModel.title" placeholder="请输入标题" :maxlength="100"></el-input>
                 </el-form-item>
-                <el-form-item label="文章简介" >
-                    <el-input type="textarea"
-                    :autosize="{ minRows: 2, maxRows: 4}" v-model="articleModel.profile" placeholder="请输入简介" :maxlength="250"></el-input>
+                <el-form-item label="文章简介">
+                    <el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" v-model="articleModel.profile"
+                        placeholder="请输入简介" :maxlength="250"></el-input>
                 </el-form-item>
                 <el-form-item label="文章标签">
                     <el-select placeholder="请选择" multiple v-model="articleModel.categoryId" style="width: 100%;">
@@ -375,15 +377,11 @@ const deleteArticle = async (row) => {
                             headers: 设置上传的请求头
                             on-success: 上传成功的回调函数
                     -->
-                    <el-upload
-                        v-model:file-list="fileList"
-                        list-type="picture-card"
-                        :auto-upload="true"
-                        :limit="1"
-                        :on-exceed="fileExceedsLimit"
-                        :http-request="fileUploadArticle"
-                    >
-                        <el-icon><Plus /></el-icon>
+                    <el-upload v-model:file-list="fileList" list-type="picture-card" :auto-upload="true" :limit="1"
+                        :on-exceed="fileExceedsLimit" :http-request="fileUploadArticle">
+                        <el-icon>
+                            <Plus />
+                        </el-icon>
                     </el-upload>
 
                 </el-form-item>
@@ -398,7 +396,7 @@ const deleteArticle = async (row) => {
                             :key="quilleditorKey"
                             >
                         </quill-editor> -->
-                        <editor v-model="articleModel.content" @input="(val)=> {articleModel.content=val}"></editor>
+                        <editor v-model="articleModel.content" @input="(val) => { articleModel.content = val }"></editor>
                     </div>
                 </el-form-item>
                 <el-form-item>
@@ -453,11 +451,13 @@ const deleteArticle = async (row) => {
         }
     }
 }
+
 .editor {
-  width: 100%;
-  :deep(.ql-editor) {
-    min-height: 200px;
-  }
+    width: 100%;
+
+    :deep(.ql-editor) {
+        min-height: 200px;
+    }
 }
 
 .avatar-uploader .el-upload {
@@ -467,9 +467,11 @@ const deleteArticle = async (row) => {
     position: relative;
     overflow: hidden;
 }
+
 .avatar-uploader .el-upload:hover {
-    border-color: #409EFF;  
+    border-color: #409EFF;
 }
+
 .avatar-uploader-icon {
     font-size: 28px;
     color: #8c939d;
@@ -478,6 +480,7 @@ const deleteArticle = async (row) => {
     line-height: 178px;
     text-align: center;
 }
+
 .avatar {
     width: 178px;
     height: 178px;