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. },