init.json Guide

12003

Quick Start

jianghu-init JSON manages page structure and API interfaces efficiently in JSON format, optimizing your development workflow and boosting productivity. This guide walks through jianghu-init JSON in detail.

Installation

Before you begin, make sure the jianghu-init tool is installed. Use the following commands:

  1. npm uninstall -g @jianghujs/jianghu-init
  2. # Latest version: 3.2.5
  3. npm install -g @jianghujs/jianghu-init@latest

👉 In-depth guide to installing the jianghu-init tool 👈

Generate Reference Examples in a Jianghu Project

  • First, go to an existing JianghuJS project directory.

👉 Create a jianghujs project 👈

  • Generate reference examples with:
  1. cd my-jh-project
  2. jianghu-init json --generateType=example

Running the command will add the example_class and example_student tables to the current project database and create the following files in your project directory:

  • app\view\init-json\component\exampleStudentOfClass.js
  • app\view\init-json\page\exampleClass.js
  • app\view\component\example\exampleStudentOfClass.html
  • app\view\page\exampleClass.html

These are provided for easy reference.

Step 1: Generate a Config File from a Data Table

When you have a new business table, you can generate its configuration file. For example, suppose there is a table named class:

  1. CREATE TABLE `class` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `classId` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Class ID',
  4. `className` varchar(255) DEFAULT NULL,
  5. `operation` varchar(255) COLLATE utf8mb4_bin DEFAULT 'insert' COMMENT 'Operation; insert, update, jhInsert, jhUpdate, jhDelete jhRestore',
  6. `operationByUserId` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Operator userId',
  7. `operationByUser` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Operator username',
  8. `operationAt` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Operation time; E.g: 2021-05-28T10:24:54+08:00 ',
  9. PRIMARY KEY (`id`)
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

To generate a base configuration file for the class table:

  1. jianghu-init json --generateType=json --pageType=jh-page --table=class --pageId=classManagement

After running the command, a configuration file named classManagement.js will be generated under app/view/init-json/page/.

Step 2: Generate a Page from the Config File

Use the following command to generate the page from the config file created above:

  1. jianghu-init json --generateType=page --pageType=page --file=classManagement

This will generate a page file named classManagement.html under app/view/page/ and automatically configure the corresponding _page and _resouce (resource) records in the database.

👉 Enable dev mode to auto-update the page when the config changes 👈

👉 Configure syntax highlighting for code inside strings 👈

Configuration Protocol

Structure Overview

  1. {
  2. pageType: "jh-page", // Page type: jh-page, jh-mobile-page, or jh-component
  3. pageId: "pageId", // Unique page identifier
  4. pageName: "Class Page", // Page title
  5. template: "jhTemplateV4", // Page template; default jhTemplateV4, use jhMobileTemplateV4 for mobile
  6. version: 'v3', // Version: empty or v2
  7. resourceList: [],// Page resources { actionId, resourceType, resourceData }
  8. headContent: [], // Header area: title, breadcrumbs, server-side search, scene search, etc.
  9. pageContent: [], // Main content area
  10. actionContent: [], // Trigger content (e.g., drawers, dialogs)
  11. includeList: [], // External resources { type, path }
  12. common: { // Native Vue variables + uiAction {data, props, watch, methods, computed}
  13. data: {},
  14. computed: {},
  15. watch: {},
  16. methods: {},
  17. doUiAction: {}, // Custom uiAction { [key]: [method1, method2, doUiAction1]}
  18. },
  19. style: '', // Custom styles
  20. }
Property Type Required Description
pageType string Page type: jh-page, jh-mobile-page, jh-component
pageId string Unique page ID
template string No Template
version string No Version (currently supports v2, v3)
resourceList array No
headContent array Header (e.g., breadcrumbs)
pageContent array Main content
actionContent array Triggers (drawers, dialogs, overlays, etc.)
includeList array External resources { type, path }
common object Variables & methods
style string No

HTML Tags

Within headContent, pageContent, and actionContent, you can configure a tag using a structured object, and raw HTML strings are also supported.

Object structure

  1. pageContent: [
  2. {
  3. tag: 'v-row',
  4. attrs: {
  5. align: "center",
  6. "@click": "doUiAction('updateItem')"
  7. },
  8. quickAttrs: ['no-gutters'],
  9. value: [
  10. { tag: 'v-col', attrs: { cols: 12 }, value: '' },
  11. /*html*/`<v-col cols="12"></v-col>`
  12. ]
  13. }
  14. ]
Property Type Required Description
tag string No Tag name
attr object Tag attributes such as class, @click, etc.
quickAttrs array No Shorthand attributes like v-else, small, disabled
value object No Inner HTML content

HTML string

  1. pageContent: [
  2. `<v-row align="center"><v-col cols="12"></v-col></v-row>`
  3. ]

API/Resource Configuration

Configure Jianghu resources. Defined resources are auto-synced to the database for frontend use.

  1. resourceList: [
  2. {
  3. resourceHook: { "before": [], "after": [] }, // Optional
  4. actionId: 'balance-updateItem',
  5. desc: 'Update class fund balance',
  6. resourceType: 'sql',
  7. resourceData: { "table": "class", "operation": "update" }
  8. }
  9. ]

👉 Detailed resource configuration docs 👈

Including External Resources

When a page needs to include components, styles, or scripts, use includeList. Object structure and HTML strings are both supported.

  1. includeList: [
  2. { type: 'css', path: "/<$ ctx.app.config.appId $>/public/lib/echarts.min.css" },
  3. { type: 'script', path: "/<$ ctx.app.config.appId $>/public/lib/echarts.min.js" },
  4. { type: 'html', path: "component/exampleChart/lineChart.html" },
  5. { type: 'html', path: 'component/courseBatchDrawer.html', includeType: 'auto', attrs: { id: 'courseBatchDrawer' } },
  6. { type: 'vueComponent', name: 'v-chart', component: 'VueCharts' },
  7. `<script src="/<$ ctx.app.config.appId $>/public/lib/echarts.min.js"></script>`
  8. ]
Property Type Required Description
type string Include type: css, js, html, vueComponent
path string No Include path (for css/js/html types)
includeType string No Include mode. Use auto to append tag contents to the end of the page (for css/js/html types)
attrs object No Custom tag attributes (for html type with includeType=auto)
name string No Component name (for vueComponent type)
component string No Component reference (for vueComponent type)

