fix: 初始化

This commit is contained in:
jiaojinfeng
2023-05-30 19:27:03 +08:00
commit 28db653900
4436 changed files with 500218 additions and 0 deletions

View File

@ -0,0 +1,146 @@
{
"name": "@crami/frame-lowcode",
"version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vite build",
"vite:dev": "vite",
"vite:build": "vite build",
"preview": "vite preview",
"lint": "vue-cli-service lint",
"lint:check": "vue-cli-service lint --no-fix",
"prettier": "prettier -c --write '**/*'",
"pretty-quick": "pretty-quick",
"analyz": "vue-cli-service build --mode analyz",
"toJs": "node ./scripts/tsToJs.js",
"tsc": "tsc --noEmit && vue-tsc --noEmit",
"vue-tsc": "vue-tsc --noEmit"
},
"dependencies": {
"icpx-platform": "workspace:*",
"@alilc/lowcode-types": "^1.0.15",
"@ant-design/icons-vue": "^6.1.0",
"@antv/x6": "^1.33.1",
"@antv/x6-vue-shape": "^1.4.2",
"@crami/http": "^2.0.10",
"@crami/lowcode-hooks": "^1.5.3-r2",
"@crami/lowcode-materials": "^0.0.35",
"@crami/lowcode-utils": "^1.5.2",
"@crami/lowcode-vue-renderer": "1.5.3-r7",
"@crami/ui": "^2.0.249",
"@crami/ui-types": "^2.0.105",
"@studio/formula": "^0.0.10",
"@types/lodash-es": "^4.17.6",
"ant-design-vue": "^3.1.1",
"axios": "^0.27.2",
"core-js": "^3.21.1",
"dayjs": "^1.11.5",
"echarts": "^4.9.0",
"lodash-es": "^4.17.21",
"moment":"^2.29.4",
"numeral": "^2.0.6",
"vue": "~3.2.37",
"vue-i18n": "~9.2.0-0",
"vue-router": "^4.0.14"
},
"devDependencies": {
"@babel/plugin-transform-spread": "^7.16.7",
"@babel/plugin-transform-typescript": "^7.16.8",
"@rollup/plugin-alias": "^3.1.9",
"@surely-vue/table": "2.4.7",
"@types/echarts": "^4.9.13",
"@types/lodash": "^4.14.181",
"@types/node": "^17.0.23",
"@types/nprogress": "^0.2.0",
"@types/numeral": "2.0.2",
"@types/store": "^2.0.2",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"@vitejs/plugin-legacy": "^1.8.0",
"@vitejs/plugin-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^1.3.9",
"@vue/cli-plugin-babel": "~5.0.4",
"@vue/cli-plugin-eslint": "~5.0.4",
"@vue/cli-plugin-router": "~5.0.4",
"@vue/cli-plugin-typescript": "~5.0.4",
"@vue/cli-plugin-vuex": "~5.0.4",
"@vue/cli-service": "~5.0.4",
"@vue/compiler-sfc": "~3.2.37",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.3",
"cheerio": "^1.0.0-rc.10",
"cross-env": "^7.0.3",
"echarts-wordcloud": "^1.1.3",
"eslint": "^8.12.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.6.0",
"express": "^4.17.3",
"husky": "^7.0.4",
"js-base64": "^3.7.2",
"js-md5": "^0.7.3",
"kill-port-process": "^3.0.1",
"less": "^4.1.3",
"less-loader": "^10.2.0",
"less-vars-to-js": "^1.3.0",
"lint-staged": "^12.3.7",
"mockjs": "^1.1.0",
"prettier": "^2.6.2",
"prettier-plugin-style-order": "^0.2.2",
"prettier-quick": "^0.0.5",
"raw-loader": "^4.0.2",
"stylelint": "^14.6.1",
"stylelint-config-css-modules": "^4.1.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^25.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.5.0",
"stylelint-no-unsupported-browser-features": "^5.0.3",
"stylelint-order": "^5.0.0",
"stylelint-webpack-plugin": "^3.2.0",
"typescript": "~4.6.3",
"umi-mock-middleware": "^1.0.0",
"vite": "^3.2.1",
"vite-plugin-externals": "^0.5.1",
"vite-plugin-imp": "^2.3.0",
"vite-plugin-pages": "^0.12.2",
"vite-plugin-progress": "^0.0.3",
"vite-plugin-style-import": "^1.4.1",
"vite-plugin-svg-icons": "^2.0.1",
"vue-eslint-parser": "^7.11.0",
"vue-tsc": "^0.30.6",
"webpack-bundle-analyzer": "^4.5.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged && pretty-quick --staged"
}
},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"vue-cli-service lint",
"git add"
]
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@babel/core",
"camunda-bpmn-js-behaviors",
"preact",
"webpack",
"@bpmn-io/properties-panel",
"diagram-js",
"rollup"
]
}
},
"resolutions": {
"esbuild": "0.14.34",
"@crami/router": "1.0.7",
"@types/node": "17.0.36"
}
}

View File

