midway-fatcms 0.0.1-beta.61 → 0.0.1-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/config/config.default.js +2 -2
  2. package/dist/controller/gateway/FileController.d.ts +2 -0
  3. package/dist/controller/gateway/FileController.js +15 -0
  4. package/dist/libs/crud-pro/interfaces.d.ts +2 -0
  5. package/dist/libs/crud-pro/models/keys.d.ts +4 -0
  6. package/dist/libs/crud-pro/models/keys.js +5 -0
  7. package/dist/libs/crud-pro/services/CrudProExecuteFuncService.js +13 -0
  8. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +15 -0
  9. package/dist/libs/crud-pro/utils/ModelUtils.js +6 -1
  10. package/dist/libs/utils/parseConfig.d.ts +1 -0
  11. package/dist/libs/utils/parseConfig.js +3 -0
  12. package/dist/models/bizmodels.d.ts +7 -2
  13. package/dist/service/AuthService.d.ts +1 -1
  14. package/dist/service/AuthService.js +5 -1
  15. package/dist/service/EnumInfoService.js +1 -1
  16. package/dist/service/crudstd/CrudStdRelationService.js +20 -11
  17. package/dist/service/crudstd/CrudStdService.d.ts +1 -0
  18. package/dist/service/crudstd/CrudStdService.js +13 -2
  19. package/dist/service/curd/CurdMixByAccountService.js +28 -7
  20. package/dist/service/curd/CurdMixUtils.d.ts +7 -1
  21. package/dist/service/curd/CurdMixUtils.js +59 -5
  22. package/package.json +1 -1
  23. package/src/config/config.default.ts +2 -2
  24. package/src/controller/gateway/FileController.ts +15 -1
  25. package/src/libs/crud-pro/interfaces.ts +3 -0
  26. package/src/libs/crud-pro/models/keys.ts +6 -0
  27. package/src/libs/crud-pro/services/CrudProExecuteFuncService.ts +17 -0
  28. package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +22 -2
  29. package/src/libs/crud-pro/utils/ModelUtils.ts +6 -1
  30. package/src/libs/utils/parseConfig.ts +4 -0
  31. package/src/models/bizmodels.ts +8 -2
  32. package/src/service/AuthService.ts +6 -1
  33. package/src/service/EnumInfoService.ts +1 -1
  34. package/src/service/crudstd/CrudStdRelationService.ts +25 -12
  35. package/src/service/crudstd/CrudStdService.ts +15 -2
  36. package/src/service/curd/CurdMixByAccountService.ts +31 -8
  37. package/src/service/curd/CurdMixUtils.ts +74 -5
@@ -35,7 +35,7 @@ exports.default = (appInfo) => {
35
35
  jsonLimit: '10mb',
36
36
  textLimit: '10mb',
37
37
  xmlLimit: '10mb',
38
- ignore: ['/ns/gw/proxy/*', '/ns/gw/file/uploadFile', '/ns/api/manage/deploy/uploadAssets'], // 忽略代理路径的 body 解析
38
+ ignore: ['/ns/gw/proxy/*', '/ns/gw/file/uploadFile', '/ns/gw/file/uploadFilePublic', '/ns/api/manage/deploy/uploadAssets'], // 忽略代理路径的 body 解析
39
39
  },
