vue2-client 1.19.78 → 1.19.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,10 +6,25 @@
6
6
 
7
7
  ## 一、运行方式
8
8
 
9
- ### 1. 按配置名执行:`LogicRunner.run(logicName, param)`
9
+ ### 1. 全局调用:`this.$runFrontLogic(logicName, param)`(推荐在配置脚本中使用)
10
+
11
+ 通过 Vue 插件注册,**任意组件**及**配置中的可执行脚本**(如报表的 `dataProcessScript`、按钮的 `customFunction`)内均可使用,无需 import。
12
+
13
+ - 返回 **Promise**,可 `.then()` / `.catch()` 处理结果或错误。
14
+ - 插件在 base-client 中注册,使用 `Vue.use(Plugins)` 的应用自动具备该能力。
15
+
16
+ ```javascript
17
+ // 例如在 dataProcessScript、customFunction 等配置脚本中(this 为当前组件)
18
+ var c = (data && data.patientComplaint != null) ? data.patientComplaint : ''
19
+ this.$runFrontLogic('aiDiagnosesCoverDataProcess', { patientComplaint: c, proxy: this }).catch(function (e) {
20
+ if (this.$message && this.$message.warn) this.$message.warn('执行失败,请稍后重试')
21
+ })
22
+ ```
23
+
24
+ ### 2. 按配置名执行:`LogicRunner.run(logicName, param)`
10
25
 
11
26
  - 从后端/配置中心按 `logicName` 拉取逻辑内容(与后端 Logic 同源的配置),在前端执行。
12
- - 执行时自动注入插件:`data`(即本次传入的 `param`)、`log`、`ENV`、`logic`、`dateTools`、`vueTools`。
27
+ - 执行时自动注入插件:`data`(即本次传入的 `param`)、`log`、`ENV`、`logic`、`dateTools`、`vueTools`、`http`。
13
28
  - 适合逻辑内容放在配置里、由界面传入「当前组件 / 响应式数据」的场景。
14
29
 
15
30
  ```javascript
@@ -22,7 +37,7 @@ const result = await LogicRunner.run('testVueLogic', {
22
37
  })
23
38
  ```
24
39
 
25
- ### 2. 直接执行表达式字符串:`LogicRunner.runExpression(source, params)`
40
+ ### 3. 直接执行表达式字符串:`LogicRunner.runExpression(source, params)`
26
41
 
27
42
  - 不拉取配置,直接执行一段表达式字符串。
28
43
  - `params` 即脚本内的入参(通常包含 `data` 等),无默认插件,需调用方自己构造。
