产品中心模块接口调试完成

This commit is contained in:
BianLzhaoMin 2025-09-11 13:25:08 +08:00
parent d2f3efaf7e
commit 46c1467427
12 changed files with 1038 additions and 452 deletions

View File

@ -1,18 +1,10 @@
# 页面标题
<<<<<<< HEAD
VUE_APP_TITLE = 博诺思公共微服务平台
=======
VUE_APP_TITLE = 公共服务平台
>>>>>>> 8bd21926c835e55a8bb041e202098e441d9398f4
# 开发环境配置
ENV = 'development'
<<<<<<< HEAD
# 博诺思公共微服务平台/开发环境
=======
# 公共服务平台/开发环境
>>>>>>> 8bd21926c835e55a8bb041e202098e441d9398f4
VUE_APP_BASE_API = '/dev-api'
# 路由懒加载

View File

@ -1,16 +1,8 @@
# 页面标题
<<<<<<< HEAD
VUE_APP_TITLE = 博诺思公共微服务平台
=======
VUE_APP_TITLE = 公共服务平台
>>>>>>> 8bd21926c835e55a8bb041e202098e441d9398f4
# 生产环境配置
ENV = 'production'
<<<<<<< HEAD
# 博诺思公共微服务平台/生产环境
=======
# 公共服务平台/生产环境
>>>>>>> 8bd21926c835e55a8bb041e202098e441d9398f4
VUE_APP_BASE_API = '/prod-api'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,9 +1,10 @@
import request from '@/utils/request'
import request_formdata from '@/utils/request_formdata'
// 查询产品中心列表
export function getProductCenterListAPI(data) {
return request({
url: '/dataManage/product-center/list',
url: '/product/case/list',
method: 'GET',
params: data,
})
@ -11,27 +12,36 @@ export function getProductCenterListAPI(data) {
// 新增产品中心
export function addProductCenterAPI(data) {
return request({
url: '/dataManage/product-center/add',
return request_formdata({
url: '/product/case/add',
method: 'POST',
data: data,
data,
})
}
// 编辑产品中心
export function editProductCenterAPI(data) {
return request({
url: '/dataManage/product-center/edit',
return request_formdata({
url: '/product/case/update',
method: 'POST',
data: data,
data,
})
}
// 删除产品中心
export function deleteProductCenterAPI(data) {
return request({
url: '/dataManage/product-center/delete',
url: '/product/case/delete',
method: 'POST',
data: data,
data,
})
}
// 获取产品中心详情
export function getProductCenterDetailAPI(data) {
return request({
url: '/product/case/getDetails',
method: 'POST',
data,
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,90 +1,104 @@
<template>
<el-image
:src="`${realSrc}`"
fit="cover"
:style="`width:${realWidth};height:${realHeight};`"
:preview-src-list="realSrcList"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
<el-image
:src="src1"
fit="cover"
:style="`width:${realWidth};height:${realHeight};`"
:preview-src-list="realSrcList"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</template>
<script>
import { isExternal } from "@/utils/validate"
import { isExternal } from '@/utils/validate'
export default {
name: "ImagePreview",
props: {
src: {
type: String,
default: ""
name: 'ImagePreview',
props: {
src: {
type: String,
default: '',
},
src1: {
type: String,
default: '',
},
width: {
type: [Number, String],
default: '',
},
height: {
type: [Number, String],
default: '',
},
},
width: {
type: [Number, String],
default: ""
computed: {
realSrc() {
if (!this.src) {
return
}
let real_src = this.src.split(',')[0]
if (isExternal(real_src)) {
return real_src
}
return process.env.VUE_APP_BASE_API + real_src
},
realSrcList() {
// if (!this.src) {
// return
// }
// let real_src_list = this.src.split(',')
let srcList = []
// real_src_list.forEach((item) => {
// if (isExternal(item)) {
// return srcList.push(item)
// }
// return srcList.push(process.env.VUE_APP_BASE_API + item)
// })
// return srcList
if (!this.src1) {
return
}
srcList.push(this.src1)
return srcList
},
realWidth() {
return typeof this.width == 'string'
? this.width
: `${this.width}px`
},
realHeight() {
return typeof this.height == 'string'
? this.height
: `${this.height}px`
},
},
height: {
type: [Number, String],
default: ""
}
},
computed: {
realSrc() {
if (!this.src) {
return
}
let real_src = this.src.split(",")[0]
if (isExternal(real_src)) {
return real_src
}
return process.env.VUE_APP_BASE_API + real_src
},
realSrcList() {
if (!this.src) {
return
}
let real_src_list = this.src.split(",")
let srcList = []
real_src_list.forEach(item => {
if (isExternal(item)) {
return srcList.push(item)
}
return srcList.push(process.env.VUE_APP_BASE_API + item)
})
return srcList
},
realWidth() {
return typeof this.width == "string" ? this.width : `${this.width}px`
},
realHeight() {
return typeof this.height == "string" ? this.height : `${this.height}px`
}
}
}
</script>
<style lang="scss" scoped>
.el-image {
border-radius: 5px;
background-color: #ebeef5;
box-shadow: 0 0 5px 1px #ccc;
::v-deep .el-image__inner {
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
border-radius: 5px;
background-color: #ebeef5;
box-shadow: 0 0 5px 1px #ccc;
::v-deep .el-image__inner {
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
}
::v-deep .image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: #909399;
font-size: 30px;
}
}
::v-deep .image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: #909399;
font-size: 30px;
}
}
</style>

View File

@ -1,113 +1,112 @@
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<div :class="{ hidden: hidden }" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
name: 'Pagination',
props: {
total: {
required: true,
type: Number,
},
page: {
type: Number,
default: 1,
},
limit: {
type: Number,
default: 20,
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
},
},
// 5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7,
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper',
},
background: {
type: Boolean,
default: true,
},
autoScroll: {
type: Boolean,
default: true,
},
hidden: {
type: Boolean,
default: false,
},
},
page: {
type: Number,
default: 1
data() {
return {}
},
limit: {
type: Number,
default: 20
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
},
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
},
},
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
methods: {
handleSizeChange(val) {
if (this.currentPage * val > this.total) {
this.currentPage = 1
}
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
},
// 5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
data() {
return {
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
if (this.currentPage * val > this.total) {
this.currentPage = 1
}
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
background: #fff;
}
.pagination-container.hidden {
display: none;
display: none;
}
</style>

View File

@ -103,6 +103,7 @@ export default {
methods: {
//
async handleRemove(file, fileList) {
console.log(file, fileList, 'file,fileList')
this.$emit('onUploadChange', fileList)
// if (file.response && file.response.data.length > 0) {
// this.$emit('deleteFile', {
@ -123,6 +124,10 @@ export default {
// }
// }
this.$emit('update:fileList', fileList)
if (file.id) {
this.$emit('onSelectDeleteFileId', file.id)
}
},
//

View File

@ -3,7 +3,7 @@ import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/ruoyi"
import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
@ -14,139 +14,186 @@ export let isRelogin = { show: false }
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000,
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params)
url = url.slice(0, -1)
config.params = {}
config.url = url
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。')
return config
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url // 请求地址
const s_data = sessionObj.data // 请求数据
const s_time = sessionObj.time // 请求时间
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交'
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
service.interceptors.request.use(
(config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params)
url = url.slice(0, -1)
config.params = {}
config.url = url
}
if (
!isRepeatSubmit &&
(config.method === 'post' || config.method === 'put')
) {
const requestObj = {
url: config.url,
data:
typeof config.data === 'object'
? JSON.stringify(config.data)
: config.data,
time: new Date().getTime(),
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(
`[${config.url}]: ` +
'请求数据大小超出允许的5M限制无法进行防重复提交验证。',
)
return config
}
const sessionObj = cache.session.getJSON('sessionObj')
if (
sessionObj === undefined ||
sessionObj === null ||
sessionObj === ''
) {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url // 请求地址
const s_data = sessionObj.data // 请求数据
const s_time = sessionObj.time // 请求时间
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
if (
s_data === requestObj.data &&
requestObj.time - s_time < interval &&
s_url === requestObj.url
) {
const message = '数据正在处理,请勿重复提交'
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
},
(error) => {
console.log(error)
Promise.reject(error)
},
)
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false
store.dispatch('LogOut').then(() => {
location.href = '/index'
})
}).catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error
if (message == "Network Error") {
message = "后端接口连接异常"
} else if (message.includes("timeout")) {
message = "系统接口请求超时"
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常"
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
service.interceptors.response.use(
(res) => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (
res.request.responseType === 'blob' ||
res.request.responseType === 'arraybuffer'
) {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true
MessageBox.confirm(
'登录状态已过期,您可以继续留在该页面,或者重新登录',
'系统提示',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
},
)
.then(() => {
isRelogin.show = false
store.dispatch('LogOut').then(() => {
location.href = '/index'
})
})
.catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data
}
},
(error) => {
console.log('err' + error)
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
},
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config
}).then(async (data) => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg)
}
downloadLoadingInstance.close()
}).catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
downloadLoadingInstance = Loading.service({
text: '正在下载数据,请稍候',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
})
return service
.post(url, params, {
transformRequest: [
(params) => {
return tansParams(params)
},
],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config,
})
.then(async (data) => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg =
errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg)
}
downloadLoadingInstance.close()
})
.catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
}
export default service

