Procházet zdrojové kódy

feat: 新增数据源设置

liaojiaxing před 9 měsíci
rodič
revize
57800bb41a

+ 0 - 8
apps/shalu-bigscreen-designer/src/api/index.ts

@@ -1,8 +0,0 @@
-import { http } from "@/utils/http";
-import type { PageParams, PageResponse } from "./model";
-
-export const editPageDesignApi = (data: PageParams) =>
-  http.post("/api/form/EditPageDesign", data);
-
-export const getPageDesignApi = (data: { id: string}) => 
-  http.post<PageResponse>(`/api/form/GetPageDesign`, data);

+ 14 - 0
apps/shalu-bigscreen-designer/src/mock/index.ts

@@ -42,4 +42,18 @@ export default [
       ],
     },
   },
+  {
+    url: '/mock/api/get/example/pie',
+    method: 'post',
+    timeout: 2000,
+    response: {
+      code: 0,
+      data: [
+        { name: '一月', apple: 1654, vivo: 1234, mi: 3421 },
+        { name: '二月', apple: 4322, vivo: 4321, mi: 2343 },
+        { name: '三月', apple: 4345, vivo: 3221, mi: 3221  },
+        { name: '四月', apple: 3222, vivo: 1222, mi: 1222 },
+      ],
+    },
+  },
 ] as MockMethod[]

+ 22 - 5
apps/shalu-bigscreen-designer/src/store/modules/project.ts

@@ -4,7 +4,7 @@ import { asyncComponentAll } from "@shalu/dashboard-ui";
 import { ScreenFillEnum } from "@/enum/screenFillEnum";
 import { update, defaultsDeep } from "lodash";
 import { getNormalizedContainer } from "@/utils/common";
