Commit 607e8c7249a0d04a01bee8be38073a152e97b32d

Authored by fengtao
2 parents c06c58dc 23345cac

Merge branch 'main' into ft_local_dev

Showing 53 changed files with 1447 additions and 691 deletions
... ... @@ -22,7 +22,6 @@
22 22 theme = htmlRoot = null;
23 23 }
24 24 })();
25   -
26 25 </script>
27 26 <div id="app">
28 27 <style>
... ... @@ -152,17 +151,17 @@
152 151 }
153 152 }
154 153 </style>
155   - <div class="app-loading">
  154 + <div id="first-screen-loading" class="app-loading">
156 155 <div class="app-loading-wrap">
157   - <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" />
  156 + <!-- <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" /> -->
158 157 <div class="app-loading-dots">
159 158 <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
160 159 </div>
161   - <div class="app-loading-title"><%= title %></div>
  160 + <!-- <div class="app-loading-title"><%= title %></div> -->
162 161 </div>
163 162 </div>
164 163 </div>
165   -
  164 +
166 165 <script type="module" src="/src/main.ts"></script>
167 166 </body>
168 167 </html>
... ...
1 1 import { defHttp } from '/@/utils/http/axios';
2   -import { FileUploadResponse } from './model/index';
  2 +import { FileUploadResponse, Platform } from './model/index';
