mp-weixin-back 0.0.1
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.
- package/.editorconfig +25 -0
- package/.prettierrc.js +8 -0
- package/client.d.ts +19 -0
- package/dist/index.d.mts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +72376 -0
- package/dist/index.mjs +72366 -0
- package/package.json +49 -0
- package/readme.md +75 -0
- package/src/context.ts +54 -0
- package/src/index.ts +47 -0
- package/test/data/index.vue +13 -0
- package/test/generate.spec.ts +14 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +15 -0
- package/types/index.ts +35 -0
- package/utils/constant.ts +2 -0
- package/utils/index.ts +159 -0
- package/vite.config.ts +10 -0
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mp-weixin-back",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "监听微信小程序的手势返回和页面默认导航栏的返回",
|
|
5
|
+
"main": "./dist/index.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup",
|
|
8
|
+
"test": "vitest"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"微信小程序",
|
|
12
|
+
"手势返回",
|
|
13
|
+
"vite",
|
|
14
|
+
"uniapp"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./client": {
|
|
24
|
+
"types": "./client.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@babel/generator": "^7.26.2",
|
|
31
|
+
"@babel/parser": "^7.26.2",
|
|
32
|
+
"@babel/traverse": "^7.25.9",
|
|
33
|
+
"@types/babel__generator": "^7.6.8",
|
|
34
|
+
"@types/node": "^22.9.3",
|
|
35
|
+
"@vitejs/plugin-vue": "^5.2.0",
|
|
36
|
+
"@vue/compiler-sfc": "^3.2.45",
|
|
37
|
+
"@vue/test-utils": "^2.4.6",
|
|
38
|
+
"ast-kit": "^1.3.1",
|
|
39
|
+
"happy-dom": "^15.11.6",
|
|
40
|
+
"tsup": "^8.3.5",
|
|
41
|
+
"typescript": "^5.7.2",
|
|
42
|
+
"vite": "^5.4.11",
|
|
43
|
+
"vitest": "^2.1.5",
|
|
44
|
+
"vue": "^3.5.13"
|
|
45
|
+
},
|
|
46
|
+
"lint-staged": {
|
|
47
|
+
"*": "prettier --write"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
### 功能描述
|
|
2
|
+
|
|
3
|
+
监听手势返回和页面默认导航栏的返回事件
|
|
4
|
+
|
|
5
|
+
### 在项目中使用
|
|
6
|
+
|
|
7
|
+
#### 下载
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
npm install mp-weixin-back
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
#### 使用
|
|
14
|
+
|
|
15
|
+
`vite.config.ts` 中配置
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import mpBackPlugin from 'mp-weixin-back'
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
plugins: [mpBackPlugin()],
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
在 vue3 中使用
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import onPageBack from 'mp-weixin-back-helper'
|
|
29
|
+
|
|
30
|
+
onPageBack(() => {
|
|
31
|
+
console.log('触发了手势返回')
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
onPageBack 的类型定义为:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
type Config = {
|
|
39
|
+
/**
|
|
40
|
+
* 是否阻止默认的回退事件,默认为 false
|
|
41
|
+
*/
|
|
42
|
+
preventDefault: boolean
|
|
43
|
+
/**
|
|
44
|
+
* 阻止次数,默认是 `1`
|
|
45
|
+
*/
|
|
46
|
+
frequency: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function onPageBack(callback: () => void, params: Partial<Config>)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### 引入类型
|
|
53
|
+
|
|
54
|
+
在项目目录中的`src/env.d.ts` 或`src/shime-uni.d.ts` 文件中引入
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
/// <reference types="mp-weixin-back/client" />
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
或在 `tsconfig.json` 的 `compilerOptions` 下配置
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"compilerOptions": {
|
|
65
|
+
"types": ["mp-weixin-back/client"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### todolist
|
|
71
|
+
|
|
72
|
+
- [ ] 兼容 uniapp 的 Vue2 项目
|
|
73
|
+
- [ ] debug 模式
|
|
74
|
+
- [ ] 热更新 pages.json 文件
|
|
75
|
+
- [ ] 单元测试
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import { ContextConfig, PagesJson } from '../types'
|
|
4
|
+
import { transformVueFile } from '../utils'
|
|
5
|
+
|
|
6
|
+
export class pageContext {
|
|
7
|
+
config: ContextConfig
|
|
8
|
+
pages: string[] = []
|
|
9
|
+
|
|
10
|
+
constructor(config: ContextConfig) {
|
|
11
|
+
this.config = config
|
|
12
|
+
}
|
|
13
|
+
getPagesJsonPath() {
|
|
14
|
+
const pagesJsonPath = path.join(this.config.root, 'src/pages.json')
|
|
15
|
+
return pagesJsonPath
|
|
16
|
+
}
|
|
17
|
+
// 获取页面配置详情
|
|
18
|
+
async getPagesJsonInfo() {
|
|
19
|
+
const hasPagesJson = fs.existsSync(this.getPagesJsonPath())
|
|
20
|
+
if (!hasPagesJson) return
|
|
21
|
+
try {
|
|
22
|
+
const content = await fs.promises.readFile(this.getPagesJsonPath(), 'utf-8')
|
|
23
|
+
const pagesContent = JSON.parse(content) as PagesJson
|
|
24
|
+
const { pages, subpackages } = pagesContent
|
|
25
|
+
if (pages) {
|
|
26
|
+
const mainPages = pages.reduce((acc: string[], current) => {
|
|
27
|
+
acc.push(current.path)
|
|
28
|
+
return acc
|
|
29
|
+
}, [])
|
|
30
|
+
this.pages.push(...mainPages)
|
|
31
|
+
}
|
|
32
|
+
if (subpackages) {
|
|
33
|
+
const root = subpackages.root
|
|
34
|
+
const subPages = subpackages.pages.reduce((acc: string[], current) => {
|
|
35
|
+
acc.push(`${root}/${current.path}`.replace('//', '/'))
|
|
36
|
+
return acc
|
|
37
|
+
}, [])
|
|
38
|
+
this.pages.push(...subPages)
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error('请正确配置项目的pages.json文件')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// 获取指定id的page
|
|
45
|
+
getPageById(id: string) {
|
|
46
|
+
const path = (id.split('src/')[1] || '').replace('.vue', '')
|
|
47
|
+
// 页面级别
|
|
48
|
+
return this.pages.find((i) => i === path) || null
|
|
49
|
+
}
|
|
50
|
+
async transform(code: string, id: string) {
|
|
51
|
+
const result = await transformVueFile.call(this, code, id)
|
|
52
|
+
return result
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { pageContext } from './context'
|
|
2
|
+
import { virtualFileId } from '../utils/constant'
|
|
3
|
+
import type { Plugin } from 'vite'
|
|
4
|
+
import type { Config, UserOptions } from '../types'
|
|
5
|
+
|
|
6
|
+
function MpBackPlugin(userOptions: UserOptions = {}): Plugin {
|
|
7
|
+
let context: pageContext
|
|
8
|
+
|
|
9
|
+
const defaultOptions: Config = {
|
|
10
|
+
preventDefault: false,
|
|
11
|
+
frequency: 1,
|
|
12
|
+
}
|
|
13
|
+
const options = { ...defaultOptions, ...userOptions }
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
name: 'vite-plugin-mp-weixin-back',
|
|
17
|
+
enforce: 'pre',
|
|
18
|
+
configResolved(config) {
|
|
19
|
+
context = new pageContext({ ...options, mode: config.mode, root: config.root })
|
|
20
|
+
},
|
|
21
|
+
buildStart() {
|
|
22
|
+
context.getPagesJsonInfo()
|
|
23
|
+
},
|
|
24
|
+
resolveId(id) {
|
|
25
|
+
if (id === virtualFileId) {
|
|
26
|
+
return virtualFileId
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
load(id) {
|
|
30
|
+
if (id.includes('node_modules')) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
// 导出一个对象
|
|
34
|
+
if (id === virtualFileId) {
|
|
35
|
+
return `export default function onPageBack() {}`
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async transform(code, id) {
|
|
39
|
+
if (id.includes('node_modules') || !id.includes('.vue')) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
return context.transform(code, id)
|
|
43
|
+
},
|
|
44
|
+
} as Plugin
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default MpBackPlugin
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import Index from './data/index.vue'
|
|
5
|
+
|
|
6
|
+
describe('generate page-container components', () => {
|
|
7
|
+
it('page has template tag', async () => {
|
|
8
|
+
const wrapper = mount(Index)
|
|
9
|
+
await wrapper.vm.$nextTick()
|
|
10
|
+
|
|
11
|
+
const pageContainerRef = wrapper.find('page-container')
|
|
12
|
+
expect(pageContainerRef.exists()).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2018",
|
|
4
|
+
"lib": ["esnext"],
|
|
5
|
+
"types": ["node"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"strictNullChecks": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipDefaultLibCheck": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
}
|
|
15
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
format: ['cjs', 'esm'],
|
|
6
|
+
sourcemap: false, // 生成 sourcemap
|
|
7
|
+
dts: {
|
|
8
|
+
resolve: true
|
|
9
|
+
},
|
|
10
|
+
clean: true, // 构建前清理 dist 目录
|
|
11
|
+
minify: false, // 是否压缩代码
|
|
12
|
+
target: 'es6',
|
|
13
|
+
external: ['vite'] // 标记 Vite 为外部依赖,避免打包到插件中
|
|
14
|
+
})
|
|
15
|
+
|
package/types/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type UserOptions = Partial<Config>
|
|
2
|
+
|
|
3
|
+
export type Config = {
|
|
4
|
+
/**
|
|
5
|
+
* 是否阻止默认的回退事件,默认为 false
|
|
6
|
+
*/
|
|
7
|
+
preventDefault: boolean
|
|
8
|
+
/**
|
|
9
|
+
* 阻止次数,默认是 `1`
|
|
10
|
+
*/
|
|
11
|
+
frequency: number
|
|
12
|
+
/**
|
|
13
|
+
* 页面回退时触发
|
|
14
|
+
*/
|
|
15
|
+
onPageBack?: (params: BackParams) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type BackParams = {
|
|
19
|
+
/**
|
|
20
|
+
* 当前页面路径
|
|
21
|
+
*/
|
|
22
|
+
page: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ContextConfig = Config & {
|
|
26
|
+
mode: string
|
|
27
|
+
root: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type Pages = { path: string }[]
|
|
31
|
+
|
|
32
|
+
export type PagesJson = {
|
|
33
|
+
pages: Pages
|
|
34
|
+
subpackages: { root: string; pages: Pages }
|
|
35
|
+
}
|
package/utils/index.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import generate from '@babel/generator'
|
|
2
|
+
import { parse } from '@vue/compiler-sfc'
|
|
3
|
+
import { babelParse, walkAST } from 'ast-kit'
|
|
4
|
+
import { pageContext } from '../src/context'
|
|
5
|
+
import { virtualFileId } from './constant'
|
|
6
|
+
import type { SFCDescriptor } from '@vue/compiler-sfc'
|
|
7
|
+
import type { Node } from '@babel/types'
|
|
8
|
+
|
|
9
|
+
function isArrowFunction(func: Function) {
|
|
10
|
+
if (typeof func !== 'function') return false
|
|
11
|
+
return !func.hasOwnProperty('prototype') && func.toString().includes('=>')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function parseSFC(code: string): Promise<SFCDescriptor> {
|
|
15
|
+
try {
|
|
16
|
+
return parse(code).descriptor
|
|
17
|
+
} catch (error) {
|
|
18
|
+
throw new Error(`解析vue文件失败,请检查文件是否正确`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function transformVueFile(this: pageContext, code: string, id: string) {
|
|
23
|
+
// 检查代码中是否已经包含page-container组件
|
|
24
|
+
if (code.includes('<page-container')) {
|
|
25
|
+
return code
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 检查是否包含template标签
|
|
29
|
+
if (!code.includes('<template')) {
|
|
30
|
+
return code
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const componentStr =
|
|
34
|
+
'<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>'
|
|
35
|
+
|
|
36
|
+
const sfc = await parseSFC(code)
|
|
37
|
+
const setupCode = sfc.scriptSetup?.loc.source
|
|
38
|
+
const setupAst = babelParse(setupCode || '', sfc.scriptSetup?.lang)
|
|
39
|
+
let pageBackConfig = this.config
|
|
40
|
+
let hasPageBack = false,
|
|
41
|
+
hasImportRef = false,
|
|
42
|
+
pageBackFnName = 'onPageBack',
|
|
43
|
+
callbackCode = ``
|
|
44
|
+
|
|
45
|
+
if (setupAst) {
|
|
46
|
+
walkAST<Node>(setupAst, {
|
|
47
|
+
enter(node) {
|
|
48
|
+
if (node.type == 'ImportDeclaration' && node.source.value.includes(virtualFileId)) {
|
|
49
|
+
const importSpecifier = node.specifiers[0]
|
|
50
|
+
hasPageBack = true
|
|
51
|
+
pageBackFnName = importSpecifier.local.name
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (node.type == 'ImportDeclaration' && node.source.value === 'vue') {
|
|
55
|
+
const importSpecifiers = node.specifiers
|
|
56
|
+
for (let i = 0; i < importSpecifiers.length; i++) {
|
|
57
|
+
const element = importSpecifiers[i]
|
|
58
|
+
if (element.local.name == 'ref') {
|
|
59
|
+
hasImportRef = true
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
node.type == 'ExpressionStatement' &&
|
|
67
|
+
node.expression.type == 'CallExpression' &&
|
|
68
|
+
node.expression.callee.loc?.identifierName == pageBackFnName
|
|
69
|
+
) {
|
|
70
|
+
const callback = node.expression.arguments[0] // 获取第一个参数
|
|
71
|
+
const backArguments = node.expression.arguments[1]
|
|
72
|
+
// 第二个参数为object才有效,覆盖插件传入的配置
|
|
73
|
+
if (backArguments && backArguments.type == 'ObjectExpression') {
|
|
74
|
+
const config = new Function(`return (${generate(backArguments).code});`)()
|
|
75
|
+
pageBackConfig = { ...pageBackConfig, ...config }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
callback &&
|
|
80
|
+
(callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression')
|
|
81
|
+
) {
|
|
82
|
+
const body = callback.body
|
|
83
|
+
if (body.type === 'BlockStatement') {
|
|
84
|
+
// 遍历 BlockStatement 的内容
|
|
85
|
+
body.body.forEach((statement) => {
|
|
86
|
+
callbackCode += generate(statement).code // 将 AST 节点生成代码
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!hasPageBack) return
|
|
96
|
+
|
|
97
|
+
// 不阻止默认行为就返回到上一层
|
|
98
|
+
if (!pageBackConfig.preventDefault) {
|
|
99
|
+
callbackCode += `uni.navigateBack({ delta: 1 });`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 处理统一的返回方法
|
|
103
|
+
const configBack = (() => {
|
|
104
|
+
if (!pageBackConfig.onPageBack) return ''
|
|
105
|
+
if (typeof pageBackConfig.onPageBack !== 'function') {
|
|
106
|
+
throw new Error('`onPageBack` must be a function')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const params = JSON.stringify({
|
|
110
|
+
page: this.getPageById(id),
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const hasFunction = pageBackConfig.onPageBack.toString().includes('function')
|
|
114
|
+
|
|
115
|
+
if (isArrowFunction(pageBackConfig.onPageBack) || hasFunction) {
|
|
116
|
+
return `(${pageBackConfig.onPageBack})(${params});`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return `(function ${pageBackConfig.onPageBack})()`
|
|
120
|
+
})()
|
|
121
|
+
|
|
122
|
+
const beforeLeaveStr = `
|
|
123
|
+
${!hasImportRef ? "import { ref } from 'vue'" : ''}
|
|
124
|
+
let __MP_BACK_FREQUENCY__ = 1
|
|
125
|
+
const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(true);
|
|
126
|
+
const onBeforeLeave = () => {
|
|
127
|
+
console.log("__MP_BACK_FREQUENCY__", __MP_BACK_FREQUENCY__, ${pageBackConfig.frequency})
|
|
128
|
+
if (__MP_BACK_FREQUENCY__ < ${pageBackConfig.frequency}) {
|
|
129
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = true
|
|
132
|
+
}, 0);
|
|
133
|
+
__MP_BACK_FREQUENCY__++
|
|
134
|
+
}
|
|
135
|
+
// 运行配置的匿名函数
|
|
136
|
+
${configBack}
|
|
137
|
+
${callbackCode}
|
|
138
|
+
};
|
|
139
|
+
`
|
|
140
|
+
|
|
141
|
+
// 在template标签后插入page-container组件和script setup声明
|
|
142
|
+
const result = code.replace(
|
|
143
|
+
/(<template.*?>)([\s\S]*?)(<\/template>)([\s\S]*?)(<script\s+(?:lang="ts"\s+)?setup.*?>|$)/,
|
|
144
|
+
(match, templateStart, templateContent, templateEnd, middleContent, scriptSetup) => {
|
|
145
|
+
// 处理script setup标签
|
|
146
|
+
const hasScriptSetup = Boolean(scriptSetup)
|
|
147
|
+
const scriptStartTag = hasScriptSetup ? scriptSetup : '<script setup>'
|
|
148
|
+
const scriptEndTag = hasScriptSetup ? '' : '</script>'
|
|
149
|
+
|
|
150
|
+
// 构建注入的内容
|
|
151
|
+
const injectedTemplate = `${templateStart}${templateContent}\n ${componentStr}\n${templateEnd}`
|
|
152
|
+
const injectedScript = `\n${middleContent}${scriptStartTag}\n${beforeLeaveStr}\n${scriptEndTag}`
|
|
153
|
+
|
|
154
|
+
return injectedTemplate + injectedScript
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
}
|