40
40
  mysql2: {
41
41
  fatcms: {
@@ -119,7 +119,7 @@ exports.default = (appInfo) => {
119
119
  // base64: boolean,设置原始body是否是base64格式,默认为false,一般用于腾讯云的兼容
120
120
  base64: false,
121
121
  // 仅在匹配路径到 /api/upload 的时候去解析 body 中的文件信息
122
- match: ['/ns/gw/file/uploadFile', '/ns/api/manage/deploy/uploadAssets'],
122
+ match: ['/ns/gw/file/uploadFile', '/ns/gw/file/uploadFilePublic', '/ns/api/manage/deploy/uploadAssets'],
123
123
  },
124
124
  view: {
125
125
  rootDir: {
@@ -8,6 +8,7 @@ import { CommonResult } from '../../libs/utils/common-dto';
8
8
  export declare class FileController extends BaseApiController {
9
9
  protected ctx: Context;
10
10
  protected fileCenterService: FileCenterService;
11
+ uploadFilePublic(files: any, fields: any, queryData: any): Promise<CommonResult>;
11
12
  /**
12
13
  * 上传普通文件。必须是登录用户才能上传。
13
14
  * @param files
@@ -15,6 +16,7 @@ export declare class FileController extends BaseApiController {
15
16
  * @param queryData
16
17
  */
17
18
  uploadFile(files: any, fields: any, queryData: any): Promise<CommonResult>;
19
+ private uploadFilePrivate;
18
20
  /**
19
21
  * 文件下载或预览
20
22
  * @param byMethod
@@ -29,6 +29,9 @@ function isTrue(obj) {
29
29
  * 文件上传下载服务
30
30
  */
31
31
  let FileController = class FileController extends BaseApiController_1.BaseApiController {
32
+ async uploadFilePublic(files, fields, queryData) {
33
+ return this.uploadFilePrivate(files, fields, queryData);
34
+ }
32
35
  /**
33
36
  * 上传普通文件。必须是登录用户才能上传。
34
37
  * @param files
@@ -36,6 +39,9 @@ let FileController = class FileController extends BaseApiController_1.BaseApiCon
36
39
  * @param queryData
37
40
  */
38
41
  async uploadFile(files, fields, queryData) {
42
+ return this.uploadFilePrivate(files, fields, queryData);
43
+ }
44
+ async uploadFilePrivate(files, fields, queryData) {
39
45
  const accessType = (queryData === null || queryData === void 0 ? void 0 : queryData.accessType) || (fields === null || fields === void 0 ? void 0 : fields.accessType) || bizmodels_1.AccessType.open;
40
46
  const referer = this.ctx.req.headers.referer;
41
47
  if (!Object.keys(bizmodels_1.AccessType).includes(accessType)) {
@@ -106,6 +112,15 @@ __decorate([
106
112
  (0, core_1.Inject)(),
107
113
  __metadata("design:type", FileCenterService_1.FileCenterService)
108
114
  ], FileController.prototype, "fileCenterService", void 0);
115
+ __decorate([
116
+ (0, core_1.Post)('/uploadFilePublic', { middleware: [(0, permission_middleware_1.checkLogin)()] }),
117
+ __param(0, (0, core_1.Files)()),
118
+ __param(1, (0, core_1.Fields)()),
119
+ __param(2, (0, core_1.Query)()),
120
+ __metadata("design:type", Function),
121
+ __metadata("design:paramtypes", [Object, Object, Object]),
122
+ __metadata("design:returntype", Promise)
123
+ ], FileController.prototype, "uploadFilePublic", null);
109
124
  __decorate([
110
125
  (0, core_1.Post)('/uploadFile', { middleware: [(0, permission_middleware_1.checkPermission)([SystemPerm_1.SystemFuncCode.DocMangeWrite, SystemPerm_1.SystemFuncCode.FileMangeWrite, SystemPerm_1.SystemFuncCode.UserAccountMangeWrite])] }),
111
126
  __param(0, (0, core_1.Files)()),
@@ -105,6 +105,8 @@ export interface IFuncCfgModel extends Record<any, any> {
105
105
  contextAsString?: string;
106
106
  contextAsNumber?: string;
107
107
  contextAsBool?: string;
108
+ executeExpressReturnObject?: string;
109
+ executeExpressReturnString?: string;
108
110
  message?: string;
109
111
  }
110
112
  export interface ISqlCfgModel extends IBaseCfgModel {
@@ -101,6 +101,10 @@ export declare const KeysOfConditions: {
101
101
  $NOT_LIKE_INCLUDE: string;
102
102
  $MATCH: string;
103
103
  $MATCH_BOOL: string;
104
+ /**
105
+ * SELECT * FROM tms_biz.表 WHERE JSON_CONTAINS(`related_user1`, JSON_ARRAY('2512274'), '$');
106
+ */
107
+ $JSON_ARRAY_CONTAINS: string;
104
108
  COMPARE_KEYS: Set<string>;
105
109
  ALL_KEYS: Set<string>;
106
110
  };
@@ -115,6 +115,10 @@ exports.KeysOfConditions = {
115
115
  $NOT_LIKE_INCLUDE: '$notLikeInclude',
116
116
  $MATCH: '$match',
117
117
  $MATCH_BOOL: '$matchBool',
118
+ /**
119
+ * SELECT * FROM tms_biz.表 WHERE JSON_CONTAINS(`related_user1`, JSON_ARRAY('2512274'), '$');
120
+ */
121
+ $JSON_ARRAY_CONTAINS: '$jsonArrayContains',
118
122
  COMPARE_KEYS: new Set([]),
119
123
  ALL_KEYS: new Set([]),
120
124
  };
@@ -137,6 +141,7 @@ function initKeysOfConditions() {
137
141
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$LIKE_INCLUDE);
138
142
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$NOT_LIKE);
139
143
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$NOT_LIKE_INCLUDE);
144
+ addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$JSON_ARRAY_CONTAINS);
140
145
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$MATCH);
141
146
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$MATCH_BOOL);
142
147
  addIgnoreCase(exports.KeysOfConditions.COMPARE_KEYS, exports.KeysOfConditions.$RANGE);
@@ -5,6 +5,8 @@ const CrudProServiceBase_1 = require("./CrudProServiceBase");
5
5
  const TypeUtils_1 = require("../utils/TypeUtils");
6
6
  const MixinUtils_1 = require("../utils/MixinUtils");
7
7
  const _ = require("lodash");
8
+ const moment = require("moment");
9
+ const ejs = require("ejs");
8
10
  const exceptions_1 = require("../exceptions");
9
11
  const keys_1 = require("../models/keys");
10
12
  const { isNil, isNumeric } = TypeUtils_1.TypeUtils;
@@ -20,6 +22,17 @@ class CrudProExecuteFuncService extends CrudProServiceBase_1.CrudProServiceBase
20
22
  if (funCfg.___PLAIN_OBJECT___ === true) {
21
23
  return funCfg;
22
24
  }
25
+ const funcContextAny = funcContext;
26
+ funcContextAny.moment = moment;
27
+ funcContextAny._ = _;
28
+ if (isNotNil(funCfg.executeExpressReturnString)) {
29
+ const expressResult = ejs.render(funCfg.executeExpressReturnString, funcContextAny);
30
+ return expressResult;
31
+ }
32
+ if (isNotNil(funCfg.executeExpressReturnObject)) {
33
+ const expressResult = ejs.render(funCfg.executeExpressReturnObject, funcContextAny);
34
+ return JSON.parse(expressResult);
35
+ }
23
36
  if (isNotNil(funCfg.const)) {
24
37
  return funCfg.const;
25
38
  }
@@ -208,6 +208,16 @@ class CrudProGenSqlCondition {
208
208
  tmpSql = `${toSqlColumnName(key)} like concat('%', ?::text, '%')`; // like包含
209
209
  }
210
210
  }
211
+ else if (equalsIgnoreCase(keys_1.KeysOfConditions.$JSON_ARRAY_CONTAINS, compare)) {
212
+ if (this.sqlCfg.sqlDbType === keys_1.SqlDbType.mysql) {
213
+ tmpArgList.push(value0);
214
+ tmpSql = `JSON_CONTAINS( ${toSqlColumnName(key)} , JSON_ARRAY(?), '$')`; // MYSQL JSON_CONTAINS包含
215
+ }
216
+ if (this.sqlCfg.sqlDbType === keys_1.SqlDbType.postgres) {
217
+ tmpArgList.push(value0);
218
+ tmpSql = `${toSqlColumnName(key)} @> ('["' || ? || '"]')::jsonb`; // POSTGRES JSON_CONTAINS包含
219
+ }
220
+ }
211
221
  else if (equalsIgnoreCase(keys_1.KeysOfConditions.$NOT_LIKE_INCLUDE, compare)) {
212
222
  tmpArgList.push(value0);
213
223
  tmpSql = `${toSqlColumnName(key)} COLLATE UTF8MB4_GENERAL_CI not like concat('%',?, '%')`; // like不包含
@@ -308,6 +318,11 @@ class CrudProGenSqlCondition {
308
318
  throwCommonException(keys_1.KeysOfConditions.$NOT_LIKE_INCLUDE, '字符串');
309
319
  }
310
320
  }
321
+ else if (equalsIgnoreCase(keys_1.KeysOfConditions.$JSON_ARRAY_CONTAINS, compare)) {
322
+ if (!isBasicType(value0)) {
323
+ throwCommonException(keys_1.KeysOfConditions.$JSON_ARRAY_CONTAINS, '字符串或数字');
324
+ }
325
+ }
311
326
  else if (equalsIgnoreCase(keys_1.KeysOfConditions.$MATCH, compare)) {
312
327
  if (!isBasicType(value0)) {
313
328
  throwCommonException(keys_1.KeysOfConditions.$MATCH, '字符串');
@@ -36,7 +36,12 @@ const ModelUtils = {
36
36
  return defaultConfigs_1.DEFAULT_MAX_LIMIT;
37
37
  },
38
38
  checkFuncCfgValid(cfgModel) {
39
- const arr = [cfgModel.functionName, cfgModel.const, cfgModel.constString, cfgModel.constNumber, cfgModel.constBool, cfgModel.context, cfgModel.contextAsString, cfgModel.contextAsNumber, cfgModel.contextAsBool];
39
+ const arr = [
40
+ cfgModel.functionName, cfgModel.const, cfgModel.constString, cfgModel.constNumber,
41
+ cfgModel.constBool, cfgModel.context,
42
+ cfgModel.contextAsString, cfgModel.contextAsNumber, cfgModel.contextAsBool,
43
+ cfgModel.executeExpressReturnObject, cfgModel.executeExpressReturnString
44
+ ];
40
45
  for (let i = 0; i < arr.length; i++) {
41
46
  const arrElement = arr[i];
42
47
  if (typeof arrElement !== 'undefined') {
@@ -1,6 +1,7 @@
1
1
  interface IEnumInfo {
2
2
  label: string;
3
3
  value: string | number;
4
+ style?: string;
4
5
  children?: IEnumInfo[];
5
6
  }
6
7
  declare function parseConfigContentToEnumInfo(configEntity: any): IEnumInfo[];
@@ -14,6 +14,9 @@ function parseTreeTableToIEnumInfo(dataSource) {
14
14
  label: label || nodeName,
15
15
  value: value || key,
16
16
  };
17
+ if (item && typeof item.style === 'string' && item.style) {
18
+ enumInfo.style = item.style.trim();
19
+ }
17
20
  if (children) {
18
21
  enumInfo.children = children;
19
22
  }
@@ -17,6 +17,7 @@ export interface ITableBaseInfo {
17
17
  export interface IStdCrudCfgObj {
18
18
  tableBaseInfo: ITableBaseInfo;
19
19
  authSetting: any;
20
+ kanbanAuthSetting: any;
20
21
  actionColumns?: any[];
21
22
  queryFormFields?: any[];
22
23
  tableColumns?: any[];
@@ -29,9 +30,13 @@ export interface ICrudStdAppInfo {
29
30
  actionsMap?: any;
30
31
  status: number;
31
32
  }
33
+ export interface ISettingKeyActionCfg {
34
+ settingKey: string;
35
+ hasOperationPerm: boolean;
36
+ }
32
37
  export interface ICrudStdAppInfoForSettingKey extends ICrudStdAppInfo {
33
- settingKeyActionCfg?: any;
34
- buttonSettingKeyActionCfg?: any;
38
+ settingKeyActionCfg?: ISettingKeyActionCfg;
39
+ buttonSettingKeyActionCfg?: ISettingKeyActionCfg;
35
40
  authConfig?: string[];
36
41
  authType?: string;
37
42
  }
@@ -39,7 +39,7 @@ export declare class AuthService {
39
39
  * @param loginName
40
40
  * @param workbenchCode
41
41
  */
42
- createUserSession(loginName: string, workbenchCode: string): Promise<{
42
+ createUserSession(loginName: string, workbenchCode: string, bizExt?: any): Promise<{
43
43
  sessionId: string;
44
44
  loginName: string;
45
45
  accountId: any;
@@ -107,7 +107,7 @@ let AuthService = class AuthService {
107
107
  * @param loginName
108
108
  * @param workbenchCode
109
109
  */
110
- async createUserSession(loginName, workbenchCode) {
110
+ async createUserSession(loginName, workbenchCode, bizExt) {
111
111
  const userAccount = await this.queryUserAccountByLoginName(loginName);
112
112
  if (!userAccount) {
113
113
  throw new exceptions_1.CommonException('USER_ACCOUNT_NOT_EXIST', '用户账号不存在');
@@ -126,7 +126,11 @@ let AuthService = class AuthService {
126
126
  accountId,
127
127
  workbenchCode,
128
128
  accountType: userSession_1.SYS_ACCOUNT_TYPE,
129
+ bizExt: {},
129
130
  };
131
+ if (bizExt && typeof bizExt === 'object') {
132
+ sessionInfo.bizExt = bizExt;
133
+ }
130
134
  await this.userSessionService.saveUserSession(sessionInfo);
131
135
  return {
132
136
  sessionId,
@@ -86,7 +86,7 @@ let EnumInfoService = class EnumInfoService {
86
86
  return null;
87
87
  }
88
88
  const rows = (0, parseConfig_1.parseConfigContentToEnumInfo)(obj);
89
- return rows.map(e => ({ label: e.label, value: e.value, children: e.children })).filter(filterEmpty);
89
+ return rows.map(e => ({ label: e.label, value: e.value, style: e.style, children: e.children })).filter(filterEmpty);
90
90
  }
91
91
  async queryEnumInfoItemByDict(code) {
92
92
  const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
@@ -65,6 +65,16 @@ class ColumnsRelationMaker {
65
65
  }
66
66
  }
67
67
  }
68
+ function addMarkerColumnRelation(maker, dataIndex, componentName) {
69
+ // 关联用户信息
70
+ if (componentName === CrudStdConstant_1.tableColumnRenders.RenderUserInfo) {
71
+ maker.addColumnRelationByAccount(dataIndex);
72
+ }
73
+ // 关联枚举文案/状态标签颜色
74
+ if (componentName === CrudStdConstant_1.tableColumnRenders.RenderDictEnumText || componentName === CrudStdConstant_1.tableColumnRenders.RenderDictEnumStatus) {
75
+ maker.addColumnRelationByDictEnumStatus(dataIndex);
76
+ }
77
+ }
68
78
  let CrudStdRelationService = class CrudStdRelationService extends BaseService_1.BaseService {
69
79
  async addCfgModelColumnsRelation(cfgModel, appInfo) {
70
80
  var _a;
@@ -77,22 +87,21 @@ let CrudStdRelationService = class CrudStdRelationService extends BaseService_1.
77
87
  const tableColumn = tableColumns[i];
78
88
  const dataIndex = _.get(tableColumn, 'dataIndex');
79
89
  const componentName = _.get(tableColumn, 'component.componentName');
80
- // 关联用户信息
81
- if (componentName === CrudStdConstant_1.tableColumnRenders.RenderUserInfo) {
82
- maker.addColumnRelationByAccount(dataIndex);
83
- }
84
- // 关联枚举文案/状态标签颜色
85
- if (componentName === CrudStdConstant_1.tableColumnRenders.RenderDictEnumText || componentName === CrudStdConstant_1.tableColumnRenders.RenderDictEnumStatus) {
86
- maker.addColumnRelationByDictEnumStatus(dataIndex);
87
- }
90
+ addMarkerColumnRelation(maker, dataIndex, componentName);
88
91
  // 子属性的配置
89
92
  const balloonTipFields = _.get(tableColumn, 'component.props.balloonTipFields');
90
93
  if (Array.isArray(balloonTipFields) && balloonTipFields.length > 0) {
91
94
  for (let j = 0; j < balloonTipFields.length; j++) {
92
95
  const { component, name } = balloonTipFields[j] || {};
93
- if (component === CrudStdConstant_1.tableColumnRenders.RenderUserInfo) {
94
- maker.addColumnRelationByAccount(name);
95
- }
96
+ addMarkerColumnRelation(maker, name, component);
97
+ }
98
+ }
99
+ // 子属性的配置
100
+ const linkDataConfig = _.get(tableColumn, 'component.props.linkDataConfig');
101
+ if (Array.isArray(linkDataConfig) && linkDataConfig.length > 0) {
102
+ for (let j = 0; j < linkDataConfig.length; j++) {
103
+ const { component, name } = linkDataConfig[j] || {};
104
+ addMarkerColumnRelation(maker, name, component);
96
105
  }
97
106
  }
98
107
  }
@@ -10,6 +10,7 @@ import { ApiBaseService } from '../base/ApiBaseService';
10
10
  export declare const SPECIAL_SETTING_KEY: {
11
11
  QUERY_LIST: string;
12
12
  QUERY_ONE: string;
13
+ KANBAN_VIEW_STATUS_UPDATE: string;
13
14
  };
14
15
  export interface ICrudStdActionParams {
15
16
  appCode: string;
@@ -28,6 +28,7 @@ const MixinUtils_1 = require("../../libs/crud-pro/utils/MixinUtils");
28
28
  exports.SPECIAL_SETTING_KEY = {
29
29
  QUERY_LIST: 'QUERY_LIST',
30
30
  QUERY_ONE: 'QUERY_ONE',
31
+ KANBAN_VIEW_STATUS_UPDATE: 'KANBAN_VIEW_STATUS_UPDATE', // 看板视图中更新状态
31
32
  };
32
33
  function isNotEmptyStr(str) {
33
34
  return typeof str === 'string' && str.trim().length > 0;
@@ -59,6 +60,7 @@ let CrudStdService = class CrudStdService extends ApiBaseService_1.ApiBaseServic
59
60
  * 执行普通CRUD
60
61
  */
61
62
  async executeStdQuery(stdAction, params, sqlSimpleName) {
63
+ var _a;
62
64
  const appCode = stdAction.appCode;
63
65
  const appInfo = await this.getParsedCrudStdAppForSettingKey(stdAction);
64
66
  const stdCrudCfgObj = appInfo.stdCrudCfgObj;
@@ -79,7 +81,7 @@ let CrudStdService = class CrudStdService extends ApiBaseService_1.ApiBaseServic
79
81
  if (typeof maxLimit === 'number' && maxLimit > 0) {
80
82
  cfgModel.maxLimit = maxLimit;
81
83
  }
82
- if (!appInfo.settingKeyActionCfg.hasOperationPerm) {
84
+ if (!((_a = appInfo.settingKeyActionCfg) === null || _a === void 0 ? void 0 : _a.hasOperationPerm)) {
83
85
  throw new devops_1.BizException('没有权限');
84
86
  }
85
87
  // 软删除操作。
@@ -163,7 +165,16 @@ let CrudStdService = class CrudStdService extends ApiBaseService_1.ApiBaseServic
163
165
  }
164
166
  const actionsMap = appInfo.actionsMap;
165
167
  const appSchema = appInfo.stdCrudCfgObj;
166
- if (settingKey === exports.SPECIAL_SETTING_KEY.QUERY_LIST || settingKey === exports.SPECIAL_SETTING_KEY.QUERY_ONE) {
168
+ // 看板视图中更新状态
169
+ if (settingKey === exports.SPECIAL_SETTING_KEY.KANBAN_VIEW_STATUS_UPDATE) {
170
+ const kanbanAuthSettingValues = _.get(appSchema, 'kanbanAuthSetting.values');
171
+ const { update_auth_type, update_auth_config_by_func_code, update_auth_config_by_role_code } = (kanbanAuthSettingValues || {});
172
+ const authConfig = update_auth_type === 'byFuncCode' ? update_auth_config_by_func_code : update_auth_config_by_role_code;
173
+ const hasOperationPerm = this.ctx.userSession.isAuthPass(update_auth_type, authConfig);
174
+ appInfo.settingKeyActionCfg = { settingKey, hasOperationPerm };
175
+ }
176
+ // 列表查询或详情查询
177
+ else if (settingKey === exports.SPECIAL_SETTING_KEY.QUERY_LIST || settingKey === exports.SPECIAL_SETTING_KEY.QUERY_ONE) {
167
178
  const authSettingValues = _.get(appSchema, 'authSetting.values');
168
179
  const { auth_type, auth_config_by_func_code, auth_config_by_role_code } = authSettingValues || {};
169
180
  const authConfig = auth_type === 'byFuncCode' ? auth_config_by_func_code : auth_config_by_role_code;
@@ -20,21 +20,29 @@ const MixinUtils_1 = require("../../libs/crud-pro/utils/MixinUtils");
20
20
  const global_config_1 = require("../../libs/global-config/global-config");
21
21
  const dictMixUtils = new CurdMixUtils_1.CrudMixUtils(CurdMixUtils_1.RelatedType.accountBasic);
22
22
  const TMP_CTX_KEY = _.uniqueId('CurdMixByAccountService');
23
+ const TMP_CTX_KEY2 = _.uniqueId('CurdMixByAccountService2');
23
24
  let CurdMixByAccountService = class CurdMixByAccountService {
24
25
  async handleExecuteContextPrepare(executeContext) {
25
- const { SystemDbName, SystemDbType, toFatcmsUserAccountId } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
26
+ const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
26
27
  const relations = dictMixUtils.pickColumnRelations(executeContext);
27
28
  if (!relations || relations.length === 0) {
28
29
  return;
29
30
  }
31
+ const isArrayModeMap = new Map();
30
32
  const accountIds = new Set();
31
33
  dictMixUtils.forEachRowAndColumnsRelation(executeContext, (row, columnRelation) => {
32
34
  const { sourceColumn } = columnRelation;
33
35
  if (sourceColumn) {
34
36
  const accountId = _.get(row, sourceColumn);
35
37
  if (accountId) {
36
- const fatcmsUserAccountId = toFatcmsUserAccountId(accountId);
37
- accountIds.add(fatcmsUserAccountId);
38
+ const { isArray, fatcmsUserAccountIdList, fatcmsUserAccountId } = (0, CurdMixUtils_1.toFatcmsUserAccountIdList)(accountId);
39
+ if (isArray) {
40
+ isArrayModeMap.set(sourceColumn, true);
41
+ fatcmsUserAccountIdList.forEach(item => accountIds.add(item));
42
+ }
43
+ else if (fatcmsUserAccountId) {
44
+ accountIds.add(fatcmsUserAccountId);
45
+ }
38
46
  }
39
47
  }
40
48
  });
@@ -59,9 +67,11 @@ let CurdMixByAccountService = class CurdMixByAccountService {
59
67
  const res = await this.curdProService.executeCrudByCfg(reqJson, cfgModel);
60
68
  const userInfos = res.getResRows();
61
69
  executeContext[TMP_CTX_KEY] = MixinUtils_1.MixinUtils.toNewMap(userInfos, (obj) => obj.account_id);
70
+ executeContext[TMP_CTX_KEY2] = isArrayModeMap;
62
71
  }
63
72
  async handleExecuteContext(executeContext) {
64
73
  const userInfoMap = executeContext[TMP_CTX_KEY];
74
+ const isArrayModeMap = executeContext[TMP_CTX_KEY2];
65
75
  if (!userInfoMap || userInfoMap.size === 0) {
66
76
  return;
67
77
  }
@@ -69,10 +79,21 @@ let CurdMixByAccountService = class CurdMixByAccountService {
69
79
  const sourceColumn = columnRelation.sourceColumn;
70
80
  const targetColumns = columnRelation.targetColumns;
71
81
  if (!Array.isArray(targetColumns) || targetColumns.length === 0) {
72
- columnRelation.targetColumns = [
73
- { from: 'nick_name', to: `${sourceColumn}_user.nick_name` },
74
- { from: 'avatar', to: `${sourceColumn}_user.avatar` },
75
- ];
82
+ const isArrayMode = isArrayModeMap.get(sourceColumn) || false;
83
+ if (isArrayMode === true) {
84
+ columnRelation.targetColumns = [
85
+ { from: 'nick_name', to: `${sourceColumn}_array_info[$ARRAY_INDEX].nick_name` },
86
+ { from: 'avatar', to: `${sourceColumn}_array_info[$ARRAY_INDEX].avatar` },
87
+ { from: 'account_id', to: `${sourceColumn}_array_info[$ARRAY_INDEX].account_id` },
88
+ ];
89
+ }
90
+ else {
91
+ columnRelation.targetColumns = [
92
+ { from: 'nick_name', to: `${sourceColumn}_user.nick_name` },
93
+ { from: 'avatar', to: `${sourceColumn}_user.avatar` },
94
+ { from: 'account_id', to: `${sourceColumn}_user.account_id` },
95
+ ];
96
+ }
76
97
  }
77
98
  dictMixUtils.copyUserInfoToRowNoRelatedCode(row, userInfoMap, columnRelation);
78
99
  });
@@ -9,6 +9,12 @@ declare enum RelatedType {
9
9
  workbenchBasic = "workbenchBasic"
10
10
  }
11
11
  declare type FuncRowColumnRelation = (row: any, columnRelation: ColumnRelation) => void;
12
+ interface IFatcmsUserAccountIdList {
13
+ isArray: boolean;
14
+ fatcmsUserAccountId?: string;
15
+ fatcmsUserAccountIdList?: string[];
16
+ }
17
+ declare function toFatcmsUserAccountIdList(accountId: any): IFatcmsUserAccountIdList;
12
18
  declare class CrudMixUtils {
13
19
  private readonly relatedType;
14
20
  constructor(relatedType: RelatedType);
@@ -48,4 +54,4 @@ declare class CrudMixUtils {
48
54
  */
49
55
  copyUserInfoToRowNoRelatedCode(row: any, map: Map<string, any>, columnRelation: ColumnRelation): void;
50
56
  }
51
- export { CrudMixUtils, RelatedType, FuncRowColumnRelation };
57
+ export { CrudMixUtils, RelatedType, FuncRowColumnRelation, toFatcmsUserAccountIdList };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RelatedType = exports.CrudMixUtils = void 0;
3
+ exports.toFatcmsUserAccountIdList = exports.RelatedType = exports.CrudMixUtils = void 0;
4
4
  const keys_1 = require("../../libs/crud-pro/models/keys");
5
5
  const MixinUtils_1 = require("../../libs/crud-pro/utils/MixinUtils");
6
6
  const global_config_1 = require("../../libs/global-config/global-config");
@@ -94,6 +94,59 @@ function copyByArraySourceValue(row, sourceValue, targetColumns, getValue) {
94
94
  }
95
95
  return resultObj.isSetArray;
96
96
  }
97
+ function toFatcmsUserAccountIdList(accountId) {
98
+ const { toFatcmsUserAccountId } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
99
+ if (!accountId) {
100
+ return {
101
+ isArray: false,
102
+ fatcmsUserAccountId: null
103
+ };
104
+ }
105
+ // 数字形式
106
+ if (typeof accountId === 'number') {
107
+ return {
108
+ isArray: false,
109
+ fatcmsUserAccountId: toFatcmsUserAccountId("" + accountId),
110
+ };
111
+ }
112
+ if (typeof accountId === 'string') {
113
+ const json = (0, functions_1.parseJsonObject)(accountId);
114
+ // 数组形式
115
+ if (json && Array.isArray(json) && json.length > 0) {
116
+ return {
117
+ isArray: true,
118
+ fatcmsUserAccountIdList: json.map(item => {
119
+ if (typeof item === 'string' || typeof item === 'number') {
120
+ return toFatcmsUserAccountId("" + item);
121
+ }
122
+ return null;
123
+ }).filter(item => item !== null),
124
+ };
125
+ }
126
+ // 单值形式
127
+ return {
128
+ isArray: false,
129
+ fatcmsUserAccountId: toFatcmsUserAccountId(accountId),
130
+ };
131
+ }
132
+ // 数组形式
133
+ if (Array.isArray(accountId)) {
134
+ return {
135
+ isArray: true,
136
+ fatcmsUserAccountIdList: accountId.map(item => {
137
+ if (typeof item === 'string' || typeof item === 'number') {
138
+ return toFatcmsUserAccountId("" + item);
139
+ }
140
+ return null;
141
+ }).filter(item => item !== null),
142
+ };
143
+ }
144
+ return {
145
+ isArray: false,
146
+ fatcmsUserAccountId: null
147
+ };
148
+ }
149
+ exports.toFatcmsUserAccountIdList = toFatcmsUserAccountIdList;
97
150
  class CrudMixUtils {
98
151
  constructor(relatedType) {
99
152
  this.relatedType = relatedType;
@@ -257,8 +310,7 @@ class CrudMixUtils {
257
310
  if (!accountId) {
258
311
  return;
259
312
  }
260
- const { toFatcmsUserAccountId } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
261
- const fatcmsUserAccountId = toFatcmsUserAccountId(accountId);
313
+ const { isArray, fatcmsUserAccountIdList, fatcmsUserAccountId } = toFatcmsUserAccountIdList(accountId);
262
314
  const getValue = (code) => {
263
315
  if (!map) {
264
316
  return null;
@@ -268,8 +320,10 @@ class CrudMixUtils {
268
320
  }
269
321
  return map[code];
270
322
  };
271
- const isSetArray = copyByArraySourceValue(row, fatcmsUserAccountId, targetColumns, getValue);
272
- if (!isSetArray) {
323
+ if (isArray) {
324
+ copyByArraySourceValue(row, fatcmsUserAccountIdList, targetColumns, getValue);
325
+ }
326
+ else if (fatcmsUserAccountId) {
273
327
  copyBySingleSourceValue(row, fatcmsUserAccountId, targetColumns, getValue);
274
328
  }
275
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midway-fatcms",
3
- "version": "0.0.1-beta.61",
3
+ "version": "0.0.1-beta.64",
4
4
  "description": "This is a midway component sample",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -41,7 +41,7 @@ export default (appInfo: any) => {
41
41
  jsonLimit: '10mb',
42
42
  textLimit: '10mb',
43
43
  xmlLimit: '10mb',
44
- ignore: ['/ns/gw/proxy/*', '/ns/gw/file/uploadFile', '/ns/api/manage/deploy/uploadAssets'], // 忽略代理路径的 body 解析
44
+ ignore: ['/ns/gw/proxy/*', '/ns/gw/file/uploadFile', '/ns/gw/file/uploadFilePublic', '/ns/api/manage/deploy/uploadAssets'], // 忽略代理路径的 body 解析
45
45
  },
46
46
 
47
47
  mysql2: {
@@ -130,7 +130,7 @@ export default (appInfo: any) => {
130
130
  // base64: boolean,设置原始body是否是base64格式,默认为false,一般用于腾讯云的兼容
131
131
  base64: false,
132
132
  // 仅在匹配路径到 /api/upload 的时候去解析 body 中的文件信息
133
- match: ['/ns/gw/file/uploadFile', '/ns/api/manage/deploy/uploadAssets'],
133
+ match: ['/ns/gw/file/uploadFile', '/ns/gw/file/uploadFilePublic', '/ns/api/manage/deploy/uploadAssets'],
134
134
  },
135
135
 
136
136
  view: {
@@ -5,7 +5,7 @@ import { BaseApiController } from '../base/BaseApiController';
5
5
  import { BizException } from '@/models/devops';
6
6
  import { AccessType, FILE_GET_TYPES } from '@/models/bizmodels';
7
7
  import { FileCenterService, PATH_PREFIX, toDownloadPaths, isImageFile } from '@/service/FileCenterService';
8
- import { checkPermission } from '@/middleware/permission.middleware';
8
+ import { checkLogin, checkPermission } from '@/middleware/permission.middleware';
9
9
  import { CommonResult } from '@/libs/utils/common-dto';
10
10
  import { SystemFuncCode } from '@/models/SystemPerm';
11
11
 
@@ -24,6 +24,13 @@ export class FileController extends BaseApiController {
24
24
  @Inject()
25
25
  protected fileCenterService: FileCenterService;
26
26
 
27
+ @Post('/uploadFilePublic', { middleware: [checkLogin()] })
28
+ async uploadFilePublic(@Files() files, @Fields() fields, @Query() queryData): Promise<CommonResult> {
29
+ return this.uploadFilePrivate(files, fields, queryData);
30
+ }
31
+
32
+
33
+
27
34
  /**
28
35
  * 上传普通文件。必须是登录用户才能上传。
29
36
  * @param files
@@ -32,6 +39,13 @@ export class FileController extends BaseApiController {
32
39
  */
33
40
  @Post('/uploadFile', { middleware: [checkPermission([SystemFuncCode.DocMangeWrite, SystemFuncCode.FileMangeWrite, SystemFuncCode.UserAccountMangeWrite])] })
34
41
  async uploadFile(@Files() files, @Fields() fields, @Query() queryData): Promise<CommonResult> {
42
+ return this.uploadFilePrivate(files, fields, queryData);
43
+ }
44
+
45
+
46
+
47
+
48
+ private async uploadFilePrivate(files: any, fields: any, queryData: any) {
35
49
  const accessType = queryData?.accessType || fields?.accessType || AccessType.open;
36
50
  const referer = this.ctx.req.headers.referer;
37
51
 
@@ -125,6 +125,9 @@ export interface IFuncCfgModel extends Record<any, any> {
125
125
  contextAsNumber?: string; // 从context中取值
126
126
  contextAsBool?: string; // 从context中取值
127
127
 
128
+ executeExpressReturnObject?: string; // 执行表达式,返回对象。可以包含context中的变量。
129
+ executeExpressReturnString?: string; // 执行表达式,返回字符串。可以包含context中的变量。
130
+
128
131
  message?: string; // 在校验阶段有用,其他阶段没有用
129
132
  }
130
133
 
@@ -121,6 +121,11 @@ export const KeysOfConditions = {
121
121
  $MATCH: '$match', // 全文索引 {age:1, name:{"$match":"张"} }
122
122
  $MATCH_BOOL: '$matchBool', // 全文索引 {age:1, name:{"$matchBool":"张"} }
123
123
 
124
+ /**
125
+ * SELECT * FROM tms_biz.表 WHERE JSON_CONTAINS(`related_user1`, JSON_ARRAY('2512274'), '$');
126
+ */
127
+ $JSON_ARRAY_CONTAINS: '$jsonArrayContains', // 全文索引 { related_user1:{"$jsonArrayContains":"2512274"} }
128
+
124
129
  COMPARE_KEYS: new Set<string>([]),
125
130
  ALL_KEYS: new Set<string>([]),
126
131
  };
@@ -145,6 +150,7 @@ function initKeysOfConditions() {
145
150
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$LIKE_INCLUDE);
146
151
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$NOT_LIKE);
147
152
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$NOT_LIKE_INCLUDE);
153
+ addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$JSON_ARRAY_CONTAINS);
148
154
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$MATCH);
149
155
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$MATCH_BOOL);
150
156
  addIgnoreCase(KeysOfConditions.COMPARE_KEYS, KeysOfConditions.$RANGE);
@@ -4,6 +4,8 @@ import { FuncContext } from '../models/FuncContext';
4
4
  import { TypeUtils } from '../utils/TypeUtils';
5
5
  import { MixinUtils } from '../utils/MixinUtils';
6
6
  import * as _ from 'lodash';
7
+ import * as moment from 'moment';
8
+ import * as ejs from 'ejs';
7
9
  import { CommonException, Exceptions } from '../exceptions';
8
10
  import { KeysOfFunCtx } from '../models/keys';
9
11
 
@@ -24,6 +26,21 @@ class CrudProExecuteFuncService extends CrudProServiceBase {
24
26
  return funCfg;
25
27
  }
26
28
 
29
+ const funcContextAny: any = funcContext;
30
+ funcContextAny.moment = moment;
31
+ funcContextAny._ = _;
32
+
33
+ if (isNotNil(funCfg.executeExpressReturnString)) {
34
+ const expressResult = ejs.render(funCfg.executeExpressReturnString, funcContextAny);
35
+ return expressResult;
36
+ }
37
+
38
+ if (isNotNil(funCfg.executeExpressReturnObject)) {
39
+ const expressResult = ejs.render(funCfg.executeExpressReturnObject, funcContextAny);
40
+ return JSON.parse(expressResult);
41
+ }
42
+
43
+
27
44
  if (isNotNil(funCfg.const)) {
28
45
  return funCfg.const;
29
46
  }
@@ -227,7 +227,22 @@ class CrudProGenSqlCondition {
227
227
  if (this.sqlCfg.sqlDbType === SqlDbType.postgres) {
228
228
  tmpSql = `${toSqlColumnName(key)} like concat('%', ?::text, '%')`; // like包含
229
229
  }
230
- } else if (equalsIgnoreCase(KeysOfConditions.$NOT_LIKE_INCLUDE, compare)) {
230
+ }
231
+
232
+ else if (equalsIgnoreCase(KeysOfConditions.$JSON_ARRAY_CONTAINS, compare)) {
233
+ if (this.sqlCfg.sqlDbType === SqlDbType.mysql) {
234
+ tmpArgList.push(value0);
235
+ tmpSql = `JSON_CONTAINS( ${toSqlColumnName(key)} , JSON_ARRAY(?), '$')`; // MYSQL JSON_CONTAINS包含
236
+ }
237
+
238
+ if (this.sqlCfg.sqlDbType === SqlDbType.postgres) {
239
+ tmpArgList.push(value0);
240
+ tmpSql = `${toSqlColumnName(key)} @> ('["' || ? || '"]')::jsonb`; // POSTGRES JSON_CONTAINS包含
241
+ }
242
+
243
+ }
244
+
245
+ else if (equalsIgnoreCase(KeysOfConditions.$NOT_LIKE_INCLUDE, compare)) {
231
246
  tmpArgList.push(value0);
232
247
  tmpSql = `${toSqlColumnName(key)} COLLATE UTF8MB4_GENERAL_CI not like concat('%',?, '%')`; // like不包含
233
248
 
@@ -323,7 +338,12 @@ class CrudProGenSqlCondition {
323
338
  if (!isBasicType(value0)) {
324
339
  throwCommonException(KeysOfConditions.$NOT_LIKE_INCLUDE, '字符串');
325
340
  }
326
- } else if (equalsIgnoreCase(KeysOfConditions.$MATCH, compare)) {
341
+ } else if (equalsIgnoreCase(KeysOfConditions.$JSON_ARRAY_CONTAINS, compare)) {
342
+ if (!isBasicType(value0)) {
343
+ throwCommonException(KeysOfConditions.$JSON_ARRAY_CONTAINS, '字符串或数字');
344
+ }
345
+ }
346
+ else if (equalsIgnoreCase(KeysOfConditions.$MATCH, compare)) {
327
347
  if (!isBasicType(value0)) {
328
348
  throwCommonException(KeysOfConditions.$MATCH, '字符串');
329
349
  }
@@ -41,7 +41,12 @@ const ModelUtils = {
41
41
  return DEFAULT_MAX_LIMIT;
42
42
  },
43
43
  checkFuncCfgValid(cfgModel: IFuncCfgModel): boolean {
44
- const arr = [cfgModel.functionName, cfgModel.const, cfgModel.constString, cfgModel.constNumber, cfgModel.constBool, cfgModel.context, cfgModel.contextAsString, cfgModel.contextAsNumber, cfgModel.contextAsBool];
44
+ const arr = [
45
+ cfgModel.functionName, cfgModel.const, cfgModel.constString, cfgModel.constNumber,
46
+ cfgModel.constBool, cfgModel.context,
47
+ cfgModel.contextAsString, cfgModel.contextAsNumber, cfgModel.contextAsBool,
48
+ cfgModel.executeExpressReturnObject, cfgModel.executeExpressReturnString
49
+ ];
45
50
  for (let i = 0; i < arr.length; i++) {
46
51
  const arrElement = arr[i];
47
52
  if (typeof arrElement !== 'undefined') {
@@ -4,6 +4,7 @@ import { parseJsonObject } from './functions';
4
4
  interface IEnumInfo {
5
5
  label: string;
6
6
  value: string | number;
7
+ style?: string;
7
8
  children?: IEnumInfo[];
8
9
  }
9
10
 
@@ -18,6 +19,9 @@ function parseTreeTableToIEnumInfo(dataSource: any[]): IEnumInfo[] {
18
19
  label: label || nodeName,
19
20
  value: value || key,
20
21
  };
22
+ if (item && typeof item.style === 'string' && item.style) {
23
+ enumInfo.style = item.style.trim();
24
+ }
21
25
  if (children) {
22
26
  enumInfo.children = children;
23
27
  }
@@ -20,6 +20,7 @@ export interface ITableBaseInfo {
20
20
  export interface IStdCrudCfgObj {
21
21
  tableBaseInfo: ITableBaseInfo;
22
22
  authSetting: any;
23
+ kanbanAuthSetting: any;
23
24
  actionColumns?: any[];
24
25
  queryFormFields?: any[];
25
26
  tableColumns?: any[];
@@ -34,9 +35,14 @@ export interface ICrudStdAppInfo {
34
35
  status: number;
35
36
  }
36
37
 
38
+ export interface ISettingKeyActionCfg {
39
+ settingKey: string;
40
+ hasOperationPerm: boolean;
41
+ }
42
+
37
43
  export interface ICrudStdAppInfoForSettingKey extends ICrudStdAppInfo {
38
- settingKeyActionCfg?: any;
39
- buttonSettingKeyActionCfg?: any;
44
+ settingKeyActionCfg?: ISettingKeyActionCfg;
45
+ buttonSettingKeyActionCfg?: ISettingKeyActionCfg;
40
46
  authConfig?: string[];
41
47
  authType?: string;
42
48
  }
@@ -130,7 +130,7 @@ export class AuthService {
130
130
  * @param loginName
131
131
  * @param workbenchCode
132
132
  */
133
- public async createUserSession(loginName: string, workbenchCode: string) {
133
+ public async createUserSession(loginName: string, workbenchCode: string, bizExt?: any) {
134
134
  const userAccount = await this.queryUserAccountByLoginName(loginName);
135
135
 
136
136
  if (!userAccount) {
@@ -152,8 +152,13 @@ export class AuthService {
152
152
  accountId,
153
153
  workbenchCode,
154
154
  accountType: SYS_ACCOUNT_TYPE,
155
+ bizExt: {},
155
156
  };
156
157
 
158
+ if (bizExt && typeof bizExt === 'object') {
159
+ sessionInfo.bizExt = bizExt;
160
+ }
161
+
157
162
  await this.userSessionService.saveUserSession(sessionInfo);
158
163
 
159
164
  return {
@@ -104,7 +104,7 @@ export class EnumInfoService {
104
104
  }
105
105
 
106
106
  const rows = parseConfigContentToEnumInfo(obj);
107
- return rows.map(e => ({ label: e.label, value: e.value, children: e.children })).filter(filterEmpty);
107
+ return rows.map(e => ({ label: e.label, value: e.value, style: e.style, children: e.children })).filter(filterEmpty);
108
108
  }
109
109
 
110
110
  private async queryEnumInfoItemByDict(code: string): Promise<IEnumInfo[]> {
@@ -68,6 +68,19 @@ class ColumnsRelationMaker {
68
68
  }
69
69
 
70
70
 
71
+ function addMarkerColumnRelation(maker: ColumnsRelationMaker, dataIndex: string, componentName: string) {
72
+ // 关联用户信息
73
+ if (componentName === tableColumnRenders.RenderUserInfo) {
74
+ maker.addColumnRelationByAccount(dataIndex);
75
+ }
76
+
77
+ // 关联枚举文案/状态标签颜色
78
+ if (componentName === tableColumnRenders.RenderDictEnumText || componentName === tableColumnRenders.RenderDictEnumStatus) {
79
+ maker.addColumnRelationByDictEnumStatus(dataIndex);
80
+ }
81
+ }
82
+
83
+
71
84
  @Provide()
72
85
  export class CrudStdRelationService extends BaseService {
73
86
  @Inject()
@@ -90,24 +103,24 @@ export class CrudStdRelationService extends BaseService {
90
103
  const dataIndex = _.get(tableColumn, 'dataIndex');
91
104
  const componentName = _.get(tableColumn, 'component.componentName');
92
105
 
93
- // 关联用户信息
94
- if (componentName === tableColumnRenders.RenderUserInfo) {
95
- maker.addColumnRelationByAccount(dataIndex);
96
- }
97
-
98
- // 关联枚举文案/状态标签颜色
99
- if (componentName === tableColumnRenders.RenderDictEnumText || componentName === tableColumnRenders.RenderDictEnumStatus) {
100
- maker.addColumnRelationByDictEnumStatus(dataIndex);
101
- }
106
+ addMarkerColumnRelation(maker, dataIndex, componentName);
102
107
 
103
108
  // 子属性的配置
104
109
  const balloonTipFields = _.get(tableColumn, 'component.props.balloonTipFields');
105
110
  if (Array.isArray(balloonTipFields) && balloonTipFields.length > 0) {
106
111
  for (let j = 0; j < balloonTipFields.length; j++) {
107
112
  const { component, name } = balloonTipFields[j] || {};
108
- if (component === tableColumnRenders.RenderUserInfo) {
109
- maker.addColumnRelationByAccount(name);
110
- }
113
+ addMarkerColumnRelation(maker, name, component);
114
+ }
115
+ }
116
+
117
+
118
+ // 子属性的配置
119
+ const linkDataConfig = _.get(tableColumn, 'component.props.linkDataConfig');
120
+ if (Array.isArray(linkDataConfig) && linkDataConfig.length > 0) {
121
+ for (let j = 0; j < linkDataConfig.length; j++) {
122
+ const { component, name } = linkDataConfig[j] || {};
123
+ addMarkerColumnRelation(maker, name, component);
111
124
  }
112
125
  }
113
126
 
@@ -22,6 +22,7 @@ import { MixinUtils } from '@/libs/crud-pro/utils/MixinUtils';
22
22
  export const SPECIAL_SETTING_KEY = {
23
23
  QUERY_LIST: 'QUERY_LIST',
24
24
  QUERY_ONE: 'QUERY_ONE',
25
+ KANBAN_VIEW_STATUS_UPDATE: 'KANBAN_VIEW_STATUS_UPDATE', // 看板视图中更新状态
25
26
  };
26
27
 
27
28
  export interface ICrudStdActionParams {
@@ -115,7 +116,7 @@ export class CrudStdService extends ApiBaseService {
115
116
  cfgModel.maxLimit = maxLimit;
116
117
  }
117
118
 
118
- if (!appInfo.settingKeyActionCfg.hasOperationPerm) {
119
+ if (!appInfo.settingKeyActionCfg?.hasOperationPerm) {
119
120
  throw new BizException('没有权限');
120
121
  }
121
122
 
@@ -225,7 +226,19 @@ export class CrudStdService extends ApiBaseService {
225
226
  const actionsMap = appInfo.actionsMap;
226
227
  const appSchema = appInfo.stdCrudCfgObj;
227
228
 
228
- if (settingKey === SPECIAL_SETTING_KEY.QUERY_LIST || settingKey === SPECIAL_SETTING_KEY.QUERY_ONE) {
229
+
230
+ // 看板视图中更新状态
231
+ if (settingKey === SPECIAL_SETTING_KEY.KANBAN_VIEW_STATUS_UPDATE) {
232
+
233
+ const kanbanAuthSettingValues = _.get(appSchema, 'kanbanAuthSetting.values');
234
+ const { update_auth_type, update_auth_config_by_func_code, update_auth_config_by_role_code } = (kanbanAuthSettingValues || {});
235
+ const authConfig = update_auth_type === 'byFuncCode' ? update_auth_config_by_func_code : update_auth_config_by_role_code;
236
+ const hasOperationPerm = this.ctx.userSession.isAuthPass(update_auth_type as any, authConfig);
237
+ appInfo.settingKeyActionCfg = { settingKey, hasOperationPerm };
238
+
239
+ }
240
+ // 列表查询或详情查询
241
+ else if (settingKey === SPECIAL_SETTING_KEY.QUERY_LIST || settingKey === SPECIAL_SETTING_KEY.QUERY_ONE) {
229
242
  const authSettingValues = _.get(appSchema, 'authSetting.values');
230
243
  const { auth_type, auth_config_by_func_code, auth_config_by_role_code } = authSettingValues || {};
231
244
  const authConfig = auth_type === 'byFuncCode' ? auth_config_by_func_code : auth_config_by_role_code;
@@ -3,7 +3,7 @@ import { Context } from '@midwayjs/koa';
3
3
  import * as _ from 'lodash';
4
4
  import { CurdProService } from './CurdProService';
5
5
  import { HandleExecuteContextType, IExecuteContextHandler } from '@/libs/crud-pro/models/ExecuteContext';
6
- import { CrudMixUtils, RelatedType } from './CurdMixUtils';
6
+ import { CrudMixUtils, RelatedType, toFatcmsUserAccountIdList } from './CurdMixUtils';
7
7
  import { SystemTables } from '@/models/SystemTables';
8
8
  import { KeysOfSimpleSQL } from '@/libs/crud-pro/models/keys';
9
9
  import { ColumnRelation, IRequestCfgModel, IRequestModel } from '@/libs/crud-pro/interfaces';
@@ -13,6 +13,9 @@ import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
13
13
  const dictMixUtils = new CrudMixUtils(RelatedType.accountBasic);
14
14
 
15
15
  const TMP_CTX_KEY = _.uniqueId('CurdMixByAccountService');
16
+ const TMP_CTX_KEY2 = _.uniqueId('CurdMixByAccountService2');
17
+
18
+
16
19
 
17
20
  @Provide()
18
21
  export class CurdMixByAccountService implements IExecuteContextHandler {
@@ -23,21 +26,28 @@ export class CurdMixByAccountService implements IExecuteContextHandler {
23
26
  protected curdProService: CurdProService;
24
27
 
25
28
  async handleExecuteContextPrepare(executeContext: HandleExecuteContextType) {
26
- const { SystemDbName, SystemDbType, toFatcmsUserAccountId } = GLOBAL_STATIC_CONFIG.getConfig();
29
+ const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
27
30
 
28
31
  const relations = dictMixUtils.pickColumnRelations(executeContext);
29
32
  if (!relations || relations.length === 0) {
30
33
  return;
31
34
  }
32
35
 
36
+ const isArrayModeMap = new Map<string, boolean>();
37
+
33
38
  const accountIds = new Set<string>();
34
39
  dictMixUtils.forEachRowAndColumnsRelation(executeContext, (row: any, columnRelation: ColumnRelation) => {
35
40
  const { sourceColumn } = columnRelation;
36
41
  if (sourceColumn) {
37
42
  const accountId = _.get(row, sourceColumn);
38
43
  if (accountId) {
39
- const fatcmsUserAccountId = toFatcmsUserAccountId(accountId);
40
- accountIds.add(fatcmsUserAccountId);
44
+ const { isArray, fatcmsUserAccountIdList, fatcmsUserAccountId } = toFatcmsUserAccountIdList(accountId);
45
+ if(isArray) {
46
+ isArrayModeMap.set(sourceColumn, true);
47
+ fatcmsUserAccountIdList.forEach(item => accountIds.add(item));
48
+ } else if(fatcmsUserAccountId) {
49
+ accountIds.add(fatcmsUserAccountId);
50
+ }
41
51
  }
42
52
  }
43
53
  });
@@ -66,10 +76,13 @@ export class CurdMixByAccountService implements IExecuteContextHandler {
66
76
  const res = await this.curdProService.executeCrudByCfg(reqJson, cfgModel);
67
77
  const userInfos = res.getResRows();
68
78
  executeContext[TMP_CTX_KEY] = MixinUtils.toNewMap(userInfos, (obj: any) => obj.account_id);
79
+ executeContext[TMP_CTX_KEY2] = isArrayModeMap;
69
80
  }
70
81
 
71
82
  async handleExecuteContext(executeContext: HandleExecuteContextType): Promise<void> {
72
83
  const userInfoMap = executeContext[TMP_CTX_KEY] as Map<string, any>;
84
+ const isArrayModeMap = executeContext[TMP_CTX_KEY2] as Map<string, boolean>;
85
+
73
86
  if (!userInfoMap || userInfoMap.size === 0) {
74
87
  return;
75
88
  }
@@ -78,10 +91,20 @@ export class CurdMixByAccountService implements IExecuteContextHandler {
78
91
  const sourceColumn = columnRelation.sourceColumn;
79
92
  const targetColumns = columnRelation.targetColumns;
80
93
  if (!Array.isArray(targetColumns) || targetColumns.length === 0) {
81
- columnRelation.targetColumns = [
82
- { from: 'nick_name', to: `${sourceColumn}_user.nick_name` },
83
- { from: 'avatar', to: `${sourceColumn}_user.avatar` },
84
- ];
94
+ const isArrayMode = isArrayModeMap.get(sourceColumn) || false;
95
+ if(isArrayMode === true) {
96
+ columnRelation.targetColumns = [
97
+ { from: 'nick_name', to: `${sourceColumn}_array_info[$ARRAY_INDEX].nick_name` },
98
+ { from: 'avatar', to: `${sourceColumn}_array_info[$ARRAY_INDEX].avatar` },
99
+ { from: 'account_id', to: `${sourceColumn}_array_info[$ARRAY_INDEX].account_id` },
100
+ ];
101
+ } else {
102
+ columnRelation.targetColumns = [
103
+ { from: 'nick_name', to: `${sourceColumn}_user.nick_name` },
104
+ { from: 'avatar', to: `${sourceColumn}_user.avatar` },
105
+ { from: 'account_id', to: `${sourceColumn}_user.account_id` },
106
+ ];
107
+ }
85
108
  }
86
109
  dictMixUtils.copyUserInfoToRowNoRelatedCode(row, userInfoMap, columnRelation);
87
110
  });
@@ -104,6 +104,74 @@ function copyByArraySourceValue(row: any, sourceValue: any, targetColumns: CopyA
104
104
  return resultObj.isSetArray;
105
105
  }
106
106
 
107
+
108
+
109
+ interface IFatcmsUserAccountIdList {
110
+ isArray: boolean;
111
+ fatcmsUserAccountId?: string;
112
+ fatcmsUserAccountIdList?: string[];
113
+ }
114
+
115
+ function toFatcmsUserAccountIdList(accountId: any): IFatcmsUserAccountIdList {
116
+ const { toFatcmsUserAccountId } = GLOBAL_STATIC_CONFIG.getConfig();
117
+
118
+ if (!accountId) {
119
+ return {
120
+ isArray: false,
121
+ fatcmsUserAccountId: null
122
+ };
123
+ }
124
+
125
+ // 数字形式
126
+ if (typeof accountId === 'number') {
127
+ return {
128
+ isArray: false,
129
+ fatcmsUserAccountId: toFatcmsUserAccountId("" + accountId),
130
+ }
131
+ }
132
+
133
+ if (typeof accountId === 'string') {
134
+ const json = parseJsonObject(accountId);
135
+
136
+ // 数组形式
137
+ if (json && Array.isArray(json) && json.length > 0) {
138
+ return {
139
+ isArray: true,
140
+ fatcmsUserAccountIdList: json.map(item => {
141
+ if (typeof item === 'string' || typeof item === 'number') {
142
+ return toFatcmsUserAccountId("" + item)
143
+ }
144
+ return null;
145
+ }).filter(item => item !== null),
146
+ }
147
+ }
148
+ // 单值形式
149
+ return {
150
+ isArray: false,
151
+ fatcmsUserAccountId: toFatcmsUserAccountId(accountId),
152
+ }
153
+ }
154
+
155
+ // 数组形式
156
+ if (Array.isArray(accountId)) {
157
+ return {
158
+ isArray: true,
159
+ fatcmsUserAccountIdList: accountId.map(item => {
160
+ if (typeof item === 'string' || typeof item === 'number') {
161
+ return toFatcmsUserAccountId("" + item)
162
+ }
163
+ return null;
164
+ }).filter(item => item !== null),
165
+ }
166
+ }
167
+
168
+ return {
169
+ isArray: false,
170
+ fatcmsUserAccountId: null
171
+ };
172
+ }
173
+
174
+
107
175
  class CrudMixUtils {
108
176
  private readonly relatedType: RelatedType;
109
177
  constructor(relatedType: RelatedType) {
@@ -291,8 +359,7 @@ class CrudMixUtils {
291
359
  return;
292
360
  }
293
361
 
294
- const { toFatcmsUserAccountId } = GLOBAL_STATIC_CONFIG.getConfig();
295
- const fatcmsUserAccountId = toFatcmsUserAccountId(accountId);
362
+ const { isArray, fatcmsUserAccountIdList, fatcmsUserAccountId } = toFatcmsUserAccountIdList(accountId);
296
363
 
297
364
  const getValue = (code: string) => {
298
365
  if (!map) {
@@ -304,11 +371,13 @@ class CrudMixUtils {
304
371
  return map[code];
305
372
  };
306
373
 
307
- const isSetArray = copyByArraySourceValue(row, fatcmsUserAccountId, targetColumns, getValue);
308
- if (!isSetArray) {
374
+ if (isArray) {
375
+ copyByArraySourceValue(row, fatcmsUserAccountIdList, targetColumns, getValue);
376
+ } else if (fatcmsUserAccountId) {
309
377
  copyBySingleSourceValue(row, fatcmsUserAccountId, targetColumns, getValue);
310
378
  }
379
+
311
380
  }
312
381
  }
313
382
 
314
- export { CrudMixUtils, RelatedType, FuncRowColumnRelation };
383
+ export { CrudMixUtils, RelatedType, FuncRowColumnRelation, toFatcmsUserAccountIdList };