View File

@ -0,0 +1,208 @@
import axios from 'axios'
import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
let downloadLoadingInstance
// 是否显示重新登录
export let isRelogin = { show: false }
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000,
})
// request拦截器
service.interceptors.request.use(
(config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params)
url = url.slice(0, -1)
config.params = {}
config.url = url
}
if (
!isRepeatSubmit &&
(config.method === 'post' || config.method === 'put')
) {
if (config.data instanceof FormData) {
// 对于FormData我们创建一个对象来保存数据以便验证
const formDataObj = new FormData()
config.data.forEach((value, key) => {
formDataObj.append(key, value)
})
config.data = formDataObj
delete config.headers['Content-Type']
}
const requestObj = {
url: config.url,
data:
typeof config.data === 'object'
? JSON.stringify(config.data)
: config.data,
time: new Date().getTime(),
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(
`[${config.url}]: ` +
'请求数据大小超出允许的5M限制无法进行防重复提交验证。',
)
return config
}
const sessionObj = cache.session.getJSON('sessionObj')
if (
sessionObj === undefined ||
sessionObj === null ||
sessionObj === ''
) {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url // 请求地址
const s_data = sessionObj.data // 请求数据
const s_time = sessionObj.time // 请求时间
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
if (
s_data === requestObj.data &&
requestObj.time - s_time < interval &&
s_url === requestObj.url
) {
const message = '数据正在处理,请勿重复提交'
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
},
(error) => {
console.log(error)
Promise.reject(error)
},
)
// 响应拦截器
service.interceptors.response.use(
(res) => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (
res.request.responseType === 'blob' ||
res.request.responseType === 'arraybuffer'
) {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true
MessageBox.confirm(
'登录状态已过期,您可以继续留在该页面,或者重新登录',
'系统提示',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
},
)
.then(() => {
isRelogin.show = false
store.dispatch('LogOut').then(() => {
location.href = '/index'
})
})
.catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')
} else {
return res.data
}
},
(error) => {
console.log('err' + error)
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
},
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = Loading.service({
text: '正在下载数据,请稍候',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
})
return service
.post(url, params, {
transformRequest: [
(params) => {
return tansParams(params)
},
],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config,
})
.then(async (data) => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg =
errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg)
}
downloadLoadingInstance.close()
})
.catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
}
export default service