@ -0,0 +1,165 @@
import { config } from '@crami/lowcode-vue-renderer';
import { exports } from '../../bootstrap';
import { Http } from '@crami/http';
import { HModal } from '@crami/ui';
interface ModelCommonParam {
modelName: string;
// dsName: string;
// projectCode: string;
// version: string;
}
interface ModelSaveParam {
data: { [prop in string]: any }[];
}
interface ModelUpdateParam {
id: string;
data: { [prop in string]: any }[];
}
interface ModelDetailParam {
id: string;
}
interface ModelDeleteParam {
id: string;
}
interface ModelDeleteAllParam {
ids: string[];
deleteForeignAfterConfirmation?: boolean;
}
interface ModelBatchParam {
ids: string[];
}
function commonParam() {
const { projectCode, dsName, version, appId, appCode } = exports.router.currentRoute.value.meta;
const userId = config.getGlobalDataSourceParams()?.userId;
return {
dsName,
projectCode,
appId,
appCode,
userId,
version,
lang: 'zhs',
};
}
/** 单表 */
// 保存
export function saveByModel(param: ModelSaveParam & ModelCommonParam): Promise<any> {
const _common = commonParam();
return Http.post({ url: `/loki-server/domain/save`, params: Object.assign(_common, param) });
}
// 更新
export function updateByModel(param: ModelUpdateParam & ModelCommonParam): Promise<any> {
const _common = commonParam();
return Http.post({ url: `/loki-server/domain/update`, params: Object.assign(_common, param) });
}
// 删除
export function deleteByModel(param: ModelDeleteParam & ModelCommonParam): Promise<any> {
const _common = commonParam();
return Http.request({
url: `/loki-server/domain/id`,
method: 'DELETE',
data: Object.assign(_common, param),
});
}
// 批量删除
export async function deleteAllByModel(
param: ModelDeleteAllParam & ModelCommonParam,
): Promise<any> {
param.deleteForeignAfterConfirmation = false;
const data = await getForeignKeyByModel(param.modelName);
const hasTip = data?.some(foreignKey => {
return foreignKey?.foreignKeyList.some(item => {
// 当
return item.deletingSourceType === 'deleteAfterConfirmation';
});
});
if (hasTip) {
param.deleteForeignAfterConfirmation = await new Promise(resolve => {
HModal.confirm({
title: '删除提示',
content: '是否要删除从表数据?',
okText: '删除',
okType: 'danger',
cancelText: '不删除',
onOk() {
resolve(true);
},
onCancel() {
resolve(false);
},
});
});
}
const _common = commonParam();
return Http.request({
url: `/loki-server/domain/ids`,
method: 'DELETE',
data: Object.assign(_common, param),
});
}
// 单条详情
export function infoByModel(param: ModelDetailParam & ModelCommonParam): Promise<any> {
const _common = commonParam();
return Http.request({
url: `/loki-server/domain/info`,
method: 'GET',
params: Object.assign(_common, param),
});
}
// 批量详情
export function batchInfoByModel(param: ModelBatchParam & ModelCommonParam): Promise<any> {
const _common = commonParam();
return Http.request({
url: `/loki-server/domain/batchInfo`,
method: 'GET',
params: Object.assign(_common, param),
});
}
/** 多表 主外键 */
// 主外键保存
export function saveByModelFk(param): Promise<any> {
const _common = commonParam();
return Http.post({ url: '/loki-server/domain/saveFk', params: Object.assign(_common, param) });
}
// 主外键更新
export function updateByModelFk(param): Promise<any> {
const _common = commonParam();
return Http.post({ url: '/loki-server/domain/updateFk', params: Object.assign(_common, param) });
}
const modelInfoCache = new Map();
/**
* 获取模型主外键信息
* @param modelName
* @returns
*/
export function getForeignKeyByModel(modelName): Promise<any> {
const _cache = modelInfoCache.get(modelName);
if (_cache) {
return _cache;
}
const _common = commonParam();
return Http.get({
url: '/loki-server/model/foreignKey',
params: {
fileName: modelName,
..._common,
},
}).then(data => {
modelInfoCache.set(modelName, data.data);
return data.data;
});
}

View File

@ -0,0 +1,30 @@
import { Http } from '@crami/http';
import { config } from '@crami/lowcode-vue-renderer';
export interface PageSchema {
componentsMap: Array<any>;
componentsTree: Array<any>;
}
/**
* 获取schema
*
* @param pageId
* @return Promise<ResponseBody<PageSchema>>
*/
export function getSchemaById(params: {
pageId: string;
projectCode: string;
appCode: string;
}): Promise<any> {
const { pageId, projectCode, appCode } = params;
return Http.request({
url: '/loki-server/page/schema',
method: 'GET',
params: {
fileName: pageId.match(/.*\/(.+).pageSchema/)?.[1],
projectCode,
appCode,
userId: config.getGlobalDataSourceParams()?.userId,
},
});
}

View File

@ -0,0 +1,41 @@
import { GetQueryString } from './utils/qs';
import { config } from '@crami/lowcode-vue-renderer';
import '@crami/lowcode-materials/dist/view.css';
// 注册umd包
const _W = window as any;
import * as Vue from 'vue';
_W.Vue = Vue;
import * as cramiBui from '@crami/bui';
_W.CramiBui = cramiBui;
import * as cramiUI from '@crami/ui';
_W.cramiUI = cramiUI;
import * as _ from 'lodash-es';
_W._ = _;
import * as moment from 'moment';
_W.moment = moment;
import * as antd from 'ant-design-vue';
_W.antd = antd;
// 添加依赖注入
export const exports: any = {};
function bootstrap(define) {
exports.router = define.router;
define.router.afterEach(to => {
const { dsName, projectCode, appId, appCode, version } = to.meta || {};
const { userId } = to.query;
config.setGlobalDataSourceParams({
userId: GetQueryString('userId') || userId,
dsName,
projectCode,
appCode,
appId,
version,
});
});
}
export default bootstrap;

View File

@ -0,0 +1,25 @@
import { buildComponents } from '@crami/lowcode-utils';
import { getSchemaById } from '../../api/page';
import pkgJson from '../../views/preview/packages.json';
export async function getSchema(params): Promise<{ schema: string; components: any }> {
const packages = pkgJson;
const projectSchema: any = await getSchemaById(params);
if (!projectSchema?.data) {
return Promise.reject('获取schema错误');
}
const { componentsMap: componentsMapArray, componentsTree } = projectSchema.data as any;
const componentsMap: any = {};
componentsMapArray.forEach((component: any) => {
componentsMap[component.componentName] = component;
});
const libraryMap = {};
packages.forEach(({ package: _package, library }) => {
libraryMap[_package] = library;
});
const components = buildComponents(libraryMap, componentsMap);
return { schema: componentsTree[0], components };
}

View File

