Commit e0535b05124400b672355f4c9ab5a856d9a5143c

Authored by ww
1 parent 0814f5f9

feat: implement picture component

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, rect, size) => {
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 :id="getControlsWidgetId()" class="widget-charts"></div>
91   -</template>
92   -
93   -<style scoped>
94   - .widget-charts {
95   - min-width: 10px;
96   - min-height: 10px;
97   - width: 100%;
98   - height: 100%;
99   - }
100   -
101   - .widget-charts > div {
102   - width: 100%;
103   - height: 100%;
104   - }
105   -</style>
  1 +<script lang="ts" setup>
  2 + import { computed } from 'vue';
  3 + import { Tooltip, Image } from 'ant-design-vue';
  4 + import {
  5 + DEFAULT_DATE_FORMAT,
  6 + DEFAULT_RADIO_RECORD,
  7 + fontSize,
  8 + RadioRecord,
  9 + } from '../../detail/config/util';
  10 + import { PictureComponentLayout, PictureComponentValue } from './pictureComponent.config';
  11 + import { dateUtil } from '/@/utils/dateUtil';
  12 +
  13 + const props = defineProps<{
  14 + layout: PictureComponentLayout;
  15 + value: PictureComponentValue;
  16 + radio: RadioRecord;
  17 + }>();
  18 +
  19 + const fallback =
  20 + 'data:image/png;base64,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';
  21 +
  22 + const getRadio = computed(() => {
  23 + const { radio } = props.radio || DEFAULT_RADIO_RECORD;
  24 + console.log(props.radio);
  25 + return radio;
  26 + });
  27 +
  28 + const getWidth = computed(() => {
  29 + const marign = 5;
  30 + const { width = 180 } = props.radio || {};
  31 + return width - 5;
  32 + });
  33 +</script>
  34 +
  35 +<template>
  36 + <section class="w-full h-full flex flex-col justify-center items-center">
  37 + <Image :width="getWidth" :src="props?.value?.value || ''" :fallback="fallback" />
  38 + <div
  39 + class="w-full text-center text-gray-400 truncate"
  40 + :style="{ fontSize: fontSize({ radio: getRadio, basic: 12, max: 16 }) }"
  41 + >
  42 + <Tooltip
  43 + placement="topLeft"
  44 + :title="props?.value?.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT)"
  45 + >
  46 + <span>更新时间:</span>
  47 + <span class="truncate">
  48 + {{ props?.value?.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT) }}
  49 + </span>
  50 + </Tooltip>
  51 + </div>
  52 + </section>
  53 +</template>
  54 +
  55 +<style scoped></style>
... ...
  1 +import PictureComponent from './PictureComponent.vue';
  2 +
  3 +import { PictureComponentType } from './pictureComponent.config';
  4 +import { Component } from 'vue';
  5 +
  6 +interface PictureComponentList {
  7 + id: PictureComponentType;
  8 + component: Component;
  9 +}
  10 +// {
  11 +// id: 'instrument-component-1',
  12 +// layout: { chartOption: instrumentComponent1() },
  13 +// component: DashBoardComponent,
  14 +// value: { id: buildUUID() },
  15 +// }
  16 +const pictureComponentList: PictureComponentList[] = [
  17 + {
  18 + id: 'picture-component-1',
  19 + component: PictureComponent,
  20 + },
  21 +];
  22 +
  23 +export { PictureComponent, pictureComponentList };
... ...
  1 +import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
  2 +
  3 +export type PictureComponentType = 'picture-component-1';
  4 +
  5 +export interface PictureComponentLayout {}
  6 +
  7 +export interface PictureComponentValue {
  8 + value?: string;
  9 + updateTime?: string;
  10 +}
  11 +
  12 +export const transformPictureConfig = (
  13 + config: PictureComponentLayout,
  14 + record: DataComponentRecord,
  15 + dataSourceRecord: DataSource
  16 +) => {
  17 + return {};
  18 +};
... ...
... ... @@ -4,13 +4,15 @@
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 { reactive, ref, shallowReactive, unref, nextTick, watch } from 'vue';
  7 + import { 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 11 import type { ComponentInfo, DataSource } from '/@/api/dataBoard/model';
12 12 import { useMessage } from '/@/hooks/web/useMessage';
13 13 import { DataBoardLayoutInfo } from '../../types/type';
  14 + import { FrontComponent } from '../config/help';
  15 + import { computed } from '@vue/reactivity';
14 16
15 17 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> };
16 18
... ... @@ -133,6 +135,10 @@
133 135 });
134 136 };
135 137
  138 + const showSettingButton = computed(() => {
  139 + return props.frontId !== FrontComponent.PICTURE_COMPONENT_1;
  140 + });
  141 +
