@talknote/create-talknote-h5 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.
Files changed (43) hide show
  1. package/README.md +54 -0
  2. package/bin/create-talknote-h5.js +124 -0
  3. package/package.json +25 -0
  4. package/template/.editorconfig +16 -0
  5. package/template/.eslintignore +6 -0
  6. package/template/.eslintrc.js +6 -0
  7. package/template/.prettierignore +14 -0
  8. package/template/.prettierrc.js +5 -0
  9. package/template/.stylelintrc.js +3 -0
  10. package/template/README.md +153 -0
  11. package/template/_gitignore +42 -0
  12. package/template/_npmrc +1 -0
  13. package/template/config/config.ts +14 -0
  14. package/template/jest-setup.ts +1 -0
  15. package/template/jest.config.ts +15 -0
  16. package/template/logo.png +0 -0
  17. package/template/mock/app.ts +5 -0
  18. package/template/mock/user.ts +33 -0
  19. package/template/package.json +49 -0
  20. package/template/plugin.ts +10 -0
  21. package/template/src/app.tsx +76 -0
  22. package/template/src/assets/demoIcon/home.png +0 -0
  23. package/template/src/assets/demoIcon/home1.png +0 -0
  24. package/template/src/assets/demoIcon/list.png +0 -0
  25. package/template/src/assets/demoIcon/list1.png +0 -0
  26. package/template/src/assets/demoIcon/setting.png +0 -0
  27. package/template/src/assets/demoIcon/setting1.png +0 -0
  28. package/template/src/assets/yay.jpg +0 -0
  29. package/template/src/components/SearchBar/__tests__/__snapshots__/index.test.tsx.snap +143 -0
  30. package/template/src/components/SearchBar/__tests__/index.test.tsx +9 -0
  31. package/template/src/components/SearchBar/index.less +3 -0
  32. package/template/src/components/SearchBar/index.tsx +8 -0
  33. package/template/src/favicon.ico +0 -0
  34. package/template/src/global.css +9 -0
  35. package/template/src/models/index.ts +34 -0
  36. package/template/src/models/todo.ts +9 -0
  37. package/template/src/pages/index/index.css +20 -0
  38. package/template/src/pages/index/index.tsx +24 -0
  39. package/template/src/pages/list/index.less +4 -0
  40. package/template/src/pages/list/index.tsx +65 -0
  41. package/template/src/services/api.ts +5 -0
  42. package/template/tsconfig.json +3 -0
  43. package/template/typings.d.ts +1 -0
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # create-talknote-h5
2
+
3
+ 用于创建 Talknote Alita H5 项目的脚手架。
4
+
5
+ ## 使用方式
6
+
7
+ 发布到 npm 后执行:
8
+
9
+ ```bash
10
+ yarn create talknote-h5 my-project
11
+ ```
12
+
13
+ 其中 `my-project` 是必填的项目名称。项目名称只允许小写字母、数字、点号、下划线和连字符,并且必须以小写字母或数字开头。
14
+
15
+ 生成完成后执行:
16
+
17
+ ```bash
18
+ cd my-project
19
+ yarn
20
+ yarn start
21
+ ```
22
+
23
+ 脚手架不会自动安装依赖,也不会初始化 Git 仓库。
24
+
25
+ ## 本地验证
26
+
27
+ 在脚手架根目录执行:
28
+
29
+ ```bash
30
+ npm test
31
+ ```
32
+
33
+ 也可以在脚手架根目录直接执行命令入口:
34
+
35
+ ```bash
36
+ node bin/create-talknote-h5.js my-project
37
+ ```
38
+
39
+ ## 模板维护
40
+
41
+ 生成项目的全部内容位于 `template/`。修改模板时,不要把 `node_modules`、构建产物或 Git 数据放入该目录。
42
+
43
+ 模板中的 `_gitignore` 和 `_npmrc` 会在生成项目时分别恢复为 `.gitignore` 和 `.npmrc`。
44
+
45
+ ## 发布
46
+
47
+ 确认包名和版本后,由维护者自行登录 npm 并发布:
48
+
49
+ ```bash
50
+ npm login
51
+ npm publish
52
+ ```
53
+
54
+ 发布完成后即可使用 `yarn create talknote-h5 my-project`。
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const USAGE = 'Usage: yarn create talknote-h5 <project-name>';
8
+ const PROJECT_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
9
+ const SPECIAL_FILE_NAMES = {
10
+ _gitignore: '.gitignore',
11
+ _npmrc: '.npmrc',
12
+ };
13
+
14
+ function printUsageError(message) {
15
+ process.stderr.write(`Error: ${message}\n${USAGE}\n`);
16
+ return 1;
17
+ }
18
+
19
+ function removeTree(targetPath) {
20
+ if (!fs.existsSync(targetPath)) {
21
+ return;
22
+ }
23
+
24
+ const targetStat = fs.lstatSync(targetPath);
25
+ if (!targetStat.isDirectory() || targetStat.isSymbolicLink()) {
26
+ fs.unlinkSync(targetPath);
27
+ return;
28
+ }
29
+
30
+ for (const entryName of fs.readdirSync(targetPath)) {
31
+ removeTree(path.join(targetPath, entryName));
32
+ }
33
+ fs.rmdirSync(targetPath);
34
+ }
35
+
36
+ function copyTemplateEntries(sourceDirectory, targetDirectory) {
37
+ for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) {
38
+ const targetName = SPECIAL_FILE_NAMES[entry.name] || entry.name;
39
+ const sourcePath = path.join(sourceDirectory, entry.name);
40
+ const targetPath = path.join(targetDirectory, targetName);
41
+
42
+ if (entry.isDirectory()) {
43
+ fs.mkdirSync(targetPath);
44
+ copyTemplateEntries(sourcePath, targetPath);
45
+ continue;
46
+ }
47
+
48
+ if (entry.isSymbolicLink()) {
49
+ fs.symlinkSync(fs.readlinkSync(sourcePath), targetPath);
50
+ continue;
51
+ }
52
+
53
+ fs.copyFileSync(sourcePath, targetPath);
54
+ }
55
+ }
56
+
57
+ function updateGeneratedPackageName(targetDirectory, projectName) {
58
+ const packagePath = path.join(targetDirectory, 'package.json');
59
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
60
+ packageJson.name = projectName;
61
+ fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
62
+ }
63
+
64
+ function main(argumentsList) {
65
+ if (argumentsList.length !== 1) {
66
+ return printUsageError(
67
+ 'Project name is required and must be the only argument.',
68
+ );
69
+ }
70
+
71
+ const projectName = argumentsList[0];
72
+ if (!PROJECT_NAME_PATTERN.test(projectName)) {
73
+ return printUsageError(
74
+ `Invalid project name "${projectName}". Use lowercase letters, numbers, dots, underscores, and hyphens.`,
75
+ );
76
+ }
77
+
78
+ const targetDirectory = path.resolve(process.cwd(), projectName);
79
+ if (fs.existsSync(targetDirectory)) {
80
+ process.stderr.write(
81
+ `Error: Target directory already exists: ${targetDirectory}\n`,
82
+ );
83
+ return 1;
84
+ }
85
+
86
+ const templateDirectory = path.resolve(__dirname, '..', 'template');
87
+ let targetCreated = false;
88
+
89
+ try {
90
+ fs.mkdirSync(targetDirectory);
91
+ targetCreated = true;
92
+ copyTemplateEntries(templateDirectory, targetDirectory);
93
+ updateGeneratedPackageName(targetDirectory, projectName);
94
+ } catch (error) {
95
+ if (targetCreated) {
96
+ try {
97
+ removeTree(targetDirectory);
98
+ } catch (cleanupError) {
99
+ process.stderr.write(
100
+ `Error: Could not clean up ${targetDirectory}: ${cleanupError.message}\n`,
101
+ );
102
+ }
103
+ }
104
+
105
+ process.stderr.write(`Error: Could not create project: ${error.message}\n`);
106
+ return 1;
107
+ }
108
+
109
+ process.stdout.write(
110
+ `✓ Created ${projectName}\n\nNext steps:\n cd ${projectName}\n yarn\n yarn start\n`,
111
+ );
112
+ return 0;
113
+ }
114
+
115
+ if (require.main === module) {
116
+ process.exitCode = main(process.argv.slice(2));
117
+ }
118
+
119
+ module.exports = {
120
+ copyTemplateEntries,
121
+ main,
122
+ removeTree,
123
+ updateGeneratedPackageName,
124
+ };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@talknote/create-talknote-h5",
3
+ "publishConfig": {
4
+ "access": "public",
5
+ "registry": "https://registry.npmjs.org/"
6
+ },
7
+ "version": "0.0.1",
8
+ "description": "Create a Talknote Alita H5 project.",
9
+ "license": "MIT",
10
+ "private": false,
11
+ "bin": {
12
+ "create-talknote-h5": "bin/create-talknote-h5.js"
13
+ },
14
+ "files": [
15
+ "bin",
16
+ "template",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "test": "node tests/create-talknote-h5.test.js"
21
+ },
22
+ "engines": {
23
+ "node": ">=14.0.1"
24
+ }
25
+ }
@@ -0,0 +1,16 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ charset = utf-8
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
11
+
12
+ [*.md]
13
+ trim_trailing_whitespace = false
14
+
15
+ [Makefile]
16
+ indent_style = tab
@@ -0,0 +1,6 @@
1
+ /scripts
2
+ .history
3
+ public
4
+ dist
5
+ .umi
6
+ mock
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ extends: require.resolve('alita/eslint'),
3
+ rules: {
4
+ 'selector-class-pattern': 0,
5
+ },
6
+ };
@@ -0,0 +1,14 @@
1
+ node_modules
2
+ .umi
3
+ .umi-production
4
+ *.png
5
+ *.jpg
6
+ *.svg
7
+ .editorconfig
8
+ .env
9
+ .prettierignore
10
+ .gitkeep
11
+ .DS_Store
12
+ .npmrc
13
+ .eslintignore
14
+ .gitignore
@@ -0,0 +1,5 @@
1
+ const prettier = require('alita/prettier');
2
+
3
+ module.exports = {
4
+ ...prettier,
5
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ extends: require.resolve('alita/stylelint'),
3
+ };
@@ -0,0 +1,153 @@
1
+ <h1 align="center">Alita Pro</h1>
2
+
3
+ 帮助你快速使用 alita
4
+
5
+ ```
6
+ pnpm i
7
+ pnpm start
8
+ ```
9
+
10
+ ### 首页
11
+
12
+ ![image](https://user-images.githubusercontent.com/11746742/179654827-2ca22b45-0ec9-471e-8238-9bfa56d864a0.png)
13
+
14
+ ### 列表页
15
+
16
+ 列表页支持滚动加载更多,固定头部,状态保持
17
+
18
+ ![image](https://user-images.githubusercontent.com/11746742/179654907-585061f1-02cc-464e-ac75-c6ae12b2c666.png)
19
+
20
+
21
+ ### 关于尺寸标注
22
+
23
+ 100% 宽为 750px,可在蓝湖等工具中将标准改为 750px,这样可以直接使用标注的尺寸用于 caa class 中的尺寸,最终运行会根据手机适配,将 px 自动转为对应的 rem 值。
24
+
25
+ 值得注意的是,写在内联样式 style 中的 px 值不会被转化,需要手动写明 rem 单位,直接将 px 值除于 100 即可,如 750px,在 style 中应该写为 7.5rem
26
+
27
+ ### 关于路由
28
+
29
+ 采用约定式路由,只有 `src/pages/**/index.tsx` 会被识别成路由,可使用 npx alita g pages name 来新增页面。
30
+
31
+ > Q: 为什么仅 index.tsx 会被识别成路由?如果我想要嵌套的路由,如 hello/hero 我该怎么办?
32
+ > A: 仅 index.tsx 被识别成路由,会让整个文件结构变得很干净,在移动端上不需要嵌套路由,一般短位链接优势更大
33
+
34
+ ### 自带头部
35
+
36
+ 可在 `src/app.tsx#L24` 中配置
37
+
38
+ ```tsx
39
+ const titleList: TitleListItem[] = [
40
+ {
41
+ pagePath: '/',
42
+ title: '首页',
43
+ },
44
+ {
45
+ pagePath: '/list',
46
+ title: '列表',
47
+ },
48
+ ];
49
+ ```
50
+
51
+ 也可以在页面中独立配置
52
+
53
+ ```tsx
54
+ import { setPageNavBar, useLocation } from 'alita';
55
+ import { LeftOutline } from 'antd-mobile-icons';
56
+ import { useEffect } from 'react';
57
+
58
+ const location = useLocation();
59
+ useEffect(() => {
60
+ setPageNavBar({
61
+ pagePath: location.pathname,
62
+ navBar: {
63
+ mode: 'dark',
64
+ pageTitle: '首页动态标题',
65
+ pageBackground: '#fff',
66
+ back: '返回',
67
+ icon: <LeftOutline />,
68
+ leftContent: <div>首页</div>,
69
+ rightContent: <div>详情</div>,
70
+ onLeftClick: (navigate) => {
71
+ navigate(-1);
72
+ },
73
+ hideNavBar: false,
74
+ },
75
+ });
76
+ }, []);
77
+ ```
78
+
79
+ > 关闭默认 NavBar 上述 hideNavBar 配置改为 hideNavBar: true
80
+
81
+ ### 底部 Tabs
82
+
83
+ 可在 `src/app.tsx#L42` 中配置
84
+
85
+ ```tsx
86
+ const tabList: TabBarListItem[] = [
87
+ {
88
+ pagePath: '/',
89
+ text: '首页',
90
+ iconPath: HomeGary,
91
+ selectedIconPath: HomeBlue,
92
+ title: '首页',
93
+ iconSize: '',
94
+ badge: '',
95
+ },
96
+ {
97
+ pagePath: '/list',
98
+ text: '列表',
99
+ iconPath: ListGary,
100
+ selectedIconPath: ListBlue,
101
+ title: '列表',
102
+ iconSize: '',
103
+ badge: '',
104
+ },
105
+ ];
106
+ ```
107
+
108
+ 也可以在页面中独立配置
109
+
110
+ ```tsx
111
+ import { setTabBarList, useLocation } from 'alita';
112
+ import { useEffect } from 'react';
113
+
114
+ const location = useLocation();
115
+ useEffect(() => {
116
+ setTabBarList({
117
+ pagePath: location.pathname,
118
+ text: '首页',
119
+ iconSize: '18px',
120
+ badge: '12',
121
+ onPress: () => {
122
+ navigate(location.pathname, { replace: true });
123
+ return '';
124
+ },
125
+ remove: false,
126
+ });
127
+ }, []);
128
+ ```
129
+
130
+ > 关闭 app.ts 中已经配置好的 Tab ,上述 remove 配置改为 remove: true
131
+
132
+ ### 格式化
133
+
134
+ ```
135
+ pnpm format
136
+ ```
137
+
138
+ ### 构建
139
+
140
+ ```
141
+ pnpm build
142
+ ```
143
+
144
+ ### 调试工具
145
+
146
+ 在 `config/config.ts` 中增加配置
147
+
148
+ ```
149
+ aconsole: {
150
+ console: {},
151
+ inspx: {},
152
+ },
153
+ ```
@@ -0,0 +1,42 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ **/node_modules
5
+
6
+ # production
7
+ /dist
8
+ .mfsu
9
+ # misc
10
+ .DS_Store
11
+ npm-debug.log*
12
+ yarn-error.log
13
+
14
+ /coverage
15
+ .idea
16
+ yarn.lock
17
+ package-lock.json
18
+ pnpm-lock.yaml
19
+ *bak
20
+
21
+
22
+ # visual studio code
23
+ .history
24
+ *.log
25
+ functions/*
26
+ .temp/**
27
+
28
+ # umi
29
+ .umi
30
+ .umi-production
31
+ /.env.local
32
+ /.umirc.local.ts
33
+ /.umirc.local.js
34
+ /config/config.local.ts
35
+ /config/config.local.js
36
+
37
+ # screenshot
38
+ screenshot
39
+ .firebase
40
+ .eslintcache
41
+
42
+ build
@@ -0,0 +1 @@
1
+ registry=https://registry.npmjs.org/
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'alita';
2
+
3
+ export default defineConfig({
4
+ appType: 'h5',
5
+ keepalive: [/list/],
6
+ aconsole: {
7
+ console: {},
8
+ inspx: {},
9
+ },
10
+ mobileLayout: 'mobile5',
11
+ mfsu: {},
12
+ hash: false,
13
+ npmClient: ''
14
+ });
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,15 @@
1
+ import { Config, configUmiAlias, createConfig } from 'alita/test';
2
+
3
+ export default async () => {
4
+ return (await configUmiAlias({
5
+ ...createConfig({
6
+ target: 'browser',
7
+ }),
8
+ setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
9
+ // if you require some es-module npm package, please uncomment below line and insert your package name
10
+ // transformIgnorePatterns: ['node_modules/(?!.*(lodash-es|your-es-pkg-name)/)']
11
+ transformIgnorePatterns: [
12
+ 'node_modules/(?!.*(@umijs/renderer-react|umi/client/client/|@umijs/plugins/libs|antd-mobile/es/))',
13
+ ],
14
+ })) as Config.InitialOptions;
15
+ };
Binary file
@@ -0,0 +1,5 @@
1
+ export default {
2
+ 'POST /api/hello': {
3
+ text: 'Antd Mobile',
4
+ },
5
+ };
@@ -0,0 +1,33 @@
1
+ import { Request, Response } from 'express';
2
+
3
+ const waitTime = (time: number = 100) => {
4
+ return new Promise((resolve) => {
5
+ setTimeout(() => {
6
+ resolve(true);
7
+ }, time);
8
+ });
9
+ };
10
+
11
+ export default {
12
+ 'POST /api/login/account': async (req: Request, res: Response) => {
13
+ const { password, username } = req.body;
14
+ await waitTime(2000);
15
+ if (password === 'xiaohuoni' && username === 'antd.mobile') {
16
+ res.send({
17
+ message: 'ok',
18
+ success: true,
19
+ });
20
+ return;
21
+ }
22
+ res.send({
23
+ message: 'login error',
24
+ success: false,
25
+ });
26
+ },
27
+ 'POST /api/login/outLogin': (req: Request, res: Response) => {
28
+ res.send({ data: {}, success: true });
29
+ },
30
+ 'POST /api/register': (req: Request, res: Response) => {
31
+ res.send({ status: 'ok', currentAuthority: 'user', success: true });
32
+ },
33
+ };
@@ -0,0 +1,49 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "private": true,
4
+ "scripts": {
5
+ "build": "alita build",
6
+ "dev": "alita dev",
7
+ "format": "prettier --write .",
8
+ "lint-staged": "lint-staged",
9
+ "lint-staged:js": "eslint --ext .ts,.tsx",
10
+ "plugin": "alita plugin list",
11
+ "start": "alita dev",
12
+ "test": "jest"
13
+ },
14
+ "lint-staged": {
15
+ "**/*.less": "stylelint --syntax less",
16
+ "**/*.css": "stylelint --syntax css",
17
+ "**/*.{ts,tsx}": "npm run lint-staged:js",
18
+ "**/*.{ts,tsx,json,jsx,less}": [
19
+ "git add",
20
+ "prettier --write"
21
+ ]
22
+ },
23
+ "dependencies": {
24
+ "@alita/flow": "^3.1.1",
25
+ "alita": "^3.2.27",
26
+ "antd-mobile": "^5.15.1",
27
+ "react": "^18.2.0",
28
+ "react-dom": "^18.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@testing-library/jest-dom": "^5.16.4",
32
+ "@testing-library/react": "^13.4.0",
33
+ "@types/jest": "^27.5.2",
34
+ "@types/react": "^18.0.15",
35
+ "@types/react-dom": "^18.0.6",
36
+ "@types/testing-library__jest-dom": "^5.14.5",
37
+ "jest": "^27.5.1",
38
+ "prettier": "^2.8.8",
39
+ "ts-node": "^10.9.1",
40
+ "typescript": "^5.2.2"
41
+ },
42
+ "engines": {
43
+ "node": ">=14.0.1"
44
+ },
45
+ "gitHooks": {
46
+ "pre-commit": "lint-staged",
47
+ "commit-msg": "alita verify-commit"
48
+ }
49
+ }
@@ -0,0 +1,10 @@
1
+ import { IApi } from 'alita';
2
+
3
+ export default (api: IApi) => {
4
+ api.onDevCompileDone((opts) => {
5
+ // console.log('> onDevCompileDone', opts.isFirstCompile);
6
+ });
7
+ api.onBuildComplete((opts) => {
8
+ // console.log('> onBuildComplete', opts.isFirstCompile);
9
+ });
10
+ };
@@ -0,0 +1,76 @@
1
+ import HomeGary from '@/assets/demoIcon/home.png';
2
+ import HomeBlue from '@/assets/demoIcon/home1.png';
3
+ import ListGary from '@/assets/demoIcon/list.png';
4
+ import ListBlue from '@/assets/demoIcon/list1.png';
5
+ import type {
6
+ NavBarListItem,
7
+ NavBarProps,
8
+ ResponseError,
9
+ TabBarListItem,
10
+ TabBarProps,
11
+ TitleListItem,
12
+ } from 'alita';
13
+
14
+ export const request = {
15
+ prefix: '/api',
16
+ method: 'get',
17
+ errorHandler: (error: ResponseError) => {
18
+ // 集中处理错误
19
+ console.log(11111111);
20
+ console.log(error);
21
+ },
22
+ };
23
+
24
+ const titleList: TitleListItem[] = [
25
+ {
26
+ pagePath: '/',
27
+ title: '首页',
28
+ },
29
+ {
30
+ pagePath: '/list',
31
+ title: '列表',
32
+ },
33
+ ];
34
+ const navList: NavBarListItem[] = [];
35
+ const navBar: NavBarProps = {
36
+ navList,
37
+ fixed: false,
38
+ onLeftClick: () => {
39
+ // router.goBack();
40
+ },
41
+ };
42
+ const tabList: TabBarListItem[] = [
43
+ {
44
+ pagePath: '/',
45
+ text: '首页',
46
+ iconPath: HomeGary,
47
+ selectedIconPath: HomeBlue,
48
+ title: '首页',
49
+ iconSize: '',
50
+ badge: '',
51
+ },
52
+ {
53
+ pagePath: '/list',
54
+ text: '列表',
55
+ iconPath: ListGary,
56
+ selectedIconPath: ListBlue,
57
+ title: '列表',
58
+ iconSize: '',
59
+ badge: '',
60
+ },
61
+ ];
62
+
63
+ const tabBar: TabBarProps = {
64
+ color: `#999999`,
65
+ selectedColor: '#00A0FF',
66
+ borderStyle: 'white',
67
+ position: 'bottom',
68
+ list: tabList,
69
+ };
70
+
71
+ export const mobileLayout = {
72
+ documentTitle: '默认标题',
73
+ navBar,
74
+ tabBar,
75
+ titleList,
76
+ };
Binary file
@@ -0,0 +1,143 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[` 1`] = `
4
+ <div>
5
+ <div
6
+ class="search-bar-wrap"
7
+ >
8
+ <div
9
+ class="adm-search-bar"
10
+ >
11
+ <div
12
+ class="adm-search-bar-input-box"
13
+ >
14
+ <div
15
+ class="adm-search-bar-input-box-icon"
16
+ >
17
+ <svg
18
+ class="antd-mobile-icon"
19
+ height="1em"
20
+ style="vertical-align: -0.125em;"
21
+ viewBox="0 0 48 48"
22
+ width="1em"
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ xmlns:xlink="http://www.w3.org/1999/xlink"
25
+ >
26
+ <title>
27
+ 1A7F0A7E-6023-4A8C-9C26-4DD5452D0C85@2x
28
+ </title>
29
+ <g
30
+ fill="none"
31
+ fill-rule="evenodd"
32
+ id="SearchOutline-SearchOutline"
33
+ stroke="none"
34
+ stroke-width="1"
35
+ >
36
+ <g
37
+ id="SearchOutline-编组"
38
+ >
39
+ <rect
40
+ fill="#FFFFFF"
41
+ height="48"
42
+ id="SearchOutline-矩形"
43
+ opacity="0"
44
+ width="48"
45
+ x="0"
46
+ y="0"
47
+ />
48
+ <path
49
+ d="M10.2434135,10.1505371 C17.2346315,3.28315429 28.5696354,3.28315429 35.5608534,10.1505371 C42.3159331,16.7859644 42.5440954,27.4048667 36.2453405,34.3093889 L43.7095294,41.6422249 C43.8671196,41.7970419 43.8693677,42.0502979 43.7145508,42.2078881 C43.7128864,42.2095822 43.7112069,42.2112616 43.7095126,42.2129259 L42.1705322,43.7246464 C42.014915,43.8775072 41.7655181,43.8775006 41.6099089,43.7246316 L34.0775268,36.3248916 L34.0775268,36.3248916 C27.0485579,41.8551751 16.7593545,41.4200547 10.2434135,35.0195303 C3.25219551,28.1521474 3.25219551,17.0179199 10.2434135,10.1505371 Z M12.3532001,12.2229532 C6.52718516,17.9457722 6.52718516,27.2242951 12.3532001,32.9471142 C18.1792151,38.6699332 27.6250517,38.6699332 33.4510667,32.9471142 C39.2770817,27.2242951 39.2770817,17.9457722 33.4510667,12.2229532 C27.6250517,6.50013419 18.1792151,6.50013419 12.3532001,12.2229532 Z"
50
+ fill="currentColor"
51
+ fill-rule="nonzero"
52
+ id="SearchOutline-形状"
53
+ />
54
+ </g>
55
+ </g>
56
+ </svg>
57
+ </div>
58
+ <div
59
+ class="adm-input adm-search-bar-input"
60
+ >
61
+ <input
62
+ class="adm-input-element"
63
+ enterkeyhint="search"
64
+ type="search"
65
+ value=""
66
+ />
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ `;
73
+
74
+ exports[`SearchBar Render correctly 1`] = `
75
+ <div>
76
+ <div
77
+ class="search-bar-wrap"
78
+ >
79
+ <div
80
+ class="adm-search-bar"
81
+ >
82
+ <div
83
+ class="adm-search-bar-input-box"
84
+ >
85
+ <div
86
+ class="adm-search-bar-input-box-icon"
87
+ >
88
+ <svg
89
+ class="antd-mobile-icon"
90
+ height="1em"
91
+ style="vertical-align: -0.125em;"
92
+ viewBox="0 0 48 48"
93
+ width="1em"
94
+ xmlns="http://www.w3.org/2000/svg"
95
+ xmlns:xlink="http://www.w3.org/1999/xlink"
96
+ >
97
+ <title>
98
+ 1A7F0A7E-6023-4A8C-9C26-4DD5452D0C85@2x
99
+ </title>
100
+ <g
101
+ fill="none"
102
+ fill-rule="evenodd"
103
+ id="SearchOutline-SearchOutline"
104
+ stroke="none"
105
+ stroke-width="1"
106
+ >
107
+ <g
108
+ id="SearchOutline-编组"
109
+ >
110
+ <rect
111
+ fill="#FFFFFF"
112
+ height="48"
113
+ id="SearchOutline-矩形"
114
+ opacity="0"
115
+ width="48"
116
+ x="0"
117
+ y="0"
118
+ />
119
+ <path
120
+ d="M10.2434135,10.1505371 C17.2346315,3.28315429 28.5696354,3.28315429 35.5608534,10.1505371 C42.3159331,16.7859644 42.5440954,27.4048667 36.2453405,34.3093889 L43.7095294,41.6422249 C43.8671196,41.7970419 43.8693677,42.0502979 43.7145508,42.2078881 C43.7128864,42.2095822 43.7112069,42.2112616 43.7095126,42.2129259 L42.1705322,43.7246464 C42.014915,43.8775072 41.7655181,43.8775006 41.6099089,43.7246316 L34.0775268,36.3248916 L34.0775268,36.3248916 C27.0485579,41.8551751 16.7593545,41.4200547 10.2434135,35.0195303 C3.25219551,28.1521474 3.25219551,17.0179199 10.2434135,10.1505371 Z M12.3532001,12.2229532 C6.52718516,17.9457722 6.52718516,27.2242951 12.3532001,32.9471142 C18.1792151,38.6699332 27.6250517,38.6699332 33.4510667,32.9471142 C39.2770817,27.2242951 39.2770817,17.9457722 33.4510667,12.2229532 C27.6250517,6.50013419 18.1792151,6.50013419 12.3532001,12.2229532 Z"
121
+ fill="currentColor"
122
+ fill-rule="nonzero"
123
+ id="SearchOutline-形状"
124
+ />
125
+ </g>
126
+ </g>
127
+ </svg>
128
+ </div>
129
+ <div
130
+ class="adm-input adm-search-bar-input"
131
+ >
132
+ <input
133
+ class="adm-input-element"
134
+ enterkeyhint="search"
135
+ type="search"
136
+ value=""
137
+ />
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ `;
@@ -0,0 +1,9 @@
1
+ import { render } from '@testing-library/react';
2
+ import SearchBar from '..';
3
+
4
+ describe('SearchBar', () => {
5
+ it('Render correctly', () => {
6
+ const { container } = render(<SearchBar />);
7
+ expect(container).toMatchSnapshot();
8
+ });
9
+ });
@@ -0,0 +1,3 @@
1
+ .search-bar-wrap {
2
+ padding: 24px;
3
+ }
@@ -0,0 +1,8 @@
1
+ import { SearchBar } from 'antd-mobile';
2
+ import './index.less';
3
+
4
+ export default () => (
5
+ <div className="search-bar-wrap">
6
+ <SearchBar />
7
+ </div>
8
+ );
Binary file
@@ -0,0 +1,9 @@
1
+ html,
2
+ body,
3
+ #root {
4
+ height: 100%;
5
+ }
6
+
7
+ body {
8
+ margin: 0;
9
+ }
@@ -0,0 +1,34 @@
1
+ import { query } from '@/services/api';
2
+ import type { DvaModel } from 'alita';
3
+
4
+ export interface IndexModelState {
5
+ name: string;
6
+ }
7
+
8
+ const IndexModel: DvaModel<IndexModelState> = {
9
+ namespace: 'index',
10
+
11
+ state: {
12
+ name: 'Hello Alita',
13
+ },
14
+
15
+ effects: {
16
+ *query({ payload }, { call, put }) {
17
+ const data = yield call(query, payload);
18
+ yield put({
19
+ type: 'save',
20
+ payload: { name: data.text },
21
+ });
22
+ },
23
+ },
24
+ reducers: {
25
+ save(state, action) {
26
+ return {
27
+ ...state,
28
+ ...action.payload,
29
+ };
30
+ },
31
+ },
32
+ };
33
+
34
+ export default IndexModel;
@@ -0,0 +1,9 @@
1
+ import { useState } from 'react';
2
+
3
+ export default function () {
4
+ const [todos, setTodos] = useState(['foo', 'bar']);
5
+ return {
6
+ todos,
7
+ setTodos,
8
+ };
9
+ }
@@ -0,0 +1,20 @@
1
+ .normal {
2
+ font-family: Georgia, sans-serif;
3
+ margin-top: 4em;
4
+ text-align: center;
5
+ }
6
+
7
+ .welcome {
8
+ height: 512px;
9
+ background: url('~@/assets/yay.jpg') no-repeat center 0;
10
+ background-size: 512px 512px;
11
+ }
12
+
13
+ .description {
14
+ font-size: 35px;
15
+ margin: 18px 0 0;
16
+ }
17
+
18
+ .description code {
19
+ background: #f7f7f7;
20
+ }
@@ -0,0 +1,24 @@
1
+ import { useNavigate } from 'alita';
2
+ import { Button } from 'antd-mobile';
3
+ import styles from './index.css';
4
+
5
+ export default function ({}) {
6
+ const navigate = useNavigate();
7
+ return (
8
+ <div className={styles.normal}>
9
+ <div className={styles.welcome} />
10
+ <p className={styles.description}>
11
+ To get started, edit <code>src/pages/index.js</code> and save to reload.
12
+ </p>
13
+ <Button
14
+ size="large"
15
+ color="primary"
16
+ fill="solid"
17
+ block
18
+ onClick={() => navigate('/list')}
19
+ >
20
+ Go to List
21
+ </Button>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,4 @@
1
+ .list-icon {
2
+ width: 150px !important;
3
+ height: 150px !important;
4
+ }
@@ -0,0 +1,65 @@
1
+ import { Content, Header, Page } from '@alita/flow';
2
+ import { useNavigate } from 'alita';
3
+ import { InfiniteScroll, List } from 'antd-mobile';
4
+ import { useEffect, useState } from 'react';
5
+ import SearchBar from '../../components/SearchBar';
6
+
7
+ let count = 0;
8
+
9
+ async function mockRequest() {
10
+ console.log('mockRequest');
11
+ if (count >= 5) {
12
+ return [];
13
+ }
14
+ count++;
15
+ return [
16
+ 'A',
17
+ 'B',
18
+ 'C',
19
+ 'D',
20
+ 'E',
21
+ 'F',
22
+ 'G',
23
+ 'H',
24
+ 'I',
25
+ 'J',
26
+ 'K',
27
+ 'L',
28
+ 'M',
29
+ 'N',
30
+ 'O',
31
+ 'P',
32
+ 'Q',
33
+ ];
34
+ }
35
+
36
+ export default () => {
37
+ const [data, setData] = useState<string[]>([]);
38
+ const [hasMore, setHasMore] = useState(true);
39
+ const navigate = useNavigate();
40
+ async function loadMore() {
41
+ const append = await mockRequest();
42
+ setData((val) => [...val, ...append]);
43
+ setHasMore(append.length > 0);
44
+ }
45
+ useEffect(() => {
46
+ loadMore();
47
+ }, []);
48
+ return (
49
+ <Page>
50
+ <Header>
51
+ <SearchBar placeholder="请输入内容" />
52
+ </Header>
53
+ <Content>
54
+ <List>
55
+ {data.map((item, index) => (
56
+ <List.Item key={index} onClick={() => navigate(-1)}>
57
+ {item}
58
+ </List.Item>
59
+ ))}
60
+ </List>
61
+ <InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
62
+ </Content>
63
+ </Page>
64
+ );
65
+ };
@@ -0,0 +1,5 @@
1
+ import { request } from 'alita';
2
+
3
+ export async function query(): Promise<any> {
4
+ return request('/api/hello', { method: 'POST' });
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./src/.umi/tsconfig.json"
3
+ }
@@ -0,0 +1 @@
1
+ import 'alita/typings';