前面介绍了如何使用中国城市数据字典,在这里我们再讲述如何实现国家城市的数据联动。具体而言,当用户选择了美国,就显示美国的城市数据字典;选择了中国,就显示中国的城市数据字典

1. 前端表单渲染

1.1 JSON Schema配置

为了实现数据联动,我们需要实现一个自定义渲染组件,首先修改一下JSON Schema的配置:

src/suite-vendor/test-party/modules/test-party/backend/src/config/validation/schema/party.js

  1. 1const moduleInfo = app.meta.mockUtil.parseInfoFromPackage(__dirname);
  2. 2schemas.party = {
  3. 3 type: 'object',
  4. 4 properties: {
  5. 5 ...
  6. 6 partyCity: {
  7. 7 type: 'string',
  8. 8 ebType: 'component',
  9. 9 ebTitle: 'Party City',
  10. 10 ebRender: {
  11. 11 module: moduleInfo.relativeName,
  12. 12 name: 'renderPartyCity',
  13. 13 },
  14. 14 ebParams: {
  15. 15 separator: '/',
  16. 16 leafOnly: true,
  17. 17 },
  18. 18 },
  19. 19 ...
  20. 20 },
  21. 21 };
名称 说明
type 字段类型
ebType 字段渲染类型,用于标示前端渲染组件类型。component是使用自定义渲染组件
ebTitle 字段标题,用于前端渲染,支持国际化
ebRender 自定义渲染组件的信息
ebParams 渲染参数,具体内容由渲染组件决定
  • ebRender
名称 说明
module 自定义渲染组件所属的模块名称,可以通过moduleInfo.relativeName动态获取当前模块名称,在这里的值就是test-party
name 自定义渲染组件的名称
  • ebParams
名称 说明
separator 多级字典项的title在合并时所采用的分隔符
leafOnly 当选择字典项时,是否只能选择叶子结点

1.2 定义渲染组件

关于前端自定义组件的概念,请参见:模块前端开发-Component

src/suite-vendor/test-party/modules/test-party/front/src/components/renderPartyCity.jsx

  1. 1export default {
  2. 2 props: {
  3. 3 context: {
  4. 4 type: Object,
  5. 5 },
  6. 6 },
  7. 7 data() {
  8. 8 return {};
  9. 9 },
  10. 10 watch: {
  11. 11 partyCountry() {
  12. 12 this.context.setValue(null);
  13. 13 },
  14. 14 },
  15. 15 computed: {
  16. 16 partyCountry() {
  17. 17 return this.context.getValue('partyCountry');
  18. 18 },
  19. 19 },
  20. 20 created() {},
  21. 21 methods: {},
  22. 22 render() {
  23. 23 const { parcel, key, property } = this.context;
  24. 24 // only support 1/86
  25. 25 const partyCountry = this.partyCountry;
  26. 26 if (partyCountry !== '1' && partyCountry !== '86') return null;
  27. 27 // render
  28. 28 const propertyNew = this.$meta.util.extend({}, property, {
  29. 29 ebType: 'dict',
  30. 30 ebParams: {
  31. 31 dictKey: partyCountry === '1' ? 'a-dictbooster:citiesUSA' : 'a-dictbooster:citiesChina',
  32. 32 mode: 'tree',
  33. 33 },
  34. 34 ebRender: null,
  35. 35 });
  36. 36 return <eb-list-item-validate parcel={parcel} dataKey={key} property={propertyNew}></eb-list-item-validate>;
  37. 37 },
  38. 38};
  • 行3:由于该组件是在表单渲染时使用的,因此表单渲染引擎会向该组件传入prop: context。通过context,既可以取得当前字段的信息,也可以取得表单渲染引擎的信息

  • 行16:创建一个计算属性: partyCountry,该属性的值可以通过context动态获取

  • 行11:创建一个watch: partyCountry,当计算属性partyCountry变更时清除当前城市的值

  • 行25/26:取得当前partyCountry的值。出于演示目的,只支持美国中国

  • 行28:根据partyCountry的当前值,使用不同的dictKey,与字段的property进行合并,从而产生新的property

  • 行36:使用新的property配置重新渲染当前字段。其中,eb-list-item-validate是CabloyJS前端的内置组件

