first commit
This commit is contained in:
3
.browserslistrc
Normal file
3
.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not ie <= 10
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
432
README.md
Normal file
432
README.md
Normal file
@@ -0,0 +1,432 @@
|
||||
|
||||
|
||||
## 前言:
|
||||
|
||||
前端发展真的是一个轮回,越发展越偏向**原生**,`Vue3`开发阶段就参与并使用了,但是`Vue3`真的垃圾,十分的不喜欢这种垃圾的语法,钟爱`class`和`ts装饰器`,所以有了本项目!!
|
||||
|
||||
本项目作为`Vue`脚手架而存在,已在很多公司项目中落地并经过不断打磨,同时为了更优雅的代码开发,本人写了很多周边的辅助插件,装饰器,shell,为你提供类似`java`开发中`SpringBoot`那般优雅的爽快感!
|
||||
|
||||
**围绕项目开发的插件有:**
|
||||
|
||||
1: [vue-cli-plugin-tsx-autorouter](https://www.npmjs.com/package/vue-cli-plugin-tsx-autorouter) tsx自动路由插件
|
||||
受`Nuxt.js`的思想启发,针对本项目`tsx`开发,根据页面结构自动生成路由配置文件,借助`babel`的`ast`实现提取`tsx`中类的属性进而实现路由`meta`。
|
||||
|
||||
2:./bin/tinit 封装shell命令行工具,实现通过命令行直接创建众多代码文件
|
||||
|
||||
3:解耦前端 页面 & JSON数据,实现JSON映射存储到类属性:
|
||||
- 1: [json-mapper-class](https://www.npmjs.com/package/json-mapper-class) 核心插件
|
||||
- 2: [json-class-interface](https://www.npmjs.com/package/json-class-interface) `TypeScript`项目中通用的JSON数据自动生成类型工具,支持生成 [json-mapper-class](https://www.npmjs.com/package/json-mapper-class) 标准代码。
|
||||
- 3: [json-class-desktop](https://gitee.com/bmycode/json-class-desktop) 基于`tauri` `Svelte`写的[json-class-interface](https://www.npmjs.com/package/json-class-interface) 桌面端工具,支持`Mac`,`linux`,`windows`。
|
||||
|
||||
4:装饰器(将封装成通用TS项目插件):
|
||||
- 1: 让代码更加优雅简洁高效的请求中间层装饰器 @GET,@POST,@DELETE,@PUT
|
||||
```typescript
|
||||
@GET()
|
||||
@Mapper(ResponseType)
|
||||
public async IndexTest (params: object): Promise<ResponseType[]> {
|
||||
// 留空,不要写请求代码 即可发送请求,除非 useHandle: true,这时 方法入参 params 为 ajax 接口数据,直接处理 params
|
||||
// 即可,同时也不需要写请求代码!!
|
||||
}
|
||||
```
|
||||
- 2: 依赖注入装饰器:@Autowired
|
||||
- 3: 其他装饰器:@GlobalMethod,@Mapper,@RegVuePlugs,@RegServiceAndMethods,@Start 等等..
|
||||
|
||||
5:AutoServiceCodeGeneration:实时监听`configure.config.ts`配置文件,然后根据`ApiList`中的对象`key`名称自动生成请求中间层的代码,降低手动复制的低效率!
|
||||
介绍:
|
||||
1: 文件监听:已通过Nodejs流操作实现文件监听,读取,提取,核心代码,并实现同一文件中不同编辑下的新旧数据`Diff`。
|
||||
2:请求中间层代码自动生成:**思路已有,挑战性不足,所以还未进行代码实现**
|
||||
|
||||
|
||||
## TsxVueClassApi
|
||||
|
||||
基于`Vue Class Api`,`TypeScript`,`JSX`,搭建的一套项目脚手架,实现了很多常用的注解,同时基于`babel`插件通过`Ast`语法树进行路由配置文件的自动生成等。
|
||||
|
||||
如果你喜欢`JSX`,那么这套脚手架你应该会十分喜欢,代码语法如下:
|
||||
|
||||
```tsx
|
||||
import "@pageLess/Home.less";
|
||||
import { Component } from 'vue-property-decorator';
|
||||
import HelloWorld from '@components/HelloWorld';
|
||||
import { HomeLogic } from "@logic/page/Home.logic";
|
||||
import { Getter } from "vuex-class";
|
||||
import { IndexUtils } from "@utils/index.utils";
|
||||
import { Autowired } from "@ann/Ioc.annotation";
|
||||
import { VueCustomize, RouterMeta } from "@logic/Base.type";
|
||||
|
||||
@Component
|
||||
export default class Home extends VueCustomize {
|
||||
// RouterMeta 是 vue-router 路由meta配置信息,会被 我们的插件
|
||||
// vue-cli-plugin-tsx-autorouter 进行 ast 提取注入到 自动生成的 路由配置文件中
|
||||
public RouterMeta: RouterMeta = {
|
||||
title: '首页',
|
||||
showNav: true,
|
||||
isLogin: true
|
||||
}
|
||||
|
||||
// 抽离组件所有逻辑到独立类,解耦 页面层 和 逻辑层
|
||||
// 通过 Autowired 依赖注入装饰器 实现 注入 逻辑 -> 页面
|
||||
// 同时 传入 vue 的 this,实现逻辑层可以调用vue方法
|
||||
@Autowired(Home)
|
||||
private logic!: HomeLogic<Home>
|
||||
|
||||
@Autowired()
|
||||
public readonly Utils!: IndexUtils;
|
||||
|
||||
@Getter('GettersTabsContentArr')
|
||||
public readonly getterFoo!: Array<any>;
|
||||
|
||||
public async created() {
|
||||
await this.logic.StartUp();
|
||||
}
|
||||
|
||||
public ClickButton(): JSX.Element {
|
||||
return (
|
||||
<a-button type="primary" onClick={async () => {
|
||||
console.log("按钮被点击22222");
|
||||
}}>
|
||||
Primary
|
||||
</a-button>
|
||||
)
|
||||
}
|
||||
|
||||
public back() {
|
||||
console.log("qqqq");
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<div class="home">
|
||||
<img src="/img/baidu.png" alt="" />
|
||||
<h1 on-click={this.back}>111</h1>
|
||||
<HelloWorld msg="向HelloWorld props传参" />
|
||||
<input type="text" v-model={this.logic.title} />
|
||||
<h2>{this.logic.title}</h2>
|
||||
{this.ClickButton()}
|
||||
<ul>
|
||||
{
|
||||
this.logic.List.map(v => {
|
||||
return <li>
|
||||
{v.user_id}
|
||||
{v.user_name}
|
||||
</li>
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**这个脚手架我们正在项目中使用,同时也在不断的开发和完善!!!!**
|
||||
|
||||
## 1:安装
|
||||
|
||||
需要额外全局安装的插件有:
|
||||
|
||||
```bash
|
||||
npm i -g concurrently cross-env gulp
|
||||
```
|
||||
|
||||
然后下载项目,初始化并启动:
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/bmycode/vue-class-api-tsx.git
|
||||
npm i
|
||||
npm run start
|
||||
# 注意!!!:网站运行后使用电脑本机的局域网ip地址来访问,例如:http://192.168.1.101:8080/#/Home
|
||||
```
|
||||
|
||||
**注意**
|
||||
|
||||
`Vue`官方原生底层就支持`JSX`语法,但是官方没有关于`Vue`中使用`JSX`的文档,而且很多`Vue`ui框架和第三方插件都没有相关语法,但是这不影响我们在`Vue`中正常使用`JSX`。很多语法需要特殊注意和改造一下!!
|
||||
|
||||
## 2:使用
|
||||
|
||||
下面说下这个脚手架的使用和注意事项。
|
||||
|
||||
### 2.1:项目结构
|
||||
|
||||
请仔细看文件后面的中文说明!!!
|
||||
|
||||
```txt
|
||||
├── README.md
|
||||
├── babel.config.js babel配置文件
|
||||
├── bin 终端NewFile工具
|
||||
│ ├── config
|
||||
│ │ └── index.js
|
||||
│ └── tinit
|
||||
├── dist 打包后的文件夹
|
||||
├── gulpfile.js gulp配置文件,实现监听page自动生成路由文件
|
||||
├── package.json 核心依赖配置文件
|
||||
├── public 公共的资源文件
|
||||
├── src 存放核心源码
|
||||
│ ├── application 页面相关
|
||||
│ │ ├── app.tsx 最外层顶级父组件
|
||||
│ │ ├── assets 静态资源回合webpack打包进源码
|
||||
│ │ ├── components 公共组件
|
||||
│ │ ├── logic 【重要】TSX组件中的js逻辑抽象层
|
||||
│ │ │ ├── base.logic.ts 【重要】所有TSX组件的js逻辑父类
|
||||
│ │ │ ├── base.type.ts 组件中 通用类型的编写
|
||||
│ │ │ └── page 【重要】对应页面的TSX组件逻辑抽象
|
||||
│ │ └── page 【重要】存放TSX页面组件
|
||||
│ └── core 核心JS层
|
||||
│ ├── annotation 存放自己开发的装饰器
|
||||
│ ├── JsonToClass json-mapper-class 的源代码
|
||||
│ │ ├── http.annotation.ts 请求装饰器
|
||||
│ │ ├── ioc.annotation.ts IOC依赖装饰器
|
||||
│ │ └── register.annotation.ts 启动装饰器
|
||||
│ ├── config 站点配置文件
|
||||
│ │ ├── configure.config.ts 核心配置文件
|
||||
│ │ └── route.config.ts 自动生成的路由配置文件
|
||||
│ ├── dao axios的封装
|
||||
│ ├── model TS存取器
|
||||
│ ├── run 项目的启动类
|
||||
│ │ ├── init.run.ts 初始化Vue各种配置
|
||||
│ ├── service 请求的service层封装
|
||||
│ │ └── impl 请求service的具体类
|
||||
│ ├── store Vuex的配置
|
||||
│ ├── types 自定义的声明文件
|
||||
│ └── utils 工具函数
|
||||
├── tsconfig.json TS的配置文件
|
||||
└── vue.config.js Vue-cli3 的配合文件
|
||||
```
|
||||
|
||||
### 2.2:编码注意事项和规范,严格遵守!!!
|
||||
|
||||
1:`page/`文件夹中的所有`tsx`组件必须继承自`VueCustomize`,`VueCustomize`是所有页面的父类,可以在这里定义一些公共的类型!
|
||||
|
||||
2:`logic/`文件夹中的所有`logic.ts`必须继承`BaseLogic`并实现接口`Methods`,`BaseLogic`是所有页面逻辑层的公共封装,可以把使用率高的接口或者变量放在`BaseLogic`
|
||||
中去定义和实现,这样页面的逻辑层子类可以实现直接使用!
|
||||
|
||||
3:`logic/`中页面的逻辑层可以使用装饰器`@Autowired()`去注入多个`service`的依赖,实现对`ajax`中间层方法的调用,以发送`ajax`请求!具体的看下面代码
|
||||
|
||||
```typescript
|
||||
import { Autowired } from "@/core/annotation/Ioc.annotation";
|
||||
import { UserServiceImpl, ResponseType} from "@/core/service/impl/User.service.impl";
|
||||
import { BaseLogic } from "@logic/Base.logic";
|
||||
import { Methods, VueCustomize } from "@logic/Base.type";
|
||||
|
||||
export class HomeLogic<T extends VueCustomize> extends BaseLogic implements Methods {
|
||||
|
||||
@Autowired()
|
||||
public readonly UserServiceImpl!: UserServiceImpl;
|
||||
|
||||
public title: string = "12222";
|
||||
|
||||
public List: ResponseType[] = [];
|
||||
|
||||
constructor(_: T) {
|
||||
super();
|
||||
this._ = _
|
||||
}
|
||||
|
||||
/**
|
||||
* 在这里面调用需要打开页面就执行的ajax请求
|
||||
* @constructor
|
||||
*/
|
||||
public async StartUp (): Promise<void> {
|
||||
await this.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求首页数据
|
||||
*/
|
||||
public async getData (): Promise<void> {
|
||||
this.List = await this.UserServiceImpl.IndexTest({ type: 1, page: 1 });
|
||||
console.log("this.List: ", this.List);
|
||||
|
||||
|
||||
// let res = await this.UserServiceImpl.login({ type: 2, page: 2 });
|
||||
// console.log("this.res: ", res);
|
||||
|
||||
// this._.$setToken("")
|
||||
// await this._.$router.push('/About')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4:`logic/`中必须实现构造函数`constructor`,在构造函数中保存`tsx`页面传入指向`vue`实例的`this`,这样才能在`logic`中正确使用`vue`的一些方法,具体看代码:
|
||||
|
||||
```typescript
|
||||
constructor(_: T) {
|
||||
super();
|
||||
this._ = _
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求首页数据
|
||||
*/
|
||||
public async getData (): Promise<void> {
|
||||
this.List = await this.UserServiceImpl.IndexTest({ type: 1, page: 1 });
|
||||
console.log("this.List: ", this.List);
|
||||
}
|
||||
```
|
||||
|
||||
5:如何将自定义的方法通过 `Vue.prototype.xxx` 挂载到`Vue`上以方便全局使用?
|
||||
|
||||
答:在`utils/index.utils.ts`中定义方法的时候请使用装饰器`@GlobalMethod()`装饰方法,然后如果你在`tsx`页面组件中需要使用此方法,那么请在`base.type.ts`中定义变量并编写变量类型!
|
||||
|
||||
同时如果你需要在`logic`逻辑层使用此方法,那么需要在`base.logic.ts`的`VueType`接口中编写类型。具体请看代码:
|
||||
|
||||
```typescript
|
||||
// src/core/utils/index.utils.ts
|
||||
|
||||
import { ConfigureConfig } from "@config/configure.config";
|
||||
import { GlobalMethod } from "@ann/Register.annotation";
|
||||
|
||||
export class IndexUtils {
|
||||
/**
|
||||
* 方法上打上 @GlobalMethod() 注解,这个方法会自动成为vue的全局方法
|
||||
* 组件中直接:this.$setToken() 即可
|
||||
*/
|
||||
@GlobalMethod()
|
||||
public setToken (args: string): void {
|
||||
localStorage.setItem("token", args)
|
||||
}
|
||||
}
|
||||
|
||||
// src/application/logic/base.logic.ts
|
||||
|
||||
// 定义 $setToken 方法和入参类型,返回值等!
|
||||
export class VueCustomize extends Vue {
|
||||
$setToken!: (token: string) => void
|
||||
}
|
||||
```
|
||||
|
||||
如果你觉得上面方式使用全局方法太过于麻烦,那么你可以在你需要使用的`logic`类或`tsx`组件中使用装饰器`@Autowired()`来注入`index.utils.ts`到类属性中,代码示例:
|
||||
|
||||
```typescript
|
||||
import "@pageLess/Home.less";
|
||||
import { Component } from 'vue-property-decorator';
|
||||
import HelloWorld from '@components/HelloWorld';
|
||||
import { HomeLogic } from "@logic/page/Home.logic";
|
||||
import { Getter } from "vuex-class";
|
||||
// 1:导入需要被注入的类
|
||||
import { IndexUtils } from "@utils/index.utils";
|
||||
// 2:导入装饰器
|
||||
import { Autowired } from "@ann/Ioc.annotation";
|
||||
import { VueCustomize, RouterMeta } from "@logic/Base.type";
|
||||
import { toClass, toInterface, configFilePath } from "json-class-interface";
|
||||
@Component
|
||||
export default class Home extends VueCustomize {
|
||||
|
||||
// 抽离组件所有逻辑到独立类,解耦页面和数据
|
||||
@Autowired(Home)
|
||||
private logic!: HomeLogic<Home>
|
||||
|
||||
// RouterMeta 是 vue-router 路由meta配置信息,会被ast提取注入到自动生成的路由配置文件中
|
||||
public RouterMeta: RouterMeta = {
|
||||
title: '首页',
|
||||
showNav: true,
|
||||
isLogin: true
|
||||
}
|
||||
|
||||
// 3:将 IndexUtils 注入到属性 Utils 中
|
||||
@Autowired()
|
||||
public readonly Utils!: IndexUtils;
|
||||
|
||||
@Getter('GettersTabsContentArr')
|
||||
public readonly getterFoo!: Array<any>;
|
||||
|
||||
|
||||
public async created () {
|
||||
// 4:使用 setToken 方法
|
||||
this.Utils.setToken("1111")
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ClickButton(): JSX.Element {
|
||||
return (
|
||||
<a-button type="primary" onClick={async () => {
|
||||
console.log("按钮被点击22222");
|
||||
}}>
|
||||
Primary
|
||||
</a-button>
|
||||
)
|
||||
}
|
||||
|
||||
public back() {
|
||||
console.log("qqqq");
|
||||
}
|
||||
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<div class="home">
|
||||
<img src="/img/baidu.png" alt="" />
|
||||
<h1 on-click={this.back}>111</h1>
|
||||
<HelloWorld msg="向HelloWorld props传参" />
|
||||
<input type="text" v-model={this.logic.title} />
|
||||
<h2>{this.logic.title}</h2>
|
||||
{this.ClickButton()}
|
||||
<ul>
|
||||
{
|
||||
this.logic.List.map(v => {
|
||||
return <li>
|
||||
{v.user_id}
|
||||
{v.user_name}
|
||||
</li>
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3:使用注意事项
|
||||
|
||||
**1:** 项目中的路由使用的是`vue-router`,同时我借鉴了`Nuxt.js`的思想,自己开发了路由配置文件自动生成的插件,这个插件是`Vue-cli 3`的插件,所以不能在`Vue cli 2`的项目中使用!!
|
||||
|
||||
插件源码已经提交`npm`包市场!请完整阅读下面的插件文档,路由的配置文件让插件帮我们自动生成就好了!!!
|
||||
|
||||
[vue-cli-plugin-tsx-autorouter](https://www.npmjs.com/package/vue-cli-plugin-tsx-autorouter)
|
||||
|
||||
**2:** 同时借鉴`PHP`开发框架`Laravel`开发终端命令行工具,通过命令帮我们创建带有模板代码能直接运行的文件!代码放在 `bin/` 文件夹中!
|
||||
|
||||
使用手册:
|
||||
|
||||
```bash
|
||||
╭─bmy@MacBook-Pro /Applications/Source/TsxVueClassApi ‹master*›
|
||||
╰─$ ./bin/tinit -h
|
||||
Usage: tinit [options]
|
||||
|
||||
Options:
|
||||
-t, --file-type <type> 文件类型: [page,logic,service,impl,less]
|
||||
-n, --file-name <name> 被创建的文件名称
|
||||
-i, --is-create <is> 是否为 -t 参数自动创建相关文件类型 (default: false)
|
||||
-v, --version 显示当前的版本
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
参数说明:
|
||||
|
||||
- -t:需要被创建的文件类型,取值 page(TSX页面),logic(TSX页面的js抽象层),service(请求中间层的接口),impl(实现接口的类),less(css样式文件)。可以多传!
|
||||
- -n:需要被创建的文件名
|
||||
- -i:如果数值为true,那么会自动创建 -t 参数所有的文件类型
|
||||
|
||||
使用案例:
|
||||
|
||||
```bash
|
||||
╭─bmy@MacBook-Pro /Applications/Source/TsxVueClassApi ‹master*›
|
||||
╰─$ ./bin/tinit -t page logic -n Test
|
||||
成功创建文件...
|
||||
```
|
||||
|
||||
上面命令执行后会分别在`src/application/page`中创建创建文件`Text.tsx`页面组件,同时在`src/application/logic/page`中创建`Test.logic.ts`的逻辑层文件。
|
||||
|
||||
多试几次,请自行熟练掌握!!
|
||||
|
||||
## 3:开发中功能
|
||||
|
||||
- [x] 【重要】1:实现`ajax`请求到的数据和`model`层的映射绑定,这种数据绑定关系,可以完全解决后端改接口字段从而导致前端代码也需要同步大量修改的问题!!
|
||||
|
||||
- 插件文档:[https://gitee.com/bmycode/json-to-class-or-interface](https://gitee.com/bmycode/json-to-class-or-interface)
|
||||
|
||||
|
||||
- [x] 【不太重要】1:实时监听`configure.config.ts`配置文件,然后根据`ApiList`中的对象`key`名称自动在`service`层的类中生成对应的方法,降低手动复制的低效率!
|
||||
|
||||
- 已实现功能,但未整合进项目!
|
||||
9
babel.config.js
Normal file
9
babel.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
1
bin/.pydio
Normal file
1
bin/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
a06d094b-d1c2-4adc-8c5c-0a9986f5aea8
|
||||
1
bin/config/.pydio
Normal file
1
bin/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
97f1b24f-9aff-44d4-b3af-c8a5ea6943c7
|
||||
94
bin/config/index.js
Normal file
94
bin/config/index.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
pageConfig: (name) => {
|
||||
return `
|
||||
import "@pageLess/${ name }.less";
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { ${ name }Logic } from "@logic/page/${ name }.logic";
|
||||
import { Route, RawLocation } from 'vue-router';
|
||||
import { BaseLayout, RouterMeta } from "@layout/base.layout";
|
||||
|
||||
@Component
|
||||
export default class ${ name } extends BaseLayout {
|
||||
private service: ${ name }Logic = new ${ name }Logic(this);
|
||||
private RouterMeta: RouterMeta = {
|
||||
title: '${ name }',
|
||||
showNav: true,
|
||||
isLogin: true
|
||||
};
|
||||
|
||||
public async created() {
|
||||
await this.service.StartUp();
|
||||
};
|
||||
|
||||
// 路由拦截器
|
||||
beforeRouteEnter(to: Route, from: Route, next: (to?: RawLocation | false | ((vm: Vue) => void)) => void) {
|
||||
next();
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<div class="${ name }">
|
||||
${ name }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
logicConfig: (name) => {
|
||||
return `
|
||||
import { BaseLogic, VueType, Methods } from "@logic/Base.logic";
|
||||
import { Inject } from "@ann/Ioc.annotation";
|
||||
import { UserServiceImpl } from "@impl/User.service.impl";
|
||||
|
||||
export class ${ name }Logic extends BaseLogic implements Methods {
|
||||
|
||||
@Inject()
|
||||
public readonly UserServiceImpl!: UserServiceImpl;
|
||||
|
||||
constructor(point: any) {
|
||||
super();
|
||||
this.VueApp = point;
|
||||
}
|
||||
|
||||
public async StartUp(): Promise<void> {
|
||||
|
||||
}
|
||||
}
|
||||
`
|
||||
},
|
||||
serviceConfig: (name) => {
|
||||
return `
|
||||
export interface ${ name }Service {
|
||||
IndexClass(params: object): Promise<any>;
|
||||
}
|
||||
`
|
||||
},
|
||||
implPathConfig: (name) => {
|
||||
return `
|
||||
import { ${ name }Service } from "@service/${ name }.service";
|
||||
import { Injectable } from "@ann/Ioc.annotation";
|
||||
import { GET } from "@ann/Http.annotation";
|
||||
|
||||
@Injectable()
|
||||
export class ${ name }ServiceImpl implements ${ name }Service {
|
||||
|
||||
@GET({ useHandle: false })
|
||||
public async IndexClass(params: object): Promise<any> { }
|
||||
|
||||
}
|
||||
`
|
||||
},
|
||||
lessConfig: (name) => {
|
||||
return `
|
||||
@import "../color/index";
|
||||
@import "../mixin/index";
|
||||
.${ name } {
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
1
bin/service-code-generation/.pydio
Normal file
1
bin/service-code-generation/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
1331c103-912a-4f75-9045-03aaa69824f8
|
||||
1
bin/service-code-generation/config/.pydio
Normal file
1
bin/service-code-generation/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
8bb6e31a-650f-4f25-9e76-72e5118f6438
|
||||
6
bin/service-code-generation/config/index.js
Normal file
6
bin/service-code-generation/config/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
// 需要被监听的api地址路径
|
||||
apiListPath: "F:\\desktop\\vue-class-api-tsx\\src\\core\\config\\configure.config.ts",
|
||||
// 生成的service层代码存放路径
|
||||
outPutServicePath: "F:\\desktop\\vue-class-api-tsx\\src\\core\\service"
|
||||
}
|
||||
29
bin/service-code-generation/index.js
Normal file
29
bin/service-code-generation/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const config = require('./config')
|
||||
const readApiList = require('./utils/ReadApiList')
|
||||
const file = require('./utils/File')
|
||||
let ServerMap = new Map();
|
||||
// Map(3) {
|
||||
// 'Car' => Map(2) { 'IndexTest' => 'IndexTest', 'carList' => 'carList' },
|
||||
// 'Index' => Map(1) { 'reg' => 'reg' },
|
||||
// 'Order' => Map(1) { 'test' => 'test' }
|
||||
// }
|
||||
let main = async () => {
|
||||
let readApi = new readApiList(config.apiListPath)
|
||||
let res = await readApi.getFileList()
|
||||
|
||||
res.forEach(v => {
|
||||
// [ 'Car', 'IndexTest' ]
|
||||
let key = v.split(":")[0].split("_")
|
||||
if (!ServerMap.get(key[0])) {
|
||||
ServerMap.set(key[0], new Map())
|
||||
}
|
||||
ServerMap.get(key[0]).set(key[1],key[1])
|
||||
});
|
||||
|
||||
// 根据接口对象的配置文件,自动生成service文件和代码
|
||||
ServerMap.forEach((value,key) => {
|
||||
file.createdService(key,value)
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
1
bin/service-code-generation/utils/.pydio
Normal file
1
bin/service-code-generation/utils/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
7fcbe2a8-4ef1-42fd-8123-b5ed331cbd41
|
||||
59
bin/service-code-generation/utils/File.js
Normal file
59
bin/service-code-generation/utils/File.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const { writeFileSync, existsSync } = require("fs");
|
||||
const config = require("../config");
|
||||
|
||||
class File {
|
||||
|
||||
createdService(ServiceName, ServiceMethods) {
|
||||
let fullPath = `${config.outPutServicePath}\\${ServiceName}.service.ts`
|
||||
if (!existsSync(fullPath)) {
|
||||
let mArray = Array.from(ServiceMethods, ([name, value]) => ({ name, value }));
|
||||
writeFileSync(fullPath, this.stripIndent`
|
||||
// @ts-nocheck
|
||||
import { GET, Mapper, POST, PUT, DELETE } from "@ann/Http.annotation";
|
||||
import { property, defaultValue, deserialize } from "@ann/JsonToClass";
|
||||
|
||||
// 建议用工具json-class-desktop生成这个类 类型
|
||||
export class ResponseType {
|
||||
@property("id")
|
||||
user_id!: number;
|
||||
}
|
||||
|
||||
export class ${ServiceName}Service {
|
||||
${mArray.map(v => {
|
||||
return `
|
||||
@GET()
|
||||
@Mapper(ResponseType)
|
||||
public async ${v.name} (params: object): Promise<ResponseType[]> {}
|
||||
`
|
||||
}).toString().replaceAll(",", "")
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stripIndent(template, ...expressions) {
|
||||
let result = template.reduce((prev, next, i) => {
|
||||
let expression = expressions[i - 1];
|
||||
if ( Array.isArray(expression)) {
|
||||
expression = expression.join('');
|
||||
}
|
||||
return prev + expression + next;
|
||||
});
|
||||
|
||||
const match = result.match(/^[^\S\n]*(?=\S)/gm);
|
||||
const indent = match && Math.min(...match.map(el => el.length));
|
||||
|
||||
if (indent) {
|
||||
const regexp = new RegExp(`^.{${indent}}`, 'gm');
|
||||
result = result.replace(regexp, '');
|
||||
}
|
||||
|
||||
result = result.replace(/^[^\S\n]+/gm, ' ');
|
||||
result = result.trim();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new File
|
||||
61
bin/service-code-generation/utils/ReadApiList.js
Normal file
61
bin/service-code-generation/utils/ReadApiList.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const { createReadStream } = require('fs')
|
||||
|
||||
class ReadApiList {
|
||||
apiListPath = "";
|
||||
lineLength = 0;
|
||||
textArr = [];
|
||||
lineBuffer = Buffer.alloc(4096);
|
||||
text = "";
|
||||
|
||||
constructor(path) {
|
||||
this.apiListPath = path
|
||||
}
|
||||
|
||||
getFileList() {
|
||||
this.text = createReadStream(this.apiListPath)
|
||||
return new Promise((s, e) => {
|
||||
this.textArr = [];
|
||||
let status = false
|
||||
this.text.on('data', (data) => {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i] == 10 || data[i] == 13) {
|
||||
if (data[i] == 10) {
|
||||
try {
|
||||
var line = this.lineBuffer.slice(0, this.lineLength);
|
||||
if (line.length != 0) {
|
||||
let str = line.toString('utf8')
|
||||
.replace(/^\s+|\s+$/g, '')
|
||||
.replace(/\'/g, "")
|
||||
.replace(/,/g, "")
|
||||
|
||||
if (str.includes("start -")) {
|
||||
status = true
|
||||
continue;
|
||||
}
|
||||
if (str.includes("end -")) {
|
||||
status = false
|
||||
break;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
// console.log("str: ", str.split(":"))
|
||||
this.textArr.push(str);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lineLength = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.lineBuffer[this.lineLength] = data[i];
|
||||
this.lineLength++;
|
||||
}
|
||||
}
|
||||
|
||||
s(this.textArr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReadApiList
|
||||
105
bin/tinit
Executable file
105
bin/tinit
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env node
|
||||
const {program} = require('commander');
|
||||
const packageConfig = require("../package.json");
|
||||
const fs = require("fs");
|
||||
const path = require('path');
|
||||
const Config = require("./config");
|
||||
|
||||
var isWin = /^win/.test(process.platform);
|
||||
const RootPath = process.cwd();
|
||||
|
||||
program
|
||||
.requiredOption('-t, --file-type <type>', '文件类型: [page,logic,service,impl,less]')
|
||||
.option('-n, --file-name <name>', '被创建的文件名称')
|
||||
.option('-i, --is-create <is>', '是否为 -t 参数自动创建相关文件类型', false);
|
||||
program.version(packageConfig.version, '-v, --version', '显示当前的版本');
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
// 存储终端的参数
|
||||
let programOptions = program.opts();
|
||||
let OtherFileType = program.parse(process.argv).args
|
||||
|
||||
// tsx 页面路径
|
||||
let PagePath = isWin ? `${ RootPath }\\src\\application\\page\\${ UpperCase(programOptions.fileName) }.tsx` : `${ RootPath }/src/application/page/${ UpperCase(programOptions.fileName) }.tsx`;
|
||||
// logic 页面路径
|
||||
let LogicPath = isWin ? `${ RootPath }\\src\\application\\logic\\page\\${ UpperCase(programOptions.fileName) }.logic.ts` : `${ RootPath }/src/application/logic/page/${ UpperCase(programOptions.fileName) }.logic.ts`;
|
||||
// service 页面路径
|
||||
let ServicePath = isWin ? `${ RootPath }\\src\\core\\service\\${ UpperCase(programOptions.fileName) }.service.ts` : `${ RootPath }/src/core/service/${ UpperCase(programOptions.fileName) }.service.ts`;
|
||||
// Impl 页面路径
|
||||
let ImplPath = isWin ? `${ RootPath }\\src\\core\\service\\impl\\${ UpperCase(programOptions.fileName) }.service.impl.ts` : `${ RootPath }/src/core/service/impl/${ UpperCase(programOptions.fileName) }.service.impl.ts`;
|
||||
// Less 页面路径
|
||||
let LessPath = isWin ? `${ RootPath }\\src\\application\\assets\\less\\page\\${ UpperCase(programOptions.fileName) }.less` : `${ RootPath }/src/application/assets/less/page/${ UpperCase(programOptions.fileName) }.less`;
|
||||
|
||||
// 需要创建的文件类型
|
||||
let FileList = ['page', 'logic', 'service', 'impl', 'less'];
|
||||
|
||||
// 是否需要为 controller 联合创建所有文件
|
||||
if (programOptions.isCreate) {
|
||||
FileList.forEach(val => replaceTplText(val));
|
||||
console.log("成功创建文件...");
|
||||
} else {
|
||||
// 循环创建多个文件
|
||||
if (OtherFileType.length != 0) {
|
||||
[...OtherFileType, programOptions.fileType].forEach(v => replaceTplText(v))
|
||||
} else {
|
||||
replaceTplText(programOptions.fileType)
|
||||
}
|
||||
console.log("成功创建文件...");
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换模板的内容,并将内容写入文件。
|
||||
*/
|
||||
let FileText = ""
|
||||
|
||||
function replaceTplText(action) {
|
||||
switch (action) {
|
||||
case 'page':
|
||||
fs.writeFileSync(PagePath, Config.pageConfig(UpperCase(programOptions.fileName)));
|
||||
break;
|
||||
case 'logic':
|
||||
fs.writeFileSync(LogicPath, Config.logicConfig(UpperCase(programOptions.fileName)));
|
||||
break;
|
||||
case 'service':
|
||||
fs.writeFileSync(ServicePath, Config.serviceConfig(UpperCase(programOptions.fileName)));
|
||||
break;
|
||||
case 'impl':
|
||||
fs.writeFileSync(ImplPath, Config.implPathConfig(UpperCase(programOptions.fileName)));
|
||||
break;
|
||||
case 'less':
|
||||
fs.writeFileSync(LessPath, Config.lessConfig(UpperCase(programOptions.fileName)));
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单词首字母大写
|
||||
* @param str
|
||||
* @returns {void | string | *}
|
||||
* @constructor
|
||||
*/
|
||||
function UpperCase(str) {
|
||||
return str.replace(str[0], str[0].toUpperCase())
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param path
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
function GetFileText(path) {
|
||||
return (fs.readFileSync(pathJoin(path), 'utf-8')).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接路径
|
||||
* @param paths
|
||||
* @returns {string}
|
||||
*/
|
||||
function pathJoin(paths) {
|
||||
return path.join(__dirname, paths)
|
||||
}
|
||||
70
gulpfile.js
Normal file
70
gulpfile.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
const gulp = require('gulp');
|
||||
const exec = require('child_process').exec;
|
||||
const file = require('./test');
|
||||
var oldText = [], newText = [];
|
||||
|
||||
|
||||
/**
|
||||
* 执行 vue-cli-service route 命令进行路由编译
|
||||
*/
|
||||
gulp.task('AutoCompileRouter', function (cb) {
|
||||
return exec('npm run route', (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.log("编译失败: ", err);
|
||||
cb(err)
|
||||
} else {
|
||||
console.log("编译成功");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('AutoAddUrl', async (cb) => {
|
||||
newText = await file.getFileList();
|
||||
console.log("newText ------------- ", newText)
|
||||
let cy = diff(newText, oldText);
|
||||
console.log("diff ------------- ", cy)
|
||||
oldText = newText;
|
||||
cb()
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听 page 所有文件的修改,自动触发 AutoCompileRouter 任务
|
||||
*/
|
||||
gulp.task('auto', async () => {
|
||||
oldText = await file.getFileList();
|
||||
console.log("oldText ------------- ", oldText)
|
||||
// gulp.watch('src/application/page/**/*', gulp.parallel('AutoCompileRouter'));
|
||||
gulp.watch('src/core/config/configure.config.ts', gulp.parallel('AutoAddUrl'));
|
||||
});
|
||||
|
||||
/**
|
||||
* diff 对比文件修改前后的差异,取出差异和结果
|
||||
* @param {*} newV 修改后
|
||||
* @param {*} oldV 修改前
|
||||
* @returns
|
||||
*/
|
||||
function diff(newV, oldV) {
|
||||
let status = "";
|
||||
|
||||
let oV = null, nV = null;
|
||||
if (newV.length > oldV.length) {
|
||||
status = "add"
|
||||
oV = new Set(newV)
|
||||
nV = new Set(oldV)
|
||||
} else {
|
||||
status = "delete"
|
||||
oV = new Set(oldV);
|
||||
nV = new Set(newV);
|
||||
}
|
||||
|
||||
return {
|
||||
status,
|
||||
item: Array.from(new Set([...oV].filter(x => !nV.has(x))))
|
||||
}
|
||||
}
|
||||
|
||||
gulp.task('default', gulp.parallel('auto'));
|
||||
19372
package-lock.json
generated
Normal file
19372
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
package.json
Normal file
57
package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "tsx-vue-class-api",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "concurrently \"npm run serve\" \"gulp\" ",
|
||||
"serve": "cross-env NODE_ENV=dev vue-cli-service serve",
|
||||
"build": "cross-env NODE_ENV=production vue-cli-service build",
|
||||
"route": "vue-cli-service route",
|
||||
"auto": "node bin/service-code-generation/index.js"
|
||||
},
|
||||
"bin": {
|
||||
"tinit": "bin/tinit"
|
||||
},
|
||||
"dependencies": {
|
||||
"ant-design-vue": "^1.6.5",
|
||||
"axios": "^0.20.0",
|
||||
"core-js": "^3.6.5",
|
||||
"isarray": "^2.0.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
"vue-property-decorator": "^8.4.2",
|
||||
"vue-router": "^3.2.0",
|
||||
"vuex": "^3.4.0",
|
||||
"vuex-class": "^0.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.5",
|
||||
"@babel/generator": "^7.23.5",
|
||||
"@babel/parser": "^7.23.5",
|
||||
"@babel/traverse": "^7.23.5",
|
||||
"@babel/types": "^7.23.5",
|
||||
"@bmy/cli-service": "^4.5.19",
|
||||
"@types/isarray": "^2.0.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "^6.1.0",
|
||||
"compression-webpack-plugin": "^5.0.1",
|
||||
"es6-promise": "^4.2.8",
|
||||
"glob": "^7.1.3",
|
||||
"gulp": "^4.0.2",
|
||||
"less": "^3.0.4",
|
||||
"less-loader": "^5.0.0",
|
||||
"pify": "^4.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"typescript": "~3.9.3",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"vue-cli-plugin-tsx-autorouter": "^1.3.6",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack-bundle-analyzer": "^3.8.0",
|
||||
"yamljs": "^0.3.0"
|
||||
}
|
||||
}
|
||||
1
public/.pydio
Normal file
1
public/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
da7a2feb-9485-4fcf-855a-b6dbcedaad05
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
1
public/fonts/.pydio
Normal file
1
public/fonts/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
282d544a-c6a9-4e6d-ab20-93a15ca73330
|
||||
1
public/img/.pydio
Normal file
1
public/img/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
609d9c08-74bf-4b6b-b612-c63a5bf29b24
|
||||
BIN
public/img/baidu.png
Normal file
BIN
public/img/baidu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
22
public/index.html
Normal file
22
public/index.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
~ Copyright (c) 2020. bmy
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||
<link href="<%= BASE_URL %>favicon.ico" rel="icon">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
1
src/.pydio
Normal file
1
src/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
37bbcaf2-9403-4c45-a641-e6a09bbec803
|
||||
1
src/application/.pydio
Normal file
1
src/application/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
55e88823-96b5-4e92-a121-1743226e7284
|
||||
23
src/application/app.tsx
Normal file
23
src/application/app.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import "@less/imports.less"
|
||||
import { Component } from 'vue-property-decorator';
|
||||
import { VueCustomize } from "@logic/base.type";
|
||||
|
||||
@Component
|
||||
export default class App extends VueCustomize {
|
||||
protected render () {
|
||||
return (
|
||||
<div id="app">
|
||||
<div id="nav">
|
||||
<router-link to="/Home">Home</router-link>
|
||||
|
|
||||
<router-link to="/About">About</router-link>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
1
src/application/assets/.pydio
Normal file
1
src/application/assets/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e9a6fea5-8124-47ee-8764-2a2b38559e29
|
||||
1
src/application/assets/less/.pydio
Normal file
1
src/application/assets/less/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
08d2ddbb-779b-48d9-a2f0-eff54b1a49ba
|
||||
1
src/application/assets/less/common/.pydio
Normal file
1
src/application/assets/less/common/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
984cb101-d8c8-46fc-bdf2-404c29895807
|
||||
463
src/application/assets/less/common/normalize.less
vendored
Normal file
463
src/application/assets/less/common/normalize.less
vendored
Normal file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
html, body, #app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
line-height: 1.15; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 3 */
|
||||
-webkit-text-size-adjust: 100%; /* 3 */
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
background-color: #fafafa;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the outline on focused links when they are also active or hovered
|
||||
* in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the border, margin, and padding in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
8
src/application/assets/less/common/resetUi.less
Normal file
8
src/application/assets/less/common/resetUi.less
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
// 重置ui框架css
|
||||
.el-button {
|
||||
width: 300px;
|
||||
}
|
||||
1
src/application/assets/less/components/.pydio
Normal file
1
src/application/assets/less/components/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
29ff899f-7099-4b35-97d9-7eb12e8b1b67
|
||||
6
src/application/assets/less/imports.less
Normal file
6
src/application/assets/less/imports.less
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
// 公共css
|
||||
@import "./common/normalize";
|
||||
@import "./common/resetUi";
|
||||
1
src/application/assets/less/mixin/.pydio
Normal file
1
src/application/assets/less/mixin/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e36141e2-2743-4aa7-89cf-e34088c48df2
|
||||
3
src/application/assets/less/mixin/_.less
Normal file
3
src/application/assets/less/mixin/_.less
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
@import "./color.less";
|
||||
@import "./index.less";
|
||||
313
src/application/assets/less/mixin/color.less
Normal file
313
src/application/assets/less/mixin/color.less
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
// 公共css样式,可以在 页面组件中直接使用变量名称
|
||||
// 具体看 components/home.styl
|
||||
@theme-color: red;
|
||||
@test-color: yellow;
|
||||
|
||||
@red50: #ffebee;
|
||||
@red100: #ffcdd2;
|
||||
@red200: #ef9a9a;
|
||||
@red300: #e57373;
|
||||
@red400: #ef5350;
|
||||
@red500: #f44336;
|
||||
@red600: #e53935;
|
||||
@red700: #d32f2f;
|
||||
@red800: #c62828;
|
||||
@red900: #b71c1c;
|
||||
@redA100: #ff8a80;
|
||||
@redA200: #ff5252;
|
||||
@redA400: #ff1744;
|
||||
@redA700: #d50000;
|
||||
@red: @red500;
|
||||
|
||||
@pink50: #fce4ec;
|
||||
@pink100: #f8bbd0;
|
||||
@pink200: #f48fb1;
|
||||
@pink300: #f06292;
|
||||
@pink400: #ec407a;
|
||||
@pink500: #e91e63;
|
||||
@pink600: #d81b60;
|
||||
@pink700: #c2185b;
|
||||
@pink800: #ad1457;
|
||||
@pink900: #880e4f;
|
||||
@pinkA100: #ff80ab;
|
||||
@pinkA200: #ff4081;
|
||||
@pinkA400: #f50057;
|
||||
@pinkA700: #c51162;
|
||||
@pink: @pink500;
|
||||
|
||||
@purple50: #f3e5f5;
|
||||
@purple100: #e1bee7;
|
||||
@purple200: #ce93d8;
|
||||
@purple300: #ba68c8;
|
||||
@purple400: #ab47bc;
|
||||
@purple500: #9c27b0;
|
||||
@purple600: #8e24aa;
|
||||
@purple700: #7b1fa2;
|
||||
@purple800: #6a1b9a;
|
||||
@purple900: #4a148c;
|
||||
@purpleA100: #ea80fc;
|
||||
@purpleA200: #e040fb;
|
||||
@purpleA400: #d500f9;
|
||||
@purpleA700: #aa00ff;
|
||||
@purple: @purple500;
|
||||
|
||||
@deepPurple50: #ede7f6;
|
||||
@deepPurple100: #d1c4e9;
|
||||
@deepPurple200: #b39ddb;
|
||||
@deepPurple300: #9575cd;
|
||||
@deepPurple400: #7e57c2;
|
||||
@deepPurple500: #673ab7;
|
||||
@deepPurple600: #5e35b1;
|
||||
@deepPurple700: #512da8;
|
||||
@deepPurple800: #4527a0;
|
||||
@deepPurple900: #311b92;
|
||||
@deepPurpleA100: #b388ff;
|
||||
@deepPurpleA200: #7c4dff;
|
||||
@deepPurpleA400: #651fff;
|
||||
@deepPurpleA700: #6200ea;
|
||||
@deepPurple: @deepPurple500;
|
||||
|
||||
@indigo50: #e8eaf6;
|
||||
@indigo100: #c5cae9;
|
||||
@indigo200: #9fa8da;
|
||||
@indigo300: #7986cb;
|
||||
@indigo400: #5c6bc0;
|
||||
@indigo500: #3f51b5;
|
||||
@indigo600: #3949ab;
|
||||
@indigo700: #303f9f;
|
||||
@indigo800: #283593;
|
||||
@indigo900: #1a237e;
|
||||
@indigoA100: #8c9eff;
|
||||
@indigoA200: #536dfe;
|
||||
@indigoA400: #3d5afe;
|
||||
@indigoA700: #304ffe;
|
||||
@indigo: @indigo500;
|
||||
|
||||
@blue50: #e3f2fd;
|
||||
@blue100: #bbdefb;
|
||||
@blue200: #90caf9;
|
||||
@blue300: #64b5f6;
|
||||
@blue400: #42a5f5;
|
||||
@blue500: #2196f3;
|
||||
@blue600: #1e88e5;
|
||||
@blue700: #1976d2;
|
||||
@blue800: #1565c0;
|
||||
@blue900: #0d47a1;
|
||||
@blueA100: #82b1ff;
|
||||
@blueA200: #448aff;
|
||||
@blueA400: #2979ff;
|
||||
@blueA700: #2962ff;
|
||||
@blue: @blue500;
|
||||
|
||||
@lightBlue50: #e1f5fe;
|
||||
@lightBlue100: #b3e5fc;
|
||||
@lightBlue200: #81d4fa;
|
||||
@lightBlue300: #4fc3f7;
|
||||
@lightBlue400: #29b6f6;
|
||||
@lightBlue500: #03a9f4;
|
||||
@lightBlue600: #039be5;
|
||||
@lightBlue700: #0288d1;
|
||||
@lightBlue800: #0277bd;
|
||||
@lightBlue900: #01579b;
|
||||
@lightBlueA100: #80d8ff;
|
||||
@lightBlueA200: #40c4ff;
|
||||
@lightBlueA400: #00b0ff;
|
||||
@lightBlueA700: #0091ea;
|
||||
@lightBlue: @lightBlue500;
|
||||
|
||||
@cyan50: #e0f7fa;
|
||||
@cyan100: #b2ebf2;
|
||||
@cyan200: #80deea;
|
||||
@cyan300: #4dd0e1;
|
||||
@cyan400: #26c6da;
|
||||
@cyan500: #00bcd4;
|
||||
@cyan600: #00acc1;
|
||||
@cyan700: #0097a7;
|
||||
@cyan800: #00838f;
|
||||
@cyan900: #006064;
|
||||
@cyanA100: #84ffff;
|
||||
@cyanA200: #18ffff;
|
||||
@cyanA400: #00e5ff;
|
||||
@cyanA700: #00b8d4;
|
||||
@cyan: @cyan500;
|
||||
|
||||
@teal50: #e0f2f1;
|
||||
@teal100: #b2dfdb;
|
||||
@teal200: #80cbc4;
|
||||
@teal300: #4db6ac;
|
||||
@teal400: #26a69a;
|
||||
@teal500: #009688;
|
||||
@teal600: #00897b;
|
||||
@teal700: #00796b;
|
||||
@teal800: #00695c;
|
||||
@teal900: #004d40;
|
||||
@tealA100: #a7ffeb;
|
||||
@tealA200: #64ffda;
|
||||
@tealA400: #1de9b6;
|
||||
@tealA700: #00bfa5;
|
||||
@teal: @teal500;
|
||||
|
||||
@green50: #e8f5e9;
|
||||
@green100: #c8e6c9;
|
||||
@green200: #a5d6a7;
|
||||
@green300: #81c784;
|
||||
@green400: #66bb6a;
|
||||
@green500: #4caf50;
|
||||
@green600: #43a047;
|
||||
@green700: #388e3c;
|
||||
@green800: #2e7d32;
|
||||
@green900: #1b5e20;
|
||||
@greenA100: #b9f6ca;
|
||||
@greenA200: #69f0ae;
|
||||
@greenA400: #00e676;
|
||||
@greenA700: #00c853;
|
||||
@green: @green500;
|
||||
|
||||
@lightGreen50: #f1f8e9;
|
||||
@lightGreen100: #dcedc8;
|
||||
@lightGreen200: #c5e1a5;
|
||||
@lightGreen300: #aed581;
|
||||
@lightGreen400: #9ccc65;
|
||||
@lightGreen500: #8bc34a;
|
||||
@lightGreen600: #7cb342;
|
||||
@lightGreen700: #689f38;
|
||||
@lightGreen800: #558b2f;
|
||||
@lightGreen900: #33691e;
|
||||
@lightGreenA100: #ccff90;
|
||||
@lightGreenA200: #b2ff59;
|
||||
@lightGreenA400: #76ff03;
|
||||
@lightGreenA700: #64dd17;
|
||||
@lightGreen: @lightGreen500;
|
||||
|
||||
@lime50: #f9fbe7;
|
||||
@lime100: #f0f4c3;
|
||||
@lime200: #e6ee9c;
|
||||
@lime300: #dce775;
|
||||
@lime400: #d4e157;
|
||||
@lime500: #cddc39;
|
||||
@lime600: #c0ca33;
|
||||
@lime700: #afb42b;
|
||||
@lime800: #9e9d24;
|
||||
@lime900: #827717;
|
||||
@limeA100: #f4ff81;
|
||||
@limeA200: #eeff41;
|
||||
@limeA400: #c6ff00;
|
||||
@limeA700: #aeea00;
|
||||
@lime: @lime500;
|
||||
|
||||
@yellow50: #fffde7;
|
||||
@yellow100: #fff9c4;
|
||||
@yellow200: #fff59d;
|
||||
@yellow300: #fff176;
|
||||
@yellow400: #ffee58;
|
||||
@yellow500: #ffeb3b;
|
||||
@yellow600: #fdd835;
|
||||
@yellow700: #fbc02d;
|
||||
@yellow800: #f9a825;
|
||||
@yellow900: #f57f17;
|
||||
@yellowA100: #ffff8d;
|
||||
@yellowA200: #ffff00;
|
||||
@yellowA400: #ffea00;
|
||||
@yellowA700: #ffd600;
|
||||
@yellow: @yellow500;
|
||||
|
||||
@amber50: #fff8e1;
|
||||
@amber100: #ffecb3;
|
||||
@amber200: #ffe082;
|
||||
@amber300: #ffd54f;
|
||||
@amber400: #ffca28;
|
||||
@amber500: #ffc107;
|
||||
@amber600: #ffb300;
|
||||
@amber700: #ffa000;
|
||||
@amber800: #ff8f00;
|
||||
@amber900: #ff6f00;
|
||||
@amberA100: #ffe57f;
|
||||
@amberA200: #ffd740;
|
||||
@amberA400: #ffc400;
|
||||
@amberA700: #ffab00;
|
||||
@amber: @amber500;
|
||||
|
||||
@orange50: #fff3e0;
|
||||
@orange100: #ffe0b2;
|
||||
@orange200: #ffcc80;
|
||||
@orange300: #ffb74d;
|
||||
@orange400: #ffa726;
|
||||
@orange500: #ff9800;
|
||||
@orange600: #fb8c00;
|
||||
@orange700: #f57c00;
|
||||
@orange800: #ef6c00;
|
||||
@orange900: #e65100;
|
||||
@orangeA100: #ffd180;
|
||||
@orangeA200: #ffab40;
|
||||
@orangeA400: #ff9100;
|
||||
@orangeA700: #ff6d00;
|
||||
@orange: @orange500;
|
||||
|
||||
@deepOrange50: #fbe9e7;
|
||||
@deepOrange100: #ffccbc;
|
||||
@deepOrange200: #ffab91;
|
||||
@deepOrange300: #ff8a65;
|
||||
@deepOrange400: #ff7043;
|
||||
@deepOrange500: #ff5722;
|
||||
@deepOrange600: #f4511e;
|
||||
@deepOrange700: #e64a19;
|
||||
@deepOrange800: #d84315;
|
||||
@deepOrange900: #bf360c;
|
||||
@deepOrangeA100: #ff9e80;
|
||||
@deepOrangeA200: #ff6e40;
|
||||
@deepOrangeA400: #ff3d00;
|
||||
@deepOrangeA700: #dd2c00;
|
||||
@deepOrange: @deepOrange500;
|
||||
|
||||
@brown50: #efebe9;
|
||||
@brown100: #d7ccc8;
|
||||
@brown200: #bcaaa4;
|
||||
@brown300: #a1887f;
|
||||
@brown400: #8d6e63;
|
||||
@brown500: #795548;
|
||||
@brown600: #6d4c41;
|
||||
@brown700: #5d4037;
|
||||
@brown800: #4e342e;
|
||||
@brown900: #3e2723;
|
||||
@brown: @brown500;
|
||||
|
||||
@blueGrey50: #eceff1;
|
||||
@blueGrey100: #cfd8dc;
|
||||
@blueGrey200: #b0bec5;
|
||||
@blueGrey300: #90a4ae;
|
||||
@blueGrey400: #78909c;
|
||||
@blueGrey500: #607d8b;
|
||||
@blueGrey600: #546e7a;
|
||||
@blueGrey700: #455a64;
|
||||
@blueGrey800: #37474f;
|
||||
@blueGrey900: #263238;
|
||||
@blueGrey: @blueGrey500;
|
||||
|
||||
@grey50: #fafafa;
|
||||
@grey100: #f5f5f5;
|
||||
@grey200: #eeeeee;
|
||||
@grey300: #e0e0e0;
|
||||
@grey400: #bdbdbd;
|
||||
@grey500: #9e9e9e;
|
||||
@grey600: #757575;
|
||||
@grey700: #616161;
|
||||
@grey800: #424242;
|
||||
@grey900: #212121;
|
||||
@grey: @grey500;
|
||||
|
||||
@black: #000000;
|
||||
@white: #ffffff;
|
||||
|
||||
@transparent: rgba(0, 0, 0, 0);
|
||||
@fullBlack: rgba(0, 0, 0, 1);
|
||||
@darkBlack: rgba(0, 0, 0, 0.87);
|
||||
@lightBlack: rgba(0, 0, 0, 0.54);
|
||||
@minBlack: rgba(0, 0, 0, 0.26);
|
||||
@faintBlack: rgba(0, 0, 0, 0.12);
|
||||
@fullWhite: rgba(255, 255, 255, 1);
|
||||
@darkWhite: rgba(255, 255, 255, 0.87);
|
||||
@lightWhite: rgba(255, 255, 255, 0.54);
|
||||
297
src/application/assets/less/mixin/index.less
Normal file
297
src/application/assets/less/mixin/index.less
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
.clearfix() {
|
||||
&:after,
|
||||
&:before {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.hairline(@position, @color) when (@position = top) {
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: @color;
|
||||
display: block;
|
||||
z-index: 15;
|
||||
// .transform-origin(50% 0%);
|
||||
html.pixel-ratio-2 & {
|
||||
.transform(scaleY(0.5));
|
||||
}
|
||||
|
||||
html.pixel-ratio-3 & {
|
||||
.transform(scaleY(0.33));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hairline(@position, @color) when (@position = left) {
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: @color;
|
||||
display: block;
|
||||
z-index: 15;
|
||||
// .transform-origin(0% 50%);
|
||||
html.pixel-ratio-2 & {
|
||||
.transform(scaleX(0.5));
|
||||
}
|
||||
|
||||
html.pixel-ratio-3 & {
|
||||
.transform(scaleX(0.33));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.hairline(@position, @color) when (@position = bottom) {
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: auto;
|
||||
top: auto;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: @color;
|
||||
display: block;
|
||||
z-index: 15;
|
||||
|
||||
html.pixel-ratio-2 & {
|
||||
.transform(scaleY(0.5));
|
||||
}
|
||||
|
||||
html.pixel-ratio-3 & {
|
||||
.transform(scaleY(0.33));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hairline(@position, @color) when (@position = right) {
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: @color;
|
||||
display: block;
|
||||
z-index: 15;
|
||||
// .transform-origin(100% 50%);
|
||||
html.pixel-ratio-2 & {
|
||||
.transform(scaleX(0.5));
|
||||
}
|
||||
|
||||
html.pixel-ratio-3 & {
|
||||
.transform(scaleX(0.33));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For right and bottom
|
||||
.hairline-remove(@position) when not (@position = left) and not (@position = top) {
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// For left and top
|
||||
.hairline-remove(@position) when not (@position = right) and not (@position = bottom) {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// For right and bottom
|
||||
.hairline-color(@position, @color) when not (@position = left) and not (@position = top) {
|
||||
&:after {
|
||||
background-color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
// For left and top
|
||||
.hairline-color(@position, @color) when not (@position = right) and not (@position = bottom) {
|
||||
&:before {
|
||||
background-color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
// Encoded SVG Background
|
||||
.encoded-svg-background(@svg) {
|
||||
@url: `encodeURIComponent(@{svg})`;
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,@{url}");
|
||||
}
|
||||
|
||||
// Preserve3D
|
||||
.preserve3d() {
|
||||
-webkit-transform-style: preserve-3d;
|
||||
-moz-transform-style: preserve-3d;
|
||||
-ms-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
// Shadow
|
||||
.depth(@level:1) {
|
||||
& when (@level = 0) {
|
||||
box-shadow: none;
|
||||
}
|
||||
& when (@level = 1) {
|
||||
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 1px 3px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 2) {
|
||||
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 3) {
|
||||
box-shadow: 0 3px 3px -2px rgba(0, 0, 0, .2), 0 3px 4px 0 rgba(0, 0, 0, .14), 0 1px 8px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 4) {
|
||||
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, .2), 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 5) {
|
||||
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 5px 8px 0 rgba(0, 0, 0, .14), 0 1px 14px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 6) {
|
||||
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 7) {
|
||||
box-shadow: 0 4px 5px -2px rgba(0, 0, 0, .2), 0 7px 10px 1px rgba(0, 0, 0, .14), 0 2px 16px 1px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 8) {
|
||||
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 9) {
|
||||
box-shadow: 0 5px 6px -3px rgba(0, 0, 0, .2), 0 9px 12px 1px rgba(0, 0, 0, .14), 0 3px 16px 2px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 10) {
|
||||
box-shadow: 0 6px 6px -3px rgba(0, 0, 0, .2), 0 10px 14px 1px rgba(0, 0, 0, .14), 0 4px 18px 3px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 11) {
|
||||
box-shadow: 0 6px 7px -4px rgba(0, 0, 0, .2), 0 11px 15px 1px rgba(0, 0, 0, .14), 0 4px 20px 3px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 12) {
|
||||
box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 12px 17px 2px rgba(0, 0, 0, .14), 0 5px 22px 4px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 13) {
|
||||
box-shadow: 0 7px 8px -4px rgba(0, 0, 0, .2), 0 13px 19px 2px rgba(0, 0, 0, .14), 0 5px 24px 4px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 14) {
|
||||
box-shadow: 0 7px 9px -4px rgba(0, 0, 0, .2), 0 14px 21px 2px rgba(0, 0, 0, .14), 0 5px 26px 4px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 15) {
|
||||
box-shadow: 0 8px 9px -5px rgba(0, 0, 0, .2), 0 15px 22px 2px rgba(0, 0, 0, .14), 0 6px 28px 5px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 16) {
|
||||
box-shadow: 0 8px 10px -5px rgba(0, 0, 0, .2), 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 17) {
|
||||
box-shadow: 0 8px 11px -5px rgba(0, 0, 0, .2), 0 17px 26px 2px rgba(0, 0, 0, .14), 0 6px 32px 5px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 18) {
|
||||
box-shadow: 0 9px 11px -5px rgba(0, 0, 0, .2), 0 18px 28px 2px rgba(0, 0, 0, .14), 0 7px 34px 6px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 19) {
|
||||
box-shadow: 0 9px 12px -6px rgba(0, 0, 0, .2), 0 19px 29px 2px rgba(0, 0, 0, .14), 0 7px 36px 6px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 20) {
|
||||
box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 20px 31px 3px rgba(0, 0, 0, .14), 0 8px 38px 7px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 21) {
|
||||
box-shadow: 0 10px 13px -6px rgba(0, 0, 0, .2), 0 21px 33px 3px rgba(0, 0, 0, .14), 0 8px 40px 7px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 22) {
|
||||
box-shadow: 0 10px 14px -6px rgba(0, 0, 0, .2), 0 22px 35px 3px rgba(0, 0, 0, .14), 0 8px 42px 7px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 23) {
|
||||
box-shadow: 0 11px 14px -7px rgba(0, 0, 0, .2), 0 23px 36px 3px rgba(0, 0, 0, .14), 0 9px 44px 8px rgba(0, 0, 0, .12);
|
||||
}
|
||||
& when (@level = 24) {
|
||||
box-shadow: 0 11px 15px -7px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12);
|
||||
}
|
||||
// & when (@level = 1) {
|
||||
// box-shadow: rgba(0, 0, 0, 0.117647) 0px 1px 6px, rgba(0, 0, 0, 0.117647) 0px 1px 4px;
|
||||
// }
|
||||
// & when (@level = 2) {
|
||||
// box-shadow: rgba(0, 0, 0, 0.156863) 0px 3px 10px, rgba(0, 0, 0, 0.227451) 0px 3px 10px;
|
||||
// }
|
||||
// & when (@level = 3) {
|
||||
// box-shadow: rgba(0, 0, 0, 0.188235) 0px 10px 30px, rgba(0, 0, 0, 0.227451) 0px 6px 10px;
|
||||
// }
|
||||
// & when (@level = 4) {
|
||||
// box-shadow: rgba(0, 0, 0, 0.247059) 0px 14px 45px, rgba(0, 0, 0, 0.219608) 0px 10px 18px;
|
||||
// }
|
||||
// & when (@level = 5) {
|
||||
// box-shadow: rgba(0, 0, 0, 0.298039) 0px 19px 60px, rgba(0, 0, 0, 0.219608) 0px 15px 20px;
|
||||
// }
|
||||
}
|
||||
|
||||
// Highlighted Links
|
||||
.active-highlight(@color:rgba(255, 255, 255, 0.15)) {
|
||||
&:before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: @color;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 100% 100%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
.transition(600ms);
|
||||
}
|
||||
&.active-state:before,
|
||||
html:not(.watch-active-state) &:active:before {
|
||||
opacity: 1;
|
||||
.transition(150ms);
|
||||
}
|
||||
}
|
||||
|
||||
.active-highlight-color(@color) {
|
||||
&:before {
|
||||
background-image: -webkit-radial-gradient(center, circle cover, @color 66%, rgba(red(@color), green(@color), blue(@color), 0) 66%);
|
||||
background-image: radial-gradient(circle at center, @color 66%, rgba(red(@color), green(@color), blue(@color), 0) 66%);
|
||||
}
|
||||
}
|
||||
|
||||
// No Scrollbar
|
||||
.no-scrollbar() {
|
||||
&::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ellipsis() {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
1
src/application/assets/less/page/.pydio
Normal file
1
src/application/assets/less/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
88b35c97-9424-4ba5-a009-00ba813fb0fa
|
||||
8
src/application/assets/less/page/About.less
Normal file
8
src/application/assets/less/page/About.less
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
@import "../mixin/_";
|
||||
|
||||
.About {
|
||||
}
|
||||
11
src/application/assets/less/page/Home.less
Normal file
11
src/application/assets/less/page/Home.less
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
@import "../mixin/_";
|
||||
|
||||
.home {
|
||||
h1 {
|
||||
color: @theme-color;
|
||||
}
|
||||
}
|
||||
1
src/application/components/.pydio
Normal file
1
src/application/components/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
50ce6829-d7b9-49e3-91b2-ead60352a710
|
||||
27
src/application/components/HelloWorld.tsx
Normal file
27
src/application/components/HelloWorld.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class HelloWorld extends Vue {
|
||||
|
||||
@Prop({ default: 'default value' }) public readonly msg!: string;
|
||||
|
||||
private mounted () {
|
||||
}
|
||||
|
||||
private showAlert () {
|
||||
alert("showAlert")
|
||||
}
|
||||
|
||||
private render () {
|
||||
return (
|
||||
<div class="hello">
|
||||
<h1>{ this.msg }</h1>
|
||||
<h2 onClick={ () => this.showAlert() }>HelloWorld组件点击事件:{ this.msg }</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
1
src/application/logic/.pydio
Normal file
1
src/application/logic/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e99cbd4e-9620-464d-ad1f-a319540d4885
|
||||
39
src/application/logic/base.logic.ts
Normal file
39
src/application/logic/base.logic.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import { VueCustomize } from "@logic/base.type";
|
||||
|
||||
/**
|
||||
* logic 层的父类
|
||||
* 主要实现 页面公共参数和ajax方法的地方
|
||||
*/
|
||||
export class BaseLogic {
|
||||
|
||||
// 保存Vue对象
|
||||
public _!: any;
|
||||
|
||||
public page: number = 1;
|
||||
public total_page: number = 1;
|
||||
public count: number = 10;
|
||||
|
||||
// $refs 类型
|
||||
public $refs!: { [key: string]: Vue | Element | Vue[] | Element[] };
|
||||
public $confirm!: any;
|
||||
public $message!: any;
|
||||
// 请求的总页数
|
||||
public TotalCount!: string;
|
||||
|
||||
// 请求参数
|
||||
public PageParams: { limit: number, page: number } = {
|
||||
limit : 10, page : 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 【页面中公共的多次被用到的接口】可以在这个父类中定义,
|
||||
* 子类logic中直接使用,避免方法的冗余
|
||||
* @constructor
|
||||
*/
|
||||
public async DepartmentListRequest(): Promise<void> {
|
||||
}
|
||||
}
|
||||
31
src/application/logic/base.type.ts
Normal file
31
src/application/logic/base.type.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
|
||||
import { Vue } from 'vue-property-decorator';
|
||||
|
||||
// 组件中路由元信息的类型
|
||||
export interface RouterMeta {
|
||||
title?: string,
|
||||
isLogin?: boolean,
|
||||
showNav: boolean,
|
||||
}
|
||||
|
||||
// 对 logic 的部分通用变量和方法进行约束的接口
|
||||
export interface Methods {
|
||||
StartUp(): Promise<void>;
|
||||
PageLeave?(): Promise<void>;
|
||||
}
|
||||
|
||||
// tsx组件中一些方法找不到声明文件,需要在这里定义
|
||||
export class VueCustomize extends Vue {
|
||||
$setToken!: (token: string) => void
|
||||
$test!: () => string
|
||||
$formatDate!: (data: number) => string
|
||||
$isLogin!: () => void
|
||||
$setStorage!: (key: String, data: any) => void
|
||||
$getStorage!: (key: String) => any
|
||||
}
|
||||
|
||||
1
src/application/logic/page/.pydio
Normal file
1
src/application/logic/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
d3709c65-896f-4fce-83c8-72248f572930
|
||||
18
src/application/logic/page/about.logic.ts
Normal file
18
src/application/logic/page/about.logic.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import { Autowired } from "@ann/ioc.annotation";
|
||||
import { BaseLogic } from "@logic/base.logic";
|
||||
import { Methods, VueCustomize } from "@logic/base.type";
|
||||
|
||||
export class AboutLogic <T extends VueCustomize> extends BaseLogic implements Methods {
|
||||
|
||||
constructor(_: T) {
|
||||
super();
|
||||
this._ = _
|
||||
}
|
||||
|
||||
public async StartUp (): Promise<void> {
|
||||
}
|
||||
}
|
||||
51
src/application/logic/page/home.logic.ts
Normal file
51
src/application/logic/page/home.logic.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
import { Autowired } from "@ann/ioc.annotation";
|
||||
import { UserService, ResponseType } from "@service/user.service";
|
||||
import { BaseLogic } from "@logic/base.logic";
|
||||
import { Methods, VueCustomize } from "@logic/base.type";
|
||||
|
||||
export class HomeLogic<T extends VueCustomize> extends BaseLogic implements Methods {
|
||||
|
||||
@Autowired()
|
||||
public readonly UserService!: UserService;
|
||||
|
||||
public title: string = "12222";
|
||||
|
||||
public List: ResponseType[] = [];
|
||||
|
||||
// constructor() {
|
||||
// super();
|
||||
// }
|
||||
|
||||
constructor(_: T) {
|
||||
super();
|
||||
console.log("constructor params: ", _);
|
||||
this._ = _
|
||||
}
|
||||
|
||||
/**
|
||||
* 在这里面调用需要打开页面就执行的ajax请求
|
||||
* @constructor
|
||||
*/
|
||||
public async StartUp (): Promise<void> {
|
||||
await this.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求首页数据
|
||||
*/
|
||||
public async getData (): Promise<void> {
|
||||
this.List = await this.UserService.UserList({ type: 1, page: 1 });
|
||||
console.log("this.List: ", this.List);
|
||||
|
||||
// let res = await this.UserService.login({ type: 2, page: 2 });
|
||||
// console.log("this.res: ", res);
|
||||
|
||||
//
|
||||
// await this._.$router.push('/About')
|
||||
}
|
||||
}
|
||||
1
src/application/page/.pydio
Normal file
1
src/application/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
9b3b1288-db3f-4438-9341-a54217a9d678
|
||||
39
src/application/page/about.tsx
Normal file
39
src/application/page/about.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
import "@pageLess/About.less";
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { AboutLogic } from "@logic/page/about.logic";
|
||||
import { RawLocation, Route } from 'vue-router';
|
||||
import { VueCustomize, RouterMeta } from "@logic/base.type";
|
||||
import { Autowired } from "@ann/ioc.annotation";
|
||||
|
||||
@Component
|
||||
export default class About extends VueCustomize {
|
||||
|
||||
@Autowired(About)
|
||||
private logic!: AboutLogic<About>
|
||||
|
||||
private RouterMeta: RouterMeta = {
|
||||
title: '关于我页面',
|
||||
showNav: true,
|
||||
isLogin: true
|
||||
};
|
||||
|
||||
public async created () {
|
||||
await this.logic.StartUp();
|
||||
};
|
||||
|
||||
// 路由拦截器
|
||||
beforeRouteEnter (to: Route, from: Route, next: (to?: RawLocation | false | ((vm: Vue) => void)) => void) {
|
||||
next();
|
||||
}
|
||||
|
||||
protected render () {
|
||||
return (
|
||||
<div class="About">
|
||||
About
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
78
src/application/page/home.tsx
Normal file
78
src/application/page/home.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
|
||||
import "@pageLess/Home.less";
|
||||
import { Component } from 'vue-property-decorator';
|
||||
import HelloWorld from '@components/HelloWorld';
|
||||
import { HomeLogic } from "@logic/page/home.logic";
|
||||
import { Getter } from "vuex-class";
|
||||
import { IndexUtils } from "@utils/index.utils";
|
||||
import { Autowired } from "@ann/ioc.annotation";
|
||||
import { VueCustomize, RouterMeta } from "@logic/base.type";
|
||||
// import { toClass, toInterface, configFilePath } from "json-class-interface";
|
||||
|
||||
|
||||
@Component
|
||||
export default class Home extends VueCustomize {
|
||||
|
||||
public RouterMeta: RouterMeta = {
|
||||
title: '首页',
|
||||
showNav: true,
|
||||
isLogin: true
|
||||
}
|
||||
|
||||
@Autowired(Home)
|
||||
private logic!: HomeLogic<Home>
|
||||
|
||||
@Autowired()
|
||||
public readonly Utils!: IndexUtils;
|
||||
|
||||
@Getter('GettersTabsContentArr')
|
||||
public readonly getterFoo!: Array<any>;
|
||||
|
||||
|
||||
public async created() {
|
||||
await this.logic.StartUp();
|
||||
}
|
||||
|
||||
public ClickButton(): JSX.Element {
|
||||
return (
|
||||
<a-button type="primary" onClick={async () => {
|
||||
console.log("按钮被点击22222");
|
||||
}}>
|
||||
Primary
|
||||
</a-button>
|
||||
)
|
||||
}
|
||||
|
||||
public back() {
|
||||
console.log("qqqq");
|
||||
}
|
||||
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<div class="home">
|
||||
<img src="/img/baidu.png" alt="" />
|
||||
<h1 on-click={this.back}>111</h1>
|
||||
<HelloWorld msg="向HelloWorld props传参" />
|
||||
<input type="text" v-model={this.logic.title} />
|
||||
<h2>{this.logic.title}</h2>
|
||||
{this.ClickButton()}
|
||||
<ul>
|
||||
{
|
||||
this.logic.List.map(v => {
|
||||
return <li>
|
||||
{v.user_id}
|
||||
{v.user_name}
|
||||
</li>
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
1
src/core/.pydio
Normal file
1
src/core/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
d7cca889-05d9-4311-b88d-a72968fc4581
|
||||
1
src/core/annotation/.pydio
Normal file
1
src/core/annotation/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
c2c6ec3b-b4b9-42f4-92b0-4e10d1e027a6
|
||||
1
src/core/annotation/JsonToClass/.pydio
Normal file
1
src/core/annotation/JsonToClass/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
cbff3cfc-8ebf-4480-a698-e4a7c6040408
|
||||
103
src/core/annotation/JsonToClass/decorators.ts
Normal file
103
src/core/annotation/JsonToClass/decorators.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { setStore } from './store';
|
||||
import { StoreItemType, StoreItemOptions } from './typing';
|
||||
|
||||
export function serialize(serializer: StoreItemType['serializer'], disallowIgnore = false) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
serializer,
|
||||
disallowIgnoreSerializer: disallowIgnore,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function deserialize(deserializer: StoreItemType['deserializer'], disallowIgnore = false) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
deserializer,
|
||||
disallowIgnoreDeserializer: disallowIgnore,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function beforeDeserialize(beforeDeserializer: StoreItemType['beforeDeserializer'], disallowIgnore = false) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
beforeDeserializer,
|
||||
disallowIgnoreBeforeDeserializer: disallowIgnore,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function afterSerialize(afterSerializer: StoreItemType['afterSerializer'], disallowIgnore = false) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
afterSerializer,
|
||||
disallowIgnoreAfterSerializer: disallowIgnore,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function serializeTarget() {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
serializeTarget: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function property(originalKey?: string, targetClass?: StoreItemType['targetClass'], isOptional = false) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
|
||||
// propertyKey 使用装饰器的 属性 名称,例如:id
|
||||
// originalKey json 数据的 key,例如: { i: 1, name: 123 } 中的 i
|
||||
// console.log("property target: ", target);
|
||||
// console.log("property propertyKey: ", propertyKey);
|
||||
// console.log("property originalKey: ", originalKey);
|
||||
|
||||
const config: StoreItemOptions = {
|
||||
originalKey: originalKey || propertyKey,
|
||||
key: propertyKey,
|
||||
};
|
||||
if (targetClass) {
|
||||
config.targetClass = targetClass.prototype.constructor;
|
||||
// console.log("有 targetClass ...");
|
||||
// console.log("config: ",config.targetClass);
|
||||
}
|
||||
if (isOptional) {
|
||||
config.optional = isOptional;
|
||||
}
|
||||
setStore(target, config);
|
||||
};
|
||||
}
|
||||
|
||||
export function typed(targetClass: StoreItemType['targetClass']) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
targetClass,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function optional() {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
optional: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function defaultValue(value: any) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
setStore(target, {
|
||||
key: propertyKey,
|
||||
default: value,
|
||||
});
|
||||
};
|
||||
}
|
||||
4
src/core/annotation/JsonToClass/index.ts
Normal file
4
src/core/annotation/JsonToClass/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './to-class';
|
||||
export * from './to-plain';
|
||||
export * from './decorators';
|
||||
export * from './typing';
|
||||
27
src/core/annotation/JsonToClass/store.ts
Normal file
27
src/core/annotation/JsonToClass/store.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
// @ts-nocheck
|
||||
import { StoreItemOptions, StoreItemType } from './typing';
|
||||
|
||||
const store = new Map<Function, Map<string, StoreItemType>>();
|
||||
|
||||
export const originalKeyStores = new Map<Function, Map<string, StoreItemOptions[]>>();
|
||||
export const keyStores = new Map<Function, Map<string, StoreItemType>>();
|
||||
|
||||
export const setStore = (target: Function, options: StoreItemOptions) => {
|
||||
const { key, ...rest } = options;
|
||||
// 获取被装饰 属性 所在类的 构造函数,作为存储 Map 的 key
|
||||
const storeKey = target.constructor;
|
||||
// 判断是否已经存储过,如果已经存在 get 获取之前存在的 key 对应的数值,不存在 创建新的 map
|
||||
const targetStore = store.has(storeKey) ? store.get(storeKey) : new Map<string, StoreItemType>();
|
||||
|
||||
// 判断被装饰的 属性(id,name)是否已经存在, 存在 获取 key 对应 的 StoreItemType,不存在为 {}
|
||||
let value = targetStore.has(key) ? targetStore.get(key) : {};
|
||||
value = {
|
||||
...value,
|
||||
...rest,
|
||||
};
|
||||
|
||||
targetStore.set(key, value);
|
||||
store.set(storeKey, targetStore);
|
||||
};
|
||||
|
||||
export default store;
|
||||
79
src/core/annotation/JsonToClass/to-class.ts
Normal file
79
src/core/annotation/JsonToClass/to-class.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import isArray from 'isarray';
|
||||
import { getOriginalKeyStore, isUndefined, isNullOrUndefined } from './utils';
|
||||
import { JosnType, StoreItemOptions, BasicClass, ToClassOptions } from './typing';
|
||||
|
||||
export const arrayItemToClass = <T>(arrayVal: any[], Clazz: BasicClass<T>, options: ToClassOptions): any => {
|
||||
return arrayVal.map((v: any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
isArray(v) ? arrayItemToClass(v, Clazz, options) : objectToClass(v, Clazz, options),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param jsonObj json 数据
|
||||
* @param Clazz 传入类的 构造函数
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
const objectToClass = <T>(jsonObj: Record<string, any>, Clazz: BasicClass<T>, options: ToClassOptions): T => {
|
||||
// 初始化 传入的类
|
||||
const instance: any = new Clazz();
|
||||
const originalKeyStore = getOriginalKeyStore(Clazz);
|
||||
|
||||
originalKeyStore.forEach((propertiesOptions: StoreItemOptions[], originalKey) => {
|
||||
const originalValue = jsonObj[originalKey];
|
||||
propertiesOptions.forEach((storeItemoptions: StoreItemOptions) => {
|
||||
// console.log("propertiesOptions storeItemoptions: ", storeItemoptions);
|
||||
const { key, beforeDeserializer, deserializer, targetClass, optional } = storeItemoptions;
|
||||
|
||||
const disallowIgnoreDeserializer = storeItemoptions.disallowIgnoreDeserializer || !options.ignoreDeserializer;
|
||||
const disallowIgnoreBeforeDeserializer =
|
||||
storeItemoptions.disallowIgnoreBeforeDeserializer || !options.ignoreBeforeDeserializer;
|
||||
|
||||
const isValueNotExist = options.distinguishNullAndUndefined ? isUndefined : isNullOrUndefined;
|
||||
let value = originalValue;
|
||||
if (isValueNotExist(value)) {
|
||||
if (!isValueNotExist(storeItemoptions.default)) {
|
||||
instance[key] = storeItemoptions.default;
|
||||
return;
|
||||
}
|
||||
if (!optional) {
|
||||
throw new Error(`Can't map '${originalKey}' to ${Clazz.name}.${key}, property '${originalKey}' not found`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
value =
|
||||
beforeDeserializer && disallowIgnoreBeforeDeserializer
|
||||
? beforeDeserializer(value, instance, jsonObj, options)
|
||||
: value;
|
||||
if (value && targetClass) {
|
||||
if (isArray(value)) {
|
||||
value = arrayItemToClass(value, targetClass, options);
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
value = toClass(value, targetClass, options);
|
||||
}
|
||||
}
|
||||
instance[key] =
|
||||
deserializer && disallowIgnoreDeserializer ? deserializer(value, instance, jsonObj, options) : value;
|
||||
});
|
||||
});
|
||||
return instance;
|
||||
};
|
||||
|
||||
export const toClasses = <T>(rawJson: JosnType[], Clazz: BasicClass<T>, options: ToClassOptions = {}): T[] => {
|
||||
if (!isArray(rawJson)) {
|
||||
throw new Error(`rawJson ${rawJson} must be an array`);
|
||||
}
|
||||
const { constructor } = Clazz.prototype;
|
||||
return rawJson.map((item: object) => objectToClass<T>(item, constructor, options));
|
||||
};
|
||||
|
||||
export const toClass = <T>(rawJson: JosnType, Clazz: BasicClass<T>, options: ToClassOptions = {}): T => {
|
||||
const { constructor } = Clazz.prototype;
|
||||
return objectToClass<T>(rawJson as object, constructor, options);
|
||||
};
|
||||
|
||||
export const JsontoClass = toClass
|
||||
export const JsontoClasses = toClasses
|
||||
55
src/core/annotation/JsonToClass/to-plain.ts
Normal file
55
src/core/annotation/JsonToClass/to-plain.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// @ts-nocheck
|
||||
import isArray from 'isarray';
|
||||
import { getKeyStore, isUndefined, isNullOrUndefined } from './utils';
|
||||
import { JosnType, BasicClass, StoreItemType, ToPlainOptions } from './typing';
|
||||
|
||||
export const arrayItemToObject = <T>(arrayVal: any[], Clazz: BasicClass<T>, options: ToPlainOptions): any => {
|
||||
return arrayVal.map((v: any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
isArray(v) ? arrayItemToObject(v, Clazz, options) : classToObject(v, Clazz, options),
|
||||
);
|
||||
};
|
||||
|
||||
const classToObject = <T>(instance: JosnType, Clazz: BasicClass<T>, options: ToPlainOptions): JosnType => {
|
||||
const obj: JosnType = {};
|
||||
const keyStore = getKeyStore(Clazz);
|
||||
keyStore.forEach((propertiesOption: StoreItemType, key: keyof JosnType) => {
|
||||
const isValueNotExist = options.distinguishNullAndUndefined ? isUndefined : isNullOrUndefined;
|
||||
const instanceValue = isValueNotExist(instance[key]) ? propertiesOption.default : instance[key];
|
||||
const { originalKey, afterSerializer, serializer, targetClass, optional } = propertiesOption;
|
||||
const disallowIgnoreSerializer = propertiesOption.disallowIgnoreSerializer || !options.ignoreSerializer;
|
||||
const disallowIgnoreAfterSerializer =
|
||||
propertiesOption.disallowIgnoreAfterSerializer || !options.ignoreAfterSerializer;
|
||||
|
||||
if (isValueNotExist(instanceValue)) {
|
||||
if (!optional) {
|
||||
throw new Error(`Property '${Clazz.name}.${key}' not found`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let value =
|
||||
serializer && disallowIgnoreSerializer ? serializer(instanceValue, instance, obj, options) : instanceValue;
|
||||
if (value && targetClass) {
|
||||
if (isArray(value)) {
|
||||
value = arrayItemToObject(value, targetClass, options);
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
value = toPlain(value, targetClass, options);
|
||||
}
|
||||
}
|
||||
obj[originalKey] =
|
||||
afterSerializer && disallowIgnoreAfterSerializer ? afterSerializer(value, instance, obj, options) : value;
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
export const toPlains = <T>(instances: (T | JosnType)[], Clazz: BasicClass<T>, options: ToPlainOptions = {}): any[] => {
|
||||
if (!isArray(instances)) {
|
||||
throw new Error(`${Clazz} instances must be an array`);
|
||||
}
|
||||
return instances.map((item: JosnType) => classToObject<T>(item, Clazz, options));
|
||||
};
|
||||
|
||||
export const toPlain = <T>(instance: T | JosnType, Clazz: BasicClass<T>, options: ToPlainOptions = {}): any => {
|
||||
return classToObject<T>(instance, Clazz, options);
|
||||
};
|
||||
38
src/core/annotation/JsonToClass/typing.ts
Normal file
38
src/core/annotation/JsonToClass/typing.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export type StoreItemType = {
|
||||
originalKey?: string;
|
||||
targetClass?: BasicClass;
|
||||
serializer?: (value: any, instance: any, origin: any, options: ToPlainOptions) => any;
|
||||
disallowIgnoreSerializer?: boolean;
|
||||
afterSerializer?: (value: any, instance: any, origin: any, options: ToPlainOptions) => any;
|
||||
disallowIgnoreAfterSerializer?: boolean;
|
||||
deserializer?: (value: any, instance: any, origin: any, options: ToClassOptions) => any;
|
||||
disallowIgnoreDeserializer?: boolean;
|
||||
beforeDeserializer?: (value: any, instance: any, origin: any, options: ToClassOptions) => any;
|
||||
disallowIgnoreBeforeDeserializer?: boolean;
|
||||
default?: any;
|
||||
autoTypeDetection?: boolean;
|
||||
optional?: boolean;
|
||||
serializeTarget?: boolean;
|
||||
};
|
||||
|
||||
export type StoreItemOptions = StoreItemType & {
|
||||
key: string;
|
||||
};
|
||||
|
||||
export type BasicClass<T = any> = {
|
||||
new (...args: any[]): T;
|
||||
};
|
||||
|
||||
export type JosnType = { [key: string]: any };
|
||||
|
||||
export interface ToClassOptions {
|
||||
ignoreDeserializer?: boolean;
|
||||
ignoreBeforeDeserializer?: boolean;
|
||||
distinguishNullAndUndefined?: boolean;
|
||||
}
|
||||
|
||||
export interface ToPlainOptions {
|
||||
ignoreSerializer?: boolean;
|
||||
ignoreAfterSerializer?: boolean;
|
||||
distinguishNullAndUndefined?: boolean;
|
||||
}
|
||||
78
src/core/annotation/JsonToClass/utils.ts
Normal file
78
src/core/annotation/JsonToClass/utils.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
// @ts-nocheck
|
||||
import { StoreItemOptions, StoreItemType, BasicClass } from './typing';
|
||||
import store, { keyStores, originalKeyStores } from './store';
|
||||
|
||||
export const isNull = (val: any) => val === null;
|
||||
|
||||
export const isUndefined = (val: any) => val === undefined;
|
||||
|
||||
export const isNullOrUndefined = (val: any) => isNull(val) || isUndefined(val);
|
||||
|
||||
export const getOriginalKeyStore = <T>(Clazz: BasicClass<T>) => {
|
||||
let curLayer = Clazz;
|
||||
|
||||
const cacheOriginalKeyStore = originalKeyStores.get(curLayer);
|
||||
|
||||
// console.log("cacheOriginalKeyStore: ", cacheOriginalKeyStore);
|
||||
|
||||
if (cacheOriginalKeyStore) {
|
||||
return cacheOriginalKeyStore;
|
||||
}
|
||||
const originalKeyStore = new Map<string, StoreItemOptions[]>();
|
||||
while (curLayer.name && curLayer.prototype) {
|
||||
const { constructor } = curLayer.prototype;
|
||||
const targetStore = store.get(constructor);
|
||||
// console.log("targetStore: ", targetStore);
|
||||
|
||||
if (targetStore) {
|
||||
targetStore.forEach((storeItem, key) => {
|
||||
const item = {
|
||||
key,
|
||||
...storeItem,
|
||||
};
|
||||
|
||||
// console.log("item: ", item);
|
||||
|
||||
if (!originalKeyStore.has(storeItem.originalKey)) {
|
||||
originalKeyStore.set(storeItem.originalKey, [item]);
|
||||
} else {
|
||||
const exists = originalKeyStore.get(storeItem.originalKey);
|
||||
if (!exists.find((exist: StoreItemOptions) => exist.key === key)) {
|
||||
originalKeyStore.set(storeItem.originalKey, [...originalKeyStore.get(storeItem.originalKey), item]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
curLayer = Object.getPrototypeOf(constructor);
|
||||
}
|
||||
originalKeyStores.set(Clazz, originalKeyStore);
|
||||
return originalKeyStore;
|
||||
};
|
||||
|
||||
export const getKeyStore = <T>(Clazz: BasicClass<T>) => {
|
||||
const cacheKeyStore = keyStores.get(Clazz);
|
||||
if (cacheKeyStore) {
|
||||
return cacheKeyStore;
|
||||
}
|
||||
const keyStore = new Map<string, StoreItemType>();
|
||||
const originalKeyStore = getOriginalKeyStore(Clazz);
|
||||
originalKeyStore.forEach(storeItems => {
|
||||
const [firstStoreItem] = storeItems;
|
||||
if (storeItems.length === 1) {
|
||||
keyStore.set(firstStoreItem.key, firstStoreItem);
|
||||
} else {
|
||||
const hasStoreItems = storeItems.filter(storeItem => storeItem.serializeTarget);
|
||||
if (hasStoreItems.length !== 1) {
|
||||
throw new Error(
|
||||
`Only one of keys(${storeItems.map(storeItem => storeItem.key).join(', ')}) in ${
|
||||
Clazz.name
|
||||
} can contain a serializeTarget when use toPlain`,
|
||||
);
|
||||
}
|
||||
const hasStoreItem = hasStoreItems[0];
|
||||
keyStore.set(hasStoreItem.key, hasStoreItem);
|
||||
}
|
||||
});
|
||||
keyStores.set(Clazz, keyStore);
|
||||
return keyStore;
|
||||
};
|
||||
120
src/core/annotation/http.annotation.ts
Normal file
120
src/core/annotation/http.annotation.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2023. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
|
||||
import { ConfigureConfig } from "@config/configure.config";
|
||||
import AxioDao from "@dao/index.dao";
|
||||
import { JsontoClass, JsontoClasses } from "@ann/JsonToClass";
|
||||
|
||||
|
||||
interface ParamsType {
|
||||
url?: string;
|
||||
useHandle?: boolean;
|
||||
header?: { [key: string]: any };
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 接口返回 数据所对应的 类,在类的方法上定义元数据,key为mapper,值为 传入的 type
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
export function Mapper(type: { new(...args: any[]): any }) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
Reflect.defineMetadata('mapper', type, target, propertyKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET 请求注解
|
||||
* @param RequestParams
|
||||
* @constructor
|
||||
*/
|
||||
export function GET(RequestParams?: ParamsType) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
CheckParams('get', target, propertyKey, descriptor, <ParamsType>RequestParams)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求注解
|
||||
* @param RequestParams
|
||||
* @constructor
|
||||
*/
|
||||
export function POST(RequestParams?: ParamsType) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
CheckParams('post', target, propertyKey, descriptor, <ParamsType>RequestParams)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT 请求注解
|
||||
* @param RequestParams
|
||||
* @constructor
|
||||
*/
|
||||
export function PUT(RequestParams?: ParamsType) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
CheckParams('put', target, propertyKey, descriptor, <ParamsType>RequestParams)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE 请求注解
|
||||
* @param RequestParams
|
||||
* @constructor
|
||||
*/
|
||||
export function DELETE(RequestParams?: ParamsType) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
CheckParams('delete', target, propertyKey, descriptor, <ParamsType>RequestParams)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET POST PUT DELETE 四个装饰器最终 都会走这里代码进行发送请求
|
||||
// 所以 在 http.annotation.ts 文件中 引入插件 json-class-interface
|
||||
// 调 json-class-interface 的方法 来配置 生成的 class 存放路径 为 /service/type 文件夹
|
||||
// 请求完成后 先使用 装饰器 的 target.name+_+propertyKey 作为文件名并 在路径下生成空的 .ts 文件
|
||||
// 然后调用 json-class-interface 的 toClass 方法传入propertyKey作为第一层类的名称
|
||||
// 并传入 res 请求到的json数据,得到最终的 类代码,然后让 json-class-interface 将 类代码 写入刚生成的 .ts 文件中
|
||||
|
||||
|
||||
// 唯一需要开发者手动做的事情如下,举例:
|
||||
// User.service.impl.ts 中 的 xxx 方法 GET 装饰器 传入 刚生成文件中的 propertyKey 类名
|
||||
// 这里的代码会自动调用 插件 json-mapper-class 进行 JsontoClasses 或者 JsontoClass 进行序列化
|
||||
|
||||
// 伪代码
|
||||
// configTypePath("service/type")
|
||||
// wirteFileType(`${target.name_propertyKey}.ts`, toClass(propertyKey,res) )
|
||||
|
||||
//
|
||||
function CheckParams(
|
||||
methods: "get" | "post" | "put" | "delete",
|
||||
target: any, propertyKey: string,
|
||||
descriptor: PropertyDescriptor,
|
||||
Params: ParamsType) {
|
||||
|
||||
Params == undefined ? Params = {} : null;
|
||||
Params.url = Params.url || ConfigureConfig.AjaxConfig.ApiList[propertyKey]
|
||||
Params.useHandle = Params.useHandle || false
|
||||
Params.header = Params.header || {}
|
||||
let MapperClass = Reflect.getMetadata('mapper', target, propertyKey);
|
||||
|
||||
if (MapperClass) {
|
||||
let OldMethods = descriptor.value;
|
||||
descriptor.value = async (data: { [key: string]: any }) => {
|
||||
let res = await AxioDao[methods]({
|
||||
url: <string>Params.url, data,
|
||||
headers: Params.header
|
||||
});
|
||||
res = res instanceof Array ? JsontoClasses(res, MapperClass) : JsontoClass(res, MapperClass)
|
||||
if (Params.useHandle) {
|
||||
return await OldMethods.call(target, res)
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
14
src/core/annotation/ioc.annotation.ts
Normal file
14
src/core/annotation/ioc.annotation.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
export function Autowired(params?: any) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
let typeClass = Reflect.getMetadata('design:type', target, propertyKey);
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
value: params ? new typeClass(new params) : new typeClass(),
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
53
src/core/annotation/register.annotation.ts
Normal file
53
src/core/annotation/register.annotation.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import Vue from 'vue';
|
||||
import { ConfigureConfig } from "@config/configure.config";
|
||||
import { CreateElement } from 'vue/types/umd';
|
||||
import Es6Promise from 'es6-promise'
|
||||
|
||||
Es6Promise.polyfill()
|
||||
|
||||
export function Start<T extends { new ( ...args: any[] ): { } }>( _ : T ) : T {
|
||||
// @ts-ignore
|
||||
return class extends _ {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
(new Vue({
|
||||
// @ts-ignore
|
||||
router: this.Router, store: this.VueX, render: (h: CreateElement) => h(this.app)
|
||||
}) as Vue).$mount('#app')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function RegServiceAndMethods(constructor: { new(...args: any[]): {}; }) {
|
||||
// newInstance(require.context("../service/impl", false, /\.ts$/))
|
||||
newInstance(require.context("../utils", false, /\.ts$/))
|
||||
}
|
||||
|
||||
function newInstance(context: __WebpackModuleApi.RequireContext) {
|
||||
context.keys().forEach((key: any) => {
|
||||
let m = context(key), className: string = Object.keys(m)[0];
|
||||
new m[className]()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自动注册Vue插件
|
||||
* @constructor
|
||||
*/
|
||||
export function RegVuePlugs(constructor: { new(...args: any[]): {}; }) {
|
||||
ConfigureConfig.VuePlugs.forEach((v: any) => Vue.use(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动挂载Vue 全局方法
|
||||
* @constructor
|
||||
*/
|
||||
export function GlobalMethod(target: any, propertyKey: string) {
|
||||
Vue.prototype[`$${ propertyKey }`] = target[propertyKey]
|
||||
}
|
||||
1
src/core/config/.pydio
Normal file
1
src/core/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
f0ed9e16-3f2b-4d50-9858-120dc2e2ae2c
|
||||
53
src/core/config/configure.config.ts
Normal file
53
src/core/config/configure.config.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
|
||||
import VueRouter, { RouterOptions } from 'vue-router';
|
||||
import Vuex from 'vuex';
|
||||
import Antd from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/antd.css';
|
||||
import { AutoRoutesConfig } from "@config/route.config";
|
||||
import { PluginObject } from "vue";
|
||||
|
||||
export class ConfigureConfig {
|
||||
|
||||
/**
|
||||
* 接口配置: 测试环境基地址,正式环境基地址,具体页面接口
|
||||
*/
|
||||
public static AjaxConfig: {
|
||||
DevUrl: string,
|
||||
ProdUrl: string,
|
||||
ApiList: { [key: string]: string }
|
||||
} = {
|
||||
DevUrl: 'http://127.0.0.1:8080',
|
||||
ProdUrl: 'http://127.0.0.1:9090/api',
|
||||
ApiList: {
|
||||
// start - 注释内的key都将被Node.js监听并diff取出实时编辑后的差异项,然后程序决定是否增删请求中间层xx文件中的xx方法代码【不要删除】
|
||||
UserList: '/UserList',
|
||||
// User_IndexTest: '/test',
|
||||
// Car_carList: '/test',
|
||||
// Car_addCar: '/test',
|
||||
// Index_reg: '/reg',
|
||||
// Order_test: '/test',
|
||||
// end - 注释内的key都将被Node.js监听并diff取出实时编辑后的差异项,然后程序决定是否增删请求中间层xx文件中的xx方法代码【不要删除】
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Vue 插件
|
||||
*/
|
||||
public static VuePlugs: PluginObject<never>[] = [
|
||||
VueRouter, Vuex, Antd
|
||||
];
|
||||
|
||||
/**
|
||||
* 路由配置
|
||||
*/
|
||||
public static RouterConfigUrl: RouterOptions = {
|
||||
mode: 'hash',
|
||||
base: './',
|
||||
routes: AutoRoutesConfig
|
||||
};
|
||||
}
|
||||
18
src/core/config/route.config.ts
Normal file
18
src/core/config/route.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// 根据page目录结构,自动生成的路由配置文件
|
||||
// 参考Nuxt.js 源码,同时自己 + ast 实现 https://zh.nuxtjs.org/guide/routing
|
||||
// @ts-ignore
|
||||
|
||||
export const AutoRoutesConfig = [
|
||||
{
|
||||
name: "About",
|
||||
path: "/About",
|
||||
component: () => import(/* webpackChunkName: 'About' */ '@page/about'),
|
||||
meta: { title: '关于我页面', showNav: true, isLogin: true }
|
||||
},
|
||||
{
|
||||
name: "Home",
|
||||
path: "/Home",
|
||||
component: () => import(/* webpackChunkName: 'Home' */ '@page/home'),
|
||||
meta: { title: '首页', showNav: true, isLogin: true }
|
||||
}
|
||||
];
|
||||
1
src/core/dao/.pydio
Normal file
1
src/core/dao/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
852dea68-05ee-4958-8884-27c503363732
|
||||
98
src/core/dao/index.dao.ts
Normal file
98
src/core/dao/index.dao.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig } from "axios";
|
||||
import { IndexUtils } from "@utils/index.utils";
|
||||
|
||||
/**
|
||||
* axios的封装
|
||||
*/
|
||||
export class AxioDao {
|
||||
instance: AxiosInstance;
|
||||
|
||||
public constructor() {
|
||||
this.instance = axios.create({
|
||||
baseURL: IndexUtils.CheckAjaxUrl(),
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
this.ResponseInterceptor();
|
||||
this.RequestInterceptor();
|
||||
}
|
||||
|
||||
public async get(params: AxiosRequestConfig): Promise<any> {
|
||||
return await this.instance.get(<string>params.url, {
|
||||
params: params.data,
|
||||
headers: Object.assign({ Authorization: localStorage.getItem("token") }, params.headers)
|
||||
});
|
||||
}
|
||||
|
||||
public async post(params: AxiosRequestConfig): Promise<any> {
|
||||
return await this.instance.post(<string>params.url, params.data, {
|
||||
headers: Object.assign({ Authorization: localStorage.getItem("token") }, params.headers)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Put 请求
|
||||
* 请求参数请参考接口:RequestParams
|
||||
* @param params
|
||||
*/
|
||||
public async put(params: AxiosRequestConfig): Promise<any> {
|
||||
return await this.instance.put(<string>params.url, params.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 请求
|
||||
* 请求参数请参考接口:RequestParams
|
||||
* @param params
|
||||
*/
|
||||
public async delete(params: AxiosRequestConfig): Promise<any> {
|
||||
return await this.instance.delete(<string>params.url, { params: params.data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
* @constructor
|
||||
*/
|
||||
public async ResponseInterceptor(): Promise<any> {
|
||||
this.instance.interceptors.response.use(
|
||||
(response: any) => {
|
||||
return response.data.result;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log(`
|
||||
❌ 请求错误 ❌
|
||||
1、基地址: ${error.config.baseURL}
|
||||
2、短地址: ${error.config.url}
|
||||
3、请求 方式: ${error.config.method}
|
||||
4、请求 参数: ${JSON.stringify(error.config.params)}
|
||||
5、请求 头 : ${JSON.stringify(error.config.headers)}
|
||||
|
||||
------------------------------------------------------------
|
||||
|
||||
6、错误信息为: ${error.message}
|
||||
7、错误代码为: ${error.stack}
|
||||
`);
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加请求拦截器
|
||||
* @constructor
|
||||
*/
|
||||
public async RequestInterceptor(): Promise<any> {
|
||||
this.instance.interceptors.request.use((config: any) => {
|
||||
return config
|
||||
}, function (error: AxiosError) {
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new AxioDao();
|
||||
1
src/core/run/.pydio
Normal file
1
src/core/run/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
45df40b6-06b8-4b49-9ece-3df4b113d74f
|
||||
51
src/core/run/init.run.ts
Normal file
51
src/core/run/init.run.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import 'reflect-metadata';
|
||||
import 'babel-polyfill'
|
||||
// import '@types/RouterHooks';
|
||||
import Vue, { VueConstructor, Component } from 'vue';
|
||||
import App from '@application/app'
|
||||
import { ConfigureConfig } from "@config/configure.config";
|
||||
import NavigationGuards from "@utils/navigation-guards.utils";
|
||||
import VueRouter, { RawLocation, Route } from 'vue-router';
|
||||
import Vuex, { Store } from 'vuex'
|
||||
import StoreModeule from '@store/index'
|
||||
import { RegServiceAndMethods, RegVuePlugs, Start } from "@ann/register.annotation";
|
||||
import { Autowired } from "@ann/ioc.annotation";
|
||||
|
||||
@Start
|
||||
@RegVuePlugs
|
||||
@RegServiceAndMethods
|
||||
export class InitRun {
|
||||
|
||||
@Autowired()
|
||||
protected NavigationGuards!: NavigationGuards;
|
||||
|
||||
protected Router!: VueRouter;
|
||||
protected VueX!: Store<typeof StoreModeule>;
|
||||
protected app: Component = App;
|
||||
|
||||
constructor() {
|
||||
Vue.config.productionTip = false;
|
||||
this.VueRouterAndVuex()
|
||||
}
|
||||
|
||||
private VueRouterAndVuex(): void {
|
||||
this.Router = new VueRouter(ConfigureConfig.RouterConfigUrl);
|
||||
// this.NavigationGuards.beforeEach(this.Router)
|
||||
// this.NavigationGuards.afterEach(this.Router)
|
||||
|
||||
// 重写路由Push
|
||||
const routerPush: (location: RawLocation) => Promise<Route> = VueRouter.prototype.push;
|
||||
VueRouter.prototype.push = function push(location: RawLocation): Promise<Route> {
|
||||
// @ts-ignore
|
||||
return routerPush.call(this, location).catch((error: Error) => error)
|
||||
};
|
||||
|
||||
this.VueX = new Vuex.Store(StoreModeule)
|
||||
}
|
||||
}
|
||||
|
||||
new InitRun();
|
||||
1
src/core/service/.pydio
Normal file
1
src/core/service/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
b6c30b87-ba50-4860-a92d-3dfe07d0ff5c
|
||||
46
src/core/service/user.service.ts
Normal file
46
src/core/service/user.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2023. bmy
|
||||
*/
|
||||
import { GET, Mapper } from "@ann/http.annotation";
|
||||
import { property, defaultValue, deserialize } from "@ann/JsonToClass";
|
||||
|
||||
// ajax接口数据返回 为 ResponseType[]
|
||||
// 这里定义类,将 接口返回的数据 映射到 类中存储
|
||||
export class ResponseType {
|
||||
// 接口返回数据的字段 为 id,这里映射改为 user_id,代码里面用 user_id
|
||||
@property("id")
|
||||
@defaultValue(0)
|
||||
user_id!: number;
|
||||
|
||||
@property("name")
|
||||
@defaultValue("如果接口字段无数值,那这个参数就是默认值")
|
||||
user_name!: string
|
||||
}
|
||||
|
||||
export class UserService {
|
||||
/**
|
||||
* 1 提供 四个【请求装饰器】:@GET @POST @PUT @DELETE
|
||||
* 参数说明:
|
||||
* url 可选:接口请求短地址,如果不填默认使用 configure.config.ts 中和方法名IndexTest同名的对象key中地址
|
||||
* 如果传,使用你传的地址
|
||||
*
|
||||
* useHandle 可选:是否需要对网络请求的结果进行修改后再返回给页面,如果需要传 true,则请求方法的入参 params 会变为ajax请求
|
||||
* 得到的接口数据,处理完数据后,请 return 结果。否则 IndexTest 代码内部留空,节省大量 冗余代码!!
|
||||
*
|
||||
* header 可选:自定义请求头,例如文件上传等 可以在这自定义
|
||||
*
|
||||
* 2 @Mapper() 装饰器
|
||||
* 参数说明:
|
||||
* 请求方法的返回类型(必须用类来描述JSON数据类型),会被 Reflect.defineMetadata 注入 元数据,在 上一步
|
||||
* 【请求装饰器】结束请求后,会从 Reflect.getMetadata 取出 元数据,然后 被 JsonToClass 使用到。
|
||||
* JsonToClass 会将json数据映射到 @Mapper() 装饰器的 参数中,也就是 json数据自动存储到类属性中
|
||||
*/
|
||||
@GET()
|
||||
@Mapper(ResponseType)
|
||||
// @ts-ignore
|
||||
public async UserList (params: object | ResponseType[]): Promise<ResponseType[]> {
|
||||
// 留空,不要写请求代码 即可发送请求,除非 useHandle: true,这时 方法入参 params 为 ajax 接口数据,直接处理 params
|
||||
// 即可,也不需要写请求代码!!
|
||||
}
|
||||
|
||||
}
|
||||
1
src/core/store/.pydio
Normal file
1
src/core/store/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
6dd651dd-0493-4995-a44b-c15ccfdf75a4
|
||||
10
src/core/store/getters.ts
Normal file
10
src/core/store/getters.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
export default {
|
||||
GettersTabsContentArr: (state: any) => state.PublicConfig.TabsContentArr,
|
||||
GettersTabsActive: (state: any) => state.PublicConfig.TabsActive,
|
||||
GettersAsideMenu: (state: any) => state.PublicConfig.AsideMenu,
|
||||
GettersIsLogin: (state: any) => state.PublicConfig.isLogin,
|
||||
}
|
||||
12
src/core/store/index.ts
Normal file
12
src/core/store/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import index from './modules'
|
||||
import getters from './getters'
|
||||
|
||||
// 统一导出各模块
|
||||
export default {
|
||||
modules: { ...index },
|
||||
getters
|
||||
}
|
||||
1
src/core/store/modules/.pydio
Normal file
1
src/core/store/modules/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
d3359e2f-bc25-4e14-b202-20080d06b04a
|
||||
9
src/core/store/modules/index.ts
Normal file
9
src/core/store/modules/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import PublicConfig from './publicConfig'
|
||||
|
||||
export default {
|
||||
PublicConfig
|
||||
}
|
||||
117
src/core/store/modules/publicConfig.ts
Normal file
117
src/core/store/modules/publicConfig.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
interface state {
|
||||
TabsContentArr: Array<{
|
||||
title: string,
|
||||
url: string,
|
||||
closable: boolean,
|
||||
icon: string
|
||||
}>,
|
||||
TabsActive: string,
|
||||
isLogin: boolean,
|
||||
AsideMenu: Array<{
|
||||
web_href: string,
|
||||
web_title: string,
|
||||
icon: string,
|
||||
children: Array<{
|
||||
web_href: string,
|
||||
web_title: string,
|
||||
icon: string,
|
||||
}>
|
||||
}>
|
||||
}
|
||||
|
||||
export default {
|
||||
state: {
|
||||
isLogin: false,
|
||||
TabsContentArr: [
|
||||
{ title: '系统首页', url: '/', closable: false, icon: 'el-icon-house' },
|
||||
],
|
||||
TabsActive: '/',
|
||||
AsideMenu: [
|
||||
{
|
||||
web_href: '/', web_title: '系统首页', icon: 'el-icon-house',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
web_href: '/System', web_title: '系统设置', icon: 'el-icon-setting',
|
||||
children: [
|
||||
{ web_href: '/System/MenuConfig', web_title: '菜单配置', icon: 'el-icon-tickets' },
|
||||
{ web_href: '/System/wardenList', web_title: '管理员列表', icon: 'el-icon-user' },
|
||||
]
|
||||
},
|
||||
{
|
||||
web_href: '/Jurisdiction', web_title: '权限管理', icon: 'el-icon-lock',
|
||||
children: [
|
||||
{ web_href: '/Jurisdiction/RolePermissions', web_title: '角色权限', icon: 'el-icon-key' },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
mutations: {
|
||||
// 保存顶部菜单
|
||||
SET_TABCONTENT: (state: state, activeMenu: string) => {
|
||||
// 检查是否已经添加过了
|
||||
let s = state.TabsContentArr.filter(function (val: any, index: number) {
|
||||
return activeMenu == val.url
|
||||
});
|
||||
// 如果没有添加过
|
||||
if (s.length == 0) {
|
||||
state.AsideMenu.forEach((v, i) => {
|
||||
// 如果等于,表示一级菜单
|
||||
if (activeMenu == v.web_href) {
|
||||
state.TabsContentArr.push({
|
||||
title: v.web_title,
|
||||
url: v.web_href,
|
||||
icon: v.icon,
|
||||
closable: true
|
||||
})
|
||||
} else {
|
||||
v.children.forEach((va, ix) => {
|
||||
if (activeMenu == va.web_href) {
|
||||
state.TabsContentArr.push({
|
||||
title: va.web_title,
|
||||
url: va.web_href,
|
||||
icon: va.icon,
|
||||
closable: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
// 切换激活
|
||||
SET_TABACTIVE: (state: state, active: string) => {
|
||||
state.TabsActive = active
|
||||
},
|
||||
// 移除激活
|
||||
SET_REMOVETABS: (state: state, targetName: string) => {
|
||||
let tabs = state.TabsContentArr;
|
||||
let activeName = state.TabsActive;
|
||||
// 如果删除的是当前激活菜单
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab: { [key: string]: any }, index: number) => {
|
||||
if (tab.url === targetName) {
|
||||
let nextTab: { [key: string]: any } = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.url;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
state.TabsActive = activeName;
|
||||
state.TabsContentArr = tabs.filter((tab: { [key: string]: any }) => {
|
||||
return tab.url !== targetName
|
||||
});
|
||||
},
|
||||
// 修改状态
|
||||
SET_LOGINSTATUS: (state: state, status: boolean) => {
|
||||
state.isLogin = status
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
1
src/core/types/.pydio
Normal file
1
src/core/types/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e5b979e5-1ac3-429c-bf03-7d570eba9033
|
||||
11
src/core/types/RouterHooks.ts
Normal file
11
src/core/types/RouterHooks.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import Component from 'vue-class-component'
|
||||
|
||||
Component.registerHooks([
|
||||
'beforeRouteEnter',
|
||||
'beforeRouteLeave',
|
||||
'beforeRouteUpdate'
|
||||
])
|
||||
359
src/core/types/process.d.ts
vendored
Normal file
359
src/core/types/process.d.ts
vendored
Normal file
@@ -0,0 +1,359 @@
|
||||
// Type definitions for webpack (module API) 1.15
|
||||
// Project: https://github.com/webpack/webpack
|
||||
// Definitions by: use-strict <https://github.com/use-strict>
|
||||
// rhonsby <https://github.com/rhonsby>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.1
|
||||
|
||||
/**
|
||||
* Webpack module API - variables and global functions available inside modules
|
||||
*/
|
||||
|
||||
declare namespace __WebpackModuleApi {
|
||||
interface RequireResolve {
|
||||
(id: string): string | number;
|
||||
}
|
||||
|
||||
interface RequireContext {
|
||||
keys(): string[];
|
||||
(id: string): any;
|
||||
<T>(id: string): T;
|
||||
resolve(id: string): string;
|
||||
/** The module id of the context module. This may be useful for module.hot.accept. */
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface RequireFunction {
|
||||
/**
|
||||
* Returns the exports from a dependency. The call is sync. No request to the server is fired. The compiler ensures that the dependency is available.
|
||||
*/
|
||||
(path: string): any;
|
||||
<T>(path: string): T;
|
||||
/**
|
||||
* Behaves similar to require.ensure, but the callback is called with the exports of each dependency in the paths array. There is no option to provide a chunk name.
|
||||
*/
|
||||
(paths: string[], callback: (...modules: any[]) => void): void;
|
||||
/**
|
||||
* Download additional dependencies on demand. The paths array lists modules that should be available. When they are, callback is called. If the callback is a function expression, dependencies in that source part are extracted and also loaded on demand. A single request is fired to the server, except if all modules are already available.
|
||||
*
|
||||
* This creates a chunk. The chunk can be named. If a chunk with this name already exists, the dependencies are merged into that chunk and that chunk is used.
|
||||
*/
|
||||
ensure(paths: string[], callback: (require: NodeRequire) => void, chunkName?: string): void;
|
||||
ensure(paths: string[], callback: (require: NodeRequire) => void, errorCallback?: (error: any) => void, chunkName?: string): void;
|
||||
context(path: string, deep?: boolean, filter?: RegExp, mode?: "sync" | "eager" | "weak" | "lazy" | "lazy-once"): RequireContext;
|
||||
/**
|
||||
* Returns the module id of a dependency. The call is sync. No request to the server is fired. The compiler ensures that the dependency is available.
|
||||
*
|
||||
* The module id is a number in webpack (in contrast to node.js where it is a string, the filename).
|
||||
*/
|
||||
resolve: NodeJS.RequireResolve;
|
||||
/**
|
||||
* Like require.resolve, but doesn’t include the module into the bundle. It’s a weak dependency.
|
||||
*/
|
||||
resolveWeak(path: string): number | string;
|
||||
/**
|
||||
* Ensures that the dependency is available, but don’t execute it. This can be use for optimizing the position of a module in the chunks.
|
||||
*/
|
||||
include(path: string): void;
|
||||
/**
|
||||
* Multiple requires to the same module result in only one module execution and only one export. Therefore a cache in the runtime exists. Removing values from this cache cause new module execution and a new export. This is only needed in rare cases (for compatibility!).
|
||||
*/
|
||||
cache: {
|
||||
[id: string]: NodeModule | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
interface Module {
|
||||
exports: any;
|
||||
id: string;
|
||||
filename: string;
|
||||
loaded: boolean;
|
||||
parent: NodeModule | null | undefined;
|
||||
children: NodeModule[];
|
||||
hot?: Hot;
|
||||
}
|
||||
// @ts-ignore
|
||||
type ModuleId = string|number;
|
||||
|
||||
interface HotNotifierInfo {
|
||||
type:
|
||||
| 'self-declined'
|
||||
| 'declined'
|
||||
| 'unaccepted'
|
||||
| 'accepted'
|
||||
| 'disposed'
|
||||
| 'accept-errored'
|
||||
| 'self-accept-errored'
|
||||
| 'self-accept-error-handler-errored';
|
||||
/**
|
||||
* The module in question.
|
||||
*/
|
||||
moduleId: number;
|
||||
/**
|
||||
* For errors: the module id owning the accept handler.
|
||||
*/
|
||||
dependencyId?: number;
|
||||
/**
|
||||
* For declined/accepted/unaccepted: the chain from where the update was propagated.
|
||||
*/
|
||||
chain?: number[];
|
||||
/**
|
||||
* For declined: the module id of the declining parent
|
||||
*/
|
||||
parentId?: number;
|
||||
/**
|
||||
* For accepted: the modules that are outdated and will be disposed
|
||||
*/
|
||||
outdatedModules?: number[];
|
||||
/**
|
||||
* For accepted: The location of accept handlers that will handle the update
|
||||
*/
|
||||
outdatedDependencies?: {
|
||||
[dependencyId: number]: number[];
|
||||
};
|
||||
/**
|
||||
* For errors: the thrown error
|
||||
*/
|
||||
error?: Error;
|
||||
/**
|
||||
* For self-accept-error-handler-errored: the error thrown by the module
|
||||
* before the error handler tried to handle it.
|
||||
*/
|
||||
originalError?: Error;
|
||||
}
|
||||
|
||||
interface Hot {
|
||||
/**
|
||||
* Accept code updates for the specified dependencies. The callback is called when dependencies were replaced.
|
||||
* @param dependencies
|
||||
* @param callback
|
||||
*/
|
||||
accept(dependencies: string[], callback?: (updatedDependencies: ModuleId[]) => void): void;
|
||||
/**
|
||||
* Accept code updates for the specified dependencies. The callback is called when dependencies were replaced.
|
||||
* @param dependency
|
||||
* @param callback
|
||||
*/
|
||||
accept(dependency: string, callback?: () => void): void;
|
||||
/**
|
||||
* Accept code updates for this module without notification of parents.
|
||||
* This should only be used if the module doesn’t export anything.
|
||||
* The errHandler can be used to handle errors that occur while loading the updated module.
|
||||
* @param errHandler
|
||||
*/
|
||||
accept(errHandler?: (err: Error) => void): void;
|
||||
/**
|
||||
* Do not accept updates for the specified dependencies. If any dependencies is updated, the code update fails with code "decline".
|
||||
*/
|
||||
decline(dependencies: string[]): void;
|
||||
/**
|
||||
* Do not accept updates for the specified dependencies. If any dependencies is updated, the code update fails with code "decline".
|
||||
*/
|
||||
decline(dependency: string): void;
|
||||
/**
|
||||
* Flag the current module as not update-able. If updated the update code would fail with code "decline".
|
||||
*/
|
||||
decline(): void;
|
||||
/**
|
||||
* Add a one time handler, which is executed when the current module code is replaced.
|
||||
* Here you should destroy/remove any persistent resource you have claimed/created.
|
||||
* If you want to transfer state to the new module, add it to data object.
|
||||
* The data will be available at module.hot.data on the new module.
|
||||
* @param callback
|
||||
*/
|
||||
dispose(callback: (data: any) => void): void;
|
||||
dispose(callback: <T>(data: T) => void): void;
|
||||
/**
|
||||
* Add a one time handler, which is executed when the current module code is replaced.
|
||||
* Here you should destroy/remove any persistent resource you have claimed/created.
|
||||
* If you want to transfer state to the new module, add it to data object.
|
||||
* The data will be available at module.hot.data on the new module.
|
||||
* @param callback
|
||||
*/
|
||||
addDisposeHandler(callback: (data: any) => void): void;
|
||||
addDisposeHandler<T>(callback: (data: T) => void): void;
|
||||
/**
|
||||
* Remove a handler.
|
||||
* This can useful to add a temporary dispose handler. You could i. e. replace code while in the middle of a multi-step async function.
|
||||
* @param callback
|
||||
*/
|
||||
removeDisposeHandler(callback: (data: any) => void): void;
|
||||
removeDisposeHandler<T>(callback: (data: T) => void): void;
|
||||
/**
|
||||
* Throws an exceptions if status() is not idle.
|
||||
* Check all currently loaded modules for updates and apply updates if found.
|
||||
* If no update was found, the callback is called with null.
|
||||
* If autoApply is truthy the callback will be called with all modules that were disposed.
|
||||
* apply() is automatically called with autoApply as options parameter.
|
||||
* If autoApply is not set the callback will be called with all modules that will be disposed on apply().
|
||||
* @param autoApply
|
||||
* @param callback
|
||||
*/
|
||||
check(autoApply: boolean, callback: (err: Error, outdatedModules: ModuleId[]) => void): void;
|
||||
/**
|
||||
* Throws an exceptions if status() is not idle.
|
||||
* Check all currently loaded modules for updates and apply updates if found.
|
||||
* If no update was found, the callback is called with null.
|
||||
* The callback will be called with all modules that will be disposed on apply().
|
||||
* @param callback
|
||||
*/
|
||||
check(callback: (err: Error, outdatedModules: ModuleId[]) => void): void;
|
||||
/**
|
||||
* If status() != "ready" it throws an error.
|
||||
* Continue the update process.
|
||||
* @param options
|
||||
* @param callback
|
||||
*/
|
||||
apply(options: AcceptOptions, callback: (err: Error, outdatedModules: ModuleId[]) => void): void;
|
||||
/**
|
||||
* If status() != "ready" it throws an error.
|
||||
* Continue the update process.
|
||||
* @param callback
|
||||
*/
|
||||
apply(callback: (err: Error, outdatedModules: ModuleId[]) => void): void;
|
||||
/**
|
||||
* Return one of idle, check, watch, watch-delay, prepare, ready, dispose, apply, abort or fail.
|
||||
*/
|
||||
status(): string;
|
||||
/** Register a callback on status change. */
|
||||
status(callback: (status: string) => void): void;
|
||||
/** Register a callback on status change. */
|
||||
addStatusHandler(callback: (status: string) => void): void;
|
||||
/**
|
||||
* Remove a registered status change handler.
|
||||
* @param callback
|
||||
*/
|
||||
removeStatusHandler(callback: (status: string) => void): void;
|
||||
|
||||
active: boolean;
|
||||
data: any;
|
||||
}
|
||||
|
||||
interface AcceptOptions {
|
||||
/**
|
||||
* If true the update process continues even if some modules are not accepted (and would bubble to the entry point).
|
||||
*/
|
||||
ignoreUnaccepted?: boolean;
|
||||
/**
|
||||
* Ignore changes made to declined modules.
|
||||
*/
|
||||
ignoreDeclined?: boolean;
|
||||
/**
|
||||
* Ignore errors throw in accept handlers, error handlers and while reevaluating module.
|
||||
*/
|
||||
ignoreErrored?: boolean;
|
||||
/**
|
||||
* Notifier for declined modules.
|
||||
*/
|
||||
onDeclined?: (info: HotNotifierInfo) => void;
|
||||
/**
|
||||
* Notifier for unaccepted modules.
|
||||
*/
|
||||
onUnaccepted?: (info: HotNotifierInfo) => void;
|
||||
/**
|
||||
* Notifier for accepted modules.
|
||||
*/
|
||||
onAccepted?: (info: HotNotifierInfo) => void;
|
||||
/**
|
||||
* Notifier for disposed modules.
|
||||
*/
|
||||
onDisposed?: (info: HotNotifierInfo) => void;
|
||||
/**
|
||||
* Notifier for errors.
|
||||
*/
|
||||
onErrored?: (info: HotNotifierInfo) => void;
|
||||
/**
|
||||
* Indicates that apply() is automatically called by check function
|
||||
*/
|
||||
autoApply?: boolean;
|
||||
}
|
||||
/**
|
||||
* Inside env you can pass any variable
|
||||
*/
|
||||
interface NodeProcess {
|
||||
// @ts-ignore
|
||||
env: {
|
||||
NODE_ENV: string
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
type __Require1 = (id: string) => any;
|
||||
// @ts-ignore
|
||||
type __Require2 = <T>(id: string) => T;
|
||||
// @ts-ignore
|
||||
type RequireLambda = __Require1 & __Require2;
|
||||
}
|
||||
|
||||
interface NodeRequire extends NodeJS.Require {}
|
||||
|
||||
declare var require: NodeRequire;
|
||||
|
||||
/**
|
||||
* The resource query of the current module.
|
||||
*
|
||||
* e.g. __resourceQuery === "?test" // Inside "file.js?test"
|
||||
*/
|
||||
declare var __resourceQuery: string;
|
||||
|
||||
/**
|
||||
* Equals the config options output.publicPath.
|
||||
*/
|
||||
declare var __webpack_public_path__: string;
|
||||
|
||||
/**
|
||||
* The raw require function. This expression isn’t parsed by the Parser for dependencies.
|
||||
*/
|
||||
declare var __webpack_require__: any;
|
||||
|
||||
/**
|
||||
* The internal chunk loading function
|
||||
*
|
||||
* @param chunkId The id for the chunk to load.
|
||||
* @param callback A callback function called once the chunk is loaded.
|
||||
*/
|
||||
declare var __webpack_chunk_load__: (chunkId: any, callback: (require: __WebpackModuleApi.RequireLambda) => void) => void;
|
||||
|
||||
/**
|
||||
* Access to the internal object of all modules.
|
||||
*/
|
||||
declare var __webpack_modules__: any[];
|
||||
|
||||
/**
|
||||
* Access to the hash of the compilation.
|
||||
*
|
||||
* Only available with the HotModuleReplacementPlugin or the ExtendedAPIPlugin
|
||||
*/
|
||||
declare var __webpack_hash__: any;
|
||||
|
||||
/**
|
||||
* Generates a require function that is not parsed by webpack. Can be used to do cool stuff with a global require function if available.
|
||||
*/
|
||||
declare var __non_webpack_require__: any;
|
||||
|
||||
/**
|
||||
* Adds nonce to all scripts that webpack loads.
|
||||
*
|
||||
* To activate the feature a __webpack_nonce__ variable needs to be set in your entry script.
|
||||
*/
|
||||
declare var __webpack_nonce__: string;
|
||||
|
||||
/**
|
||||
* Equals the config option debug
|
||||
*/
|
||||
declare var DEBUG: boolean;
|
||||
|
||||
interface NodeModule extends NodeJS.Module {}
|
||||
|
||||
declare var module: NodeModule;
|
||||
|
||||
/**
|
||||
* Declare process variable
|
||||
*/
|
||||
declare namespace NodeJS {
|
||||
interface Process extends __WebpackModuleApi.NodeProcess {}
|
||||
interface RequireResolve extends __WebpackModuleApi.RequireResolve {}
|
||||
interface Module extends __WebpackModuleApi.Module {}
|
||||
interface Require extends __WebpackModuleApi.RequireFunction {}
|
||||
}
|
||||
declare var process: NodeJS.Process;
|
||||
48
src/core/types/shims-tsx.d.ts
vendored
Normal file
48
src/core/types/shims-tsx.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
import Vue, { VNode } from 'vue'
|
||||
|
||||
declare global {
|
||||
namespace Vue {
|
||||
any
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface ElementAttributesProperty {
|
||||
$props: any // 设置类组件的props类型检查属性
|
||||
}
|
||||
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {
|
||||
}
|
||||
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {
|
||||
}
|
||||
|
||||
interface IntrinsicAttributes {
|
||||
// 给组件元素增加属性
|
||||
on?: any
|
||||
vModel?: any
|
||||
vShow?: boolean
|
||||
value?: any
|
||||
oninput?: Function
|
||||
nativeOnClick?: (event: MouseEvent) => void
|
||||
oncontextmenu?: (event: MouseEvent) => void
|
||||
onmouseup?: (event: MouseEvent) => void
|
||||
onclick?: (event: MouseEvent) => void
|
||||
onclick_prevent?: (event: MouseEvent) => void
|
||||
ref?: string
|
||||
key?: string | number
|
||||
class?: string | string[]
|
||||
slot?: string
|
||||
style?: Partial<CSSStyleDeclaration> | string | object | object[]
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/core/types/shims-vue.d.ts
vendored
Normal file
8
src/core/types/shims-vue.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
1
src/core/utils/.pydio
Normal file
1
src/core/utils/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
23f23313-fd2c-4dfc-aeaa-6758f2f76ec2
|
||||
60
src/core/utils/index.utils.ts
Normal file
60
src/core/utils/index.utils.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
|
||||
import { ConfigureConfig } from "@config/configure.config";
|
||||
import { GlobalMethod } from "@ann/register.annotation";
|
||||
|
||||
export class IndexUtils {
|
||||
|
||||
/**
|
||||
* 动态返回接口基地址
|
||||
* @constructor
|
||||
*/
|
||||
public static CheckAjaxUrl(): string {
|
||||
if(process.env.NODE_ENV === 'production') {
|
||||
return ConfigureConfig.AjaxConfig.ProdUrl;
|
||||
} else {
|
||||
return ConfigureConfig.AjaxConfig.DevUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法上打上 @GlobalMethod() 注解,这个方法会自动成为vue的全局方法
|
||||
* 组件中直接:this.$setToken() 即可
|
||||
*/
|
||||
@GlobalMethod
|
||||
public setToken(args: string): void {
|
||||
localStorage.setItem("token", args)
|
||||
}
|
||||
|
||||
@GlobalMethod
|
||||
public setStorage<T extends [] | {} | string>(key: string, data: T): void {
|
||||
if (data.constructor == Array || data.constructor == Object) {
|
||||
localStorage.setItem(`moehu_${key}`, JSON.stringify(data));
|
||||
} else {
|
||||
// @ts-ignore
|
||||
localStorage.setItem(`moehu_${key}`, data.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@GlobalMethod
|
||||
public getStorage(key: String): typeof JSON | boolean | string{
|
||||
let Storage: string | null = localStorage.getItem(`moehu_${key}`);
|
||||
if (Storage == null) {
|
||||
return false
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(Storage);
|
||||
} catch (e) {
|
||||
return Storage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@GlobalMethod
|
||||
public isPhone(phone: string): boolean {
|
||||
return /^1[3-9]\d{9}$/.test(phone)
|
||||
}
|
||||
}
|
||||
37
src/core/utils/navigation-guards.utils.ts
Normal file
37
src/core/utils/navigation-guards.utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
import VueRouter from 'vue-router';
|
||||
|
||||
export default class NavigationGuardsUtils {
|
||||
|
||||
public beforeEach(Router: VueRouter) : void {
|
||||
Router.beforeEach((to, from, next) => {
|
||||
document.title = to.meta && to.meta.title;
|
||||
if (to.meta && to.meta.isLogin) {
|
||||
if (localStorage.getItem("moehu_token") != null) {
|
||||
next()
|
||||
} else {
|
||||
next({
|
||||
path: '/login'
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (localStorage.getItem("moehu_token") != null
|
||||
&& to.path == "/login") {
|
||||
Router.back();
|
||||
}
|
||||
next()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public afterEach(Router: VueRouter) : void {
|
||||
Router.afterEach((to, from) => {
|
||||
console.log("Router.afterEach")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
56
test.js
Normal file
56
test.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const fs = require('fs')
|
||||
|
||||
class readFileLine {
|
||||
|
||||
configPath = "./src/core/config/configure.config.ts";
|
||||
lineLength = 0;
|
||||
textArr = [];
|
||||
lineBuffer = new Buffer(4096);
|
||||
|
||||
getFileList() {
|
||||
this.text = fs.createReadStream(this.configPath)
|
||||
return new Promise((s, e) => {
|
||||
this.textArr = [];
|
||||
let status = false
|
||||
this.text.on('data', (data) => {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i] == 10 || data[i] == 13) {
|
||||
if (data[i] == 10) {
|
||||
try {
|
||||
var line = this.lineBuffer.slice(0, this.lineLength);
|
||||
if (line.length != 0) {
|
||||
let str = line.toString('utf8')
|
||||
.replace(/^\s+|\s+$/g, '')
|
||||
.replace(/\'/g, "")
|
||||
.replace(/,/g, "")
|
||||
|
||||
if (str.includes("start -")) {
|
||||
status = true
|
||||
continue;
|
||||
}
|
||||
if (str.includes("end -")) {
|
||||
status = false
|
||||
break;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
this.textArr.push(str);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lineLength = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.lineBuffer[this.lineLength] = data[i];
|
||||
this.lineLength++;
|
||||
}
|
||||
}
|
||||
|
||||
s(this.textArr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new readFileLine();
|
||||
22
test2.js
Normal file
22
test2.js
Normal file
@@ -0,0 +1,22 @@
|
||||
let parser = require("@babel/parser")
|
||||
let _traverse = require("@babel/traverse")
|
||||
const traverse = _traverse.default;
|
||||
|
||||
let _generator = require("@babel/generator")
|
||||
const generator = _generator.default;
|
||||
|
||||
const code = `let val = 1 + 2`
|
||||
const ast = parser.parse(code)
|
||||
// console.log(ast)
|
||||
|
||||
traverse(ast, {
|
||||
enter(path) { // 这个path会找到所有的node
|
||||
if (path.node.type == 'Identifier') {
|
||||
path.node.name = 'modify'
|
||||
path.stop()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const ret = generator(ast)
|
||||
console.log(ret)
|
||||
104
tsconfig.json
Normal file
104
tsconfig.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
"@core/*": [
|
||||
"src/core/*"
|
||||
],
|
||||
"@router/*": [
|
||||
"src/core/router/*"
|
||||
],
|
||||
"@store/*": [
|
||||
"src/core/store/*"
|
||||
],
|
||||
"@types/*": [
|
||||
"src/core/types/*"
|
||||
],
|
||||
"@config/*": [
|
||||
"src/core/config/*"
|
||||
],
|
||||
"@utils/*": [
|
||||
"src/core/utils/*"
|
||||
],
|
||||
"@run/*": [
|
||||
"src/core/run/*"
|
||||
],
|
||||
"@ann/*": [
|
||||
"src/core/annotation/*"
|
||||
],
|
||||
"@model/*": [
|
||||
"src/core/model/*"
|
||||
],
|
||||
"@dao/*": [
|
||||
"src/core/dao/*"
|
||||
],
|
||||
"@entity/*": [
|
||||
"src/core/entity/*"
|
||||
],
|
||||
"@service/*": [
|
||||
"src/core/service/*"
|
||||
],
|
||||
"@impl/*": [
|
||||
"src/core/service/impl/*"
|
||||
],
|
||||
"@application/*": [
|
||||
"src/application/*"
|
||||
],
|
||||
"@img/*": [
|
||||
"src/application/assets/img/*"
|
||||
],
|
||||
"@less/*": [
|
||||
"src/application/assets/less/*"
|
||||
],
|
||||
"@pageLess/*": [
|
||||
"src/application/assets/less/page/*"
|
||||
],
|
||||
"@components/*": [
|
||||
"src/application/components/*"
|
||||
],
|
||||
"@page/*": [
|
||||
"src/application/page/*"
|
||||
],
|
||||
"@logic/*": [
|
||||
"src/application/logic/*"
|
||||
],
|
||||
"@layout/*": [
|
||||
"src/application/layout/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
174
vue.config.js
Normal file
174
vue.config.js
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
* Email:2271608011@qq.com
|
||||
* Github:https://github.com/helpcode
|
||||
*/
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
const path = require('path');
|
||||
const { ProgressPlugin } = require("webpack")
|
||||
const chalk = require("chalk");
|
||||
|
||||
let js = [
|
||||
'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js',
|
||||
'https://cdn.bootcdn.net/ajax/libs/vuex/3.5.1/vuex.min.js',
|
||||
'https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js',
|
||||
'https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js',
|
||||
'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.2/index.min.js'
|
||||
]
|
||||
|
||||
let exclude = {
|
||||
'vue': 'Vue',
|
||||
'vuex': 'Vuex',
|
||||
'vue-router': 'VueRouter',
|
||||
'axios': 'axios',
|
||||
'element-ui': 'ELEMENT'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
publicPath: './',
|
||||
filenameHashing: true,
|
||||
productionSourceMap: false,
|
||||
parallel: require('os').cpus().length > 1,
|
||||
lintOnSave: false,
|
||||
pluginOptions: {
|
||||
route: {
|
||||
// 默认:page。存放Vue路由页面的文件夹名称
|
||||
TemplateFolderName: 'page',
|
||||
// 默认:./src/application。从src/开始到TemplateFolderName文件夹父一级文件夹相对路径
|
||||
RootFolderName: './src/application',
|
||||
// 默认 src/core/config/route.config.ts。生成的配置文件保存路径,相对插件的位置来。
|
||||
SaveConfigPath: '../../../src/core/config/route.config.ts'
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
hot: true,
|
||||
port: 8080,
|
||||
compress: false,
|
||||
open: false,
|
||||
openPage: '#/Home',
|
||||
progress: false,
|
||||
overlay: {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
// before 这块代码都可以删除,仅仅为脚手架提供一个用于测试的接口
|
||||
before: function (app, server) {
|
||||
app.all('*', function (req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Headers', '*');
|
||||
res.header('Access-Control-Allow-Methods', '*');
|
||||
res.header('Content-Type', 'application/json;charset=utf-8');
|
||||
next();
|
||||
});
|
||||
app.get('/UserList', function (req, res) {
|
||||
res.json({
|
||||
status: 200,
|
||||
message: '请求成功',
|
||||
result: [
|
||||
{ id: 1, name: '张三' },
|
||||
{ id: 2, name: '李四' }
|
||||
]
|
||||
});
|
||||
});
|
||||
},
|
||||
// proxy: {
|
||||
// '/api': {
|
||||
// target: 'http://115.159.153.142:9010/h5',
|
||||
// changeOrigin: true,
|
||||
// ws: true,
|
||||
// pathRewrite: {
|
||||
// '^/api': ''
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
},
|
||||
configureWebpack: config => {
|
||||
// 设置程序核心入口
|
||||
config.entry.app = ['babel-polyfill', './src/core/run/init.run.ts'];
|
||||
|
||||
if (isProduction) {
|
||||
// 需要被打包排除的插件列表
|
||||
config.externals = exclude
|
||||
|
||||
// 开启 Gzip压缩
|
||||
return {
|
||||
plugins: [
|
||||
new BundleAnalyzerPlugin(),
|
||||
|
||||
// new CompressionPlugin({
|
||||
// test: /\.js$|\.html$|\.css/, //匹配文件名
|
||||
// threshold: 10240, //对超过10k的数据进行压缩
|
||||
// deleteOriginalAssets: false //是否删除原文件
|
||||
// })
|
||||
]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
plugins: [
|
||||
new ProgressPlugin({
|
||||
handler: (percentage, message, ...args) => {
|
||||
console.log(`${chalk.yellow("进度: ")}${chalk.green.bold(~~(percentage * 100) + "%")} ${args[0] ? chalk.black(args[0]) : " " } ${chalk.blue.bold(message)}`)
|
||||
},
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
chainWebpack: (config) => {
|
||||
if (isProduction) {
|
||||
// 去除混淆加密
|
||||
config.optimization.minimize(false)
|
||||
|
||||
// 移除 prefetch,preload 插件 减少不必要的请求
|
||||
config.plugins.delete('prefetch')
|
||||
config.plugins.delete('preload')
|
||||
|
||||
// 生产环境下注入一个变量cdn 变量可以在html中通过htmlWebpackPlugin.options进行获取
|
||||
config.plugin('html')
|
||||
.tap(args => {
|
||||
args[0].cdn = js;
|
||||
return args;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置路径别名,项目中引用资源的时候路径可以更简短
|
||||
* 但是ts文件在用的时候,编译器会报错,需要添加 @ts-ignore
|
||||
* 忽略开发工具的错误。
|
||||
*/
|
||||
config.resolve.alias
|
||||
.set('@', resolve('src'))
|
||||
.set('@core', resolve('src/core'))
|
||||
.set('@router', resolve('src/core/router'))
|
||||
.set('@store', resolve('src/core/store'))
|
||||
.set('@types', resolve('src/core/types'))
|
||||
.set('@config', resolve('src/core/config'))
|
||||
.set('@utils', resolve('src/core/utils'))
|
||||
.set('@run', resolve('src/core/run'))
|
||||
.set('@ann', resolve('src/core/annotation'))
|
||||
.set('@model', resolve('src/core/model'))
|
||||
.set('@dao', resolve('src/core/dao'))
|
||||
.set('@entity', resolve('src/core/entity'))
|
||||
.set('@service', resolve('src/core/service'))
|
||||
.set('@impl', resolve('src/core/service/impl'))
|
||||
|
||||
.set('@application', resolve('src/application'))
|
||||
.set('@img', resolve('src/application/assets/img'))
|
||||
.set('@less', resolve('src/application/assets/less'))
|
||||
.set('@pageLess', resolve('src/application/assets/less/page'))
|
||||
.set('@componentsLess', resolve('src/application/assets/less/components'))
|
||||
.set('@components', resolve('src/application/components'))
|
||||
.set('@page', resolve('src/application/page'))
|
||||
.set('@logic', resolve('src/application/logic'))
|
||||
.set('@layout', resolve('src/application/layout'))
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir);
|
||||
}
|
||||
115
思路.txt
Normal file
115
思路.txt
Normal file
@@ -0,0 +1,115 @@
|
||||
需要被实现的功能
|
||||
|
||||
将ajax请求到的数据存和数据中间层model进行映射,数据存入model中,model可以对数据key进行任意命名,然后vue的tsx页面组件可以依赖注入model
|
||||
然后使用model进行页面的渲染!
|
||||
|
||||
好处:
|
||||
|
||||
实现中间层的目的在于完全解耦了数据和页面的绑定,如果后端进行接口字段的大量修改,可以在不修改前端页面代码字段的情况下只需要修改model即可!!
|
||||
|
||||
|
||||
原本程序思路整理:
|
||||
|
||||
1:Tsx组件在对应的logic逻辑中使用service层发送请求,核心代码细节:
|
||||
|
||||
@Inject()
|
||||
public readonly UserServiceImpl!: UserServiceImpl;
|
||||
|
||||
await this.UserServiceImpl.IndexTest({ type: 1, page: 1 });
|
||||
|
||||
|
||||
2:虽然service层的方法被logic组件调用,但是方法只是空壳,方法内部无法编写任何逻辑和返回,请求发送在 请求装饰器中实现,代码细节:
|
||||
|
||||
@GET({ useHandle: false, header: { test: '1111' } })
|
||||
public async IndexTest(params: object): Promise<any> { }
|
||||
|
||||
3:装饰器 @GET() 会根据方法名 IndexTest 去 配置文件 configure.config.ts 中自动寻找对应key的接口地址来自动 发送请求。
|
||||
|
||||
|
||||
4:请求成功后,装饰器会根据参数 useHandle 的true or false 决定是否把数据 返还给方法,如果数据需要特殊处理,那么设置为true
|
||||
方法的入参 IndexTest(params: object) params 会被装饰器调用存放ajax请求到的数据,我们可以对数据进行处理然后再把数据交给页面来
|
||||
渲染展示。代码实现细节:
|
||||
|
||||
descriptor.value = async (data: { [key:string]: any }) => await OldMethods.apply(target, [
|
||||
// @ts-ignore
|
||||
await AxioDao[methods]({
|
||||
url: Params.url,
|
||||
data: data,
|
||||
header: Params.header
|
||||
})
|
||||
])
|
||||
|
||||
5:TSX 页面组件拿到数据,可以渲染页面了!
|
||||
|
||||
改造思路分析:
|
||||
|
||||
1:需要拦截 步骤4 到 步骤5 的过程,在步骤4 和 步骤5 中间实现Model层,在请求装饰器中拿到ajax的数据后应该把数据给 中间层 Model基类,
|
||||
中间层Model存储数据。
|
||||
|
||||
2:用户自定义的 XxxModel 继承 Model类,用户自定义的 XxxModel 通过自定义的装饰器实现和Model中存储
|
||||
的ajax数据进行数据的映射和复制,伪代码如下
|
||||
|
||||
// ajax 数据
|
||||
{
|
||||
name: '刘兵',
|
||||
age: 18,
|
||||
like: ['编程','骑车']
|
||||
}
|
||||
|
||||
// 基类Model
|
||||
class Model {}
|
||||
|
||||
// 关联基类Model 和 ajax 数据的具体model,
|
||||
// 核心数据关联在 装饰器 @Mapping() 中实现。
|
||||
@Injectable()
|
||||
export class IndexTestModel extends Model {
|
||||
|
||||
@Mapping("name")
|
||||
public myName: string;
|
||||
|
||||
@Mapping("id")
|
||||
public myId: number;
|
||||
|
||||
@Mapping("like")
|
||||
public myLike!: Array<string>;
|
||||
}
|
||||
|
||||
3:原本步骤5中再依赖注入 XxxModel,从 XxxModel 中获取数据,然后渲染Vue的Tsx页面,实现的效果如下:
|
||||
|
||||
@Inject()
|
||||
public readonly IndexModel!: IndexTestModel;
|
||||
|
||||
<ul>
|
||||
{
|
||||
this.IndexModel.myLike.map(v => {
|
||||
return <li>{ v }</li>
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
|
||||
|
||||
对于步骤1 和 步骤2 实现具体过程的详细思路拆解和代码过程
|
||||
|
||||
状态:待思考 ---> 思考介绍 ---> 待实现!
|
||||
|
||||
角色:
|
||||
|
||||
1:基类Model
|
||||
主要封装对存放在存取器里面的数据进行增删改读的一些操作
|
||||
|
||||
2:GET,POST 等..这些请求装饰器,获取到数据后,将数据构造为对象,格式为:
|
||||
{
|
||||
IndexTestModel: ajax 数据
|
||||
}
|
||||
|
||||
然后存入存取器中。
|
||||
|
||||
3:在用户在自定义的Model中,例如 IndexTestModel 中使用属性装饰器 @Mapping("字段名")装饰类的属性,
|
||||
@Mapping 可以获取被装饰属性的类,根据类名去存取器中获取对应的ajax数据,
|
||||
然后 @Mapping 装饰器可也以获得被装饰属性的名称,使用名称key去寻找ajax里面的值value
|
||||
|
||||
最后在 @Mapping装饰器中将数值取出,赋值给被装饰的类属性
|
||||
|
||||
4:页面上使用 用户自定的model,因为model继承基类model,所以基类model可以实现很多其他骚操作或者常用的
|
||||
一些方法,方便拓展!
|
||||
|
||||
Reference in New Issue
Block a user