@@ -50,6 +65,7 @@ const result = await LogicRunner.runExpression(expression, {
50
65
  | `logic` | LogicRunner 自身,可再调 `logic.run(其他逻辑名, 参数)`(会再拉配置执行)。 |
51
66
  | `dateTools` | 日期工具,与后端 dateTools 用法一致。 |
52
67
  | `vueTools` | 前端专用:`vueTools.getComponent(vueRef, componentName)`,等价于 `vueRef.$refs[componentName]`,用于按 ref 名取子组件。 |
68
+ | `http` | 请求插件(返回 Promise):`http.post(url, body)`、`http.get(url, params)`。返回值为响应体(response.data),执行器会自动等待 Promise 完成后再继续,可在 Logic 中直接写 `res = http.post('/api/xxx', { key: value })` 再使用 `res.xxx`。 |
53
69
 
54
70
  在**直接执行表达式**(`runExpression`)时,没有默认插件,完全由第二个参数 `params` 提供,例如把当前表单数据放进 `params.WF_FORM`,脚本里用 `data.WF_FORM` 访问(若 params 里 key 为 `data` 且值为该对象)。
55
71
 
@@ -62,8 +78,9 @@ const result = await LogicRunner.runExpression(expression, {
62
78
  - **条件**:`条件 : 结果 , 默认值`,无 if/else,用逗号接默认值。
63
79
  - **循环**:用数组的 `each`,例如 `data.xxx.each( row = row + 1 )`,其中 `row` 表示当前项。
64
80
  - **JSON**:支持 `{ key: value }`、`[ a, b ]`。
65
- - **字符串插值**:字符串内用 `"现在是{dateTools.getNow2()}"` 形式嵌入表达式。
81
+ - **字符串插值**:仅支持双引号字符串,字符串内用 `"现在是{dateTools.getNow2()}"` 形式嵌入表达式(不再支持 `$...$` 形式的字符串)。`$` 可作为标识符的一部分使用,与普通字母一致,例如 `data.$runFrontLogic(...)`。
66
82
  - 其他:`return`、`try/catch/finally`、`validate { ... }`、`assert`、lambda `=> { }` 等,与后端一致。
83
+ - **异步请求**:表达式语法不支持 `await`,需使用返回 Promise 的插件方法。`http.post(url, body)`、`http.get(url, params)` 返回 Promise,执行器会在调用处自动等待完成并把结果赋给左侧变量,例如:`res = http.post('/hai-api/suggest-by-complaint', { chief_complaint: data.c }), sug = res.suggestions, ...`。
67
84
 
68
85
  更完整的语法见 `src/expression/core/Program.js`(解析)和 `src/expression/TestExpression.js`(大量表达式示例)。
69
86
 
@@ -128,9 +145,10 @@ const result = await LogicRunner.runExpression(expression, {
128
145
 
129
146
  | 内容 | 路径 |
130
147
  |------|------|
148
+ | **前端 Logic Vue 插件** | `src/base-client/plugins/FrontLogicPlugin.js`(注册 `$runFrontLogic`) |
131
149
  | 运行器 | `src/logic/LogicRunner.js` |
132
150
  | 表达式解析与执行 | `src/expression/ExpressionRunner.js`、`src/expression/core/Program.js`、`src/expression/core/Expression.js` |
133
- | 前端插件 | `src/logic/plugins/index.js`、`src/logic/plugins/common/VueTools.js`、`src/logic/plugins/common/DateTools.js` |
151
+ | 前端插件 | `src/logic/plugins/index.js`、`src/logic/plugins/common/VueTools.js`、`src/logic/plugins/common/DateTools.js`、`src/logic/plugins/common/HttpTools.js` |
134
152
  | 页面示例 | `src/pages/LogicCallExample/index.vue` |
135
153
  | 工作流表达式示例 | `src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowHandle.vue`(`evaluateExpression`) |
136
154
  | 表达式语法用例 | `src/expression/TestExpression.js` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.19.78",
3
+ "version": "1.19.80",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -1135,6 +1135,28 @@ export default {
1135
1135
  */
1136
1136
  handleProgressUpdated (data, record, dataIndex) {
1137
1137
  this.$emit('progressUpdated', data, record, dataIndex)
1138
+ },
1139
+ async beforeExportSetHeaders() {
1140
+ const hasListener = !!this.$listeners?.beforeExportSetHeaders
1141
+
1142
+ if (!hasListener) return []
1143
+
1144
+ const result = await new Promise(resolve => {
1145
+ this.$emit('beforeExport', resolve)
1146
+ })
1147
+
1148
+ // 字符串 → 包装成数组
1149
+ if (typeof result === 'string') {
1150
+ return [result]
1151
+ }
1152
+
1153
+ // 数组 → 原样返回
1154
+ if (Array.isArray(result)) {
1155
+ return result
1156
+ }
1157
+
1158
+ // 其他情况 → 兜底为空数组
1159
+ return []
1138
1160
  }
1139
1161
  },
1140
1162
  action: {
@@ -3,15 +3,7 @@
3
3
  <x-form-table
4
4
  title="示例表单"
5
5
  :queryParamsName="queryParamsName"
6
- :fixedAddForm="fixedAddForm"
7
- :externalSelectedRowKeys="selectedKeys"
8
- @action="action"
9
- @selectRow="selectRow"
10
- @columnClick="columnClick"
11
- @ceshi="ceshi"
12
- @rowDblClick="rowDblClick"
13
- :defaultPageSize="5"
14
- serviceName="af-revenue"
6
+ serviceName="af-linepatrol"
15
7
  ref="xFormTable"
16
8
  ></x-form-table>
17
9
  </a-card>
@@ -28,7 +20,7 @@ export default {
28
20
  data() {
29
21
  return {
30
22
  // 查询配置文件名
31
- queryParamsName: 'address_management',
23
+ queryParamsName: 't_subhighpressure_pipelineCRUD',
32
24
  // 查询配置左侧tree
33
25
  xTreeConfigName: 'addressType',
34
26
  // 新增表单固定值
@@ -1,186 +1,186 @@
1
- // print.js
2
-
3
- export function printElement (elementToPrint) {
4
- // 创建一个新的浏览器窗口
5
- const printWindow = window.open('', '_blank', 'height=1024,width=768')
6
- // 设置新窗口的文档内容
7
- printWindow.document.write(`
8
- <html>
9
- <head>
10
- <title>Print</title>
11
- <style>
12
- @page {
13
- size: auto;
14
- margin: 0mm;
15
- }
16
- html, body {
17
- margin: 0;
18
- padding: 0;
19
- width: 100%;
20
- height: 100%;
21
- }
22
- #print-container {
23
- display: none
24
- }
25
- .img{
26
- width: 95%;
27
- height: 180px;
28
- object-fit: cover;
29
- }
30
- .reportMain {
31
- text-align: center;
32
- margin: 0 auto;
33
- font-size: 16px;
34
- color: #000;
35
- background-color: #fff;
36
- padding: 15px;
37
- border-radius: 8px;
38
-
39
- .reportTitle {
40
- font-weight: bold;
41
- }
42
-
43
- .subTitle {
44
- display: flex;
45
- justify-content: space-between;
46
- margin-bottom: 1%;
47
-
48
- .subTitleItems {
49
- max-width: 30%;
50
- }
51
- }
52
-
53
- .inputsDiv {
54
- display: flex;
55
- justify-content: space-between;
56
- .inputsDivItem {
57
- display: flex;
58
- align-items: center;
59
- padding: 0 4px;
60
- white-space: nowrap;
61
- .inputsDivItemLabel {
62
- padding: 0 4px;
63
- }
64
- }
65
- }
66
-
67
- .reportTable {
68
- width: 100%;
69
- border-collapse: collapse;
70
- table-layout:fixed;
71
- word-break:break-all;
72
- text-align: center;
73
- }
74
- }
75
- .reportMainForDisplay {
76
- text-align: center;
77
- margin: 10% auto;
78
- font-size: 16px;
79
- color: #000;
80
- background-color: #fff;
81
- padding: 15px;
82
- border-radius: 8px;
83
-
84
- .reportTitle {
85
- font-weight: bold;
86
- }
87
-
88
- .subTitle {
89
- display: flex;
90
- justify-content: space-between;
91
-
92
- .subTitleItems {
93
- max-width: 30%;
94
- }
95
- }
96
-
97
- .inputsDiv {
98
- display: flex;
99
- justify-content: space-around;
100
- .inputsDivItem {
101
- display: flex;
102
- align-items: center;
103
- padding: 0 4px;
104
- white-space: nowrap;
105
- .inputsDivItemLabel {
106
- padding: 0 4px;
107
- }
108
- }
109
- }
110
-
111
- .reportTable {
112
- width: 100%;
113
- border-collapse: collapse;
114
- table-layout:fixed;
115
- word-break:break-all;
116
- }
117
- }
118
- .reportMainNoPadding {
119
- text-align: center;
120
- margin: 0 auto;
121
- font-size: 16px;
122
- color: #000;
123
- background-color: #fff;
124
- border-radius: 8px;
125
-
126
- .reportTitle {
127
- font-weight: bold;
128
- }
129
-
130
- .subTitle {
131
- display: flex;
132
- justify-content: space-between;
133
-
134
- .subTitleItems {
135
- max-width: 30%;
136
- }
137
- }
138
-
139
- .inputsDiv {
140
- display: flex;
141
- justify-content: space-between;
142
- .inputsDivItem {
143
- display: flex;
144
- align-items: center;
145
- padding: 0 4px;
146
- white-space: nowrap;
147
- .inputsDivItemLabel {
148
- padding: 0 4px;
149
- }
150
- }
151
- }
152
-
153
- .reportTable {
154
- width: 100%;
155
- border-collapse: collapse;
156
- table-layout:fixed;
157
- word-break:break-all;
158
- }
159
- }
160
- .tools{
161
- position: fixed;
162
- right: 2%;
163
- text-align: right;
164
- width: 60%;
165
- cursor: pointer;
166
- .toolsItem{
167
- width: 15%;
168
- margin-right: 3%;
169
- display: inline-block;
170
- }
171
- }
172
- </style>
173
- </head>
174
- <body>
175
- <!-- 将需要打印的元素内容复制到新窗口中 -->
176
- ${elementToPrint.innerHTML}
177
- </body>
178
- </html>
179
- `)
180
- // 延迟执行打印,以确保新窗口的内容已加载完成
181
- printWindow.document.close() // 关闭文档流,确保内容完全加载
182
- setTimeout(() => {
183
- printWindow.print() // 调用打印方法
184
- printWindow.close()
185
- }, 500) // 延迟500毫秒后执行打印
186
- }
1
+ // print.js
2
+
3
+ export function printElement (elementToPrint) {
4
+ // 创建一个新的浏览器窗口
5
+ const printWindow = window.open('', '_blank', 'height=1024,width=768')
6
+ // 设置新窗口的文档内容
7
+ printWindow.document.write(`
8
+ <html>
9
+ <head>
10
+ <title>Print</title>
11
+ <style>
12
+ @page {
13
+ size: auto;
14
+ margin: 0mm;
15
+ }
16
+ html, body {
17
+ margin: 0;
18
+ padding: 0;
19
+ width: 100%;
20
+ height: 100%;
21
+ }
22
+ #print-container {
23
+ display: none
24
+ }
25
+ .img{
26
+ width: 95%;
27
+ height: 180px;
28
+ object-fit: cover;
29
+ }
30
+ .reportMain {
31
+ text-align: center;
32
+ margin: 0 auto;
33
+ font-size: 16px;
34
+ color: #000;
35
+ background-color: #fff;
36
+ padding: 15px;
37
+ border-radius: 8px;
38
+
39
+ .reportTitle {
40
+ font-weight: bold;
41
+ }
42
+
43
+ .subTitle {
44
+ display: flex;
45
+ justify-content: space-between;
46
+ margin-bottom: 1%;
47
+
48
+ .subTitleItems {
49
+ max-width: 30%;
50
+ }
51
+ }
52
+
53
+ .inputsDiv {
54
+ display: flex;
55
+ justify-content: space-between;
56
+ .inputsDivItem {
57
+ display: flex;
58
+ align-items: center;
59
+ padding: 0 4px;
60
+ white-space: nowrap;
61
+ .inputsDivItemLabel {
62
+ padding: 0 4px;
63
+ }
64
+ }
65
+ }
66
+
67
+ .reportTable {
68
+ width: 100%;
69
+ border-collapse: collapse;
70
+ table-layout:fixed;
71
+ word-break:break-all;
72
+ text-align: center;
73
+ }
74
+ }
75
+ .reportMainForDisplay {
76
+ text-align: center;
77
+ margin: 10% auto;
78
+ font-size: 16px;
79
+ color: #000;
80
+ background-color: #fff;
81
+ padding: 15px;
82
+ border-radius: 8px;
83
+
84
+ .reportTitle {
85
+ font-weight: bold;
86
+ }
87
+
88
+ .subTitle {
89
+ display: flex;
90
+ justify-content: space-between;
91
+
92
+ .subTitleItems {
93
+ max-width: 30%;
94
+ }
95
+ }
96
+
97
+ .inputsDiv {
98
+ display: flex;
99
+ justify-content: space-around;
100
+ .inputsDivItem {
101
+ display: flex;
102
+ align-items: center;
103
+ padding: 0 4px;
104
+ white-space: nowrap;
105
+ .inputsDivItemLabel {
106
+ padding: 0 4px;
107
+ }
108
+ }
109
+ }
110
+
111
+ .reportTable {
112
+ width: 100%;
113
+ border-collapse: collapse;
114
+ table-layout:fixed;
115
+ word-break:break-all;
116
+ }
117
+ }
118
+ .reportMainNoPadding {
119
+ text-align: center;
120
+ margin: 0 auto;
121
+ font-size: 16px;
122
+ color: #000;
123
+ background-color: #fff;
124
+ border-radius: 8px;
125
+
126
+ .reportTitle {
127
+ font-weight: bold;
128
+ }
129
+
130
+ .subTitle {
131
+ display: flex;
132
+ justify-content: space-between;
133
+
134
+ .subTitleItems {
135
+ max-width: 30%;
136
+ }
137
+ }
138
+
139
+ .inputsDiv {
140
+ display: flex;
141
+ justify-content: space-between;
142
+ .inputsDivItem {
143
+ display: flex;
144
+ align-items: center;
145
+ padding: 0 4px;
146
+ white-space: nowrap;
147
+ .inputsDivItemLabel {
148
+ padding: 0 4px;
149
+ }
150
+ }
151
+ }
152
+
153
+ .reportTable {
154
+ width: 100%;
155
+ border-collapse: collapse;
156
+ table-layout:fixed;
157
+ word-break:break-all;
158
+ }
159
+ }
160
+ .tools{
161
+ position: fixed;
162
+ right: 2%;
163
+ text-align: right;
164
+ width: 60%;
165
+ cursor: pointer;
166
+ .toolsItem{
167
+ width: 15%;
168
+ margin-right: 3%;
169
+ display: inline-block;
170
+ }
171
+ }
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <!-- 将需要打印的元素内容复制到新窗口中 -->
176
+ ${elementToPrint.innerHTML}
177
+ </body>
178
+ </html>
179
+ `)
180
+ // 延迟执行打印,以确保新窗口的内容已加载完成
181
+ printWindow.document.close() // 关闭文档流,确保内容完全加载
182
+ setTimeout(() => {
183
+ printWindow.print() // 调用打印方法
184
+ printWindow.close()
185
+ }, 500) // 延迟500毫秒后执行打印
186
+ }
@@ -27,7 +27,9 @@ export default {
27
27
  // 选中的查询条件字段名
28
28
  selectedQueryConditions: [],
29
29
  // 查询条件的 labelData 数据
30
- queryLabelData: null
30
+ queryLabelData: null,
31
+ // 自定义表头行数据
32
+ customHeaders: [],
31
33
  }
32
34
  },
33
35
  inject: ['tableContext', 'getFormLabelData'],
@@ -63,10 +65,11 @@ export default {
63
65
  },
64
66
  methods: {
65
67
  // 导出选中或本页数据
66
- handleExport (type, title) {
68
+ handleExport (type, title, customHeaders = []) {
67
69
  this.modalTitle = '选择导出列'
68
70
  this.exportFileName = title || '查询导出'
69
71
  this.type = 'export'
72
+ this.customHeaders = customHeaders
70
73
  this.handle(type)
71
74
  },
72
75
  // 打印选中或本页数据
@@ -74,6 +77,7 @@ export default {
74
77
  this.modalTitle = '选择打印数据列'
75
78
  this.type = 'print'
76
79
  this.printData = printData
80
+ this.customHeaders = []
77
81
  this.handle(type)
78
82
  },
79
83
  handle (type) {
@@ -88,7 +92,6 @@ export default {
88
92
  // 获取查询条件的 labelData,并默认全部选中
89
93
  if (this.getFormLabelData) {
90
94
  this.queryLabelData = this.getFormLabelData()
91
- console.log('>>>> this.queryLabelData: ', JSON.stringify(this.queryLabelData))
92
95
  // 默认全部选中所有查询条件字段(根据 labels 数组的索引)
93
96
  if (this.queryLabelData && this.queryLabelData.labels && this.queryLabelData.labels.length > 0) {
94
97
  this.selectedQueryConditions = this.queryLabelData.labels.map((_, index) => index)
@@ -225,6 +228,7 @@ export default {
225
228
  const fileNamePrefix = this.exportFileName || '查询导出'
226
229
  const name = `${fileNamePrefix}_${dateTime}`
227
230
  const queryData = this.buildDisplayText(this.queryLabelData?.labelData, this.queryLabelData?.labels, this.selectedQueryConditions)
231
+ this.customHeaders.push(queryData)
228
232
  const param = {
229
233
  queryParamsName: this.tableContext.queryParamsName,
230
234
  queryParams: this.tableContext.queryParams,
@@ -233,7 +237,7 @@ export default {
233
237
  form: conditionParams,
234
238
  userId: this.tableContext.currUser.id,
235
239
  fileName: name,
236
- queryData: queryData
240
+ headerData: this.customHeaders
237
241
  }
238
242
  exportData(param, this.tableContext.serviceName).then(res => {
239
243
  this.closeModal()
@@ -1397,8 +1397,13 @@ export default {
1397
1397
  const indexColumn = this.tableColumns.find(i => i?.slotType === 'index')
1398
1398
  return indexColumn ? indexColumn.dataIndex : this.tableColumns[0].dataIndex
1399
1399
  },
1400
- handleExport (type) {
1401
- this.$refs.exportExcel.handleExport(type, this.title)
1400
+ async handleExport(type) {
1401
+ const formTable = this.getSelf && this.getSelf()
1402
+ let customHeaders = []
1403
+ if (formTable && typeof formTable.beforeExportSetHeaders === 'function') {
1404
+ customHeaders = await formTable.beforeExportSetHeaders()
1405
+ }
1406
+ this.$refs.exportExcel.handleExport(type, this.title, customHeaders)
1402
1407
  },
1403
1408
  handlePrint (type) {
1404
1409
  const printData = {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 前端业务逻辑插件
3
+ * 将「运行前端 Logic 表达式」注册为 Vue 全局方法,可在任意组件或配置脚本中调用。
4
+ * 使用方式:
5
+ * - 在组件中:this.$runFrontLogic('logicName', { key: value })
6
+ * - 返回 Promise,可 .then() / .catch()
7
+ */
8
+ import LogicRunner from '@vue2-client/logic/LogicRunner'
9
+
10
+ const runFrontLogic = (logicName, param) => LogicRunner.run(logicName, param)
11
+
12
+ const FrontLogicPlugin = {
13
+ install (Vue) {
14
+ Vue.$runFrontLogic = runFrontLogic
15
+ Vue.prototype.$runFrontLogic = runFrontLogic
16
+ }
17
+ }
18
+
19
+ export default FrontLogicPlugin
@@ -4,6 +4,7 @@ import getConfig from '@vue2-client/base-client/plugins/Config'
4
4
  import moment from '@vue2-client/base-client/plugins/moment'
5
5
  import recording from '@vue2-client/base-client/plugins/Recording'
6
6
  import ComponentRegistryPlugin from '@vue2-client/base-client/plugins/ComponentRegistryPlugin'
7
+ import FrontLogicPlugin from '@vue2-client/base-client/plugins/FrontLogicPlugin'
7
8
 
8
9
  import VueI18nPlugin from './i18n-extend'
9
10
  import AuthorityPlugin from './authority-plugin'
@@ -20,6 +21,7 @@ const Plugins = {
20
21
  Vue.use(TabsPagePlugin)
21
22
  Vue.use(recording)
22
23
  Vue.use(ComponentRegistryPlugin)
24
+ Vue.use(FrontLogicPlugin)
23
25
  }
24
26
  }
25
27
  export default Plugins
@@ -16,7 +16,7 @@ export default class Program {
16
16
  */
17
17
  tokens = []
18
18
  /**
19
- * 字符串处理环节堆栈,$进入字符串处理环节,“{}”中间部分脱离字符串处理环节
19
+ * 字符串处理环节堆栈,双引号进入字符串处理环节,“{}”中间部分脱离字符串处理环节
20
20
  */
21
21
  inStrings = []
22
22
 
@@ -340,7 +340,7 @@ export default class Program {
340
340
  } else if (t.getType() === TokenType.Oper && t.getValue() === '[') {
341
341
  // JSON array
342
342
  return this.JsonArray()
343
- } else if (t.getType() === TokenType.Oper && (t.getValue() === '$' || t.getValue() === '"')) {
343
+ } else if (t.getType() === TokenType.Oper && t.getValue() === '"') {
344
344
  // String concatenation sequence
345
345
  return this.StringUnion()
346
346
  } else if (t.getType() === TokenType.Int || t.getType() === TokenType.Double || t.getType() === TokenType.Bool) {
@@ -559,7 +559,7 @@ export default class Program {
559
559
  let value
560
560
  if (name.getType() === TokenType.Identy) {
561
561
  value = name.getValue()
562
- } else if (name.getType() === TokenType.Oper && (name.getValue() === '$' || name.getValue() === '"')) {
562
+ } else if (name.getType() === TokenType.Oper && name.getValue() === '"') {
563
563
  const expression = this.StringUnion().children[1]
564
564
  value = expression.value
565
565
  } else {
@@ -772,10 +772,10 @@ export default class Program {
772
772
  // If in string state, process the string
773
773
  if (this.inString) {
774
774
  const startPos = this.pos
775
- while (hasKeyword && value !== '{' && value !== '}' && value !== '$' && value !== '"') {
775
+ while (hasKeyword && value !== '{' && value !== '}' && value !== '"') {
776
776
  if (value === '\\') {
777
777
  const nextStrValue = this.SOURCE.charAt(this.pos + 1)
778
- if (this.pos + 1 < this.SOURCE_LENGTH && ['{', '}', '"', '$', '\\'].includes(nextStrValue)) {
778
+ if (this.pos + 1 < this.SOURCE_LENGTH && ['{', '}', '"', '\\'].includes(nextStrValue)) {
779
779
  this.pos++
780
780
  } else {
781
781
  break
@@ -798,7 +798,7 @@ export default class Program {
798
798
  .replace(/\\"/g, '"')
799
799
  .replace('\\\\', '\\'), sPos)
800
800
 
801
- if (hasKeyword && (value === '$' || value === '"')) {
801
+ if (hasKeyword && value === '"') {
802
802
  this.pos++
803
803
  }
804
804
  return t
@@ -863,9 +863,9 @@ export default class Program {
863
863
  const str = this.SOURCE.substring(oldPos, this.pos)
864
864
  return new Token(TokenType.Int, parseInt(str, 10), sPos)
865
865
  }
866
- } else if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || value === '_' || /[\u4e00-\u9fa5]/.test(value)) {
866
+ } else if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value)) {
867
867
  const oldPos = this.pos
868
- while (hasKeyword && ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value === '_' || /[\u4e00-\u9fa5]/.test(value))) {
868
+ while (hasKeyword && ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value))) {
869
869
  this.pos++
870
870
  if (this.pos < this.SOURCE_LENGTH) {
871
871
  value = this.SOURCE.charAt(this.pos)
@@ -907,8 +907,8 @@ export default class Program {
907
907
  this.pos += 1
908
908
  }
909
909
  return new Token(TokenType.Oper, str, sPos)
910
- } else if ('()[],;.:@{}"$'.includes(value)) {
911
- if (value === '$' || value === '"') {
910
+ } else if ('()[],;.:@{}"'.includes(value)) {
911
+ if (value === '"') {
912
912
  this.inStrings.push(false)
913
913
  this.inString = true
914
914
  } else if ((value === '{' || value === '}') && this.inStrings.length) {
@@ -1,5 +1,7 @@
1
1
  // 自定义 Console 类
2
+ // 与 Vue 实例的 logger 对齐:支持按路径配置开关、输出前缀;业务逻辑中 log.xx 使用 Expression 序列化
2
3
  import Expression from '../core/Expression'
4
+ import { isLogEnabled } from '@vue2-client/utils/logger'
3
5
 
4
6
  export default class LogicConsole {
5
7
  constructor () {
@@ -16,6 +18,20 @@ export default class LogicConsole {
16
18
  return LogicConsole.instance
17
19
  }
18
20
 
21
+ /**
22
+ * 带路径与配置检查的输出(供 createLogger 使用)
23
+ * @param {string} level - 'log' | 'debug' | 'info' | 'warn' | 'error'
24
+ * @param {string} logPath - 日志路径,如 'logic.xxx'
25
+ * @param {*[]} args - 参数,会经 convert 序列化
26
+ */
27
+ _output (level, logPath, ...args) {
28
+ if (!isLogEnabled(logPath)) return
29
+ const prefix = `log[${logPath}]:`
30
+ const newArgs = this.convert(args)
31
+ const consoleMethod = console[level] || console.log
32
+ consoleMethod(prefix, ...newArgs)
33
+ }
34
+
19
35
  debug (...args) {
20
36
  const newArgs = this.convert(args)
21
37
  console.debug(...newArgs)
@@ -36,6 +52,31 @@ export default class LogicConsole {
36
52
  console.error(...newArgs)
37
53
  }
38
54
 
55
+ /** 与 Vue logger 一致:log 对应 console.log */
56
+ log (...args) {
57
+ const newArgs = this.convert(args)
58
+ console.log(...newArgs)
59
+ }
60
+
61
+ /**
62
+ * 创建带路径的 logger,与 Vue 实例的 logger 行为一致:
63
+ * 从 logger.config.js / config.js 读取配置按路径开关,输出带 log[path]: 前缀,参数经 Expression 序列化。
64
+ * 业务逻辑中应使用此方法传入 logicName,便于按 logic 控制日志。
65
+ * @param {string} logPath - 日志路径,如 'logic.aiDiagnosis' 或 'logic.xxx'
66
+ * @returns {{ log: Function, debug: Function, info: Function, warn: Function, error: Function }}
67
+ */
68
+ static createLogger (logPath) {
69
+ const path = typeof logPath === 'string' && logPath ? logPath : 'logic'
70
+ const instance = LogicConsole.getInstance()
71
+ return {
72
+ log: (...args) => instance._output('log', path, ...args),
73
+ debug: (...args) => instance._output('debug', path, ...args),
74
+ info: (...args) => instance._output('info', path, ...args),
75
+ warn: (...args) => instance._output('warn', path, ...args),
76
+ error: (...args) => instance._output('error', path, ...args),
77
+ }
78
+ }
79
+
39
80
  convert (args) {
40
81
  return args.map(obj => {
41
82
  return Expression.toJSONString(obj)
@@ -17,7 +17,7 @@ export default class Program {
17
17
  */
18
18
  private readonly tokens: Token[] = [];
19
19
  /**
20
- * 字符串处理环节堆栈,$进入字符串处理环节,“{}”中间部分脱离字符串处理环节
20
+ * 字符串处理环节堆栈,双引号进入字符串处理环节,“{}”中间部分脱离字符串处理环节
21
21
  */
22
22
  private readonly inStrings: boolean[] = [];
23
23
 
@@ -343,7 +343,7 @@ export default class Program {
343
343
  } else if (t.getType() === TokenType.Oper && t.getValue() === "[") {
344
344
  // JSON array
345
345
  return this.JsonArray();
346
- } else if (t.getType() === TokenType.Oper && (t.getValue() === "$" || t.getValue() === "\"")) {
346
+ } else if (t.getType() === TokenType.Oper && t.getValue() === "\"") {
347
347
  // String concatenation sequence
348
348
  return this.StringUnion();
349
349
  } else if (t.getType() === TokenType.Int || t.getType() === TokenType.Double || t.getType() === TokenType.Bool) {
@@ -562,7 +562,7 @@ export default class Program {
562
562
  let value: string;
563
563
  if (name.getType() === TokenType.Identy) {
564
564
  value = <string>name.getValue();
565
- } else if (name.getType() === TokenType.Oper && (name.getValue() === "$" || name.getValue() === "\"")) {
565
+ } else if (name.getType() === TokenType.Oper && name.getValue() === "\"") {
566
566
  let expression: Expression = this.StringUnion().children[1] as Expression;
567
567
  value = <string>expression.value;
568
568
  } else {
@@ -775,10 +775,10 @@ export default class Program {
775
775
  // If in string state, process the string
776
776
  if (this.inString) {
777
777
  let startPos = this.pos;
778
- while (hasKeyword && value !== '{' && value !== '}' && value !== '$' && value !== '\"') {
778
+ while (hasKeyword && value !== '{' && value !== '}' && value !== '\"') {
779
779
  if (value === '\\') {
780
780
  const nextStrValue = this.SOURCE.charAt(this.pos + 1);
781
- if (this.pos + 1 < this.SOURCE_LENGTH && ['{', '}', '\"', '$', '\\'].includes(nextStrValue)) {
781
+ if (this.pos + 1 < this.SOURCE_LENGTH && ['{', '}', '\"', '\\'].includes(nextStrValue)) {
782
782
  this.pos++;
783
783
  } else {
784
784
  break;
@@ -801,7 +801,7 @@ export default class Program {
801
801
  .replace(/\\"/g, '"')
802
802
  .replace("\\\\", "\\"), sPos);
803
803
 
804
- if (hasKeyword && (value === '$' || value === '\"')) {
804
+ if (hasKeyword && value === '\"') {
805
805
  this.pos++;
806
806
  }
807
807
  return t;
@@ -868,10 +868,10 @@ export default class Program {
868
868
  }
869
869
  }
870
870
 
871
- // Handle identifier tokens
872
- else if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || value === '_' || /[\u4e00-\u9fa5]/.test(value)) {
871
+ // Handle identifier tokens (including $ as in $runFrontLogic)
872
+ else if ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value)) {
873
873
  const oldPos = this.pos;
874
- while (hasKeyword && ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value === '_' || /[\u4e00-\u9fa5]/.test(value))) {
874
+ while (hasKeyword && ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || (value >= '0' && value <= '9') || value === '_' || value === '$' || /[\u4e00-\u9fa5]/.test(value))) {
875
875
  this.pos++;
876
876
  if (this.pos < this.SOURCE_LENGTH) {
877
877
  value = this.SOURCE.charAt(this.pos);
@@ -925,8 +925,8 @@ export default class Program {
925
925
  }
926
926
 
927
927
  // Single character operators like parentheses, commas, etc.
928
- else if ('()[],;.:@{}"$'.includes(value)) {
929
- if (value === '$' || value === '\"') {
928
+ else if ('()[],;.:@{}"'.includes(value)) {
929
+ if (value === '\"') {
930
930
  this.inStrings.push(false);
931
931
  this.inString = true;
932
932
  } else if ((value === '{' || value === '}') && this.inStrings.length) {
@@ -1,4 +1,12 @@
1
- // 自定义 Console
1
+ // 自定义 Console 类(与 Vue 实例 logger 对齐:支持按路径配置、前缀;业务逻辑中 log.xx 使用 Expression 序列化)
2
+ export interface ILogicLogger {
3
+ log: (...args: any[]) => void
4
+ debug: (...args: any[]) => void
5
+ info: (...args: any[]) => void
6
+ warn: (...args: any[]) => void
7
+ error: (...args: any[]) => void
8
+ }
9
+
2
10
  export default class LogicConsole {
3
11
 
4
12
  public debug(...args: any[]): void {
@@ -21,6 +29,15 @@ export default class LogicConsole {
21
29
  console.error(...newArgs);
22
30
  }
23
31
 
32
+ /**
33
+ * 创建带路径的 logger,与 Vue 实例的 logger 行为一致(配置开关、前缀)。
34
+ * 实际实现见 LogicConsole.js;此处仅为类型声明与兜底。
35
+ */
36
+ public static createLogger(logPath: string): ILogicLogger {
37
+ const noop = () => {}
38
+ return { log: noop, debug: noop, info: noop, warn: noop, error: noop }
39
+ }
40
+
24
41
  private convert(args: any[]): any[] {
25
42
  return args.map(arg => {
26
43
  if (typeof arg === 'object' && arg !== null && typeof arg.toString === 'function') {
@@ -34,11 +34,12 @@ export default class LogicRunner {
34
34
  throw new ServiceException('Logic资源' + logicName + '未找到', 400)
35
35
  }
36
36
  const paramStr = Expression.toJSONString(param)
37
- LogicRunner.logicConsoleInstance.info(`执行Logic[${logicName}],params: ${paramStr}`)
37
+ const logicLog = LogicConsole.createLogger('logic.' + logicName)
38
+ logicLog.info(`执行Logic[${logicName}],params: ${paramStr}`)
38
39
  // 附加用户注册的对象到业务逻辑中
39
40
  const plugins = {}
40
41
  plugins.data = param
41
- plugins.log = LogicRunner.logicConsoleInstance
42
+ plugins.log = logicLog
42
43
  plugins.ENV = result.$globalProp
43
44
  plugins.logic = this
44
45
  Object.assign(plugins, Plugins.default)
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 前端 Logic 用 HTTP 请求插件。
3
+ * 提供 post、get 方法,均返回 Promise,供表达式执行器在调用处自动等待。
4
+ * 使用项目统一的 request/restTools,走 axios 拦截器与认证。
5
+ */
6
+ import { post as postRequest, get as getRequest } from '@vue2-client/services/api/restTools'
7
+
8
+ export default class HttpTools {
9
+ static instance
10
+
11
+ constructor () {
12
+ if (HttpTools.instance) {
13
+ return HttpTools.instance
14
+ }
15
+ HttpTools.instance = this
16
+ }
17
+
18
+ static getInstance () {
19
+ if (!HttpTools.instance) {
20
+ HttpTools.instance = new HttpTools()
21
+ }
22
+ return HttpTools.instance
23
+ }
24
+
25
+ /**
26
+ * POST 请求,返回 Promise<响应体>(即 response.data)
27
+ * @param {string} url 请求地址,如 '/hai-api/suggest-by-complaint' 或 '/api/af-his/logic/xxx'
28
+ * @param {Object} body 请求体(JSON),会作为 request body 发送
29
+ * @param {Object} [config] 可选,axios 配置,如 { dedupe: false }
30
+ * @returns {Promise<*>} 解析为响应体(通常为 JSON 对象)
31
+ */
32
+ post (url, body, config) {
33
+ return postRequest(url, body || {}, config)
34
+ .then(res => res && res.data !== undefined ? res.data : res)
35
+ }
36
+
37
+ /**
38
+ * GET 请求,返回 Promise<响应体>(即 response.data)
39
+ * @param {string} url 请求地址
40
+ * @param {Object} [params] 查询参数
41
+ * @param {Object} [config] 可选,axios 配置
42
+ * @returns {Promise<*>} 解析为响应体
43
+ */
44
+ get (url, params, config) {
45
+ return getRequest(url, params, config)
46
+ .then(res => res && res.data !== undefined ? res.data : res)
47
+ }
48
+ }
@@ -1,7 +1,9 @@
1
1
  import DateTools from '@vue2-client/logic/plugins/common/DateTools'
2
2
  import VueTools from '@vue2-client/logic/plugins/common/VueTools'
3
+ import HttpTools from '@vue2-client/logic/plugins/common/HttpTools'
3
4
 
4
5
  export default {
5
6
  dateTools: DateTools.getInstance(),
6
- vueTools: VueTools.getInstance()
7
+ vueTools: VueTools.getInstance(),
8
+ http: HttpTools.getInstance()
7
9
  }
@@ -35,11 +35,12 @@ export default class LogicRunner {
35
35
  if (source == null) {
36
36
  throw new ServiceException("Logic资源" + logicName + "未找到", 400)
37
37
  }
38
- LogicRunner.logicConsoleInstance.info("执行Logic[{}],params: {}", logicName, param)
38
+ const logicLog = LogicConsole.createLogger('logic.' + logicName)
39
+ logicLog.info("执行Logic[{}],params: {}", logicName, param)
39
40
  // 附加用户注册的对象到业务逻辑中
40
41
  const plugins = new JSONObject();
41
42
  plugins.put("data", param);
42
- plugins.put("log", LogicRunner.logicConsoleInstance);
43
+ plugins.put("log", logicLog);
43
44
  plugins.put("logic", LogicRunner.prototype);
44
45
  return LogicRunner.runExpression(source, plugins);
45
46
  }
@@ -95,7 +95,7 @@ routerResource.XReportView = () => import('@vue2-client/pages/XReportView')
95
95
 
96
96
  routerResource.XReportGrid = () => import('@vue2-client/base-client/components/common/XReportGrid/XReportDemo')
97
97
 
98
- routerResource.XTab = () => import('@vue2-client/base-client/components/common/AfMap/demo.vue')
98
+ routerResource.XTab = () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue')
99
99
  routerResource.addressManage = () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue')
100
100
  // 大屏界面, 放行登录
101
101
  routerResource.bigDashboard = () => import('@vue2-client/pages/dashboard/index.vue')
@@ -98,6 +98,8 @@ function isLogEnabled(logPath) {
98
98
  const prefix = pattern.slice(0, -2)
99
99
  return logPath.startsWith(prefix + '.') || logPath === prefix
100
100
  }
101
+ // 全局通配
102
+ if (pattern === '*') return true
101
103
  return false
102
104
  })
103
105
  }
@@ -152,3 +154,4 @@ function logger(logPath) {
152
154
  }
153
155
 
154
156
  export default logger
157
+ export { isLogEnabled }
@@ -1,47 +1,47 @@
1
- import AMapLoader from '@amap/amap-jsapi-loader'
2
- let Amap
3
- async function GetGDMap (secretKey, key) {
4
- if (!Amap) {
5
- window._AMapSecurityConfig = {
6
- securityJsCode: secretKey
7
- }
8
- // 解决高德地图加载报错 ---> 禁止多种API加载方式混用
9
- AMapLoader.reset()
10
- Amap = await AMapLoader.load({
11
- key: key, // 申请好的Web端开发者Key,首次调用 load 时必填
12
- version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
13
- plugins: ['AMap.IndexCluster', 'AMP.MarkerCluster', 'AMap.InfoWindow', 'AMap.HeatMap', 'AMap.HawkEye', 'AMap.DistrictSearch',
14
- 'AMap.ToolBar', 'AMap.Geolocation', 'AMap.MouseTool',
15
- 'AMap.Geocoder', 'AMap.MarkerClusterer', 'AMap.AutoComplete', 'AMap.Scale'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
16
- AMapUI: {
17
- version: '1.1', // AMapUI 缺省 1.1
18
- plugins: ['misc/PositionPicker'] // 需要加载的 AMapUI ui插件
19
- }
20
- })
21
- }
22
- return Amap
23
- }
24
-
25
- async function getGDMap (address) {
26
- new (await GetGDMap()).Geocoder({
27
- radius: 500 // 范围,默认:500
28
- }).getLocation(address, function (status, result) {
29
- if (status === 'complete' && result.geocodes.length) {
30
- return ({ lng: result.geocodes[0].location.lng, lat: result.geocodes[0].location.lat })
31
- } else {
32
- // eslint-disable-next-line prefer-promise-reject-errors
33
- throw new Error('根据经纬度查询地址失败')
34
- }
35
- })
36
- }
37
-
38
- async function GetLocation (address) {
39
- return new Promise((resolve, reject) => {
40
- try {
41
- resolve(getGDMap(address))
42
- } catch (e) {
43
- reject(e)
44
- }
45
- })
46
- }
47
- export { GetGDMap, GetLocation }
1
+ import AMapLoader from '@amap/amap-jsapi-loader'
2
+ let Amap
3
+ async function GetGDMap (secretKey, key) {
4
+ if (!Amap) {
5
+ window._AMapSecurityConfig = {
6
+ securityJsCode: secretKey
7
+ }
8
+ // 解决高德地图加载报错 ---> 禁止多种API加载方式混用
9
+ AMapLoader.reset()
10
+ Amap = await AMapLoader.load({
11
+ key: key, // 申请好的Web端开发者Key,首次调用 load 时必填
12
+ version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
13
+ plugins: ['AMap.IndexCluster', 'AMP.MarkerCluster', 'AMap.InfoWindow', 'AMap.HeatMap', 'AMap.HawkEye', 'AMap.DistrictSearch',
14
+ 'AMap.ToolBar', 'AMap.Geolocation', 'AMap.MouseTool',
15
+ 'AMap.Geocoder', 'AMap.MarkerClusterer', 'AMap.AutoComplete', 'AMap.Scale'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
16
+ AMapUI: {
17
+ version: '1.1', // AMapUI 缺省 1.1
18
+ plugins: ['misc/PositionPicker'] // 需要加载的 AMapUI ui插件
19
+ }
20
+ })
21
+ }
22
+ return Amap
23
+ }
24
+
25
+ async function getGDMap (address) {
26
+ new (await GetGDMap()).Geocoder({
27
+ radius: 500 // 范围,默认:500
28
+ }).getLocation(address, function (status, result) {
29
+ if (status === 'complete' && result.geocodes.length) {
30
+ return ({ lng: result.geocodes[0].location.lng, lat: result.geocodes[0].location.lat })
31
+ } else {
32
+ // eslint-disable-next-line prefer-promise-reject-errors
33
+ throw new Error('根据经纬度查询地址失败')
34
+ }
35
+ })
36
+ }
37
+
38
+ async function GetLocation (address) {
39
+ return new Promise((resolve, reject) => {
40
+ try {
41
+ resolve(getGDMap(address))
42
+ } catch (e) {
43
+ reject(e)
44
+ }
45
+ })
46
+ }
47
+ export { GetGDMap, GetLocation }