Configure doUiAction

Entry point to augment doUiAction method chains.

  1. pageContent: [
  2. {
  3. tag: 'v-btn',
  4. value: 'Test doUiAction 1',
  5. attrs: { '@click': 'doUiAction("doSomething")' },
  6. quickAttrs: ['small']
  7. },
  8. `<v-btn @click="doUiAction('doSomething')">Test doUiAction 2</v-btn>`
  9. ],
  10. common: {
  11. doUiAction: {
  12. startUpdataBalance: ['async.method1', 'method2'],
  13. doSomething: ['method3(123)', 'doUiAction.startUpdataBalance']
  14. },
  15. methods: {
  16. async method1 (id) {
  17. console.log('method1', id)
  18. },
  19. async method2 () {
  20. console.log('method2')
  21. },
  22. async method3(actionData) {
  23. console.log('method3')
  24. }
  25. }
  26. }
  • By default, method chains run in sequence with await. You can prefix with async. to mark steps as async, e.g.:
  1. doUiAction: {
  2. startUpdataBalance: ['async.method1', 'method2'],
  3. },
  4. // Actual execution
  5. case 'startUpdataBalance':
  6. this.method1(uiActionData);
  7. await this.method2(uiActionData);
  8. break;
  • By default, items in the chain call methods. You can prefix with doUiAction. to include another flow, e.g.:
  1. doUiAction: {
  2. doSomething: ['method3(123)', 'doUiAction.startUpdataBalance'],
  3. doSomething2: ['method3(123)', 'async.doUiAction.startUpdataBalance']
  4. },
  5. // Actual execution
  6. case 'doSomething':
  7. await this.method3(123, uiActionData);
  8. await this.doUiAction('startUpdataBalance', uiActionData);
  9. break;
  10. case 'doSomething2':
  11. await this.method3(123, uiActionData);
  12. this.doUiAction('startUpdataBalance', uiActionData);
  13. break;

You can reference method names, method names with args, or doUiAction names in the chain.

Configuration Examples — Desktop (PC)

Basic Page

  1. const content = {
  2. pageType: "jh-page", pageId: "classManagement", pageName: "classManagement Page", template: 'jhTemplateV4', version: 'v3',
  3. resourceList: [
  4. {
  5. actionId: "selectItemList",
  6. resourceType: "sql",
  7. desc: "✅ Query list — classManagement",
  8. resourceData: { table: "class", operation: "select" }
  9. },
  10. // ... insertItem/updateItem/deleteItem
  11. ], // { actionId: '', resourceType: '', resourceData: {}, resourceHook: {}, desc: '' }
  12. headContent: [
  13. { tag: 'jh-page-title', value: "classManagement", attrs: { cols: 12, sm: 6, md:4 }, helpBtn: true, slot: [] },
  14. { tag: 'v-spacer' },
  15. {
  16. tag: 'jh-search',
  17. attrs: { cols: 12, sm: 6, md:8 },
  18. searchBtn: true,
  19. value: [
  20. { tag: "v-text-field", model: "serverSearchWhereLike.className", attrs: {prefix: 'Prefix'} },
  21. ],
  22. // New in v3
  23. data: {
  24. serverSearchWhereLike: { name: '' },
  25. serverSearchWhere: { semester: '', segment: 'Primary' },
  26. }
  27. }
  28. ],
  29. pageContent: [
  30. {
  31. tag: 'jh-table',
  32. attrs: { },
  33. colAttrs: { clos: 12 },
  34. cardAttrs: { class: 'rounded-lg elevation-0' },
  35. headActionList: [
  36. { tag: 'v-btn', value: 'Create', attrs: { color: 'success', class: 'mr-2', '@click': 'doUiAction("startCreateItem")', small: true } },
  37. { tag: 'v-spacer' },
  38. // Default filter
  39. {
  40. tag: 'v-col',
  41. attrs: { cols: '12', sm: '6', md: '3', xs: 8, class: 'pa-0' },
  42. value: [
  43. { tag: 'v-text-field', attrs: {prefix: 'Filter', 'v-model': 'searchInput', class: 'jh-v-input', ':dense': true, ':filled': true, ':single-line': true} },
  44. ],
  45. }
  46. ],
  47. headers: [
  48. { text: "id", value: "id", width: 80, sortable: true, class: "fixed", cellClass: "fixed" },
  49. { text: "Class ID", value: "classId", width: 80, sortable: true },
  50. { text: "className", value: "className", width: 80, sortable: true },
  51. { text: "Operation", value: "operation", width: 80, sortable: true },
  52. { text: "Operator userId", value: "operationByUserId", width: 80, sortable: true },
  53. { text: "Operator username", value: "operationByUser", width: 80, sortable: true },
  54. { text: "Operation time", value: "operationAt", width: 80, sortable: true },
  55. { text: "Actions", value: "action", type: "action", width: 'window.innerWidth < 500 ? 70 : 120', align: "center", class: "fixed", cellClass: "fixed" },
  56. // width expressions must be wrapped in strings
  57. ],
  58. value: [
  59. // vuetify table custom slot
  60. ],
  61. rowActionList: [
  62. { text: 'Edit', icon: 'mdi-note-edit-outline', color: 'success', click: 'doUiAction("startUpdateItem", item)' }, // Works on both desktop and mobile collapse
  63. { text: 'Delete', icon: 'mdi-trash-can-outline', color: 'error', click: 'doUiAction("deleteItem", item)' } // Works on both desktop and mobile collapse
  64. ],
  65. }
  66. ],
  67. actionContent: [
  68. {
  69. tag: 'jh-create-drawer',
  70. key: "create",
  71. attrs: {},
  72. title: 'Create',
  73. headSlot: [
  74. { tag: 'v-spacer'}
  75. ],
  76. contentList: [
  77. {
  78. label: "Create",
  79. type: "form",
  80. formItemList: [
  81. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  82. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  83. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  84. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  85. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  86. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  87. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  88. ],
  89. action: [{
  90. tag: "v-btn",
  91. value: "Create",
  92. attrs: {
  93. color: "success",
  94. ':small': true,
  95. '@click': "doUiAction('createItem')"
  96. }
  97. }],
  98. },
  99. ]
  100. },
  101. {
  102. tag: 'jh-update-drawer',
  103. key: "update",
  104. attrs: {},
  105. title: 'Edit',
  106. headSlot: [
  107. { tag: 'v-spacer'}
  108. ],
  109. contentList: [
  110. {
  111. label: "Edit",
  112. type: "form",
  113. formItemList: [
  114. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  115. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  116. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  117. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  118. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  119. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  120. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  121. ],
  122. action: [{
  123. tag: "v-btn",
  124. value: "Edit",
  125. attrs: {
  126. color: "success",
  127. ':small': true,
  128. '@click': "doUiAction('updateItem')"
  129. }
  130. }],
  131. },
  132. { label: "Operation history", type: "component", componentPath: "recordHistory", attrs: { table: 'class', pageId: 'classManagement', ':id': 'updateItem.id' } },
  133. ]
  134. },
  135. ],
  136. includeList: [], // { type: < js | css | html | vueComponent >, path: '' }
  137. common: {
  138. data: {
  139. constantObj: {},
  140. validationRules: {
  141. requireRules: [
  142. v => !!v || 'Required',
  143. ],
  144. },
  145. serverSearchWhereLike: { className: '' }, // Server-side LIKE search
  146. serverSearchWhere: { }, // Server-side search
  147. serverSearchWhereIn: { }, // Server-side IN search
  148. filterMap: {}, // Client-side filter criteria
  149. },
  150. dataExpression: {
  151. isMobile: 'window.innerWidth < 500'
  152. }, // data expressions
  153. watch: {},
  154. computed: {
  155. tableDataComputed() {
  156. if(this.filterMap) {
  157. return this.tableData.filter(row => {
  158. for (const key in this.filterMap) {
  159. if (this.filterMap[key] && row[key] !== this.filterMap[key]) {
  160. return false;
  161. }
  162. }
  163. return true;
  164. });
  165. } else {
  166. return this.tableData;
  167. }
  168. },
  169. },
  170. doUiAction: {}, // Extra uiAction { [key]: [action1, action2] }
  171. methods: {}
  172. },
  173. };
  174. module.exports = content;