@ -0,0 +1,89 @@
import { defineComponent } from 'vue';
import { HModal } from '@crami/ui';
import VueRenderer from '@crami/lowcode-vue-renderer';
import { getSchema } from './getSchema';
const config = {
create: {
title: '新建对象',
},
update: {
title: '编辑对象',
},
detail: {
title: '浏览对象',
},
};
const ModalPage = defineComponent({
data() {
return {
title: '',
visible: false,
schema: '',
components: [],
modelName: '',
};
},
props: ['params', 'routeMeta', 'userToken'],
mounted() {},
expose: ['open', 'close'],
methods: {
async init() {
const { projectCode, appCode } = this.routeMeta;
const { pageId, __pageType, modelName, title } = this.params;
if (pageId) {
const { schema, components } = await getSchema({
pageId,
projectCode,
appCode,
});
this.schema = schema;
this.components = components;
} else if (__pageType && modelName) {
this.schema = Object.assign({}, config[__pageType]?.schema);
}
this.title = title || config[__pageType]?.title;
},
open() {
this.init();
this.visible = true;
},
close() {
this.params?.closeHandler();
this.onCancel();
},
onCancel() {
this.visible = false;
},
},
render(props) {
const { params, routeMeta, userToken } = props;
return (
<HModal
footer={null}
width="70%"
visible={this.visible}
title={this.title}
destroyOnClose={true}
onCancel={this.onCancel}
>
{this.schema && this.visible && (
<VueRenderer
scope={{
...params,
routeMeta,
userToken,
closePage: this.close,
cancelPage: this.onCancel,
}}
schema={this.schema}
components={this.components}
></VueRenderer>
)}
</HModal>
);
},
});
export default ModalPage;

View File

@ -0,0 +1,2 @@
import * as LCStandardAction from './scripts';
(window as any).LCStandardAction = LCStandardAction;

View File

@ -0,0 +1,13 @@
// 刷新数据源
export function refreshDataSource(this: any, params: { dataSource: string; param: string }) {
const { dataSource, param } = params;
if (!dataSource) return false;
this.reloadDataSource(dataSource);
}
// 刷新所有数据源
export function refreshDataSourceAll(this: any) {
this.reloadDataSource();
}
export function queryGridDataList(params) {}

View File

@ -0,0 +1,8 @@
export function callMicroFlow(params: any) {
(window as any)[params.microflow](params);
}
export function callNanoFlow(params: any) {
(window as any)[params.nanoflow](params);
}
export function callWorkFlow() {}
export function completeUserTask() {}

View File

@ -0,0 +1,7 @@
export * from './modal';
export * from './test';
export * from './datasource';
export * from './flow';
export * from './method';
export * from './object';
export * from './page';

View File

