Commit 128a5ebdb2fc8c44107a4752d92b422ac4e8c83c

Authored by ww
1 parent 29b5c630

wip: implement digitalDashboardComponent && DashboardComponent

... ... @@ -7,6 +7,7 @@ import {
7 7 Layout,
8 8 UpdateDataBoardLayoutParams,
9 9 UpdateDataBoardParams,
  10 + UpdateDataComponentParams,
10 11 } from './model';
11 12 import { defHttp } from '/@/utils/http/axios';
12 13
... ... @@ -22,6 +23,7 @@ enum DataComponentUrl {
22 23 GET_DATA_COMPONENT = '/data_component',
23 24 ADD_DATA_COMPONENT = '/data_component',
24 25 DELETE_DATA_COMPONENT = '/data_component',
  26 + UPDATE_DATA_COMPONENT = '/data_component',
25 27 }
26 28
27 29 /**
... ... @@ -118,3 +120,15 @@ export const deleteDataComponent = (params: string[]) => {
118 120 },
119 121 });
120 122 };
  123 +
  124 +/**
  125 + * @description 更新数据组件
  126 + * @param params
  127 + * @returns
  128 + */
  129 +export const updateDataComponent = (params: UpdateDataComponentParams) => {
  130 + return defHttp.post({
  131 + url: `${DataComponentUrl.UPDATE_DATA_COMPONENT}/${params.boardId}/update`,
  132 + params: params.record,
  133 + });
  134 +};
... ...
... ... @@ -111,3 +111,8 @@ export interface UpdateDataBoardLayoutParams {
111 111 boardId: string;
112 112 layout: Layout[];
113 113 }
  114 +
  115 +export interface UpdateDataComponentParams {
  116 + boardId: string;
  117 + record: Partial<DataComponentRecord>;
  118 +}
... ...
... ... @@ -3,67 +3,31 @@
3 3 import type { PropType } from 'vue';
4 4 import { nextTick, onMounted, onUnmounted, ref, unref } from 'vue';
5 5 import { init } from 'echarts';
6   -
7   - interface DataSource {
8   - id: string | number;
9   - }
  6 + import { instrumentComponent1 } from './dashBoardComponent.config';
  7 + import { dateUtil } from '/@/utils/dateUtil';
10 8
11 9 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 10 add: {
21 11 type: Function,
22   - required: true,
  12 + },
  13 + layout: {
  14 + type: Object as PropType<Recordable>,
  15 + default: () => ({}),
  16 + },
  17 + value: {
  18 + type: Object as PropType<Recordable>,
  19 + default: () => ({}),
23 20 },
24 21 });
25 22
26   - const getControlsWidgetId = () => `widget-chart-${props.dataSource.id}`;
  23 + const getControlsWidgetId = () => `widget-chart-${props.value.id}`;
27 24
28 25 const chartRef = ref<Nullable<ECharts>>(null);
29 26
30 27 function initChart() {
31 28 const chartDom = document.getElementById(getControlsWidgetId())!;
32 29 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   - };
  30 + const option: EChartsOption = props.layout || instrumentComponent1();