Title — Title Only

  1. headContent: [
  2. { tag: 'jh-page-title', value: "classManagement", attrs: { cols: 12, sm: 6, md:4 }, helpBtn: true, slot: [] }
  3. ]

Title — Server-side Search

  1. headContent: [
  2. { tag: 'jh-page-title', value: "classManagement", attrs: { cols: 12, sm: 6, md:4 }, helpBtn: true, slot: [] },
  3. { tag: 'v-spacer' },
  4. {
  5. tag: 'jh-search',
  6. attrs: { cols: 12, sm: 6, md:8 },
  7. value: [
  8. { tag: "v-text-field", model: "serverSearchWhereLike.className", attrs: {prefix: 'Prefix'} },
  9. ],
  10. searchBtn: true,
  11. data: {
  12. serverSearchWhereLike: { name: '' },
  13. }
  14. }
  15. ]

Title — Scene Search

  1. headContent: [
  2. { tag: 'jh-page-title', value: "classManagement", attrs: { cols: 12, sm: 6, md:4 }, helpBtn: true, slot: [] },
  3. { tag: 'v-spacer' },
  4. {
  5. tag: 'jh-scene',
  6. attrs: { ':showActionBtn': false, ':mobile': false },
  7. // New in v3
  8. data: {
  9. sceneCreateForm: {},
  10. serverSceneSearchWhere: {},
  11. serverSceneSearchWhereIn: {},
  12. serverSceneSearchWhereLike: {},
  13. serverSceneSearchWhereOptions: [],
  14. serverSceneSearchWhereOrOptions: [],
  15. currentSceneId: 'Public',
  16. defaultSceneList: [
  17. { name: "All", where: {}, whereIn: { "articlePublishStatus": ["public", "draft"] } },
  18. { name: "Public", where: { "articlePublishStatus": "public"}, whereIn: {} },
  19. { name: "Draft", where: { "articlePublishStatus": "draft"}, whereIn: {} },
  20. { name: "Recycle Bin", where: { "articlePublishStatus": "deleted"}, whereIn: {} },
  21. ],
  22. maxSceneDisplay: 5,
  23. }
  24. },
  25. ]

Content — Data Table

  1. pageContent: [
  2. {
  3. tag: 'jh-table',
  4. props: {
  5. serverPagination: true // Enable server-side pagination; default limit 50
  6. },
  7. attrs: {},
  8. colAttrs: { clos: 12 },
  9. cardAttrs: { class: 'rounded-lg elevation-0' },
  10. headActionList: [
  11. { tag: 'v-btn', value: 'Create', attrs: { color: 'success', class: 'mr-2', '@click': 'doUiAction("startCreateItem")', small: true } },
  12. { tag: 'v-spacer' }
  13. ],
  14. headers: [
  15. { text: "id", value: "id", width: 80, sortable: true, class: "fixed", cellClass: "fixed" },
  16. { text: "Class ID", value: "classId", width: 80, sortable: true },
  17. { text: "className", value: "className", width: 80, sortable: true },
  18. { text: "Operation", value: "operation", width: 80, sortable: true },
  19. { text: "Actions", value: "action", type: "action", width: 'window.innerWidth < 500 ? 70 : 120', align: "center", class: "fixed", cellClass: "fixed" },
  20. ],
  21. value: [
  22. // v-table slots
  23. // { tag: 'template', attrs: {'slot': 'body', 'slot-scope': "item"}, value: "<div>Test slot</div>" },
  24. // { tag: 'template', attrs: {'slot': 'item.className', 'slot-scope': "{item, index}"}, value: "<div>{{item.className}}</div>" },
  25. ],
  26. rowActionList: [
  27. { text: 'Edit', icon: 'mdi-note-edit-outline', color: 'success', click: 'doUiAction("startUpdateItem", item)' },
  28. { text: 'Delete', icon: 'mdi-trash-can-outline', color: 'error', click: 'doUiAction("deleteItem", item)' }
  29. ],
  30. }
  31. ]

Content — Data Table (Enable Column Settings)