@ -0,0 +1,453 @@
import { reactive } from 'vue';
import { isJSFunction, isJSExpression } from '@alilc/lowcode-types';
import { cloneDeep, isUndefined } from 'lodash-es';
// 调用组件方法
export function callComponentMethod(
this: any,
params: { component: string; method: string; addImportParam: boolean; params: any },
importParam: any,
) {
const { component, method, addImportParam, params: _params } = params;
// 处理单参数的情况
if (isJSFunction(_params) || isJSExpression(_params)) {
const value = this[_params.value.replace('__self.', '')];
if (addImportParam) {
this.$refs[component]?.[method](importParam, value);
} else {
this.$refs[component]?.[method](value);
}
return;
}
// 处理参数
Object.keys(_params)?.map((key: string) => {
if (isJSFunction(_params[key]) || isJSExpression(_params[key])) {
const value = this[_params[key].value.replace('__self.', '')];
_params[key] = value;
}
});
if (addImportParam) {
this.$refs[component]?.[method](importParam, _params);
} else {
this.$refs[component]?.[method](_params);
}
}
// 从表单中获取filter信息支持HBForm和ProForm
function getFilterByForm(formRef: string) {
const instance = this.$refs[formRef];
const formData = instance?.getData() || {};
const schemas: any[] = instance.$props.schemas || instance.$attrs.schemas;
return Object.keys(formData)
.filter(key => !!formData[key])
.map(key => {
const formItem =
schemas?.find(_schema => _schema.dataIndex === key || _schema.key === key) || {};
return {
field: key,
value: formData[key],
operation: formItem.operation || 'LIKE',
andOrFlag: formItem.andOrFlag || 'AND',
};
});
}
// 表格搜索
export function tableSearch(this: any, params: { component: string; currentNodeId: string }) {
const filter = getFilterByForm.bind(this)(params.currentNodeId);
const _extraFilter = this.$refs[params.component].$attrs.extraFilter;
let extraFilter = [];
if (_extraFilter && _extraFilter.length > 0) {
extraFilter = _extraFilter.filter(config => {
return !isUndefined(config.value);
});
}
this.dataSourceMap?.[`${params.component}__data`]?.load({
filter: [...filter, ...extraFilter],
});
}
// 主从表格搜索
export function doubleTableSearch(
this: any,
params: { mainTable: string; fromTable: string; currentNodeId: string },
) {
const filter = getFilterByForm.bind(this)(params.currentNodeId);
const extraFilter = this.$refs[params.mainTable].$attrs.extraFilter || [];
this.dataSourceMap?.[`${params.mainTable}__data`]?.load({
filter: [...filter, ...extraFilter],
});
this[`${params.fromTable}__data`] = { data: [], total: 0 };
// this.$refs[params.fromTable].setData([]);
}
interface FromTableSearchParams {
fromTable: string;
parentField: {
type: string;
name: string;
field: string;
};
childField: {
type: string;
name: string;
field: string;
};
}
// 子表搜索
export function fromTableSearch(this: any, params: FromTableSearchParams, event: any, record: any) {
const { fromTable, parentField, childField } = params;
this.dataSourceMap[`${fromTable}__data`].load({
filter: [
{
field: childField.field,
value: record[parentField.field],
operation: '=',
andOrFlag: 'AND',
},
],
});
}
// 搜索所有表格
export function tableSearchAll(this: any) {
Object.values(this.$refs).map((ref: any) => {
console.log(ref.type);
});
}
export function arrParamsToObjParams(params: { name: string; value: any }[]) {
const _params = [];
params.map(param => {
const { name, value } = param;
_params.push({
field: name,
value: value,
operation: '=',
andOrFlag: 'AND',
});
});
return _params;
}
/**
* 树表格根节点获取
* @param this
* @param params
*/
export async function treeTableSearch(
this: any,
params: { component: string; currentNodeId: string },
) {
const { component, currentNodeId } = params;
const key = component || currentNodeId;
const dataSourceId = `${key}__data`;
const instance = this.$refs[key];
const config = instance.$attrs.__treeTableConfig;
this[dataSourceId] = reactive({
total: 0,
data: [],
});
// 获取根数据
config.map(async child => {
const modelName = child.config.modelName;
const params = arrParamsToObjParams(child.config.params);
const { total, data } = await this.dataSourceMap[dataSourceId].load(
{
filter: [...params],
modelName,
},
{ assignToScope: false },
);
data?.map(item => {
item.children = [];
item.__node_key__ = child.key;
});
const { total: prevTotal, data: prevData } = this[dataSourceId];
this[dataSourceId] = {
total: total + prevTotal,
data: [...prevData, ...data],
};
});
}
// 根据属性数据和key value获取数据
export function getTreeNodeDataByKey(data: any[], key: string, value: any) {
if (!data?.length) {
return null;
}
let _cache = null;
const fn = arr => {
arr.some(item => {
if (item[key] === value) {
return (_cache = item);
} else if (item?.children?.length) {
fn(item.children);
}
});
};
fn(data);
return _cache;
}
/**
* 树表格展开
* @param this
* @param params
* @param rest
* @returns
*/
export async function treeTableExpand(
this: any,
params: { currentNodeId: string; finish?: boolean },
...rest
) {
if (!rest[0]) {
return false;
}
const currentRow = rest[1];
const { currentNodeId, finish } = params;
// 如果已经存在数据,并且不是强制刷新
if (currentRow?.children?.length && !finish) {
return false;
}
currentRow.children = [];
// 获取信息
const key = currentNodeId;
const instance = this.$refs[key];
const config = instance.$attrs.__treeTableConfig;
const rowKey = instance.$props.rowKey;
const dataSourceId = `${key}__data`;
// 获取所在位置和节点
const __parentNodeKey = currentRow.__node_key__;
const __parentNode = getTreeNodeDataByKey(config, 'key', __parentNodeKey);
// 获取子数据
__parentNode?.children?.map(async child => {
const modelName = child.config.modelName;
const params = arrParamsToObjParams(child.config.params);
// 组装查询参数
const filter = [
{
field: child.config.searchField,
value: currentRow[__parentNode.config.valueField],
operation: '=',
andOrFlag: 'AND',
},
...params,
];
const { data } = await this.dataSourceMap[dataSourceId].load(
{
filter,
modelName,
},
{ assignToScope: false },
);
// 配置数据信息
data.map(item => {
if (child?.children?.length) {
item.children = [];
}
item.__node_key__ = child.key;
});
const currentData = getTreeNodeDataByKey(this[dataSourceId].data, rowKey, currentRow[rowKey]);
currentData.children.push(...data);
console.log(this[dataSourceId]);
});
}
/**
* 联动Tab组
* @param this
* @param params
* @param eventParam
*/
export function selectLinkTabs(
this: any,
params: { component: string; currentNodeId: string },
selectedKeys: Array<string>,
node: any,
) {
const __treeDatasourceConfig = this.$refs[params.currentNodeId].__treeDatasourceConfig;
const getNodeByKey = (nodeKey: string) => {
const getNodeDeep = (children: any[]): any => {
let tempNode: Node | null = null;
children.forEach((_node: any) => {
if (_node.key === nodeKey) {
tempNode = _node;
} else {
if (_node.children && _node.children.length > 0) {
tempNode = getNodeDeep(_node.children);
}
}
});
return tempNode;
};
return getNodeDeep(__treeDatasourceConfig);
};
// 基于视图的数据源value5取的就是节点上的key自定义数据源则要自己塞上NodeKey
// 因为访问的是通用列表查询,后台不存在节点的概念
const nodeKey = node.node.dataRef.value5 || node.node.dataRef.NodeKey;
const nodeId = node.node.dataRef.value1 || node.node.dataRef.EID;
const tempNode = getNodeByKey(nodeKey);
const linkTabs = tempNode?.linkConfig?.PageLinkList;
if (linkTabs) {
const nodeData = node.node.dataRef._nodeData || {};
this.$refs[params.component]?.selectLinkTabs(linkTabs, {
...tempNode,
nodeKey: nodeId,
...nodeData,
});
}
}
/**
* 懒加载子节点
* @param params 参数
* @param node 点击的节点
* @returns
*/
export function proTreeLoadData(
this: any,
params: { dataSourceId: string; treeViewName: string },
node: any,
) {
if (node.isLeaf) {
return Promise.resolve(true);
}
if (node.dataRef.children && node.dataRef.children.length > 0) {
return Promise.resolve(true);
}
const { value1, value2, value3, value4, value5, value6 } = node.dataRef;
const _params = {
treeViewName: params.treeViewName,
loadAllStructure: false,
value1: value1,
value2: value2,
value3: value3,
value4: value4,
value5: value5,
value6: value6,
};
return this.dataSourceMap[params.dataSourceId].load(_params).then(childData => {
if (childData && childData.length > 0) {
node.dataRef.children = [...childData];
} else {
node.dataRef.isLeaf = true;
}
return true;
});
}
function getListByNodes(nodesConfig: any[], treeDataSource) {
const promiseArr = nodesConfig
.map(node => {
if (node?.nodeConfig?.DataSource?.name) {
const { name, field } = node.nodeConfig.DataSource;
return treeDataSource.load({ modelName: name }).then(res => {
if (res && res.total > 0) {
const data = res.data;
return data.map(item => {
return {
key: `${node.key}_${item.EID}`,
title: item[field],
isLeaf: node.isLeaf,
NodeKey: node.key,
DataSource: cloneDeep(node.DataSource),
childNodesConfig: cloneDeep(node.children),
_nodeData: item,
};
});
}
return [];
});
} else {
// 固定节点
return Promise.resolve([
{
key: `${node.key}_${Date.now()}`,
title: node.nodeConfig.NodeName,
isLeaf: node.isLeaf,
NodeKey: node.key,
DataSource: cloneDeep(node.DataSource),
childNodesConfig: cloneDeep(node.children),
},
]);
}
})
.filter(item => item);
if (promiseArr.length > 0) {
return Promise.all(promiseArr)
.then(res => {
if (res && res.length > 0) {
let resData = [];
res.forEach(item => {
if (item && item.length > 0) {
resData = resData.concat(item);
}
});
return resData;
}
})
.catch(err => {
console.error(err);
});
}
return Promise.resolve([]);
}
/**
* 加载根节点数据
* @param this
* @param params
*/
export function loadTreeData(this: any, params: { currentNodeId: string }) {
const nodeId = params.currentNodeId;
const instance = this.$refs[nodeId];
const dataSourceId = `treeDataSource_${nodeId}`;
// 查找根节点数据源
const nodes = instance.__treeDatasourceConfig || [];
getListByNodes(nodes, this.dataSourceMap[dataSourceId]).then(resData => {
if ((resData as any[]).length > 0) {
instance.$.props.treeData = cloneDeep(resData);
}
});
}
/**
* 加载子节点数据
* @param this
* @param params
*/
export function loadChildTreeData(this: any, params: { currentNodeId: string }, node: any) {
if (node.isLeaf) {
return Promise.resolve(true);
}
if (node.dataRef.HasLoad) {
return Promise.resolve(true);
}
node.dataRef.children = [];
const nodeId = params.currentNodeId;
const dataSourceId = `treeDataSource_${nodeId}`;
// 查找根节点数据源
const nodes = node.dataRef.childNodesConfig;
return getListByNodes(nodes, this.dataSourceMap[dataSourceId]).then(resData => {
node.dataRef.HasLoad = true;
const resArr = resData as any[];
if (resArr.length > 0) {
node.dataRef.children = cloneDeep(resArr);
} else {
node.dataRef.isLeaf = true;
}
return true;
});
}

