index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <div class="biz-display-container">
  3. <div v-if="headerShow" class="biz-display-header">
  4. <slot name="header">
  5. {{ title }}
  6. </slot>
  7. </div>
  8. <div class="biz-display-body" :style="{ height: bodyHeight }">
  9. <template v-if="displayType === 'chart'">
  10. <gw-chart :option="data"></gw-chart>
  11. </template>
  12. <template v-if="displayType === 'list'">
  13. <biz-table
  14. :table-data="tableData"
  15. :columns="columns"
  16. :click-config="clickConfig"
  17. :pagination-config="paginationConfig"
  18. :current-page="currentPage"
  19. :total="total"
  20. @row-click="handleRowClick"
  21. @page-change="handlePageChange"
  22. />
  23. </template>
  24. <template v-if="displayType === 'table'">
  25. <gw-table-two :columnNumber="columnNumber" :data="data"></gw-table-two>
  26. </template>
  27. <template v-if="displayType === 'text'">
  28. <span v-html="data"></span>
  29. </template>
  30. </div>
  31. </div>
  32. </template>
  33. <script setup>
  34. import GwChart from "@/components/chart/GwEchart.vue";
  35. import {getChartOption, getFormListData, getText} from '@/utils/biz'
  36. import GwTableTwo from "./GwTableTwo.vue";
  37. import BizTable from "./BizTable.vue"; // 引入新组件
  38. import bus from '@/utils/bus'
  39. import {filterData} from "@/utils/data.js";
  40. import {isDate, isNumber} from "@/utils/validate.js";
  41. import {parseTime} from "@/utils/ruoyi.js";
  42. import {getBizDataById} from "@/api/standardization/bizDataShowConfig.js";
  43. import {getDataSetBizDataById} from "@/api/register/regCom.js";
  44. defineExpose({
  45. loadData
  46. })
  47. const slots = useSlots();
  48. const props = defineProps({
  49. config: {
  50. type: Object,
  51. },
  52. showTitle: {
  53. type: Boolean,
  54. default: false
  55. },
  56. source: {
  57. type: String,
  58. default: "BIZ_DATA_SHOW"
  59. },
  60. });
  61. const headerShow = computed(() => (!!title.value || slots.header) && props.showTitle)
  62. const bodyHeight = computed(() => {
  63. if (headerShow.value) {
  64. return 'calc(100% - 1rem - 20px)'
  65. } else {
  66. return '100%'
  67. }
  68. })
  69. const data = ref(null)
  70. const title = ref(null)
  71. const displayType = ref(null)
  72. const columnNumber = ref(1)
  73. const columns = ref([])
  74. const clickConfig = ref(null)
  75. const paginationConfig = ref({})
  76. // 分页相关数据
  77. const tableData = ref([])
  78. const currentPage = ref(1)
  79. const total = ref(0)
  80. function loadData(config) {
  81. if (!config) {
  82. return
  83. }
  84. config.value = config
  85. displayType.value = config.type || 'list'
  86. title.value = config.name
  87. // 解析渲染配置
  88. const renderingOptions = JSON.parse(config.renderingOptions || '{}')
  89. clickConfig.value = renderingOptions.clickAction || {type: 'none'}
  90. paginationConfig.value = renderingOptions.pagination || {enabled: false}
  91. switch (displayType.value) {
  92. case 'chart':
  93. nextTick(() => {
  94. getChartOption(config).then(r => data.value = r);
  95. })
  96. return;
  97. case 'list':
  98. columns.value = renderingOptions.columns
  99. // 加载表格数据(支持分页)
  100. loadTableData(config, 1)
  101. return;
  102. case 'table':
  103. columnNumber.value = renderingOptions.columnNumber
  104. return getFormListData(config).then(r => data.value = r);
  105. case 'text':
  106. return getText(config).then(r => data.value = r);
  107. }
  108. }
  109. // 加载表格数据(支持分页)
  110. async function loadTableData(config, page = 1) {
  111. try {
  112. const params = {}
  113. if (paginationConfig.value.enabled) {
  114. params[paginationConfig.value.pageNumParam || 'pageNum'] = page
  115. params[paginationConfig.value.pageSizeParam || 'pageSize'] = paginationConfig.value.pageSize || 10
  116. }
  117. let res;
  118. switch (props.source) {
  119. case "DATA_SET":
  120. res = await getDataSetBizDataById(config.id, params)
  121. break
  122. case "BIZ_DATA_SHOW":
  123. default:
  124. res = await getBizDataById(config.id, params)
  125. break
  126. }
  127. if (res && res.data) {
  128. if (paginationConfig.value.enabled) {
  129. // 提取数据列表
  130. const dataList = getDataByPath(res.data, paginationConfig.value.dataListField)
  131. // 提取总数
  132. total.value = getDataByPath(res.data, paginationConfig.value.totalField || 'total') || 0
  133. tableData.value = Array.isArray(dataList) ? dataList : []
  134. currentPage.value = page
  135. } else {
  136. // 不分页情况
  137. tableData.value = Array.isArray(res.data) ? res.data : []
  138. total.value = tableData.value.length
  139. }
  140. }
  141. } catch (error) {
  142. console.error('加载数据失败:', error)
  143. tableData.value = []
  144. total.value = 0
  145. }
  146. }
  147. // 根据路径获取数据
  148. function getDataByPath(obj, path) {
  149. if (!obj || !path) return obj
  150. const paths = path.split('.')
  151. let result = obj
  152. for (const p of paths) {
  153. result = result[p]
  154. if (result === undefined) break
  155. }
  156. return result
  157. }
  158. // 处理行点击事件
  159. function handleRowClick({row, column, event}) {
  160. if (!clickConfig.value || clickConfig.value.type === 'none') {
  161. return
  162. }
  163. if (clickConfig.value.type === 'map') {
  164. const latitude = row[clickConfig.value.latitudeField]
  165. const longitude = row[clickConfig.value.longitudeField]
  166. if (latitude && longitude) {
  167. // 跳转到地图页面,传递经纬度参数
  168. bus.emit('show-map-position', {latitude, longitude, highlight: true})
  169. }
  170. } else if (clickConfig.value.type === 'detail') {
  171. // 查看详情逻辑
  172. if (clickConfig.value.detailPath) {
  173. // 可以在这里实现跳转到详情页的逻辑
  174. console.log('跳转到详情页:', clickConfig.value.detailPath, row)
  175. }
  176. } else if (clickConfig.value.type === 'link') {
  177. // 跳转链接逻辑
  178. if (clickConfig.value.linkUrl) {
  179. window.open(clickConfig.value.linkUrl, clickConfig.value.linkTarget || '_blank')
  180. }
  181. }
  182. }
  183. // 处理分页变化
  184. function handlePageChange(page) {
  185. // if (props.config && props.config.type === 'list') {
  186. if (displayType.value === 'list') {
  187. loadTableData(props.config, page)
  188. }
  189. }
  190. function getValueByKey(obj, key) {
  191. let value = filterData(obj, key);
  192. if (isNumber(value)) {
  193. value = value.toFixed(2)
  194. } else if (isDate(value)) {
  195. value = parseTime(new Date(value), '{m}-{d} {h}')
  196. }
  197. return value
  198. }
  199. watch(() => props.config, (config) => {
  200. if (config) {
  201. loadData(config)
  202. }
  203. }, {immediate: true})
  204. </script>
  205. <style lang="scss" scoped>
  206. .biz-display-container {
  207. width: 100%;
  208. height: 100%;
  209. .biz-display-header {
  210. display: flex;
  211. justify-content: center;
  212. align-items: center;
  213. font-size: 1rem;
  214. text-align: center;
  215. padding: 10px 0;
  216. }
  217. .biz-display-body {
  218. height: calc(100% - 1rem - 20px);
  219. }
  220. }
  221. </style>