|
@@ -1,14 +1,431 @@
|
|
|
<script lang="ts" setup>
|
|
|
-import { ref, onMounted, watch } from "vue";
|
|
|
+import { ref, onMounted, reactive, watch, inject } from "vue";
|
|
|
+import { useRouter, useRoute } from 'vue-router'
|
|
|
+import { post, get } from "@/utils/request";
|
|
|
+import { GET_STRUCT_BY_TABLE_NAME, ADD_OR_UPDATE_REPORT_FORM } from "../api"
|
|
|
+import { generateSql, rangeGetSql } from "../function"
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
|
|
-onMounted(() => {})
|
|
|
+import { VueDraggable } from 'vue-draggable-plus'
|
|
|
+
|
|
|
+import rangeFilter from "./rangeFilter.vue";
|
|
|
+import previewTable from "./previewTable.vue";
|
|
|
+import { confirmAction } from "@/utils/tools";
|
|
|
+
|
|
|
+interface collapseItemList {
|
|
|
+ busObject: any,
|
|
|
+ columnList: any[],
|
|
|
+}
|
|
|
+
|
|
|
+interface CollapseItem {
|
|
|
+ data: {
|
|
|
+ label: string;
|
|
|
+ };
|
|
|
+ list: collapseItemList;
|
|
|
+}
|
|
|
+
|
|
|
+const globalPopup = inject<GlobalPopup>('globalPopup')
|
|
|
+const router = useRouter()
|
|
|
+const route = useRoute()
|
|
|
+const reportName = ref('')
|
|
|
+const addEditData = JSON.parse(sessionStorage.getItem('reportJson') || '{}')
|
|
|
+const activeNames = ref([])
|
|
|
+const collapseList = ref<CollapseItem[]>([])
|
|
|
+
|
|
|
+// 拖拽表格参数
|
|
|
+const tableData = ref<any>([]);
|
|
|
+const tableColumns = ref<any>([{}, {}, {}]);
|
|
|
+
|
|
|
+// 全部Loading的Visable
|
|
|
+const allLoading = reactive({
|
|
|
+ collapseListLoading: false,
|
|
|
+ saveLoading: false
|
|
|
+})
|
|
|
+const allVisable = reactive({
|
|
|
+ previewTableVisable: false
|
|
|
+})
|
|
|
+
|
|
|
+// ref
|
|
|
+const rangeFilterRef = ref<InstanceType<typeof rangeFilter> | null>(null);
|
|
|
+const previewTableRef = ref<InstanceType<typeof previewTable> | null>(null);
|
|
|
+
|
|
|
+// 保存预览的判断以及生成
|
|
|
+function preMethod(): any {
|
|
|
+ const rangeFilterData = rangeFilterRef.value?.getRangeData()
|
|
|
+ if(!tableData.value.length) {
|
|
|
+ ElMessage.warning('请选择列')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if(rangeFilterData.length) {
|
|
|
+ for(let i in rangeFilterData) {
|
|
|
+ if(!rangeFilterData[i].twoDisable && !rangeFilterData[i].filterValueTwo) {
|
|
|
+ ElMessage.warning('请选择第二个条件')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const tabeName = tableData.value.map((item: any) => item.tableName)
|
|
|
+ const tableNames = addEditData.mindMapJSON.selectNodes[0].data.tblName
|
|
|
+ if(!tabeName.includes(tableNames)) {
|
|
|
+ ElMessage.warning('请选择主表数据')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const list = rangeFilterData.filter((item: any) => item.filterValueThree)
|
|
|
+ const SQL = generateSql(tableData.value, rangeFilterData, addEditData)
|
|
|
+ const rangeSQL = rangeGetSql(rangeFilterData, addEditData)
|
|
|
+ const finalSql = `${SQL} ${list.length ? rangeSQL : 'where 1=1'}`
|
|
|
+
|
|
|
+ return {
|
|
|
+ finalSql,
|
|
|
+ formTransConditionJson: '',
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 保存页面
|
|
|
+function saveReport() {
|
|
|
+ const rangeFilterData = rangeFilterRef.value?.getRangeData()
|
|
|
+
|
|
|
+ const { finalSql } = preMethod()
|
|
|
+
|
|
|
+ // 需要转存的 JSON
|
|
|
+ const formJson = {
|
|
|
+ addEditData,
|
|
|
+ tableData: tableData.value,
|
|
|
+ rangeFilterData: rangeFilterData,
|
|
|
+ }
|
|
|
+
|
|
|
+ const formFieldHead = tableData.value.map((item: any) => {
|
|
|
+ return {
|
|
|
+ tableName: item.tableName,
|
|
|
+ columnName: item.columnName,
|
|
|
+ columnVal: item.columnComment
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const { reportFormName, description, userAccessList = [], visibleRangeData = [], parentStoreId, privilege, id } = addEditData.addFormVal
|
|
|
+
|
|
|
+ const formVal: any = {
|
|
|
+ reportFormName,
|
|
|
+ description,
|
|
|
+ parentStoreId: parentStoreId ? parentStoreId : 0,
|
|
|
+ privilege: privilege ? 1 : 2,
|
|
|
+ executeSql: finalSql,
|
|
|
+ userIds: visibleRangeData.filter((item: any) => item.isUser).map((item: any) => item.id).join(','),
|
|
|
+ departmentIds: visibleRangeData.filter((item: any) => !item.isUser).map((item: any) => item.id).join(','),
|
|
|
+ formJson: JSON.stringify(formJson),
|
|
|
+ formFieldHead: JSON.stringify(formFieldHead),
|
|
|
+ }
|
|
|
+
|
|
|
+ if(id) {
|
|
|
+ formVal.id = id
|
|
|
+ }
|
|
|
+
|
|
|
+ allLoading.saveLoading = true
|
|
|
+ post(ADD_OR_UPDATE_REPORT_FORM, { ...formVal }).then(_res => {
|
|
|
+ globalPopup?.showSuccess('保存成功')
|
|
|
+ sessionStorage.removeItem('reportJson');
|
|
|
+ sessionStorage.removeItem('editReportJson');
|
|
|
+ router.go(-2)
|
|
|
+ }).finally(() => {
|
|
|
+ allLoading.saveLoading = false
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 预览页面
|
|
|
+function preview() {
|
|
|
+ const rangeFilterData = rangeFilterRef.value?.getRangeData()
|
|
|
+
|
|
|
+ const { finalSql } = preMethod()
|
|
|
+
|
|
|
+ allVisable.previewTableVisable = true
|
|
|
+ setTimeout(() => {
|
|
|
+ previewTableRef.value?.initializedData(finalSql, rangeFilterData, tableData.value)
|
|
|
+ }, 100)
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭页面
|
|
|
+function closePage() {
|
|
|
+ ElMessageBox.confirm('要保存当前修改吗', '', {
|
|
|
+ distinguishCancelAndClose: true,
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ }).then(() => {
|
|
|
+ saveReport()
|
|
|
+ }).catch((action) => {
|
|
|
+ if(action === 'cancel') {
|
|
|
+ sessionStorage.removeItem('reportJson');
|
|
|
+ sessionStorage.removeItem('editReportJson');
|
|
|
+ router.go(-2)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 分组
|
|
|
+function grouping(index: number) {
|
|
|
+ const val = tableData.value[index]?.singleColumnOperation?.grouping
|
|
|
+ if(!tableData.value[index].singleColumnOperation) {
|
|
|
+ tableData.value[index].singleColumnOperation = {}
|
|
|
+ tableData.value[index].singleColumnOperation.grouping = val ? false : true
|
|
|
+ } else {
|
|
|
+ tableData.value[index].singleColumnOperation.grouping = val ? false : true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 统计
|
|
|
+function statistics(index: number) {
|
|
|
+ const val = tableData.value[index]?.singleColumnOperation?.statistics
|
|
|
+ if(!tableData.value[index].singleColumnOperation) {
|
|
|
+ tableData.value[index].singleColumnOperation = {}
|
|
|
+ tableData.value[index].singleColumnOperation.statistics = val ? false : true
|
|
|
+ } else {
|
|
|
+ tableData.value[index].singleColumnOperation.statistics = val ? false : true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 修改名称
|
|
|
+function changeName(index: number) {
|
|
|
+ ElMessageBox.prompt('修改名称', '', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ inputPattern: /^.+$/,
|
|
|
+ inputErrorMessage: '请输入',
|
|
|
+ }).then(({ value }) => {
|
|
|
+ tableData.value[index].columnComment = value
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 删除列
|
|
|
+function deleteColumn(index: number) {
|
|
|
+ tableData.value.splice(index, 1);
|
|
|
+}
|
|
|
+
|
|
|
+// 添加列
|
|
|
+function addTable(row: any) {
|
|
|
+ const { tableName = '', columnName = '', dictCode = '', columnComment = '' } = row.data
|
|
|
+ const index = row.newIndex;
|
|
|
+ console.log(tableData.value, '<====== tableData.value')
|
|
|
+ if (tableData.value.filter((item: any) => `${item.tableName}.${item.columnName}.${item.dictCode ? item.dictCode : ''}` === `${tableName}.${columnName}.${dictCode}`).length >= 2) {
|
|
|
+ ElMessage.warning('该列已存在')
|
|
|
+ tableData.value.splice(index, 1);
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if(tableData.value.filter((item: any) => item.columnComment === columnComment).length >= 2) {
|
|
|
+ ElMessage.warning(`【${columnComment}】列名称已存在、请先修改列名称`)
|
|
|
+ tableData.value.splice(index, 1);
|
|
|
+ return
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function listOnClone(clonedItem: any) {
|
|
|
+ clonedItem.prop = `col_${Date.now()}`; // 赋予唯一 key
|
|
|
+}
|
|
|
+
|
|
|
+async function getCollapseListData() {
|
|
|
+ const { mindMapJSON = {} } = addEditData
|
|
|
+ const selectNodes = mindMapJSON?.selectNodes
|
|
|
+ allLoading.collapseListLoading = true
|
|
|
+ for (let i in selectNodes) {
|
|
|
+ console.log(selectNodes, '<===== selectNodes')
|
|
|
+ const { dictCode = '', tblName = '' } = selectNodes[i].data
|
|
|
+ const res = await post(GET_STRUCT_BY_TABLE_NAME, { tableName: tblName })
|
|
|
+
|
|
|
+ res.data?.columnList.forEach((item: any) => {
|
|
|
+ item.dictCode = dictCode
|
|
|
+ });
|
|
|
+
|
|
|
+ selectNodes[i].list = res.data
|
|
|
+ }
|
|
|
+ collapseList.value = selectNodes
|
|
|
+ allLoading.collapseListLoading = false
|
|
|
+}
|
|
|
+
|
|
|
+function goBack() {
|
|
|
+ router.go(-1)
|
|
|
+}
|
|
|
+
|
|
|
+function displayBackData() {
|
|
|
+ const json = JSON.parse(sessionStorage.getItem('editReportJson') || '{}')
|
|
|
+ const { rangeFilterData = [], tableData: tableDataList = [] } = json
|
|
|
+ if(rangeFilterData.length) {
|
|
|
+ setTimeout(() => {
|
|
|
+ rangeFilterRef.value?.setRangeData(rangeFilterData, false)
|
|
|
+ }, 300)
|
|
|
+ }
|
|
|
+ if(tableDataList.length) {
|
|
|
+ setTimeout(() => {
|
|
|
+ tableData.value = tableDataList
|
|
|
+ }, 300)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ console.log(addEditData)
|
|
|
+ const { reportFormName } = addEditData.addFormVal
|
|
|
+ reportName.value = reportFormName
|
|
|
+ getCollapseListData()
|
|
|
+ displayBackData()
|
|
|
+ setTimeout(() => {
|
|
|
+ rangeFilterRef.value?.setIsItAnEditor()
|
|
|
+ }, 100)
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div>
|
|
|
- 111222333
|
|
|
+ <div class="dragEdit flex flex-row h-full bg-white rounded-md w-full">
|
|
|
+ <div class="w-[15%] h-full overflow-auto scroll-bar p-4" v-loading="allLoading.collapseListLoading">
|
|
|
+ <el-collapse v-model="activeNames">
|
|
|
+ <el-collapse-item :title="item.data.newLabel || item.data.label" :name="index" v-for="(item, index) in (collapseList as any)" :key="index">
|
|
|
+ <VueDraggable v-model="item.list.columnList" :animation="150"
|
|
|
+ :group="{ name: 'people', pull: 'clone', put: false }" :sort="false"
|
|
|
+ class="flex flex-col gap-2 p-4 w-300px bg-gray-500/5 rounded" @clone="listOnClone">
|
|
|
+ <div v-for="listItem in item.list.columnList" class="cursor-all-scroll px-3 py-2 bg-white">
|
|
|
+ {{ listItem.columnComment }}
|
|
|
+ </div>
|
|
|
+ </VueDraggable>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ </div>
|
|
|
+ <div class="w-[85%] h-full">
|
|
|
+ <div class="h-full w-full flex flex-col p-3 box-border">
|
|
|
+ <div class="dragEdit-header">
|
|
|
+ <div class="flex justify-between">
|
|
|
+ <div><el-button @click="goBack()">返回上一级</el-button></div>
|
|
|
+ <div class="text-[20px] font-bold">{{ reportName }}</div>
|
|
|
+ <div>
|
|
|
+ <el-button @click="closePage()">关闭</el-button>
|
|
|
+ <el-button @click="preview()">预览</el-button>
|
|
|
+ <el-button type="primary" @click="saveReport()" :loading="allLoading.saveLoading">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="range mt-3 mb-3">
|
|
|
+ <div class="flex items-center pb-3 font-bold text-[16px]">
|
|
|
+ 数据范围
|
|
|
+ </div>
|
|
|
+ <rangeFilter ref="rangeFilterRef" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 中间 -->
|
|
|
+ <div class="flex justify-between items-center mb-3">
|
|
|
+ <div class="flex items-center">
|
|
|
+ <div class="font-bold text-[16px]">报表示例</div>
|
|
|
+ </div>
|
|
|
+ <div>点击顶部预览按钮可查看全部数据</div>
|
|
|
+ </div>
|
|
|
+ <!-- 表格 -->
|
|
|
+ <div class="flex-1 relative border-2">
|
|
|
+ <div class="table-container scroll-bar">
|
|
|
+ <VueDraggable v-model="tableData" group="people" target=".sort-target" @add="addTable">
|
|
|
+ <div class="absolute top-0 left-0 text-center text-[#999] w-full text-[20px] p-3" v-if="!tableData.length">
|
|
|
+ <div class="border-2 border-dashed py-2">这里是表格头部区域,请将左侧字段拖入此区域生成报表</div>
|
|
|
+ </div>
|
|
|
+ <table class="table table-striped">
|
|
|
+ <thead class="thead-dark">
|
|
|
+ <tr class="sort-target">
|
|
|
+ <th class="cursor-move" v-for="(header, headerIndex) in tableData" :key="header.value">
|
|
|
+ <div class="w-full flex justify-between items-center">
|
|
|
+ <div>{{ header.columnComment }}</div>
|
|
|
+ <el-dropdown>
|
|
|
+ <span class="el-dropdown-link">
|
|
|
+ <el-icon color="#075985">
|
|
|
+ <InfoFilled />
|
|
|
+ </el-icon>
|
|
|
+ </span>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item v-if="['varchar', 'text'].includes(header.dataType)">
|
|
|
+ <el-text :underline="false" type="primary" @click="grouping(headerIndex)">
|
|
|
+ {{ header?.singleColumnOperation?.grouping ? '取消分组' : '分组' }}
|
|
|
+ </el-text>
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item v-if="['decimal'].includes(header.dataType)">
|
|
|
+ <el-text :underline="false" type="primary" @click="statistics(headerIndex)">
|
|
|
+ {{ header?.singleColumnOperation?.statistics ? '取消统计' : '统计' }}
|
|
|
+ </el-text>
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item>
|
|
|
+ <el-text :underline="false" type="primary" @click="changeName(headerIndex)">
|
|
|
+ 修改名称
|
|
|
+ </el-text>
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item>
|
|
|
+ <el-text :underline="false" type="danger"
|
|
|
+ @click="deleteColumn(headerIndex)">删除列</el-text>
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
+ </th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="item in tableColumns" :key="item.name">
|
|
|
+ <td v-for="header in tableData" :key="header.value">
|
|
|
+ 示例数据
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </VueDraggable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 全屏预览 -->
|
|
|
+ <el-dialog v-model="allVisable.previewTableVisable" fullscreen top="40vh" width="70%" draggable>
|
|
|
+ <previewTable ref="previewTableRef"></previewTable>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+.dragEdit {
|
|
|
+ .table {
|
|
|
+ display: table;
|
|
|
+ width: 100%;
|
|
|
+ font-size: 15px;
|
|
|
+ border-collapse: collapse;
|
|
|
+ margin: 0 0;
|
|
|
+ overflow-x: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ tr {
|
|
|
+ background-color: #fff;
|
|
|
+ border-top: 1px solid #e2e2e3;
|
|
|
+ transition: background-color .5s;
|
|
|
+ }
|
|
|
+
|
|
|
+ th {
|
|
|
+ text-align: left;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: rgba(60, 60, 67, .78);
|
|
|
+ background-color: #f6f6f7;
|
|
|
+ border: 1px solid #e2e2e3;
|
|
|
+ padding: 8px 16px;
|
|
|
+ min-width: 240px;
|
|
|
+ }
|
|
|
+
|
|
|
+ td {
|
|
|
+ padding: 8px 16px;
|
|
|
+ font-size: 14px;
|
|
|
+ min-width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cursor-move {
|
|
|
+ cursor: move;
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-container {
|
|
|
+ overflow-x: auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|