Common

  1. pageContent: [
  2. {
  3. tag: 'jh-table',
  4. attrs: {}, // v-data-table attrs
  5. colAttrs: {}, // Parent v-col attrs
  6. cardAttrs: {}, // Parent v-card attrs
  7. headers: [], // v-data-table headers
  8. value: [],
  9. showTableColumnSettingBtn: true,
  10. headActionList: [], // v-col slot above table
  11. rowActionList: [], // Action buttons in the action column
  12. }
  13. ],

Modes

  1. pageContent: [
  2. {
  3. tag: 'jh-table',
  4. attrs: {},
  5. value: [],
  6. showTableColumnSettingBtn: true,
  7. headActionList: [],
  8. rowActionList: [],
  9. }
  10. ],
  11. common: {
  12. data: {
  13. columnSettingGroup: {
  14. 'Mode 01': [
  15. "netName",
  16. "stage",
  17. "followUpDate",
  18. "district",
  19. "memberId",
  20. "followUpBy",
  21. "currentResidence",
  22. "openVisitationStatus",
  23. "totalVisitationCount",
  24. "visitorAssignInfo",
  25. "operationAt",
  26. "action"
  27. ],
  28. 'Mode 02': [
  29. "netName",
  30. "stage",
  31. "duoxingRoomName",
  32. "followUpDate",
  33. "tag",
  34. "projectList",
  35. "followUpBy",
  36. "district",
  37. "memberId",
  38. "currentResidence",
  39. "operationAt",
  40. "action"
  41. ],
  42. 'Mode 03': [
  43. "netName",
  44. "orgId",
  45. "orgName",
  46. "roleId",
  47. "isDefaultOrg"
  48. ],
  49. },
  50. }
  51. }
Property Type Required Description
showTableColumnSettingBtn boolean No Enable column settings; default false

Drawers — Create/Edit Form

  1. actionContent: [
  2. {
  3. tag: 'jh-create-drawer',
  4. key: "create",
  5. attrs: {},
  6. title: 'Create',
  7. headSlot: [
  8. { tag: 'v-spacer'}
  9. ],
  10. contentList: [
  11. {
  12. label: "Create",
  13. type: "form",
  14. formItemList: [
  15. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  16. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  17. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  18. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  19. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  20. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  21. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  22. ],
  23. action: [{
  24. tag: "v-btn",
  25. value: "Create",
  26. attrs: {
  27. color: "success",
  28. '@click': "doUiAction('createItem')"
  29. },
  30. quickAttrs: ['small']
  31. }],
  32. },
  33. ]
  34. },
  35. {
  36. tag: 'jh-update-drawer',
  37. key: "update",
  38. props: {
  39. mergeForm: true // prepareFormData will merge object forms
  40. },
  41. attrs: {},
  42. title: 'Edit',
  43. headSlot: [
  44. { tag: 'v-spacer'}
  45. ],
  46. contentList: [
  47. {
  48. label: "Edit",
  49. type: "form",
  50. formItemList: [
  51. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  52. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  53. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  54. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  55. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  56. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  57. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  58. ],
  59. action: [{
  60. tag: "v-btn",
  61. value: "Edit",
  62. attrs: {
  63. color: "success",
  64. '@click': "doUiAction('updateItem')"
  65. },
  66. quickAttrs: ['small']
  67. }],
  68. },
  69. { label: "Operation history", type: "component", componentPath: "recordHistory", attrs: { table: 'class', pageId: 'classManagement', ':id': 'updateItem.id' } },
  70. ]
  71. },
  72. ]

Drawer — Detail

  1. pageContent: [
  2. `<v-btn @click="openTestDetailDrawer">Open test detail drawer</v-btn>`,
  3. ],
  4. actionContent: [
  5. {
  6. tag: 'jh-drawer',
  7. key: "testDetail",
  8. attrs: {},
  9. title: 'Test Details',
  10. contentList: [
  11. {
  12. type: "form",
  13. formItemList: [
  14. { tag: 'span', value: 'Course Info', md: 12, attrs: {class: 'title pl-2'}},
  15. { tag: 'div', md: 12, value: [/*html*/ `
  16. <div class="grey lighten-5">
  17. <v-row class="ma-0 pa-2">
  18. <v-col cols="12" md="3" v-for="(item, index) in courseInfoItems" :key="index">
  19. <span>{{item.title}}: {{item.value}}</span>
  20. </v-col>
  21. </v-row>
  22. </div>
  23. `]
  24. }
  25. ]
  26. }
  27. ]
  28. }
  29. ],
  30. "common": {
  31. data: {
  32. courseInfoItems: [
  33. { title: 'Course Name', value: 'IT Beginner Course' }
  34. ]
  35. }
  36. }

Drawer — Custom Drawer

  1. pageContent: [
  2. `<v-btn @click="openTestCustomDrawer">Open test custom drawer</v-btn>`,
  3. ],
  4. actionContent: [
  5. {
  6. tag: 'jh-drawer',
  7. key: "testCustom",
  8. attrs: {},
  9. title: 'Test Custom Drawer',
  10. contentList: [
  11. `Custom content`
  12. ]
  13. }
  14. ]

Drawer — Multi-tab Drawer

  1. pageContent: [
  2. `<v-btn @click="openTestMultiTabDrawer">Open test multi-tab drawer</v-btn>`,
  3. ],
  4. actionContent: [
  5. {
  6. tag: 'jh-drawer',
  7. key: "testMultiTab",
  8. attrs: {},
  9. title: 'Test Multi-tab',
  10. contentList: [
  11. { label: "Operation History", type: "component", componentPath: "recordHistory" },
  12. { label: "Student List", type: "component", componentPath: "example/studentOfClass", bind: ['classId'] },
  13. ]
  14. }
  15. ]

Drawer — Custom Header Button

  1. pageContent: [
  2. `<v-btn @click="viewTestCustomBtn">Open test custom-button drawer</v-btn>`
  3. ],
  4. actionContent: [
  5. {
  6. tag: 'jh-create-drawer',
  7. key: "testCustomBtn",
  8. attrs: {},
  9. title: 'Test Custom Button',
  10. headSlot: [
  11. { tag: 'v-spacer' },
  12. { tag: 'v-btn', value: 'Custom Button', attrs: { color: 'success', class: 'mr-2', '@click': 'doUiAction("123")', small: true } },
  13. ],
  14. contentList: [ /* Content list */ ]
  15. }
  16. ]