View File

@ -11,19 +11,45 @@
<el-row>
<el-col :span="12">
<el-form-item label="名称" prop="name">
<el-input v-model="addAndEditForm.name" />
<el-input
clearable
maxlength="50"
show-word-limit
placeholder="请输入名称"
v-model="addAndEditForm.name"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="类型" prop="type">
<el-input v-model="addAndEditForm.type" />
<el-form-item label="类型" prop="typeId">
<el-select
clearable
filterable
style="width: 100%"
placeholder="请选择类型"
@change="handleTypeIdChange"
v-model="addAndEditForm.typeId"
>
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in dict.type.tb_product_type"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="链接" prop="link">
<el-input v-model="addAndEditForm.link" />
<el-form-item label="链接" prop="linkUrl">
<el-input
clearable
maxlength="500"
show-word-limit
placeholder="请输入链接"
v-model="addAndEditForm.linkUrl"
/>
</el-form-item>
</el-col>
<el-col :span="12">
@ -33,6 +59,7 @@
:file-size="10"
:file-type="['jpg', 'png', 'jpeg']"
:file-list.sync="addAndEditForm.cover"
@onSelectDeleteFileId="handleSelectDeleteFileId"
:is-uploaded="addAndEditForm.cover.length >= 1"
/>
</el-form-item>
@ -40,12 +67,10 @@
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="是否支持访问" prop="isSupportAccess">
<el-radio-group
v-model="addAndEditForm.isSupportAccess"
>
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
<el-form-item label="是否支持访问" prop="isAccess">
<el-radio-group v-model="addAndEditForm.isAccess">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -53,12 +78,15 @@
<el-row>
<el-col :span="24">
<el-form-item label="产品介绍" prop="productIntroduction">
<el-form-item label="产品介绍" prop="introduction">
<el-input
clearable
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="addAndEditForm.productIntroduction"
maxlength="2000"
show-word-limit
placeholder="请输入产品介绍"
:autosize="{ minRows: 6, maxRows: 10 }"
v-model="addAndEditForm.introduction"
/>
</el-form-item>
</el-col>
@ -99,56 +127,73 @@
</el-row>
<!-- 产品案例表单绑定当前Tab的formInfo -->
<el-form
label-width="120px"
v-if="currentTabFormInfo"
:model="currentTabFormInfo"
:ref="`caseForm_${currentTab}`"
:rules="addAndEditFormCaseRules"
>
<el-row>
<el-col :span="12">
<el-form-item label="案例公司" prop="caseCompany">
<el-input v-model="currentTabFormInfo.caseCompany" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="案例图片" prop="caseSort">
<el-input
type="number"
min="1"
v-model.number="currentTabFormInfo.caseSort"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="案例图片" prop="caseImage">
<UploadImgFormData
:limit="6"
:file-size="10"
:file-type="['jpg', 'png', 'jpeg']"
:file-list.sync="currentTabFormInfo.caseImage"
:is-uploaded="
currentTabFormInfo.caseImage.length >= 6
"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="案例介绍" prop="caseIntroduction">
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="currentTabFormInfo.caseIntroduction"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template v-for="item in editableTabs">
<el-form
:key="item.name"
label-width="120px"
:model="item.formInfo"
:ref="`caseForm_${item.name}`"
:rules="addAndEditFormCaseRules"
v-show="currentTab === item.name"
>
<el-row>
<el-col :span="12">
<el-form-item label="案例公司" prop="caseCompany">
<el-input
maxlength="50"
show-word-limit
placeholder="请输入案例公司"
v-model="item.formInfo.caseCompany"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="案例排序" prop="caseSort">
<el-input
clearable
placeholder="请输入案例排序"
v-model.number="item.formInfo.caseSort"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item
label="案例图片"
prop="caseImage"
required
>
<UploadImgFormData
:limit="6"
:file-size="10"
:file-type="['jpg', 'png', 'jpeg']"
:file-list.sync="item.formInfo.caseImage"
@onSelectDeleteFileId="handleSelectDeleteFileId"
:is-uploaded="
item.formInfo.caseImage.length >= 6
"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="案例介绍" prop="caseIntroduction">
<el-input
type="textarea"
maxlength="8000"
show-word-limit
placeholder="请输入案例介绍"
:autosize="{ minRows: 6, maxRows: 10 }"
v-model="item.formInfo.caseIntroduction"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<!-- 底部按钮 -->
<el-row class="add-and-edit-form-footer">
@ -163,45 +208,62 @@
</template>
<script>
import {
addProductCenterAPI,
editProductCenterAPI,
getProductCenterDetailAPI,
} from '@/api/dataManage/product-center'
import TitleTip from '@/components/TitleTip'
import UploadImgFormData from '@/components/UploadImgFormData'
export default {
name: 'AddAndEditForm',
dicts: ['tb_product_type'],
components: { TitleTip, UploadImgFormData },
props: {
formType: {
type: Number,
default: 1,
},
detailsId: {
type: String,
default: '',
},
},
data() {
return {
currentTab: '1', // Tab
//
addAndEditForm: {
name: '',
type: '',
link: '',
cover: '',
isSupportAccess: 1,
productIntroduction: '', //
name: '', //
typeId: '', //
typeName: '', //
cover: [], //
linkUrl: '', //
isAccess: '1', // 访
introduction: '', //
},
//
addAndEditFormRules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
],
type: [
typeId: [
{ required: true, message: '请输入类型', trigger: 'blur' },
],
link: [
linkUrl: [
{ required: true, message: '请输入链接', trigger: 'blur' },
],
cover: [
{ required: true, message: '请输入封面', trigger: 'blur' },
],
isSupportAccess: [
isAccess: [
{
required: true,
message: '请选择是否支持访问',
trigger: 'change',
}, // radiochange
],
productIntroduction: [
introduction: [
{
required: true,
message: '请输入产品介绍',
@ -221,17 +283,12 @@ export default {
caseImage: [
{
required: true,
message: '请输入案例图片',
trigger: 'blur',
trigger: 'change',
message: '请上传案例图片',
},
],
caseSort: [
{ required: true, message: '请输入排序', trigger: 'blur' },
{
type: 'number',
message: '排序必须为数字',
trigger: 'blur',
}, //
],
caseIntroduction: [
{
@ -244,16 +301,20 @@ export default {
// TabsTab
editableTabs: [
{
title: '案例1',
name: '1',
title: '案例1',
formInfo: {
id: '',
caseSort: 1,
caseImage: '',
caseImage: [],
caseCompany: '',
caseIntroduction: '',
},
},
],
editDeleteCaseId: [], // ID
editDeleteFileId: [], // ID
}
},
computed: {
@ -271,6 +332,7 @@ export default {
)
},
},
methods: {
//
handleCancel() {
@ -287,16 +349,85 @@ export default {
},
//
async handleSave() {
console.log(this.addAndEditForm, '基本信息表单')
this.$message.closeAll()
try {
// 1.
await this.$refs.addAndEditForm.validate()
// 2.
const caseFormPromises = this.editableTabs.map((tab) => {
return this.$refs[`caseForm_${tab.name}`].validate()
// const caseFormPromises = this.editableTabs.map((tab) => {
// return this.$refs[`caseForm_${tab.name}`].validate()
// })
// await Promise.all(caseFormPromises)
// await this.$refs[`caseForm_${this.currentTab}`].validate()
// 2.
const requiredFields = [
{ key: 'caseCompany', name: '案例公司' },
{ key: 'caseSort', name: '案例排序' },
{ key: 'caseIntroduction', name: '案例介绍' },
]
let isValid = true
let errorMessage = ''
let errorCaseIndex = ''
//
this.editableTabs.forEach((tab, tabIndex) => {
if (!isValid) return //
//
requiredFields.forEach((field) => {
if (!isValid) return
const value = tab.formInfo[field.key]
// undefinednull
if (
value === '' ||
value === undefined ||
value === null
) {
isValid = false
errorCaseIndex = tabIndex
errorMessage = `案例${tabIndex + 1}${
field.name
}为必填项`
}
// 0
else if (
field.key === 'caseSort' &&
(isNaN(value) || value === '')
) {
isValid = false
errorCaseIndex = tabIndex
errorMessage = `案例${tabIndex + 1}${
field.name
}必须为有效数字`
}
})
//
if (isValid && tab.formInfo.caseImage.length === 0) {
isValid = false
errorMessage = `案例${
tabIndex + 1
}请至少上传一张案例图片`
errorCaseIndex = tabIndex
}
})
await Promise.all(caseFormPromises)
//
if (!isValid) {
this.currentTab = this.editableTabs[errorCaseIndex].name
this.$nextTick(async () => {
await this.$refs[
`caseForm_${this.editableTabs[errorCaseIndex].name}`
][0].validate()
})
throw new Error(errorMessage)
}
// 3.
const submitData = {
@ -304,10 +435,63 @@ export default {
cases: this.editableTabs.map((tab) => tab.formInfo),
}
console.log('提交数据:', submitData)
//
this.$message.success('保存成功')
this.$emit('closeDialog', true)
//
const formData = new FormData()
const filesTypes = []
const { cover } = this.addAndEditForm
cover.forEach((item) => {
if (!item.id) {
formData.append('files', item.raw)
filesTypes.push('s')
}
})
const addAndEditFormParams = this.addAndEditForm
delete addAndEditFormParams.cover
for (const key in addAndEditFormParams) {
formData.append(key, addAndEditFormParams[key])
}
const caseCompanyList = this.editableTabs.map((item, index) => {
item.formInfo.caseImage.forEach((file) => {
if (!file.id) {
formData.append('files', file.raw)
filesTypes.push(index)
}
})
return {
caseCompany: item.formInfo.caseCompany,
caseIntroduction: item.formInfo.caseIntroduction,
caseSort: item.formInfo.caseSort,
id: item.formInfo.id,
}
})
formData.append('json', JSON.stringify(caseCompanyList))
formData.append('fileType', filesTypes.join(','))
if (this.formType === 2) {
formData.append('delCase', this.editDeleteCaseId.join(','))
formData.append('delFile', this.editDeleteFileId.join(','))
formData.append('id', this.detailsId)
}
const API =
this.formType === 2
? editProductCenterAPI
: addProductCenterAPI
const res = await API(formData)
if (res.code === 200) {
this.$modal.msgSuccess(
this.formType === 2 ? '编辑成功' : '新增成功',
)
this.$emit('closeDialog', true)
}
} catch (error) {
console.log(error, 'error')
//
this.$message.error('请完善所有必填项后再保存')
}
@ -319,8 +503,9 @@ export default {
title: `案例${newTabIndex}`,
name: newTabIndex.toString(),
formInfo: {
id: '',
caseCompany: '',
caseImage: '',
caseImage: [],
caseIntroduction: '',
caseSort: newTabIndex,
},
@ -341,6 +526,13 @@ export default {
const removeIndex = this.editableTabs.findIndex(
(tab) => tab.name === removeTabName,
)
if (this.formType === 2) {
this.editDeleteCaseId.push(
this.editableTabs[removeIndex].formInfo?.id,
)
}
this.editableTabs.splice(removeIndex, 1)
// TabTab
@ -348,18 +540,100 @@ export default {
this.currentTab = this.editableTabs[0].name
}
// Tab Tab1
if (this.currentTabIndex >= removeIndex) {
this.currentTab =
this.editableTabs[this.currentTabIndex - 1].name
}
//
this.editableTabs.forEach((tab, index) => {
tab.title = `案例${index + 1}`
tab.name = (index + 1).toString()
tab.formInfo.caseSort = index + 1
})
},
// TabTab
handleTabClick() {
this.$nextTick(() => {
this.$refs[`caseForm_${this.currentTab}`]?.validate(
(isValid) => {},
)
// this.$nextTick(() => {
// this.$refs[`caseForm_${this.currentTab}`]?.validate(
// (isValid) => {},
// )
// })
},
// ID
handleTypeIdChange(value) {
this.addAndEditForm.typeName = this.dict.type.tb_product_type.find(
(item) => item.value === value,
).label
},
//
async getProductCenterDetail() {
const res = await getProductCenterDetailAPI({
id: this.detailsId,
})
console.log(res, '产品中心详情')
const {
name,
typeId,
typeName,
image,
linkUrl,
introduction,
isAccess,
list,
} = res?.data
this.addAndEditForm = {
name,
typeId,
typeName,
linkUrl,
introduction,
isAccess,
cover: [
{ url: image.url, id: image.id, name: image.originalName },
],
}
this.editableTabs = list.map((item, index) => {
return {
title: `案例${index + 1}`,
name: (index + 1).toString(),
formInfo: {
id: item.id,
caseCompany: item.caseCompany,
caseIntroduction: item.caseIntroduction,
caseSort: item.caseSort,
caseImage: item.imageList.map((e) => {
return {
id: e.id,
url: e.url,
name: e.originalName,
}
}),
},
}
})
},
// ID
handleSelectDeleteFileId(id) {
this.editDeleteFileId.push(id)
},
},
watch: {
detailsId: {
handler(newVal) {
if (newVal) {
this.getProductCenterDetail()
}
},
immediate: true,
},
},
}

View File

@ -20,11 +20,14 @@
clearable
filterable
placeholder="选择类型"
v-model="queryParams.type"
v-model="queryParams.typeId"
>
<el-option value="1" label="智慧基建" />
<el-option value="2" label="智慧后勤" />
<el-option value="3" label="数智装备" />
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
v-for="item in dict.type.tb_product_type"
/>
</el-select>
</el-form-item>
<el-form-item>
@ -58,7 +61,7 @@
新增
</el-button>
</el-col>
<el-col :span="1.5">
<!-- <el-col :span="1.5">
<el-button
plain
size="mini"
@ -78,7 +81,7 @@
@click="onHandleExport"
>
导出
</el-button>
</el-button> -->
</el-col>
</el-row>
@ -95,22 +98,24 @@
:key="column.prop"
:prop="column.prop"
:label="column.label"
show-overflow-tooltip
v-for="column in columns"
>
<template slot-scope="scope">
<template v-if="column.prop === 'cover'">
<template v-if="column.prop === 'linkImage'">
<ImagePreview
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
:width="50"
:height="50"
:src1="scope.row.linkImage"
/>
</template>
<template v-else-if="column.prop === 'link'">
<template v-else-if="column.prop === 'linkUrl'">
<a
:href="scope.row[column.prop]"
target="_blank"
class="link-text"
>{{ scope.row[column.prop] }}
:href="scope.row[column.prop]"
>
{{ scope.row[column.prop] }}
</a>
</template>
<template v-else>
@ -142,49 +147,56 @@
</el-table-column>
</el-table>
<!-- 新增和编辑表单 -->
<!-- 分页 -->
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getProductCenterListFun"
/>
<!-- 新增和编辑表单 -->
<DialogModel
:dialogConfig="dialogConfig"
@closeDialogOuter="handleCloseDialogOuter"
>
<template #outerContent>
<AddAndEditForm @closeDialog="closeDialog" />
<AddAndEditForm
:formType="formType"
:detailsId="detailsId"
@closeDialog="closeDialog"
/>
</template>
</DialogModel>
</div>
</template>
<script>
import AddAndEditForm from './components/addAndEditForm.vue'
import {
getProductCenterListAPI,
deleteProductCenterAPI,
} from '@/api/dataManage/product-center'
import DialogModel from '@/components/DialogModel'
import AddAndEditForm from './components/addAndEditForm.vue'
export default {
name: 'ProductCenter',
dicts: ['tb_product_type'],
components: {
AddAndEditForm,
DialogModel,
},
data() {
return {
tableData: [
{
name: '产品1',
type: '智慧基建',
cover: 'https://www.baidu.com',
link: 'https://www.baidu.com',
createBy: 'admin',
},
{
name: '产品2',
type: '智慧后勤',
cover: 'https://www.baidu.com',
link: 'https://www.baidu.com',
createBy: 'admin',
},
],
total: 0,
formType: 1, // 1: 2:
detailsId: '', // id
tableData: [],
queryParams: {
pageNum: 1,
pageSize: 10,
name: undefined,
type: undefined,
typeId: undefined,
},
columns: [
{
@ -192,19 +204,19 @@ export default {
label: '名称',
},
{
prop: 'type',
prop: 'typeName',
label: '类型',
},
{
prop: 'cover',
prop: 'linkImage',
label: '封面',
},
{
prop: 'link',
prop: 'linkUrl',
label: '链接',
},
{
prop: 'createBy',
prop: 'createUser',
label: '创建人',
},
{
@ -223,33 +235,66 @@ export default {
}
},
methods: {
onHandleQuery() {
console.log(this.queryParams)
//
async getProductCenterListFun() {
const res = await getProductCenterListAPI(this.queryParams)
this.tableData = res?.rows
this.total = res?.total
},
//
onHandleQuery() {
this.getProductCenterListFun()
},
//
onResetQuery() {
this.queryParams = {
name: undefined,
type: undefined,
typeId: undefined,
pageNum: 1,
pageSize: 10,
}
this.getProductCenterListFun()
},
//
onHandleAdd() {
console.log('新增')
this.formType = 1
this.detailsId = null
this.dialogConfig.outerTitle = '新增'
this.dialogConfig.outerVisible = true
},
//
onHandleImport() {
console.log('导入')
},
//
onHandleExport() {
console.log('导出')
},
//
onHandleEdit(row) {
console.log('编辑', row)
this.formType = 2
this.detailsId = row.id
this.dialogConfig.outerTitle = '编辑'
this.dialogConfig.outerVisible = true
},
//
onHandleDelete(row) {
console.log('删除', row)
this.$confirm('确定删除该产品中心吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await deleteProductCenterAPI({ id: row.id })
if (res.code === 200) {
this.$modal.msgSuccess('删除成功')
this.onHandleQuery()
}
})
.catch(() => {
console.log('取消')
})
},
//
handleCloseDialogOuter() {
@ -264,7 +309,7 @@ export default {
},
},
created() {
// this.onHandleQuery()
this.getProductCenterListFun()
},
}
</script>