View File

@ -0,0 +1,11 @@
export interface OpenModalParams {
pageId?: string;
type?: 'create' | 'update' | 'detail';
modelName?: string; // 模型路径
}
// 打开页面弹窗
export function openModal(this: any, params: OpenModalParams) {
console.log('打开弹窗-openModal', this, params);
this?.openModal(params);
}

View File

@ -0,0 +1,286 @@
import { getTreeNodeDataByKey } from '../../utils';
import { message } from '@crami/ui';
import {
deleteAllByModel,
infoByModel,
saveByModel,
updateByModel,
saveByModelFk,
updateByModelFk,
} from '../../api/commonApi/model';
import { openModal } from './modal';
import { treeTableExpand, treeTableSearch } from './method';
// 新建表单
export function createObject(
this: any,
params: { modelName: string; pageId: string; component: string },
) {
const { pageId, component } = params;
openModal.bind(this)({
pageId,
__pageType: 'create',
closeHandler: () => {
// 树表格
if (this.$refs[component].$attrs.__treeTableConfig) {
treeTableSearch.bind(this)({ component });
} else {
// 普通表格
this.reloadDataSource(`${component}__data`);
}
},
});
}
// 新建子数据表单
export function addChildObject(
this: any,
params: { modelName: string; pageId: string; component: string },
) {
const { modelName, pageId, component } = params;
const instance = this.$refs[component];
const rows = instance.getSelectRows();
if (!rows?.length) {
message.warn('请选择一条数据');
return false;
}
const currentRow = rows[0];
const config = instance.$attrs.__treeTableConfig;
const __parentNodeKey = currentRow.__node_key__;
const __parentNode = getTreeNodeDataByKey(config, 'key', __parentNodeKey);
const currentNode = __parentNode?.children?.find(child => child?.config?.modelName === modelName);
if (!currentNode) {
message.warn('此数据下不存在这种子数据');
return false;
}
const __extendData = {
[currentNode.config.searchField]: currentRow[__parentNode.config.valueField],
};
openModal.bind(this)({
pageId,
__extendData,
__pageType: 'create',
closeHandler: () => {
currentRow.children = [];
treeTableExpand.bind(this)({ currentNodeId: component, finish: true }, true, currentRow);
},
});
}
// 编辑表单
export async function editObject(
this: any,
params: { modelName: string; pageId: string; component: string },
) {
const { modelName, pageId } = params;
const instance = this.$refs[params.component];
const primaryCode = instance.$props.rowKey;
const rows = instance.getSelectRows();
if (!rows?.length) {
return false;
}
const res = await infoByModel({
id: rows[0][primaryCode],
modelName,
});
openModal.bind(this)({
pageId,
__pageType: 'update',
formInitialData: res.data,
closeHandler: () => {
this.reloadDataSource(`${params.component}__data`);
},
});
}
// 浏览表单
export async function detailObject(
this: any,
params: { modelName: string; pageId: string; component: string },
) {
const { modelName, pageId } = params;
const instance = this.$refs[params.component];
const primaryCode = instance.$props.rowKey;
const rows = instance.getSelectRows();
if (!rows?.length) {
return false;
}
const res = await infoByModel({
id: rows[0]?.[primaryCode],
modelName,
});
openModal.bind(this)({
pageId,
__pageType: 'detail',
formInitialData: res.data,
});
}
// 保存表单,分为单表、多表
export function saveChanges(this: any, params: { currentNodeId: string }) {
const { currentNodeId } = params;
const instance = this.$refs[currentNodeId];
const compName = instance.$.type.name;
// 单表类型的
if (['HBForm', 'ProForm'].includes(compName)) {
saveChangesBySingle.bind(this)(instance);
}
// 主外键类型的
if (['HButton'].includes(compName)) {
saveChangesByMultip.bind(this)();
}
}
// 单表保存
function saveChangesBySingle(this: any, instance) {
const formData: any = instance.getData();
const primaryCode = instance.$attrs.primaryCode;
const modelName = this.modelName || instance.$attrs.__modelName || instance.modelName || '';
if (!modelName || !formData) {
return;
}
// 混入外部传入的数据
if (this.__extendData) {
Object.assign(formData, this.__extendData);
}
if (this.__pageType === 'create' || !this.__pageType) {
instance.submitButtonLoading();
delete formData[primaryCode];
saveByModel({
data: formData,
modelName,
})
.then((data: any) => {
this?.closePage(data);
})
.finally(() => {
instance.submitButtonLoading();
});
return false;
}
if (this.__pageType === 'update') {
instance.submitButtonLoading();
delete formData['CREATETIME'];
updateByModel({
id: formData[primaryCode],
data: formData,
modelName,
})
.then((data: any) => {
this?.closePage(data);
})
.finally(() => {
instance.submitButtonLoading();
});
return false;
}
}
// 多表保存
function saveChangesByMultip(this: any) {
const instanceKeys = Object.keys(this.$refs);
const forms = instanceKeys
.filter(key => ['HBForm', 'ProForm'].includes(this.$refs[key].$.type.name))
.map(key => this.$refs[key]);
const tables = instanceKeys
.filter(key => this.$refs[key].$.type.name === 'HBProTable')
.map(key => this.$refs[key]);
const modelName = forms[0].$attrs.__modelName;
const primaryCode = forms[0].$attrs.primaryCode;
const formData: any = forms[0].getData();
delete formData['CREATETIME'];
const pageData = {};
Object.keys(formData).map(key => {
pageData[`${modelName}_${key}`] = formData[key];
});
if (this.__pageType === 'create' || !this.__pageType) {
tables.map(_table => {
const keys = _table.$props.columns
.filter(column => column.dataIndex !== '__pro-table-custom-operation__')
.map(column => column.key || column.dataIndex);
const tableModelName = _table.$attrs.__modelName;
pageData[tableModelName] = _table
.getDisplayAllData()
.map(setModelNameToTableDataKey(keys, tableModelName));
});
delete pageData[`${modelName}_${primaryCode}`];
saveByModelFk({
data: pageData,
modelName,
}).then((data: any) => {
this?.closePage(data);
});
}
if (this.__pageType === 'update') {
tables.map(_table => {
const keys: string[] = _table.$props.columns
.filter(column => column.dataIndex !== '__pro-table-custom-operation__')
.map(column => column.key || column.dataIndex);
const tableModelName = _table.$attrs.__modelName;
const primaryCode = _table.$props.rowKey;
pageData[`${tableModelName}-insert`] = _table
.getAddRows()
.map(setModelNameToTableDataKey(keys, tableModelName));
const updateKeys: string[] = [...keys];
if (!keys.includes(primaryCode)) {
updateKeys.push(primaryCode);
}
pageData[`${tableModelName}-update`] = _table
.getUpdateRows()
.map(setModelNameToTableDataKey(updateKeys, tableModelName));
pageData[`${tableModelName}-delete`] = _table.getDelRows().map(row => row[primaryCode]);
});
updateByModelFk({
id: pageData[`${modelName}_${primaryCode}`],
data: pageData,
modelName,
}).then((data: any) => {
this?.closePage(data);
});
}
}
// 取消保存
export function cancelChanges(this: any) {
this?.cancelPage();
}
// 删除记录
export function deleteRecord(this: any, params: { component: string }) {
const instance = this.$refs[params.component];
const rows = instance.getSelectRows();
const primaryCode = instance.$props.rowKey;
if (!rows?.length) {
return;
}
const modelName = instance.$attrs.__modelName || instance.modelName || this.modelName || '';
deleteAllByModel({
ids: rows.map(row => row[primaryCode]),
modelName,
}).then(() => {
instance.removeSelectedRows();
});
}
// 工具函数——————————————————————————————————
// 设置模型名称到表格数据上
function setModelNameToTableDataKey(keys, modelName) {
return item => {
const _rowData = {};
keys.map(key => {
_rowData[`${modelName}_${key}`] = item[key];
});
return _rowData;
};
}