Drawer — Close-Check on Dismiss

  1. actionContent: [
  2. {
  3. tag: 'jh-create-drawer',
  4. key: "create",
  5. attrs: {},
  6. title: 'Create',
  7. isCheckFormBeforeClose: true,
  8. onCheckFormConfirm: 'doUiAction("createItem", { notConfirmed: true })',
  9. headSlot: [
  10. { tag: 'v-spacer'}
  11. ],
  12. contentList: [
  13. {
  14. label: "Create",
  15. type: "form",
  16. formItemList: [
  17. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  18. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  19. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  20. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  21. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  22. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  23. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  24. ],
  25. action: [{
  26. tag: "v-btn",
  27. value: "Create",
  28. attrs: {
  29. color: "success",
  30. ':small': true,
  31. '@click': "doUiAction('createItem')"
  32. }
  33. }],
  34. },
  35. ]
  36. }
  37. ]
Property Type Required Description
key string Form key, e.g., create, update
title string Drawer title
props.mergeForm boolean No Merge form on submit
attrs object No v-navigation-drawer attributes
isCheckFormBeforeClose boolean No Enable close-check; default false. The checked form object key must match the drawer key (e.g., form object createItem, drawer key create).
onCheckFormConfirm string No Method to save content
contentList array Drawer content

Configuration Examples — Mobile

