|
@@ -58,6 +58,9 @@ const formData = reactive({
|
|
|
productName: '',
|
|
productName: '',
|
|
|
productDetails: '',
|
|
productDetails: '',
|
|
|
productUrl: '',
|
|
productUrl: '',
|
|
|
|
|
+ isCore: 0,
|
|
|
|
|
+ productIntroduction:'',
|
|
|
|
|
+ productScene:''
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const quillOptions = {
|
|
const quillOptions = {
|
|
@@ -84,22 +87,65 @@ const quillOptions = {
|
|
|
placeholder: '请输入产品详情...',
|
|
placeholder: '请输入产品详情...',
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+// const getImagesFromContent = (content) => {
|
|
|
|
|
+// if (!content) return []
|
|
|
|
|
+//
|
|
|
|
|
+// const delta = typeof content === 'string' ?
|
|
|
|
|
+// quillRef.value?.clipboard?.convert(content) :
|
|
|
|
|
+// content
|
|
|
|
|
+// const images = []
|
|
|
|
|
+//
|
|
|
|
|
+// if (delta && delta.ops) {
|
|
|
|
|
+// delta.ops.forEach(op => {
|
|
|
|
|
+// if (op.insert && op.insert.image) {
|
|
|
|
|
+// images.push(op.insert.image)
|
|
|
|
|
+// }
|
|
|
|
|
+// })
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+// return images
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
// 新增:获取富文本中的所有图片
|
|
// 新增:获取富文本中的所有图片
|
|
|
const getImagesFromContent = (content) => {
|
|
const getImagesFromContent = (content) => {
|
|
|
if (!content) return []
|
|
if (!content) return []
|
|
|
|
|
|
|
|
- const delta = typeof content === 'string' ?
|
|
|
|
|
- quillRef.value?.clipboard?.convert(content) :
|
|
|
|
|
- content
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let delta = null
|
|
|
|
|
+ // 分两种情况处理:HTML字符串 / Quill Delta对象
|
|
|
|
|
+ if (typeof content === 'string') {
|
|
|
|
|
+ // 情况1:content是HTML字符串 → 优先用DOM解析提取图片,兼容Quill未初始化的场景
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 创建临时DOM元素解析HTML
|
|
|
|
|
+ const tempDiv = document.createElement('div')
|
|
|
|
|
+ tempDiv.innerHTML = content
|
|
|
|
|
+ const imgElements = tempDiv.querySelectorAll('img')
|
|
|
|
|
+ // 直接提取所有img标签的src属性
|
|
|
|
|
+ return Array.from(imgElements).map(img => img.src).filter(src => src)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('解析HTML图片失败,降级使用Quill转换:', e)
|
|
|
|
|
+ // 降级方案:尝试用Quill的clipboard转换
|
|
|
|
|
+ if (quillRef.value?.clipboard) {
|
|
|
|
|
+ delta = quillRef.value.clipboard.convert(content) || { ops: [] }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return [] // Quill未初始化,直接返回空
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 情况2:content是Delta对象 → 直接使用
|
|
|
|
|
+ delta = content
|
|
|
|
|
+ }
|
|
|
|
|
+ // 处理Delta对象提取图片
|
|
|
const images = []
|
|
const images = []
|
|
|
- if (delta && delta.ops) {
|
|
|
|
|
|
|
+ if (delta && delta.ops && Array.isArray(delta.ops)) {
|
|
|
delta.ops.forEach(op => {
|
|
delta.ops.forEach(op => {
|
|
|
- if (op.insert && op.insert.image) {
|
|
|
|
|
|
|
+ // 兼容Quill的image embed格式:op.insert是对象且包含image属性
|
|
|
|
|
+ if (op.insert && typeof op.insert === 'object' && op.insert.image) {
|
|
|
images.push(op.insert.image)
|
|
images.push(op.insert.image)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return images
|
|
return images
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -122,8 +168,8 @@ const onEditorReady = (quill) => {
|
|
|
ElMessage.error('请选择图片文件')
|
|
ElMessage.error('请选择图片文件')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if (file.size > 5 * 1024 * 1024) {
|
|
|
|
|
- ElMessage.error('图片大小不能超过5MB')
|
|
|
|
|
|
|
+ if (file.size > 10 * 1024 * 1024) {
|
|
|
|
|
+ ElMessage.error('图片大小不能超过10MB')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -219,8 +265,8 @@ const handleCoverUpload = (event) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 验证文件大小 (5MB)
|
|
// 验证文件大小 (5MB)
|
|
|
- if (file.size > 5 * 1024 * 1024) {
|
|
|
|
|
- ElMessage.error('图片大小不能超过5MB')
|
|
|
|
|
|
|
+ if (file.size > 10 * 1024 * 1024) {
|
|
|
|
|
+ ElMessage.error('图片大小不能超过10MB')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -295,7 +341,7 @@ const handleAdd = () => {
|
|
|
dialogVisible.value = true
|
|
dialogVisible.value = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 编辑新闻
|
|
|
|
|
|
|
+// 编辑产品
|
|
|
const handleEdit = async (row) => {
|
|
const handleEdit = async (row) => {
|
|
|
dialogTitle.value = '编辑产品'
|
|
dialogTitle.value = '编辑产品'
|
|
|
isEdit.value = true
|
|
isEdit.value = true
|
|
@@ -305,10 +351,13 @@ const handleEdit = async (row) => {
|
|
|
ElMessage.error(res.msg)
|
|
ElMessage.error(res.msg)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- if(res.data.isSpecial){
|
|
|
|
|
- res.data.isSpecial = 1
|
|
|
|
|
- }else{
|
|
|
|
|
- res.data.isSpecial = 0
|
|
|
|
|
|
|
+
|
|
|
|
|
+ console.log(1111,res.data.isCore)
|
|
|
|
|
+
|
|
|
|
|
+ if (res.data.isCore === '0') {
|
|
|
|
|
+ res.data.isCore = 0
|
|
|
|
|
+ } else {
|
|
|
|
|
+ res.data.isCore = 1
|
|
|
}
|
|
}
|
|
|
Object.assign(formData, res.data)
|
|
Object.assign(formData, res.data)
|
|
|
coverFile.value = null
|
|
coverFile.value = null
|
|
@@ -413,6 +462,10 @@ const handleSave = async () => {
|
|
|
submitData.append('productType', formData.productType)
|
|
submitData.append('productType', formData.productType)
|
|
|
submitData.append('productCategory', formData.productCategory)
|
|
submitData.append('productCategory', formData.productCategory)
|
|
|
submitData.append('productDetails', formData.productDetails)
|
|
submitData.append('productDetails', formData.productDetails)
|
|
|
|
|
+ submitData.append('productModel', formData.productModel)
|
|
|
|
|
+ submitData.append('isCore', formData.isCore)
|
|
|
|
|
+ submitData.append('productIntroduction', formData.productIntroduction)
|
|
|
|
|
+ submitData.append('productScene', formData.productScene)
|
|
|
|
|
|
|
|
// 如果有新的封面文件,添加到FormData
|
|
// 如果有新的封面文件,添加到FormData
|
|
|
if (coverFile.value) {
|
|
if (coverFile.value) {
|
|
@@ -434,7 +487,7 @@ const handleSave = async () => {
|
|
|
const actualInsertedImages = currentImages.filter(img => !originalImages.value.includes(img))
|
|
const actualInsertedImages = currentImages.filter(img => !originalImages.value.includes(img))
|
|
|
|
|
|
|
|
// console.log('实际删除的图片:', actualDeletedImages)
|
|
// console.log('实际删除的图片:', actualDeletedImages)
|
|
|
- // console.log('实际新增的图片:', actualInsertedImages)
|
|
|
|
|
|
|
+ console.log('实际新增的图片:', actualInsertedImages)
|
|
|
|
|
|
|
|
// 发送图片变化信息给后端
|
|
// 发送图片变化信息给后端
|
|
|
if (actualDeletedImages.length > 0) {
|
|
if (actualDeletedImages.length > 0) {
|
|
@@ -445,7 +498,7 @@ const handleSave = async () => {
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
// 新增模式:所有图片都是新增的
|
|
// 新增模式:所有图片都是新增的
|
|
|
- const currentImages = getImagesFromContent(quillRef.value.getContents())
|
|
|
|
|
|
|
+ const currentImages = getImagesFromContent(quillRef.value.getContents());
|
|
|
if (currentImages.length > 0) {
|
|
if (currentImages.length > 0) {
|
|
|
submitData.append('insertedImages', JSON.stringify(currentImages))
|
|
submitData.append('insertedImages', JSON.stringify(currentImages))
|
|
|
}
|
|
}
|
|
@@ -476,7 +529,6 @@ const handleSave = async () => {
|
|
|
dialogVisible.value = false
|
|
dialogVisible.value = false
|
|
|
getList()
|
|
getList()
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('保存失败:', error)
|
|
|
|
|
ElMessage.error('保存失败')
|
|
ElMessage.error('保存失败')
|
|
|
} finally {
|
|
} finally {
|
|
|
uploadLoading.value = false
|
|
uploadLoading.value = false
|
|
@@ -491,6 +543,10 @@ const resetForm = () => {
|
|
|
productCategory: '',
|
|
productCategory: '',
|
|
|
productDetails: '',
|
|
productDetails: '',
|
|
|
productUrl: '',
|
|
productUrl: '',
|
|
|
|
|
+ productModel: '',
|
|
|
|
|
+ isCore:0,
|
|
|
|
|
+ productIntroduction:'',
|
|
|
|
|
+ productScene:''
|
|
|
})
|
|
})
|
|
|
coverFile.value = null
|
|
coverFile.value = null
|
|
|
if (fileInputRef.value) {
|
|
if (fileInputRef.value) {
|
|
@@ -541,10 +597,10 @@ const handleDialogClose = () => {
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="6">
|
|
<el-col :span="6">
|
|
|
<el-select
|
|
<el-select
|
|
|
- v-model="searchParams.productCategory"
|
|
|
|
|
- placeholder="请选择产品分类"
|
|
|
|
|
- clearable
|
|
|
|
|
- @keyup.enter="getList"
|
|
|
|
|
|
|
+ v-model="searchParams.productCategory"
|
|
|
|
|
+ placeholder="请选择产品分类"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @keyup.enter="getList"
|
|
|
>
|
|
>
|
|
|
<el-option label="软件产品" value="软件产品"/>
|
|
<el-option label="软件产品" value="软件产品"/>
|
|
|
<el-option label="硬件产品" value="硬件产品"/>
|
|
<el-option label="硬件产品" value="硬件产品"/>
|
|
@@ -552,18 +608,18 @@ const handleDialogClose = () => {
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="6">
|
|
<el-col :span="6">
|
|
|
<el-input
|
|
<el-input
|
|
|
- v-model="searchParams.productType"
|
|
|
|
|
- placeholder="请输入产品类型"
|
|
|
|
|
- clearable
|
|
|
|
|
- @keyup.enter="getList"
|
|
|
|
|
|
|
+ v-model="searchParams.productType"
|
|
|
|
|
+ placeholder="请输入产品类型"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @keyup.enter="getList"
|
|
|
/>
|
|
/>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="6">
|
|
<el-col :span="6">
|
|
|
<el-input
|
|
<el-input
|
|
|
- v-model="searchParams.productName"
|
|
|
|
|
- placeholder="请输入产品名称"
|
|
|
|
|
- clearable
|
|
|
|
|
- @keyup.enter="getList"
|
|
|
|
|
|
|
+ v-model="searchParams.productName"
|
|
|
|
|
+ placeholder="请输入产品名称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @keyup.enter="getList"
|
|
|
/>
|
|
/>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="6">
|
|
<el-col :span="6">
|
|
@@ -597,10 +653,18 @@ const handleDialogClose = () => {
|
|
|
class="w-full"
|
|
class="w-full"
|
|
|
>
|
|
>
|
|
|
<el-table-column type="selection" width="55"/>
|
|
<el-table-column type="selection" width="55"/>
|
|
|
- <el-table-column prop="productCategory" label="产品分类" min-width="200"/>
|
|
|
|
|
- <el-table-column prop="productType" label="产品类型" width="120" />
|
|
|
|
|
- <el-table-column prop="productName" label="产品名称" width="120" />
|
|
|
|
|
- <el-table-column prop="productUrl" label="产品封面" width="120">
|
|
|
|
|
|
|
+ <el-table-column prop="productCategory" label="产品分类"/>
|
|
|
|
|
+ <el-table-column prop="productType" label="产品类型"/>
|
|
|
|
|
+ <el-table-column prop="productModel" label="产品型号" width="220"/>
|
|
|
|
|
+ <el-table-column prop="productName" label="产品名称" width="320"/>
|
|
|
|
|
+ <el-table-column prop="isSpecial" label="核心产品" width="100">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-tag :type="row.isCore === '1' ? 'danger' : 'info'">
|
|
|
|
|
+ {{ row.isCore === '1' ? '是' : '否' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="productUrl" label="产品封面">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
<!-- 修改图片预览方式,使用自定义点击事件 -->
|
|
<!-- 修改图片预览方式,使用自定义点击事件 -->
|
|
|
<div v-if="row.productUrl" class="cursor-pointer" @click="handleImagePreview(row.productUrl)">
|
|
<div v-if="row.productUrl" class="cursor-pointer" @click="handleImagePreview(row.productUrl)">
|
|
@@ -613,13 +677,13 @@ const handleDialogClose = () => {
|
|
|
<span v-else class="text-gray-400">无图片</span>
|
|
<span v-else class="text-gray-400">无图片</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column prop="createBy" label="创建人" width="120"/>
|
|
|
|
|
- <el-table-column prop="createTime" label="创建时间" width="180">
|
|
|
|
|
|
|
+ <el-table-column prop="createBy" label="创建人"/>
|
|
|
|
|
+ <el-table-column prop="createTime" label="创建时间">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
{{ row.createTime ? new Date(row.createTime).toLocaleString() : '-' }}
|
|
{{ row.createTime ? new Date(row.createTime).toLocaleString() : '-' }}
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="200" fixed="right">
|
|
|
|
|
|
|
+ <el-table-column label="操作" width="300" fixed="right">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
<el-button size="small" @click="handleView(row)">
|
|
<el-button size="small" @click="handleView(row)">
|
|
|
<Eye class="w-4 h-4 mr-1"/>
|
|
<Eye class="w-4 h-4 mr-1"/>
|
|
@@ -672,6 +736,25 @@ const handleDialogClose = () => {
|
|
|
<el-form-item label="产品名称" required>
|
|
<el-form-item label="产品名称" required>
|
|
|
<el-input v-model="formData.productName" placeholder="请输入产品名称"/>
|
|
<el-input v-model="formData.productName" placeholder="请输入产品名称"/>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <el-form-item label="产品描述" required>
|
|
|
|
|
+ <label for="name" class="text-white-100 text-sm font-medium text-gray-700">描述存在多条时请用“ · ”符号隔开</label>
|
|
|
|
|
+ <el-input v-model="formData.productIntroduction" placeholder="请输入产品描述"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="使用场景" required>
|
|
|
|
|
+ <el-input v-model="formData.productScene" placeholder="请输入使用场景"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="产品型号" required>
|
|
|
|
|
+ <el-input v-model="formData.productModel" placeholder="请输入产品型号"/>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="核心产品">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="formData.isCore"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ active-text="是"
|
|
|
|
|
+ inactive-text="否"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
|
|
|
<!-- 修改新闻封面上传组件 -->
|
|
<!-- 修改新闻封面上传组件 -->
|
|
|
<el-form-item label="产品封面">
|
|
<el-form-item label="产品封面">
|
|
@@ -727,7 +810,7 @@ const handleDialogClose = () => {
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
|
|
|
- <!-- 添加图片预览对话框 -->
|
|
|
|
|
|
|
+ <!-- 添加图片预览对话框 -->
|
|
|
<el-dialog
|
|
<el-dialog
|
|
|
v-model="imagePreviewVisible"
|
|
v-model="imagePreviewVisible"
|
|
|
title="图片预览"
|
|
title="图片预览"
|