1.3 注册渲染组件

由于前面定义的渲染组件renderPartyCity.jsx在模块test-party中,为了让表单渲染引擎可以找到该组件,需要在模块的components清单中注册

src/suite-vendor/test-party/modules/test-party/front/src/components.js

  1. 1import renderPartyCity from './components/renderPartyCity.jsx';
  2. 2
  3. 3export default {
  4. 4 renderPartyCity,
  5. 5};

2. 后端code/title转换

2.1 meta配置

由于城市的数据字典是不固定的,因此需要禁用系统默认的转换逻辑,而由我们自行转换,需要在宴会meta信息中配置一下:

src/suite-vendor/test-party/modules/test-party/backend/src/meta.js

  1. 1 base: {
  2. 2 atoms: {
  3. 3 party: {
  4. 4 info: {
  5. 5 ...
  6. 6 dict: {
  7. 7 fields: {
  8. 8 ...
  9. 9 partyCity: {
  10. 10 translate: false,
  11. 11 },
  12. 12 ...
  13. 13 },
  14. 14 },
  15. 15 },
  16. 16 },
  17. 17 },
  18. 18 },
  • dict.fields.partyCity
名称 说明
translate 设置为false,从而禁用系统默认的转换逻辑

2.2 code/title转换

我们知道,用户查看宴会列表或者宴会条目最终都会执行后端的方法ctx.bean.atom.selectctx.bean.atom.read。而这两个方法最终会调用指定原子类型bean组件中的方法:select/read。因此,我们需要在这两个方法中进行code/title转换

src/suite-vendor/test-party/modules/test-party/backend/src/bean/atom.party.js

  1. 1module.exports = app => {
  2. 2 class Atom extends app.meta.AtomCmsBase {
  3. 3 ...
  4. 4 async read({ atomClass, options, key, user }) {
  5. 5 // super
  6. 6 const item = await super.read({ atomClass, options, key, user });
  7. 7 if (!item) return null;
  8. 8 // read
  9. 9 await this._translate(item);
  10. 10 await this._getMeta(item, options);
  11. 11 // ok
  12. 12 return item;
  13. 13 }
  14. 14
  15. 15 async select({ atomClass, options, items, user }) {
  16. 16 // super
  17. 17 await super.select({ atomClass, options, items, user });
  18. 18 // select
  19. 19 for (const item of items) {
  20. 20 await this._translate(item);
  21. 21 await this._getMeta(item, options);
  22. 22 }
  23. 23 }
  24. 24 ...
  25. 25 async _translate(item) {
  26. 26 // dictKey
  27. 27 const partyCountry = item.partyCountry;
  28. 28 if (partyCountry !== '1' && partyCountry !== '86') return;
  29. 29 const dictKey = partyCountry === '1' ? 'a-dictbooster:citiesUSA' : 'a-dictbooster:citiesChina';
  30. 30 // code
  31. 31 const code = item.partyCity;
  32. 32 if (!code) return;
  33. 33 // findItem
  34. 34 const res = await this.ctx.bean.dict.findItem({
  35. 35 dictKey,
  36. 36 code,
  37. 37 options: { separator: '/' },
  38. 38 });
  39. 39 if (res) {
  40. 40 item._partyCityTitle = res.titleFull;
  41. 41 item._partyCityTitleLocale = res.titleLocaleFull;
  42. 42 }
  43. 43 }
  44. 44 ...
  45. 45 }
  46. 46
  47. 47 return Atom;
  48. 48};
  • 行10:read调用this._getMeta

  • 行21:select调用this._getMeta

  • 行29:根据partyCountry的当前值使用不同的城市数据字典

  • 行34:调用this.ctx.bean.dict.findItem获取对应字典项的信息

  • 行40/41:将title的完整路径和国际化值写入数据,从而完成code/title转换

3. 前端显示Title

由于我们前面自行完成的code/title转换,向数据注入值的规则与系统默认的一致,因此在前端显示title的逻辑也没有变化。故从略