136 142 watch(
137 143 () => props.record,
138 144 () => {
... ... @@ -183,6 +189,7 @@
183 189 </Tooltip>
184 190 <Tooltip title="设置">
185 191 <SettingOutlined
  192 + v-show="showSettingButton"
186 193 @click="handleSetting(item)"
187 194 class="cursor-pointer text-lg !leading-30px"
188 195 />
... ...
... ... @@ -4,6 +4,7 @@
4 4 import TextComponent from '../../components/TextComponent/TextComponent.vue';
5 5 import { textComponentConfig } from '../../components/TextComponent/config';
6 6 import { instrumentComponentConfig } from '../../components/InstrumentComponent';
  7 + import { pictureComponentList } from '../../components/PictureComponent';
7 8 const props = defineProps<{
8 9 value: string;
9 10 }>();
... ... @@ -35,7 +36,25 @@
35 36 </template>
36 37 </List>
37 38 </Tabs.TabPane>
38   - <Tabs.TabPane key="2" tab="仪表组件">
  39 + <Tabs.TabPane key="2" tab="图片组件">
  40 + <List
  41 + :grid="{ gutter: 10, column: 3, xs: 3, sm: 3, md: 3, lg: 3, xl: 3, xxl: 3 }"
  42 + :data-source="pictureComponentList"
  43 + >
  44 + <template #renderItem="{ item }">
  45 + <List.Item class="!flex !justify-center">
  46 + <VisualWidgetSelect
  47 + :checked-id="props.value"
  48 + :control-id="item.id"
  49 + @change="handleCheck"
  50 + >
  51 + <component :is="item.component" />
  52 + </VisualWidgetSelect>
  53 + </List.Item>
  54 + </template>
  55 + </List>
  56 + </Tabs.TabPane>
  57 + <Tabs.TabPane key="3" tab="仪表组件">
39 58 <List
40 59 :grid="{ gutter: 10, column: 3, xs: 3, sm: 3, md: 3, lg: 3, xl: 3, xxl: 3 }"
41 60 :data-source="instrumentComponentConfig"
... ...
... ... @@ -18,7 +18,7 @@ export const basicSchema: FormSchema[] = [
18 18 },
19 19 },
20 20 {
21   - field: 'remake',
  21 + field: 'remark',
22 22 label: '组件备注',
23 23 component: 'InputTextArea',
24 24 componentProps: {
... ...
... ... @@ -10,6 +10,8 @@ import {
10 10 } from '../../components/TextComponent/config';
11 11 import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
12 12 import DashBoardComponent from '../../components/InstrumentComponent/DashBoardComponent.vue';
  13 +import PictureComponent from '../../components/PictureComponent/PictureComponent.vue';
  14 +import { transformPictureConfig } from '../../components/PictureComponent/pictureComponent.config';
13 15 import { WidgetComponentType } from './visualOptions';
14 16 import {
15 17 DashboardComponentLayout,
... ... @@ -27,6 +29,7 @@ export enum FrontComponent {
27 29 INSTRUMENT_COMPONENT_1 = 'instrument-component-1',
28 30 INSTRUMENT_COMPONENT_2 = 'instrument-component-2',
29 31 DIGITAL_DASHBOARD_COMPONENT = 'digital-dashboard-component',
  32 + PICTURE_COMPONENT_1 = 'picture-component-1',
30 33 }
31 34
32 35 export interface ComponentConfig {
... ... @@ -94,3 +97,9 @@ frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, {
94 97 ComponentConfig: {},
95 98 transformConfig: transformDashboardComponentConfig,
96 99 });
  100 +
  101 +frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, {
  102 + Component: PictureComponent,
  103 + ComponentConfig: {},
  104 + transformConfig: transformPictureConfig,
  105 +});
... ...
1 1 import { InstrumentComponentType } from '../../components/InstrumentComponent/dashBoardComponent.config';
2 2 import { DigitalDashBoardComponentType } from '../../components/InstrumentComponent/digitalDashBoard.config';
  3 +import { PictureComponentType } from '../../components/PictureComponent/pictureComponent.config';
3 4 import { TextComponentType } from '../../components/TextComponent/config';
4 5 import { FormSchema } from '/@/components/Form';
5 6
6 7 export type WidgetComponentType =
7 8 | TextComponentType
8 9 | InstrumentComponentType
9   - | DigitalDashBoardComponentType;
  10 + | DigitalDashBoardComponentType
  11 + | PictureComponentType;
10 12
11 13 export interface VisualOptionParams {
12 14 [visualOptionField.FONT_COLOR]: string;
... ...