67 31
68 32 nextTick(() => {
69 33 option && unref(chartRef)?.setOption(option);
... ... @@ -76,7 +40,7 @@
76 40
77 41 onMounted(() => {
78 42 initChart();
79   - props.add(props.dataSource.id, update);
  43 + props.add && props.add(props.value.id, update);
80 44 });
81 45
82 46 onUnmounted(() => {
... ... @@ -89,7 +53,13 @@
89 53 <template>
90 54 <div class="flex flex-col w-full h-full min-w-3 min-h-3">
91 55 <div :id="getControlsWidgetId()" class="widget-charts w-full h-full"></div>
92   - <div class="text-xs text-center text-gray-400">更新时间:</div>
  56 + <div>{{}}</div>
  57 + <div class="text-xs text-center text-gray-400">
  58 + <span>更新时间:</span>
  59 + <span>
  60 + {{ props.value.updateTime || dateUtil().format('YYYY-MM-DD HH:mm:ss') }}
  61 + </span>
  62 + </div>
93 63 </div>
94 64 </template>
95 65
... ...
1   -<script lang="ts" setup></script>
  1 +<script lang="ts" setup>
  2 + import { computed } from 'vue';
  3 + import { Space } from 'ant-design-vue';
  4 + import type { DigitalDashBoardLayout, DigitalDashBoardValue } from './digitalDashBoard.config';
  5 + import { dateUtil } from '/@/utils/dateUtil';
2 6
3   -<template> </template>
  7 + const props = defineProps<{
  8 + layout: DigitalDashBoardLayout;
  9 + value: DigitalDashBoardValue;
  10 + }>();
  11 +
  12 + const integerPart = computed(() => {
  13 + const { value = 0 } = props.value;
  14 + const { max = 5 } = props.layout;
  15 + let _value = value?.toFixed(2).split('.')[0];
  16 + if (_value.length < max) _value = _value.padStart(5, '0');
  17 +
  18 + if (_value.length > max) _value = ''.padStart(5, '9');
  19 +
  20 + return _value;
  21 + });
  22 +
  23 + const decimalPart = computed(() => {
  24 + const { value = 0 } = props.value;
  25 + const { keepNumber = 2 } = props.layout;
  26 + let _value = value?.toFixed(2).split('.')[1];
  27 + if (_value.length < keepNumber) _value = _value.padStart(5, '0');
  28 +
  29 + if (_value.length > keepNumber) _value = ''.padStart(5, '0');
  30 +
  31 + return _value;
  32 + });
  33 +</script>
  34 +
  35 +<template>
  36 + <section class="w-full h-full">
  37 + <div class="flex flex-col w-full h-full">
  38 + <div class="flex-1 flex justify-center items-center">
  39 + <div class="flex flex-col">
  40 + <Space justify="end" class="justify-end">
  41 + <div
  42 + v-for="number in integerPart"
  43 + :key="number"
  44 + class="border border-gray-400 p-2"
  45 + :style="{ color: props.value.valueColor }"
  46 + >
  47 + {{ number }}
  48 + </div>
  49 + </Space>
  50 + <Space justify="end" class="justify-end mt-2">
  51 + <div
  52 + v-for="number in decimalPart"
  53 + :key="number"
  54 + class="border border-gray-400 p-1"
  55 + :style="{ color: props.value.valueColor }"
  56 + >
  57 + {{ number }}
  58 + </div>
  59 + </Space>
  60 + </div>
  61 + </div>
  62 +
  63 + <div class="text-center">
  64 + <span>{{ props.value.name || '电表' }}</span>
  65 + <span class="px-1">({{ props.value.unit || 'kw/h' }})</span>
  66 + </div>
  67 + <div class="text-center mt-1 text-gray-400 text-xs">
  68 + <span class="mr-1">更新时间:</span>
  69 + <span>{{ props.value.updateTime || dateUtil().format('YYYY-MM-DD HH:mm:ss') }}</span>
  70 + </div>
  71 + </div>
  72 + <div></div>
  73 + </section>
  74 +</template>
... ...
  1 +import { EChartsOption } from 'echarts';
  2 +import { visualOptionField } from '../../detail/config/visualOptions';
  3 +
  4 +export type InstrumentComponentType = 'instrument-component-1' | 'instrument-component-2';
  5 +
  6 +export type GradientKey =
  7 + | visualOptionField.FIRST_PHASE_COLOR
  8 + | visualOptionField.FIRST_PHASE_VALUE
  9 + | visualOptionField.SECOND_PHASE_COLOR
  10 + | visualOptionField.SECOND_PHASE_VALUE
  11 + | visualOptionField.THIRD_PHASE_COLOR
  12 + | visualOptionField.THIRD_PHASE_VALUE;
  13 +export interface GradientInfoRecord {
  14 + key: GradientKey;
  15 + value: number | string;
  16 +}
  17 +
  18 +export interface DashBoardValue {
  19 + unit?: string;
  20 + name?: string;
  21 + updateTime?: string;
  22 + value?: number;
  23 + valueColor?: string;
  24 + gradientInfo?: GradientInfoRecord[];
  25 +}
  26 +
  27 +export const instrumentComponent1 = (params?: { value: number; unit: string }): EChartsOption => {
  28 + const { value = 10, unit = '°C' } = params || {};
  29 + return {
  30 + series: [
  31 + {
  32 + type: 'gauge',
  33 + center: ['50%', '60%'],
  34 + startAngle: 200,
  35 + endAngle: -20,
  36 + min: 0,
  37 + max: 60,
  38 + splitNumber: 12,
  39 + itemStyle: {
  40 + color: '#FFAB91',
  41 + },
  42 + progress: {
  43 + show: true,
  44 + width: 30,
  45 + },
  46 + pointer: {
  47 + show: false,
  48 + },
  49 + axisLine: {
  50 + lineStyle: {
  51 + width: 30,
  52 + },
  53 + },
  54 + axisTick: {
  55 + distance: -45,
  56 + splitNumber: 5,
  57 + lineStyle: {
  58 + width: 2,
  59 + color: '#999',
  60 + },
  61 + },
  62 + splitLine: {
  63 + distance: -52,
  64 + length: 14,
  65 + lineStyle: {
  66 + width: 3,
  67 + color: '#999',
  68 + },
  69 + },
  70 + axisLabel: {
  71 + distance: -20,
  72 + color: '#999',
  73 + fontSize: 20,
  74 + },
  75 + anchor: {
  76 + show: false,
  77 + },
  78 + title: {
  79 + show: false,
  80 + },
  81 + detail: {
  82 + valueAnimation: true,
  83 + width: '60%',
  84 + lineHeight: 40,
  85 + borderRadius: 8,
  86 + offsetCenter: [0, '-15%'],
  87 + fontSize: 16,
  88 + fontWeight: 'bolder',
  89 + formatter: `{value} ${unit}`,
  90 + color: 'auto',
  91 + },
  92 + data: [
  93 + {
  94 + value: value,
  95 + },
  96 + ],
  97 + },
  98 + {
  99 + type: 'gauge',
  100 + center: ['50%', '60%'],
  101 + startAngle: 200,
  102 + endAngle: -20,
  103 + min: 0,
  104 + max: 60,
  105 + itemStyle: {
  106 + color: '#FD7347',
  107 + },
  108 + progress: {
  109 + show: true,
  110 + width: 8,
  111 + },
  112 + pointer: {
  113 + show: false,
  114 + },
  115 + axisLine: {
  116 + show: false,
  117 + },
  118 + axisTick: {
  119 + show: false,
  120 + },
  121 + splitLine: {
  122 + show: false,
  123 + },
  124 + axisLabel: {
  125 + show: false,
  126 + },
  127 + detail: {
  128 + show: false,
  129 + },
  130 + data: [
  131 + {
  132 + value: value,
  133 + },
  134 + ],
  135 + },
  136 + ],
  137 + };
  138 +};
  139 +
  140 +export const instrumentComponent2 = (params?: {
  141 + gradient: GradientInfoRecord[];
  142 + value: number;
  143 + unit: string;
  144 +}): EChartsOption => {
  145 + const { gradient = [], value = 0, unit = 'km/h' } = params || {};
  146 + return {
  147 + series: [
  148 + {
  149 + type: 'gauge',
  150 + axisLine: {
  151 + lineStyle: {
  152 + width: 30,
  153 + color: [
  154 + [
  155 + 0.3,
  156 + (getGradientValue(visualOptionField.FIRST_PHASE_COLOR, gradient) as string) ||
  157 + '#67e0e3',
  158 + ],
  159 + [
  160 + 0.7,
  161 + (getGradientValue(visualOptionField.SECOND_PHASE_COLOR, gradient) as string) ||
  162 + '#37a2da',
  163 + ],
  164 + [
  165 + 1,
  166 + (getGradientValue(visualOptionField.THIRD_PHASE_COLOR, gradient) as string) ||
  167 + '#fd666d',
  168 + ],
  169 + ],
  170 + },
  171 + },
  172 + pointer: {
  173 + itemStyle: {
  174 + color: 'auto',
  175 + },
  176 + },
  177 + axisTick: {
  178 + distance: -30,
  179 + length: 8,
  180 + lineStyle: {
  181 + color: '#fff',
  182 + width: 2,
  183 + },
  184 + },
  185 + splitLine: {
  186 + distance: -30,
  187 + length: 30,
  188 + lineStyle: {
  189 + color: '#fff',
  190 + width: 4,
  191 + },
  192 + },
  193 + axisLabel: {
  194 + color: 'auto',
  195 + distance: 40,
  196 + fontSize: 14,
  197 + },
  198 + detail: {
  199 + valueAnimation: true,
  200 + formatter: `{value} ${unit}`,
  201 + color: 'auto',
  202 + fontSize: '16',
  203 + },
  204 + data: [
  205 + {
  206 + value: value,
  207 + },
  208 + ],
  209 + },
  210 + ],
  211 + };
  212 +};
  213 +
  214 +export const getGradientValue = (key: GradientKey, record: GradientInfoRecord[]) => {
  215 + return record.find((item) => item.key === key)?.value;
  216 +};
... ...
  1 +export type DigitalDashBoardComponentType = 'digital-dashboard';
  2 +
  3 +export interface DigitalDashBoardLayout {
  4 + max: number;
  5 + keepNumber: number;
  6 +}
  7 +
  8 +export interface DigitalDashBoardValue {
  9 + unit?: string;
  10 + name?: string;
  11 + updateTime?: string;
  12 + value?: number;
  13 + valueColor?: string;
  14 +}
... ...
  1 +import { Component } from 'vue';
  2 +import { WidgetComponentType } from '../../detail/config/visualOptions';
  3 +import { instrumentComponent1, instrumentComponent2 } from './dashBoardComponent.config';
  4 +import DashBoardComponent from './DashBoardComponent.vue';
  5 +import DigitalDashBoard from './DigitalDashBoard.vue';
  6 +import { buildUUID } from '/@/utils/uuid';
  7 +
  8 +interface InstrumentComponentConfig {
  9 + id: WidgetComponentType;
  10 + layout: Recordable;
  11 + component: Component;
  12 + value: Recordable;
  13 +}
  14 +
  15 +export const instrumentComponentConfig: InstrumentComponentConfig[] = [
  16 + {
  17 + id: 'instrument-component-1',
  18 + layout: instrumentComponent1(),
  19 + component: DashBoardComponent,
  20 + value: { id: buildUUID() },
  21 + },
  22 + {
  23 + id: 'instrument-component-2',
  24 + layout: instrumentComponent2(),
  25 + component: DashBoardComponent,
  26 + value: { id: buildUUID() },
  27 + },
  28 + {
  29 + id: 'digital-dashboard',
  30 + layout: {},
  31 + component: DigitalDashBoard,
  32 + value: {},
  33 + },
  34 +];
... ...
... ... @@ -18,6 +18,13 @@ export interface TextComponentValue {
18 18 iconColor?: string;
19 19 }
20 20
  21 +export type TextComponentType =
  22 + | 'text-component-1'
  23 + | 'text-component-2'
  24 + | 'text-component-3'
  25 + | 'text-component-4'
  26 + | 'text-component-5';
  27 +
21 28 type TextComponentDefault = TextComponentLayout & { value: TextComponentValue };
22 29
23 30 export const TextComponent1Config: TextComponentDefault = {
... ...
... ... @@ -10,8 +10,6 @@
10 10
11 11 const slot = useSlots();
12 12
13   - console.log({ dataSource: props.dataSource });
14   -
15 13 const { update, add, remove } = useUpdateCenter();
16 14
17 15 onMounted(() => {
... ...
... ... @@ -4,27 +4,28 @@
4 4 import { FormActionType, useForm } from '/@/components/Form';
5 5 import { basicSchema, dataSourceSchema } from '../config/basicConfiguration';
6 6 import BasicForm from '/@/components/Form/src/BasicForm.vue';
7   - import { onMounted, reactive, ref, shallowReactive, unref, nextTick } from 'vue';
  7 + import { reactive, ref, shallowReactive, unref, nextTick, watch } from 'vue';
8 8 import VisualOptionsModal from './VisualOptionsModal.vue';
9 9 import { useModal } from '/@/components/Modal';
10 10 import { buildUUID } from '/@/utils/uuid';
11   - import type { DataComponentRecord, ComponentInfo, DataSource } from '/@/api/dataBoard/model';
  11 + import type { ComponentInfo, DataSource } from '/@/api/dataBoard/model';
12 12 import { useMessage } from '/@/hooks/web/useMessage';
  13 + import { DataBoardLayoutInfo } from '../../types/type';
13 14
14 15 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> };
15 16
16 17 type DataSourceEl = DataSource & { id: string };
17 18
18 19 const props = defineProps<{
19   - record: DataComponentRecord;
  20 + record: DataBoardLayoutInfo;
20 21 frontId?: string;
21 22 }>();
22 23
23 24 const { createMessage } = useMessage();
24 25
25   - const componentRecord = reactive<DataComponentRecord>({
26   - id: 'string',
27   - } as unknown as DataComponentRecord);
  26 + // const componentRecord = reactive<DataBoardLayoutInfo>({
  27 + // ...props.record,
  28 + // } as unknown as DataBoardLayoutInfo);
28 29
29 30 const dataSource = ref<DataSourceEl[]>([{ id: buildUUID() } as unknown as DataSourceEl]);
30 31
... ... @@ -38,7 +39,6 @@
38 39
39 40 const setFormEl = (el: any, id: string) => {
40 41 if (!dataSourceEl[id] && el) {
41   - console.log({ el, id });
42 42 const { formActionType } = el as unknown as { formActionType: FormActionType };
43 43 dataSourceEl[id] = formActionType;
44 44 }
... ... @@ -59,12 +59,12 @@
59 59 for (const id of hasExistEl) {
60 60 const index = unref(dataSource).findIndex((item) => item.id === id);
61 61 const value = (dataSourceEl[id] as FormActionType).getFieldsValue() as DataSource;
  62 + if (!~index) continue;
62 63 const componentInfo = unref(dataSource)[index].componentInfo || {};
63   - ~index &&
64   - _dataSource.push({
65   - ...value,
66   - componentInfo: { ...componentInfo },
67   - });
  64 + _dataSource.push({
  65 + ...value,
  66 + componentInfo: { ...componentInfo },
  67 + });
68 68 }
69 69 return _dataSource;
70 70 };
... ... @@ -113,19 +113,38 @@
113 113 };
114 114
115 115 const echoDataSource = () => {
116   - basicMethod.setFieldsValue(props.record);
117   - // dataSourceMethod.setFieldsValue(props.record);
  116 + basicMethod.setFieldsValue(props.record.record);
  117 + dataSource.value = [];
  118 + dataSource.value = props.record.record.dataSource.map((item) => {
  119 + const id = buildUUID();
  120 +
  121 + dataSource.value.push({
  122 + id,
  123 + ...item,
  124 + });
  125 +
  126 + nextTick(() => {
  127 + (dataSourceEl[id] as FormActionType).setFieldsValue(item);
  128 + });
  129 + return {
  130 + id,
  131 + ...item,
  132 + };
  133 + });
118 134 };
119 135
  136 + watch(
  137 + () => props.record,
  138 + () => {
  139 + if (Object.keys(props.record).length) echoDataSource();
  140 + }
  141 + );
  142 +
120 143 const handleRowComponentInfo = (recordId: string, value: ComponentInfo) => {
121 144 const index = unref(dataSource).findIndex((item) => item.id === recordId);
122 145 ~index && (unref(dataSource)[index].componentInfo = value);
123 146 };
124 147
125   - onMounted(() => {
126   - echoDataSource();
127   - });
128   -
129 148 defineExpose({
130 149 getAllDataSourceFieldValue,
131 150 });
... ... @@ -179,7 +198,11 @@
179 198 <div class="text-center">
180 199 <Button type="primary" @click="handleAdd">添加数据源</Button>
181 200 </div>
182   - <VisualOptionsModal @close="handleRowComponentInfo" @register="registerVisualOptionModal" />
  201 + <VisualOptionsModal
  202 + :value="props.frontId"
  203 + @close="handleRowComponentInfo"
  204 + @register="registerVisualOptionModal"
  205 + />
183 206 </section>
184 207 </template>
185 208
... ...
... ... @@ -4,11 +4,12 @@
4 4 import BasicConfiguration from './BasicConfiguration.vue';
5 5 import VisualConfiguration from './VisualConfiguration.vue';
6 6 import { computed, ref, unref } from 'vue';
7   - import type { DataComponentRecord } from '/@/api/dataBoard/model';
8 7 import { RouteParams, useRoute } from 'vue-router';
9   - import { addDataComponent, updateDataBoardLayout } from '/@/api/dataBoard';
  8 + import { addDataComponent, updateDataBoardLayout, updateDataComponent } from '/@/api/dataBoard';
10 9 import { useModalInner } from '/@/components/Modal';
11 10 import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH } from '../../config/config';
  11 + import { DataBoardLayoutInfo } from '../../types/type';
  12 + import { useMessage } from '/@/hooks/web/useMessage';
12 13
13 14 interface DataComponentRouteParams extends RouteParams {
14 15 id: string;
... ... @@ -18,11 +19,7 @@
18 19
19 20 const ROUTE = useRoute();
20 21
21   - const [register, { closeModal }] = useModalInner();
22   -
23   - const basicConfigurationEl = ref<{
24   - getAllDataSourceFieldValue: Fn<any, Recordable>;
25   - }>();
  22 + const { createMessage } = useMessage();
26 23
27 24 const boardId = computed(() => {
28 25 return (ROUTE.params as DataComponentRouteParams).id;
... ... @@ -30,12 +27,26 @@
30 27
31 28 const frontId = ref('');
32 29
33   - const componentRecord = ref<DataComponentRecord>({} as unknown as DataComponentRecord);
  30 + const isEdit = ref(false);
  31 +
  32 + const componentRecord = ref<DataBoardLayoutInfo>({} as unknown as DataBoardLayoutInfo);
  33 +
  34 + const [register, { closeModal }] = useModalInner(
  35 + (record: DataBoardLayoutInfo & { isEdit: boolean }) => {
  36 + componentRecord.value = record;
  37 + frontId.value = record.record.frontId;
  38 + isEdit.value = record.isEdit;
  39 + }
  40 + );
  41 +
  42 + const basicConfigurationEl = ref<{
  43 + getAllDataSourceFieldValue: Fn<any, Recordable>;
  44 + }>();
34 45
35 46 const handleSubmit = () => {
36 47 const { getAllDataSourceFieldValue } = unref(basicConfigurationEl)!;
37 48 const value = getAllDataSourceFieldValue();
38   - handleAddComponent(value);
  49 + unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value);
39 50 };
40 51
41 52 const handleAddComponent = async (value: Recordable) => {
... ... @@ -44,6 +55,7 @@
44 55 boardId: unref(boardId),
45 56 record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value },
46 57 });
  58 + createMessage.success('创建成功');
47 59 const id = data.data.id;
48 60 await updateDataBoardLayout({
49 61 boardId: unref(boardId),
... ... @@ -51,7 +63,28 @@
51 63 });
52 64 closeModal();
53 65 emit('submit');
54   - } catch (error) {}
  66 + } catch (error) {
  67 + // createMessage.error('创建失败');
  68 + }
  69 + };
  70 +
  71 + const handleUpdateComponent = async (value: Recordable) => {
  72 + try {
  73 + await updateDataComponent({
  74 + boardId: unref(boardId),
  75 + record: {
  76 + id: unref(componentRecord).i,
  77 + dataBoardId: unref(boardId),
  78 + frontId: unref(frontId),
  79 + ...value,
  80 + },
  81 + });
  82 + createMessage.success('修改成功');
  83 + closeModal();
  84 + emit('submit');
  85 + } catch (error) {
  86 + // createMessage.error('修改失败');
  87 + }
55 88 };
56 89 </script>
57 90
... ... @@ -61,6 +94,7 @@
61 94 @register="register"
62 95 title="自定义组件"
63 96 width="70%"
  97 + destroy-on-close
64 98 @ok="handleSubmit"
65 99 >
66 100 <section>
... ...
... ... @@ -3,6 +3,7 @@
3 3 import VisualWidgetSelect from './VisualWidgetSelect.vue';
4 4 import TextComponent from '../../components/TextComponent/TextComponent.vue';
5 5 import { textComponentConfig } from '../../components/TextComponent/config';
  6 + import { instrumentComponentConfig } from '../../components/InstrumentComponent';
6 7 const props = defineProps<{
7 8 value: string;
8 9 }>();
... ... @@ -35,7 +36,22 @@
35 36 </List>
36 37 </Tabs.TabPane>
37 38 <Tabs.TabPane key="2" tab="仪表组件">
38   - <div>仪表组件</div>
  39 + <List
  40 + :grid="{ gutter: 10, column: 3, xs: 3, sm: 3, md: 3, lg: 3, xl: 3, xxl: 3 }"
  41 + :data-source="instrumentComponentConfig"
  42 + >
  43 + <template #renderItem="{ item }">
  44 + <List.Item class="!flex !justify-center">
  45 + <VisualWidgetSelect
  46 + :checked-id="props.value"
  47 + :control-id="item.id"
  48 + @change="handleCheck"
  49 + >
  50 + <component :is="item.component" :layout="item.layout" :value="item.value" />
  51 + </VisualWidgetSelect>
  52 + </List.Item>
  53 + </template>
  54 + </List>
39 55 </Tabs.TabPane>
40 56 </Tabs>
41 57 </section>
... ...
1 1 <script lang="ts" setup>
2   - import { onMounted, ref, unref } from 'vue';
3   - import { modeOne, modeTwo, modeThree, modeFour } from '../config/visualOptions';
  2 + import { ref, unref } from 'vue';
  3 + import { WidgetComponentType, schemasMap } from '../config/visualOptions';
4 4 import { useForm, BasicForm } from '/@/components/Form';
5 5 import { BasicModal, useModalInner } from '/@/components/Modal';
6 6 import { ComponentInfo } from '/@/api/dataBoard/model';
  7 + import { computed } from '@vue/reactivity';
7 8
8 9 const emit = defineEmits(['close']);
9 10
  11 + const props = defineProps<{
  12 + value?: string;
  13 + }>();
  14 +
10 15 const recordId = ref('');
11 16
  17 + const getSchemas = computed(() => {
  18 + return schemasMap.get((props.value as WidgetComponentType) || 'text-component-1');
  19 + });
  20 +
12 21 const [registerForm, method] = useForm({
13   - schemas: modeTwo,
14 22 showActionButtonGroup: false,
15 23 labelWidth: 120,
16 24 baseColProps: {
... ... @@ -45,6 +53,6 @@
45 53 title="选项"
46 54 width="60%"
47 55 >
48   - <BasicForm @register="registerForm" />
  56 + <BasicForm @register="registerForm" :schemas="getSchemas" />
49 57 </BasicModal>
50 58 </template>
... ...
  1 +import { InstrumentComponentType } from '../../components/InstrumentComponent/dashBoardComponent.config';
  2 +import { DigitalDashBoardComponentType } from '../../components/InstrumentComponent/digitalDashBoard.config';
  3 +import { TextComponentType } from '../../components/TextComponent/config';
1 4 import { FormSchema } from '/@/components/Form';
2 5 export enum defaultOptions {
3 6 fontColor = '#rer',
4 7 }
5 8
  9 +export type WidgetComponentType =
  10 + | TextComponentType
  11 + | InstrumentComponentType
  12 + | DigitalDashBoardComponentType;
  13 +
6 14 export enum visualOptionField {
7 15 FONT_COLOR = 'fontColor',
8 16 UNIT = 'unit',
... ... @@ -137,3 +145,13 @@ export const modeFour: FormSchema[] = [
137 145 },
138 146 },
139 147 ];
  148 +
  149 +export const schemasMap = new Map<WidgetComponentType, FormSchema[]>();
  150 +
  151 +schemasMap.set('text-component-1', modeOne);
  152 +schemasMap.set('text-component-2', modeOne);
  153 +schemasMap.set('text-component-3', modeOne);
  154 +schemasMap.set('text-component-4', modeTwo);
  155 +schemasMap.set('text-component-4', modeTwo);
  156 +schemasMap.set('instrument-component-1', modeOne);
  157 +schemasMap.set('instrument-component-2', modeThree);
... ...
1 1 <script lang="ts" setup>
2 2 import { Button, PageHeader } from 'ant-design-vue';
3   - import { GridItem, GridLayout, Layout } from 'vue3-grid-layout';
  3 + import { GridItem, GridLayout } from 'vue3-grid-layout';
4 4 import { nextTick, onMounted, ref } from 'vue';
5 5 import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue';
6 6 import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue';
7 7 import { DropMenu } from '/@/components/Dropdown';
8 8 import DataBindModal from './components/DataBindModal.vue';
9 9 import { useModal } from '/@/components/Modal';
10   - import { MoreActionEvent } from '../config/config';
11   - import { deleteDataComponent, getDataComponent } from '/@/api/dataBoard';
  10 + import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, MoreActionEvent } from '../config/config';
  11 + import {
  12 + addDataComponent,
  13 + deleteDataComponent,
  14 + getDataComponent,
  15 + updateDataBoardLayout,
  16 + } from '/@/api/dataBoard';
12 17 import { useRoute } from 'vue-router';
13 18 import { computed, unref } from '@vue/reactivity';
14   - import { DataComponentRecord, DataSource, Layout as LayoutRecord } from '/@/api/dataBoard/model';
  19 + import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
15 20 import { frontComponentMap, FrontComponentType } from './config/help';
16 21 import { useMessage } from '/@/hooks/web/useMessage';
17   - const handleBack = () => {};
18   -
19   - type DataBoardRecord = DataComponentRecord & { layout: LayoutRecord };
20   -
21   - type DataBoardLayoutInfo = Layout & {
22   - record: DataComponentRecord & { width: number; height: number };
23   - };
  22 + import { DataBoardLayoutInfo } from '../types/type';
24 23
25 24 const ROUTE = useRoute();
26 25
27   - const { createMessage } = useMessage();
  26 + const { createMessage, createConfirm } = useMessage();
28 27 const getBoardId = computed(() => {
29 28 return (ROUTE.params as { id: string }).id;
30 29 });
... ... @@ -74,7 +73,7 @@
74 73 height,
75 74 };
76 75 });
77   - console.log(unref(dataBoardList));
  76 +