Basic Page

  1. const content = {
  2. pageType: "jh-mobile-page", pageId: "mobile/classManagement", pageName: "classManagement Page", template: "jhMobileTemplateV4", version: 'v2',
  3. resourceList: [
  4. {
  5. actionId: "selectItemList",
  6. resourceType: "sql",
  7. desc: "✅ Query list — classManagement",
  8. resourceData: { table: "class", operation: "select" }
  9. },
  10. // ... insertItem/updateItem/deleteItem
  11. ], // { actionId: '', resourceType: '', resourceData: {}, resourceHook: {}, desc: '' }
  12. headContent: [
  13. { tag: 'jh-page-title', value: "Class Management", helpBtn: true, slot: [] },
  14. {
  15. tag: 'jh-order',
  16. // New in v3
  17. data: {
  18. tableDataOrder: [ { column: "createAt", order: "desc" } ],
  19. tableDataOrderList: [
  20. { text: "Registration Time ↓", value: [ { column: "createAt", order: "desc" } ] },
  21. { text: "Exam Score ↓", value: [ { column: "middleSchoolExamScore", order: "desc" } ] },
  22. { text: "Updated At ↓", value: [ { column: "operationAt", order: "desc" } ] },
  23. ],
  24. }
  25. },
  26. {
  27. tag: 'jh-search',
  28. searchList: [
  29. { tag: 'v-select', model: "serverSearchWhere.semester", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Semester:', color: 'success', ':items': 'constantObj.semester' } },
  30. { tag: 'v-select', model: "serverSearchWhere.segment", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Division:', color: 'success', ':items': 'constantObj.segment' } },
  31. { tag: 'v-text-field', model: "serverSearchWhereLike.name", colAttrs: { class: 'pb-0' }, attrs: { label: 'Student Name:', color: 'success' }, quickAttrs: ['clearable'] },
  32. ],
  33. data: {
  34. serverSearchWhere: { semester: '', segment: 'Primary' },
  35. serverSearchWhereLike: { name: '' },
  36. }
  37. },
  38. { tag: 'v-spacer'},
  39. { tag: 'jh-mode', data: { viewMode: 'simple' } },
  40. ],
  41. pageContent: [
  42. {
  43. tag: 'jh-list',
  44. props: {
  45. limit: 10,
  46. rightArrowText: '',
  47. },
  48. attrs: { cols: 12, class: 'p-0 pb-7', ':style': '`height: calc(100vh - 140px); overflow-y: auto;overscroll-behavior: contain`' },
  49. headers: [
  50. {text: "Student ID", value: "studentId", width: 80, isSimpleMode: true},
  51. {text: "Name", value: "name", width: 90, isTitle: true, slot: [`
  52. <div v-if="item.isMonitor" class="ml-1">
  53. <v-icon color="warning" small>mdi-shield-star</v-icon>
  54. <span>Prefect</span>
  55. </div>
  56. `]},
  57. {text: "Gender", value: "gender", width: 60, isSimpleMode: true},
  58. {text: "Status", value: "studentStatus", width: 80},
  59. {text: "Grade", value: "level", width: 70},
  60. {text: "Admission — Referrer", value: "recommendBy", width: 100, isDetailMode: true},
  61. {text: "Admission — Owner", value: "followUpByUserName", width: 100, isDetailMode: true},
  62. {text: "Follow-up Stage", value: "followUpStage", width: 80},
  63. {text: "Registered At", value: "createAt", width: 150},
  64. {text: "Operator", value: "operationByUser", width: 90},
  65. {text: "Operation Time", value: "operationAt", width: 150},
  66. {text: 'Actions', value: 'action', align: 'center', sortable: false, width: 'window.innerWidth < 500 ? 90: 180', class: 'fixed', cellClass: 'fixed'},
  67. ],
  68. rowActionList: [
  69. { text: "Edit", icon: 'mdi-note-edit-outline', color: 'success', click: 'doUiAction("startUpdateItem", item)' }
  70. ],
  71. },
  72. {
  73. tag: 'jh-action',
  74. attrs: { class: 'h-16 w-16 p-2 fixed right-4 bottom-32' },
  75. actionList: [
  76. { tag: 'v-btn', value: 'Create', icon: 'mdi-plus', color: 'success', click: "doUiAction('startCreateItem')" },
  77. { tag: 'v-btn', value: 'Randomly Assign Owner', icon: 'mdi-plus', color: 'success', click: "doUiAction('randomFollowUp')", ':disabled': '!tableSelected.length' },
  78. ]
  79. }
  80. ],
  81. actionContent: [
  82. {
  83. tag: 'jh-create-drawer',
  84. key: "create",
  85. attrs: {},
  86. title: 'Create',
  87. headSlot: [
  88. { tag: 'v-spacer'}
  89. ],
  90. contentList: [
  91. {
  92. label: "Create",
  93. type: "form",
  94. formItemList: [
  95. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  96. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  97. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  98. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  99. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  100. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  101. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  102. ],
  103. action: [{
  104. tag: "v-btn",
  105. value: "Create",
  106. attrs: {
  107. color: "success",
  108. ':small': true,
  109. '@click': "doUiAction('createItem')"
  110. }
  111. }],
  112. },
  113. ]
  114. },
  115. {
  116. tag: 'jh-update-drawer',
  117. key: "update",
  118. attrs: {},
  119. title: 'Edit',
  120. headSlot: [
  121. { tag: 'v-spacer'}
  122. ],
  123. contentList: [
  124. {
  125. label: "Edit",
  126. type: "form",
  127. formItemList: [
  128. { label: "id", model: "id", tag: "v-text-field", rules: "validationRules.requireRules" },
  129. { label: "Class ID", model: "classId", tag: "v-text-field", rules: "validationRules.requireRules" },
  130. { label: "className", model: "className", tag: "v-text-field", rules: "validationRules.requireRules" },
  131. { label: "Operation", model: "operation", tag: "v-text-field", rules: "validationRules.requireRules" },
  132. { label: "Operator userId", model: "operationByUserId", tag: "v-text-field", rules: "validationRules.requireRules" },
  133. { label: "Operator username", model: "operationByUser", tag: "v-text-field", rules: "validationRules.requireRules" },
  134. { label: "Operation time", model: "operationAt", tag: "v-text-field", rules: "validationRules.requireRules" },
  135. ],
  136. action: [{
  137. tag: "v-btn",
  138. value: "Edit",
  139. attrs: {
  140. color: "success",
  141. ':small': true,
  142. '@click': "doUiAction('updateItem')"
  143. }
  144. }],
  145. },
  146. { label: "Operation history", type: "component", componentPath: "recordHistory", attrs: { table: 'class', pageId: 'classManagement', ':id': 'updateItem.id' } },
  147. ]
  148. },
  149. {
  150. tag: 'jh-detail-drawer',
  151. key: "detail",
  152. attrs: {},
  153. title: "Details",
  154. headSlot: [],
  155. card: {
  156. tag: 'div',
  157. colAttrs: { class: 'pa-0' },
  158. value: `
  159. <div class="p-3 border-b" >
  160. <div class="flex justify-between mb-2 items-center">
  161. <div>
  162. <span class="font-weight-bold text-base">{{ detailItem.name }}</span>
  163. </div>
  164. <span :class="'text-sm '">{{ detailItem.followUpStatus }}</span>
  165. </div>
  166. <div class="text-gray-500 flex justify-between">Intended Division: {{ detailItem.segment }}</div>
  167. <div class="text-gray-500 flex justify-between">Registration Date: {{ detailItem.createAt }}</div>
  168. </div>
  169. <div class="pa-0 h-2 bg-gray-100" ></div>
  170. `
  171. },
  172. contentList: [
  173. {
  174. type: 'preview',
  175. md: 12,
  176. formItemList: [
  177. { label: "Gender", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.gender }}" },
  178. { label: "ID Number", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.icNumber }}" },
  179. { label: "Home Address", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.residentialAddress }}" },
  180. { label: "Intended Division", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.segment }}" },
  181. { label: "Intended Grade", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.level }}" },
  182. { label: "Intended Mode", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.afterSchoolCareType }}" },
  183. { label: "Previous School", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.schoolRoll }}" },
  184. { label: "Exam Score", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.middleSchoolExamScore }}" },
  185. { label: "Exam Registration No.", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.middleSchoolExamSequence }}" }
  186. ],
  187. action: [
  188. {
  189. tag: 'v-btn',
  190. value: "Edit",
  191. attrs: {
  192. color: 'primary',
  193. '@click': "doUiAction('startUpdateItem', detailItem); closeDetailDrawer()"
  194. },
  195. quickAttrs: ['small']
  196. }
  197. ]
  198. }
  199. ]
  200. },
  201. ],
  202. includeList: [], // { type: < js | css | html | vueComponent >, path: '' }
  203. common: {
  204. data: {
  205. constantObj: {},
  206. validationRules: {
  207. requireRules: [
  208. v => !!v || 'Required',
  209. ],
  210. },
  211. filterMap: {},
  212. },
  213. dataExpression: {
  214. isMobile: 'window.innerWidth < 500'
  215. },
  216. watch: {},
  217. computed: {
  218. tableDataComputed() {
  219. if(this.filterMap) {
  220. return this.tableData.filter(row => {
  221. for (const key in this.filterMap) {
  222. if (this.filterMap[key] && row[key] !== this.filterMap[key]) {
  223. return false;
  224. }
  225. }
  226. return true;
  227. });
  228. } else {
  229. return this.tableData;
  230. }
  231. },
  232. },
  233. doUiAction: {},
  234. async created() {
  235. await this.doUiAction('getTableData');
  236. },
  237. methods: {}
  238. },
  239. };
  240. module.exports = content;

Title — Title Only

  1. headContent: [
  2. { tag: 'jh-page-title', value: "Class Management" },
  3. ],

