|
@@ -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;
|