对接ppt分析

This commit is contained in:
wangchengming 2025-09-16 21:54:23 +08:00
parent 96203bec76
commit 5a152d6912
10 changed files with 265 additions and 82 deletions

View File

@ -6,3 +6,5 @@ VITE_APP_ENV = 'development'
# 媒介管理系统/开发环境
VITE_APP_BASE_API = '/dev-api'
VITE_APP_Analysis_API = '/ppt-analysis-api'

View File

@ -6,6 +6,8 @@ VITE_APP_ENV = 'production'
# 媒介管理系统/生产环境
VITE_APP_BASE_API = '/prod-api'
VITE_APP_Analysis_API = '/ppt-analysis-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

View File

@ -1,9 +1,9 @@
import request from '@/utils/request'
import request from '@/utils/requestAnalysis'
// 上传PPT分析文件
export function uploadPPTAnalysisFile(query) {
return request({
url: '/upload',
url: '/api/upload',
method: 'post',
data: query,
timeout: 300000, // 300秒 = 5分钟根据文件大小调整
@ -16,7 +16,7 @@ export function uploadPPTAnalysisFile(query) {
// 获取PPT分析任务分页列表
export function pptAnalysisTaskPageList(query) {
return request({
url: '/tasks',
url: '/api/tasks',
method: 'get',
params: query
})
@ -25,7 +25,7 @@ export function pptAnalysisTaskPageList(query) {
// 获取PPT分析统计
export function pptAnalysisStatistics(query) {
return request({
url: '/statistics',
url: '/api/statistics',
method: 'get',
params: query
})
@ -34,7 +34,7 @@ export function pptAnalysisStatistics(query) {
// 删除PPT分析任务
export function deletePPTAnalysisTask(taskId) {
return request({
url: '/tasks/' + taskId,
url: '/api/tasks/' + taskId,
method: 'DELETE'
})
}
@ -42,7 +42,7 @@ export function deletePPTAnalysisTask(taskId) {
// 导出PPT分析报告
export function exportPPTAnalysisReport(taskId) {
return request({
url: '/export/' + taskId,
url: '/api/export/' + taskId,
method: 'get',
responseType: 'blob'
})

View File

@ -0,0 +1,152 @@
import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
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'
import useUserStore from '@/store/modules/user'
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: import.meta.env.VITE_APP_Analysis_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.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
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false
useUserStore().logOut().then(() => {
location.href = '/index'
})
}).catch(() => {
isRelogin.show = false
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' })
return Promise.reject(new Error(msg))
} else if (code !== 200) {
ElNotification.error({ title: msg })
return Promise.reject('error')
} else {
return Promise.resolve(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) + "异常"
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", 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']
ElMessage.error(errMsg)
}
downloadLoadingInstance.close()
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
}
export default service

View File

@ -39,12 +39,12 @@ const mediaType = ref(null)
const docUploadList = ref([])
//
const requestDocUpload = (options) => {
const requestDocUpload = (options) => {
proxy.$modal.loading('正在上传文件,请耐心等待...')
const { file } = options
var formData = new FormData();
formData.append('mediaType', mediaType.value);
formData.append('file', file);
formData.append('file', file);
importPriceByExcel(formData).then(res => {
if (res.code == 200) {
proxy.$modal.msgSuccess("导入成功")
@ -54,6 +54,9 @@ const requestDocUpload = (options) => {
proxy.$modal.closeLoading()
proxy.$modal.msgError(res.msg);
}
}).catch(error => {
proxy.$modal.closeLoading()
proxy.$modal.msgError(res.msg);
})
}
//

View File

@ -627,8 +627,8 @@ const renderMassMarks = () => {
mediaByMap(queryParams.value).then(res => {
if (res.code == 200) {
res.data.forEach(itemPoint => {
if (itemPoint.businessType == 1) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "mediaId": itemPoint.mediaId, "style": 0 })
if (itemPoint.businessType == 2) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "mediaId": itemPoint.mediaId, "style": 1 })
if (itemPoint.dataScopeDeptId == 220) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "mediaId": itemPoint.mediaId, "style": 0 })
if (itemPoint.dataScopeDeptId == 219) points.value.push({ "lnglat": [itemPoint.x, itemPoint.y], "mediaId": itemPoint.mediaId, "style": 1 })
});
}
}).then(res => {

View File

@ -178,10 +178,10 @@ const addBusGeoConvertTask = () => {
}
//
const handleStartTask = (row) => {
// if (row.state == 1) {
// proxy.$modal.msgSuccess("...")
// return
// }
if (row.state == 1) {
proxy.$modal.msgSuccess("任务已经开始,请等待...")
return
}
startBusGeoConvert({ id: row.id }).then(res => {
if (res.code == 200) {
getTaskRecordPageList()
@ -192,8 +192,7 @@ const handleStartTask = (row) => {
//
const handleResultOption = (row) => {
if (row.progress == '0') {
if (row.state !== 2) {
return
}
exportBusGeoConvert({ id: row.id }).then(res => {

View File

@ -27,76 +27,63 @@
<el-col :span="12">
<div class="toolItemTitle mb20">使用统计</div>
<el-form ref="detailFormRef" :model="detailForm" label-width="160px" class="myUsingDetail">
<div class="splineBar" />
<el-form-item label="统计周期">
{{ detailForm.mediaName }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="周期剩余天数">
{{ detailForm.mediaCategoryStr }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="处理PPT文件数">
{{ detailForm.displayFormStr }}
{{ detailForm.analysis_statistics.unique_tools }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="提取图片总数">
{{ detailForm.mediaSize }}
{{ detailForm.analysis_statistics.total_images }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="生成报告总数">
{{ detailForm.provinceName }}
{{ detailForm.task_statistics.completed_tasks }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="汇总报告数量">
{{ detailForm.address }}
<el-form-item label="生成失败报告数量">
{{ detailForm.task_statistics.failed_tasks }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="分项报告数量">
{{ detailForm.advantages }}
</el-form-item>
<div class="splineBar" />
<div class="splineBar" />
<el-form-item label="Adobe图片数量">
{{ detailForm.advantages }}
</el-form-item>
<div class="splineBar" />
<el-form-item label="最后更新时间">
{{ detailForm.advantages }}
{{ detailForm.analysis_statistics.adobe_images }}
</el-form-item>
<div class="splineBar" />
</el-form>
</el-col>
</el-row>
<div class="toolItemTitle mb20">任务记录</div>
<div class="toolItemTitle mb20">
任务记录
<el-icon class="refreshIcon" @click="getTaskRecordPageList">
<Refresh />
</el-icon>
</div>
<el-table v-loading="loading" :data="taskList" height="calc(100% - 422px)">
<el-table-column label="序号" align="center" width="80">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="文件名称" align="left" prop="fileName" min-width="180"
<el-table-column label="文件名称" align="left" prop="file_name" min-width="180"
:show-overflow-tooltip="true" />
<el-table-column label="任务进度" align="center" prop="process" min-width="150">
<el-table-column label="任务进度" align="center" prop="progress" min-width="150">
<template #default="scope">
<el-progress class="myprogress" type="circle" :percentage="scope.row.process" width="40"
stroke-width="2" :color="colors" />
<el-progress class="myprogress" type="circle"
:percentage="scope.row.progress == null ? 0 : scope.row.progress" :width="40"
:stroke-width="2" :color="colors" />
</template>
</el-table-column>
<el-table-column label="操作时间" align="center" prop="completed_at" width="210">
<template #default="scope">
<span>{{ parseTime(scope.row.completed_at) }}</span>
</template>
</el-table-column>
<el-table-column label="操作时间" align="center" prop="optionTime" width="210" />
<el-table-column label="操作" :width="160" align="center">
<template #default="scope">
<el-button v-if="scope.row.process == 0" link type="primary"
v-hasPermi="['system:pptTemplate:edit']">
<span v-if="toolType == 1">开始比对</span>
<span v-if="toolType == 2">开始分析</span>
<span v-if="toolType == 3">开始执行</span>
<el-button v-if="scope.row.progress == 100" link type="primary"
@click="handleResultOption(scope.row)">
导出结果
</el-button>
<el-button v-if="scope.row.process != 0" link type="primary"
@click="handleResultOption(scope.row)" v-hasPermi="['system:pptTemplate:remove']">
<span v-if="toolType == 1">比对结果</span>
<span v-else>导出结果</span>
</el-button>
<el-button link type="primary" v-hasPermi="['system:pptTemplate:remove']">
<el-button link type="primary" @click="handleDeleteTask(scope.row)">
删除
</el-button>
</template>
@ -113,14 +100,17 @@
<script setup>
import { onMounted, nextTick, defineEmits, ref } from 'vue'
import { Close } from '@element-plus/icons-vue'
import { uploadPPTAnalysisFile, pptAnalysisTaskPageList, pptAnalysisStatistics, deletePPTAnalysisTask, exportPPTAnalysisReport } from "@/api/pptAnalysis"
import { uploadPPTAnalysisFile, pptAnalysisTaskPageList, pptAnalysisStatistics, deletePPTAnalysisTask, exportPPTAnalysisReport } from "@/api/pptAnalysis"
const { proxy } = getCurrentInstance()
const emit = defineEmits(['handleGoBack']);
const baseUrl = import.meta.env.VITE_APP_BASE_API
const formTitle = ref('')
const detailForm = ref({})
const detailForm = ref({
analysis_statistics: {},
task_statistics: {}
})
//
const docUploadList = ref([])
@ -141,29 +131,21 @@ const data = reactive({
}
})
const { queryParams } = toRefs(data)
//
const requestDocUpload = (options) => {
const { file } = options
var formData = new FormData();
formData.append('file', file);
proxy.$modal.loading('正在上传文件,请耐心等待...')
uploadPPTAnalysisFile(formData).then(res => {
if (res.code == 200) {
proxy.$modal.msgSuccess("上传成功")
docUploadList.value.push({
name: res.originalFilename,
url: baseUrl + res.fileName,
suffix: res.suffix
})
// form.value.fileUrl = baseUrl + res.fileName
// form.value.fileName = res.fileName
// form.value.newFileName = res.newFileName
// form.value.originalFileName = res.originalFilename
// form.value.size = res.size
// form.value.suffix = res.suffix
} else {
proxy.$modal.msgError(res.msg);
}
proxy.$modal.msgSuccess("上传成功")
proxy.$modal.closeLoading()
docUploadList.value = []
getTaskRecordPageList()
}).catch(res => {
proxy.$modal.msgError(res.msg);
proxy.$modal.closeLoading()
})
}
//
@ -185,24 +167,61 @@ const removeDocUpload = (file, fileList) => {
);
}
//
const handleDeleteTask = (row) => {
proxy.$modal.confirm('是否确认该任务?').then(function () {
return deletePPTAnalysisTask(row.task_id)
}).then(() => {
getTaskRecordPageList()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => { })
}
//
const handleResultOption = (row) => {
if (row.progress == 0) {
return
}
exportPPTAnalysisReport(row.task_id).then(res => {
const downLoadName = 'PPT分析结果_' + getCurrentTime() + '.xlsx'
// a
const a = document.createElement('a')
a.href = URL.createObjectURL(res)
// adownload
a.setAttribute('download', downLoadName)
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
})
}
const getCurrentTime = () => {
//
var getTime = new Date().getTime(); //
var time = new Date(getTime); //
var year = time.getFullYear(); //
var month = (time.getMonth() + 1).toString().padStart(2, '0'); //
var date = time.getDate().toString().padStart(2, '0'); //
var hour = time.getHours().toString().padStart(2, '0'); //
var minute = time.getMinutes().toString().padStart(2, '0'); //
var second = time.getSeconds().toString().padStart(2, '0'); //
var gettime = year + month + date + hour + minute + second
return gettime
}
// 使
const getStatistics = () => {
pptAnalysisStatistics().then(res => {
console.log('统计信息', res)
detailForm.value = res
})
}
//
const getTaskRecordPageList = () => {
loading.value = true
pptAnalysisTaskPageList(queryParams.value).then(res => {
console.log('分析任务列表', res)
// taskList.value = res.data.list
// total.value = res.data.total
// loading.value = false
taskList.value = res.tasks
total.value = res.pagination.total
loading.value = false
})
}
//

View File

@ -634,8 +634,8 @@ const renderMassMarks = () => {
//
points.value = []
outdoorMediaList.value.forEach(itemPoint => {
if (itemPoint.businessType == 1) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 0 })
if (itemPoint.businessType == 2) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 1 })
if (itemPoint.dataScopeDeptId == 220) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 0 })
if (itemPoint.dataScopeDeptId == 219) points.value.push({ "lnglat": [itemPoint.mapX, itemPoint.mapY], "name": itemPoint.mediaName, "mediaId": itemPoint.id, "style": 1 })
});
// MassMarks
massMarks.value = new AMap.MassMarks(points.value, {

View File

@ -3,6 +3,7 @@ import path from 'path'
import createVitePlugins from './vite/plugins'
const baseUrl = 'http://43.143.229.145:9002/prod-api' // 后端接口
const analysisUrl = 'http://43.143.229.145:9002/ppt-analysis-api'
// https://vitejs.dev/config/
export default defineConfig(({ mode, command }) => {
@ -13,7 +14,7 @@ export default defineConfig(({ mode, command }) => {
// 默认情况下vite 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
base: VITE_APP_ENV === 'production' ? '/' : '/',
plugins: createVitePlugins(env, command === 'build'),
plugins: createVitePlugins(env, command === 'build'),
resolve: {
// https://cn.vitejs.dev/config/#resolve-alias
alias: {
@ -52,6 +53,11 @@ export default defineConfig(({ mode, command }) => {
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '')
},
'/ppt-analysis-api': {
target: analysisUrl,
changeOrigin: true,
rewrite: (p) => p.replace(/^\/ppt-analysis-api/, '')
},
// springdoc proxy
'^/v3/api-docs/(.*)': {
target: baseUrl,