View File

@ -0,0 +1,34 @@
// 打开页面
export function showAPage(this: any, params: any) {
this.showAPage(params);
}
export function closePage(this: any) {
this.$router.back();
}
export function showWorkFlowAdminPage() {}
export function showUserTaksPage() {}
export function syncchronize() {}
export function signOut() {}
export function openLink(this: any, params: any) {
if (!params.address) {
return;
}
let prefix = '';
switch (params.linktype) {
case 'email':
prefix = 'emailto:';
break;
case 'tel':
prefix = 'tel:';
break;
case 'text':
prefix = 'sms:';
break;
case 'web':
default:
prefix = '';
}
window.open(`${prefix}${params.address}`);
}

View File

@ -0,0 +1,4 @@
export function testFn(this: any, params: any) {
console.log('testFn:this', this);
console.log('testFn:params', params);
}

View File

@ -0,0 +1,2 @@
export * from './tree';
export * from './qs';

View File

@ -0,0 +1,4 @@
export const GetQueryString = (name: string) => {
const reg = new RegExp('(?<=(?:^|&|\\?)' + name + '=)([^&]*)(?=(&|$))', 'g');
return window.location.search.match(reg)?.[0];
};

View File

@ -0,0 +1,18 @@
// 根据属性数据和key value获取数据
export function getTreeNodeDataByKey(data: any[], key: string, value: any) {
if (!data?.length) {
return null;
}
let _cache = null;
const fn = arr => {
arr.some(item => {
if (item[key] === value) {
return (_cache = item);
} else if (item?.children?.length) {
fn(item.children);
}
});
};
fn(data);
return _cache;
}

View File