3 3 enum API {
4 4 SELECT_DETAIL = '/enterprise/get',
5 5 UPDATE_DETAIL = '/enterprise/update',
... ... @@ -67,7 +67,7 @@ export const bgUpload = (file) => {
67 67
68 68 // 获取平台定制详情
69 69 export const getPlatForm = () => {
70   - return defHttp.get({
  70 + return defHttp.get<Platform>({
71 71 url: API.SELECT_PLATFORM,
72 72 });
73 73 };
... ...
... ... @@ -5,3 +5,17 @@ export interface FileUploadResponse {
5 5 size: number;
6 6 fileStaticUri: string;
7 7 }
  8 +
  9 +export interface Platform {
  10 + id: string;
  11 + creator: string;
  12 + createTime: string;
  13 + updater: string;
  14 + updateTime: string;
  15 + name: string;
  16 + logo: string;
  17 + background: string;
  18 + copyright: string;
  19 + presentedOurselves: string;
  20 + domain: string;
  21 +}
... ...
... ... @@ -10,6 +10,7 @@ export { default as ApiSelect } from './src/components/ApiSelect.vue';
10 10 export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
11 11 export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
12 12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
  13 +export { default as ApiUpload } from './src/components/ApiUpload.vue';
13 14
14 15 //注册自定义组件
15 16 export {
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import { UploadDragger, Spin } from 'ant-design-vue';
  8 + import { InboxOutlined } from '@ant-design/icons-vue';
  9 + import { useMessage } from '/@/hooks/web/useMessage';
  10 + import { isBoolean, isFunction } from '/@/utils/is';
  11 + import { computed, ref, unref } from 'vue';
  12 + import { cloneDeep } from 'lodash-es';
  13 + interface FileItem {
  14 + uid: string;
  15 + name?: string;
  16 + status?: string;
  17 + response?: string;
  18 + url?: string;
  19 + }
  20 +
  21 + const emit = defineEmits(['update:fileList']);
  22 +
  23 + const { createMessage } = useMessage();
  24 +
  25 + const loading = ref(false);
  26 +
  27 + const componentDisabled = ref(false);
  28 +
  29 + const setLoading = (spin: boolean) => {
  30 + loading.value = spin;
  31 + };
  32 +
  33 + const props = withDefaults(
  34 + defineProps<{
  35 + fileList?: FileItem[];
  36 + accept?: string;
  37 + maxSize?: number;
  38 + disabled?: boolean;
  39 + listType?: string;
  40 + multiple?: boolean;
  41 + showUploadList?: boolean | { showPreviewIcon?: boolean; showRemoveIcon?: boolean };
  42 + transformFile?: (file: File) => string | Blob | Promise<string | Blob | File>;
  43 + api: (file: string | Blob | Promise<string | Blob | File>) => Promise<FileItem>;
  44 + }>(),
  45 + {
  46 + fileList: () => [],
  47 + maxSize: 5 * 1024 * 1024,
  48 + showUploadList: () => ({ showPreviewIcon: true, showRemoveIcon: true }),
  49 + }
  50 + );
  51 +
  52 + const handleBeforeUpload = (file: File) => {
  53 + if (file.size > props.maxSize) {
  54 + createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024)}mb`);
  55 + return false;
  56 + }
  57 + handleUpload(file);
  58 + return false;
  59 + };
  60 +
  61 + const getDisabled = computed(() => {
  62 + const { disabled } = props;
  63 + if (isBoolean(disabled)) {
  64 + return disabled ? componentDisabled.value : disabled;
  65 + }
  66 + return componentDisabled.value;
  67 + });
  68 +
  69 + const handleUpload = async (file: File | string | Blob | Promise<string | Blob | File>) => {
  70 + try {
  71 + setLoading(true);
  72 + componentDisabled.value = true;
  73 + console.log({ componentDisabled: unref(componentDisabled), getDisabled: unref(getDisabled) });
  74 + const { transformFile, api } = props;
  75 + if (transformFile && isFunction(transformFile)) file = await transformFile(file as File);
  76 + if (api && isFunction(api)) {
  77 + const data = await api(file);
  78 + emit('update:fileList', cloneDeep([...props.fileList, data]));
  79 + }
  80 + } catch (error) {
  81 + window.console.error(error);
  82 + } finally {
  83 + setLoading(false);
  84 + componentDisabled.value = false;
  85 + }
  86 + };
  87 +
  88 + const handleRemove = (file: FileItem) => {
  89 + const _fileList = cloneDeep(props.fileList);
  90 + const index = _fileList.findIndex((item) => item.uid === file.uid);
  91 + ~index && _fileList.splice(index, 1);
  92 + emit('update:fileList', _fileList);
  93 + };
  94 +
  95 + const handlePreview = (file: FileItem) => {
  96 + console.log('preview', file);
  97 + };
  98 +
  99 + const handleDownload = (file: FileItem) => {
  100 + console.log('download', file);
  101 + };
  102 +</script>
  103 +
  104 +<template>
  105 + <UploadDragger
  106 + :file-list="props.fileList"
  107 + :disabled="getDisabled"
  108 + :before-upload="handleBeforeUpload"
  109 + @preview="handlePreview"
  110 + @download="handleDownload"
  111 + :remove="handleRemove"
  112 + >
  113 + <Spin :spinning="loading">
  114 + {{ getDisabled }}
  115 + <div class="w-full h-full flex flex-col justify-center content-center">
  116 + <InboxOutlined class="text-[3rem] !text-blue-500" />
  117 + <div class="m-2 text-gray-400">点击上传或拖拽上传</div>
  118 + </div>
  119 + </Spin>
  120 + </UploadDragger>
  121 +</template>
... ...
... ... @@ -14,6 +14,7 @@ import { setupGlobDirectives } from '/@/directives';
14 14 import { setupI18n } from '/@/locales/setupI18n';
15 15 import { registerGlobComp } from '/@/components/registerGlobComp';
16 16 import '/@/assets/iconfont/iconfont';
  17 +import { usePlatform } from './views/system/customize/hook/usePlatformInfo';
17 18
18 19 if (import.meta.env.DEV) {
19 20 import('ant-design-vue/dist/antd.less');
... ... @@ -23,6 +24,8 @@ async function bootstrap() {
23 24 // Configure store
24 25 setupStore(app);
25 26
  27 + await usePlatform();
  28 +
26 29 // Initialize internal system configuration
27 30 initAppConfigStore();
28 31
... ... @@ -47,6 +50,7 @@ async function bootstrap() {
47 50 // Mount when the route is ready
48 51 // https://next.router.vuejs.org/api/#isready
49 52 await router.isReady();
  53 +
50 54 app.mount('#app', true);
51 55 }
52 56
... ...
... ... @@ -39,7 +39,7 @@
39 39 width: '200px',
40 40 height: '200px',
41 41 color: '#409eff',
42   - muted: false, //静音
  42 + muted: true, //静音
43 43 webFullScreen: false,
44 44 autoPlay: true, //自动播放
45 45 currentTime: 0,
... ... @@ -238,7 +238,7 @@
238 238 class="h-full flex flex-col justify-center items-center"
239 239 v-if="!cameraList.length"
240 240 />
241   - <Row :gutter="16" class="h-full mx-0">
  241 + <Row :gutter="16" class="h-full mx-0 content-start">
242 242 <Col
243 243 v-for="item in cameraList"
244 244 :key="item.id"
... ...
  1 +<script lang="ts" setup>
  2 + import { BasicModal, useModalInner } from '/@/components/Modal';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchema } from '../config/packageDetail.config';
  5 + defineEmits(['register']);
  6 +
  7 + const [registerModal] = useModalInner((_record: Recordable) => {});
  8 +
  9 + const [registerForm] = useForm({
  10 + schemas: formSchema,
  11 + showActionButtonGroup: false,
  12 + // labelCol: { span: 8 },
  13 + labelWidth: 100,
  14 + wrapperCol: { span: 16 },
  15 + });
  16 +</script>
  17 +
  18 +<template>
  19 + <BasicModal title="包管理" @register="registerModal">
  20 + <BasicForm @register="registerForm" />
  21 + </BasicModal>
  22 +</template>
... ...
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +
  3 +export const columns: BasicColumn[] = [
  4 + {
  5 + title: '创建时间',
  6 + dataIndex: 'createTime',
  7 + width: 120,
  8 + },
  9 + {
  10 + title: '标题',
  11 + dataIndex: 'title',
  12 + width: 120,
  13 + },
  14 + {
  15 + title: '版本',
  16 + dataIndex: 'version',
  17 + width: 120,
  18 + },
  19 + {
  20 + title: '版本标签',
  21 + dataIndex: 'versionLabel',
  22 + width: 120,
  23 + },
  24 + {
  25 + title: '包类型',
  26 + dataIndex: 'pkgType',
  27 + width: 120,
  28 + },
  29 + {
  30 + title: '直接URL',
  31 + dataIndex: 'url',
  32 + width: 120,
  33 + },
  34 + {
  35 + title: '文件大小',
  36 + dataIndex: 'fileSize',
  37 + width: 120,
  38 + },
  39 + {
  40 + title: '校验和',
  41 + dataIndex: 'vaildateTotal',
  42 + width: 120,
  43 + },
  44 +];
  45 +
  46 +export const searchFormSchema: FormSchema[] = [
  47 + {
  48 + field: 'name',
  49 + label: '标题',
  50 + component: 'Input',
  51 + colProps: { span: 8 },
  52 + },
  53 +];
... ...
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +export enum PackageField {
  4 + TITLE = 'title',
  5 + VERSION = 'version',
  6 + VERSION_LABEL = 'versionLabel',
  7 + DEVICE_CONFIGURATION = 'deviceConfiguration',
  8 + PACKAGE_TYPE = 'packageType',
  9 + PACKAGE_UPDATE_TYPE = 'PackageUpdateType',
  10 + PACKAGE_BINARY_FILE = 'packageBinaryFile',
  11 + PACKAGE_EXTERNAL_URL = 'packageEexternalUrl',
  12 + CHECK_SUM_WAY = 'checkSumWay',
  13 + ALG = 'alg',
  14 + CHECK_SUM = 'checkSum',
  15 + DESCRIPTION = 'description',
  16 +}
  17 +
  18 +export enum PackageUpdateType {
  19 + BINARY_FILE = 'binaryFile',
  20 + EXTERNAL_URL = 'externalUrl',
  21 +}
  22 +
  23 +export enum PackageType {
  24 + FIRMWARE = 'firmware',
  25 + SOFTWARE = 'software',
  26 +}
  27 +
  28 +export enum CheckSumWay {
  29 + AUTO = 'auto',
  30 + MANUAL = 'manual',
  31 +}
  32 +
  33 +export enum ALG {
  34 + MD5 = 'md5',
  35 + SHA_256 = 'sha-256',
  36 + SHA_384 = 'sha-384',
  37 + SHA_512 = 'sha-512',
  38 + CRC_32 = 'crc-32',
  39 + MURMUR3_32 = 'murmur3-32',
  40 + MURMUR3_128 = 'murmur3-128',
  41 +}
  42 +
  43 +export const formSchema: FormSchema[] = [
  44 + {
  45 + field: PackageField.TITLE,
  46 + label: '标题',
  47 + component: 'Input',
  48 + rules: [{ required: true, message: '标题为必填项' }],
  49 + componentProps: {
  50 + placeholder: '请输入标题',
  51 + },
  52 + },
  53 + {
  54 + field: PackageField.VERSION,
  55 + label: '版本',
  56 + component: 'Input',
  57 + rules: [{ required: true, message: '版本为必填项' }],
  58 + componentProps: {
  59 + placeholder: '请输入版本',
  60 + },
  61 + },
  62 + {
  63 + field: PackageField.VERSION_LABEL,
  64 + label: '版本标签',
  65 + component: 'Input',
  66 + helpMessage: ['自定义标签应与您设备报告的软件包版本相匹配'],
  67 + componentProps: {
  68 + placeholder: '请输入版本标签',
  69 + },
  70 + },
  71 + {
  72 + field: PackageField.DEVICE_CONFIGURATION,
  73 + label: '设备配置',
  74 + component: 'Select',
  75 + helpMessage: ['上传的包仅适用于具有所选配置文件的设备'],
  76 + defaultValue: 'default',
  77 + rules: [{ required: true, message: '设备配置为必填项' }],
  78 + componentProps: () => {
  79 + return {
  80 + options: [{ label: 'default', value: 'default' }],
  81 + placeholder: '请选择设备配置',
  82 + };
  83 + },
  84 + },
  85 + {
  86 + field: PackageField.PACKAGE_TYPE,
  87 + label: '包类型',
  88 + component: 'Select',
  89 + helpMessage: ['上传包后,您将无法修改标题、版本、设备配置文件和包类型'],
  90 + defaultValue: PackageType.FIRMWARE,
  91 + rules: [{ required: true, message: '包类型为必填项' }],
  92 + componentProps: () => {
  93 + return {
  94 + options: [
  95 + { label: '固件', value: PackageType.FIRMWARE },
  96 + { label: '软件', value: PackageType.SOFTWARE },
  97 + ],
  98 + placeholder: '请选择设备配置',
  99 + };
  100 + },
  101 + },
  102 + {
  103 + field: PackageField.PACKAGE_UPDATE_TYPE,
  104 + label: '上传方式',
  105 + component: 'RadioGroup',
  106 + defaultValue: PackageUpdateType.BINARY_FILE,
  107 + componentProps: () => {
  108 + return {
  109 + options: [
  110 + { label: '上传二进制文件', value: PackageUpdateType.BINARY_FILE },
  111 + { label: '使用外部URL', value: PackageUpdateType.EXTERNAL_URL },
  112 + ],
  113 + };
  114 + },
  115 + },
  116 + {
  117 + field: PackageField.PACKAGE_BINARY_FILE,
  118 + label: '二进制文件',
  119 + ifShow: ({ model }) => {
  120 + return model[PackageField.PACKAGE_UPDATE_TYPE] === PackageUpdateType.BINARY_FILE;
  121 + },
  122 + component: 'Upload',
  123 + componentProps: {
  124 + api: () => {
  125 + return {};
  126 + },
  127 + },
  128 + },
  129 + {
  130 + field: PackageField.PACKAGE_EXTERNAL_URL,
  131 + label: '外部URL',
  132 + component: 'Input',
  133 + ifShow: ({ model }) => {
  134 + return model[PackageField.PACKAGE_UPDATE_TYPE] === PackageUpdateType.EXTERNAL_URL;
  135 + },
  136 + rules: [{ required: true, message: '外部URL为必填项' }],
  137 + componentProps: {
  138 + placeholder: '请输入外部URL',
  139 + },
  140 + },
  141 + {
  142 + field: PackageField.CHECK_SUM_WAY,
  143 + label: '校验和方式',
  144 + component: 'RadioGroup',
  145 + defaultValue: CheckSumWay.AUTO,
  146 + componentProps: () => {
  147 + return {
  148 + options: [
  149 + { label: '自动生成', value: CheckSumWay.AUTO },
  150 + { label: '手动生成', value: CheckSumWay.MANUAL },
  151 + ],
  152 + };
  153 + },
  154 + },
  155 + {
  156 + field: PackageField.ALG,
  157 + label: '校验和算法',
  158 + component: 'Select',
  159 + ifShow: ({ model }) => {
  160 + return model[PackageField.CHECK_SUM_WAY] === CheckSumWay.MANUAL;
  161 + },
  162 + componentProps: {
  163 + placeholder: '请选择校验和算法',
  164 + options: Object.keys(ALG).map((key) => {
  165 + return {
  166 + label: String(ALG[key]).toUpperCase(),
  167 + value: ALG[key],
  168 + };
  169 + }),
  170 + },
  171 + },
  172 + {
  173 + field: PackageField.CHECK_SUM,
  174 + label: '校验和',
  175 + component: 'Input',
  176 + ifShow: ({ model }) => {
  177 + return model[PackageField.CHECK_SUM_WAY] === CheckSumWay.MANUAL;
  178 + },
  179 + helpMessage: ['如果校验和为空,会自动生成'],
  180 + componentProps: {
  181 + placeholder: '请输入校验和',
  182 + },
  183 + },
  184 + {
  185 + field: PackageField.DESCRIPTION,
  186 + label: '描述',
  187 + component: 'InputTextArea',
  188 + componentProps: {
  189 + placeholder: '请输入描述',
  190 + },
  191 + },
  192 +];
... ...
  1 +<script lang="ts" setup>
  2 + import { Button } from 'ant-design-vue';
  3 + import { columns, searchFormSchema } from './config/config';
  4 + import { PageWrapper } from '/@/components/Page';
  5 + import { BasicTable, useTable } from '/@/components/Table';
  6 + import PackageDetailModal from './components/PackageDetailModal.vue';
  7 + import { useModal } from '/@/components/Modal';
  8 + // import { ApiUpload } from '/@/components/Form';
  9 + // import { computed, ref, unref } from 'vue';
  10 +
  11 + const [register] = useTable({
  12 + columns,
  13 + title: '包仓库',
  14 + formConfig: {
  15 + labelWidth: 120,
  16 + schemas: searchFormSchema,
  17 + },
  18 + useSearchForm: true,
  19 + showTableSetting: true,
  20 + });
  21 +
  22 + const [registerModal, { openModal }] = useModal();
  23 +
  24 + const handleCreatePackage = () => {
  25 + openModal(true);
  26 + };
  27 +
  28 + // const fileList = ref([]);
  29 + // const handleUpload = async (file: File) => {
  30 + // console.log(file);
  31 + // return new Promise((resolve) => {
  32 + // setTimeout(() => {
  33 + // resolve({
  34 + // uid: file.uid,
  35 + // type: file.type,
  36 + // name: file.name,
  37 + // linkProps: { download: 'http://www.baidu.cn' },
  38 + // });
  39 + // }, 3000);
  40 + // });
  41 + // };
  42 +</script>
  43 +
  44 +<template>
  45 + <PageWrapper dense contentFullHeight contentClass="flex flex-col">
  46 + <!-- <div class="w-40 h-40">
  47 + <ApiUpload v-model:file-list="fileList" :api="handleUpload" />
  48 + </div> -->
  49 + <BasicTable @register="register">
  50 + <template #toolbar>
  51 + <Button @click="handleCreatePackage" type="primary">新增包</Button>
  52 + </template>
  53 + </BasicTable>
  54 + <PackageDetailModal @register="registerModal" />
  55 + </PageWrapper>
  56 +</template>
... ...
... ... @@ -6,149 +6,161 @@
6 6 <a-button type="primary" @click="handleAdd"> 新增场景联动 </a-button>
7 7 </Authority>
8 8 <Authority value="api:yt:sceneLinkage:delete">
9   - <Popconfirm title="您确定要批量删除数据" ok-text="确定" cancel-text="取消" @confirm="handleDeleteOrBatchDelete(null)">
  9 + <Popconfirm
  10 + title="您确定要批量删除数据"
  11 + ok-text="确定"
  12 + cancel-text="取消"
  13 + @confirm="handleDeleteOrBatchDelete(null)"
  14 + >
10 15 <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
11 16 </Popconfirm>
12 17 </Authority>
13 18 </template>
14 19 <template #action="{ record }">
15   - <TableAction :actions="[
16   - {
17   - label: '查看',
18   - auth: 'api:yt:sceneLinkage:get',
19   - icon: 'ant-design:eye-outlined',
20   - onClick: handleView.bind(null, record),
21   - },
22   - {
23   - label: '编辑',
24   - auth: 'api:yt:sceneLinkage:update',
25   - icon: 'clarity:note-edit-line',
26   - onClick: handleEdit.bind(null, record),
27   - ifShow: record.creator === userId && record.status !== 1,
28   - },
29   - {
30   - label: '删除',
31   - auth: 'api:yt:sceneLinkage:delete',
32   - icon: 'ant-design:delete-outlined',
33   - color: 'error',
34   - ifShow: record.creator === userId && record.status !== 1,
35   - popConfirm: {
36   - title: '是否确认删除',
37   - confirm: handleDeleteOrBatchDelete.bind(null, record),
  20 + <TableAction
  21 + :actions="[
  22 + {
  23 + label: '查看',
  24 + auth: 'api:yt:sceneLinkage:get',
  25 + icon: 'ant-design:eye-outlined',
  26 + onClick: handleView.bind(null, record),
38 27 },
39   - },
40   - ]" />
  28 + {
  29 + label: '编辑',
  30 + auth: 'api:yt:sceneLinkage:update',
  31 + icon: 'clarity:note-edit-line',
  32 + onClick: handleEdit.bind(null, record),
  33 + ifShow: record.creator === userId && record.status !== 1,
  34 + },
  35 + {
  36 + label: '删除',
  37 + auth: 'api:yt:sceneLinkage:delete',
  38 + icon: 'ant-design:delete-outlined',
  39 + color: 'error',
  40 + ifShow: record.creator === userId && record.status !== 1,
  41 + popConfirm: {
  42 + title: '是否确认删除',
  43 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  44 + },
  45 + },
  46 + ]"
  47 + />
41 48 </template>
42 49
43 50 <template #status="{ record }">
44   - <Switch :checked="record.status === 1" :loading="record.pendingStatus" checkedChildren="启用"
45   - unCheckedChildren="禁用" @change="(checked:boolean)=>statusChange(checked,record)" />
  51 + <Switch
  52 + :checked="record.status === 1"
  53 + :loading="record.pendingStatus"
  54 + checkedChildren="启用"
  55 + unCheckedChildren="禁用"
  56 + @change="(checked:boolean)=>statusChange(checked,record)"
  57 + />
46 58 </template>
47 59 </BasicTable>
48 60 <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" />
49 61 </div>
50 62 </template>
51 63 <script lang="ts" setup>
52   -import { nextTick } from 'vue';
53   -import { BasicTable, useTable, TableAction } from '/@/components/Table';
54   -import { useDrawer } from '/@/components/Drawer';
55   -import {
56   - screenLinkPageGetApi,
57   - screenLinkPageDeleteApi,
58   - screenLinkPagePutApi,
59   -} from '/@/api/ruleengine/ruleengineApi';
60   -import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
61   -import { Switch, Popconfirm } from 'ant-design-vue';
62   -import { columns, searchFormSchema } from './config/config.data.ts';
63   -import { USER_INFO_KEY } from '/@/enums/cacheEnum';
64   -import { getAuthCache } from '/@/utils/auth';
65   -import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue';
66   -import { useMessage } from '/@/hooks/web/useMessage';
67   -import { Authority } from '/@/components/Authority';
68   -
69   -const userInfo: any = getAuthCache(USER_INFO_KEY);
70   -const userId = userInfo.userId;
  64 + import { nextTick } from 'vue';
  65 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  66 + import { useDrawer } from '/@/components/Drawer';
  67 + import {
  68 + screenLinkPageGetApi,
  69 + screenLinkPageDeleteApi,
  70 + screenLinkPagePutApi,
  71 + } from '/@/api/ruleengine/ruleengineApi';
  72 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  73 + import { Switch, Popconfirm } from 'ant-design-vue';
  74 + import { columns, searchFormSchema } from './config/config.data';
  75 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  76 + import { getAuthCache } from '/@/utils/auth';
  77 + import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue';
  78 + import { useMessage } from '/@/hooks/web/useMessage';
  79 + import { Authority } from '/@/components/Authority';
71 80
72   -const [registerDrawer, { openDrawer }] = useDrawer();
73   -const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({
74   - title: '场景联动列表',
75   - api: screenLinkPageGetApi,
76   - columns,
77   - formConfig: {
78   - labelWidth: 120,
79   - schemas: searchFormSchema,
80   - },
81   - useSearchForm: true,
82   - showTableSetting: true,
83   - bordered: true,
84   - showIndexColumn: false,
85   - actionColumn: {
86   - width: 200,
87   - title: '操作',
88   - dataIndex: 'action',
89   - slots: { customRender: 'action' },
90   - fixed: 'right',
91   - },
92   -});
93   -const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } =
94   - useBatchDelete(screenLinkPageDeleteApi, handleSuccess, setProps);
95   -selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
96   - // Demo:status为1的选择框禁用
97   - if (record.status === 1) {
98   - return { disabled: true };
99   - } else {
100   - return { disabled: false };
101   - }
102   -};
103   -nextTick(() => {
104   - setProps(selectionOptions);
105   -});
106   -
107   -function handleAdd() {
108   - window.localStorage.setItem('isViewDisabledBtn', 'noView')
109   - openDrawer(true, {
110   - isUpdate: false,
111   - });
112   -}
  81 + const userInfo: any = getAuthCache(USER_INFO_KEY);
  82 + const userId = userInfo.userId;
113 83
114   -function handleEdit(record: Recordable) {
115   - window.localStorage.setItem('isViewDisabledBtn', 'noView')
116   - openDrawer(true, {
117   - record,
118   - isUpdate: true,
  84 + const [registerDrawer, { openDrawer }] = useDrawer();
  85 + const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({
  86 + title: '场景联动列表',
  87 + api: screenLinkPageGetApi,
  88 + columns,
  89 + formConfig: {
  90 + labelWidth: 120,
  91 + schemas: searchFormSchema,
  92 + },
  93 + useSearchForm: true,
  94 + showTableSetting: true,
  95 + bordered: true,
  96 + showIndexColumn: false,
  97 + actionColumn: {
  98 + width: 200,
  99 + title: '操作',
  100 + dataIndex: 'action',
  101 + slots: { customRender: 'action' },
  102 + fixed: 'right',
  103 + },
119 104 });
120   -}
121   -function handleView(record: Recordable) {
122   - window.localStorage.setItem('isViewDisabledBtn', 'isView')
123   - openDrawer(true, {
124   - record,
125   - isUpdate: 3,
126   - });
127   -}
128   -function handleSuccess() {
129   - reload();
130   -}
131   -
132   -const statusChange = async (checked, record) => {
133   - setProps({
134   - loading: true,
135   - });
136   - setSelectedRowKeys([]);
137   - resetSelectedRowKeys();
138   - const newStatus = checked ? 1 : 0;
139   - const { createMessage } = useMessage();
140   - try {
141   - await screenLinkPagePutApi({ id: record.id, status: newStatus });
142   - if (newStatus) {
143   - createMessage.success(`启用成功`);
  105 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } =
  106 + useBatchDelete(screenLinkPageDeleteApi, handleSuccess, setProps);
  107 + selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
  108 + // Demo:status为1的选择框禁用
  109 + if (record.status === 1) {
  110 + return { disabled: true };
144 111 } else {
145   - createMessage.success('禁用成功');
  112 + return { disabled: false };
146 113 }
147   - } finally {
148   - setProps({
149   - loading: false,
  114 + };
  115 + nextTick(() => {
  116 + setProps(selectionOptions);
  117 + });
  118 +
  119 + function handleAdd() {
  120 + window.localStorage.setItem('isViewDisabledBtn', 'noView');
  121 + openDrawer(true, {
  122 + isUpdate: false,
  123 + });
  124 + }
  125 +
  126 + function handleEdit(record: Recordable) {
  127 + window.localStorage.setItem('isViewDisabledBtn', 'noView');
  128 + openDrawer(true, {
  129 + record,
  130 + isUpdate: true,
  131 + });
  132 + }
  133 + function handleView(record: Recordable) {
  134 + window.localStorage.setItem('isViewDisabledBtn', 'isView');
  135 + openDrawer(true, {
  136 + record,
  137 + isUpdate: 3,
150 138 });
  139 + }
  140 + function handleSuccess() {
151 141 reload();
152 142 }
153   -};
  143 +
  144 + const statusChange = async (checked, record) => {
  145 + setProps({
  146 + loading: true,
  147 + });
  148 + setSelectedRowKeys([]);
  149 + resetSelectedRowKeys();
  150 + const newStatus = checked ? 1 : 0;
  151 + const { createMessage } = useMessage();
  152 + try {
  153 + await screenLinkPagePutApi({ id: record.id, status: newStatus });
  154 + if (newStatus) {
  155 + createMessage.success(`启用成功`);
  156 + } else {
  157 + createMessage.success('禁用成功');
  158 + }
  159 + } finally {
  160 + setProps({
  161 + loading: false,
  162 + });
  163 + reload();
  164 + }
  165 + };
154 166 </script>
... ...
  1 +import { getPlatForm } from '/@/api/oem';
  2 +
  3 +enum DefaultPlatform {
  4 + LOGO = '/resource/img/logo.png',
  5 + // TITLE = 'ThingsKit',
  6 + ICO = '/favicon.ico',
  7 +}
  8 +
  9 +export const usePlatform = async () => {
  10 + const platformInfo = await getPlatForm();
  11 +
  12 + const createLoadingEffect = () => {
  13 + const wrap = document.createElement('div');
  14 + wrap.setAttribute('class', 'app-loading-wrap');
  15 + const img = document.createElement('img');
  16 + img.setAttribute('src', platformInfo.logo || DefaultPlatform.LOGO);
  17 + img.setAttribute('class', 'app-loading-logo');
  18 + img.setAttribute('alt', 'Logo');
  19 + const dots = document.createElement('div');
  20 + dots.setAttribute('class', 'app-loading-dots');
  21 + const dotWrap = document.createElement('span');
  22 + dotWrap.setAttribute('class', 'dot dot-spin');
  23 + for (let i = 0; i < 4; i++) {
  24 + dotWrap.appendChild(document.createElement('i'));
  25 + }
  26 + const title = document.createElement('div');
  27 + title.setAttribute('class', 'app-loading-title');
  28 + const textNode = document.createTextNode(
  29 + platformInfo.name || import.meta.env.VITE_GLOB_APP_TITLE
  30 + );
  31 + title.appendChild(textNode);
  32 + wrap.appendChild(img);
  33 + dots.appendChild(dotWrap);
  34 + wrap.appendChild(dots);
  35 + wrap.appendChild(title);
  36 + return wrap;
  37 + };
  38 +
  39 + const replaceSiteIco = () => {
  40 + const linkEl = document.querySelectorAll('link[rel*="icon"]');
  41 + linkEl.forEach((item) => {
  42 + item.setAttribute('href', platformInfo.logo || DefaultPlatform.ICO);
  43 + });
  44 + };
  45 +
  46 + const replaceLoadingEffect = () => {
  47 + const loadingWrapper = document.getElementById('first-screen-loading');
  48 + loadingWrapper!.innerHTML = '';
  49 + loadingWrapper?.appendChild(createLoadingEffect());
  50 + };
  51 +
  52 + const bootstrap = () => {
  53 + replaceSiteIco();
  54 + replaceLoadingEffect();
  55 + };
  56 +
  57 + bootstrap();
  58 +};
... ...
src/views/visual/board/components/ControlComponent/SlidingSwitch.vue renamed from src/views/visual/board/components/Other/SlidingSwitch.vue
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
2   - const props = defineProps<{
3   - value?: boolean;
4   - }>();
  7 + import { RadioRecord } from '../../detail/config/util';
  8 +
  9 + interface VisualComponentProps<Layout = Recordable, Value = Recordable> {
  10 + value?: Value;
  11 + layout?: Layout;
  12 + radio?: RadioRecord;
  13 + random?: boolean;
  14 + add?: (key: string, method: Fn) => void;
  15 + update?: () => void;
  16 + remove?: (key: string) => void;
  17 + }
  18 + const props = defineProps<VisualComponentProps>();
5 19
6 20 const emit = defineEmits(['update:value', 'change']);
7 21
... ... @@ -14,7 +28,12 @@
14 28
15 29 <template>
16 30 <label class="sliding-switch">
17   - <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange" />
  31 + <input
  32 + :value="props.value?.value"
  33 + type="checkbox"
  34 + :checked="props.value?.value"
  35 + @change="handleChange"
  36 + />
18 37 <span class="slider"></span>
19 38 <span class="on">ON</span>
20 39 <span class="off">OFF</span>
... ... @@ -40,12 +59,9 @@
40 59 .slider {
41 60 width: 80px;
42 61 height: 40px;
  62 + display: flex;
  63 + align-items: center;
43 64 box-sizing: border-box;
44   - position: absolute;
45   - top: 0;
46   - left: 0;
47   - right: 0;
48   - bottom: 0;
49 65 border: 2px solid #ecf0f3;
50 66 border-radius: 20px;
51 67 box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
... ... @@ -57,15 +73,14 @@
57 73 }
58 74
59 75 .slider::after {
60   - position: absolute;
61 76 cursor: pointer;
62 77 display: block;
63 78 content: '';
64 79 width: 24px;
65 80 height: 24px;
66 81 border-radius: 50%;
67   - top: 6px;
68   - left: 6px;
  82 + margin-left: 6px;
  83 + margin-right: 6px;
69 84 background-color: #ecf0f3;
70 85 box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
71 86 inset 2px 2px 4px hsl(0deg 0% 100% / 10%), 2px 2px 8px rgb(0 0 0 / 30%);
... ... @@ -80,13 +95,16 @@
80 95 input:checked ~ .slider::after {
81 96 transform: translateX(35px);
82 97 }
  98 +
83 99 input:not(:checked) ~ .on {
84 100 opacity: 0;
85   - transform: translateX(0px);
  101 + transform: translateX(0);
86 102 }
87 103
88 104 .on,
89 105 .off {
  106 + position: absolute;
  107 + top: 0;
90 108 display: inline-block;
91 109 margin-left: 3px;
92 110 width: 34px;
... ... @@ -97,7 +115,9 @@
97 115 .on {
98 116 color: #039be5;
99 117 }
  118 +
100 119 .off {
  120 + right: 6px;
101 121 color: #999;
102 122 }
103 123 }
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import { Switch } from 'ant-design-vue';
  8 + import { computed } from 'vue';
  9 + import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util';
  10 + import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
  11 + import {
  12 + ControlComponentDefaultConfig,
  13 + ControlComponentValue,
  14 + ControlComponentLayout,
  15 + } from './control.config';
  16 + const props = withDefaults(
  17 + defineProps<{
  18 + layout?: ControlComponentLayout;
  19 + value?: ControlComponentValue;
  20 + radio?: RadioRecord;
  21 + }>(),
  22 + {
  23 + value: () => ControlComponentDefaultConfig,
  24 + }
  25 + );
  26 + const getRadio = computed(() => {
  27 + return props.radio || DEFAULT_RADIO_RECORD;
  28 + });
  29 +</script>
  30 +
  31 +<template>
  32 + <div class="flex items-center w-full h-full p-4">
  33 + <div class="flex-auto flex truncate">
  34 + <SvgIcon
  35 + :name="props.value?.icon! || ControlComponentDefaultConfig.icon!"
  36 + prefix="iconfont"
  37 + :style="{
  38 + color: props.value?.iconColor || ControlComponentDefaultConfig.iconColor,
  39 + width: fontSize({ radioRecord: getRadio, basic: 30, min: 16 }),
  40 + height: fontSize({ radioRecord: getRadio, basic: 30, min: 16 }),
  41 + }"
  42 + />
  43 + <span class="flex-auto mx-4 flex items-center truncate inline-block">属性名</span>
  44 + </div>
  45 + <Switch />
  46 + </div>
  47 +</template>
... ...
src/views/visual/board/components/ControlComponent/ToggleSwitch.vue renamed from src/views/visual/board/components/Other/ToggleSwitch.vue
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
  7 + import { computed } from '@vue/reactivity';
  8 + import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util';
  9 + import { ControlComponentValue } from './control.config';
  10 +
2 11 const props = defineProps<{
3   - value?: boolean;
  12 + value?: ControlComponentValue;
  13 + layout?: Recordable;
  14 + radio?: RadioRecord;
4 15 }>();
5 16
6 17 const emit = defineEmits(['update:value', 'change']);
... ... @@ -10,12 +21,27 @@
10 21 emit('update:value', _value);
11 22 emit('change', _value);
12 23 };
  24 +
  25 + const getRadio = computed(() => {
  26 + return props.radio! || DEFAULT_RADIO_RECORD;
  27 + });
13 28 </script>
14 29
15 30 <template>
16   - <div class="toggle-switch">
  31 + <div
  32 + class="toggle-switch"
  33 + :style="{
  34 + width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }),
  35 + height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }),
  36 + }"
  37 + >
17 38 <label class="switch">
18   - <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange" />
  39 + <input
  40 + :value="props.value?.value"
  41 + type="checkbox"
  42 + :checked="props.value?.value"
  43 + @change="handleChange"
  44 + />
19 45 <div class="button">
20 46 <div class="light"></div>
21 47 <div class="dots"></div>
... ... @@ -29,9 +55,10 @@
29 55
30 56 <style scoped>
31 57 .toggle-switch {
32   - flex: 1 1 auto;
  58 + /* flex: 1 1 auto; */
33 59 max-width: 75px;
34   - height: 97.5px;
  60 +
  61 + /* height: 97.5px; */
35 62 display: flex;
36 63 }
37 64
... ... @@ -76,12 +103,12 @@
76 103 transform-origin: center center -20px;
77 104 transform: translateZ(20px) rotateX(-25deg);
78 105 transform-style: preserve-3d;
79   - background-color: #9b0621;
80 106 width: 100%;
81 107 height: 100%;
82 108 position: relative;
83 109 cursor: pointer;
84 110 background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
  111 + background-color: #9b0621;
85 112 background-repeat: no-repeat;
86 113 }
87 114
... ... @@ -113,7 +140,7 @@
113 140 transform: translateY(30px) rotateX(-90deg);
114 141 position: absolute;
115 142 bottom: 0;
116   - box-shadow: 0 30px 8px 0px black, 0 60px 20px 0px rgb(0 0 0 / 50%);
  143 + box-shadow: 0 30px 8px 0 black, 0 60px 20px 0 rgb(0 0 0 / 50%);
117 144 }
118 145
119 146 .switch .light {
... ...
  1 +import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
  2 +
  3 +export interface ControlComponentLayout {
  4 + [key: string]: any;
  5 +}
  6 +
  7 +export interface ControlComponentValue {
  8 + value?: boolean;
  9 + name?: string;
  10 + icon?: string;
  11 + iconColor?: string;
  12 +}
  13 +
  14 +export const ControlComponentDefaultConfig: ControlComponentValue = {
  15 + icon: 'shuiwen',
  16 + iconColor: '#367BFF',
  17 +};
  18 +
  19 +export const transformControlConfig = (
  20 + _ComponentConfig: Recordable,
  21 + _record: DataComponentRecord,
  22 + dataSourceRecord: DataSource
  23 +) => {
  24 + return {
  25 + value: {
  26 + value: dataSourceRecord.componentInfo.value,
  27 + icon: dataSourceRecord.componentInfo.icon,
  28 + } as ControlComponentValue,
  29 + };
  30 +};
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
2 7 import type { ECharts, EChartsOption } from 'echarts';
3 8 import { PropType, watch } from 'vue';
... ... @@ -7,21 +12,21 @@
7 12 DashboardComponentLayout,
8 13 DashBoardValue,
9 14 instrumentComponent1,
10   - InstrumentComponentType,
11 15 update_instrument_1_font,
12 16 update_instrument_2_font,
13 17 update_instrument_1_value,
14 18 update_instrument_2_value,
15 19 } from './dashBoardComponent.config';
16   - import { dateUtil } from '/@/utils/dateUtil';
17 20 import {
18 21 DEFAULT_RADIO_RECORD,
19 22 RadioRecord,
20   - DEFAULT_DATE_FORMAT,
21 23 fontSize,
  24 + getUpdateTime,
22 25 } from '../../detail/config/util';
23 26 import { Tooltip } from 'ant-design-vue';
24 27 import { useThrottleFn } from '@vueuse/shared';
  28 + import { buildUUID } from '/@/utils/uuid';
  29 + import { FrontComponent } from '../help';
25 30
26 31 const props = defineProps({
27 32 add: {
... ... @@ -33,7 +38,7 @@
33 38 },
34 39 value: {
35 40 type: Object as PropType<DashBoardValue>,
36   - default: () => ({}),
  41 + default: () => ({ id: buildUUID() }),
37 42 },
38 43 radio: {
39 44 type: Object as PropType<RadioRecord>,
... ... @@ -60,7 +65,6 @@
60 65 }
61 66
62 67 const getRadio = computed(() => {
63   - // const { radio } = props.radio;
64 68 return props.radio;
65 69 });
66 70
... ... @@ -68,7 +72,6 @@
68 72 const realWidth = unref(chartRef)?.getWidth();
69 73 const realHeight = unref(chartRef)?.getHeight();
70 74 const radioRecord = props.radio;
71   - // const widht
72 75 return {
73 76 ...radioRecord,
74 77 height: realHeight || radioRecord.height,
... ... @@ -76,7 +79,7 @@
76 79 };
77 80 });
78 81
79   - const beforeUpdateFn = (componentType: InstrumentComponentType) => {
  82 + const beforeUpdateFn = (componentType: FrontComponent) => {
80 83 if (componentType === 'instrument-component-1') return update_instrument_1_font;
81 84 if (componentType === 'instrument-component-2') return update_instrument_2_font;
82 85 return (_radio: RadioRecord) => {};
... ... @@ -88,7 +91,7 @@
88 91 unref(chartRef)?.resize();
89 92 }
90 93
91   - const getUpdateValueFn = (componentType: InstrumentComponentType) => {
  94 + const getUpdateValueFn = (componentType: FrontComponent) => {
92 95 if (componentType === 'instrument-component-1') return update_instrument_1_value;
93 96 if (componentType === 'instrument-component-2') return update_instrument_2_value;
94 97 return (_radio: DashBoardValue) => {};
... ... @@ -101,11 +104,6 @@
101 104
102 105 watch(() => props.value, updateChartValue);
103 106
104   - // watch(
105   - // () => [props.value.gradientInfo, props.value.unit, props.value.valueColor, props.value.name],
106   - // updateChartValue
107   - // );
108   -
109 107 const updateChartFont = useThrottleFn(() => {
110 108 const option = beforeUpdateFn(props.layout.componentType);
111 109 setTimeout(() => {
... ... @@ -122,8 +120,6 @@
122 120
123 121 watch(() => props.layout.componentType, updateChartType);
124 122
125   - // watch(() => props.value.gradientInfo, updateChartType);
126   -
127 123 let timeout: Nullable<number> = null;
128 124
129 125 function handleRandomValue() {
... ... @@ -170,22 +166,11 @@
170 166 color: '#999',
171 167 }"
172 168 >
173   - <Tooltip
174   - placement="top"
175   - :title="
176   - props.value?.updateTime
177   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
178   - : '暂无更新时间'
179   - "
180   - >
  169 + <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)">
181 170 <div class="truncate">
182 171 <span class="mr-2">更新时间:</span>
183 172 <span>
184   - {{
185   - props.value?.updateTime
186   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
187   - : '暂无更新时间'
188   - }}
  173 + {{ getUpdateTime(props.value?.updateTime) }}
189 174 </span>
190 175 </div>
191 176 </Tooltip>
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
2 7 import { computed, onMounted, onUnmounted, ref, unref } from 'vue';
3 8 import { Space, Tooltip } from 'ant-design-vue';
... ... @@ -6,22 +11,27 @@
6 11 DigitalDashBoardLayout,
7 12 DigitalDashBoardValue,
8 13 } from './digitalDashBoard.config';
9   - import { dateUtil } from '/@/utils/dateUtil';
10 14 import {
11 15 fontSize,
12 16 RadioRecord,
13   - DEFAULT_DATE_FORMAT,
  17 + getUpdateTime,
14 18 DEFAULT_RADIO_RECORD,
15 19 DEFAULT_ANIMATION_INTERVAL,
16 20 } from '../../detail/config/util';
17 21 import { isNaN } from 'lodash';
18 22
19   - const props = defineProps<{
20   - layout: DigitalDashBoardLayout;
21   - value: DigitalDashBoardValue;
22   - radio?: RadioRecord;
23   - random?: boolean;
24   - }>();
  23 + const props = withDefaults(
  24 + defineProps<{
  25 + layout?: DigitalDashBoardLayout;
  26 + value?: DigitalDashBoardValue;
  27 + radio?: RadioRecord;
  28 + random?: boolean;
  29 + }>(),
  30 + {
  31 + value: () => ({}),
  32 + layout: () => ({ max: 5, keepNumber: 2 }),
  33 + }
  34 + );
25 35
26 36 const changeValue = ref(0);
27 37
... ... @@ -53,8 +63,6 @@
53 63 });
54 64
55 65 const getRadio = computed(() => {
56   - // const { radio } = props.radio || DEFAULT_RADIO_RECORD;
57   - // return radio;
58 66 return props.radio || DEFAULT_RADIO_RECORD;
59 67 });
60 68
... ... @@ -160,22 +168,11 @@
160 168 color: '#999',
161 169 }"
162 170 >
163   - <Tooltip
164   - placement="top"
165   - :title="
166   - props.value?.updateTime
167   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
168   - : '暂无更新时间'
169   - "
170   - >
  171 + <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)">
171 172 <div class="truncate">
172 173 <span class="mr-1">更新时间:</span>
173 174 <span>
174   - {{
175   - props.value?.updateTime
176   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
177   - : '暂无更新时间'
178   - }}
  175 + {{ getUpdateTime(props.value?.updateTime) }}
179 176 </span>
180 177 </div>
181 178 </Tooltip>
... ... @@ -188,11 +185,12 @@
188 185 <style scoped lang="less">
189 186 .digital-wrapper__int {
190 187 border-radius: 1px;
191   - box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, 0.7);
192   - background: url('/@/assets/images/digital-wrapper-bg-int.png') 0px -1px no-repeat;
  188 + box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.7);
  189 + background: url('/@/assets/images/digital-wrapper-bg-int.png') 0 -1px no-repeat;
193 190 padding: 5px;
194 191 background-size: 100% 100%;
195 192 }
  193 +
196 194 .digital-text_int {
197 195 display: inline-block;
198 196 overflow-wrap: break-word;
... ... @@ -203,11 +201,12 @@
203 201
204 202 .digital-wrapper__float {
205 203 border-radius: 1px;
206   - box-shadow: inset 0px 1px 3px 0px rgba(112, 22, 15, 1);
207   - background: url('/@/assets/images/digital-wrapper-bg-float.png') 0px -1px no-repeat;
  204 + box-shadow: inset 0 1px 3px 0 rgba(112, 22, 15, 1);
  205 + background: url('/@/assets/images/digital-wrapper-bg-float.png') 0 -1px no-repeat;
208 206 padding: 5px;
209 207 background-size: 100% 100%;
210 208 }
  209 +
211 210 .digital-text_float {
212 211 display: inline-block;
213 212 overflow-wrap: break-word;
... ...
1 1 import { EChartsOption } from 'echarts';
  2 +import { FrontComponent, Gradient, GradientColor } from '../../const/const';
2 3 import { fontSize, RadioRecord } from '../../detail/config/util';
3   -import { Gradient, visualOptionField } from '../../detail/config/visualOptions';
4 4 import {
5 5 ComponentInfo,
6 6 DataComponentRecord,
... ... @@ -10,16 +10,6 @@ import {
10 10 import { isArray } from '/@/utils/is';
11 11 import { buildUUID } from '/@/utils/uuid';
12 12
13   -export type InstrumentComponentType = 'instrument-component-1' | 'instrument-component-2';
14   -
15   -export type GradientKey =
16   - | visualOptionField.FIRST_PHASE_COLOR
17   - | visualOptionField.FIRST_PHASE_VALUE
18   - | visualOptionField.SECOND_PHASE_COLOR
19   - | visualOptionField.SECOND_PHASE_VALUE
20   - | visualOptionField.THIRD_PHASE_COLOR
21   - | visualOptionField.THIRD_PHASE_VALUE;
22   -
23 13 export interface GradientInfoRecord {
24 14 key: Gradient;
25 15 value: number;
... ... @@ -38,13 +28,7 @@ export interface DashBoardValue {
38 28
39 29 export interface DashboardComponentLayout {
40 30 chartOption: EChartsOption;
41   - componentType: InstrumentComponentType;
42   -}
43   -
44   -export enum GradientColor {
45   - FIRST = '#67e0e3',
46   - SECOND = '#37a2da',
47   - THIRD = '#fd666d',
  31 + componentType: FrontComponent;
48 32 }
49 33
50 34 export const instrumentComponent1 = (params?: Partial<ComponentInfo>): EChartsOption => {
... ... @@ -94,7 +78,6 @@ export const instrumentComponent1 = (params?: Partial<ComponentInfo>): EChartsOp
94 78 axisLabel: {
95 79 distance: 0,
96 80 color: '#999',
97   - // fontSize: 20,
98 81 },
99 82 anchor: {
100 83 show: false,
... ... @@ -173,7 +156,6 @@ export const instrumentComponent2 = (params?: Partial<ComponentInfo>): EChartsOp
173 156
174 157 const firstGradient = firstRecord?.value ? firstRecord.value / max : 0.3;
175 158 const secondGradient = secondRecord?.value ? secondRecord.value / max : 0.7;
176   - // const thirdGradient = thirdRecord?.value ? thirdRecord.value / max : 1;
177 159
178 160 return {
179 161 series: [
... ... @@ -307,12 +289,15 @@ export const update_instrument_2_value = (params: DashBoardValue) => {
307 289
308 290 let max = thirdRecord?.value || secondRecord?.value || firstRecord?.value || 70;
309 291 max = Number(1 + Array(String(max).length).fill(0).join(''));
  292 + max = value > 1 ? Number(1 + Array(String(value).length).fill(0).join('')) / 2 : 100 / 2;
  293 + max = value > max ? max * 2 : max;
310 294
311 295 const firstGradient = firstRecord?.value ? firstRecord.value / max : 0.3;
312 296 const secondGradient = secondRecord?.value ? secondRecord.value / max : 0.7;
313 297 return {
314 298 series: [
315 299 {
  300 + max: max < 100 ? 100 : max,
316 301 data: [{ value: handleValue(value) }],
317 302 detail: {
318 303 formatter: `{value} ${unit ?? ''}`,
... ... @@ -354,7 +339,7 @@ export const Instrument2DefaultConfig: Partial<ComponentInfo> = {
354 339
355 340 export const transformDashboardComponentConfig = (
356 341 config: DashboardComponentLayout,
357   - record: DataComponentRecord,
  342 + _record: DataComponentRecord,
358 343 dataSourceRecord: DataSource
359 344 ) => {
360 345 let chartOption = config.chartOption;
... ...
1 1 import { ComponentInfo } from '/@/api/dataBoard/model';
2 2
3   -export type DigitalDashBoardComponentType = 'digital-dashboard-component';
4   -
5 3 export interface DigitalDashBoardLayout {
6 4 max: number;
7 5 keepNumber: number;
... ...
1 1 import { EChartsOption } from 'echarts';
2   -import { Component } from 'vue';
3   -import { WidgetComponentType } from '../../detail/config/visualOptions';
4   -import {
5   - Instrument1DefaultConfig,
6   - Instrument2DefaultConfig,
7   - instrumentComponent1,
8   - instrumentComponent2,
9   - InstrumentComponentType,
10   -} from './dashBoardComponent.config';
11   -import DashBoardComponent from './DashBoardComponent.vue';
12   -import DigitalDashBoard from './DigitalDashBoard.vue';
13   -import { buildUUID } from '/@/utils/uuid';
  2 +import { InstrumentComponentType } from './dashBoardComponent.config';
14 3
15 4 export interface DashboardComponentLayout {
16 5 chartOption: EChartsOption;
17 6 componentType: InstrumentComponentType;
18 7 }
19   -
20   -interface InstrumentComponentConfig {
21   - id: WidgetComponentType;
22   - layout: DashboardComponentLayout;
23   - component: Component;
24   - value: Recordable;
25   -}
26   -
27   -export const instrumentComponentConfig: InstrumentComponentConfig[] = [
28   - {
29   - id: 'instrument-component-1',
30   - layout: {
31   - chartOption: instrumentComponent1(Instrument1DefaultConfig),
32   - componentType: 'instrument-component-1',
33   - },
34   - component: DashBoardComponent,
35   - value: { id: buildUUID() },
36   - },
37   - {
38   - id: 'instrument-component-2',
39   - layout: {
40   - chartOption: instrumentComponent2(Instrument2DefaultConfig),
41   - componentType: 'instrument-component-2',
42   - },
43   - component: DashBoardComponent,
44   - value: { id: buildUUID() },
45   - },
46   - {
47   - id: 'digital-dashboard-component',
48   - layout: {},
49   - component: DigitalDashBoard,
50   - value: {},
51   - },
52   -];
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import { nextTick, onMounted, ref, unref } from 'vue';
  8 + import { useScript } from '/@/hooks/web/useScript';
  9 + import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
  10 +
  11 + const wrapRef = ref<HTMLDivElement | null>(null);
  12 + const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
  13 +
  14 + async function initMap() {
  15 + await toPromise();
  16 + await nextTick();
  17 + const wrapEl = unref(wrapRef);
  18 + if (!wrapEl) return;
  19 + const BMap = (window as any).BMap;
  20 + const map = new BMap.Map(wrapEl);
  21 + const point = new BMap.Point(116.404, 39.915);
  22 + map.centerAndZoom(point, 15);
  23 + map.enableScrollWheelZoom(true);
  24 + }
  25 +
  26 + onMounted(() => {
  27 + initMap();
  28 + });
  29 +</script>
  30 +
  31 +<template>
  32 + <div class="w-full h-full flex justify-center items-center">
  33 + <div ref="wrapRef" class="w-[95%] h-[95%]"></div>
  34 + </div>
  35 +</template>
... ...
  1 +import { ComponentConfig } from '../help';
  2 +
  3 +export const transfromMapComponentConfig: ComponentConfig['transformConfig'] = (
  4 + _componentConfig,
  5 + _record,
  6 + _dataSourceRecord
  7 +) => {
  8 + return {};
  9 +};
... ...
... ... @@ -12,6 +12,7 @@
12 12
13 13 const isEdit = ref(false);
14 14 const recordId = ref<Nullable<string>>(null);
  15 + const loading = ref(false);
15 16
16 17 const [registerModal, { changeLoading, closeModal }] = useModalInner(
17 18 (record: DataBoardRecord & { isEdit: boolean }) => {
... ... @@ -33,6 +34,7 @@
33 34 await method.validate();
34 35 try {
35 36 const value = method.getFieldsValue() as AddDataBoardParams;
  37 + loading.value = true;
36 38 changeLoading(true);
37 39 await addDataBoard(value);
38 40 createMessage.success('创建成功');
... ... @@ -42,12 +44,14 @@
42 44 createMessage.error('创建失败');
43 45 } finally {
44 46 changeLoading(false);
  47 + loading.value = false;
45 48 }
46 49 };
47 50
48 51 const handleEditPanel = async () => {
49 52 await method.validate();
50 53 try {
  54 + loading.value = true;
51 55 const value = method.getFieldsValue() as UpdateDataBoardParams;
52 56 value.id = unref(recordId) as string;
53 57 changeLoading(true);
... ... @@ -59,6 +63,7 @@
59 63 createMessage.error('编辑失败');
60 64 } finally {
61 65 changeLoading(false);
  66 + loading.value = false;
62 67 }
63 68 };
64 69
... ... @@ -75,6 +80,7 @@
75 80 :title="isEdit ? '编辑看板' : '创建看板'"
76 81 @register="registerModal"
77 82 @ok="handleGetValue"
  83 + :okButtonProps="{ loading }"
78 84 >
79 85 <BasicForm @register="registerForm" />
80 86 </BasicModal>
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
2 7 import { computed, ref, watch } from 'vue';
3 8 import { Tooltip, Image as AntImage } from 'ant-design-vue';
4 9 import {
5   - DEFAULT_DATE_FORMAT,
  10 + getUpdateTime,
6 11 DEFAULT_RADIO_RECORD,
7 12 fontSize,
8 13 RadioRecord,
9 14 } from '../../detail/config/util';
10   - import { PictureComponentLayout, PictureComponentValue } from './pictureComponent.config';
11   - import { dateUtil } from '/@/utils/dateUtil';
  15 + import { PictureComponentValue } from './pictureComponent.config';
12 16
13 17 const props = defineProps<{
14   - layout?: PictureComponentLayout;
  18 + layout?: Recordable;
15 19 value?: PictureComponentValue;
16 20 radio?: RadioRecord;
17 21 }>();
... ... @@ -22,8 +26,6 @@
22 26 const getImagBase64 = ref(fallback);
23 27
24 28 const getRadio = computed(() => {
25   - // const { radio } = props.radio || DEFAULT_RADIO_RECORD;
26   - // return radio;
27 29 return props.radio || DEFAULT_RADIO_RECORD;
28 30 });
29 31
... ... @@ -67,26 +69,14 @@
67 69 class="w-full h-full flex flex-col justify-center items-center justify-between widget-picture"
68 70 >
69 71 <AntImage :width="getWidth" :src="getImagBase64" :fallback="fallback" />
70   - <!-- <Image :style="{ width: `${getWidth}px` }" :src="getImagBase64" /> -->
71 72 <div
72 73 class="w-full text-center truncate p-5"
73 74 :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 12 }), color: '#999' }"
74 75 >
75   - <Tooltip
76   - placement="top"
77   - :title="
78   - props.value?.updateTime
79   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
80   - : '暂无更新时间'
81   - "
82   - >
  76 + <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)">
83 77 <span class="mr-1">更新时间:</span>
84 78 <span class="truncate">
85   - {{
86   - props.value?.updateTime
87   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
88   - : '暂无更新时间'
89   - }}
  79 + {{ getUpdateTime(props.value?.updateTime) }}
90 80 </span>
91 81 </Tooltip>
92 82 </div>
... ...
1 1 import PictureComponent from './PictureComponent.vue';
2 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 };
  3 +export { PictureComponent };
... ...
1 1 import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
2 2
3   -export type PictureComponentType = 'picture-component-1';
4   -
5   -export interface PictureComponentLayout {}
6   -
7 3 export interface PictureComponentValue {
8 4 value?: string;
9 5 updateTime?: string;
10 6 }
11 7
12 8 export const transformPictureConfig = (
13   - config: PictureComponentLayout,
14   - record: DataComponentRecord,
  9 + _config: Recordable,
  10 + _record: DataComponentRecord,
15 11 dataSourceRecord: DataSource
16 12 ) => {
17 13 const componentInfo = dataSourceRecord.componentInfo;
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
1 6 <script lang="ts" setup>
2 7 import { computed } from 'vue';
3 8 import { Statistic, Tooltip } from 'ant-design-vue';
4 9 import {
  10 + getUpdateTime,
5 11 fontSize,
6 12 RadioRecord,
7 13 DEFAULT_RADIO_RECORD,
8   - DEFAULT_DATE_FORMAT,
9 14 } from '../../detail/config/util';
10 15 import { TextComponentDefaultConfig, TextComponentLayout, TextComponentValue } from './config';
11 16 import { SvgIcon } from '/@/components/Icon';
12   - import { dateUtil } from '/@/utils/dateUtil';
13 17 const props = defineProps({
14 18 layout: {
15 19 type: Object as PropType<TextComponentLayout>,
... ... @@ -47,8 +51,6 @@
47 51 });
48 52
49 53 const getRadio = computed(() => {
50   - // const { radio } = props.radio;
51   - // return radio;
52 54 return props.radio || DEFAULT_RADIO_RECORD;
53 55 });
54 56 </script>
... ... @@ -84,37 +86,21 @@
84 86 />
85 87 </div>
86 88 <div :style="{ color: '#666', fontSize: fontSize({ radioRecord: getRadio, basic: 16 }) }">
87   - <!-- {{ getShowUnit ? props.value.unit : '' }} -->
88 89 {{ props.value.name }}
89 90 </div>
90   - <!-- <div class="truncate" :style="{ fontSize: fontSize({ radio: getRadio, basic: 16 }) }">
91   - {{ props.value.name }}
92   - </div> -->
93 91 </div>
94 92 </div>
95 93 </div>
96 94
97 95 <div class="text-center text-xs truncate p-5" style="color: #999">
98   - <Tooltip
99   - v-if="getShowUpdate"
100   - placement="top"
101   - :title="
102   - props.value?.updateTime
103   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
104   - : '暂无更新时间'
105   - "
106   - >
  96 + <Tooltip v-if="getShowUpdate" placement="top" :title="getUpdateTime(props.value?.updateTime)">
107 97 <div
108 98 :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 12 }) }"
109 99 class="truncate"
110 100 >
111 101 <span class="mr-1">更新时间:</span>
112 102 <span class="truncate">
113   - {{
114   - props.value?.updateTime
115   - ? dateUtil(props?.value?.updateTime).format(DEFAULT_DATE_FORMAT)
116   - : '暂无更新时间'
117   - }}
  103 + {{ getUpdateTime(props.value?.updateTime) }}
118 104 </span>
119 105 </div>
120 106 </Tooltip>
... ...
... ... @@ -18,13 +18,6 @@ export interface TextComponentValue {
18 18 deviceName?: 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   -
28 21 type TextComponentDefault = TextComponentLayout;
29 22
30 23 export const TextComponent1Config: TextComponentDefault = {
... ... @@ -32,10 +25,6 @@ export const TextComponent1Config: TextComponentDefault = {
32 25 base: true,
33 26 };
34 27
35   -export const TextComponent2Config: TextComponentDefault = {
36   - id: 'text-component-2',
37   - base: false,
38   -};
39 28 export const TextComponent3Config: TextComponentDefault = {
40 29 id: 'text-component-3',
41 30 base: false,
... ... @@ -62,17 +51,9 @@ export const TextComponentDefaultConfig: Partial<ComponentInfo> = {
62 51 icon: 'shuiwen',
63 52 };
64 53
65   -export const textComponentConfig: TextComponentDefault[] = [
66   - TextComponent1Config,
67   - // TextComponent2Config,
68   - TextComponent3Config,
69   - TextComponent4Config,
70   - TextComponent5Config,
71   -];
72   -
73 54 export const transformTextComponentConfig = (
74 55 config: TextComponentDefault,
75   - record: DataComponentRecord,
  56 + _record: DataComponentRecord,
76 57 dataSourceRecord: DataSource
77 58 ) => {
78 59 return {
... ...
... ... @@ -3,8 +3,11 @@
3 3 import { DropMenu } from '/@/components/Dropdown';
4 4 import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
5 5 import { Tooltip } from 'ant-design-vue';
6   - // import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
7   - import { isBataBoardSharePage, MoreActionEvent } from '../../config/config';
  6 + import {
  7 + isBataBoardSharePage,
  8 + MoreActionEvent,
  9 + VisualComponentPermission,
  10 + } from '../../config/config';
8 11 import { computed } from '@vue/reactivity';
9 12 import { usePermission } from '/@/hooks/web/usePermission';
10 13 import { DataSource } from '/@/api/dataBoard/model';
... ... @@ -19,9 +22,9 @@
19 22 const { hasPermission } = usePermission();
20 23 const dropMenuList = computed<DropMenu[]>(() => {
21 24 const basicMenu: DropMenu[] = [];
22   - const hasUpdatePermission = hasPermission('api:yt:data_component:update:update');
23   - const hasDeletePermission = hasPermission('api:yt:data_component:delete');
24   - const hasCopyPermission = hasPermission('api:yt:dataBoardDetail:copy');
  25 + const hasUpdatePermission = hasPermission(VisualComponentPermission.UPDATE);
  26 + const hasDeletePermission = hasPermission(VisualComponentPermission.DELETE);
  27 + const hasCopyPermission = hasPermission(VisualComponentPermission.COPY);
25 28 if (hasUpdatePermission)
26 29 basicMenu.push({
27 30 text: '编辑组件',
... ... @@ -69,7 +72,6 @@
69 72 >
70 73 <Tooltip :title="item.deviceName" placement="topLeft">
71 74 <div class="flex p-1">
72   - <!-- <SvgIcon name="" prefix="iconfont" class="!fill-emerald-400" /> -->
73 75 <div class="truncate font-bold">{{ item.deviceRename || item.deviceName }}</div>
74 76 </div>
75 77 </Tooltip>
... ...
1 1 <script lang="ts" setup>
2   - import { useSlots } from 'vue';
3 2 import { useUpdateCenter } from '../../hook/useUpdateCenter';
4 3 import { FrontDataSourceRecord } from '../../types/type';
5   - // import type { WidgetWrapperRegister } from './type';
6   - // import { DataSource } from '/@/api/dataBoard/model';
7 4
8 5 const props = defineProps<{
9 6 dataSource: FrontDataSourceRecord[];
10 7 }>();
11 8
12   - const slot = useSlots();
13   -
14 9 const { update, add, remove } = useUpdateCenter();
15 10
16 11 defineExpose({ update });
... ... @@ -20,7 +15,7 @@
20 15 <section class="widget">
21 16 <slot name="header"></slot>
22 17
23   - <div class="widget-content" :style="{ height: slot.header ? 'calc(100% - 22px)' : '100%' }">
  18 + <div class="widget-content">
24 19 <div
25 20 v-for="item in props.dataSource"
26 21 :key="item.id"
... ... @@ -28,10 +23,7 @@
28 23 class="widget-item"
29 24 >
30 25 <div class="widget-box">
31   - <div
32   - class="widget-controls-container"
33   - :style="{ height: slot.footer ? 'calc(100% - 20px)' : '100%' }"
34   - >
  26 + <div class="widget-controls-container">
35 27 <slot
36 28 name="controls"
37 29 :record="item"
... ... @@ -40,12 +32,6 @@
40 32 :update="update"
41 33 ></slot>
42 34 </div>
43   - <div class="widget-value">
44   - <slot name="value" :record="item"></slot>
45   - </div>
46   - <div class="widget-label">
47   - <slot name="label" :record="item"></slot>
48   - </div>
49 35 </div>
50 36 </div>
51 37 </div>
... ... @@ -61,14 +47,14 @@
61 47 height: 100%;
62 48 background-color: #fff;
63 49 border-radius: 3px;
64   - box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.1);
  50 + box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1);
65 51 }
  52 +
66 53 .widget-content {
67 54 display: flex;
68 55 flex-wrap: wrap;
69 56 justify-content: center;
70 57 align-items: center;
71   -
72 58 width: 100%;
73 59 height: 100%;
74 60 }
... ... @@ -94,7 +80,7 @@
94 80 .widget-controls-container {
95 81 flex: 1 1 auto;
96 82 width: 100%;
97   - height: calc(100% - 20px);
  83 + height: 100%;
98 84 display: flex;
99 85 align-items: center;
100 86 justify-content: center;
... ...
1   -// import { DataBoardLayoutInfo } from '../../types/type';
2   -
3   -// export type WidgetWrapperRegister = (dataSource: DataBoardLayoutInfo['record'][]) => any;
  1 +import {
  2 + TextComponent1Config,
  3 + TextComponent3Config,
  4 + TextComponent4Config,
  5 + TextComponent5Config,
  6 + TextComponentDefaultConfig,
  7 + transformTextComponentConfig,
  8 +} from './TextComponent/config';
  9 +import { ComponentInfo } from '/@/api/dataBoard/model';
  10 +import { transformPictureConfig } from './PictureComponent/pictureComponent.config';
  11 +import {
  12 + DashboardComponentLayout,
  13 + Instrument1DefaultConfig,
  14 + Instrument2DefaultConfig,
  15 + instrumentComponent1,
  16 + instrumentComponent2,
  17 + transformDashboardComponentConfig,
  18 +} from './InstrumentComponent/dashBoardComponent.config';
  19 +import { DigitalComponentDefaultConfig } from './InstrumentComponent/digitalDashBoard.config';
  20 +import { transformControlConfig } from './ControlComponent/control.config';
  21 +
  22 +import TextComponent from './TextComponent/TextComponent.vue';
  23 +import DashBoardComponent from './InstrumentComponent/DashBoardComponent.vue';
  24 +import PictureComponent from './PictureComponent/PictureComponent.vue';
  25 +import DigitalDashBoard from './InstrumentComponent/DigitalDashBoard.vue';
  26 +import ToggleSwitch from './ControlComponent/ToggleSwitch.vue';
  27 +import SlidingSwitch from './ControlComponent/SlidingSwitch.vue';
  28 +import SwitchWithIcon from './ControlComponent/SwitchWithIcon.vue';
  29 +import MapComponent from './MapComponent/MapComponent.vue';
  30 +import { transfromMapComponentConfig } from './MapComponent/map.config';
  31 +import { ComponentConfig } from '../types/type';
  32 +import { FrontComponent, FrontComponentCategory } from '../const/const';
  33 +
  34 +export const frontComponentDefaultConfigMap = new Map<FrontComponent, Partial<ComponentInfo>>();
  35 +
  36 +export const frontComponentMap = new Map<FrontComponent, ComponentConfig>();
  37 +
  38 +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, {
  39 + Component: TextComponent,
  40 + ComponentName: '文本组件1',
  41 + ComponentKey: FrontComponent.TEXT_COMPONENT_1,
  42 + ComponentConfig: TextComponent1Config,
  43 + ComponentCategory: FrontComponentCategory.TEXT,
  44 + transformConfig: transformTextComponentConfig,
  45 +});
  46 +
  47 +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, {
  48 + Component: TextComponent,
  49 + ComponentName: '文本组件2',
  50 + ComponentKey: FrontComponent.TEXT_COMPONENT_3,
  51 + ComponentConfig: TextComponent3Config,
  52 + ComponentCategory: FrontComponentCategory.TEXT,
  53 + transformConfig: transformTextComponentConfig,
  54 +});
  55 +
  56 +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, {
  57 + Component: TextComponent,
  58 + ComponentName: '文本组件3',
  59 + ComponentKey: FrontComponent.TEXT_COMPONENT_4,
  60 + ComponentConfig: TextComponent4Config,
  61 + ComponentCategory: FrontComponentCategory.TEXT,
  62 + transformConfig: transformTextComponentConfig,
  63 +});
  64 +
  65 +frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, {
  66 + Component: TextComponent,
  67 + ComponentName: '文本组件4',
  68 + ComponentKey: FrontComponent.TEXT_COMPONENT_5,
  69 + ComponentConfig: TextComponent5Config,
  70 + ComponentCategory: FrontComponentCategory.TEXT,
  71 + transformConfig: transformTextComponentConfig,
  72 +});
  73 +
  74 +frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, {
  75 + Component: DashBoardComponent,
  76 + ComponentName: '仪表盘',
  77 + ComponentKey: FrontComponent.INSTRUMENT_COMPONENT_1,
  78 + ComponentConfig: {
  79 + chartOption: instrumentComponent1(Instrument1DefaultConfig),
  80 + componentType: FrontComponent.INSTRUMENT_COMPONENT_1,
  81 + } as DashboardComponentLayout,
  82 + ComponentCategory: FrontComponentCategory.INSTRUMENT,
  83 + transformConfig: transformDashboardComponentConfig,
  84 +});
  85 +
  86 +frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, {
  87 + Component: DashBoardComponent,
  88 + ComponentName: '阶段仪表盘',
  89 + ComponentKey: FrontComponent.INSTRUMENT_COMPONENT_2,
  90 + ComponentConfig: {
  91 + chartOption: instrumentComponent2(Instrument2DefaultConfig),
  92 + componentType: FrontComponent.INSTRUMENT_COMPONENT_2,
  93 + } as DashboardComponentLayout,
  94 + ComponentCategory: FrontComponentCategory.INSTRUMENT,
  95 + transformConfig: transformDashboardComponentConfig,
  96 +});
  97 +
  98 +frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, {
  99 + Component: DigitalDashBoard,
  100 + ComponentName: '数字仪表盘',
  101 + ComponentKey: FrontComponent.DIGITAL_DASHBOARD_COMPONENT,
  102 + ComponentCategory: FrontComponentCategory.INSTRUMENT,
  103 + transformConfig: transformDashboardComponentConfig,
  104 +});
  105 +
  106 +frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, {
  107 + Component: PictureComponent,
  108 + ComponentName: '图片组件',
  109 + ComponentKey: FrontComponent.PICTURE_COMPONENT_1,
  110 + ComponentCategory: FrontComponentCategory.PICTURE,
  111 + transformConfig: transformPictureConfig,
  112 +});
  113 +
  114 +frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, {
  115 + Component: SwitchWithIcon,
  116 + ComponentName: '控制按钮1',
  117 + ComponentKey: FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON,
  118 + ComponentCategory: FrontComponentCategory.CONTROL,
  119 + transformConfig: transformControlConfig,
  120 +});
  121 +
  122 +frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, {
  123 + Component: SlidingSwitch,
  124 + ComponentName: '控制按钮2',
  125 + ComponentKey: FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH,
  126 + ComponentCategory: FrontComponentCategory.CONTROL,
  127 + transformConfig: transformControlConfig,
  128 +});
  129 +
  130 +frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, {
  131 + Component: ToggleSwitch,
  132 + ComponentName: '控制按钮3',
  133 + ComponentKey: FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH,
  134 + ComponentCategory: FrontComponentCategory.CONTROL,
  135 + transformConfig: transformControlConfig,
  136 +});
  137 +
  138 +frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK, {
  139 + Component: MapComponent,
  140 + ComponentName: '实时轨迹',
  141 + ComponentKey: FrontComponent.MAP_COMPONENT_TRACK,
  142 + ComponentCategory: FrontComponentCategory.MAP,
  143 + transformConfig: transfromMapComponentConfig,
  144 +});
  145 +
  146 +frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_1, TextComponentDefaultConfig);
  147 +frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_3, TextComponentDefaultConfig);
  148 +frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_4, TextComponentDefaultConfig);
  149 +frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_5, TextComponentDefaultConfig);
  150 +
  151 +frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, Instrument1DefaultConfig);
  152 +frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, Instrument2DefaultConfig);
  153 +
  154 +frontComponentDefaultConfigMap.set(
  155 + FrontComponent.DIGITAL_DASHBOARD_COMPONENT,
  156 + DigitalComponentDefaultConfig
  157 +);
  158 +
  159 +export const getComponentDefaultConfig = (key: FrontComponent) => {
  160 + return frontComponentDefaultConfigMap.get(key) || {};
  161 +};
... ...
1   -import { Component } from 'vue';
2   -import IndicatorLight from '../cpns/IndicatorLight.vue';
3   -
4   -export const dashboardComponentMap = new Map<string, Component>();
5   -
6   -dashboardComponentMap.set('IndicatorLight', IndicatorLight);
... ... @@ -4,7 +4,19 @@ export enum MoreActionEvent {
4 4 DELETE = 'delete',
5 5 }
6 6
7   -// export enum
  7 +export enum VisualBoardPermission {
  8 + UPDATE = 'api:yt:data_board:update:update',
  9 + DELETE = 'api:yt:data_board:delete',
  10 + CREATE = '',
  11 + DETAIL = 'api:yt:data_component:list',
  12 +}
  13 +
  14 +export enum VisualComponentPermission {
  15 + UPDATE = 'api:yt:data_component:update:update',
  16 + DELETE = 'api:yt:data_component:delete',
  17 + COPY = 'api:yt:dataBoardDetail:copy',
  18 + CREATE = 'api:yt:data_component:add:post',
  19 +}
8 20
9 21 export const DEFAULT_MAX_COL = 24;
10 22 export const DEFAULT_WIDGET_WIDTH = 6;
... ...
  1 +export enum FrontComponentCategory {
  2 + TEXT = 'text',
  3 + PICTURE = 'picture',
  4 + INSTRUMENT = 'instrument',
  5 + CONTROL = 'control',
  6 + MAP = 'map',
  7 +}
  8 +
  9 +export const FrontComponentCategoryName = {
  10 + [FrontComponentCategory.TEXT]: '文本组件',
  11 + [FrontComponentCategory.INSTRUMENT]: '仪表组件',
  12 + [FrontComponentCategory.CONTROL]: '控制组件',
  13 + [FrontComponentCategory.PICTURE]: '图片组件',
  14 + [FrontComponentCategory.MAP]: '地图组件',
  15 +};
  16 +
  17 +export enum FrontComponent {
  18 + TEXT_COMPONENT_1 = 'text-component-1',
  19 + TEXT_COMPONENT_2 = 'text-component-2',
  20 + TEXT_COMPONENT_3 = 'text-component-3',
  21 + TEXT_COMPONENT_4 = 'text-component-4',
  22 + TEXT_COMPONENT_5 = 'text-component-5',
  23 + INSTRUMENT_COMPONENT_1 = 'instrument-component-1',
  24 + INSTRUMENT_COMPONENT_2 = 'instrument-component-2',
  25 + DIGITAL_DASHBOARD_COMPONENT = 'digital-dashboard-component',
  26 + PICTURE_COMPONENT_1 = 'picture-component-1',
  27 + CONTROL_COMPONENT_TOGGLE_SWITCH = 'control-component-toggle-switch',
  28 + CONTROL_COMPONENT_SWITCH_WITH_ICON = 'control-component-switch-with-icon',
  29 + CONTROL_COMPONENT_SLIDING_SWITCH = 'control-component-sliding-switch',
  30 + MAP_COMPONENT_TRACK = 'map-component-track',
  31 +}
  32 +
  33 +export enum Gradient {
  34 + FIRST = 'first',
  35 + SECOND = 'second',
  36 + THIRD = 'third',
  37 +}
  38 +export enum GradientColor {
  39 + FIRST = '#67e0e3',
  40 + SECOND = '#37a2da',
  41 + THIRD = '#fd666d',
  42 +}
  43 +
  44 +export enum visualOptionField {
  45 + FONT_COLOR = 'fontColor',
  46 + UNIT = 'unit',
  47 + ICON_COLOR = 'iconColor',
  48 + ICON = 'icon',
  49 + FIRST_PHASE_COLOR = 'firstPhaseColor',
  50 + SECOND_PHASE_COLOR = 'secondPhaseColor',
  51 + THIRD_PHASE_COLOR = 'thirdPhaseColor',
  52 + FIRST_PHASE_VALUE = 'firstPhaseValue',
  53 + SECOND_PHASE_VALUE = 'secondPhaseValue',
  54 + THIRD_PHASE_VALUE = 'thirdPhaseValue',
  55 +}
... ...
... ... @@ -2,17 +2,17 @@
2 2 import { CopyOutlined, DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue';
3 3 import { Tooltip, Button } from 'ant-design-vue';
4 4 import { FormActionType, useForm } from '/@/components/Form';
5   - import { basicSchema, dataSourceSchema } from '../config/basicConfiguration';
  5 + import { basicSchema } from '../config/basicConfiguration';
6 6 import BasicForm from '/@/components/Form/src/BasicForm.vue';
7   - import { ref, shallowReactive, unref, nextTick, watch } from 'vue';
  7 + import { ref, shallowReactive, unref, nextTick, watch, computed } 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 + import { getDataSourceComponent } from './DataSourceForm/help';
  15 + import { FrontComponent } from '../../const/const';
16 16
17 17 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> };
18 18
... ... @@ -20,7 +20,7 @@
20 20
21 21 const props = defineProps<{
22 22 record: DataBoardLayoutInfo;
23   - frontId?: string;
  23 + frontId?: FrontComponent;
24 24 defaultConfig?: Partial<ComponentInfo>;
25 25 }>();
26 26
... ... @@ -39,7 +39,7 @@
39 39 const dataSourceEl = shallowReactive<DataSourceFormEL>({} as unknown as DataSourceFormEL);
40 40
41 41 const setFormEl = (el: any, id: string) => {
42   - if (!dataSourceEl[id] && el) {
  42 + if (id && el) {
43 43 const { formActionType } = el as unknown as { formActionType: FormActionType };
44 44 dataSourceEl[id] = formActionType;
45 45 }
... ... @@ -112,8 +112,6 @@
112 112 return;
113 113 }
114 114
115   - // const defaultConfig = getComponentDefaultConfig(props.frontId as WidgetComponentType);
116   -
117 115 const componentInfo: ComponentInfo = {
118 116 ...(props.defaultConfig || {}),
119 117 ...(item.componentInfo || {}),
... ... @@ -177,6 +175,10 @@
177 175 ~index && (unref(dataSource)[index].componentInfo = value);
178 176 };
179 177
  178 + const dataSourceComponent = computed(() => {
  179 + return getDataSourceComponent(props.frontId as FrontComponent);
  180 + });
  181 +
180 182 defineExpose({
181 183 getAllDataSourceFieldValue,
182 184 validate,
... ... @@ -198,17 +200,8 @@
198 200 选择设备
199 201 </div>
200 202 <div class="pl-2 flex-auto">
201   - <BasicForm
202   - :ref="(el) => setFormEl(el, item.id)"
203   - :schemas="dataSourceSchema"
204   - class="w-full flex-1 data-source-form"
205   - :show-action-button-group="false"
206   - :row-props="{
207   - gutter: 10,
208   - }"
209   - layout="inline"
210   - :label-col="{ span: 0 }"
211   - />
  203 + <!-- <BasicDataSourceForm /> -->
  204 + <component :is="dataSourceComponent" :ref="(el) => setFormEl(el, item.id)" />
212 205 </div>
213 206 <div class="flex justify-center gap-3 w-24">
214 207 <Tooltip title="复制">
... ...
... ... @@ -12,7 +12,7 @@
12 12 import { decode } from '../../config/config';
13 13 import { ComponentInfo } from '/@/api/dataBoard/model';
14 14 import { useCalcGridLayout } from '../../hook/useCalcGridLayout';
15   - import { FrontComponent } from '../config/help';
  15 + import { FrontComponent } from '../../const/const';
16 16
17 17 interface DataComponentRouteParams extends RouteParams {
18 18 id: string;
... ... @@ -23,9 +23,9 @@
23 23 }>();
24 24
25 25 const emit = defineEmits(['update', 'create', 'register']);
26   -
27 26 const ROUTE = useRoute();
28 27
  28 + const loading = ref(false);
29 29 const { createMessage } = useMessage();
30 30
31 31 const boardId = computed(() => {
... ... @@ -62,11 +62,15 @@
62 62 };
63 63
64 64 const handleSubmit = async () => {
65   - const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!;
66   - await validate();
67   - const value = getAllDataSourceFieldValue();
68   - unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value);
69   - resetForm();
  65 + try {
  66 + const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!;
  67 + await validate();
  68 + const value = getAllDataSourceFieldValue();
  69 + unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value);
  70 + resetForm();
  71 + } catch (error: unknown) {
  72 + window.console.error(error);
  73 + }
70 74 };
71 75
72 76 const { calcLayoutInfo } = useCalcGridLayout();
... ... @@ -78,6 +82,7 @@
78 82 }
79 83 const layout = calcLayoutInfo(unref(props.layout));
80 84 changeOkLoading(true);
  85 + loading.value = true;
81 86 await addDataComponent({
82 87 boardId: unref(boardId),
83 88 record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value, layout },
... ... @@ -90,12 +95,14 @@
90 95 // createMessage.error('创建失败');
91 96 } finally {
92 97 changeOkLoading(false);
  98 + loading.value = false;
93 99 }
94 100 };
95 101
96 102 const handleUpdateComponent = async (value: Recordable) => {
97 103 try {
98 104 changeOkLoading(true);
  105 + loading.value = true;
99 106 const res = await updateDataComponent({
100 107 boardId: unref(boardId),
101 108 record: {
... ... @@ -113,6 +120,7 @@
113 120 // createMessage.error('修改失败');
114 121 } finally {
115 122 changeOkLoading(false);
  123 + loading.value = false;
116 124 }
117 125 };
118 126
... ... @@ -130,10 +138,11 @@
130 138 :destroy-on-close="true"
131 139 @ok="handleSubmit"
132 140 @cancel="resetForm"
  141 + :ok-button-props="{ loading }"
133 142 >
134 143 <section>
135 144 <Tabs type="card">
136   - <Tabs.TabPane key="1" tab="基础配置">
  145 + <Tabs.TabPane key="basicConfig" tab="基础配置">
137 146 <BasicConfiguration
138 147 ref="basicConfigurationEl"
139 148 :front-id="frontId"
... ... @@ -141,7 +150,7 @@
141 150 :defaultConfig="componentDefaultConfig"
142 151 />
143 152 </Tabs.TabPane>
144   - <Tabs.TabPane key="2" tab="可视化配置">
  153 + <Tabs.TabPane key="visualConfig" tab="可视化配置">
145 154 <VisualConfiguration v-model:value="frontId" @change="handleComponentCheckedChange" />
146 155 </Tabs.TabPane>
147 156 </Tabs>
... ...
  1 +<script lang="ts" setup>
  2 + import { ref } from 'vue';
  3 + import { dataSourceSchema } from '../../config/basicConfiguration';
  4 + import { FormActionType } from '/@/components/Form';
  5 + import BasicForm from '/@/components/Form/src/BasicForm.vue';
  6 + const formEl = ref<Nullable<FormActionType>>(null);
  7 +
  8 + defineExpose({ formActionType: formEl });
  9 +</script>
  10 +
  11 +<template>
  12 + <BasicForm
  13 + ref="formEl"
  14 + :schemas="dataSourceSchema"
  15 + class="w-full flex-1 data-source-form"
  16 + :show-action-button-group="false"
  17 + :row-props="{
  18 + gutter: 10,
  19 + }"
  20 + layout="inline"
  21 + :label-col="{ span: 0 }"
  22 + />
  23 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { ref, unref } from 'vue';
  3 + import { BasicForm, FormActionType } from '/@/components/Form';
  4 + import { controlFormSchema } from '../../config/basicConfiguration';
  5 +
  6 + const formEl = ref<Nullable<FormActionType>>();
  7 +
  8 + const setFormEl = (el: any) => {
  9 + formEl.value = el;
  10 + };
  11 +
  12 + const getFieldsValue = () => {
  13 + return unref(formEl)!.getFieldsValue();
  14 + };
  15 +
  16 + const validate = async () => {
  17 + await unref(formEl)!.validate();
  18 + };
  19 +
  20 + const setFieldsValue = async (record: Recordable) => {
  21 + await unref(formEl)!.setFieldsValue(record);
  22 + };
  23 +
  24 + const clearValidate = async (name?: string | string[]) => {
  25 + await unref(formEl)!.clearValidate(name);
  26 + };
  27 + defineExpose({
  28 + formActionType: { getFieldsValue, validate, setFieldsValue, clearValidate },
  29 + });
  30 +</script>
  31 +
  32 +<template>
  33 + <div class="w-full flex-1">
  34 + <BasicForm
  35 + :ref="(el) => setFormEl(el)"
  36 + :schemas="controlFormSchema"
  37 + class="w-full flex-1 data-source-form"
  38 + :show-action-button-group="false"
  39 + :row-props="{
  40 + gutter: 10,
  41 + }"
  42 + layout="inline"
  43 + :label-col="{ span: 0 }"
  44 + />
  45 + </div>
  46 +</template>
... ...
  1 +import { Component } from 'vue';
  2 +import { FrontComponent } from '../../../const/const';
  3 +import BasicDataSourceForm from './BasicDataSourceForm.vue';
  4 +import ControlDataSourceForm from './ControlDataSourceForm.vue';
  5 +
  6 +const dataSourceComponentMap = new Map<FrontComponent, Component>();
  7 +
  8 +dataSourceComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, ControlDataSourceForm);
  9 +
  10 +export const getDataSourceComponent = (frontId: FrontComponent) => {
  11 + if (dataSourceComponentMap.has(frontId)) return dataSourceComponentMap.get(frontId)!;
  12 + return BasicDataSourceForm;
  13 +};
... ...
1 1 <script lang="ts" setup>
2 2 import { Tabs, List } from 'ant-design-vue';
3 3 import VisualWidgetSelect from './VisualWidgetSelect.vue';
4   - import TextComponent from '../../components/TextComponent/TextComponent.vue';
5   - import { textComponentConfig } from '../../components/TextComponent/config';
6   - import { instrumentComponentConfig } from '../../components/InstrumentComponent';
7   - import { pictureComponentList } from '../../components/PictureComponent';
8   - import { getComponentDefaultConfig } from '../config/help';
9   - import { WidgetComponentType } from '../config/visualOptions';
  4 + import { getComponentDefaultConfig } from '../../components/help';
  5 + import { frontComponentMap } from '../../components/help';
  6 + import { computed } from 'vue';
  7 + import {
  8 + FrontComponent,
  9 + FrontComponentCategory,
  10 + FrontComponentCategoryName,
  11 + } from '../../const/const';
  12 +
  13 + interface DataSource {
  14 + category: string;
  15 + categoryName: string;
  16 + list: Recordable[];
  17 + }
10 18 const props = defineProps<{
11 19 value: string;
12 20 }>();
... ... @@ -14,7 +22,25 @@
14 22
15 23 const grid = { gutter: 10, column: 1, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 4 };
16 24
17   - const handleCheck = (checked: WidgetComponentType) => {
  25 + const getDataSource = computed(() => {
  26 + const _dataSource = Array.from(frontComponentMap.values());
  27 + const category = new Map<FrontComponentCategory, DataSource>();
  28 + for (const item of _dataSource) {
  29 + if (category.has(item.ComponentCategory)) {
  30 + const value = category.get(item.ComponentCategory)!;
  31 + value.list.push(item);
  32 + continue;
  33 + }
  34 + category.set(item.ComponentCategory, {
  35 + category: item.ComponentCategory,
  36 + categoryName: FrontComponentCategoryName[item.ComponentCategory],
  37 + list: [item],
  38 + });
  39 + }
  40 + return Array.from(category.values());
  41 + });
  42 +
  43 + const handleCheck = (checked: FrontComponent) => {
18 44 const defaultConfig = getComponentDefaultConfig(checked);
19 45 emit('update:value', checked);
20 46 emit('change', defaultConfig);
... ... @@ -24,51 +50,25 @@
24 50 <template>
25 51 <section>
26 52 <Tabs>
27   - <Tabs.TabPane key="1" tab="文本组件">
28   - <List :grid="grid" :data-source="textComponentConfig">
29   - <template #renderItem="{ item }">
30   - <List.Item class="!flex !justify-center">
31   - <VisualWidgetSelect
32   - :checked-id="props.value"
33   - :control-id="item.id"
34   - @change="handleCheck"
35   - >
36   - <TextComponent :layout="item" :value="item.value" />
37   - </VisualWidgetSelect>
38   - </List.Item>
39   - </template>
40   - </List>
41   - </Tabs.TabPane>
42   - <Tabs.TabPane key="2" tab="图片组件">
43   - <List :grid="grid" :data-source="pictureComponentList">
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="仪表组件">
58   - <List :grid="grid" :data-source="instrumentComponentConfig">
  53 + <Tabs.TabPane
  54 + v-for="category in getDataSource"
  55 + :key="category.category"
  56 + :tab="category.categoryName"
  57 + >
  58 + <List :grid="grid" :data-source="category.list">
59 59 <template #renderItem="{ item }">
60 60 <List.Item class="!flex !justify-center">
61 61 <VisualWidgetSelect
62 62 :checked-id="props.value"
63   - :control-id="item.id"
  63 + :control-id="item.ComponentKey"
64 64 @change="handleCheck"
65 65 >
66   - <component
67   - :is="item.component"
68   - :random="true"
69   - :layout="item.layout"
70   - :value="item.value"
71   - />
  66 + <template #default>
  67 + <component :is="item.Component" :random="true" :layout="item.ComponentConfig" />
  68 + </template>
  69 + <template #description>
  70 + {{ item.ComponentName || '选择' }}
  71 + </template>
72 72 </VisualWidgetSelect>
73 73 </List.Item>
74 74 </template>
... ...
1 1 <script lang="ts" setup>
2 2 import { ref, unref } from 'vue';
3   - import {
4   - WidgetComponentType,
5   - schemasMap,
6   - VisualOptionParams,
7   - visualOptionField,
8   - Gradient,
9   - } from '../config/visualOptions';
  3 + import { schemasMap, VisualOptionParams } from '../config/visualOptions';
10 4 import { useForm, BasicForm } from '/@/components/Form';
11 5 import { BasicModal, useModalInner } from '/@/components/Modal';
12 6 import { ComponentInfo } from '/@/api/dataBoard/model';
13 7 import { computed } from '@vue/reactivity';
  8 + import { FrontComponent, Gradient, visualOptionField } from '../../const/const';
14 9
15 10 const emit = defineEmits(['close', 'register']);
16 11
... ... @@ -21,7 +16,7 @@
21 16 const recordId = ref('');
22 17
23 18 const getSchemas = computed(() => {
24   - return schemasMap.get((props.value as WidgetComponentType) || 'text-component-1');
  19 + return schemasMap.get((props.value as FrontComponent) || 'text-component-1');
25 20 });
26 21
27 22 const [registerForm, method] = useForm({
... ...
... ... @@ -30,16 +30,19 @@
30 30 <slot></slot>
31 31 </div>
32 32 <Card.Meta>
33   - <template #description> 选择 </template>
  33 + <template #description>
  34 + <slot name="description"></slot>
  35 + </template>
34 36 </Card.Meta>
35 37 </Card>
36 38 </template>
37 39
38 40 <style scoped>
39 41 .widget-select {
40   - box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.1);
  42 + box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1);
41 43 border-width: 2px;
42 44 }
  45 +
43 46 .widget-select:deep(.ant-card-body) {
44 47 /* height: 240px; */
45 48 width: 240px;
... ...
... ... @@ -217,3 +217,24 @@ export const dataSourceSchema: FormSchema[] = [
217 217 },
218 218 },
219 219 ];
  220 +
  221 +export const controlFormSchema: FormSchema[] = [
  222 + {
  223 + field: DataSourceField.DEVICE_RENAME,
  224 + component: 'Input',
  225 + label: '设备',
  226 + colProps: { span: 8 },
  227 + componentProps: {
  228 + placeholder: '设备重命名',
  229 + },
  230 + },
  231 + {
  232 + field: DataSourceField.ATTRIBUTE_RENAME,
  233 + component: 'Input',
  234 + label: '属性',
  235 + colProps: { span: 8 },
  236 + componentProps: {
  237 + placeholder: '属性重命名',
  238 + },
  239 + },
  240 +];
... ...
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   - TextComponentDefaultConfig,
10   - transformTextComponentConfig,
11   -} from '../../components/TextComponent/config';
12   -import { ComponentInfo, DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
13   -import DashBoardComponent from '../../components/InstrumentComponent/DashBoardComponent.vue';
14   -import PictureComponent from '../../components/PictureComponent/PictureComponent.vue';
15   -import { transformPictureConfig } from '../../components/PictureComponent/pictureComponent.config';
16   -import { WidgetComponentType } from './visualOptions';
17   -import {
18   - DashboardComponentLayout,
19   - Instrument1DefaultConfig,
20   - Instrument2DefaultConfig,
21   - instrumentComponent1,
22   - instrumentComponent2,
23   - transformDashboardComponentConfig,
24   -} from '../../components/InstrumentComponent/dashBoardComponent.config';
25   -import DigitalDashBoard from '../../components/InstrumentComponent/DigitalDashBoard.vue';
26   -import { DigitalComponentDefaultConfig } from '../../components/InstrumentComponent/digitalDashBoard.config';
27   -export enum FrontComponent {
28   - TEXT_COMPONENT_1 = 'text-component-1',
29   - TEXT_COMPONENT_2 = 'text-component-2',
30   - TEXT_COMPONENT_3 = 'text-component-3',
31   - TEXT_COMPONENT_4 = 'text-component-4',
32   - TEXT_COMPONENT_5 = 'text-component-5',
33   - INSTRUMENT_COMPONENT_1 = 'instrument-component-1',
34   - INSTRUMENT_COMPONENT_2 = 'instrument-component-2',
35   - DIGITAL_DASHBOARD_COMPONENT = 'digital-dashboard-component',
36   - PICTURE_COMPONENT_1 = 'picture-component-1',
37   -}
38   -
39   -export interface ComponentConfig {
40   - Component: Component;
41   - ComponentConfig: Recordable;
42   - transformConfig: (
43   - ComponentConfig: Recordable,
44   - record: DataComponentRecord,
45   - dataSourceRecord: DataSource
46   - ) => Recordable;
47   -}
48   -
49   -export const frontComponentDefaultConfigMap = new Map<
50   - WidgetComponentType,
51   - Partial<ComponentInfo>
52   ->();
53   -
54   -export const frontComponentMap = new Map<WidgetComponentType, ComponentConfig>();
55   -
56   -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, {
57   - Component: TextComponent,
58   - ComponentConfig: TextComponent1Config,
59   - transformConfig: transformTextComponentConfig,
60   -});
61   -
62   -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_2, {
63   - Component: TextComponent,
64   - ComponentConfig: TextComponent2Config,
65   - transformConfig: transformTextComponentConfig,
66   -});
67   -
68   -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, {
69   - Component: TextComponent,
70   - ComponentConfig: TextComponent3Config,
71   - transformConfig: transformTextComponentConfig,
72   -});
73   -
74   -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, {
75   - Component: TextComponent,
76   - ComponentConfig: TextComponent4Config,
77   - transformConfig: transformTextComponentConfig,
78   -});
79   -
80   -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, {
81   - Component: TextComponent,
82   - ComponentConfig: TextComponent5Config,
83   - transformConfig: transformTextComponentConfig,
84   -});
85   -
86   -frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, {
87   - Component: DashBoardComponent,
88   - ComponentConfig: {
89   - chartOption: instrumentComponent1(),
90   - componentType: FrontComponent.INSTRUMENT_COMPONENT_1,
91   - } as DashboardComponentLayout,
92   - transformConfig: transformDashboardComponentConfig,
93   -});
94   -
95   -frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, {
96   - Component: DashBoardComponent,
97   - ComponentConfig: {
98   - chartOption: instrumentComponent2(),
99   - componentType: FrontComponent.INSTRUMENT_COMPONENT_2,
100   - } as DashboardComponentLayout,
101   - transformConfig: transformDashboardComponentConfig,
102   -});
103   -
104   -frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, {
105   - Component: DigitalDashBoard,
106   - ComponentConfig: {},
107   - transformConfig: transformDashboardComponentConfig,
108   -});
109   -
110   -frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, {
111   - Component: PictureComponent,
112   - ComponentConfig: {},
113   - transformConfig: transformPictureConfig,
114   -});
115   -
116   -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_1, TextComponentDefaultConfig);
117   -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_2, TextComponentDefaultConfig);
118   -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_3, TextComponentDefaultConfig);
119   -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_4, TextComponentDefaultConfig);
120   -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_5, TextComponentDefaultConfig);
121   -
122   -frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, Instrument1DefaultConfig);
123   -frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, Instrument2DefaultConfig);
124   -
125   -frontComponentDefaultConfigMap.set(
126   - FrontComponent.DIGITAL_DASHBOARD_COMPONENT,
127   - DigitalComponentDefaultConfig
128   -);
129   -
130   -export const getComponentDefaultConfig = (key: WidgetComponentType) => {
131   - return frontComponentDefaultConfigMap.get(key) || {};
132   -};
  1 +import { dateUtil } from '/@/utils/dateUtil';
  2 +
1 3 export interface RadioRecord {
2 4 width: number;
3 5 height: number;
... ... @@ -16,6 +18,10 @@ export const DEFAULT_RADIO_RECORD: RadioRecord = {
16 18
17 19 export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
18 20
  21 +export const getUpdateTime = (updateTime?: string) => {
  22 + return updateTime ? dateUtil(updateTime).format(DEFAULT_DATE_FORMAT) : '暂无更新时间';
  23 +};
  24 +
19 25 export const calcScale = (
20 26 width: number,
21 27 height: number,
... ...
1   -import { InstrumentComponentType } from '../../components/InstrumentComponent/dashBoardComponent.config';
2   -import { DigitalDashBoardComponentType } from '../../components/InstrumentComponent/digitalDashBoard.config';
3   -import { PictureComponentType } from '../../components/PictureComponent/pictureComponent.config';
4   -import { TextComponentType } from '../../components/TextComponent/config';
  1 +import { FrontComponent, GradientColor } from '../../const/const';
5 2 import { FormSchema } from '/@/components/Form';
6 3
7   -export type WidgetComponentType =
8   - | TextComponentType
9   - | InstrumentComponentType
10   - | DigitalDashBoardComponentType
11   - | PictureComponentType;
12   -
13 4 export interface VisualOptionParams {
14 5 [visualOptionField.FONT_COLOR]: string;
15 6 [visualOptionField.UNIT]: string;
... ... @@ -23,17 +14,6 @@ export interface VisualOptionParams {
23 14 [visualOptionField.THIRD_PHASE_VALUE]: string;
24 15 }
25 16
26   -export enum Gradient {
27   - FIRST = 'first',
28   - SECOND = 'second',
29   - THIRD = 'third',
30   -}
31   -export enum GradientColor {
32   - FIRST = '#67e0e3',
33   - SECOND = '#37a2da',
34   - THIRD = '#fd666d',
35   -}
36   -
37 17 export enum visualOptionField {
38 18 FONT_COLOR = 'fontColor',
39 19 UNIT = 'unit',
... ... @@ -198,13 +178,13 @@ export const modeFour: FormSchema[] = [
198 178 },
199 179 ];
200 180
201   -export const schemasMap = new Map<WidgetComponentType, FormSchema[]>();
  181 +export const schemasMap = new Map<FrontComponent, FormSchema[]>();
202 182
203   -schemasMap.set('text-component-1', modeOne);
204   -schemasMap.set('text-component-2', modeOne);
205   -schemasMap.set('text-component-3', modeOne);
206   -schemasMap.set('text-component-4', modeTwo);
207   -schemasMap.set('text-component-5', modeTwo);
208   -schemasMap.set('instrument-component-1', modeOne);
209   -schemasMap.set('instrument-component-2', modeThree);
210   -schemasMap.set('digital-dashboard-component', modeFour);
  183 +schemasMap.set(FrontComponent.TEXT_COMPONENT_1, modeOne);
  184 +schemasMap.set(FrontComponent.TEXT_COMPONENT_2, modeOne);
  185 +schemasMap.set(FrontComponent.TEXT_COMPONENT_3, modeOne);
  186 +schemasMap.set(FrontComponent.TEXT_COMPONENT_4, modeTwo);
  187 +schemasMap.set(FrontComponent.TEXT_COMPONENT_5, modeTwo);
  188 +schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, modeOne);
  189 +schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, modeThree);
  190 +schemasMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, modeFour);
... ...
... ... @@ -16,6 +16,7 @@
16 16 DEFAULT_WIDGET_WIDTH,
17 17 isBataBoardSharePage,
18 18 MoreActionEvent,
  19 + VisualComponentPermission,
19 20 } from '../config/config';
20 21 import {
21 22 addDataComponent,
... ... @@ -32,11 +33,10 @@
32 33 DataSource,
33 34 Layout,
34 35 } from '/@/api/dataBoard/model';
35   - import { frontComponentMap } from './config/help';
  36 + import { frontComponentMap } from '../components/help';
36 37 import { calcScale } from './config/util';
37 38 import { useMessage } from '/@/hooks/web/useMessage';
38 39 import { DataBoardLayoutInfo } from '../types/type';
39   - import { WidgetComponentType } from './config/visualOptions';
40 40 import Authority from '/@/components/Authority/src/Authority.vue';
41 41 import { useSocketConnect } from '../hook/useSocketConnect';
42 42 import { buildUUID } from '/@/utils/uuid';
... ... @@ -44,6 +44,7 @@
44 44 import trendIcon from '/@/assets/svg/trend.svg';
45 45 import backIcon from '/@/assets/images/back.png';
46 46 import { useCalcGridLayout } from '../hook/useCalcGridLayout';
  47 + import { FrontComponent } from '../const/const';
47 48
48 49 const ROUTE = useRoute();
49 50
... ... @@ -298,7 +299,7 @@
298 299
299 300 const getComponent = (record: DataComponentRecord) => {
300 301 const frontComponent = record.frontId;
301   - const component = frontComponentMap.get(frontComponent as WidgetComponentType);
  302 + const component = frontComponentMap.get(frontComponent as FrontComponent);
302 303 return component?.Component;
303 304 };
304 305
... ... @@ -307,8 +308,8 @@
307 308 dataSourceRecord: DataSource
308 309 ) => {
309 310 const frontComponent = record.frontId;
310   - const component = frontComponentMap.get(frontComponent as WidgetComponentType);
311   - return component?.transformConfig(component.ComponentConfig, record, dataSourceRecord);
  311 + const component = frontComponentMap.get(frontComponent as FrontComponent);
  312 + return component?.transformConfig(component.ComponentConfig || {}, record, dataSourceRecord);
312 313 };
313 314
314 315 const handleUpdate = async (id: string) => {
... ... @@ -360,9 +361,7 @@
360 361 await deleteDataComponent({ dataBoardId, ids: [id] });
361 362 createMessage.success('删除成功');
362 363 await getDataBoardComponent();
363   - } catch (error) {
364   - // createMessage.error('删除失败');
365   - }
  364 + } catch (error) {}
366 365 };
367 366
368 367 const [registerHistoryDataModal, historyDataModalMethod] = useModal();
... ... @@ -391,7 +390,7 @@
391 390 </div>
392 391 </template>
393 392 <template #extra>
394   - <Authority value="api:yt:data_component:add:post">
  393 + <Authority :value="VisualComponentPermission.CREATE">
395 394 <Button v-if="!getIsSharePage" type="primary" @click="handleOpenCreatePanel">
396 395 创建组件
397 396 </Button>
... ... @@ -439,7 +438,6 @@
439 438 :data-source="item.record.dataSource"
440 439 >
441 440 <template #header>
442   - <!-- <div>header</div> -->
443 441 <BaseWidgetHeader
444 442 :record="item.record.dataSource"
445 443 :id="item.record.id"
... ... @@ -448,11 +446,6 @@
448 446 >
449 447 <template #moreAction>
450 448 <Tooltip title="趋势">
451   - <!-- <LineChartOutlined
452   - v-if="!getIsSharePage"
453   - class="cursor-pointer mx-1"
454   - @click="handleOpenHistroyDataModal(item.record.dataSource)"
455   - /> -->
456 449 <img
457 450 :src="trendIcon"
458 451 v-if="!getIsSharePage"
... ... @@ -494,14 +487,18 @@
494 487 .vue-grid-item:not(.vue-grid-placeholder) {
495 488 background: #fff;
496 489 border: none !important;
  490 +
497 491 /* border: 1px solid black; */
498 492 }
  493 +
499 494 .vue-grid-item .resizing {
500 495 opacity: 0.9;
501 496 }
  497 +
502 498 .vue-grid-item .static {
503 499 background: #cce;
504 500 }
  501 +
505 502 .vue-grid-item .text {
506 503 font-size: 24px;
507 504 text-align: center;
... ... @@ -514,16 +511,20 @@
514 511 height: 100%;
515 512 width: 100%;
516 513 }
  514 +
517 515 .vue-grid-item .no-drag {
518 516 height: 100%;
519 517 width: 100%;
520 518 }
  519 +
521 520 .vue-grid-item .minMax {
522 521 font-size: 12px;
523 522 }
  523 +
524 524 .vue-grid-item .add {
525 525 cursor: pointer;
526 526 }
  527 +
527 528 .vue-draggable-handle {
528 529 position: absolute;
529 530 width: 20px;
... ... @@ -540,12 +541,6 @@
540 541 cursor: pointer;
541 542 }
542 543
543   - // .container {
544   - // display: grid;
545   - // grid-template-columns: 3;
546   - // grid-row: 3;
547   - // }
548   -
549 544 .grid-item-layout {
550 545 overflow: hidden;
551 546 border: 1px solid #eee !important;
... ...
... ... @@ -8,6 +8,10 @@ interface GapRecord {
8 8 endIndex: Nullable<number>;
9 9 }
10 10
  11 +/**
  12 + * @description calculate where to place the component
  13 + * @returns
  14 + */
11 15 export function useCalcGridLayout() {
12 16 const calcLayoutInfo = (
13 17 layoutInfo: Layout[],
... ...
... ... @@ -7,7 +7,7 @@
7 7 import { useMessage } from '/@/hooks/web/useMessage';
8 8 import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
9 9 import { DropMenu } from '/@/components/Dropdown';
10   - import { DATA_BOARD_SHARE_URL, MoreActionEvent } from './config/config';
  10 + import { DATA_BOARD_SHARE_URL, MoreActionEvent, VisualBoardPermission } from './config/config';
11 11 import { useModal } from '/@/components/Modal';
12 12 import PanelDetailModal from './components/PanelDetailModal.vue';
13 13 import { getDataBoardList, deleteDataBoard } from '/@/api/dataBoard';
... ... @@ -36,7 +36,7 @@
36 36 labelWidth: 80,
37 37 layout: 'inline',
38 38 submitButtonOptions: {
39   - loading: loading,
  39 + loading: loading as unknown as boolean,
40 40 },
41 41 submitFunc: async () => {
42 42 try {
... ... @@ -51,7 +51,7 @@
51 51 },
52 52 });
53 53
54   - //分页相关
  54 + // about pagination
55 55 const page = ref(1);
56 56 const pageSize = ref(10);
57 57 const total = ref(0);
... ... @@ -90,8 +90,8 @@
90 90
91 91 const { hasPermission } = usePermission();
92 92 const dropMenuList = computed<DropMenu[]>(() => {
93   - const hasUpdatePermission = hasPermission('api:yt:data_board:update:update');
94   - const hasDeletePermission = hasPermission('api:yt:data_board:delete');
  93 + const hasUpdatePermission = hasPermission(VisualBoardPermission.UPDATE);
  94 + const hasDeletePermission = hasPermission(VisualBoardPermission.DELETE);
95 95 const basicMenu: DropMenu[] = [];
96 96 if (hasUpdatePermission)
97 97 basicMenu.push({
... ... @@ -147,31 +147,21 @@
147 147 };
148 148
149 149 const handleRemove = async (record: DataBoardRecord) => {
150   - // TODO 删除确认
151 150 try {
152 151 await deleteDataBoard([record.id]);
153 152 createMessage.success('删除成功');
154 153 await getDatasource();
155   - } catch (error) {
156   - // createMessage.error('删除失败');
157   - }
  154 + } catch (error) {}
158 155 };
159 156
160 157 const [registerModal, { openModal }] = useModal();
161 158
162 159 const handleViewBoard = (record: DataBoardRecord) => {
163   - const hasDetailPermission = hasPermission('api:yt:data_component:list');
  160 + const hasDetailPermission = hasPermission(VisualBoardPermission.DETAIL);
164 161 if (hasDetailPermission) {
165 162 const boardId = encode(record.id);
166 163 const boardName = encode(record.name);
167 164 router.push(`/visual/board/detail/${boardId}/${boardName}`);
168   - // router.push({
169   - // name: 'visualBoardDetail'
170   - // params: {
171   - // boardId: encode(record.id),
172   - // boardName: encode(record.name),
173   - // },
174   - // });
175 165 } else createMessage.warning('没有权限');
176 166 };
177 167
... ... @@ -186,8 +176,6 @@
186 176 '.ant-spin-container'
187 177 ) as HTMLElement;
188 178 listContainerEl &&
189   - // (listContainerEl.style.minHeight = listContainerHeight + 'px') &&
190   - // (listContainerEl.style.maxHeight = listContainerHeight + 'px') &&
191 179 (listContainerEl.style.height = listContainerHeight + 'px') &&
192 180 (listContainerEl.style.overflowY = 'auto') &&
193 181 (listContainerEl.style.overflowX = 'hidden');
... ... @@ -210,7 +198,6 @@
210 198 <div class="bg-light-100 mb-6 w-full p-3 search-form">
211 199 <BasicForm class="flex-auto w-full" @register="searchFormRegister" />
212 200 </div>
213   - <!-- <div> </div> -->
214 201 <Spin :spinning="loading">
215 202 <List
216 203 ref="listEL"
... ... @@ -235,7 +222,6 @@
235 222 <MoreOutlined class="rotate-90 transform cursor-pointer" />
236 223 </Dropdown>
237 224 </template>
238   - <!-- <template #cover>title</template> -->
239 225 <section @click="handleViewBoard(item)">
240 226 <div class="flex data-card__info">
241 227 <div>
... ... @@ -247,16 +233,19 @@
247 233 </Statistic>
248 234 </div>
249 235 </div>
250   - <div class="flex justify-between mt-4 text-sm" style="color: #999999">
  236 + <div class="flex justify-between mt-4 text-sm" style="color: #999">
251 237 <div>
252 238 <span>
253 239 {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }}
254 240 </span>
255   - <!-- <span v-if="item.viewType === ViewType.PUBLIC_VIEW">
  241 + <span
  242 + style="display: none"
  243 + v-if="item.viewType === ViewType.PUBLIC_VIEW && false"
  244 + >
256 245 <Tooltip title="点击复制分享链接">
257 246 <ShareAltOutlined class="ml-2" @click.stop="handleCopyShareUrl(item)" />
258 247 </Tooltip>
259   - </span> -->
  248 + </span>
260 249 </div>
261 250 <div>{{ item.createTime }}</div>
262 251 </div>
... ... @@ -278,15 +267,17 @@
278 267 .data-card:deep(.ant-card-head-title) {
279 268 padding: 0;
280 269 }
  270 +
281 271 .data-card:deep(.ant-card-extra) {
282 272 padding: 0;
283 273 }
  274 +
284 275 .data-card:deep(.ant-card-body) {
285 276 padding: 20px;
286 277 }
287 278
288 279 .data-card__info {
289   - color: #666666;
  280 + color: #666;
290 281
291 282 &::before {
292 283 content: '';
... ... @@ -299,8 +290,10 @@
299 290
300 291 .search-form {
301 292 width: 100%;
  293 +
302 294 form {
303 295 width: 100%;
  296 +
304 297 :deep(.ant-row) {
305 298 width: 100%;
306 299 }
... ...
  1 +import { Component } from 'vue';
1 2 import { Layout } from 'vue3-grid-layout';
  3 +import { FrontComponent, FrontComponentCategory, visualOptionField } from '../const/const';
  4 +import { RadioRecord } from '../detail/config/util';
2 5 import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
3 6
4 7 export type FrontDataSourceRecord = DataSource;
... ... @@ -8,3 +11,39 @@ export type DataBoardLayoutInfo = Layout & {
8 11 width?: number;
9 12 height?: number;
10 13 };
  14 +
  15 +export interface ComponentConfig {
  16 + Component: Component;
  17 + ComponentName?: string;
  18 + ComponentKey: FrontComponent;
  19 + ComponentConfig?: Recordable;
  20 + ComponentCategory: FrontComponentCategory;
  21 + transformConfig: (
  22 + componentConfig: Recordable,
  23 + record: DataComponentRecord,
  24 + dataSourceRecord: DataSource
  25 + ) => Recordable;
  26 +}
  27 +
  28 +export interface VisualOptionParams {
  29 + [visualOptionField.FONT_COLOR]: string;
  30 + [visualOptionField.UNIT]: string;
  31 + [visualOptionField.ICON_COLOR]: string;
  32 + [visualOptionField.ICON]: string;
  33 + [visualOptionField.FIRST_PHASE_COLOR]: string;
  34 + [visualOptionField.SECOND_PHASE_COLOR]: string;
  35 + [visualOptionField.THIRD_PHASE_COLOR]: string;
  36 + [visualOptionField.FIRST_PHASE_VALUE]: string;
  37 + [visualOptionField.SECOND_PHASE_VALUE]: string;
  38 + [visualOptionField.THIRD_PHASE_VALUE]: string;
  39 +}
  40 +
  41 +export interface VisualComponentProps<Layout = Recordable, Value = Recordable> {
  42 + value?: Value;
  43 + layout?: Layout;
  44 + radio?: RadioRecord;
  45 + random?: boolean;
  46 + add?: (key: string, method: Fn) => void;
  47 + update?: () => void;
  48 + remove?: (key: string) => void;
  49 +}
... ...