Title — Sort & Filter & Mode Switch

  1. headContent: [
  2. { tag: 'jh-page-title', value: "Class Management" },
  3. {
  4. tag: 'jh-order',
  5. data: {
  6. tableDataOrder: [ { column: "createAt", order: "desc" } ],
  7. tableDataOrderList: [
  8. { text: "Registration Time ↓", value: [ { column: "createAt", order: "desc" } ] },
  9. { text: "Exam Score ↓", value: [ { column: "middleSchoolExamScore", order: "desc" } ] },
  10. { text: "Updated At ↓", value: [ { column: "operationAt", order: "desc" } ] },
  11. ],
  12. }
  13. }, // Fixed variables: model 'tableDataOrder', items 'tableDataOrderList'
  14. {
  15. tag: 'jh-search',
  16. searchList: [
  17. { tag: 'v-select', model: "serverSearchWhere.semester", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Semester:', color: 'success', ':items': 'constantObj.semester' } },
  18. { tag: 'v-select', model: "serverSearchWhere.segment", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Division:', color: 'success', ':items': 'constantObj.segment' } },
  19. { tag: 'v-text-field', model: "serverSearchWhereLike.name", colAttrs: { class: 'pb-0' }, attrs: { label: 'Student Name:', color: 'success' }, quickAttrs: ['clearable'] },
  20. ],
  21. data: {
  22. serverSearchWhereLike: { name: '' },
  23. serverSearchWhere: { semester: '', segment: 'Primary' },
  24. }
  25. },
  26. { tag: 'v-spacer'},
  27. { tag: 'jh-mode', data: { viewMode: 'simple' } },
  28. ],
  29. common: {
  30. data: {
  31. constantObj: {
  32. },
  33. }
  34. }

Content — Data List

  1. pageContent: [
  2. {
  3. tag: 'jh-list',
  4. props: {
  5. limit: 10, // Server-side pagination size
  6. },
  7. attrs: { cols: 12, class: 'p-0 pb-7', ':style': '`height: calc(100vh - 140px); overflow-y: auto;overscroll-behavior: contain`' },
  8. headers: [
  9. {text: "Student ID", value: "studentId", width: 80, isSimpleMode: true},
  10. {text: "Name", value: "name", width: 90, isTitle: true, slot: [`
  11. <div v-if="item.isMonitor" class="ml-1">
  12. <v-icon color="warning" small>mdi-shield-star</v-icon>
  13. <span>Prefect</span>
  14. </div>
  15. `]},
  16. {text: "Gender", value: "gender", width: 60, isSimpleMode: true},
  17. {text: "Status", value: "studentStatus", width: 80},
  18. {text: "Grade", value: "level", width: 70},
  19. {text: "Admission — Referrer", value: "recommendBy", width: 100, isDetailMode: true},
  20. {text: "Admission — Owner", value: "followUpByUserName", width: 100, isDetailMode: true},
  21. {text: "Follow-up Stage", value: "followUpStage", width: 80},
  22. {text: "Registered At", value: "createAt", width: 150},
  23. {text: "Operator", value: "operationByUser", width: 90},
  24. {text: "Operation Time", value: "operationAt", width: 150},
  25. {text: 'Actions', value: 'action'},
  26. ],
  27. rowActionList: [
  28. ]
  29. }
  30. ],
  31. common: {
  32. async created() {
  33. await this.doUiAction('getTableData');
  34. },
  35. doUiAction: {
  36. startDetailItem: ['startDetailItem']
  37. },
  38. methods: {
  39. }
  40. }

jh-list

Property Type Required Description
props.limit string No Page size
props.rightArrowText string No Text on right arrow
headers array Field definitions
headers.isSimpleMode boolean No Show in simple mode only; default false
headers.isTitle boolean No Treat as row title
rowActionList array No Row actions in detail mode

Content — Data List (Detail Drawer)

  1. pageContent: [
  2. {
  3. tag: 'jh-list',
  4. props: {
  5. limit: 10,
  6. rightArrowText: '',
  7. },
  8. attrs: { cols: 12, class: 'p-0 pb-7', ':style': '`height: calc(100vh - 140px); overflow-y: auto;overscroll-behavior: contain`' },
  9. headers: [
  10. {text: "Student ID", value: "studentId", width: 80, isSimpleMode: true},
  11. {text: "Name", value: "name", width: 90, isTitle: true, slot: [`
  12. <div v-if="item.isMonitor" class="ml-1">
  13. <v-icon color="warning" small>mdi-shield-star</v-icon>
  14. <span>Prefect</span>
  15. </div>
  16. `]},
  17. {text: "Gender", value: "gender", width: 60, isSimpleMode: true},
  18. {text: "Status", value: "studentStatus", width: 80},
  19. {text: "Follow-up Stage", value: "followUpStage", width: 80},
  20. {text: "Registered At", value: "createAt", width: 150},
  21. {text: 'Actions', value: 'action', align: 'center', sortable: false, width: 'window.innerWidth < 500 ? 90: 180', class: 'fixed', cellClass: 'fixed'},
  22. ],
  23. rowActionList: [
  24. { text: "Edit", icon: 'mdi-note-edit-outline', color: 'success', click: 'doUiAction("startUpdateItem", item)' }
  25. ],
  26. }
  27. ],
  28. actionContent: [
  29. {
  30. tag: 'jh-detail-drawer',
  31. key: "detail",
  32. attrs: {},
  33. title: "Details",
  34. headSlot: [],
  35. card: {
  36. tag: 'div',
  37. colAttrs: { class: 'pa-0' },
  38. value: `
  39. <div class="p-3 border-b" >
  40. <div class="flex justify-between mb-2 items-center">
  41. <div>
  42. <span class="font-weight-bold text-base">{{ detailItem.name }}</span>
  43. </div>
  44. <span :class="'text-sm '">{{ detailItem.followUpStatus }}</span>
  45. </div>
  46. <div class="text-gray-500 flex justify-between">Intended Division: {{ detailItem.segment }}</div>
  47. <div class="text-gray-500 flex justify-between">Registration Date: {{ detailItem.createAt }}</div>
  48. </div>
  49. <div class="pa-0 h-2 bg-gray-100" ></div>
  50. `
  51. },
  52. contentList: [
  53. {
  54. type: 'preview',
  55. md: 12,
  56. formItemList: [
  57. { label: "Gender", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.gender }}" },
  58. { label: "ID Number", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.icNumber }}" },
  59. { label: "Home Address", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.residentialAddress }}" },
  60. { label: "Intended Division", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.segment }}" },
  61. { label: "Intended Grade", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.level }}" },
  62. { label: "Intended Mode", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.afterSchoolCareType }}" },
  63. { label: "Previous School", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.schoolRoll }}" },
  64. { label: "Exam Score", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.middleSchoolExamScore }}" },
  65. { label: "Exam Registration No.", tag: "span", colAttrs: { class: 'border-b pb-2 flex justify-between' }, value: "{{ detailItem.middleSchoolExamSequence }}" }
  66. ],
  67. action: [
  68. {
  69. tag: 'v-btn',
  70. value: "Edit",
  71. attrs: {
  72. color: 'primary',
  73. '@click': "doUiAction('startUpdateItem', detailItem); closeDetailDrawer()"
  74. },
  75. }
  76. ]
  77. }
  78. ]
  79. }
  80. ],
  81. common: {
  82. async created() {
  83. await this.doUiAction('getTableData');
  84. },
  85. }