-import { editPageDesignApi } from "@/api/index";
+import { editPageDesignApi } from "@shalu/service";
 import { message } from "ant-design-vue";
 
 type ProjectState = {
@@ -63,15 +63,19 @@ export const useProjectStore = defineStore({
     showReffer: true,
   }),
   getters: {
+    // 参考线
     referLines(state) {
       return state.projectInfo.pages[state.activePageIndex].referLines;
     },
+    // 当前全部元素
     elements(state) {
       return state.projectInfo.pages[state.activePageIndex].elements;
     },
+    // 当前页面
     currentPage(state) {
       return state.projectInfo.pages[state.activePageIndex];
     },
+    // 当前选中元素
     currentSelectedElements(state) {
       const list: CustomElement[] = [];
       state.projectInfo.pages[state.activePageIndex].elements.forEach((item) => {
@@ -92,9 +96,22 @@ export const useProjectStore = defineStore({
   },
   actions: {
     setProjectInfo(info: any) {
-      Object.assign(this.projectInfo, info);
+      this.projectInfo = info;
       localStorage.setItem(CURRENT_PROJECT, JSON.stringify(info));
     },
+    createProject() {
+      const project = {
+        name: "新建项目",
+        description: "这是一个新建项目",
+        sizeType: "custom",
+        width: 1280,
+        height: 720,
+        fillType: ScreenFillEnum.AUTO,
+        pages: [{ ...defaultPage }],
+      };
+      this.setProjectInfo(project as unknown as ProjectInfo);
+      return project;
+    },
     getCurrentProjectInfo(): ProjectInfo | undefined {
       let info = JSON.parse(localStorage.getItem(CURRENT_PROJECT) || "null");
       if (!info) {
@@ -135,7 +152,7 @@ export const useProjectStore = defineStore({
       }
     },
     // 添加组件
-    async addElement(element: any, position?: 'center', cancelSelect?: boolean = false) {
+    async addElement(element: any, position?: 'center', cancelSelect: boolean = false) {
       this.addCompData = null;
       if (!element) return;
 
@@ -244,9 +261,9 @@ export const useProjectStore = defineStore({
       this.projectInfo.fillType = fillType;
     },
     // 保存当前项目到服务器
-    async handleSaveProject() {
+    async handleSaveProject(pageId: string) {
       const params = {
-        appPageId: this.projectInfo.pageId,
+        appPageId: pageId,
         json: JSON.stringify(this.projectInfo),
         html: "",
         js: "",

+ 7 - 3
apps/shalu-bigscreen-designer/src/views/designer/index.vue

@@ -70,7 +70,7 @@ import { useAcionStore } from "@/store/modules/action";
 import { useRoute } from "vue-router";
 import { useRequest } from "vue-hooks-plus";
 import { useAppStore } from "@/store/modules/app";
-import { getPageDesignApi } from "@/api";
+import { getPageDesignApi } from "@shalu/service";
 
 import LayerManagement from "./component/LayerManagement.vue";
 import ComponentLibary from "./component/ComponentLibary.vue";
@@ -106,7 +106,7 @@ const { run, loading: loadingPage } = useRequest(getPageDesignApi, {
 });
 
 /* 获取项目信息 */
-projectStore.getCurrentProjectInfo();
+// projectStore.getCurrentProjectInfo();
 // 传入pageId和token,获取页面信息
 if(route.query?.pageId && route.query?.token) {
   const { token, pageId } = route.query;
@@ -143,6 +143,10 @@ const handlePreview = () => {
 };
 
 const handleSave = async () => {
+  if(!route.query?.pageId) {
+    message.error("未找到页面Id");
+    return;
+  }
   try {
     loading.value = true;
     localStorage.setItem(
@@ -150,7 +154,7 @@ const handleSave = async () => {
       JSON.stringify(projectStore.projectInfo)
     );
 
-    await projectStore.handleSaveProject();
+    await projectStore.handleSaveProject(route.query?.pageId as string);
   } catch (e) {
     loading.value = false;
     message.error("保存失败");

+ 2 - 0
package.json

@@ -30,6 +30,8 @@
   },
   "dependencies": {
     "@shalu/dashboard-ui": "workspace:^",
+    "@shalu/service": "workspace:^",
+    "@shalu/utils": "workspace:^",
     "ant-design-vue": "^4.X",
     "element-plus": "^2.7.6",
     "echarts": "^5.5.1",

+ 34 - 0
packages/service/api/index.ts

@@ -0,0 +1,34 @@
+import { http } from "../http";
+import type { PageParams, PageResponse } from "./model";
+
+// 保存页面设计
+export const editPageDesignApi = (data: PageParams) =>
+  http.post("/api/form/EditPageDesign", data);
+// 读取页面设计
+export const getPageDesignApi = (data: { id: string}) => 
+  http.post<PageResponse>(`/api/form/GetPageDesign`, data);
+
+// 获取单页面数据源列表
+export function GetAllDataSourceColumnList(data: any) {
+  return http.post(`/api/page/GetAllDataSourceColumnList`, data);
+}
+
+// 获取数据表和视图表
+export function GetAllTablesAndViews(data: any) {
+  return http.post(`/api/table/GetAllTablesAndViews`, data);
+}
+
+// 获取全部基础数据类型
+export function GetAllBasicData(data: any) {
+  return http.post(`/api/system/GetAllBasicData`, data);
+}
+
+// 获取表视图列
+export function GetTableViewColumns(data: any) {
+  return http.post(`/api/table/GetTableViewColumns`, data);
+}
+
+// 获取表视图列多语言
+export function GetTableViewFieldKey(data: any) {
+  return http.post(`/api/table/GetTableViewFieldKey`, data);
+}

apps/shalu-bigscreen-designer/src/api/model.ts → packages/service/api/model.ts


apps/shalu-bigscreen-designer/src/utils/http/axios.ts → packages/service/http/axios.ts


apps/shalu-bigscreen-designer/src/utils/http/index.ts → packages/service/http/index.ts


+ 1 - 0
packages/service/index.ts

@@ -0,0 +1 @@
+export * from './api';

+ 12 - 0
packages/service/package.json

@@ -0,0 +1,12 @@
+{
+  "name": "@shalu/service",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.ts",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}

+ 43 - 2
packages/shalu-dashboard-ui/components/charts/DataConfig.vue

@@ -6,10 +6,12 @@
     :label-col="{ span: 8 }"
     :wrapper-col="{ span: 16 }"
   >
-    <Form.Item label="类型" name="sourceType">
+    <Form.Item label="数据源" name="sourceType">
       <Select v-model:value="formModel.sourceType">
         <SelectOption :value="DataSourceType.STATIC">静态数据</SelectOption>
-        <SelectOption :value="DataSourceType.API">动态数据</SelectOption>
+        <SelectOption :value="DataSourceType.API">API</SelectOption>
+        <SelectOption :value="DataSourceType.BASIC_PATH">基础数据源</SelectOption>
+        <SelectOption :value="DataSourceType.VIEW_CODE">视图源</SelectOption>
       </Select>
     </Form.Item>
     <!-- 静态数据 -->
@@ -66,6 +68,26 @@
         >
       </Form.Item>
     </template>
+    <!-- 基础数据 -->
+     <template v-else-if="formModel.sourceType === DataSourceType.BASIC_PATH">
+      <Form.Item label="基础数据源" name="basicPath">
+        <Input placeholder="请输入或选择" v-model:value="formModel.basicPath">
+          <template #addonAfter>
+            <SettingOutlined @click="handleSetOrigin('table')" />
+          </template>
+        </Input>
+      </Form.Item>
+    </template>
+    <!-- 视图源 -->
+    <template v-else-if="formModel.sourceType === DataSourceType.VIEW_CODE">
+      <Form.Item label="视图源" name="viewCode">
+        <Input placeholder="请输入或选择" v-model:value="formModel.viewCode">
+          <template #addonAfter>
+            <SettingOutlined @click="handleSetOrigin('view')"/>
+          </template>
+        </Input>
+      </Form.Item>
+    </template>
   </Form>
   <CodeEditorModal ref="codeEditorRef" title="编辑" @ok="handleCodeSave" />
 </template>
@@ -86,6 +108,8 @@ import {
 } from "ant-design-vue";
 import { DataSourceType } from "./chartEnum";
 import { CodeEditorModal, type CodeEditorModalInstance } from "../codeEditor";
+import { SettingOutlined } from "@ant-design/icons-vue";
+import { getDataOrigin } from "@shalu/utils";
 
 /**
  * 通用数据data约定内容结构
@@ -121,6 +145,10 @@ const formModel = ref({
   refreshTime: 0,
   // 数据处理
   dataProcess: "",
+  // 基础数据源
+  basicPath: "",
+  // 视图源
+  viewCode: "",
 });
 
 const handleRefreshTimeChange = (val: unknown) => {
@@ -147,6 +175,19 @@ const handleCodeSave = (code: string) => {
   }
 };
 
+// 设置数据源
+const handleSetOrigin = async (key: 'table' | 'view') => {
+  const res = await getDataOrigin(key);
+  console.log(res)
+  if(key === 'table') {
+    formModel.value.basicPath = res.value;
+    formModel.value.viewCode = "";
+  } else {
+    formModel.value.viewCode = res.value;
+    formModel.value.basicPath = "";
+  }
+}
+
 watch(
   () => props.dataSource,
   (val) => {

+ 2 - 2
packages/shalu-dashboard-ui/components/charts/Pie/BasicPie/src/props.ts

@@ -104,10 +104,10 @@ export const defaultPropsValue: EChartsOption = {
         (res) => {
           // 取出列表
           const data = res.data;
-
+          
           // 系列数据
           const series = [
-            { type: 'pie', name: '价格', data: data.map(item => item.price) },
+            { type: 'pie', name: '月份', data: data.map(item => ({value: item.apple, name: item.name})) }
           ];
 
           // 返回图表数据

+ 4 - 0
packages/shalu-dashboard-ui/components/charts/chartEnum.ts

@@ -4,4 +4,8 @@ export enum DataSourceType {
   STATIC,
   /* 接口数据 */
   API,
+  /* 基础数据 */
+  BASIC_PATH,
+  /* 视图源 */
+  VIEW_CODE,
 }

+ 13 - 5
packages/shalu-dashboard-ui/components/charts/hooks/useChartOptions.ts

@@ -5,7 +5,6 @@ import { useRequest } from "vue-hooks-plus";
 import { DataSourceType } from "../chartEnum";
 import { message } from "ant-design-vue";
 import { cllJsCode } from "../utils";
-
 export const useChartOptions = (chartProps: Record<string, any>) => {
   const dataSource = chartProps.dataSource || {};
   const xAxis = ref<EChartsOption["xAxis"]>();
@@ -47,9 +46,8 @@ export const useChartOptions = (chartProps: Record<string, any>) => {
           // 请求后数据处理
           res = await cllJsCode(chartProps.dataSource.dataProcess, JSON.stringify(val));
         }
-    
-        xAxis.value = res.xAxis || { data: res.xData };
-        yAxis.value = res.yAxis || { data: res.yData };
+        xAxis.value = res.xAxis || res.xData ? { data: res.xData } : xAxis.value;
+        yAxis.value = res.yAxis || res.yData ? { data: res.yData } : yAxis.value;
         series.value = res.series;
       }
     },
@@ -58,6 +56,7 @@ export const useChartOptions = (chartProps: Record<string, any>) => {
     }
   );
 
+  // 数据源设置
   watch(
     () => [
       chartProps.dataSource.sourceType,
@@ -65,9 +64,12 @@ export const useChartOptions = (chartProps: Record<string, any>) => {
       chartProps.dataSource.data
     ],
     () => {
+      // 请求接口
       if (chartProps.dataSource.sourceType === DataSourceType.API) {
         refresh();
-      } else {
+      }
+      // 静态数据
+      if(chartProps.dataSource.sourceType === DataSourceType.STATIC) {
         cancel();
         const dataSource = chartProps.dataSource || {};
         const { xData, yData, series: seriesData } = dataSource?.data || {};
@@ -79,6 +81,12 @@ export const useChartOptions = (chartProps: Record<string, any>) => {
         }
         series.value = seriesData;
       }
+      // 视图或基础数据源
+      if([DataSourceType.BASIC_PATH, DataSourceType.VIEW_CODE].includes(chartProps.dataSource.sourceType)) {
+        window?.mabp && window.mabp.$doLoadComponentData(obj).then(function (res) {
+          itemList.value = res.data;
+        });
+      }
     },
     {
       deep: true,

+ 11 - 7
packages/shalu-dashboard-ui/components/charts/types.ts

@@ -9,17 +9,21 @@ export interface ChartData {
 export interface DataSource {
   // 类型
   sourceType: DataSourceType.STATIC,
+  // 基础数据源
+  basicPath?: string;
+  // 视图源
+  viewCode?: string;
   // 数据
-  data?: ChartData,
+  data?: ChartData;
   // 接口相关
-  url?: string,
+  url?: string;
   // 请求方式
-  method?: 'GET' | 'POST',
+  method?: 'GET' | 'POST';
   // 请求参数
-  params?: any,
+  params?: any;
   // 请求头
-  headers?: Record<string, any>,
-  refreshTime?: number,
+  headers?: Record<string, any>;
+  refreshTime?: number;
   // 数据处理
-  dataProcess: () => any[],
+  dataProcess: () => any[];
 }

+ 1 - 1
packages/shalu-dashboard-ui/components/text/Title/src/index.vue

@@ -7,7 +7,7 @@
 <script lang="ts">
 import { computed, defineComponent } from "vue";
 import { titleProps } from "./props";
-import { transformStyle } from "../../../../utils/transStyle";
+import { transformStyle } from "@shalu/utils";
 
 export default defineComponent({
   name: "DTitle",

+ 2 - 1
packages/shalu-dashboard-ui/tsconfig.app.json

@@ -18,6 +18,7 @@
     "jsx": "preserve",
     "declaration": true,
     "declarationDir": "lib",
+    "jsxImportSource": "vue",
 
     /* Linting */
     "strict": true,
@@ -30,6 +31,6 @@
     "components/**/*.tsx",
     "components/**/*.vue",
     "utils/**/*.ts",
-    "types/**/*.ts",
+    "types/**/*.ts", "utils/getDataOrigin.tsx",
   ]
 }

+ 259 - 0
packages/utils/getDataOrigin.tsx

@@ -0,0 +1,259 @@
+import { ref, createApp } from "vue";
+import { ElButton, ElDialog, ElInput, ElTree, ElMessage } from "element-plus";
+import {
+  GetAllTablesAndViews,
+  GetAllBasicData,
+  GetTableViewColumns,
+  GetTableViewFieldKey,
+} from "@shalu/service";
+
+import type Node from "element-plus/es/components/tree/src/model/node";
+
+type ViewItem = {
+  name: string;
+  schemaName: string;
+  type: number;
+  id: string;
+};
+
+type BisicData = {
+  disabled?: boolean;
+  displayOrder?: number;
+  id?: string;
+  isDisabled?: boolean;
+  langName?: string;
+  name: string;
+  path?: string;
+  updateTime?: string;
+  value?: string;
+};
+
+/**
+ * 获取系统内部的数据源
+ * @param type 数据源类型
+ * @returns 数据源
+ */
+export const getDataOrigin = (
+  type: "table" | "view",
+  datatype = ''
+): Promise<{ value: string; result?: any }> => {
+  const data = ref();
+  const loading = ref(false);
+  const val = ref("");
+  const filterText = ref("");
+  const treeRef = ref();
+  const valId = ref("");
+
+  // 获取视图
+  if (type === "view") {
+    data.value = [
+      { label: "系统视图", children: [] },
+      { label: "数据源视图", children: [] },
+    ];
+    loading.value = true;
+    GetAllTablesAndViews({ types: type })
+      .then((res: any) => {
+        const { bpmViewTables = [] } = res || {};
+        bpmViewTables.forEach((item: ViewItem) => {
+          const { name, schemaName, type, id } = item;
+          if (data.value[type - 1]) {
+            // type 1: 系统视图 2: 数据源视图
+            data.value[type - 1].children.push({
+              label: `${schemaName}(${name})`,
+              value: schemaName,
+              id,
+            });
+          }
+        });
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  }
+
+  // 动态加载数据
+  const onload = (node: Node, resove: (data: BisicData[]) => void) => {
+    if (node.level === 0) {
+      return resove([{ name: "基础数据" }]);
+    }
+    const data = node.data;
+    GetAllBasicData({
+      currentPage: 1,
+      pageSize: 999,
+      orderByProperty: "id",
+      Ascending: true,
+      totalPage: 1,
+      totalCount: 1,
+      filters: data?.id ? [{ name: "parentId", value: data.id }] : null,
+    }).then((res: any) => {
+      resove(res || []);
+    });
+  };
+
+  const getTreeVNode = () => {
+    return type === "table" ? (
+      <ElTree
+        ref={treeRef}
+        lazy={true}
+        load={onload}
+        props={{
+          label: (data: BisicData) =>
+            data.path ? `${data.path}(${data.name})` : data.name,
+          children: "children",
+        }}
+        onNode-click={(node: BisicData) => {
+          if (node?.path) {
+            val.value = node.path;
+          }
+        }}
+        filterNodeMethod={(value: string, data: BisicData) => {
+          // treeRef.value?.onload();
+          return (
+            data.path?.toUpperCase().includes(value.toUpperCase()) ||
+            data.name.toUpperCase().includes(value.toUpperCase())
+          );
+        }}
+        empty-text={"暂无数据"}
+      />
+    ) : (
+      <ElTree
+        ref={treeRef}
+        data={data.value}
+        defaultExpandAll={true}
+        onNode-click={(node) => {
+          if (node?.value && node.id) {
+            val.value = node.value;
+            valId.value = node.id;
+          }
+        }}
+        filterNodeMethod={(value: string, data) => {
+          return data.label?.includes(value);
+        }}
+        empty-text={"暂无数据"}
+      />
+    );
+  };
+
+  return new Promise((resolve, reject) => {
+    const mountNode = document.createElement("div");
+    const app = createApp({
+      render() {
+        return (
+          <ElDialog
+            modelValue={true}
+            title={type === "table" ? "选择数据集" : "选择视图表"}
+            width="800px"
+            style={{ height: "600px" }}
+            v-slots={{
+              footer: () => (
+                <>
+                  {type === "view" &&
+                  ["fm-search-table", "fm-subform"].includes(datatype) ? (
+                    <ElButton
+                      disabled={!val.value}
+                      type="primary"
+                      onClick={async () => {
+                        if (valId.value) {
+                          try {
+                            const tableViewResult =
+                              await GetTableViewColumns({
+                                currentPage: 1,
+                                pageSize: 2147483647,
+                                orderByProperty: "DisplayOrder",
+                                Ascending: true,
+                                totalPage: 1,
+                                totalCount: 1,
+                                filters: [
+                                  { name: "filterText" },
+                                  { name: "tableId", value: valId.value },
+                                ],
+                              });
+                            if (tableViewResult.model) {
+                              tableViewResult.model =
+                                tableViewResult.model.filter(
+                                  (item) => item.isDisplayEnable
+                                );
+                              const promises = tableViewResult.model.map(
+                                async (item) => {
+                                  if (!item.langName) {
+                                    return {
+                                      ...item,
+                                      enName: "",
+                                    };
+                                  } else {
+                                    const keyResult =
+                                      await GetTableViewFieldKey(
+                                        { key: item.langName },
+                                        token
+                                      );
+                                    return {
+                                      ...item,
+                                      enName: keyResult.en,
+                                    };
+                                  }
+                                }
+                              );
+                              tableViewResult.model = await Promise.all(
+                                promises
+                              );
+                            }
+
+                            resolve({
+                              value: val.value,
+                              result: tableViewResult,
+                            });
+                            document.body.removeChild(mountNode);
+                          } catch (error) {
+                            ElMessage.error("未知错误!");
+                          }
+                        }
+                      }}
+                    >
+                      确定并生成表列
+                    </ElButton>
+                  ) : (
+                    ""
+                  )}
+
+                  <ElButton
+                    type="primary"
+                    disabled={!val.value}
+                    onClick={() => {
+                      resolve({ value: val.value });
+                      document.body.removeChild(mountNode);
+                    }}
+                  >
+                    确定
+                  </ElButton>
+                </>
+              ),
+            }}
+            onClose={() => {
+              reject("close");
+              document.body.removeChild(mountNode);
+            }}
+          >
+            <ElInput
+              size="small"
+              placeholder="请输入关键字进行搜索"
+              v-model={filterText.value}
+              onInput={() => {
+                treeRef.value?.filter(filterText.value);
+              }}
+              style={{
+                marginBottom: "10px",
+                height: "35px",
+                fontSize: "14px",
+              }}
+            />
+            <div style={{ height: "379px", overflow: "auto" }}>
+              {getTreeVNode()}
+            </div>
+          </ElDialog>
+        );
+      },
+    });
+    document.body.appendChild(mountNode);
+    app.mount(mountNode);
+  });
+};

+ 2 - 0
packages/utils/index.ts

@@ -0,0 +1,2 @@
+export * from './getDataOrigin';
+export * from './transStyle';

+ 12 - 0
packages/utils/package.json

@@ -0,0 +1,12 @@
+{
+  "name": "@shalu/utils",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.ts",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}

packages/shalu-dashboard-ui/utils/transStyle.ts → packages/utils/transStyle.ts


+ 10 - 0
pnpm-lock.yaml

@@ -11,6 +11,12 @@ importers:
       '@shalu/dashboard-ui':
         specifier: workspace:^
         version: link:packages/shalu-dashboard-ui
+      '@shalu/service':
+        specifier: workspace:^
+        version: link:packages/service
+      '@shalu/utils':
+        specifier: workspace:^
+        version: link:packages/utils
       ant-design-vue:
         specifier: ^4.X
         version: 4.2.3(vue@3.4.31)
@@ -134,6 +140,8 @@ importers:
         specifier: ^3.0.2
         version: 3.0.2(esbuild@0.23.0)(mockjs@1.1.0)(vite@5.3.4)
 
+  packages/service: {}
+
   packages/shalu-dashboard-ui:
     dependencies:
       '@types/lodash-es':
@@ -240,6 +248,8 @@ importers:
         specifier: ^3.9.1
         version: 3.9.1(@types/node@20.14.10)(typescript@5.5.3)(vite@5.3.4)
 
+  packages/utils: {}
+
 packages:
 
   /@achrinza/node-ipc@9.2.9: