Showing
19 changed files
with
831 additions
and
230 deletions
| 1 | import { | 1 | import { |
| 2 | AddDataBoardParams, | 2 | AddDataBoardParams, |
| 3 | + AddDataComponentParams, | ||
| 3 | DataBoardList, | 4 | DataBoardList, |
| 5 | + DataComponentRecord, | ||
| 4 | GetDataBoardParams, | 6 | GetDataBoardParams, |
| 7 | + Layout, | ||
| 8 | + UpdateDataBoardLayoutParams, | ||
| 5 | UpdateDataBoardParams, | 9 | UpdateDataBoardParams, |
| 6 | } from './model'; | 10 | } from './model'; |
| 7 | import { defHttp } from '/@/utils/http/axios'; | 11 | import { defHttp } from '/@/utils/http/axios'; |
| @@ -11,6 +15,13 @@ enum DataBoardUrl { | @@ -11,6 +15,13 @@ enum DataBoardUrl { | ||
| 11 | ADD_DATA_BOARD = '/data_board/add', | 15 | ADD_DATA_BOARD = '/data_board/add', |
| 12 | DELETE_DATA_BOARD = '/data_board', | 16 | DELETE_DATA_BOARD = '/data_board', |
| 13 | UPDATE_DATA_BOARD = '/data_board/update', | 17 | UPDATE_DATA_BOARD = '/data_board/update', |
| 18 | + UPDATE_DATA_BOARD_LAYOUT = '/data_board', | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +enum DataComponentUrl { | ||
| 22 | + GET_DATA_COMPONENT = '/data_component', | ||
| 23 | + ADD_DATA_COMPONENT = '/data_component', | ||
| 24 | + DELETE_DATA_COMPONENT = '/data_component', | ||
| 14 | } | 25 | } |
| 15 | 26 | ||
| 16 | /** | 27 | /** |
| @@ -60,3 +71,50 @@ export const deleteDataBoard = (params: string[]) => { | @@ -60,3 +71,50 @@ export const deleteDataBoard = (params: string[]) => { | ||
| 60 | params: { ids: params }, | 71 | params: { ids: params }, |
| 61 | }); | 72 | }); |
| 62 | }; | 73 | }; |
| 74 | + | ||
| 75 | +/** | ||
| 76 | + * @description 更新数据组件位置 | ||
| 77 | + * @param params | ||
| 78 | + * @returns | ||
| 79 | + */ | ||
| 80 | +export const updateDataBoardLayout = (params: UpdateDataBoardLayoutParams) => { | ||
| 81 | + return defHttp.post({ | ||
| 82 | + url: `${DataBoardUrl.UPDATE_DATA_BOARD_LAYOUT}/${params.boardId}/layout/`, | ||
| 83 | + params: params.layout, | ||
| 84 | + }); | ||
| 85 | +}; | ||
| 86 | + | ||
| 87 | +/** | ||
| 88 | + * @description 获取数据组件 | ||
| 89 | + * @param params | ||
| 90 | + * @returns | ||
| 91 | + */ | ||
| 92 | +export const getDataComponent = (params: string) => { | ||
| 93 | + return defHttp.get<{ data: { componentData: DataComponentRecord[]; componentLayout: Layout[] } }>( | ||
| 94 | + { | ||
| 95 | + url: `${DataComponentUrl.GET_DATA_COMPONENT}/${params}`, | ||
| 96 | + // params: { boardId: params }, | ||
| 97 | + } | ||
| 98 | + ); | ||
| 99 | +}; | ||
| 100 | + | ||
| 101 | +export const addDataComponent = (params: AddDataComponentParams) => { | ||
| 102 | + return defHttp.post<{ data: DataComponentRecord }>({ | ||
| 103 | + url: `${DataComponentUrl.ADD_DATA_COMPONENT}/${params.boardId}/add`, | ||
| 104 | + params: params.record, | ||
| 105 | + }); | ||
| 106 | +}; | ||
| 107 | + | ||
| 108 | +/** | ||
| 109 | + * @description 删除数据组件 | ||
| 110 | + * @param params | ||
| 111 | + * @returns | ||
| 112 | + */ | ||
| 113 | +export const deleteDataComponent = (params: string[]) => { | ||
| 114 | + return defHttp.delete({ | ||
| 115 | + url: DataComponentUrl.DELETE_DATA_COMPONENT, | ||
| 116 | + params: { | ||
| 117 | + ids: params, | ||
| 118 | + }, | ||
| 119 | + }); | ||
| 120 | +}; |
| @@ -23,14 +23,6 @@ export interface Layout { | @@ -23,14 +23,6 @@ export interface Layout { | ||
| 23 | y: number; | 23 | y: number; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | -export interface Layout { | ||
| 27 | - h: number; | ||
| 28 | - id: string; | ||
| 29 | - w: number; | ||
| 30 | - x: number; | ||
| 31 | - y: number; | ||
| 32 | -} | ||
| 33 | - | ||
| 34 | export interface DataBoardRecord { | 26 | export interface DataBoardRecord { |
| 35 | name: string; | 27 | name: string; |
| 36 | roleIds: string[]; | 28 | roleIds: string[]; |
| @@ -57,3 +49,65 @@ export interface DataBoardList { | @@ -57,3 +49,65 @@ export interface DataBoardList { | ||
| 57 | items: DataBoardRecord[]; | 49 | items: DataBoardRecord[]; |
| 58 | total: number; | 50 | total: number; |
| 59 | } | 51 | } |
| 52 | + | ||
| 53 | +export interface GradientInfo { | ||
| 54 | + value: number; | ||
| 55 | + key: string; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +export interface ComponentInfo { | ||
| 59 | + fontColor: string; | ||
| 60 | + unit: string; | ||
| 61 | + gradientInfo: GradientInfo[]; | ||
| 62 | + iconColor: string; | ||
| 63 | + icon: string; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +export interface DataSource { | ||
| 67 | + attribute: string; | ||
| 68 | + deviceId: string; | ||
| 69 | + organizationId: string; | ||
| 70 | + attributeRename: string; | ||
| 71 | + deviceRename: string; | ||
| 72 | + componentInfo: ComponentInfo; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +export interface DataComponentRecord { | ||
| 76 | + dataBoardId: string; | ||
| 77 | + dataSource: DataSource[]; | ||
| 78 | + frontId: string; | ||
| 79 | + name: string; | ||
| 80 | + tenantId: string; | ||
| 81 | + tenantStatus: string; | ||
| 82 | + description: string; | ||
| 83 | + roleIds: string[]; | ||
| 84 | + updater: string; | ||
| 85 | + enabled: boolean; | ||
| 86 | + id: string; | ||
| 87 | + defaultConfig: string; | ||
| 88 | + tenantProfileId: string; | ||
| 89 | + remark: string; | ||
| 90 | + icon: string; | ||
| 91 | + tenantExpireTime: string; | ||
| 92 | + updateTime: string; | ||
| 93 | + creator: string; | ||
| 94 | + createTime: string; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +export interface AddDataComponentParams { | ||
| 98 | + boardId: string; | ||
| 99 | + record: Partial<DataComponentRecord>; | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | +export interface Layout { | ||
| 103 | + id: string; | ||
| 104 | + w: number; | ||
| 105 | + h: number; | ||
| 106 | + x: number; | ||
| 107 | + y: number; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +export interface UpdateDataBoardLayoutParams { | ||
| 111 | + boardId: string; | ||
| 112 | + layout: Layout[]; | ||
| 113 | +} |
src/utils/to.ts
0 → 100644
| 1 | +/** | ||
| 2 | + * @description use to function capture await throw error | ||
| 3 | + * @param promise | ||
| 4 | + * @param errorExt - Additional Information you can pass to the err object | ||
| 5 | + */ | ||
| 6 | +export function to<DATA = any, ERROR = any>( | ||
| 7 | + promise: any, | ||
| 8 | + errorExt?: string | ||
| 9 | +): Promise<[ERROR, DATA]> { | ||
| 10 | + return promise | ||
| 11 | + .then((data) => [null, data]) | ||
| 12 | + .catch((err) => { | ||
| 13 | + if (errorExt) { | ||
| 14 | + const parsedError = Object.assign({}, err, errorExt); | ||
| 15 | + return [parsedError, undefined]; | ||
| 16 | + } | ||
| 17 | + return [err, undefined]; | ||
| 18 | + }); | ||
| 19 | +} |
| 1 | +<script lang="ts" setup> | ||
| 2 | + import type { ECharts, EChartsOption } from 'echarts'; | ||
| 3 | + import type { PropType } from 'vue'; | ||
| 4 | + import { nextTick, onMounted, onUnmounted, ref, unref } from 'vue'; | ||
| 5 | + import { init } from 'echarts'; | ||
| 6 | + | ||
| 7 | + interface DataSource { | ||
| 8 | + id: string | number; | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + const props = defineProps({ | ||
| 12 | + dataSource: { | ||
| 13 | + type: Object as PropType<DataSource>, | ||
| 14 | + required: true, | ||
| 15 | + }, | ||
| 16 | + chartOption: { | ||
| 17 | + type: Object as PropType<EChartsOption>, | ||
| 18 | + // required: true, | ||
| 19 | + }, | ||
| 20 | + add: { | ||
| 21 | + type: Function, | ||
| 22 | + required: true, | ||
| 23 | + }, | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + const getControlsWidgetId = () => `widget-chart-${props.dataSource.id}`; | ||
| 27 | + | ||
| 28 | + const chartRef = ref<Nullable<ECharts>>(null); | ||
| 29 | + | ||
| 30 | + function initChart() { | ||
| 31 | + const chartDom = document.getElementById(getControlsWidgetId())!; | ||
| 32 | + chartRef.value = init(chartDom); | ||
| 33 | + const option: EChartsOption = props.chartOption || { | ||
| 34 | + tooltip: { | ||
| 35 | + trigger: 'item', | ||
| 36 | + // confine: true, | ||
| 37 | + extraCssText: 'position: fixed;', | ||
| 38 | + position: (point, params, dom) => { | ||
| 39 | + const parentEl = (dom as HTMLDivElement).parentElement!; | ||
| 40 | + | ||
| 41 | + const { top = 0, left = 0 } = parentEl.getBoundingClientRect()!; | ||
| 42 | + return [left, top]; | ||
| 43 | + }, | ||
| 44 | + }, | ||
| 45 | + series: [ | ||
| 46 | + { | ||
| 47 | + name: 'Access From', | ||
| 48 | + type: 'pie', | ||
| 49 | + radius: '50%', | ||
| 50 | + data: [ | ||
| 51 | + { value: 1048, name: 'Search Engine' }, | ||
| 52 | + { value: 735, name: 'Direct' }, | ||
| 53 | + { value: 580, name: 'Email' }, | ||
| 54 | + { value: 484, name: 'Union Ads' }, | ||
| 55 | + { value: 300, name: 'Video Ads' }, | ||
| 56 | + ], | ||
| 57 | + emphasis: { | ||
| 58 | + itemStyle: { | ||
| 59 | + shadowBlur: 10, | ||
| 60 | + shadowOffsetX: 0, | ||
| 61 | + shadowColor: 'rgba(0, 0, 0, 0.5)', | ||
| 62 | + }, | ||
| 63 | + }, | ||
| 64 | + }, | ||
| 65 | + ], | ||
| 66 | + }; | ||
| 67 | + | ||
| 68 | + nextTick(() => { | ||
| 69 | + option && unref(chartRef)?.setOption(option); | ||
| 70 | + }); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + function update() { | ||
| 74 | + unref(chartRef)?.resize(); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + onMounted(() => { | ||
| 78 | + initChart(); | ||
| 79 | + props.add(props.dataSource.id, update); | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + onUnmounted(() => { | ||
| 83 | + unref(chartRef)?.clear(); | ||
| 84 | + }); | ||
| 85 | + | ||
| 86 | + defineExpose({ update }); | ||
| 87 | +</script> | ||
| 88 | + | ||
| 89 | +<template> | ||
| 90 | + <div class="flex flex-col w-full h-full min-w-3 min-h-3"> | ||
| 91 | + <div :id="getControlsWidgetId()" class="widget-charts w-full h-full"></div> | ||
| 92 | + <div class="text-xs text-center text-gray-400">更新时间:</div> | ||
| 93 | + </div> | ||
| 94 | +</template> | ||
| 95 | + | ||
| 96 | +<style scoped> | ||
| 97 | + .widget-charts > div { | ||
| 98 | + width: 100%; | ||
| 99 | + height: 100%; | ||
| 100 | + } | ||
| 101 | +</style> |
| @@ -44,16 +44,21 @@ | @@ -44,16 +44,21 @@ | ||
| 44 | <div class="w-1/2"> | 44 | <div class="w-1/2"> |
| 45 | <div> | 45 | <div> |
| 46 | <div v-if="getShowIcon"> | 46 | <div v-if="getShowIcon"> |
| 47 | - <SvgIcon name="CO2" prefix="iconfont" class="!w-1/2 !h-[2em]" /> | 47 | + <SvgIcon |
| 48 | + :name="props.value.icon || 'CO2'" | ||
| 49 | + prefix="iconfont" | ||
| 50 | + class="!w-1/2 !h-[2em]" | ||
| 51 | + :style="{ color: props.value.iconColor }" | ||
| 52 | + /> | ||
| 48 | </div> | 53 | </div> |
| 49 | <div>{{ props.value.name }}</div> | 54 | <div>{{ props.value.name }}</div> |
| 50 | </div> | 55 | </div> |
| 51 | </div> | 56 | </div> |
| 52 | <div class="w-1/2 flex justify-center"> | 57 | <div class="w-1/2 flex justify-center"> |
| 53 | <Statistic | 58 | <Statistic |
| 54 | - value="123" | 59 | + :value="props.value.value || '123'" |
| 55 | :suffix="getShowUnit ? props.value.unit : ''" | 60 | :suffix="getShowUnit ? props.value.unit : ''" |
| 56 | - :value-style="{ fontSize: '1.3em' }" | 61 | + :value-style="{ fontSize: '1.3em', color: props.value.fontColor }" |
| 57 | /> | 62 | /> |
| 58 | </div> | 63 | </div> |
| 59 | </div> | 64 | </div> |
| 1 | import { formatToDateTime } from '/@/utils/dateUtil'; | 1 | import { formatToDateTime } from '/@/utils/dateUtil'; |
| 2 | - | 2 | +import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; |
| 3 | export interface TextComponentLayout { | 3 | export interface TextComponentLayout { |
| 4 | id: string; | 4 | id: string; |
| 5 | base?: boolean; | 5 | base?: boolean; |
| @@ -20,37 +20,73 @@ export interface TextComponentValue { | @@ -20,37 +20,73 @@ export interface TextComponentValue { | ||
| 20 | 20 | ||
| 21 | type TextComponentDefault = TextComponentLayout & { value: TextComponentValue }; | 21 | type TextComponentDefault = TextComponentLayout & { value: TextComponentValue }; |
| 22 | 22 | ||
| 23 | -export const textComponentConfig: TextComponentDefault[] = [ | ||
| 24 | - { id: 'text-component-1', base: true, value: { value: 123, name: '温度' } }, | ||
| 25 | - { id: 'text-component-2', base: false, value: { value: 123, name: '温度' } }, | ||
| 26 | - { | ||
| 27 | - id: 'text-component-3', | ||
| 28 | - base: false, | ||
| 29 | - showUpdate: true, | ||
| 30 | - value: { | ||
| 31 | - value: 123, | ||
| 32 | - name: '温度', | ||
| 33 | - updateTime: formatToDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss'), | ||
| 34 | - }, | ||
| 35 | - }, | ||
| 36 | - { | ||
| 37 | - id: 'text-component-4', | ||
| 38 | - base: false, | ||
| 39 | - showIcon: true, | ||
| 40 | - showUpdate: true, | ||
| 41 | - showUnit: true, | ||
| 42 | - value: { | ||
| 43 | - value: 123, | ||
| 44 | - name: '温度', | ||
| 45 | - updateTime: formatToDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss'), | ||
| 46 | - unit: '℃', | ||
| 47 | - }, | 23 | +export const TextComponent1Config: TextComponentDefault = { |
| 24 | + id: 'text-component-1', | ||
| 25 | + base: true, | ||
| 26 | + value: { value: 123, name: '温度' }, | ||
| 27 | +}; | ||
| 28 | + | ||
| 29 | +export const TextComponent2Config: TextComponentDefault = { | ||
| 30 | + id: 'text-component-2', | ||
| 31 | + base: false, | ||
| 32 | + value: { value: 123, name: '温度' }, | ||
| 33 | +}; | ||
| 34 | +export const TextComponent3Config: TextComponentDefault = { | ||
| 35 | + id: 'text-component-3', | ||
| 36 | + base: false, | ||
| 37 | + showUpdate: true, | ||
| 38 | + value: { | ||
| 39 | + value: 123, | ||
| 40 | + name: '温度', | ||
| 41 | + updateTime: formatToDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss'), | ||
| 48 | }, | 42 | }, |
| 49 | - { | ||
| 50 | - id: 'text-component-5', | ||
| 51 | - base: false, | ||
| 52 | - showIcon: true, | ||
| 53 | - showUnit: true, | ||
| 54 | - value: { value: 123, name: '温度', unit: '℃' }, | 43 | +}; |
| 44 | +export const TextComponent4Config: TextComponentDefault = { | ||
| 45 | + id: 'text-component-4', | ||
| 46 | + base: false, | ||
| 47 | + showIcon: true, | ||
| 48 | + showUpdate: true, | ||
| 49 | + showUnit: true, | ||
| 50 | + value: { | ||
| 51 | + value: 123, | ||
| 52 | + name: '温度', | ||
| 53 | + updateTime: formatToDateTime(new Date(), 'YYYY-MM-DD HH:mm:ss'), | ||
| 54 | + unit: '℃', | ||
| 55 | }, | 55 | }, |
| 56 | +}; | ||
| 57 | +export const TextComponent5Config: TextComponentDefault = { | ||
| 58 | + id: 'text-component-5', | ||
| 59 | + base: false, | ||
| 60 | + showIcon: true, | ||
| 61 | + showUnit: true, | ||
| 62 | + value: { value: 123, name: '温度', unit: '℃' }, | ||
| 63 | +}; | ||
| 64 | + | ||
| 65 | +export const textComponentConfig: TextComponentDefault[] = [ | ||
| 66 | + TextComponent1Config, | ||
| 67 | + TextComponent2Config, | ||
| 68 | + TextComponent3Config, | ||
| 69 | + TextComponent4Config, | ||
| 70 | + TextComponent5Config, | ||
| 56 | ]; | 71 | ]; |
| 72 | + | ||
| 73 | +export const transformTextComponentConfig = ( | ||
| 74 | + config: TextComponentDefault, | ||
| 75 | + record: DataComponentRecord, | ||
| 76 | + dataSourceRecord: DataSource | ||
| 77 | +) => { | ||
| 78 | + return { | ||
| 79 | + layout: { | ||
| 80 | + ...config, | ||
| 81 | + } as TextComponentLayout, | ||
| 82 | + value: { | ||
| 83 | + name: dataSourceRecord.attributeRename || dataSourceRecord.attribute, | ||
| 84 | + // value: record.va | ||
| 85 | + icon: dataSourceRecord.componentInfo.icon, | ||
| 86 | + unit: dataSourceRecord.componentInfo.unit, | ||
| 87 | + updateTime: record.updateTime || record.createTime, | ||
| 88 | + fontColor: dataSourceRecord.componentInfo.fontColor, | ||
| 89 | + iconColor: dataSourceRecord.componentInfo.iconColor, | ||
| 90 | + } as TextComponentValue, | ||
| 91 | + }; | ||
| 92 | +}; |
| @@ -7,6 +7,9 @@ | @@ -7,6 +7,9 @@ | ||
| 7 | import { MoreActionEvent } from '../../config/config'; | 7 | import { MoreActionEvent } from '../../config/config'; |
| 8 | 8 | ||
| 9 | const emit = defineEmits(['action']); | 9 | const emit = defineEmits(['action']); |
| 10 | + const props = defineProps<{ | ||
| 11 | + id: string; | ||
| 12 | + }>(); | ||
| 10 | 13 | ||
| 11 | const dropMenuList: DropMenu[] = [ | 14 | const dropMenuList: DropMenu[] = [ |
| 12 | { | 15 | { |
| @@ -27,14 +30,14 @@ | @@ -27,14 +30,14 @@ | ||
| 27 | ]; | 30 | ]; |
| 28 | 31 | ||
| 29 | const handleMenuEvent = (event: DropMenu) => { | 32 | const handleMenuEvent = (event: DropMenu) => { |
| 30 | - emit('action', event); | 33 | + emit('action', event, props.id); |
| 31 | }; | 34 | }; |
| 32 | </script> | 35 | </script> |
| 33 | 36 | ||
| 34 | <template> | 37 | <template> |
| 35 | <div class="flex justify-between"> | 38 | <div class="flex justify-between"> |
| 36 | <div class="flex flex-auto"> | 39 | <div class="flex flex-auto"> |
| 37 | - <div v-for="item in 3" class="flex mx-2" :key="item"> | 40 | + <div v-for="item in 1" class="flex mx-2" :key="item"> |
| 38 | <div class="flex items-center"> | 41 | <div class="flex items-center"> |
| 39 | <Tooltip> | 42 | <Tooltip> |
| 40 | <SvgIcon name="online" prefix="iconfont" class="!fill-emerald-400" /> | 43 | <SvgIcon name="online" prefix="iconfont" class="!fill-emerald-400" /> |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { onMounted } from 'vue'; | 2 | + import { onMounted, useSlots } from 'vue'; |
| 3 | import { useUpdateCenter } from '../../hook/useUpdateCenter'; | 3 | import { useUpdateCenter } from '../../hook/useUpdateCenter'; |
| 4 | import type { DataSource, WidgetWrapperRegister } from './type'; | 4 | import type { DataSource, WidgetWrapperRegister } from './type'; |
| 5 | 5 | ||
| @@ -8,6 +8,10 @@ | @@ -8,6 +8,10 @@ | ||
| 8 | register?: WidgetWrapperRegister; | 8 | register?: WidgetWrapperRegister; |
| 9 | }>(); | 9 | }>(); |
| 10 | 10 | ||
| 11 | + const slot = useSlots(); | ||
| 12 | + | ||
| 13 | + console.log({ dataSource: props.dataSource }); | ||
| 14 | + | ||
| 11 | const { update, add, remove } = useUpdateCenter(); | 15 | const { update, add, remove } = useUpdateCenter(); |
| 12 | 16 | ||
| 13 | onMounted(() => { | 17 | onMounted(() => { |
| @@ -29,7 +33,10 @@ | @@ -29,7 +33,10 @@ | ||
| 29 | class="widget-item" | 33 | class="widget-item" |
| 30 | > | 34 | > |
| 31 | <div class="widget-box"> | 35 | <div class="widget-box"> |
| 32 | - <div class="widget-controls-container"> | 36 | + <div |
| 37 | + class="widget-controls-container" | ||
| 38 | + :style="{ height: slot.footer ? 'calc(100% - 20px)' : '100%' }" | ||
| 39 | + > | ||
| 33 | <slot | 40 | <slot |
| 34 | name="controls" | 41 | name="controls" |
| 35 | :record="item" | 42 | :record="item" |
| 1 | +import { DataComponentRecord } from '/@/api/dataBoard/model'; | ||
| 2 | + | ||
| 1 | export interface DataSource { | 3 | export interface DataSource { |
| 2 | - id: number | string | ||
| 3 | - width: number | ||
| 4 | - height: number | 4 | + id: number | string; |
| 5 | + width: number; | ||
| 6 | + height: number; | ||
| 5 | 7 | ||
| 6 | - [key: string]: any | 8 | + [key: string]: any; |
| 7 | } | 9 | } |
| 8 | 10 | ||
| 9 | -export type WidgetWrapperRegister = (dataSource: DataSource[]) => any | 11 | +export type WidgetWrapperRegister = (dataSource: DataComponentRecord[]) => any; |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | import { CopyOutlined, DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue'; | 2 | import { CopyOutlined, DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue'; |
| 3 | import { Tooltip, Button } from 'ant-design-vue'; | 3 | import { Tooltip, Button } from 'ant-design-vue'; |
| 4 | - import { useForm } from '/@/components/Form'; | 4 | + import { FormActionType, useForm } from '/@/components/Form'; |
| 5 | import { basicSchema, dataSourceSchema } from '../config/basicConfiguration'; | 5 | import { basicSchema, dataSourceSchema } from '../config/basicConfiguration'; |
| 6 | import BasicForm from '/@/components/Form/src/BasicForm.vue'; | 6 | import BasicForm from '/@/components/Form/src/BasicForm.vue'; |
| 7 | - import { ref, unref } from 'vue'; | 7 | + import { onMounted, reactive, ref, shallowReactive, unref, nextTick } from 'vue'; |
| 8 | import VisualOptionsModal from './VisualOptionsModal.vue'; | 8 | import VisualOptionsModal from './VisualOptionsModal.vue'; |
| 9 | import { useModal } from '/@/components/Modal'; | 9 | import { useModal } from '/@/components/Modal'; |
| 10 | import { buildUUID } from '/@/utils/uuid'; | 10 | import { buildUUID } from '/@/utils/uuid'; |
| 11 | + import type { DataComponentRecord, ComponentInfo, DataSource } from '/@/api/dataBoard/model'; | ||
| 12 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 11 | 13 | ||
| 12 | - const props = defineProps(); | 14 | + type DataSourceFormEL = { [key: string]: Nullable<FormActionType> }; |
| 13 | 15 | ||
| 14 | - const dataSource = ref([{ id: 'string' }]); | 16 | + type DataSourceEl = DataSource & { id: string }; |
| 17 | + | ||
| 18 | + const props = defineProps<{ | ||
| 19 | + record: DataComponentRecord; | ||
| 20 | + frontId?: string; | ||
| 21 | + }>(); | ||
| 22 | + | ||
| 23 | + const { createMessage } = useMessage(); | ||
| 24 | + | ||
| 25 | + const componentRecord = reactive<DataComponentRecord>({ | ||
| 26 | + id: 'string', | ||
| 27 | + } as unknown as DataComponentRecord); | ||
| 28 | + | ||
| 29 | + const dataSource = ref<DataSourceEl[]>([{ id: buildUUID() } as unknown as DataSourceEl]); | ||
| 15 | 30 | ||
| 16 | const [basicRegister, basicMethod] = useForm({ | 31 | const [basicRegister, basicMethod] = useForm({ |
| 17 | schemas: basicSchema, | 32 | schemas: basicSchema, |
| @@ -19,70 +34,157 @@ | @@ -19,70 +34,157 @@ | ||
| 19 | labelWidth: 96, | 34 | labelWidth: 96, |
| 20 | }); | 35 | }); |
| 21 | 36 | ||
| 22 | - const [dataSourceRegister, dataSourceMethod] = useForm({ | ||
| 23 | - schemas: dataSourceSchema, | ||
| 24 | - showActionButtonGroup: false, | ||
| 25 | - layout: 'inline', | ||
| 26 | - labelCol: { | ||
| 27 | - span: 0, | ||
| 28 | - }, | ||
| 29 | - baseColProps: { | ||
| 30 | - span: 4, | ||
| 31 | - }, | ||
| 32 | - }); | 37 | + const dataSourceEl = shallowReactive<DataSourceFormEL>({} as unknown as DataSourceFormEL); |
| 38 | + | ||
| 39 | + const setFormEl = (el: any, id: string) => { | ||
| 40 | + if (!dataSourceEl[id] && el) { | ||
| 41 | + console.log({ el, id }); | ||
| 42 | + const { formActionType } = el as unknown as { formActionType: FormActionType }; | ||
| 43 | + dataSourceEl[id] = formActionType; | ||
| 44 | + } | ||
| 45 | + }; | ||
| 46 | + | ||
| 47 | + const getAllDataSourceFieldValue = () => { | ||
| 48 | + const _dataSource = getDataSourceField(); | ||
| 49 | + const basicInfo = basicMethod.getFieldsValue(); | ||
| 50 | + return { | ||
| 51 | + ...basicInfo, | ||
| 52 | + dataSource: _dataSource, | ||
| 53 | + }; | ||
| 54 | + }; | ||
| 55 | + | ||
| 56 | + const getDataSourceField = () => { | ||
| 57 | + const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | ||
| 58 | + const _dataSource: DataSource[] = []; | ||
| 59 | + for (const id of hasExistEl) { | ||
| 60 | + const index = unref(dataSource).findIndex((item) => item.id === id); | ||
| 61 | + const value = (dataSourceEl[id] as FormActionType).getFieldsValue() as DataSource; | ||
| 62 | + const componentInfo = unref(dataSource)[index].componentInfo || {}; | ||
| 63 | + ~index && | ||
| 64 | + _dataSource.push({ | ||
| 65 | + ...value, | ||
| 66 | + componentInfo: { ...componentInfo }, | ||
| 67 | + }); | ||
| 68 | + } | ||
| 69 | + return _dataSource; | ||
| 70 | + }; | ||
| 71 | + | ||
| 72 | + const handleCopy = async (data: DataSourceEl) => { | ||
| 73 | + const value = (dataSourceEl[data.id] as FormActionType).getFieldsValue() as DataSource; | ||
| 74 | + const index = unref(dataSource).findIndex((item) => item.id === data.id); | ||
| 33 | 75 | ||
| 34 | - const handleCopy = (data: Recordable) => { | 76 | + const componentInfo = ~index |
| 77 | + ? unref(dataSource)[index].componentInfo | ||
| 78 | + : ({} as unknown as ComponentInfo); | ||
| 79 | + | ||
| 80 | + const copyRecordId = buildUUID(); | ||
| 35 | unref(dataSource).push({ | 81 | unref(dataSource).push({ |
| 36 | - id: data.id + 1, | 82 | + ...value, |
| 83 | + id: copyRecordId, | ||
| 84 | + componentInfo, | ||
| 37 | }); | 85 | }); |
| 86 | + await nextTick(); | ||
| 87 | + (dataSourceEl[copyRecordId] as FormActionType).setFieldsValue(value); | ||
| 38 | }; | 88 | }; |
| 39 | 89 | ||
| 40 | const [registerVisualOptionModal, { openModal }] = useModal(); | 90 | const [registerVisualOptionModal, { openModal }] = useModal(); |
| 41 | 91 | ||
| 42 | - const handleSetting = () => { | ||
| 43 | - openModal(true); | 92 | + const handleSetting = (item: DataSourceEl) => { |
| 93 | + if (!props.frontId) { | ||
| 94 | + createMessage.warning('请先选择可视化组件'); | ||
| 95 | + return; | ||
| 96 | + } | ||
| 97 | + openModal(true, { | ||
| 98 | + recordId: item.id, | ||
| 99 | + componentInfo: item.componentInfo, | ||
| 100 | + }); | ||
| 44 | }; | 101 | }; |
| 45 | 102 | ||
| 46 | - const handleDelete = (data: Recordable) => { | 103 | + const handleDelete = (data: DataSourceEl) => { |
| 47 | const index = unref(dataSource).findIndex((item) => item.id === data.id); | 104 | const index = unref(dataSource).findIndex((item) => item.id === data.id); |
| 48 | - | ||
| 49 | ~index && unref(dataSource).splice(index, 1); | 105 | ~index && unref(dataSource).splice(index, 1); |
| 106 | + dataSourceEl[data.id] = null; | ||
| 50 | }; | 107 | }; |
| 51 | 108 | ||
| 52 | const handleAdd = () => { | 109 | const handleAdd = () => { |
| 53 | unref(dataSource).push({ | 110 | unref(dataSource).push({ |
| 54 | id: buildUUID(), | 111 | id: buildUUID(), |
| 55 | - }); | 112 | + } as unknown as DataSourceEl); |
| 113 | + }; | ||
| 114 | + | ||
| 115 | + const echoDataSource = () => { | ||
| 116 | + basicMethod.setFieldsValue(props.record); | ||
| 117 | + // dataSourceMethod.setFieldsValue(props.record); | ||
| 118 | + }; | ||
| 119 | + | ||
| 120 | + const handleRowComponentInfo = (recordId: string, value: ComponentInfo) => { | ||
| 121 | + const index = unref(dataSource).findIndex((item) => item.id === recordId); | ||
| 122 | + ~index && (unref(dataSource)[index].componentInfo = value); | ||
| 56 | }; | 123 | }; |
| 124 | + | ||
| 125 | + onMounted(() => { | ||
| 126 | + echoDataSource(); | ||
| 127 | + }); | ||
| 128 | + | ||
| 129 | + defineExpose({ | ||
| 130 | + getAllDataSourceFieldValue, | ||
| 131 | + }); | ||
| 57 | </script> | 132 | </script> |
| 58 | 133 | ||
| 59 | <template> | 134 | <template> |
| 60 | <section> | 135 | <section> |
| 61 | <h3 class="w-24 text-right pr-2 my-4">基础信息</h3> | 136 | <h3 class="w-24 text-right pr-2 my-4">基础信息</h3> |
| 62 | <div class="w-3/4"> | 137 | <div class="w-3/4"> |
| 63 | - <BasicForm @register="basicRegister" /> | 138 | + <BasicForm @register="basicRegister" class="w-full" /> |
| 64 | </div> | 139 | </div> |
| 65 | <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> | 140 | <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> |
| 66 | <div v-for="item in dataSource" :key="item.id" class="flex"> | 141 | <div v-for="item in dataSource" :key="item.id" class="flex"> |
| 67 | - <div class="w-24 text-right" style="flex: 0 0 96px; padding-right: 8px">选择设备</div> | 142 | + <div |
| 143 | + class="w-24 text-right leading-30px pr-8px flex right" | ||
| 144 | + style="flex: 0 0 96px; justify-content: right" | ||
| 145 | + > | ||
| 146 | + 选择设备 | ||
| 147 | + </div> | ||
| 68 | <div class="pl-2 flex-auto"> | 148 | <div class="pl-2 flex-auto"> |
| 69 | - <BasicForm @register="dataSourceRegister" /> | 149 | + <BasicForm |
| 150 | + :ref="(el) => setFormEl(el, item.id)" | ||
| 151 | + :schemas="dataSourceSchema" | ||
| 152 | + class="w-full flex-1 data-source-form" | ||
| 153 | + :show-action-button-group="false" | ||
| 154 | + :row-props="{ | ||
| 155 | + gutter: 10, | ||
| 156 | + }" | ||
| 157 | + layout="inline" | ||
| 158 | + :label-col="{ span: 0 }" | ||
| 159 | + /> | ||
| 70 | </div> | 160 | </div> |
| 71 | - <div class="flex justify-center gap-3 w-18"> | 161 | + <div class="flex justify-center gap-3 w-24"> |
| 72 | <Tooltip title="复制"> | 162 | <Tooltip title="复制"> |
| 73 | - <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg" /> | 163 | + <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg !leading-30px" /> |
| 74 | </Tooltip> | 164 | </Tooltip> |
| 75 | <Tooltip title="设置"> | 165 | <Tooltip title="设置"> |
| 76 | - <SettingOutlined @click="handleSetting()" class="cursor-pointer text-lg" /> | 166 | + <SettingOutlined |
| 167 | + @click="handleSetting(item)" | ||
| 168 | + class="cursor-pointer text-lg !leading-30px" | ||
| 169 | + /> | ||
| 77 | </Tooltip> | 170 | </Tooltip> |
| 78 | <Tooltip title="删除"> | 171 | <Tooltip title="删除"> |
| 79 | - <DeleteOutlined @click="handleDelete(item)" class="cursor-pointer text-lg" /> | 172 | + <DeleteOutlined |
| 173 | + @click="handleDelete(item)" | ||
| 174 | + class="cursor-pointer text-lg !leading-30px" | ||
| 175 | + /> | ||
| 80 | </Tooltip> | 176 | </Tooltip> |
| 81 | </div> | 177 | </div> |
| 82 | </div> | 178 | </div> |
| 83 | <div class="text-center"> | 179 | <div class="text-center"> |
| 84 | <Button type="primary" @click="handleAdd">添加数据源</Button> | 180 | <Button type="primary" @click="handleAdd">添加数据源</Button> |
| 85 | </div> | 181 | </div> |
| 86 | - <VisualOptionsModal @register="registerVisualOptionModal" /> | 182 | + <VisualOptionsModal @close="handleRowComponentInfo" @register="registerVisualOptionModal" /> |
| 87 | </section> | 183 | </section> |
| 88 | </template> | 184 | </template> |
| 185 | + | ||
| 186 | +<style scoped> | ||
| 187 | + .data-source-form:deep(.ant-row) { | ||
| 188 | + width: 100%; | ||
| 189 | + } | ||
| 190 | +</style> |
| @@ -3,26 +3,77 @@ | @@ -3,26 +3,77 @@ | ||
| 3 | import BasicModal from '/@/components/Modal/src/BasicModal.vue'; | 3 | import BasicModal from '/@/components/Modal/src/BasicModal.vue'; |
| 4 | import BasicConfiguration from './BasicConfiguration.vue'; | 4 | import BasicConfiguration from './BasicConfiguration.vue'; |
| 5 | import VisualConfiguration from './VisualConfiguration.vue'; | 5 | import VisualConfiguration from './VisualConfiguration.vue'; |
| 6 | - import { ref } from 'vue'; | 6 | + import { computed, ref, unref } from 'vue'; |
| 7 | + import type { DataComponentRecord } from '/@/api/dataBoard/model'; | ||
| 8 | + import { RouteParams, useRoute } from 'vue-router'; | ||
| 9 | + import { addDataComponent, updateDataBoardLayout } from '/@/api/dataBoard'; | ||
| 10 | + import { useModalInner } from '/@/components/Modal'; | ||
| 11 | + import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH } from '../../config/config'; | ||
| 7 | 12 | ||
| 8 | - const componentId = ref(''); | ||
| 9 | - | ||
| 10 | - interface SettingOption { | ||
| 11 | - color?: string; | 13 | + interface DataComponentRouteParams extends RouteParams { |
| 14 | + id: string; | ||
| 12 | } | 15 | } |
| 13 | 16 | ||
| 14 | - const handleSettingUpdate = (value: SettingOption) => {}; | 17 | + const emit = defineEmits(['submit']); |
| 18 | + | ||
| 19 | + const ROUTE = useRoute(); | ||
| 20 | + | ||
| 21 | + const [register, { closeModal }] = useModalInner(); | ||
| 22 | + | ||
| 23 | + const basicConfigurationEl = ref<{ | ||
| 24 | + getAllDataSourceFieldValue: Fn<any, Recordable>; | ||
| 25 | + }>(); | ||
| 26 | + | ||
| 27 | + const boardId = computed(() => { | ||
| 28 | + return (ROUTE.params as DataComponentRouteParams).id; | ||
| 29 | + }); | ||
| 30 | + | ||
| 31 | + const frontId = ref(''); | ||
| 32 | + | ||
| 33 | + const componentRecord = ref<DataComponentRecord>({} as unknown as DataComponentRecord); | ||
| 34 | + | ||
| 35 | + const handleSubmit = () => { | ||
| 36 | + const { getAllDataSourceFieldValue } = unref(basicConfigurationEl)!; | ||
| 37 | + const value = getAllDataSourceFieldValue(); | ||
| 38 | + handleAddComponent(value); | ||
| 39 | + }; | ||
| 40 | + | ||
| 41 | + const handleAddComponent = async (value: Recordable) => { | ||
| 42 | + try { | ||
| 43 | + const data = await addDataComponent({ | ||
| 44 | + boardId: unref(boardId), | ||
| 45 | + record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value }, | ||
| 46 | + }); | ||
| 47 | + const id = data.data.id; | ||
| 48 | + await updateDataBoardLayout({ | ||
| 49 | + boardId: unref(boardId), | ||
| 50 | + layout: [{ id, w: DEFAULT_WIDGET_WIDTH, h: DEFAULT_WIDGET_HEIGHT, x: 0, y: 0 }], | ||
| 51 | + }); | ||
| 52 | + closeModal(); | ||
| 53 | + emit('submit'); | ||
| 54 | + } catch (error) {} | ||
| 55 | + }; | ||
| 15 | </script> | 56 | </script> |
| 16 | 57 | ||
| 17 | <template> | 58 | <template> |
| 18 | - <BasicModal v-bind="$attrs" title="自定义组件" width="70%"> | 59 | + <BasicModal |
| 60 | + v-bind="$attrs" | ||
| 61 | + @register="register" | ||
| 62 | + title="自定义组件" | ||
| 63 | + width="70%" | ||
| 64 | + @ok="handleSubmit" | ||
| 65 | + > | ||
| 19 | <section> | 66 | <section> |
| 20 | <Tabs type="card"> | 67 | <Tabs type="card"> |
| 21 | <Tabs.TabPane key="1" tab="基础配置"> | 68 | <Tabs.TabPane key="1" tab="基础配置"> |
| 22 | - <BasicConfiguration @change="handleSettingUpdate" /> | 69 | + <BasicConfiguration |
| 70 | + ref="basicConfigurationEl" | ||
| 71 | + :front-id="frontId" | ||
| 72 | + :record="componentRecord" | ||
| 73 | + /> | ||
| 23 | </Tabs.TabPane> | 74 | </Tabs.TabPane> |
| 24 | <Tabs.TabPane key="2" tab="可视化配置"> | 75 | <Tabs.TabPane key="2" tab="可视化配置"> |
| 25 | - <VisualConfiguration v-model:value="componentId" /> | 76 | + <VisualConfiguration v-model:value="frontId" /> |
| 26 | </Tabs.TabPane> | 77 | </Tabs.TabPane> |
| 27 | </Tabs> | 78 | </Tabs> |
| 28 | </section> | 79 | </section> |
| @@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
| 22 | :data-source="textComponentConfig" | 22 | :data-source="textComponentConfig" |
| 23 | > | 23 | > |
| 24 | <template #renderItem="{ item }"> | 24 | <template #renderItem="{ item }"> |
| 25 | - <List.Item> | 25 | + <List.Item class="!flex !justify-center"> |
| 26 | <VisualWidgetSelect | 26 | <VisualWidgetSelect |
| 27 | :checked-id="props.value" | 27 | :checked-id="props.value" |
| 28 | :control-id="item.id" | 28 | :control-id="item.id" |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | + import { onMounted, ref, unref } from 'vue'; | ||
| 2 | import { modeOne, modeTwo, modeThree, modeFour } from '../config/visualOptions'; | 3 | import { modeOne, modeTwo, modeThree, modeFour } from '../config/visualOptions'; |
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 4 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 5 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
| 6 | + import { ComponentInfo } from '/@/api/dataBoard/model'; | ||
| 7 | + | ||
| 8 | + const emit = defineEmits(['close']); | ||
| 9 | + | ||
| 10 | + const recordId = ref(''); | ||
| 5 | 11 | ||
| 6 | const [registerForm, method] = useForm({ | 12 | const [registerForm, method] = useForm({ |
| 7 | schemas: modeTwo, | 13 | schemas: modeTwo, |
| @@ -12,11 +18,16 @@ | @@ -12,11 +18,16 @@ | ||
| 12 | }, | 18 | }, |
| 13 | }); | 19 | }); |
| 14 | 20 | ||
| 15 | - const [register, { closeModal }] = useModalInner(); | 21 | + const [register, { closeModal }] = useModalInner( |
| 22 | + (data: { recordId: string; componentInfo: ComponentInfo }) => { | ||
| 23 | + recordId.value = data.recordId; | ||
| 24 | + method.setFieldsValue(data.componentInfo || {}); | ||
| 25 | + } | ||
| 26 | + ); | ||
| 16 | 27 | ||
| 17 | const handleGetValue = () => { | 28 | const handleGetValue = () => { |
| 18 | const value = method.getFieldsValue(); | 29 | const value = method.getFieldsValue(); |
| 19 | - console.log(value); | 30 | + emit('close', unref(recordId), value); |
| 20 | }; | 31 | }; |
| 21 | 32 | ||
| 22 | const handleClose = () => { | 33 | const handleClose = () => { |
| @@ -26,7 +37,14 @@ | @@ -26,7 +37,14 @@ | ||
| 26 | </script> | 37 | </script> |
| 27 | 38 | ||
| 28 | <template> | 39 | <template> |
| 29 | - <BasicModal v-bind="$attrs" @register="register" @ok="handleClose" title="选项" width="60%"> | 40 | + <BasicModal |
| 41 | + v-bind="$attrs" | ||
| 42 | + destroy-on-close | ||
| 43 | + @register="register" | ||
| 44 | + @ok="handleClose" | ||
| 45 | + title="选项" | ||
| 46 | + width="60%" | ||
| 47 | + > | ||
| 30 | <BasicForm @register="registerForm" /> | 48 | <BasicForm @register="registerForm" /> |
| 31 | </BasicModal> | 49 | </BasicModal> |
| 32 | </template> | 50 | </template> |
| 1 | +import { byOganizationIdGetMasterDevice, getAttribute } from '/@/api/ruleengine/ruleengineApi'; | ||
| 1 | import { getOrganizationList } from '/@/api/system/system'; | 2 | import { getOrganizationList } from '/@/api/system/system'; |
| 2 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
| 4 | +import { copyTransFun } from '/@/utils/fnUtils'; | ||
| 5 | +import { to } from '/@/utils/to'; | ||
| 6 | + | ||
| 7 | +export enum DataSourceField { | ||
| 8 | + DEVICE_ID = 'deviceId', | ||
| 9 | +} | ||
| 3 | 10 | ||
| 4 | export const basicSchema: FormSchema[] = [ | 11 | export const basicSchema: FormSchema[] = [ |
| 5 | { | 12 | { |
| 6 | field: 'name', | 13 | field: 'name', |
| 7 | label: '组件名称', | 14 | label: '组件名称', |
| 8 | component: 'Input', | 15 | component: 'Input', |
| 16 | + componentProps: { | ||
| 17 | + placeholder: '请输入组件名称', | ||
| 18 | + }, | ||
| 9 | }, | 19 | }, |
| 10 | { | 20 | { |
| 11 | field: 'remake', | 21 | field: 'remake', |
| 12 | label: '组件备注', | 22 | label: '组件备注', |
| 13 | component: 'InputTextArea', | 23 | component: 'InputTextArea', |
| 24 | + componentProps: { | ||
| 25 | + placeholder: '请输入组件备注', | ||
| 26 | + }, | ||
| 14 | }, | 27 | }, |
| 15 | ]; | 28 | ]; |
| 16 | 29 | ||
| 17 | export const dataSourceSchema: FormSchema[] = [ | 30 | export const dataSourceSchema: FormSchema[] = [ |
| 18 | { | 31 | { |
| 19 | - field: 'org', | 32 | + field: 'organizationId', |
| 20 | component: 'ApiTreeSelect', | 33 | component: 'ApiTreeSelect', |
| 21 | label: '组织', | 34 | label: '组织', |
| 22 | colProps: { span: 6 }, | 35 | colProps: { span: 6 }, |
| 23 | - componentProps() { | 36 | + componentProps({ formActionType }) { |
| 37 | + const { setFieldsValue } = formActionType; | ||
| 24 | return { | 38 | return { |
| 25 | placeholder: '请选择组织', | 39 | placeholder: '请选择组织', |
| 26 | api: async () => { | 40 | api: async () => { |
| 27 | const data = await getOrganizationList(); | 41 | const data = await getOrganizationList(); |
| 42 | + copyTransFun(data as any as any[]); | ||
| 28 | return data; | 43 | return data; |
| 29 | }, | 44 | }, |
| 30 | - | ||
| 31 | - onChange() {}, | 45 | + onChange() { |
| 46 | + setFieldsValue({ device: null, attr: null }); | ||
| 47 | + }, | ||
| 48 | + getPopupContainer: () => document.body, | ||
| 32 | }; | 49 | }; |
| 33 | }, | 50 | }, |
| 34 | }, | 51 | }, |
| 35 | { | 52 | { |
| 36 | - field: 'device', | ||
| 37 | - component: 'Select', | 53 | + field: 'deviceId', |
| 54 | + component: 'ApiSelect', | ||
| 38 | label: '设备', | 55 | label: '设备', |
| 39 | colProps: { span: 5 }, | 56 | colProps: { span: 5 }, |
| 40 | - componentProps: { | ||
| 41 | - placeholder: '请选择设备', | 57 | + componentProps({ formModel, formActionType }) { |
| 58 | + const { setFieldsValue } = formActionType; | ||
| 59 | + const orgId = formModel['organizationId']; | ||
| 60 | + return { | ||
| 61 | + api: async () => { | ||
| 62 | + if (orgId) { | ||
| 63 | + const [, data] = await to<Record<'id' | 'name', string>[]>( | ||
| 64 | + byOganizationIdGetMasterDevice(orgId) | ||
| 65 | + ); | ||
| 66 | + if (data) return data.map((item) => ({ label: item.name, value: item.id })); | ||
| 67 | + } | ||
| 68 | + return []; | ||
| 69 | + }, | ||
| 70 | + onChange() { | ||
| 71 | + setFieldsValue({ attr: null }); | ||
| 72 | + }, | ||
| 73 | + placeholder: '请选择设备', | ||
| 74 | + getPopupContainer: () => document.body, | ||
| 75 | + }; | ||
| 42 | }, | 76 | }, |
| 43 | }, | 77 | }, |
| 44 | { | 78 | { |
| 45 | - field: 'attr', | ||
| 46 | - component: 'Select', | 79 | + field: 'attribute', |
| 80 | + component: 'ApiSelect', | ||
| 47 | label: '属性', | 81 | label: '属性', |
| 48 | colProps: { span: 5 }, | 82 | colProps: { span: 5 }, |
| 49 | - componentProps: { | ||
| 50 | - placeholder: '请选择属性', | 83 | + componentProps({ formModel }) { |
| 84 | + const orgId = formModel['organizationId']; | ||
| 85 | + const deviceId = formModel['deviceId']; | ||
| 86 | + return { | ||
| 87 | + api: async () => { | ||
| 88 | + if (orgId && deviceId) { | ||
| 89 | + const [, data] = await to<string[]>(getAttribute(orgId, deviceId)); | ||
| 90 | + // TODO attribute exist null | ||
| 91 | + if (data) return data.filter(Boolean).map((item) => ({ label: item, value: item })); | ||
| 92 | + } | ||
| 93 | + return []; | ||
| 94 | + }, | ||
| 95 | + placeholder: '请选择属性', | ||
| 96 | + getPopupContainer: () => document.body, | ||
| 97 | + }; | ||
| 51 | }, | 98 | }, |
| 52 | }, | 99 | }, |
| 53 | { | 100 | { |
| @@ -60,7 +107,7 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -60,7 +107,7 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 60 | }, | 107 | }, |
| 61 | }, | 108 | }, |
| 62 | { | 109 | { |
| 63 | - field: 'attrRename', | 110 | + field: 'attributeRename', |
| 64 | component: 'Input', | 111 | component: 'Input', |
| 65 | label: '属性', | 112 | label: '属性', |
| 66 | colProps: { span: 4 }, | 113 | colProps: { span: 4 }, |
src/views/data/board/detail/config/help.ts
0 → 100644
| 1 | +import { Component } from 'vue'; | ||
| 2 | +import TextComponent from '../../components/TextComponent/TextComponent.vue'; | ||
| 3 | +import { | ||
| 4 | + TextComponent1Config, | ||
| 5 | + TextComponent2Config, | ||
| 6 | + TextComponent3Config, | ||
| 7 | + TextComponent4Config, | ||
| 8 | + TextComponent5Config, | ||
| 9 | + transformTextComponentConfig, | ||
| 10 | +} from '../../components/TextComponent/config'; | ||
| 11 | +import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; | ||
| 12 | +export enum FrontComponent { | ||
| 13 | + TEXT_COMPONENT_1 = 'text-component-1', | ||
| 14 | + TEXT_COMPONENT_2 = 'text-component-2', | ||
| 15 | + TEXT_COMPONENT_3 = 'text-component-3', | ||
| 16 | + TEXT_COMPONENT_4 = 'text-component-4', | ||
| 17 | + TEXT_COMPONENT_5 = 'text-component-5', | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +export type FrontComponentType = | ||
| 21 | + | 'text-component-1' | ||
| 22 | + | 'text-component-2' | ||
| 23 | + | 'text-component-3' | ||
| 24 | + | 'text-component-4' | ||
| 25 | + | 'text-component-5'; | ||
| 26 | + | ||
| 27 | +export interface ComponentConfig { | ||
| 28 | + Component: Component; | ||
| 29 | + ComponentConfig: Recordable; | ||
| 30 | + transformConfig: ( | ||
| 31 | + ComponentConfig: Recordable, | ||
| 32 | + record: DataComponentRecord, | ||
| 33 | + dataSourceRecord: DataSource | ||
| 34 | + ) => Recordable; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +export const frontComponentMap = new Map<FrontComponentType, ComponentConfig>(); | ||
| 38 | + | ||
| 39 | +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, { | ||
| 40 | + Component: TextComponent, | ||
| 41 | + ComponentConfig: TextComponent1Config, | ||
| 42 | + transformConfig: transformTextComponentConfig, | ||
| 43 | +}); | ||
| 44 | + | ||
| 45 | +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_2, { | ||
| 46 | + Component: TextComponent, | ||
| 47 | + ComponentConfig: TextComponent2Config, | ||
| 48 | + transformConfig: transformTextComponentConfig, | ||
| 49 | +}); | ||
| 50 | + | ||
| 51 | +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, { | ||
| 52 | + Component: TextComponent, | ||
| 53 | + ComponentConfig: TextComponent3Config, | ||
| 54 | + transformConfig: transformTextComponentConfig, | ||
| 55 | +}); | ||
| 56 | + | ||
| 57 | +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, { | ||
| 58 | + Component: TextComponent, | ||
| 59 | + ComponentConfig: TextComponent4Config, | ||
| 60 | + transformConfig: transformTextComponentConfig, | ||
| 61 | +}); | ||
| 62 | + | ||
| 63 | +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, { | ||
| 64 | + Component: TextComponent, | ||
| 65 | + ComponentConfig: TextComponent5Config, | ||
| 66 | + transformConfig: transformTextComponentConfig, | ||
| 67 | +}); |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | import { Button, PageHeader } from 'ant-design-vue'; | 2 | import { Button, PageHeader } from 'ant-design-vue'; |
| 3 | import { GridItem, GridLayout, Layout } from 'vue3-grid-layout'; | 3 | import { GridItem, GridLayout, Layout } from 'vue3-grid-layout'; |
| 4 | - import { nextTick, ref } from 'vue'; | 4 | + import { nextTick, onMounted, ref } from 'vue'; |
| 5 | import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; | 5 | import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; |
| 6 | import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; | 6 | import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; |
| 7 | - import InformationPanel from '../components/Other/InformationPanel.vue'; | ||
| 8 | import { DropMenu } from '/@/components/Dropdown'; | 7 | import { DropMenu } from '/@/components/Dropdown'; |
| 9 | import DataBindModal from './components/DataBindModal.vue'; | 8 | import DataBindModal from './components/DataBindModal.vue'; |
| 10 | import { useModal } from '/@/components/Modal'; | 9 | import { useModal } from '/@/components/Modal'; |
| 11 | import { MoreActionEvent } from '../config/config'; | 10 | import { MoreActionEvent } from '../config/config'; |
| 11 | + import { deleteDataComponent, getDataComponent } from '/@/api/dataBoard'; | ||
| 12 | + import { useRoute } from 'vue-router'; | ||
| 13 | + import { computed, unref } from '@vue/reactivity'; | ||
| 14 | + import { DataComponentRecord, DataSource, Layout as LayoutRecord } from '/@/api/dataBoard/model'; | ||
| 15 | + import { frontComponentMap, FrontComponentType } from './config/help'; | ||
| 16 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 12 | const handleBack = () => {}; | 17 | const handleBack = () => {}; |
| 13 | 18 | ||
| 14 | - interface ChartAttr { | ||
| 15 | - id: string | number; | ||
| 16 | - width: number; | ||
| 17 | - height: number; | ||
| 18 | - } | 19 | + type DataBoardRecord = DataComponentRecord & { layout: LayoutRecord }; |
| 19 | 20 | ||
| 20 | - interface ChartSetting extends Layout { | ||
| 21 | - chart: ChartAttr[]; | ||
| 22 | - } | 21 | + type DataBoardLayoutInfo = Layout & { |
| 22 | + record: DataComponentRecord & { width: number; height: number }; | ||
| 23 | + }; | ||
| 24 | + | ||
| 25 | + const ROUTE = useRoute(); | ||
| 26 | + | ||
| 27 | + const { createMessage } = useMessage(); | ||
| 28 | + const getBoardId = computed(() => { | ||
| 29 | + return (ROUTE.params as { id: string }).id; | ||
| 30 | + }); | ||
| 23 | 31 | ||
| 24 | const widgetEl = new Map<string, Fn>(); | 32 | const widgetEl = new Map<string, Fn>(); |
| 25 | 33 | ||
| 26 | - const id = '296Charts'; | ||
| 27 | - // GridItem. | ||
| 28 | - const layout = ref<ChartSetting[]>([ | ||
| 29 | - { | ||
| 30 | - x: 0, | ||
| 31 | - y: 0, | ||
| 32 | - w: 6, | ||
| 33 | - h: 6, | ||
| 34 | - i: id, | ||
| 35 | - static: false, | ||
| 36 | - chart: [ | ||
| 37 | - { id: 'a', width: 100, height: 100 }, | ||
| 38 | - { id: 'b', width: 100, height: 100 }, | ||
| 39 | - // { id: 'c', width: 100, height: 100 }, | ||
| 40 | - // { id: 'd', width: 100, height: 100 }, | ||
| 41 | - // { id: 'e', width: 100, height: 100 }, | ||
| 42 | - // { id: 'f', width: 100, height: 100 }, | ||
| 43 | - // { id: 'g', width: 100, height: 100 }, | ||
| 44 | - // { id: 'h', width: 100, height: 100 }, | ||
| 45 | - // { id: 'i', width: 100, height: 100 }, | ||
| 46 | - ], | ||
| 47 | - }, | ||
| 48 | - { | ||
| 49 | - x: 0, | ||
| 50 | - y: 0, | ||
| 51 | - w: 6, | ||
| 52 | - h: 6, | ||
| 53 | - i: 'sdasdf', | ||
| 54 | - static: false, | ||
| 55 | - chart: [ | ||
| 56 | - { id: 'j', width: 100, height: 100 }, | ||
| 57 | - { id: 'k', width: 100, height: 100 }, | ||
| 58 | - { id: 'l', width: 100, height: 100 }, | ||
| 59 | - // { id: 'm', width: 100, height: 100 }, | ||
| 60 | - // { id: 'n', width: 100, height: 100 }, | ||
| 61 | - // { id: 'o', width: 100, height: 100 }, | ||
| 62 | - // { id: 'p', width: 100, height: 100 }, | ||
| 63 | - // { id: 'q', width: 100, height: 100 }, | ||
| 64 | - // { id: 'r', width: 100, height: 100 }, | ||
| 65 | - ], | ||
| 66 | - }, | ||
| 67 | - ]); | 34 | + const dataBoardList = ref<DataBoardLayoutInfo[]>([]); |
| 35 | + | ||
| 68 | const draggable = ref(true); | 36 | const draggable = ref(true); |
| 69 | const resizable = ref(true); | 37 | const resizable = ref(true); |
| 70 | 38 | ||
| 71 | const GirdLayoutColNum = 24; | 39 | const GirdLayoutColNum = 24; |
| 72 | - | ||
| 73 | const GridLayoutMargin = 10; | 40 | const GridLayoutMargin = 10; |
| 74 | 41 | ||
| 42 | + const defaultWidth = 6; | ||
| 43 | + const defaultHeight = 6; | ||
| 44 | + | ||
| 75 | function updateSize(i: string, newH: number, newW: number, newHPx: number, newWPx: number) { | 45 | function updateSize(i: string, newH: number, newW: number, newHPx: number, newWPx: number) { |
| 76 | newWPx = Number(newWPx); | 46 | newWPx = Number(newWPx); |
| 77 | newHPx = Number(newHPx); | 47 | newHPx = Number(newHPx); |
| 78 | 48 | ||
| 79 | - const data = layout.value.find((item) => item.i === i)!; | ||
| 80 | - const border = 2; | ||
| 81 | - const length = data.chart.length || 0; | 49 | + const data = dataBoardList.value.find((item) => item.i === i)!; |
| 50 | + const length = data.record.dataSource.length || 0; | ||
| 82 | 51 | ||
| 83 | const row = Math.floor(Math.pow(length, 0.5)); | 52 | const row = Math.floor(Math.pow(length, 0.5)); |
| 84 | const col = Math.floor(length / row); | 53 | const col = Math.floor(length / row); |
| @@ -98,13 +67,14 @@ | @@ -98,13 +67,14 @@ | ||
| 98 | width = 100; | 67 | width = 100; |
| 99 | } | 68 | } |
| 100 | 69 | ||
| 101 | - data.chart = data?.chart.map((item) => { | 70 | + data.record.dataSource = data?.record.dataSource.map((item) => { |
| 102 | return { | 71 | return { |
| 103 | ...item, | 72 | ...item, |
| 104 | width, | 73 | width, |
| 105 | height, | 74 | height, |
| 106 | }; | 75 | }; |
| 107 | }); | 76 | }); |
| 77 | + console.log(unref(dataBoardList)); | ||
| 108 | nextTick(() => { | 78 | nextTick(() => { |
| 109 | const updateFn = widgetEl.get(i); | 79 | const updateFn = widgetEl.get(i); |
| 110 | if (updateFn) updateFn(); | 80 | if (updateFn) updateFn(); |
| @@ -132,7 +102,7 @@ | @@ -132,7 +102,7 @@ | ||
| 132 | }); | 102 | }); |
| 133 | }; | 103 | }; |
| 134 | 104 | ||
| 135 | - const setComponentRef = (el: Element, record: ChartSetting) => { | 105 | + const setComponentRef = (el: Element, record: DataBoardLayoutInfo) => { |
| 136 | if (widgetEl.has(record.i)) widgetEl.delete(record.i); | 106 | if (widgetEl.has(record.i)) widgetEl.delete(record.i); |
| 137 | if (el && (el as unknown as { update: Fn }).update) | 107 | if (el && (el as unknown as { update: Fn }).update) |
| 138 | widgetEl.set(record.i, (el as unknown as { update: Fn }).update); | 108 | widgetEl.set(record.i, (el as unknown as { update: Fn }).update); |
| @@ -140,28 +110,75 @@ | @@ -140,28 +110,75 @@ | ||
| 140 | 110 | ||
| 141 | const [register, { openModal }] = useModal(); | 111 | const [register, { openModal }] = useModal(); |
| 142 | 112 | ||
| 143 | - const handleMoreAction = (event: DropMenu) => { | 113 | + const handleMoreAction = (event: DropMenu, id: string) => { |
| 144 | if (event.event === MoreActionEvent.EDIT) openModal(true); | 114 | if (event.event === MoreActionEvent.EDIT) openModal(true); |
| 115 | + if (event.event === MoreActionEvent.DELETE) handleDelete(id); | ||
| 145 | }; | 116 | }; |
| 117 | + | ||
| 118 | + const handleOpenCreatePanel = () => { | ||
| 119 | + openModal(true); | ||
| 120 | + }; | ||
| 121 | + | ||
| 122 | + const getDataBoardComponent = async () => { | ||
| 123 | + try { | ||
| 124 | + const data = await getDataComponent(unref(getBoardId)); | ||
| 125 | + dataBoardList.value = data.data.componentData.map((item) => { | ||
| 126 | + const index = data.data.componentLayout.findIndex((each) => item.id === each.id); | ||
| 127 | + const layout = data.data.componentLayout[index]; | ||
| 128 | + return { | ||
| 129 | + i: item.id, | ||
| 130 | + w: layout.w || defaultWidth, | ||
| 131 | + h: layout.h || defaultHeight, | ||
| 132 | + x: layout.x || 0, | ||
| 133 | + y: layout.y || 0, | ||
| 134 | + record: { | ||
| 135 | + ...item, | ||
| 136 | + width: 100, | ||
| 137 | + height: 100, | ||
| 138 | + }, | ||
| 139 | + }; | ||
| 140 | + }); | ||
| 141 | + } catch (error) {} | ||
| 142 | + }; | ||
| 143 | + | ||
| 144 | + const getComponent = (record: DataComponentRecord) => { | ||
| 145 | + const frontComponent = record.frontId; | ||
| 146 | + const component = frontComponentMap.get(frontComponent as FrontComponentType); | ||
| 147 | + return component?.Component; | ||
| 148 | + }; | ||
| 149 | + | ||
| 150 | + const getComponentConfig = (record: DataComponentRecord, dataSourceRecord: DataSource) => { | ||
| 151 | + const frontComponent = record.frontId; | ||
| 152 | + const component = frontComponentMap.get(frontComponent as FrontComponentType); | ||
| 153 | + return component?.transformConfig(component.ComponentConfig, record, dataSourceRecord); | ||
| 154 | + }; | ||
| 155 | + | ||
| 156 | + const handleDelete = async (id: string) => { | ||
| 157 | + try { | ||
| 158 | + await deleteDataComponent([id]); | ||
| 159 | + createMessage.success('删除成功'); | ||
| 160 | + await getDataBoardComponent(); | ||
| 161 | + } catch (error) { | ||
| 162 | + createMessage.error('删除失败'); | ||
| 163 | + } | ||
| 164 | + }; | ||
| 165 | + | ||
| 166 | + onMounted(() => { | ||
| 167 | + getDataBoardComponent(); | ||
| 168 | + }); | ||
| 146 | </script> | 169 | </script> |
| 147 | 170 | ||
| 148 | <template> | 171 | <template> |
| 149 | - <!-- <PageWrapper title="水电表看板" @back="handleBack" content="已创建组件: 3个"> | ||
| 150 | - <template #extra> | ||
| 151 | - <Button type="primary">创建组件</Button> | ||
| 152 | - </template> | ||
| 153 | - | ||
| 154 | - </PageWrapper> --> | ||
| 155 | <section class="bg-light-50 flex flex-col overflow-hidden h-full w-full"> | 172 | <section class="bg-light-50 flex flex-col overflow-hidden h-full w-full"> |
| 156 | <PageHeader title="水电表看板" @back="handleBack"> | 173 | <PageHeader title="水电表看板" @back="handleBack"> |
| 157 | <template #extra> | 174 | <template #extra> |
| 158 | - <Button type="primary">创建组件</Button> | 175 | + <Button type="primary" @click="handleOpenCreatePanel">创建组件</Button> |
| 159 | </template> | 176 | </template> |
| 160 | <div>已创建组件: 3个</div> | 177 | <div>已创建组件: 3个</div> |
| 161 | </PageHeader> | 178 | </PageHeader> |
| 162 | <section class="flex-1"> | 179 | <section class="flex-1"> |
| 163 | <GridLayout | 180 | <GridLayout |
| 164 | - v-model:layout="layout" | 181 | + v-model:layout="dataBoardList" |
| 165 | :col-num="GirdLayoutColNum" | 182 | :col-num="GirdLayoutColNum" |
| 166 | :row-height="30" | 183 | :row-height="30" |
| 167 | :margin="[GridLayoutMargin, GridLayoutMargin]" | 184 | :margin="[GridLayoutMargin, GridLayoutMargin]" |
| @@ -172,7 +189,7 @@ | @@ -172,7 +189,7 @@ | ||
| 172 | style="width: 100%" | 189 | style="width: 100%" |
| 173 | > | 190 | > |
| 174 | <GridItem | 191 | <GridItem |
| 175 | - v-for="item in layout" | 192 | + v-for="item in dataBoardList" |
| 176 | :key="item.i" | 193 | :key="item.i" |
| 177 | :static="item.static" | 194 | :static="item.static" |
| 178 | :x="item.x" | 195 | :x="item.x" |
| @@ -190,24 +207,27 @@ | @@ -190,24 +207,27 @@ | ||
| 190 | <WidgetWrapper | 207 | <WidgetWrapper |
| 191 | :key="item.i" | 208 | :key="item.i" |
| 192 | :ref="(el: Element) => setComponentRef(el, item)" | 209 | :ref="(el: Element) => setComponentRef(el, item)" |
| 193 | - :data-source="item.chart" | 210 | + :data-source="item.record.dataSource" |
| 194 | > | 211 | > |
| 195 | <template #header> | 212 | <template #header> |
| 196 | <!-- <div>header</div> --> | 213 | <!-- <div>header</div> --> |
| 197 | - <BaseWidgetHeader @action="handleMoreAction" /> | 214 | + <BaseWidgetHeader :id="item.record.id" @action="handleMoreAction" /> |
| 198 | </template> | 215 | </template> |
| 199 | - <template #controls="{ record, add }"> | ||
| 200 | - <InformationPanel /> | ||
| 201 | - <!-- <LightBulbSwitch @change="handleChange" /> --> | ||
| 202 | - <!-- <div :id="getControlsWidgetId(record.id)" class="widget-charts" /> --> | ||
| 203 | - <!-- <BaseDashboard :data-source="record" :add="add" /> --> | 216 | + <template #controls="{ record, add, remove, update }"> |
| 217 | + <component | ||
| 218 | + :is="getComponent(item.record)" | ||
| 219 | + :add="add" | ||
| 220 | + :remove="remove" | ||
| 221 | + :update="update" | ||
| 222 | + v-bind="getComponentConfig(item.record, record)" | ||
| 223 | + /> | ||
| 204 | </template> | 224 | </template> |
| 205 | </WidgetWrapper> | 225 | </WidgetWrapper> |
| 206 | </GridItem> | 226 | </GridItem> |
| 207 | </GridLayout> | 227 | </GridLayout> |
| 208 | </section> | 228 | </section> |
| 209 | </section> | 229 | </section> |
| 210 | - <DataBindModal @register="register" /> | 230 | + <DataBindModal @register="register" @submit="getDataBoardComponent" /> |
| 211 | </template> | 231 | </template> |
| 212 | 232 | ||
| 213 | <style> | 233 | <style> |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { List, Card, Statistic, Button, Tooltip } from 'ant-design-vue'; | 2 | + import { List, Card, Statistic, Button, Tooltip, Spin } from 'ant-design-vue'; |
| 3 | import { onMounted, ref, unref } from 'vue'; | 3 | import { onMounted, ref, unref } from 'vue'; |
| 4 | import { PageWrapper } from '/@/components/Page'; | 4 | import { PageWrapper } from '/@/components/Page'; |
| 5 | import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue'; | 5 | import { MoreOutlined, ShareAltOutlined } from '@ant-design/icons-vue'; |
| @@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
| 20 | 20 | ||
| 21 | const { createMessage } = useMessage(); | 21 | const { createMessage } = useMessage(); |
| 22 | 22 | ||
| 23 | + const loading = ref(false); | ||
| 23 | const dataBoardList = ref<DataBoardRecord[]>([]); | 24 | const dataBoardList = ref<DataBoardRecord[]>([]); |
| 24 | //分页相关 | 25 | //分页相关 |
| 25 | const page = ref(1); | 26 | const page = ref(1); |
| @@ -65,6 +66,7 @@ | @@ -65,6 +66,7 @@ | ||
| 65 | 66 | ||
| 66 | const getDatasource = async () => { | 67 | const getDatasource = async () => { |
| 67 | try { | 68 | try { |
| 69 | + loading.value = true; | ||
| 68 | const { total, items } = await getDataBoardList({ | 70 | const { total, items } = await getDataBoardList({ |
| 69 | page: unref(paginationProp).current, | 71 | page: unref(paginationProp).current, |
| 70 | pageSize: unref(paginationProp).pageSize, | 72 | pageSize: unref(paginationProp).pageSize, |
| @@ -73,6 +75,7 @@ | @@ -73,6 +75,7 @@ | ||
| 73 | paginationProp.value.total = total; | 75 | paginationProp.value.total = total; |
| 74 | } catch (error) { | 76 | } catch (error) { |
| 75 | } finally { | 77 | } finally { |
| 78 | + loading.value = false; | ||
| 76 | } | 79 | } |
| 77 | }; | 80 | }; |
| 78 | 81 | ||
| @@ -120,53 +123,55 @@ | @@ -120,53 +123,55 @@ | ||
| 120 | <div class="text-lg mr-6 font-bold">自定义看板</div> | 123 | <div class="text-lg mr-6 font-bold">自定义看板</div> |
| 121 | <Button type="primary" @click="handleOpenDetailModal">创建看板</Button> | 124 | <Button type="primary" @click="handleOpenDetailModal">创建看板</Button> |
| 122 | </div> | 125 | </div> |
| 123 | - <List | ||
| 124 | - :pagination="paginationProp" | ||
| 125 | - :data-source="dataBoardList" | ||
| 126 | - :grid="{ gutter: 5, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }" | ||
| 127 | - > | ||
| 128 | - <template #renderItem="{ item }"> | ||
| 129 | - <ListItem> | ||
| 130 | - <Card class="data-card cursor-pointer" @click="handleViewBoard(item)"> | ||
| 131 | - <template #extra> | ||
| 132 | - <Dropdown | ||
| 133 | - :trigger="['click']" | ||
| 134 | - @menu-event="(event) => handleMenuEvent(event, item)" | ||
| 135 | - :drop-menu-list="dropMenuList" | ||
| 136 | - > | ||
| 137 | - <MoreOutlined class="rotate-90 transform cursor-pointer" /> | ||
| 138 | - </Dropdown> | ||
| 139 | - </template> | ||
| 140 | - <!-- <template #cover>title</template> --> | ||
| 141 | - <section> | ||
| 142 | - <div class="flex justify-between items-center"> | ||
| 143 | - <div>{{ item.name }}</div> | ||
| 144 | - <div class="flex content-center"> | ||
| 145 | - <Statistic value="12"> | ||
| 146 | - <template #suffix> | ||
| 147 | - <span class="text-sm">个组件</span> | ||
| 148 | - </template> | ||
| 149 | - </Statistic> | 126 | + <Spin :spinning="loading"> |
| 127 | + <List | ||
| 128 | + :pagination="paginationProp" | ||
| 129 | + :data-source="dataBoardList" | ||
| 130 | + :grid="{ gutter: 5, column: 4, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 3 }" | ||
| 131 | + > | ||
| 132 | + <template #renderItem="{ item }"> | ||
| 133 | + <ListItem> | ||
| 134 | + <Card class="data-card cursor-pointer"> | ||
| 135 | + <template #extra> | ||
| 136 | + <Dropdown | ||
| 137 | + :trigger="['click']" | ||
| 138 | + @menu-event="(event) => handleMenuEvent(event, item)" | ||
| 139 | + :drop-menu-list="dropMenuList" | ||
| 140 | + > | ||
| 141 | + <MoreOutlined class="rotate-90 transform cursor-pointer" /> | ||
| 142 | + </Dropdown> | ||
| 143 | + </template> | ||
| 144 | + <!-- <template #cover>title</template> --> | ||
| 145 | + <section @click="handleViewBoard(item)"> | ||
| 146 | + <div class="flex justify-between items-center"> | ||
| 147 | + <div>{{ item.name }}</div> | ||
| 148 | + <div class="flex content-center"> | ||
| 149 | + <Statistic value="12"> | ||
| 150 | + <template #suffix> | ||
| 151 | + <span class="text-sm">个组件</span> | ||
| 152 | + </template> | ||
| 153 | + </Statistic> | ||
| 154 | + </div> | ||
| 150 | </div> | 155 | </div> |
| 151 | - </div> | ||
| 152 | - <div class="flex justify-between mt-4"> | ||
| 153 | - <div> | ||
| 154 | - <span> | ||
| 155 | - {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }} | ||
| 156 | - </span> | ||
| 157 | - <span v-if="item.viewType === ViewType.PUBLIC_VIEW"> | ||
| 158 | - <Tooltip title="分享链接"> | ||
| 159 | - <ShareAltOutlined class="ml-2" @click="handleCopyShareUrl(item)" /> | ||
| 160 | - </Tooltip> | ||
| 161 | - </span> | 156 | + <div class="flex justify-between mt-4"> |
| 157 | + <div> | ||
| 158 | + <span> | ||
| 159 | + {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }} | ||
| 160 | + </span> | ||
| 161 | + <span v-if="item.viewType === ViewType.PUBLIC_VIEW"> | ||
| 162 | + <Tooltip title="分享链接"> | ||
| 163 | + <ShareAltOutlined class="ml-2" @click="handleCopyShareUrl(item)" /> | ||
| 164 | + </Tooltip> | ||
| 165 | + </span> | ||
| 166 | + </div> | ||
| 167 | + <div>{{ item.updateTime || item.createTime }}</div> | ||
| 162 | </div> | 168 | </div> |
| 163 | - <div>{{ item.updateTime || item.createTime }}</div> | ||
| 164 | - </div> | ||
| 165 | - </section> | ||
| 166 | - </Card> | ||
| 167 | - </ListItem> | ||
| 168 | - </template> | ||
| 169 | - </List> | 169 | + </section> |
| 170 | + </Card> | ||
| 171 | + </ListItem> | ||
| 172 | + </template> | ||
| 173 | + </List> | ||
| 174 | + </Spin> | ||
| 170 | <PanelDetailModal @register="registerModal" @change="getDatasource" /> | 175 | <PanelDetailModal @register="registerModal" @change="getDatasource" /> |
| 171 | </PageWrapper> | 176 | </PageWrapper> |
| 172 | </template> | 177 | </template> |