@ -0,0 +1,99 @@
import { getSchemaById as getSchemaFromServer } from '../../api/page';
import pkgJson from './packages.json';
import _projectSchema from './projectSchema.json';
import { buildComponents } from '@crami/lowcode-utils';
import { isNull, isUndefined } from 'lodash-es';
import { evaluate } from '@studio/formula';
export const getSchemaById = async (params: {
pageId: string;
projectCode: string;
appCode: string;
}) => {
const packages = pkgJson;
let projectSchema: any = await getSchemaFromServer(params);
if (!projectSchema.data) {
projectSchema = _projectSchema;
} else {
projectSchema = projectSchema.data;
}
const { componentsMap: componentsMapArray, componentsTree } = projectSchema as any;
const componentsMap: any = {};
componentsMapArray.forEach((component: any) => {
componentsMap[component.componentName] = component;
});
const libraryMap = {};
packages.forEach(({ package: _package, library }) => {
libraryMap[_package] = library;
});
const components = buildComponents(libraryMap, componentsMap);
return { schema: componentsTree[0], components };
};
export interface ParamItem {
key: string;
label: string;
valueType: string;
required?: boolean;
}
export function normalizePageParams(
pageParams: { [key: string]: any },
pageParamsConfig: Array<ParamItem>,
formularData: any,
) {
let newParams = {};
if (pageParamsConfig && pageParamsConfig.length > 0) {
newParams = pageParamsConfig.reduce((params: { [key: string]: any }, curItem: ParamItem) => {
const { valueType, key } = curItem;
const value = pageParams[key];
if (isUndefined(value)) {
return params;
}
if (valueType === 'object' || valueType === 'array') {
try {
params[key] = JSON.parse(value);
} catch (error) {
console.warn(`json解析异常数据 \n ${value}`);
}
} else if (valueType === 'formular') {
// 执行公式
if (!isNull(value)) {
const result = evalFormula(value, formularData);
if (!isUndefined(result)) {
params[key] = result;
}
}
} else {
params[key] = value;
}
return params;
}, {});
}
return newParams;
}
/**
* 执行公式
* @param expression 公式
* @param data 变量值对象
* @returns 执行结果
*/
export function evalFormula(expression: string, data?: object) {
const curData = data || {};
let result;
try {
result = evaluate(expression, curData, {
evalMode: true, // evalMode 为 true 时,不用 ${} 包裹也可以执行,
allowFilter: false,
});
} catch (e) {
console.warn(
'[evalFormula]表达式执行异常,当前表达式: ',
expression,
',当前上下文数据: ',
data,
);
}
return result;
}

View File

@ -0,0 +1,3 @@
import Preview from './preview';
import PreviewOnly from './previewOnly';
export { Preview, PreviewOnly };

View File

@ -0,0 +1,94 @@
[
{
"package": "loadsh",
"version": "0.0.4",
"urls": ["./cdn/npm/loadsh@0.0.4/lodash.min.js"],
"library": "_"
},
{
"package": "xlsx",
"version": "0.18.5",
"urls": ["./cdn/npm/xlsx@0.18.5/dist/xlsx.full.min.js"],
"library": "XLSX"
},
{
"package": "vue",
"version": "3.2.37",
"urls": ["./cdn/npm/vue@3.2.37/dist/vue.global.js"],
"editUrls": ["./cdn/npm/vue@3.2.37/dist/vue.global.js"],
"library": "Vue"
},
{
"package": "vue-i18n",
"version": "9.2.2",
"urls": ["./cdn/npm/vue-i18n@9.2.2/dist/vue-i18n.global.js"],
"editUrls": ["./cdn/npm/vue-i18n@9.2.2/dist/vue-i18n.global.js"],
"library": "VueI18n"
},
{
"package": "@vueuse/shared",
"version": "8.9.4",
"urls": ["./cdn/npm/@vueuse/shared@8.9.4/index.iife.min.js"],
"library": "VueUseShared"
},
{
"package": "@vueuse/core",
"version": "8.9.4",
"urls": ["./cdn/npm/@vueuse/core@8.9.4/index.iife.min.js"],
"library": "VueUse"
},
{
"package": "dayjs",
"version": "1.11.4",
"urls": ["./cdn/npm/dayjs@1.11.4/dayjs.min.js"],
"library": "dayjs"
},
{
"package": "ant-design-vue",
"version": "3.2.10",
"urls": [
"./cdn/npm/ant-design-vue@3.2.10/dist/antd.min.js",
"./cdn/npm/ant-design-vue@3.2.10/dist/antd.min.css"
],
"library": "antd"
},
{
"package": "@surely-vue/table",
"version": "2.4.3",
"urls": [
"./cdn/npm/@surely-vue/table@2.4.7/dist/index.min.js",
"./cdn/npm/@surely-vue/table@2.4.7/dist/index.min.css"
],
"library": "STable"
},
{
"package": "echarts",
"version": "5.3.3",
"urls": ["./cdn/npm/echarts@5.3.3/dist/echarts.min.js"],
"library": "echarts"
},
{
"package": "@crami/ui",
"version": "2.0.162",
"urls": ["./cdn/crami/crami-ui/crami-monorepo.umd.js", "./cdn/crami/crami-ui/style.css"],
"library": "cramiUI"
},
{
"package": "@crami/bui-platform",
"version": "2.0.259",
"urls": ["./cdn/crami/platform/crami-bui-monorepo.umd.js"],
"library": "CramiBUIPlatform"
},
{
"package": "@crami/bui",
"version": "2.0.331",
"urls": ["./cdn/crami/crami-bui/crami-bui-monorepo.umd.js", "./cdn/crami/crami-bui/style.css"],
"library": "CramiBui"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"urls": ["./cdn/crami/crami-lc/view.js", "./cdn/crami/crami-lc/view.css"],
"library": "CramiLcUi"
}
]

View File

@ -0,0 +1,5 @@
.lowcode-plugin-sample-preview,
.lowcode-plugin-sample-preview-content {
/* height: calc(100vh - 256px); */
height: auto;
}

View File

