提交 dbfebf45 authored 作者: lidongxu's avatar lidongxu

feat(report): 新增:积木报表自定义报表设计页面

上级 f3e1cd12
......@@ -15,6 +15,11 @@ VITE_APP_PROMOTION = '/dev-promotion-api' # 促销
VITE_APP_REDIRECT_URL = 'http://localhost:8085'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = 'https://sfa-qa.wxl66.cn/report/jmreport/view'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = 'https://sfa-qa.wxl66.cn/report/jmreport/index'
# 模板表格
# 促销计划模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF3.0.xlsx'
\ No newline at end of file
......@@ -14,6 +14,10 @@ VITE_APP_PROMOTION = '/promotion-api' # 促销
VITE_APP_REDIRECT_URL = 'https://sfa.wxl66.cn/link/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = '/report/jmreport/view'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '/report/jmreport/index
# 模板表格
# 促销计划模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF3.0.xlsx'
\ No newline at end of file
......@@ -13,6 +13,11 @@ VITE_APP_PROMOTION = '/promotion-api' # 促销
VITE_APP_REDIRECT_URL = 'https://sfa-qa.wxl66.cn/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
VITE_APP_REPORT_PREVIEW_URL = '/report/jmreport/view'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '/report/jmreport/index
# 模板表格
# 促销计划模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF3.0.xlsx'
\ No newline at end of file
......@@ -11,7 +11,10 @@ export * from './bi/product'
export * from './bi/sale'
export * from './bi/store'
export * from './bi/supply'
export * from './jimu/index'
export * from './jimu/design'
export * from './jimu/ext'
export * from './jimu/list'
export * from './jimu/manager'
export * from './other/logistics'
export * from './other/version'
export * from './monitor/cache'
......@@ -31,7 +34,6 @@ export * from './system/menu'
export * from './system/notice'
export * from './system/operlog'
export * from './system/post'
export * from './system/report'
export * from './system/role'
export * from './system/user'
export * from './tool/gen'
import request from '@/utils/request'
import { getToken } from '@/utils/auth'
// 提交其他报表超链接
export function submitOtherReportAPI(data) {
// 获取文件夹结构
export function getReportFolderStructureAPI(params) {
return request({
url: '/report/share',
method: 'post',
data
url: `/report/jmreport/category/list`,
params
})
}
// 录入其他平台报表链接
export function enterOtherReportAPI(data) {
// 获取报表设计中的报表列表
export function getDesignReportListAPI(params) {
return request({
url: '/report/customer/core/save',
method: 'POST',
data
url: `/report/jmreport/query/report/folder`,
params
})
}
// 获取积木报表文件夹列表(分组)
export function getReportFolderListAPI() {
// 删除报表
export function deleteReportAPI(params) {
return request({
url: `/report/jmreport/category/list`,
headers: {
Token: getToken()
},
method: 'GET'
url: `/report/jmreport/delete`,
method: 'DELETE',
params,
data: params
})
}
/********* 报表设计 *********/
// 获取文件夹结构
export function getReportFolderStructureAPI(params) {
// 复制报表
export function copyReportAPI(params) {
return request({
url: `/report/jmreport/category/list`,
url: `/report/jmreport/reportCopy`,
params
})
}
// 获取报表设计中的报表列表
export function getDesignReportListAPI(params) {
// 新建报表(需要先调用接口创建一个 id,传给新建页面)
export function addReportAPI() {
return request({
url: `/report/jmreport/query/report/folder`,
params
url: `/report/jmreport/save`,
method: 'POST',
data: {}
})
}
// 导入报表(报表来自积木设计页面右键导出报表配置 json 文件的导入)
export function importReportAPI(data) {
return request({
url: `/report/jmreport/importReportConfig`,
method: 'POST',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
import request from '@/utils/request'
// 提交其他报表超链接
export function submitOtherReportAPI(data) {
return request({
url: '/report/share',
method: 'post',
data
})
}
// 录入其他平台报表链接
export function enterOtherReportAPI(data) {
return request({
url: '/report/customer/core/save',
method: 'POST',
data
})
}
\ No newline at end of file
import request from '@/utils/request'
// 查看报表-属于自己和被分配的报表列表(暂时不包含其他平台的报表)
export function getReportListByUserAPI(params) {
return request({
url: `/report/jimuReport/query/list`,
params
})
}
// 查看报表-搜索时:报表分类下拉列表接口
export const selReportCategoryAPI = () => {
return request({
url: `/report/jimuReportCategory/query/list`
})
}
\ No newline at end of file
import request from '@/utils/request'
// 管理员查询所有报表接口
export const selReportListAPI = (params) => {
return request({
......@@ -7,22 +8,6 @@ export const selReportListAPI = (params) => {
})
}
// 授权单个报表给单个/多个用户
export const batchAuthReportAPI = (data) => {
return request({
url: `/report/jimuReportAuth/core/authUsers`,
method: "PUT",
data
})
}
// 报表分类下拉列表接口
export const selReportCategoryAPI = () => {
return request({
url: `/report/jimuReportCategory/query/list`
})
}
// 查询-报表已分配用户列表
export const selReportAuthUserListAPI = (params) => {
return request({
......@@ -30,3 +15,12 @@ export const selReportAuthUserListAPI = (params) => {
params
})
}
// 授权单个报表,给单个/多个用户
export const batchAuthReportAPI = (data) => {
return request({
url: `/report/jimuReportAuth/core/authUsers`,
method: "PUT",
data
})
}
......@@ -26,6 +26,7 @@ import { download } from '@/utils/request'
import { useDict } from '@/utils/dict'
import { checkPermi } from '@/utils/permission'
import eventBus from '@/utils/eventBus'
import { chooseFile } from '@/utils/upload'
/****************** 移动端 ******************/
import registerMobile from "@/mobile/main.js"
......@@ -82,6 +83,7 @@ app.config.globalProperties.selectDictLabels = selectDictLabels
app.config.globalProperties.isMobile = isMobile
app.config.globalProperties.checkPermi = checkPermi
app.config.globalProperties.eventBus = eventBus
app.config.globalProperties.chooseFile = chooseFile
// 全局组件
app.component('svg-icon', SvgIcon)
......
......@@ -13,6 +13,7 @@ export * from './route'
export * from './ruoyi'
export * from './scroll-to'
export * from './theme'
export * from './upload'
export * from './url'
export * from './validate'
......
// 通过 JS 方式实现选择文件,而非组件
export const chooseFile = ({ accept, id = 'fileInput' }) => {
return new Promise((resolve, reject) => {
const fileInput = document.createElement('input')
fileInput.type = 'file'
fileInput.id = id
fileInput.accept = accept
fileInput.addEventListener('change', function () {
if (fileInput.files.length > 0) {
const file = fileInput.files[0]
resolve(file)
} else {
reject('请选择文件')
}
})
fileInput.addEventListener('change', () => {
document.body.removeChild(fileInput)
})
// 关闭窗口没选择文件
window.addEventListener('cancel', () => {
document.body.removeChild(fileInput)
})
document.body.appendChild(fileInput)
fileInput.click()
})
}
\ No newline at end of file
......@@ -41,22 +41,17 @@ const { proxy } = getCurrentInstance()
// 覆盖上传直播间分类表格
const uploadExcel = () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.id = 'fileInput';
fileInput.accept = '.xls, .xlsx'; // 限制只能选择 .xls 和 .xlsx 文件
fileInput.addEventListener('change', async function () {
const file = fileInput.files[0];
if (file) {
proxy.chooseFile({
accept: '.xls, .xlsx'
}).then(async file => {
// 在这里可以添加文件上传的逻辑
const formData = new FormData();
formData.append('file', file);
const res = await uploadLiveCate(formData)
proxy.$modal.msgSuccess(res.data)
}
});
document.body.appendChild(fileInput);
fileInput.click()
}).catch(err => {
proxy.$modal.msgError(err)
})
}
/************ 直播间分类列表 *******************/
......
......@@ -3,7 +3,8 @@
<div class="container">
<el-row :gutter="20"
class="client-fix-height container">
<category-tree :options="floderList"
<category-tree v-model="queryParams.reportType"
:options="floderList"
:defaultProps="{ label: 'title', children: 'children' }"
node-key="id"
placeholder="搜索文件夹名称"
......@@ -14,6 +15,7 @@
}"
@search="handleNodeClick">
</category-tree>
<el-col :span="20"
:xs="24"
class="right_col">
......@@ -31,6 +33,22 @@
</el-form-item>
</el-form>
<el-row :gutter="10"
class="mb8">
<el-col :span="1.5">
<el-button type="primary"
plain
icon="Plus"
@click="handleAdd">新建报表</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info"
plain
icon="Upload"
@click="handleImport">导入报表</el-button>
</el-col>
</el-row>
<el-table v-loading="loading"
:data="reportList">
<el-table-column type="selection"
......@@ -56,14 +74,14 @@
<el-button link
type="primary"
icon="View"
@click="handleUpdate(scope.row)"></el-button>
@click="handlePreview(scope.row)"></el-button>
</xl-tool-tip>
<xl-tool-tip content="修改"
placement="top">
<el-button link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"></el-button>
@click="handleEdit(scope.row)"></el-button>
</xl-tool-tip>
<xl-tool-tip content="删除"
placement="top"
......@@ -79,7 +97,7 @@
<el-button link
type="primary"
icon="CopyDocument"
@click="handleResetPwd(scope.row)"></el-button>
@click="handleCopy(scope.row)"></el-button>
</xl-tool-tip>
</template>
</el-table-column>
......@@ -97,7 +115,13 @@
<script setup>
import { getToken } from '@/utils/auth'
import { getReportFolderStructureAPI, getDesignReportListAPI } from '@/api/jimu'
import { getReportFolderStructureAPI, getDesignReportListAPI, deleteReportAPI, copyReportAPI, addReportAPI, importReportAPI } from '@/api'
import useUserStore from '@/store/modules/user'
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
const reportViewURL = import.meta.env.VITE_APP_REPORT_PREVIEW_URL // 预览
const reportEditURL = import.meta.env.VITE_APP_REPORT_EDIT_URL // 新增/编辑
/************ 文件夹分组 ********************/
const floderList = ref([{ // 文件夹结构
......@@ -131,13 +155,16 @@ const getReportFolderStructure = async (obj) => {
// 只需要在初始化时设置一次展开
if (defaultExpandList.value.length === 0) {
defaultExpandList.value = [floderList.value[0].id]
// 选中最外层文件夹
queryParams.reportType = floderList.value[0].id
}
}
getReportFolderStructure(floderList.value[0])
// 某行点击
const handleNodeClick = (row) => {
queryParams.reportType = row ? row.id : '0' // 文件夹 id 固定是 0(当不选择左侧文件夹默认查询所有)
getReportList()
}
/**************** 报表列表 ***************/
......@@ -145,7 +172,7 @@ const handleNodeClick = (row) => {
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
reportType: floderList.value[0], // 文件夹 ID
reportType: '', // 文件夹 ID
name: '', // 搜索名字字段
token: getToken()
})
......@@ -160,6 +187,78 @@ const getReportList = async () => {
total.value = res.result.total
}
getReportList()
/********* 报表功能按钮 ***********/
// 新建报表
const handleAdd = async () => {
const res = await addReportAPI()
window.open(`${reportEditURL}/${res.result.id}?menuType=${queryParams.reportType}&token=${getToken()}&tenantId=${userStore.$state.userInfo.deptId}`)
}
// 导入报表
const handleImport = () => {
proxy.chooseFile({
accept: '.json'
}).then(async file => {
// 在这里可以添加文件上传的逻辑
const formData = new FormData()
formData.append('file', file)
formData.append('type', queryParams.reportType)
const res = await importReportAPI(formData)
proxy.$modal.msgSuccess(res.result)
getReportList()
}).catch(err => {
proxy.$modal.msgError(err)
})
}
// 预览报表
const handlePreview = (row) => {
window.open(`${reportViewURL}/${row.id}?token=${getToken()}&tenantId=${userStore.$state.userInfo.deptId}`)
}
// 修改报表
const handleEdit = (row) => {
window.open(`${reportEditURL}/${row.id}?token=${getToken()}&tenantId=${userStore.$state.userInfo.deptId}`)
}
// 删除报表
const handleDelete = (row) => {
proxy.$modal.confirm('确定删除该报表吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 调用删除接口
deleteReportAPI({
id: row.id,
token: getToken()
}).then(res => {
proxy.$modal.msgSuccess('删除成功')
getReportList()
}).catch(err => {
proxy.$modal.msgError(err.message)
})
})
}
// 复制报表
const handleCopy = (row) => {
// 确认提示框
proxy.$modal.confirm('确定复制该报表吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
copyReportAPI({
id: row.id,
token: getToken()
}).then(res => {
proxy.$modal.msgSuccess('复制成功')
getReportList()
}).catch(err => {
proxy.$modal.msgError(err.message)
})
})
}
</script>
<style scoped></style>
\ No newline at end of file
<template>
<i-frame :src="openUrl"
id="jimuReportFrame"></i-frame>
</template>
<script setup>
// 报表设计器
import { getToken } from '@/utils/auth'
import iFrame from '@/components/iFrame/index'
// 获取 pinia 里保存的用户部门 id
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const openUrl = ref(`${import.meta.env.VITE_APP_REPORT_URL}/report/jmreport/list?token=` + getToken() + `&tenantId=${userStore.$state.userInfo.deptId}`)
</script>
<style lang="scss"
scoped>
#jimuReportFrame {
width: 100%;
// 为了隐藏积木报表顶部的广告栏
min-height: calc(100vh - 40px) !important;
margin-top: -45px;
}
</style>
\ No newline at end of file
......@@ -43,7 +43,7 @@
</template>
<script setup>
import { enterOtherReportAPI, getReportFolderListAPI } from '@/api'
import { enterOtherReportAPI, getReportFolderStructureAPI } from '@/api'
const { proxy } = getCurrentInstance();
// 表单数据
......@@ -98,7 +98,7 @@ const handleSubmit = async () => {
// 获取积木报表文件夹分组一级
const deptList = ref([])
const getDeptList = async () => {
const res = await getReportFolderListAPI()
const res = await getReportFolderStructureAPI()
deptList.value = res.result
}
getDeptList()
......
......@@ -15,8 +15,8 @@
@input="getReportList" />
</el-form-item>
<el-form-item label="报表类目"
prop="categoryName">
<el-select v-model="queryParams.categoryName"
prop="categoryId">
<el-select v-model="queryParams.categoryId"
placeholder="选择报表类目"
clearable
filterable
......@@ -24,27 +24,23 @@
style="width: 240px">
<el-option v-for="item in categoryList"
:label="item.name"
:value="item.name" />
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="Refresh"
@click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 表格数据 -->
<el-table v-loading="loading"
:data="reportList">
<el-table-column label="报表名称"
key="reportName"
prop="reportName"
key="name"
prop="name"
align="left"
sortable>
<template #default="scope">
<div style="display: flex; align-items: center">
<svg-icon icon-class="bg-document"></svg-icon>
<span style="margin-left: 10px">{{ scope.row.reportName }}</span>
<span style="margin-left: 10px">{{ scope.row.name }}</span>
</div>
</template>
</el-table-column>
......@@ -77,32 +73,29 @@
</el-table-column>
</el-table>
<!-- <pagination v-show="total > 0"
<pagination v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getReportList" /> -->
@pagination="getReportList" />
</div>
</div>
</template>
<script setup
name="Role">
import { getReportListAPI, selReportCategoryAPI } from "@/api"
import { getReportListByUserAPI, selReportCategoryAPI } from "@/api"
import { getToken } from '@/utils/auth'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const reportBaseURL = import.meta.env.VITE_APP_REPORT_URL
const { proxy } = getCurrentInstance()
const reportViewURL = import.meta.env.VITE_APP_REPORT_PREVIEW_URL
/*************** 报表列表部分 ****************/
// 报表搜索
const showSearch = ref(true)
const queryParams = reactive({
name: undefined, // 报表名称
categoryName: undefined, // 报表类目
categoryId: undefined, // 报表类目
pageNum: 1,
pageSize: 10
})
......@@ -114,28 +107,25 @@
})
}
getCategoryList()
// 重置
function resetQuery() {
proxy.resetForm("queryRef")
getReportList()
}
// 获取报表列表
const loading = ref(true)
const reportList = ref([])
const total = ref(0)
// 查询报表列表
function getReportList() {
loading.value = true
getReportListAPI(queryParams).then(response => {
reportList.value = response.data
getReportListByUserAPI(queryParams).then(response => {
reportList.value = response.data.rows
loading.value = false
total.value = response.data.total
})
}
getReportList()
// 点击某行报表
function previewReport(row) {
window.open(`${reportBaseURL}/report/jmreport/view/${row.id}?token=${getToken()}&tenantId=${userStore.$state.userInfo.deptId}`)
window.open(`${reportViewURL}/${row.id}?token=${getToken()}&tenantId=${userStore.$state.userInfo.deptId}`)
}
</script>
......
......@@ -27,10 +27,6 @@
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="Refresh"
@click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10"
......@@ -234,11 +230,6 @@
})
}
getCategoryList()
// 重置
function resetQuery() {
proxy.resetForm("queryRef")
getReportList()
}
// 批量分配报表
const reportMultipleBtnDis = ref(true) // 是否多选报表
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论