Content — Floating Action Menu

  1. pageContent: [
  2. {
  3. tag: 'jh-action',
  4. attrs: { class: 'h-16 w-16 p-2 fixed right-4 bottom-32' },
  5. actionList: [
  6. { tag: 'v-btn', value: 'Create', icon: 'mdi-plus', color: 'success', click: "doUiAction('startCreateItem')" },
  7. { tag: 'v-btn', value: 'Randomly Assign Owner', icon: 'mdi-plus', color: 'success', click: "doUiAction('randomFollowUp')", ':disabled': '!tableSelected.length' },
  8. ]
  9. }
  10. ]

👉 Refer to the desktop (PC) drawer configuration 👈

Command Reference

Generate Example Files & Pages

By default, generates the example_class and example_student tables, configuration files, and pages for easy reference:

  1. jianghu-init json --generateType=example

Generate Page Config File

Generate a page config file from a database table:

  1. jianghu-init json --generateType=json --pageType=page --table=class --pageId=classManagement

Render HTML from Config

Generate a page from the config file:

  1. jianghu-init json --generateType=page --pageType=page --file=classManagement

Enable Dev Mode

Enable dev mode so changes to the config file automatically update the page:

  1. jianghu-init json dev

Parameters

Parameter Description
generateType Generation type: example (generate example files), json (generate config from table), page (generate page from config)
pageType Page type: page, component
table Table name (used when generateType is json)
dev Enable development mode; watches JSON files in the current project and auto-updates the HTML files

Miscellaneous

Syntax Highlighting for Code Inside Strings

  1. In VSCode, install the following extension to enable syntax highlighting for code inside strings and improve readability:

After installing, specify the language before the template string to enable highlighting.

  1. pageContent: [
  2. /*html*/`<v-btn @click="openTestMultiTabDrawer">Open test multi-tab drawer</v-btn>`,
  3. ],
  4. style: /*css*/`
  5. .jh-v-input {
  6. width: 100%;
  7. }
  8. `
  1. You can tweak VSCode settings to better support HTML suggestions in template strings. In Settings, search for “editor.quickSuggestions” and make sure suggestions are enabled for strings:
  1. "editor.quickSuggestions": {
  2. "strings": true
  3. }

Upgrade Guide for version v2

  1. Add server-side search

    jh-page

    1. {
    2. tag: 'jh-table',
    3. props: {
    4. serverPagination: true, // requires version: v2
    5. },
    6. attrs: {},
    7. }
  2. For doUiAction.getTableData, avoid replacing the entire method chain. If customization is needed, override selectively:

    1. common.doUiAction: {
    2. getTableData: [
    3. 'prepareTableParamsDefault', // Combine search conditions into this.tableParams
    4. 'prepareTableParams', // Custom condition method
    5. 'getTableData', // Request
    6. 'formatTableData' // Format tableData
    7. ]
    8. }
  3. formatTableData method

    1. // Original
    2. formatTableData(rows) {
    3. rows.forEach(row => {
    4. row.operationAt = row.operationAt ? dayjs(row.operationAt).format('YYYY-MM-DD HH:mm:ss') : '';
    5. });
    6. return rows;
    7. }
    8. // v2
    9. formatTableData() {
    10. let tableData = this.tableDataFromBackend.map(row => {
    11. row.operationAt = row.operationAt ? dayjs(row.operationAt).format('YYYY-MM-DD HH:mm:ss') : '';
    12. return row;
    13. });
    14. this.tableData = tableData;
    15. }

Upgrade Guide for version v3

  • Refactor init-json jh components: move variables previously placed in common.data into the tag’s own data where appropriate to simplify component state management.
  1. jh-order (mobile sorting)

    1. {
    2. tag: 'jh-order',
    3. data: {
    4. tableDataOrder: [ { column: "createAt", order: "desc" } ],
    5. tableDataOrderList: [
    6. { text: "Registration Time ↓", value: [ { column: "createAt", order: "desc" } ] },
    7. { text: "Exam Score ↓", value: [ { column: "middleSchoolExamScore", order: "desc" } ] },
    8. { text: "Updated At ↓", value: [ { column: "operationAt", order: "desc" } ] },
    9. ],
    10. }
    11. },
  2. jh-search (server-side search)

    1. {
    2. tag: 'jh-search',
    3. searchList: [
    4. { tag: 'v-select', model: "serverSearchWhere.semester", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Semester:', color: 'success', ':items': 'constantObj.semester' } },
    5. { tag: 'v-select', model: "serverSearchWhere.segment", colAttrs: { class: 'pb-0' }, attrs: { prefix: 'Division:', color: 'success', ':items': 'constantObj.segment' } },
    6. { tag: 'v-text-field', model: "serverSearchWhereLike.name", colAttrs: { class: 'pb-0' }, attrs: { label: 'Student Name:', color: 'success' }, quickAttrs: ['clearable'] },
    7. ],
    8. data: {
    9. serverSearchWhereLike: { name: '' },
    10. serverSearchWhere: { semester: '', segment: 'Primary' },
    11. }
    12. },
  3. jh-mode (mode switching)

    1. { tag: 'jh-mode', data: { viewMode: 'simple' } },
  4. jh-scene (scene search)

    1. {
    2. tag: 'jh-scene',
    3. attrs: { ':showActionBtn': false, ':mobile': false },
    4. // New in v3
    5. data: {
    6. sceneCreateForm: {},
    7. serverSceneSearchWhere: {},
    8. serverSceneSearchWhereIn: {},
    9. serverSceneSearchWhereLike: {},
    10. serverSceneSearchWhereOptions: [],
    11. serverSceneSearchWhereOrOptions: [],
    12. currentSceneId: 'Public',
    13. defaultSceneList: [
    14. { name: "All", where: {}, whereIn: { "articlePublishStatus": ["public", "draft"] } },
    15. { name: "Public", where: { "articlePublishStatus": "public"}, whereIn: {} },
    16. { name: "Draft", where: { "articlePublishStatus": "draft"}, whereIn: {} },
    17. { name: "Recycle Bin", where: { "articlePublishStatus": "deleted"}, whereIn: {} },
    18. ],
    19. maxSceneDisplay: 5,
    20. }
    21. },