78 77 nextTick(() => {
79 78 const updateFn = widgetEl.get(i);
80 79 if (updateFn) updateFn();
... ... @@ -83,6 +82,7 @@
83 82
84 83 const itemResized = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => {
85 84 updateSize(i, newH, newW, newHPx, newWPx);
  85 + console.log({ newH, newW, newHPx, newWPx });
86 86 };
87 87
88 88 const itemContainerResized = (
... ... @@ -111,8 +111,15 @@
111 111 const [register, { openModal }] = useModal();
112 112
113 113 const handleMoreAction = (event: DropMenu, id: string) => {
114   - if (event.event === MoreActionEvent.EDIT) openModal(true);
115   - if (event.event === MoreActionEvent.DELETE) handleDelete(id);
  114 + if (event.event === MoreActionEvent.DELETE) {
  115 + createConfirm({
  116 + iconType: 'warning',
  117 + content: '是否确认删除?',
  118 + onOk: () => handleDelete(id),
  119 + });
  120 + }
  121 + if (event.event === MoreActionEvent.EDIT) handleUpdate(id);
  122 + if (event.event === MoreActionEvent.COPY) handleCopy(id);
116 123 };
117 124
118 125 const handleOpenCreatePanel = () => {
... ... @@ -124,7 +131,12 @@
124 131 const data = await getDataComponent(unref(getBoardId));
125 132 dataBoardList.value = data.data.componentData.map((item) => {
126 133 const index = data.data.componentLayout.findIndex((each) => item.id === each.id);
127   - const layout = data.data.componentLayout[index];
  134 + let layout;
  135 + if (!~index) {
  136 + layout = {};
  137 + } else {
  138 + layout = data.data.componentLayout[index];
  139 + }
128 140 return {
129 141 i: item.id,
130 142 w: layout.w || defaultWidth,
... ... @@ -138,6 +150,7 @@
138 150 },
139 151 };
140 152 });
  153 + console.log(unref(dataBoardList));
141 154 } catch (error) {}
142 155 };
143 156
... ... @@ -153,13 +166,40 @@
153 166 return component?.transformConfig(component.ComponentConfig, record, dataSourceRecord);
154 167 };
155 168
  169 + const handleUpdate = async (id: string) => {
  170 + const record = unref(dataBoardList).find((item) => item.i === id);
  171 + openModal(true, { isEdit: true, ...record });
  172 + };
  173 +
  174 + const handleCopy = async (id: string) => {
  175 + const record = unref(dataBoardList).find((item) => item.i === id);
  176 + console.log({ record });
  177 + try {
  178 + const data = await addDataComponent({
  179 + boardId: unref(getBoardId),
  180 + record: {
  181 + dataBoardId: unref(getBoardId),
  182 + frontId: record?.record.frontId,
  183 + dataSource: record?.record.dataSource,
  184 + },
  185 + });
  186 + createMessage.success('复制成功');
  187 + const id = data.data.id;
  188 + await updateDataBoardLayout({
  189 + boardId: unref(getBoardId),
  190 + layout: [{ id, w: DEFAULT_WIDGET_WIDTH, h: DEFAULT_WIDGET_HEIGHT, x: 0, y: 0 }],
  191 + });
  192 + getDataBoardComponent();
  193 + } catch (error) {}
  194 + };
  195 +