@ -0,0 +1,96 @@
import { defineComponent, onMounted, reactive, h, createVNode, render } from 'vue';
import VueRenderer from '@crami/lowcode-vue-renderer';
import { Spin } from '@crami/ui';
import './preview.css';
import ModalPage from '../../components/modal-page';
import { useRoute } from 'vue-router';
import { getSchemaById, normalizePageParams } from './helper';
import { previewProps } from './props';
import type { MenuRouteMeta } from 'pro-vip/src/router/typing';
export default defineComponent({
name: 'editor-preview',
props: previewProps,
setup(props) {
const data = reactive<any>({});
// 获取token
let tokenStr = '';
const hostToken = localStorage.getItem('token');
const clientToken = localStorage.getItem('access_token');
if (hostToken) {
tokenStr = JSON.parse(hostToken)?.token || '';
} else if (clientToken) {
tokenStr = clientToken;
}
const userToken = `Bearer ${tokenStr}`;
// 获取当前路由
const route = useRoute();
const { projectCode, appCode } = route.meta as Required<MenuRouteMeta>;
console.log('routermeta', route.meta);
onMounted(async () => {
const CramiLcUi = await import('@crami/lowcode-materials');
const _W = window as any;
_W.CramiLcUi = CramiLcUi;
// 获取schema
if (props.pageId) {
Object.assign(
data,
await getSchemaById({
pageId: props.pageId,
projectCode,
appCode,
}),
);
}
});
return () => {
const { schema, components } = data;
// 定义一个弹窗方法
async function openModal(params: any) {
const vnode = createVNode(ModalPage, {
params,
components,
routeMeta: route.meta,
userToken,
key: Math.random(),
});
render(vnode, document.body);
vnode.component?.ctx?.open();
}
if (!schema || !components) {
return h(Spin);
}
// 处理页面传参
const _pageParams = route.meta.pageParams
? JSON.parse(route.meta.pageParams as string)
: props.pageParams;
const formularData = {
projectCode,
appCode,
};
const pageParams = normalizePageParams(_pageParams, schema.pageParamsConfig, formularData);
return h('div', { class: 'lowcode-plugin-sample-preview' }, [
h(VueRenderer, {
scope: {
openModal,
routeMeta: route.meta,
userToken,
pageParams: pageParams,
},
class: 'lowcode-plugin-sample-preview-content',
schema,
components,
}),
]);
};
},
});

View File

@ -0,0 +1,60 @@
import { defineComponent, onMounted, reactive, toRefs, h, createVNode, render, inject } from 'vue';
import VueRenderer from '@crami/lowcode-vue-renderer';
import { Spin } from '@crami/ui';
import './preview.css';
import ModalPage from '@/components/modal-page';
import { useRoute } from 'vue-router';
import { getSchemaById, normalizePageParams } from './helper';
import { previewProps } from './props';
export default defineComponent({
name: 'EditorPreviewOnly',
props: previewProps,
mounted() {},
setup(props) {
const data = reactive<any>({});
const { pageId } = toRefs(props);
const projectCode = inject('projectCode', { value: '' });
const appCode = inject('appCode', { value: '' });
onMounted(async () => {
if (pageId.value) {
Object.assign(data, await getSchemaById(pageId.value, projectCode.value, appCode.value));
}
});
const route = useRoute();
const token: any = JSON.parse(localStorage.getItem('token') || '');
const userToken = `Bearer ${token?.token || ''}`;
return () => {
const { schema, components } = data;
if (!schema || !components) {
return h(Spin);
}
async function openModal(params: any) {
const vnode = createVNode(ModalPage, {
params,
components,
routeMeta: route.meta,
userToken,
});
render(vnode, document.body);
vnode.component.ctx.open();
}
const pageParams = normalizePageParams(props.pageParams, schema.pageParamsConfig);
return h('div', { class: 'lowcode-plugin-sample-preview' }, [
h(VueRenderer, {
scope: {
openModal,
routeMeta: route.meta,
userToken,
pageParams: pageParams,
},
class: 'lowcode-plugin-sample-preview-content',
schema,
components,
}),
]);
};
},
});

View File

@ -0,0 +1,99 @@
{
"version": "1.0.0",
"componentsMap": [
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiCol",
"destructuring": true,
"componentName": "CramiCol"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiRow",
"destructuring": true,
"componentName": "CramiRow"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiRowColContainer",
"destructuring": true,
"componentName": "CramiRowColContainer"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiBlockCell",
"destructuring": true,
"componentName": "CramiBlockCell"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiTabPanel",
"destructuring": true,
"componentName": "CramiTabPanel"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiTabs",
"destructuring": true,
"componentName": "CramiTabs"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiBlock",
"destructuring": true,
"componentName": "CramiBlock"
},
{
"package": "@crami/lowcode-materials",
"version": "0.0.0",
"exportName": "CramiPage",
"destructuring": true,
"componentName": "CramiPage"
},
{ "devMode": "lowCode", "componentName": "Page" }
],
"componentsTree": [
{
"componentName": "Page",
"id": "node_ocl7spc2i41",
"props": { "ref": "node_ocl7spc2i41" },
"fileName": "/",
"state": {
"text": { "type": "JSExpression", "value": "\"outer\"" },
"isShowDialog": { "type": "JSExpression", "value": "false" },
"info": {
"type": "JSExpression",
"value": "{\n \"info\": \"\",\n \"user\": {\n \"username\": \"\",\n \"password\": \"\"\n }\n}"
}
},
"dataSource": { "list": [] },
"css": "body, html {background: rgba(0, 0, 0, 0);}",
"lifeCycles": {
"mounted": {
"type": "JSFunction",
"value": "function mounted() {\n console.log('did mount');\n}"
},
"unmounted": {
"type": "JSFunction",
"value": "function unmounted() {\n console.log('will unmount');\n}"
}
},
"methods": {},
"hidden": false,
"title": "页面",
"isLocked": false,
"condition": true,
"conditionGroup": "",
"utils": [],
"children": []
}
],
"i18n": {}
}

View File

@ -0,0 +1,12 @@
import type { PropType } from 'vue';
export const previewProps = {
pageId: {
type: String,
default: '',
},
pageParams: {
type: Object as PropType<Array<{ [propsKey: string]: any }>>,
default: () => ({}),
},
};