123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- <script lang="ts" setup>
- 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'
- 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 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>
|