156 196 const handleDelete = async (id: string) => {
157 197 try {
158 198 await deleteDataComponent([id]);
159 199 createMessage.success('删除成功');
160 200 await getDataBoardComponent();
161 201 } catch (error) {
162   - createMessage.error('删除失败');
  202 + // createMessage.error('删除失败');
163 203 }
164 204 };
165 205
... ... @@ -170,11 +210,14 @@
170 210
171 211 <template>
172 212 <section class="bg-light-50 flex flex-col overflow-hidden h-full w-full">
173   - <PageHeader title="水电表看板" @back="handleBack">
  213 + <PageHeader title="水电表看板">
174 214 <template #extra>
175 215 <Button type="primary" @click="handleOpenCreatePanel">创建组件</Button>
176 216 </template>
177   - <div>已创建组件: 3个</div>
  217 + <div>
  218 + <span class="mr-3 text-gray-400">已创建组件:</span>
  219 + <span class="text-cyan-400"> {{ dataBoardList.length }}个</span>
  220 + </div>
178 221 </PageHeader>
179 222 <section class="flex-1">
180 223 <GridLayout
... ...
... ... @@ -159,7 +159,7 @@
159 159 {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }}
160 160 </span>
161 161 <span v-if="item.viewType === ViewType.PUBLIC_VIEW">
162   - <Tooltip title="分享链接">
  162 + <Tooltip title="点击复制分享链接">
163 163 <ShareAltOutlined class="ml-2" @click="handleCopyShareUrl(item)" />
164 164 </Tooltip>
165 165 </span>
... ...
  1 +import { Layout } from 'vue3-grid-layout';
  2 +import { DataComponentRecord } from '/@/api/dataBoard/model';
  3 +
  4 +export type DataBoardLayoutInfo = Layout & {
  5 + record: DataComponentRecord & { width: number; height: number };
  6 +};
... ...