mirror of https://github.com/abpframework/abp.git
286 changed files with 19517 additions and 2201 deletions
@ -0,0 +1,6 @@ |
|||||
|
# Getting Started with the Startup Templates |
||||
|
|
||||
|
See the following tutorials to learn how to get started with the ABP Framework using the pre-built application startup templates: |
||||
|
|
||||
|
* [Getting Started With the ASP.NET Core MVC / Razor Pages UI](Getting-Started-AspNetCore-MVC-Template.md) |
||||
|
* [Getting Started with the Angular UI](Getting-Started-Angular-Template.md) |
||||
@ -0,0 +1,3 @@ |
|||||
|
# Object Extensions |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,209 @@ |
|||||
|
# How to Make HTTP Requests |
||||
|
|
||||
|
|
||||
|
|
||||
|
## About HttpClient |
||||
|
|
||||
|
Angular has the amazing [HttpClient](https://angular.io/guide/http) for communication with backend services. It is a layer on top and a simplified representation of [XMLHttpRequest Web API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). It also is the recommended agent by Angular for any HTTP request. There is nothing wrong with using the `HttpClient` in your ABP project. |
||||
|
|
||||
|
However, `HttpClient` leaves error handling to the caller (method). In other words, HTTP errors are handled manually and by hooking into the observer of the `Observable` returned. |
||||
|
|
||||
|
```js |
||||
|
getConfig() { |
||||
|
this.http.get(this.configUrl).subscribe( |
||||
|
config => this.updateConfig(config), |
||||
|
error => { |
||||
|
// Handle error here |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Although clear and flexible, handling errors this way is repetitive work, even when error processing is delegated to the store or any other injectable. |
||||
|
|
||||
|
An `HttpInterceptor` is able to catch `HttpErrorResponse` and can be used for a centralized error handling. Nevertheless, cases where default error handler, therefore the interceptor, must be disabled require additional work and comprehension of Angular internals. Check [this issue](https://github.com/angular/angular/issues/20203) for details. |
||||
|
|
||||
|
|
||||
|
|
||||
|
## RestService |
||||
|
|
||||
|
ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by deafult. |
||||
|
|
||||
|
|
||||
|
|
||||
|
### Getting Started with RestService |
||||
|
|
||||
|
In order to use the `RestService`, you must inject it in your class as a dependency. |
||||
|
|
||||
|
```js |
||||
|
import { RestService } from '@abp/ng.core'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
/* class metadata here */ |
||||
|
}) |
||||
|
class DemoService { |
||||
|
constructor(private rest: RestService) {} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
You do not have to provide the `RestService` at module or component/directive level, because it is already **provided in root**. |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Make a Request with RestService |
||||
|
|
||||
|
You can use the `request` method of the `RestService` is for HTTP requests. Here is an example: |
||||
|
|
||||
|
```js |
||||
|
getFoo(id: number) { |
||||
|
const request: Rest.Request<null> = { |
||||
|
method: 'GET', |
||||
|
url: '/api/some/path/to/foo/' + id, |
||||
|
}; |
||||
|
|
||||
|
return this.rest.request<null, FooResponse>(request); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
The `request` method always returns an `Observable<T>`. Therefore you can do the following wherever you use `getFoo` method: |
||||
|
|
||||
|
```js |
||||
|
doSomethingWithFoo(id: number) { |
||||
|
this.demoService.getFoo(id).subscribe( |
||||
|
foo => { |
||||
|
// Do something with foo. |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
**You do not have to worry about unsubscription.** The `RestService` uses `HttpClient` behind the scenes, so every observable it returns is a finite observable, i.e. it closes subscriptions automatically upon success or error. |
||||
|
|
||||
|
|
||||
|
|
||||
|
As you see, `request` method gets a request options object with `Rest.Request<T>` type. This generic type expects the interface of the request body. You may pass `null` when there is no body, like in a `GET` or a `DELETE` request. Here is an example where there is one: |
||||
|
|
||||
|
```js |
||||
|
postFoo(body: Foo) { |
||||
|
const request: Rest.Request<Foo> = { |
||||
|
method: 'POST', |
||||
|
url: '/api/some/path/to/foo', |
||||
|
body |
||||
|
}; |
||||
|
|
||||
|
return this.rest.request<Foo, FooResponse>(request); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
You may [check here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23) for complete `Rest.Request<T>` type, which has only a few chages compared to [HttpRequest](https://angular.io/api/common/http/HttpRequest) class in Angular. |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Disable Default Error Handler of RestService |
||||
|
|
||||
|
The `request` method, used with defaults, always handles errors. Let's see how you can change that behavior and handle errors yourself: |
||||
|
|
||||
|
```js |
||||
|
deleteFoo(id: number) { |
||||
|
const request: Rest.Request<null> = { |
||||
|
method: 'DELETE', |
||||
|
url: '/api/some/path/to/foo/' + id, |
||||
|
}; |
||||
|
|
||||
|
return this.rest.request<null, void>(request, { skipHandleError: true }); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
`skipHandleError` config option, when set to `true`, disables the error handler and the returned observable starts throwing an error that you can catch in your subscription. |
||||
|
|
||||
|
```js |
||||
|
removeFooFromList(id: number) { |
||||
|
this.demoService.deleteFoo(id).subscribe( |
||||
|
foo => { |
||||
|
// Do something with foo. |
||||
|
}, |
||||
|
error => { |
||||
|
// Do something with error. |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Get a Specific API Endpoint From Application Config |
||||
|
|
||||
|
Another nice config option that `request` method receives is `apiName` (available as of v2.4), which can be used to get a specific module endpoint from application configuration. |
||||
|
|
||||
|
|
||||
|
|
||||
|
```js |
||||
|
putFoo(body: Foo, id: string) { |
||||
|
const request: Rest.Request<Foo> = { |
||||
|
method: 'PUT', |
||||
|
url: '/' + id, |
||||
|
body |
||||
|
}; |
||||
|
|
||||
|
return this.rest.request<Foo, void>(request, {apiName: 'foo'}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
`putFoo` above will request `https://localhost:44305/api/some/path/to/foo/{id}` as long as the environment variables are as follows: |
||||
|
|
||||
|
```js |
||||
|
// environment.ts |
||||
|
|
||||
|
export const environment = { |
||||
|
apis: { |
||||
|
default: { |
||||
|
url: 'https://localhost:44305', |
||||
|
}, |
||||
|
foo: { |
||||
|
url: 'https://localhost:44305/api/some/path/to/foo', |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
/* rest of the environment variables here */ |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Observe Response Object or HTTP Events Instead of Body |
||||
|
|
||||
|
`RestService` assumes you are generally interested in the body of a response and, by default, sets `observe` property as `'body'`. However, there may be times you are rather interested in something else, such as a custom proprietary header. For that, the `request` method receives `observe` property in its config object. |
||||
|
|
||||
|
```js |
||||
|
getSomeCustomHeaderValue() { |
||||
|
const request: Rest.Request<null> = { |
||||
|
method: 'GET', |
||||
|
url: '/api/some/path/that/sends/some-custom-header', |
||||
|
}; |
||||
|
|
||||
|
return this.rest.request<null, HttpResponse<any>>( |
||||
|
request, |
||||
|
{observe: Rest.Observe.Response}, |
||||
|
).pipe( |
||||
|
map(response => response.headers.get('Some-Custom-Header')) |
||||
|
); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10). |
||||
|
|
||||
|
## What's Next? |
||||
|
|
||||
|
* [Localization](./Localization.md) |
||||
@ -0,0 +1,67 @@ |
|||||
|
## Service Proxies |
||||
|
|
||||
|
It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on the server side) and **model objects** (matches to [DTOs](../../Data-Transfer-Objects) in the server side). |
||||
|
|
||||
|
In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced: |
||||
|
|
||||
|
* It generates a **big, single** .ts file which has some problems; |
||||
|
* It get **too large** when your application grows. |
||||
|
* It doesn't fit into the **[modular](../../Module-Development-Basics) approach** of the ABP framework. |
||||
|
* It creates a bit **ugly code**. We want to have a clean code (just like if we write manually). |
||||
|
* It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies. |
||||
|
|
||||
|
ABP CLI `generate-proxies` command automatically generates the typescript client proxies by creating folders which separated by module names in the `src/app` folder. |
||||
|
Run the following command in the **root folder** of the angular application: |
||||
|
|
||||
|
```bash |
||||
|
abp generate-proxy |
||||
|
``` |
||||
|
|
||||
|
It only creates proxies only for your own application's services. It doesn't create proxies for the services of the application modules you're using (by default). There are several options. See the [CLI documentation](../../CLI). |
||||
|
|
||||
|
The files generated with the `--module all` option like below: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
### Services |
||||
|
|
||||
|
Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests#restservice). |
||||
|
|
||||
|
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config) |
||||
|
|
||||
|
The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below: |
||||
|
|
||||
|
```js |
||||
|
import { AbpApplicationConfigurationService } from '../app/shared/services'; |
||||
|
|
||||
|
//... |
||||
|
export class HomeComponent{ |
||||
|
constructor(private appConfigService: AbpApplicationConfigurationService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.appConfigService.get().subscribe() |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The Angular compiler removes the services that have not been injected anywhere from the final output. See the [tree-shakable providers documentation](https://angular.io/guide/dependency-injection-providers#tree-shakable-providers). |
||||
|
|
||||
|
### Models |
||||
|
|
||||
|
The generated models match the DTOs in the back-end. Each model is generated as a class under the `src/app/*/shared/models` folder. |
||||
|
|
||||
|
There are a few [base classes](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/dtos.ts) in the `@abp/ng.core` package. Some models extend these classes. |
||||
|
|
||||
|
A class instance can be created as shown below: |
||||
|
|
||||
|
```js |
||||
|
import { IdentityRoleCreateDto } from '../identity/shared/models'; |
||||
|
//... |
||||
|
const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, isPublic: true}) |
||||
|
``` |
||||
|
|
||||
|
Initial values can optionally be passed to each class constructor. |
||||
|
|
||||
|
## What's Next? |
||||
|
|
||||
|
* [HTTP Requests](./Http-Requests) |
||||
@ -0,0 +1,113 @@ |
|||||
|
# Easy TrackByFunction Implementation |
||||
|
|
||||
|
`TrackByService` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose. |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Getting Started |
||||
|
|
||||
|
You do not have to provide the `TrackByService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components. For better type support, you may pass in the type of the iterated item to it. |
||||
|
|
||||
|
```js |
||||
|
import { TrackByService } from '@abp/ng.core'; |
||||
|
|
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
}) |
||||
|
class DemoComponent { |
||||
|
list: Item[]; |
||||
|
|
||||
|
constructor(public readonly track: TrackByService<Item>) {} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
> Noticed `track` is `public` and `readonly`? That is because we will see some examples where methods of `TrackByService` instance are directly called in the component's template. That may be considered as an anti-pattern, but it has its own advantage, especially when component inheritance is leveraged. You can always use public component properties instead. |
||||
|
|
||||
|
|
||||
|
|
||||
|
**The members are also exported as separate functions.** If you do not want to inject `TrackByService`, you can always import and use those functions directly in your classes. |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
There are two approaches available. |
||||
|
|
||||
|
1. You may inject `TrackByService` to your component and use its members. |
||||
|
2. You may use exported higher-order functions directly on component properties. |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Track Items by a Key |
||||
|
|
||||
|
You can use `by` to get a `TrackByFunction` that tracks the iterated object based on one of its keys. For type support, you may pass in the type of the iterated item to it. |
||||
|
|
||||
|
```html |
||||
|
<!-- template of DemoComponent --> |
||||
|
|
||||
|
<div *ngFor="let item of list; trackBy: track.by('id')">{%{{{ item.name }}}%}</div> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
`by` is exported as a stand-alone function and is named `trackBy`. |
||||
|
|
||||
|
```js |
||||
|
import { trackBy } from "@abp/ng.core"; |
||||
|
|
||||
|
@Component({ |
||||
|
template: ` |
||||
|
<div |
||||
|
*ngFor="let item of list; trackBy: trackById" |
||||
|
> |
||||
|
{%{{{ item.name }}}%} |
||||
|
</div> |
||||
|
`, |
||||
|
}) |
||||
|
class DemoComponent { |
||||
|
list: Item[]; |
||||
|
|
||||
|
trackById = trackBy<Item>('id'); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
### How to Track by a Deeply Nested Key |
||||
|
|
||||
|
You can use `byDeep` to get a `TrackByFunction` that tracks the iterated object based on a deeply nested key. For type support, you may pass in the type of the iterated item to it. |
||||
|
|
||||
|
```html |
||||
|
<!-- template of DemoComponent --> |
||||
|
|
||||
|
<div |
||||
|
*ngFor="let item of list; trackBy: track.byDeep('tenant', 'account', 'id')" |
||||
|
> |
||||
|
{%{{{ item.tenant.name }}}%} |
||||
|
</div> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
`byDeep` is exported as a stand-alone function and is named `trackByDeep`. |
||||
|
|
||||
|
```js |
||||
|
import { trackByDeep } from "@abp/ng.core"; |
||||
|
|
||||
|
@Component({ |
||||
|
template: ` |
||||
|
<div |
||||
|
*ngFor="let item of list; trackBy: trackByTenantAccountId" |
||||
|
> |
||||
|
{%{{{ item.name }}}%} |
||||
|
</div> |
||||
|
`, |
||||
|
}) |
||||
|
class DemoComponent { |
||||
|
list: Item[]; |
||||
|
|
||||
|
trackByTenantAccountId = trackByDeep<Item>('tenant', 'account', 'id'); |
||||
|
} |
||||
|
``` |
||||
|
After Width: | Height: | Size: 235 KiB |
@ -0,0 +1,3 @@ |
|||||
|
# ABP Datatables.Net Integration for ASP.NET Core UI |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,92 @@ |
|||||
|
# Collapse |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-collapse-body` is the main container for showing and hiding content. `abp-collapse-id` is used to show and hide the content container. Can be triggered with both `abp-button` and `a` tags. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" /> |
||||
|
<a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a> |
||||
|
|
||||
|
<abp-collapse-body id="collapseExample"> |
||||
|
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-collapse-body> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [collapse demo page](https://bootstrap-taghelpers.abp.io/Components/Collapse) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### show |
||||
|
|
||||
|
A value indicates if the collapse body will be initialized visible or hidden. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### multi |
||||
|
|
||||
|
A value indicates if an `abp-collapse-body` can be shown or hidden by an element that can show/hide multiple collapse bodies. Basically, this attribute adds "multi-collapse" class to `abp-collapse-body`. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
Sample: |
||||
|
|
||||
|
````xml |
||||
|
<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a> |
||||
|
<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" /> |
||||
|
<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" /> |
||||
|
|
||||
|
<abp-row class="mt-3"> |
||||
|
<abp-column size-sm="_6"> |
||||
|
<abp-collapse-body id="FirstCollapseExample" multi="true"> |
||||
|
Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit |
||||
|
</abp-collapse-body> |
||||
|
</abp-column> |
||||
|
<abp-column size-sm="_6"> |
||||
|
<abp-collapse-body id="SecondCollapseExample" multi="true"> |
||||
|
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. |
||||
|
</abp-collapse-body> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
```` |
||||
|
|
||||
|
## Accordion example |
||||
|
|
||||
|
`abp-accordion` is the main container for the accordion items. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-accordion> |
||||
|
<abp-accordion-item title="Collapsible Group Item #1"> |
||||
|
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
<abp-accordion-item title="Collapsible Group Item #2"> |
||||
|
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
<abp-accordion-item title="Collapsible Group Item #3"> |
||||
|
Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
</abp-accordion> |
||||
|
```` |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### active |
||||
|
|
||||
|
A value indicates if the accordion item will be initialized visible or hidden. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### title |
||||
|
|
||||
|
A value indicates the visible title of the accordion item. Should be a string value. |
||||
@ -0,0 +1,97 @@ |
|||||
|
# Dropdowns |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-dropdown` is the main container for dropdown content. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown> |
||||
|
<abp-dropdown-button text="Dropdown button" /> |
||||
|
<abp-dropdown-menu> |
||||
|
<abp-dropdown-item href="#">Action</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Another action</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Something else here</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [dropdown demo page](https://bootstrap-taghelpers.abp.io/Components/Dropdowns) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### direction |
||||
|
|
||||
|
A value indicates which direction the dropdown buttons will be displayed to. Should be one of the following values: |
||||
|
|
||||
|
* `Down` (default value) |
||||
|
* `Up` |
||||
|
* `Right` |
||||
|
* `Left` |
||||
|
|
||||
|
### dropdown-style |
||||
|
|
||||
|
A value indicates if an `abp-dropdown-button` will have split icon for dropdown. Should be one of the following values: |
||||
|
|
||||
|
* `Single` (default value) |
||||
|
* `Split` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Menu items |
||||
|
|
||||
|
`abp-dropdown-menu` is the main container for dropdown menu items. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown> |
||||
|
<abp-dropdown-button button-type="Secondary" text="Dropdown"/> |
||||
|
<abp-dropdown-menu> |
||||
|
<abp-dropdown-header>Dropdown Header</abp-dropdown-header> |
||||
|
<abp-dropdown-item href="#">Action</abp-dropdown-item> |
||||
|
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item> |
||||
|
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item> |
||||
|
<abp-dropdown-divider/> |
||||
|
<abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text> |
||||
|
<abp-dropdown-item href="#">Something else here</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### align |
||||
|
|
||||
|
A value indicates which direction `abp-dropdown-menu` items will be aligned to. Should be one of the following values: |
||||
|
|
||||
|
* `Left` (default value) |
||||
|
* `Right` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-dropdown-menu` can also contain additional HTML elements like headings, paragraphs, dividers or form element. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown > |
||||
|
<abp-dropdown-button button-type="Secondary" text="Dropdown With Form"/> |
||||
|
<abp-dropdown-menu> |
||||
|
<form class="px-4 py-3"> |
||||
|
<abp-input asp-for="EmailAddress"></abp-input> |
||||
|
<abp-input asp-for="Password"></abp-input> |
||||
|
<abp-input asp-for="RememberMe"></abp-input> |
||||
|
<abp-button button-type="Primary" text="Sign In" type="submit" /> |
||||
|
</form> |
||||
|
<abp-dropdown-divider></abp-dropdown-divider> |
||||
|
<abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Forgot password?</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
@ -0,0 +1,78 @@ |
|||||
|
# List Groups |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-list-group` is the main container for list group content. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-list-group> |
||||
|
<abp-list-group-item>Cras justo odio</abp-list-group-item> |
||||
|
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> |
||||
|
<abp-list-group-item>Morbi leo risus</abp-list-group-item> |
||||
|
<abp-list-group-item>Vestibulum at eros</abp-list-group-item> |
||||
|
</abp-list-group> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [list groups demo page](https://bootstrap-taghelpers.abp.io/Components/ListGroups) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### flush |
||||
|
|
||||
|
A value indicates `abp-list-group` items to remove some borders and rounded corners to render list group items edge-to-edge in a parent container. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### active |
||||
|
|
||||
|
A value indicates if an `abp-list-group-item` to be active. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### disabled |
||||
|
|
||||
|
A value indicates if an `abp-list-group-item` to be disabled. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### href |
||||
|
|
||||
|
A value indicates if an `abp-list-group-item` has a link. Should be a string link value. |
||||
|
|
||||
|
### type |
||||
|
|
||||
|
A value indicates an `abp-list-group-item` style class with a stateful background and color. Should be one of the following values: |
||||
|
|
||||
|
* `Default` (default value) |
||||
|
* `Primary` |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
* `Link` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-list-group-item` can also contain additional HTML elements like spans. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
````xml |
||||
|
<abp-list-group> |
||||
|
<abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item> |
||||
|
<abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item> |
||||
|
<abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item> |
||||
|
</abp-list-group> |
||||
|
```` |
||||
@ -0,0 +1,81 @@ |
|||||
|
# Modals |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-modal` is a main element to create a modal. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> |
||||
|
|
||||
|
<abp-modal centered="true" size="Large" id="myModal"> |
||||
|
<abp-modal-header title="Modal title"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
Woohoo, you're reading this text in a modal! |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="Close"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [modals demo page](https://bootstrap-taghelpers.abp.io/Components/Modals) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### centered |
||||
|
|
||||
|
A value indicates the positioning of the modal. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### size |
||||
|
|
||||
|
A value indicates the size of the modal. Should be one of the following values: |
||||
|
|
||||
|
* `Default` (default value) |
||||
|
* `Small` |
||||
|
* `Large` |
||||
|
* `ExtraLarge` |
||||
|
|
||||
|
### static |
||||
|
|
||||
|
A value indicates if the modal will be static. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-modal-footer` can have multiple buttons with alignment option. |
||||
|
|
||||
|
Add `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` to your page. |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> |
||||
|
|
||||
|
<abp-modal centered="true" size="Large" id="myModal" static="true"> |
||||
|
<abp-modal-header title="Modal title"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
Woohoo, you're reading this text in a modal! |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
```` |
||||
|
|
||||
|
### button-alignment |
||||
|
|
||||
|
A value indicates the positioning of your modal footer buttons. Should be one of the following values: |
||||
|
|
||||
|
* `Default` (default value) |
||||
|
* `Start` |
||||
|
* `Center` |
||||
|
* `Around` |
||||
|
* `Between` |
||||
|
* `End` |
||||
@ -0,0 +1,57 @@ |
|||||
|
# Paginator |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-paginator` is the abp tag for pagination. Requires `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator> |
||||
|
```` |
||||
|
|
||||
|
Model: |
||||
|
|
||||
|
````xml |
||||
|
using Microsoft.AspNetCore.Mvc.RazorPages; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components |
||||
|
{ |
||||
|
public class PaginatorModel : PageModel |
||||
|
{ |
||||
|
public PagerModel PagerModel { get; set; } |
||||
|
|
||||
|
public void OnGet(int currentPage, string sort) |
||||
|
{ |
||||
|
PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [paginator demo page](https://bootstrap-taghelpers.abp.io/Components/Paginator) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### model |
||||
|
|
||||
|
`Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` type of model can be initialized with the following data: |
||||
|
|
||||
|
* `totalCount` |
||||
|
* `shownItemsCount` |
||||
|
* `currentPage` |
||||
|
* `pageSize` |
||||
|
* `pageUrl` |
||||
|
* `sort` (default null) |
||||
|
|
||||
|
### show-info |
||||
|
|
||||
|
A value indicates if an extra information about start, end and total records will be displayed. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
@ -0,0 +1,70 @@ |
|||||
|
# Progress Bars |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-progress-bar` is the abp tag for progress bar status. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-progress-bar value="70" /> |
||||
|
|
||||
|
<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar> |
||||
|
|
||||
|
<abp-progress-bar type="Success" value="40" strip="true"/> |
||||
|
|
||||
|
<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar> |
||||
|
|
||||
|
<abp-progress-group> |
||||
|
<abp-progress-part type="Success" value="25"/> |
||||
|
<abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part> |
||||
|
<abp-progress-part type="Primary" value="50" animation="true" strip="true" /> |
||||
|
</abp-progress-group> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [progress bars demo page](https://bootstrap-taghelpers.abp.io/Components/Progress-Bars) to see it in action. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### value |
||||
|
|
||||
|
A value indicates the current progress of the bar. |
||||
|
|
||||
|
### type |
||||
|
|
||||
|
A value indicates the background color of the progress bar. Should be one of the following values: |
||||
|
|
||||
|
* `Default` (default value) |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
|
||||
|
### min-value |
||||
|
|
||||
|
Minimum value of the progress bar. Default is 0. |
||||
|
|
||||
|
### max-value |
||||
|
|
||||
|
Maximum value of the progress bar. Default is 100. |
||||
|
|
||||
|
### strip |
||||
|
|
||||
|
A value indicates if the background style of the progress bar is stripped. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
|
|
||||
|
### animation |
||||
|
|
||||
|
A value indicates if the stripped background style of the progress bar is animated. Should be one of the following values: |
||||
|
|
||||
|
* `false` (default value) |
||||
|
* `true` |
||||
@ -0,0 +1,35 @@ |
|||||
|
# Tooltips |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
`abp-tooltip` is the abp tag for tooltips. |
||||
|
|
||||
|
Basic usage: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button abp-tooltip="Tooltip"> |
||||
|
Tooltip Default |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-top="Tooltip"> |
||||
|
Tooltip on top |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-right="Tooltip"> |
||||
|
Tooltip on right |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-bottom="Tooltip"> |
||||
|
Tooltip on bottom |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button disabled="true" abp-tooltip="Tooltip"> |
||||
|
Disabled button Tooltip |
||||
|
</abp-button> |
||||
|
```` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
See the [tooltips demo page](https://bootstrap-taghelpers.abp.io/Components/Tooltips) to see it in action. |
||||
File diff suppressed because it is too large
@ -1,3 +1,166 @@ |
|||||
# 自定义应用模块: 覆盖服务 |
# 自定义应用模块: 重写服务 |
||||
|
|
||||
TODO... |
你可能想要**更改**依赖模块的**行为(业务逻辑)**. 在这种情况下,你可以使用[依赖注入](Dependency-Injection.md)的能力替换服务,控制器甚至页面模型到你自己的实现. |
||||
|
|
||||
|
注册到依赖注入的任何类,包括ABP框架的服务都可以被**替换**. |
||||
|
|
||||
|
你可以根据自己的需求使用不同的选项,下面的章节中将介绍这些选项. |
||||
|
|
||||
|
> 请注意,某些服务方法可能不是virtual,你可能无法override,我们会通过设计将其virtual,如果你发现任何方法不可以被覆盖,请[创建一个issue](https://github.com/abpframework/abp/issues/new)或者你直接修改后并发送**pull request**到GitHub. |
||||
|
|
||||
|
## 替换接口 |
||||
|
|
||||
|
如果给定的服务定义了接口,像 `IdentityUserAppService` 类实现了 `IIdentityUserAppService` 接口,你可以为这个接口创建自己的实现并且替换当前的实现. 例如: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyIdentityUserAppService : IIdentityUserAppService, ITransientDependency |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
`MyIdentityUserAppService` 通过命名约定替换了 `IIdentityUserAppService` 的当前实现. 如果你的类名不匹配,你需要手动公开服务接口: |
||||
|
|
||||
|
````csharp |
||||
|
[ExposeServices(typeof(IIdentityUserAppService))] |
||||
|
public class TestAppService : IIdentityUserAppService, ITransientDependency |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
依赖注入系统允许为一个接口注册多个服务. 注入接口时会解析最后一个注入的服务. 显式的替换服务是一个好习惯. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````csharp |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(IIdentityUserAppService))] |
||||
|
public class TestAppService : IIdentityUserAppService, ITransientDependency |
||||
|
{ |
||||
|
//... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
使用这种方法, `IIdentityUserAppService` 接口将只会有一个实现. 也可以使用以下方法替换服务: |
||||
|
|
||||
|
````csharp |
||||
|
context.Services.Replace( |
||||
|
ServiceDescriptor.Transient<IIdentityUserAppService, MyIdentityUserAppService>() |
||||
|
); |
||||
|
```` |
||||
|
|
||||
|
你可以在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法编写替换服务代码. |
||||
|
|
||||
|
## 重写一个服务类 |
||||
|
|
||||
|
大多数情况下,你会仅想改变服务当前实现的一个或几个方法. 重新实现完整的接口变的繁琐,更好的方法是继承原始类并重写方法。 |
||||
|
|
||||
|
### 示例: 重写服务方法 |
||||
|
|
||||
|
````csharp |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class MyIdentityUserAppService : IdentityUserAppService |
||||
|
{ |
||||
|
//... |
||||
|
public MyIdentityUserAppService( |
||||
|
IdentityUserManager userManager, |
||||
|
IIdentityUserRepository userRepository, |
||||
|
IGuidGenerator guidGenerator |
||||
|
) : base( |
||||
|
userManager, |
||||
|
userRepository, |
||||
|
guidGenerator) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input) |
||||
|
{ |
||||
|
if (input.PhoneNumber.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
throw new AbpValidationException( |
||||
|
"Phone number is required for new users!", |
||||
|
new List<ValidationResult> |
||||
|
{ |
||||
|
new ValidationResult( |
||||
|
"Phone number can not be empty!", |
||||
|
new []{"PhoneNumber"} |
||||
|
) |
||||
|
} |
||||
|
); } |
||||
|
|
||||
|
return await base.CreateAsync(input); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
示例中**重写**了 `IdentityUserAppService` [应用程序](Application-Services.md) `CreateAsync` 方法检查手机号码. 然后调用了基类方法继续**基本业务逻辑**. 通过这种方法你可以在基本业务逻辑**之前**和**之后**执行其他业务逻辑. |
||||
|
|
||||
|
你也可以完全**重写**整个业务逻辑去创建用户,而不是调用基类方法. |
||||
|
|
||||
|
### 示例: 重写领域服务 |
||||
|
|
||||
|
````csharp |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(IdentityUserManager))] |
||||
|
public class MyIdentityUserManager : IdentityUserManager |
||||
|
{ |
||||
|
public MyIdentityUserManager( |
||||
|
IdentityUserStore store, |
||||
|
IOptions<IdentityOptions> optionsAccessor, |
||||
|
IPasswordHasher<IdentityUser> passwordHasher, |
||||
|
IEnumerable<IUserValidator<IdentityUser>> userValidators, |
||||
|
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators, |
||||
|
ILookupNormalizer keyNormalizer, |
||||
|
IdentityErrorDescriber errors, |
||||
|
IServiceProvider services, |
||||
|
ILogger<IdentityUserManager> logger, |
||||
|
ICancellationTokenProvider cancellationTokenProvider |
||||
|
) : base( |
||||
|
store, |
||||
|
optionsAccessor, |
||||
|
passwordHasher, |
||||
|
userValidators, |
||||
|
passwordValidators, |
||||
|
keyNormalizer, |
||||
|
errors, |
||||
|
services, |
||||
|
logger, |
||||
|
cancellationTokenProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override async Task<IdentityResult> CreateAsync(IdentityUser user) |
||||
|
{ |
||||
|
if (user.PhoneNumber.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
throw new AbpValidationException( |
||||
|
"Phone number is required for new users!", |
||||
|
new List<ValidationResult> |
||||
|
{ |
||||
|
new ValidationResult( |
||||
|
"Phone number can not be empty!", |
||||
|
new []{"PhoneNumber"} |
||||
|
) |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return await base.CreateAsync(user); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
示例中类继承了 `IdentityUserManager` [领域服务](Domain-Services.md),并且重写了 `CreateAsync` 方法进行了与之前相同的手机号码检查. 结果也是一样的,但是这次我们在领域服务实现了它,假设这是我们系统的**核心领域逻辑**. |
||||
|
|
||||
|
> 这里需要 `[ExposeServices(typeof(IdentityUserManager))]` attribute,因为 `IdentityUserManager` 没有定义接口 (像 `IIdentityUserManager`) ,依赖注入系统并不会按照约定公开继承类的服务(如已实现的接口). |
||||
|
|
||||
|
参阅[本地化系统](Localization.md)了解如何自定义错误消息. |
||||
|
|
||||
|
### 重写其他服务 |
||||
|
|
||||
|
控制器,框架服务,视图组件类以及其他类型注册到依赖注入的类都可以像上面的示例那样被重写. |
||||
|
|
||||
|
## 如何找到服务? |
||||
|
|
||||
|
[模块文档](Modules/Index.md) 包含了定义的主要服务列表. 另外 你也可以查看[源码](https://github.com/abpframework/abp/tree/dev/modules)找到所有的服务. |
||||
@ -0,0 +1,3 @@ |
|||||
|
## Getting Started With the Angular Application Template |
||||
|
|
||||
|
TODO... |
||||
@ -0,0 +1,6 @@ |
|||||
|
# 启动模板入门 |
||||
|
|
||||
|
参阅下面的教程来学习如何开始使用的ABP框架预构建的应用程序启动模板: |
||||
|
|
||||
|
* [ASP.NET Core MVC/Razor页面模板入门](Getting-Started-AspNetCore-MVC-Template.md) |
||||
|
* [Angular UI模板入门](Getting-Started-Angular-Template.md) |
||||
@ -1,3 +1,469 @@ |
|||||
# ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南 |
# ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南 |
||||
|
|
||||
TODO... |
本文档解释了如何重写ASP.NET Core MVC / Razor Page 应用程序依赖[应用模块](../../Modules/Index.md)的用户界面. |
||||
|
|
||||
|
## 重写页面 |
||||
|
|
||||
|
本节介绍了[Razor 页面](https://docs.microsoft.com/zh-cn/aspnet/core/razor-pages/)开发,它是ASP.NET Core推荐的服务端渲染用户页面的方法. 预构建的模块通常使用Razor页面替代经典的MVC方式(下一节也介绍MVC模式). |
||||
|
|
||||
|
你通过有三种重写页面的需求: |
||||
|
|
||||
|
* 仅**重写页面模型**(C#)端执行其他逻辑,不更改UI. |
||||
|
* 仅**重写Razor页面**(.cshtml文件),不更改逻辑. |
||||
|
* **完全重写** 页面. |
||||
|
|
||||
|
### 重写页面模型 (C#) |
||||
|
|
||||
|
````csharp |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Identity; |
||||
|
using Volo.Abp.Identity.Web.Pages.Identity.Users; |
||||
|
|
||||
|
namespace Acme.BookStore.Web.Pages.Identity.Users |
||||
|
{ |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
[ExposeServices(typeof(EditModalModel))] |
||||
|
public class MyEditModalModel : EditModalModel |
||||
|
{ |
||||
|
public MyEditModalModel( |
||||
|
IIdentityUserAppService identityUserAppService, |
||||
|
IIdentityRoleAppService identityRoleAppService |
||||
|
) : base( |
||||
|
identityUserAppService, |
||||
|
identityRoleAppService) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
//TODO: Additional logic |
||||
|
await base.OnPostAsync(); |
||||
|
//TODO: Additional logic |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
* 这个类继承并替换 `EditModalModel` ,重写了 `OnPostAsync` 方法在基类代码的前后执行附加逻辑 |
||||
|
* 它使用 `ExposeServices` 和 `Dependency` attributes去替换这个类. |
||||
|
|
||||
|
### 重写Razor页面 (.CSHTML) |
||||
|
|
||||
|
使用[虚拟文件系统](../../Virtual-File-System.md)可以重写 `.cshtml` 文件(razor page, razor view, view component... 等.) |
||||
|
|
||||
|
虚拟文件系统允许我们将**资源嵌入到程序集中**. 通过这个方式,预构建的模块在Nuget包中定义了Razor页面. 当你依赖模块时,可以覆盖这个模块向虚拟文件系统添加的任何文件,包括页面/视图. |
||||
|
|
||||
|
#### 示例 |
||||
|
|
||||
|
这个示例重写了[账户模块](../../Modules/Account.md)定义的**登录页面**UI |
||||
|
|
||||
|
物理文件可以覆盖相同位置的嵌入文件. 账户模块在 `Pages/Account` 文件夹下定义了 `Login.cshtml` 文件. 所以你可以在同一路径下创建文件覆盖它: |
||||
|
 |
||||
|
|
||||
|
通常你想要拷贝模块的 `.cshtml` 原文件,然后进行需要的更改. 你可以在[这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到源文件. 不要拷贝 `Login.cshtml.cs` 文件,它是隐藏razor页面的代码,我们不希望覆盖它(见下节). |
||||
|
|
||||
|
这就够了,接下来你可以对文件内容做你想要的更改. |
||||
|
|
||||
|
### 完全重写Razo页面 |
||||
|
|
||||
|
也许你想要完全重写页面,Razor和页面相关的C#文件. |
||||
|
|
||||
|
在这种情况下; |
||||
|
|
||||
|
1. 像上面描述过的那术重写C#页面模型类,但不需要替换已存在的页面模型类. |
||||
|
2. 像上面描述过的那样重写Razor页面,并且更改@model指向新的页面模型 |
||||
|
|
||||
|
#### 示例 |
||||
|
|
||||
|
这个示例重写了[账户模块](../../Modules/Account.md)定义的**登录页面** |
||||
|
|
||||
|
创建一个继承自 `LoginModel`(定义在`Volo.Abp.Account.Web.Pages.Account`命名空间下)的页面模型类: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyLoginModel : LoginModel |
||||
|
{ |
||||
|
public MyLoginModel( |
||||
|
IAuthenticationSchemeProvider schemeProvider, |
||||
|
IOptions<AbpAccountOptions> accountOptions |
||||
|
) : base( |
||||
|
schemeProvider, |
||||
|
accountOptions) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public override Task<IActionResult> OnPostAsync(string action) |
||||
|
{ |
||||
|
//TODO: Add logic |
||||
|
return base.OnPostAsync(action); |
||||
|
} |
||||
|
|
||||
|
//TODO: Add new methods and properties... |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
如果需要,你可以重写任何方法或添加新的属性/方法 |
||||
|
|
||||
|
> 注意我们没有使用 `[Dependency(ReplaceServices = true)]` 或 `[ExposeServices(typeof(LoginModel))]`,因为我们不想替换依赖注入中已存在的类,我们定义了一个新的. |
||||
|
|
||||
|
拷贝 `Login.cshtml` 到你们解决方案,更改 **@model** 指定到 `MyLoginModel`: |
||||
|
|
||||
|
````xml |
||||
|
@page |
||||
|
... |
||||
|
@model Acme.BookStore.Web.Pages.Account.MyLoginModel |
||||
|
... |
||||
|
```` |
||||
|
|
||||
|
这就够了,接下来你可以做任何想要更改. |
||||
|
|
||||
|
#### 不使用继承替换页面模型 |
||||
|
|
||||
|
你不需要继承源页面模型类(像之前的示例). 你可以完全**重写实现**你自己的页面. 在这种事情下你可以从 `PageModel`,`AbpPageModel` 或任何你需要的合适的基类派生. |
||||
|
|
||||
|
## 重写视图组件 |
||||
|
|
||||
|
在ABP框架,预构建的模块和主题定义了一些**可重用的视图组件**. 这些视图组件可以像页面一样被替换. |
||||
|
|
||||
|
### 示例 |
||||
|
|
||||
|
下面是应用程序启动模板自带的 **基本主题** 的截图. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
[基本主题](../../Themes/Basic.md) 为layout定义了一些视图组件. 例如上面带有红色矩形的突出显示区域称为 **Brand组件**, 你可能想添加自己的**自己的应用程序logo**来自定义此组件. 让我们来看看如何去做. |
||||
|
|
||||
|
首先创建你的logo并且放到你的web应用程序文件夹中,我们使用 `wwwroot/logos/bookstore-logo.png` 路径. 然后在 `Themes/Basic/Components/Brand` 文件夹下复制[Brand组件视图](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml). 结果应该是类似下面的图片: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
然后对 `Default.cshtml` 文件做你想要的更改. 例如内容可以是这样的: |
||||
|
|
||||
|
````xml |
||||
|
<a href="/"> |
||||
|
<img src="~/logos/bookstore-logo.png" width="250" height="60"/> |
||||
|
</a> |
||||
|
```` |
||||
|
|
||||
|
现在你可以运行应用程序看到结果: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
如果你需要,你也可以仅使用依赖注入系统替换组件[背后的C#类代码](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/MainNavbarBrandViewComponent.cs) |
||||
|
|
||||
|
### 重写主题 |
||||
|
|
||||
|
正如上所解释的,你可以更改任何组件,layout或c#类. 参阅[主题文档]了解更多关于主题系统的信息. |
||||
|
|
||||
|
## 重写静态资源 |
||||
|
|
||||
|
重写模块的静态资源(像JavaScript,Css或图片文件)是很简单的. 只需要在解决方案的相同路径创建文件,虚拟文件系统会自动处理它. |
||||
|
|
||||
|
## 操作捆绑 |
||||
|
|
||||
|
[捆绑 & 压缩](Bundling-Minification.md) 系统提供了**动态可扩展的** 系统去创建**script**和**style**捆绑. 它允许你扩展和操作现有的包. |
||||
|
|
||||
|
### 示例: 添加全局CSS文件 |
||||
|
|
||||
|
例如APP框架定义了一个**全局样式捆绑**添加到所有的页面(事实上由主题添加layout). 让我们添加一个**自定义样式文件**到这个捆绑文件的最后,我们可以覆盖任何全局样式. |
||||
|
|
||||
|
创建在 `wwwroot` 文件夹下创建一个CSS文件 |
||||
|
|
||||
|
 |
||||
|
|
||||
|
在CSS文件中定义一些规则. 例如: |
||||
|
|
||||
|
````css |
||||
|
.card-title { |
||||
|
color: orange; |
||||
|
font-size: 2em; |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.btn-primary { |
||||
|
background-color: red; |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
然后在你的[模块](../../Module-Development-Basics.md) `ConfigureServices` 方法添加这个文件到标准的全局样式捆绑包: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpBundlingOptions>(options => |
||||
|
{ |
||||
|
options.StyleBundles.Configure( |
||||
|
StandardBundles.Styles.Global, //The bundle name! |
||||
|
bundleConfiguration => |
||||
|
{ |
||||
|
bundleConfiguration.AddFiles("/styles/my-global-styles.css"); |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
#### 全局脚本捆绑包 |
||||
|
|
||||
|
就像 `StandardBundles.Styles.Global` 一样,还有一个 `StandardBundles.Scripts.Global`,你可以添加文件或操作现有文件. |
||||
|
|
||||
|
### 示例: 操作捆绑包文件 |
||||
|
|
||||
|
上面的示例中添加了新文件到捆绑包. 如果你创建 **bundle contributor** 类则可以做到更多. 示例: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyGlobalStyleBundleContributor : BundleContributor |
||||
|
{ |
||||
|
public override void ConfigureBundle(BundleConfigurationContext context) |
||||
|
{ |
||||
|
context.Files.Clear(); |
||||
|
context.Files.Add("/styles/my-global-styles.css"); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
然后你可以添加这个contributor到已存在的捆绑中: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpBundlingOptions>(options => |
||||
|
{ |
||||
|
options.StyleBundles.Configure( |
||||
|
StandardBundles.Styles.Global, |
||||
|
bundleConfiguration => |
||||
|
{ |
||||
|
bundleConfiguration.AddContributors(typeof(MyGlobalStyleBundleContributor)); |
||||
|
} |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
示例中清除了所有的CSS文件,在现实中这并不是一个好主意,你可以找到某个特定的文件替换成你自己的文件. |
||||
|
|
||||
|
### 示例: 为特定页面添加JavaScript文件 |
||||
|
|
||||
|
上面的示例将全局包添加到布局中. 如果要在依赖模块中为特定页面定义添加CSS/JavaScript文件(或替换文件)怎么做? |
||||
|
|
||||
|
假设你想要用户进入身份模块的**角色管理**页面时运行**JavaScript代码**. |
||||
|
|
||||
|
首先在 `wwwroot`, `Pages` 或 `Views` 文件夹下创建一个标准的JavaScript文件(默认ABP支持这些文件夹下的静态文件). 根据约定我们推荐 `Pages/Identity/Roles` 文件夹: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
该文件的内容很简单: |
||||
|
|
||||
|
````js |
||||
|
$(function() { |
||||
|
abp.log.info('My custom role script file has been loaded!'); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
然后将这个文件添加到角色管页面理捆绑包中: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpBundlingOptions>(options => |
||||
|
{ |
||||
|
options.ScriptBundles |
||||
|
.Configure( |
||||
|
typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName, |
||||
|
bundleConfig => |
||||
|
{ |
||||
|
bundleConfig.AddFiles("/Pages/Identity/Roles/my-role-script.js"); |
||||
|
}); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
`typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName` 是获取角色管理页面捆绑包名称的安全方式: |
||||
|
|
||||
|
> 请注意并非每个页面都定义了这个页面的捆绑包. 它们仅在需要时定义. |
||||
|
|
||||
|
除了添加新的CSS/JavaScript文件到页面,你也可以以替换(通过捆绑包contributor)已存在. |
||||
|
|
||||
|
## 布局定制 |
||||
|
|
||||
|
布局由主题([参阅主题](Theming.md))定义设计. 它们不包含在下载的应用程序解决方案中. 通过这种方式你可以轻松的**更改**主题并获取新的功能. 你不能**直接更改**应用程序中的布局代码,除非你用自己的布局替换它(在下一部分中说明). |
||||
|
|
||||
|
有一些通用的方法可以**自定义布局**,将在下一节中介绍. |
||||
|
|
||||
|
### 菜单贡献者 |
||||
|
|
||||
|
ABP框架定义了两个**标准菜单**: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
* `StandardMenus.Main`: 应用程序的主菜单. |
||||
|
* `StandardMenus.User`: 用户菜单 (通常在屏幕的右上方). |
||||
|
|
||||
|
显示菜单是主题的责任,但**菜单项**由模板和你的应用程序代码决定. 只需要实现 `IMenuContributor` 接口并在 `ConfigureMenuAsync` 方法操作菜单项. |
||||
|
|
||||
|
渲染菜单时需要执行菜单贡献者. **应用程序启动模板** 已经定义了菜单贡献者,所以你可以使用它. 参阅[导航菜单](Navigation-Menu.md)文档了解更多. |
||||
|
|
||||
|
### 工具栏贡献者 |
||||
|
|
||||
|
[工具栏系统](Toolbars.md)用于在用户界面定义 **工具栏** . 模块 (或你的应用程序)可以将 **项** 添加到工具栏, 随后主题将在**布局**上呈现工具栏. |
||||
|
|
||||
|
只有一个 **标准工具栏** (名称为 "Main" - 定义为常量: `StandardToolbars.Main`). 对于基本主题,按如下呈现: |
||||
|
|
||||
|
在上面的屏幕快照中,主工具栏添加了两个项目:语言开关组件和用户菜单. 你可以在此处添加自己的项. |
||||
|
|
||||
|
#### 示例: 添加通知图标 |
||||
|
|
||||
|
在这个示例中,我们会添加一个**通知(响铃)图标**到语言切换项的左侧. 工具栏的项项目是一个**视图组件**. 所以,在你的项目中创建一个新的视图组件: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
**NotificationViewComponent.cs** |
||||
|
|
||||
|
````csharp |
||||
|
public class NotificationViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
public async Task<IViewComponentResult> InvokeAsync() |
||||
|
{ |
||||
|
return View("/Pages/Shared/Components/Notification/Default.cshtml"); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
**Default.cshtml** |
||||
|
|
||||
|
````xml |
||||
|
<div id="MainNotificationIcon" style="color: white; margin: 8px;"> |
||||
|
<i class="far fa-bell"></i> |
||||
|
</div> |
||||
|
```` |
||||
|
|
||||
|
现在,我们创建一个类实现 `IToolbarContributor` 接口: |
||||
|
|
||||
|
````csharp |
||||
|
public class MyToolbarContributor : IToolbarContributor |
||||
|
{ |
||||
|
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) |
||||
|
{ |
||||
|
if (context.Toolbar.Name == StandardToolbars.Main) |
||||
|
{ |
||||
|
context.Toolbar.Items |
||||
|
.Insert(0, new ToolbarItem(typeof(NotificationViewComponent))); |
||||
|
} |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
这个类向 `Main` 工具栏的第一项添加了 `NotificationViewComponent`. |
||||
|
|
||||
|
最后你需要将这个贡献者添加到 `AbpToolbarOptions`,在你模块类的 `ConfigureServices` 方法: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpToolbarOptions>(options => |
||||
|
{ |
||||
|
options.Contributors.Add(new MyToolbarContributor()); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
这就够了,当你运行应用程序后会看到工具栏上的通知图标: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
示例中的 `NotificationViewComponent` 返回没有任何数据的视图. 在实际场景中,你可能想**查询数据库**(或调用HTTP API)获取通知并传递给视图. 如果需要可以将 `JavaScript` 或 `CSS` 文件添加到工具栏的全局捆绑包中(如前所述). |
||||
|
|
||||
|
参阅[工具栏文档](Toolbars.md)了解更多关于工具栏系统. |
||||
|
|
||||
|
### 布局钩子 |
||||
|
|
||||
|
[布局钩子](Layout-Hooks.md) 系统允许你在布局页面的某些特定部分 **添加代码** . 所有主题的所有布局都应该实现这些钩子. 然后你可以将**视图组件**添加到钩子. |
||||
|
|
||||
|
#### 示例: 添加谷歌统计 |
||||
|
|
||||
|
假设你想要添加谷歌统计脚本到布局(将适用所有的页面). 首先在你的项目中**创建一个视图组件**: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
**NotificationViewComponent.cs** |
||||
|
|
||||
|
````csharp |
||||
|
public class GoogleAnalyticsViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult Invoke() |
||||
|
{ |
||||
|
return View("/Pages/Shared/Components/GoogleAnalytics/Default.cshtml"); |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
**Default.cshtml** |
||||
|
|
||||
|
````html |
||||
|
<script> |
||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ |
||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), |
||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) |
||||
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); |
||||
|
|
||||
|
ga('create', 'UA-xxxxxx-1', 'auto'); |
||||
|
ga('send', 'pageview'); |
||||
|
</script> |
||||
|
```` |
||||
|
|
||||
|
在你自己的代码中更改 `UA-xxxxxx-1` . |
||||
|
|
||||
|
然后你可以在你模块的 `ConfigureServices` 方法将这个组件添加到任何的钩子点: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpLayoutHookOptions>(options => |
||||
|
{ |
||||
|
options.Add( |
||||
|
LayoutHooks.Head.Last, //The hook name |
||||
|
typeof(GoogleAnalyticsViewComponent) //The component to add |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
现在谷歌统计代码将在页面的 `head` 所为最后一项插入. 你(或你在使用的模块)可以将多个项添加到相同的钩子,它们都会添加到布局. |
||||
|
|
||||
|
在上面我们添加 `GoogleAnalyticsViewComponent` 到所有的布局,你可能只想添加到指定的布局: |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpLayoutHookOptions>(options => |
||||
|
{ |
||||
|
options.Add( |
||||
|
LayoutHooks.Head.Last, |
||||
|
typeof(GoogleAnalyticsViewComponent), |
||||
|
layout: StandardLayouts.Application //Set the layout to add |
||||
|
); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
参阅下面的布局部分,以了解有关布局系统的更多信息. |
||||
|
|
||||
|
### 布局 |
||||
|
|
||||
|
布局系统允许主题定义标准,命名布局并且允许任何页面选择使用合适的布局. 有三种预定义的布局: |
||||
|
|
||||
|
* "**Application**": 应用程序的主要(和默认)布局. 它通常包含页眉,菜单(侧栏),页脚,工具栏等. |
||||
|
* "**Account**": 登录,注册和其他类似页面使用此布局. 默认它用于 `/Pages/Account` 文件夹下的页面. |
||||
|
* "**Empty**": 空的最小的布局. |
||||
|
|
||||
|
这些名称在 `StandardLayouts` 类定义为常量. 这是标准的布局名称,所有的主题开箱即用的实现. 你也可以创建自己的布局. |
||||
|
|
||||
|
#### 布局位置 |
||||
|
|
||||
|
你可以在[这里](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts)找到基本主题的布局文件. 你可以将它们作用构建自己的布局的参考,也可以在必要时覆盖它们. |
||||
|
|
||||
|
#### ITheme |
||||
|
|
||||
|
ABP框架使用 `ITheme` 服务通过局部名称获取布局位置. 你可以替换此服务动态的选择布局位置. |
||||
|
|
||||
|
#### IThemeManager |
||||
|
|
||||
|
`IThemeManager` 用于获取当前主题,并得到了布局路径. 任何页面可以都决定自己的布局. 例: |
||||
|
|
||||
|
````html |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theming |
||||
|
@inject IThemeManager ThemeManager |
||||
|
@{ |
||||
|
Layout = ThemeManager.CurrentTheme.GetLayout(StandardLayouts.Empty); |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
此页面将使用空白布局. 它使用 `ThemeManager.CurrentTheme.GetEmptyLayout()` 扩展方法. |
||||
|
|
||||
|
如果你设置特定目录下所有页面的布局,可以在该文件夹下的 `_ViewStart.cshtml` 文件编写以上代码. |
||||
|
|||||
@ -0,0 +1,80 @@ |
|||||
|
# Alerts |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-alert` 是创建alert的主要元素. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-alert alert-type="Primary"> |
||||
|
A simple primary alert—check it out! |
||||
|
</abp-alert> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[alerts demo页面](https://bootstrap-taghelpers.abp.io/Components/Alerts)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### alert-type |
||||
|
|
||||
|
值做为alert的Type,应为以下值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Primary` |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-alert alert-type="Warning"> |
||||
|
A simple warning alert—check it out! |
||||
|
</abp-alert> |
||||
|
```` |
||||
|
|
||||
|
### alert-link |
||||
|
|
||||
|
alert的链接. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-alert alert-type="Danger"> |
||||
|
A simple danger alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like. |
||||
|
</abp-alert> |
||||
|
```` |
||||
|
|
||||
|
### dismissible |
||||
|
|
||||
|
使alert可被忽略: |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-alert alert-type="Warning" dismissible="true"> |
||||
|
Holy guacamole! You should check in on some of those fields below. |
||||
|
</abp-alert> |
||||
|
```` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-alert`还可以包含其他HTML元素,例如标题,段落和分隔符. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-alert alert-type="Success"> |
||||
|
<h4>Well done!</h4> |
||||
|
<p>Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.</p> |
||||
|
<hr> |
||||
|
<p class="mb-0">Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p> |
||||
|
</abp-alert> |
||||
|
```` |
||||
@ -0,0 +1,180 @@ |
|||||
|
# 卡片 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-card` 是从bootstrap card元素派生的内容容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-card style="width: 18rem;"> |
||||
|
<img abp-card-image="Top" src="~/imgs/demo/300x200.png"/> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title>Card Title</abp-card-title> |
||||
|
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
||||
|
<a abp-button="Primary" href="#"> Go somewhere</a> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
```` |
||||
|
|
||||
|
##### 使用标题,文本和链接: |
||||
|
|
||||
|
`abp-card` 可以使用以下标签 |
||||
|
|
||||
|
* `abp-card-title` |
||||
|
* `abp-card-subtitle` |
||||
|
* `a abp-card-link` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-card style="width: 18rem;"> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title>Card title</abp-card-title> |
||||
|
<abp-card-subtitle class="mb-2 text-muted">Card subtitle</abp-card-subtitle> |
||||
|
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
||||
|
<a abp-card-link href="#">Card link</a> |
||||
|
<a abp-card-link href="#">Another link</a> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
```` |
||||
|
|
||||
|
##### 使用列表组: |
||||
|
|
||||
|
* `abp-list-group flush="true"` : `flush` 属性渲染到 bootstrap `list-group-flush` 的 calss 中,该类用于删除边界和圆角以使列表组项在父容器中并排显示. |
||||
|
* `abp-list-group-item` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-card style="width: 18rem;"> |
||||
|
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" /> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title>Card Title</abp-card-title> |
||||
|
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text> |
||||
|
</abp-card-body> |
||||
|
<abp-list-group flush="true"> |
||||
|
<abp-list-group-item>Cras justo odio</abp-list-group-item> |
||||
|
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> |
||||
|
<abp-list-group-item>Vestibulum at eros</abp-list-group-item> |
||||
|
</abp-list-group> |
||||
|
<abp-card-body> |
||||
|
<a abp-card-link href="#">Card link</a> |
||||
|
<a abp-card-link href="#">Another link</a> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
```` |
||||
|
|
||||
|
##### 使用页眉,页脚和块引用: |
||||
|
|
||||
|
* `abp-card-header` |
||||
|
* `abp-card-footer` |
||||
|
* `abp-blockquote` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-card style="width: 18rem;"> |
||||
|
<abp-card-header>Featured</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title> Special title treatment</abp-card-title> |
||||
|
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text> |
||||
|
<a abp-button="Primary" href="#"> Go somewhere</a> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
``` |
||||
|
|
||||
|
引用示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-card> |
||||
|
<abp-card-header>Quote</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-blockquote> |
||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
||||
|
<footer>Someone famous in Source Title</footer> |
||||
|
</abp-blockquote> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
``` |
||||
|
|
||||
|
页脚示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-card class="text-center"> |
||||
|
<abp-card-header>Featured</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-blockquote> |
||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> |
||||
|
<footer>Someone famous in Source Title</footer> |
||||
|
</abp-blockquote> |
||||
|
</abp-card-body> |
||||
|
<abp-card-footer class="text-muted"> 2 days ago</abp-card-footer> |
||||
|
</abp-card> |
||||
|
``` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[卡片demo页面](https://bootstrap-taghelpers.abp.io/Components/Cards)查看示例. |
||||
|
|
||||
|
## abp-card Attributes |
||||
|
|
||||
|
- **background:** 值指定卡片背景的颜色. |
||||
|
- **text-color**: 值指定卡片内文本的颜色. |
||||
|
- **border:** 值指定卡片边框的颜色. |
||||
|
|
||||
|
应为以下值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Primary` |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-card background="Success" text-color="Danger" border="Dark"> |
||||
|
```` |
||||
|
|
||||
|
### sizing |
||||
|
|
||||
|
卡片的默认值为100%,可以使用自定义CSS,栅格类,栅格Sass mixins或[utilities](https://getbootstrap.com/docs/4.0/utilities/sizing/)进行更改. |
||||
|
|
||||
|
````xml |
||||
|
<abp-card style="width: 18rem;"> |
||||
|
```` |
||||
|
|
||||
|
### card-deck 和 card-columns |
||||
|
|
||||
|
`abp-card` 可以在 `card-deck` 或 `card-columns` 里使用. |
||||
|
|
||||
|
````xml |
||||
|
<div class="card-deck"> |
||||
|
<abp-card background="Primary"> |
||||
|
<abp-card-header>First Deck</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title> Ace </abp-card-title> |
||||
|
<abp-card-text>Here is the content for Ace.</abp-card-text> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
<abp-card background="Info"> |
||||
|
<abp-card-header>Second Deck</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title> Beta </abp-card-title> |
||||
|
<abp-card-text>Beta content.</abp-card-text> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
<abp-card background="Warning"> |
||||
|
<abp-card-header>Third Deck</abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-card-title> Epsilon </abp-card-title> |
||||
|
<abp-card-text>Content for Epsilon.</abp-card-text> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
</div> |
||||
|
```` |
||||
@ -0,0 +1,90 @@ |
|||||
|
# Collapse |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-collapse-body` 是显示和隐藏内容的主要容器. `abp-collapse-id` 用于显示和隐藏内容容器. 可以通过 `abp-button` 和 `a` 标签触发. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" /> |
||||
|
<a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a> |
||||
|
|
||||
|
<abp-collapse-body id="collapseExample"> |
||||
|
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-collapse-body> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[collapse demo 页面](https://bootstrap-taghelpers.abp.io/Components/Collapse)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### show |
||||
|
|
||||
|
指定折叠主体初始化时是折叠还是展开. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### multi |
||||
|
|
||||
|
指定 `abp-collapse-body` 是否可以通过显示/隐藏多个折叠体的元素显示或隐藏. 此属性做为 "multi-collapse" class 添加到 `abp-collapse-body`. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a> |
||||
|
<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" /> |
||||
|
<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" /> |
||||
|
|
||||
|
<abp-row class="mt-3"> |
||||
|
<abp-column size-sm="_6"> |
||||
|
<abp-collapse-body id="FirstCollapseExample" multi="true"> |
||||
|
Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit |
||||
|
</abp-collapse-body> |
||||
|
</abp-column> |
||||
|
<abp-column size-sm="_6"> |
||||
|
<abp-collapse-body id="SecondCollapseExample" multi="true"> |
||||
|
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. |
||||
|
</abp-collapse-body> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
```` |
||||
|
|
||||
|
## 手风琴示例 |
||||
|
|
||||
|
`abp-accordion` 是手风琴项的主容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-accordion> |
||||
|
<abp-accordion-item title="Collapsible Group Item #1"> |
||||
|
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
<abp-accordion-item title="Collapsible Group Item #2"> |
||||
|
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
<abp-accordion-item title="Collapsible Group Item #3"> |
||||
|
Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. |
||||
|
</abp-accordion-item> |
||||
|
</abp-accordion> |
||||
|
```` |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### active |
||||
|
|
||||
|
指定手风琴项目在初始化时是显示还是隐藏. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### title |
||||
|
|
||||
|
指定手风琴项的标题. 应为字符串类型的值. |
||||
@ -0,0 +1,93 @@ |
|||||
|
# 下拉菜单 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-dropdown` 是下拉菜单的主要容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown> |
||||
|
<abp-dropdown-button text="Dropdown button" /> |
||||
|
<abp-dropdown-menu> |
||||
|
<abp-dropdown-item href="#">Action</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Another action</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Something else here</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅 [下拉菜单demo页面](https://bootstrap-taghelpers.abp.io/Components/Dropdowns)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### direction |
||||
|
|
||||
|
指定下拉菜单的方向. 应为以下值之一: |
||||
|
|
||||
|
* `Down` (默认值) |
||||
|
* `Up` |
||||
|
* `Right` |
||||
|
* `Left` |
||||
|
|
||||
|
### dropdown-style |
||||
|
|
||||
|
指定 `abp-dropdown-button` 是否具有用于拆分的拆分图标. 应为以下值之一: |
||||
|
|
||||
|
* `Single` (默认值) |
||||
|
* `Split` |
||||
|
|
||||
|
## Menu items |
||||
|
|
||||
|
`abp-dropdown-menu` 下拉菜单项的主要容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown> |
||||
|
<abp-dropdown-button button-type="Secondary" text="Dropdown"/> |
||||
|
<abp-dropdown-menu> |
||||
|
<abp-dropdown-header>Dropdown Header</abp-dropdown-header> |
||||
|
<abp-dropdown-item href="#">Action</abp-dropdown-item> |
||||
|
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item> |
||||
|
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item> |
||||
|
<abp-dropdown-divider/> |
||||
|
<abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text> |
||||
|
<abp-dropdown-item href="#">Something else here</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### align |
||||
|
|
||||
|
指定 `abp-dropdown-menu` 在哪个方向对齐. 应为以下值之一: |
||||
|
|
||||
|
* `Left` (默认值) |
||||
|
* `Right` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-dropdown-menu` 也可以包含其他HTML元素,例如标题,段落,分隔符或form元素. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-dropdown > |
||||
|
<abp-dropdown-button button-type="Secondary" text="Dropdown With Form"/> |
||||
|
<abp-dropdown-menu> |
||||
|
<form class="px-4 py-3"> |
||||
|
<abp-input asp-for="EmailAddress"></abp-input> |
||||
|
<abp-input asp-for="Password"></abp-input> |
||||
|
<abp-input asp-for="RememberMe"></abp-input> |
||||
|
<abp-button button-type="Primary" text="Sign In" type="submit" /> |
||||
|
</form> |
||||
|
<abp-dropdown-divider></abp-dropdown-divider> |
||||
|
<abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item> |
||||
|
<abp-dropdown-item href="#">Forgot password?</abp-dropdown-item> |
||||
|
</abp-dropdown-menu> |
||||
|
</abp-dropdown> |
||||
|
```` |
||||
@ -0,0 +1,273 @@ |
|||||
|
# 栅格 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
ABP标签助手基于bootstrap栅格系统. |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[网络demo页面](https://bootstrap-taghelpers.abp.io/Components/Grids)查看示例. |
||||
|
|
||||
|
### Sizing |
||||
|
|
||||
|
**等宽:** 创建等宽的列. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-container> |
||||
|
<abp-row> |
||||
|
<abp-column abp-border="Info">1 of 2</abp-column> |
||||
|
<abp-column abp-border="Danger">2 of 2</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column abp-border="Primary">1 of 3</abp-column> |
||||
|
<abp-column abp-border="Secondary">2 of 3</abp-column> |
||||
|
<abp-column abp-border="Dark">3 of 3</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
```` |
||||
|
|
||||
|
**列中断:** `abp-column-breaker` 用于中断当前行的自动放置宽度,然后在新行中开始. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-container> |
||||
|
<abp-row> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column-breaker/> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
```` |
||||
|
|
||||
|
**设置列宽度:** size属性用于设置特定列的宽度. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row> |
||||
|
<abp-column>1 of 3</abp-column> |
||||
|
<abp-column size="_6">2 of 3 (wider)</abp-column> |
||||
|
<abp-column>3 of 3</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column>1 of 3</abp-column> |
||||
|
<abp-column size="_5">2 of 3 (wider)</abp-column> |
||||
|
<abp-column>3 of 3</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
**可变宽度内容:** 根据内容自动调整列的大小. |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row h-align="Center"> |
||||
|
<abp-column size-lg="_2" abp-border="Info">1 of 3</abp-column> |
||||
|
<abp-column size-md="Auto" abp-border="Danger">Contrary to popular belief, Lorem Ipsum is not simply random text.</abp-column> |
||||
|
<abp-column size-lg="_2" abp-border="Warning">3 of 3</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column>1 of 3</abp-column> |
||||
|
<abp-column size-md="Auto">Variable width content</abp-column> |
||||
|
<abp-column size-lg="_2">3 of 3</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
### 响应类 |
||||
|
|
||||
|
响应式类可以在abp标签中强类型使用. |
||||
|
|
||||
|
```xml |
||||
|
<abp-row> |
||||
|
<abp-column size-sm="_8">col-sm-8</abp-column> |
||||
|
<abp-column size-sm="_4">col-sm-4</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column size-sm="_">col-sm</abp-column> |
||||
|
<abp-column size-sm="_">col-sm</abp-column> |
||||
|
<abp-column size-sm="_">col-sm</abp-column> |
||||
|
<abp-column size-sm="_">col-sm</abp-column> |
||||
|
</abp-row> |
||||
|
<!-- Stack the columns on mobile by making one full-width and the other half-width --> |
||||
|
<abp-row> |
||||
|
<abp-column size="_12" size-md="_8">.col-12 .col-md-8</abp-column> |
||||
|
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> |
||||
|
</abp-row> |
||||
|
|
||||
|
<!-- Columns start at 50% wide on mobile and bump up to 33.3% wide on desktop --> |
||||
|
<abp-row> |
||||
|
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> |
||||
|
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> |
||||
|
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column> |
||||
|
</abp-row> |
||||
|
|
||||
|
<!-- Columns are always 50% wide, on mobile and desktop --> |
||||
|
<abp-row> |
||||
|
<abp-column size="_6">.col-6</abp-column> |
||||
|
<abp-column size="_6">.col-6</abp-column> |
||||
|
</abp-row> |
||||
|
``` |
||||
|
|
||||
|
### 对齐 |
||||
|
|
||||
|
可以使用强类型的abp标签在垂直和水平方向上进行列对齐. |
||||
|
|
||||
|
**垂直对齐**: `v-align` 属性值用于对垂直齐列. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row v-align="Start"> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row v-align="Center"> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row v-align="End"> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
<abp-column>column</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
**水平对齐**: `h-align` 属性值用于对水平齐列. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row h-align="Start"> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row h-align="Center"> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row h-align="End"> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row h-align="Around"> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row h-align="Between"> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
**无间隙**: 可以使用 `gutters="false"` 删除预定义栅格类中列之间的栅格线. 这会从 `abp-row` 中消除负边距,并从所有直接子列中消除水平边距. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-row gutters="false"> |
||||
|
<abp-column size="_8">One of two columns</abp-column> |
||||
|
<abp-column size="_4">One of two columns</abp-column> |
||||
|
</abp-row> |
||||
|
``` |
||||
|
|
||||
|
**列包装**: 如果在一行中放置超过12列,则将每组额外的列作为一个单元包装到新行上. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-row> |
||||
|
<abp-column size="_9">.col-9</abp-column> |
||||
|
<abp-column size="_4">.col-4<br>Since 9 + 4 = 13 > 12, this 4-column-wide div gets wrapped onto a new line as one contiguous unit.</abp-column> |
||||
|
<abp-column size="_6">.col-6<br>Subsequent columns continue along the new line.s</abp-column> |
||||
|
</abp-row> |
||||
|
``` |
||||
|
|
||||
|
### 重新排序 |
||||
|
|
||||
|
**Order类**: `order` 属性用于控制内容的视觉顺序. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row> |
||||
|
<abp-column order="_12">First, but Last</abp-column> |
||||
|
<abp-column>Second, but unordered</abp-column> |
||||
|
<abp-column order="_6">Third, but Second</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
**偏移列**: `offset` 属性用于设置栅格列的偏移量. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
```xml |
||||
|
<abp-container> |
||||
|
<abp-row> |
||||
|
<abp-column size-md="_4">.col-md-4</abp-column> |
||||
|
<abp-column size-md="_4" offset-md="_4">.col-md-4 .offset-md-4</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column> |
||||
|
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column size-md="_6" offset-md="_3">.col-md-6 .offset-md-3</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column size-sm="_5" size-md="_6">.col-sm-5 .col-md-6</abp-column> |
||||
|
<abp-column size-sm="_5" offset-sm="_2" size-md="_6" offset-md="_">.col-sm-5 .offset-sm-2 .col-md-6 .offset-md-0</abp-column> |
||||
|
</abp-row> |
||||
|
<abp-row> |
||||
|
<abp-column size-sm="_6" size-md="_5" size-lg="_6">col-sm-6 .col-md-5 .col-lg-6</abp-column> |
||||
|
<abp-column size-sm="_6" size-md="_5" offset-md="_2" size-lg="_6" offset-lg="_">.col-sm-6 .col-md-5 .offset-md-2 .col-lg-6 .offset-lg-0</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-container> |
||||
|
``` |
||||
|
|
||||
|
## abp-row Attributes |
||||
|
|
||||
|
- **v-align:** 指定包含列的垂直位置. 应为以下值之一: |
||||
|
* `Default` (默认值) |
||||
|
* `Start` |
||||
|
* `Center` |
||||
|
* `End` |
||||
|
|
||||
|
- **h-align**: 指定包含列的水平位置. 应为以下值之一: |
||||
|
* `Default` (默认值) |
||||
|
* `Start` |
||||
|
* `Center` |
||||
|
* `Around` |
||||
|
* `Between` |
||||
|
* `End` |
||||
|
- **gutter**: 指定是否将从所有子列中删除负边距和水平填充. 如果未设置,默认为 `true`. 应为以下值之一: |
||||
|
* `true` |
||||
|
* `false` |
||||
|
|
||||
|
## abp-column Attributes |
||||
|
|
||||
|
- **size:** 指定列的宽度: `_`, `Undefined`, `_1`..`_12`, `Auto`. 或者可以与预定义值一起使用,例如: |
||||
|
- `size-sm` |
||||
|
- `size-md` |
||||
|
- `size-lg` |
||||
|
- `size-xl` |
||||
|
- **order**: 指定列的顺序: `Undefined`, `_1`..`_12`, `First` 和 `Last`. |
||||
|
- **offset:** 指定列的偏移量: `_`, `Undefined`, `_1`..`_12`, `Auto`. 或者可以与预定义值一起使用,例如: |
||||
|
- `offset-sm` |
||||
|
- `offset-md` |
||||
|
- `offset-lg` |
||||
|
- `offset-xl` |
||||
@ -0,0 +1,76 @@ |
|||||
|
# 列表组 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-list-group` 是列表组内容的主要容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-list-group> |
||||
|
<abp-list-group-item>Cras justo odio</abp-list-group-item> |
||||
|
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item> |
||||
|
<abp-list-group-item>Morbi leo risus</abp-list-group-item> |
||||
|
<abp-list-group-item>Vestibulum at eros</abp-list-group-item> |
||||
|
</abp-list-group> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[列表组demo页面](https://bootstrap-taghelpers.abp.io/Components/ListGroups)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### flush |
||||
|
|
||||
|
指定 `abp-list-group` 项可删除某些边框和圆角,以在父容器中无边框呈现列表组项. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### active |
||||
|
|
||||
|
指定 `abp-list-group-item` 是否处于active. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### disabled |
||||
|
|
||||
|
指定 `abp-list-group-item` 是否被禁用. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### href |
||||
|
|
||||
|
指定 `abp-list-group-item` 是否含有链接. 应该是一个字符串链接值. |
||||
|
|
||||
|
### type |
||||
|
|
||||
|
指定 `abp-list-group-item` 具有状态背景和颜色的样式类. 应为以下值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Primary` |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
* `Link` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-list-group-item` 还可以包含其他HTML元素(例如span). |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-list-group> |
||||
|
<abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item> |
||||
|
<abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item> |
||||
|
<abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item> |
||||
|
</abp-list-group> |
||||
|
```` |
||||
@ -0,0 +1,79 @@ |
|||||
|
# 模态框 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-modal` 是创建模态框的主要元素. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> |
||||
|
|
||||
|
<abp-modal centered="true" size="Large" id="myModal"> |
||||
|
<abp-modal-header title="Modal title"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
Woohoo, you're reading this text in a modal! |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="Close"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[模态框demo页面](https://bootstrap-taghelpers.abp.io/Components/Modals)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### centered |
||||
|
|
||||
|
指定模态框的位置. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### size |
||||
|
|
||||
|
指定模态框的大小. 应为以下值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Small` |
||||
|
* `Large` |
||||
|
* `ExtraLarge` |
||||
|
|
||||
|
### static |
||||
|
|
||||
|
指定模态框是否是静态的. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### Additional content |
||||
|
|
||||
|
`abp-modal-footer` 可以有多个带有对齐选项的按钮. |
||||
|
|
||||
|
添加 `@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal` 到你的页面. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button> |
||||
|
|
||||
|
<abp-modal centered="true" size="Large" id="myModal" static="true"> |
||||
|
<abp-modal-header title="Modal title"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
Woohoo, you're reading this text in a modal! |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)" button-alignment="Between"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
```` |
||||
|
|
||||
|
### button-alignment |
||||
|
|
||||
|
指定模态页脚按钮的位置. 应为以下值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Start` |
||||
|
* `Center` |
||||
|
* `Around` |
||||
|
* `Between` |
||||
|
* `End` |
||||
@ -0,0 +1,55 @@ |
|||||
|
# 分页器 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-paginator` 是分页器的abp标签. 需要 `Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` 类型的模型. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-paginator model="Model.PagerModel" show-info="true"></abp-paginator> |
||||
|
```` |
||||
|
|
||||
|
模型: |
||||
|
|
||||
|
````csharp |
||||
|
using Microsoft.AspNetCore.Mvc.RazorPages; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components |
||||
|
{ |
||||
|
public class PaginatorModel : PageModel |
||||
|
{ |
||||
|
public PagerModel PagerModel { get; set; } |
||||
|
|
||||
|
public void OnGet(int currentPage, string sort) |
||||
|
{ |
||||
|
PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[分页器demo页面](https://bootstrap-taghelpers.abp.io/Components/Paginator)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### model |
||||
|
|
||||
|
`Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination.PagerModel` 类型模型可以用以下数据初始化: |
||||
|
|
||||
|
* `totalCount` |
||||
|
* `shownItemsCount` |
||||
|
* `currentPage` |
||||
|
* `pageSize` |
||||
|
* `pageUrl` |
||||
|
* `sort` (默认值为null) |
||||
|
|
||||
|
### show-info |
||||
|
|
||||
|
指定是否显示开始,结束和总记录的其他信息. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
@ -0,0 +1,68 @@ |
|||||
|
# 进度条 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-progress-bar` 是进度条状态的abp标签. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-progress-bar value="70" /> |
||||
|
|
||||
|
<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar> |
||||
|
|
||||
|
<abp-progress-bar type="Success" value="40" strip="true"/> |
||||
|
|
||||
|
<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar> |
||||
|
|
||||
|
<abp-progress-group> |
||||
|
<abp-progress-part type="Success" value="25"/> |
||||
|
<abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part> |
||||
|
<abp-progress-part type="Primary" value="50" animation="true" strip="true" /> |
||||
|
</abp-progress-group> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[进度条demo页面](https://bootstrap-taghelpers.abp.io/Components/Progress-Bars)查看示例. |
||||
|
|
||||
|
## Attributes |
||||
|
|
||||
|
### value |
||||
|
|
||||
|
指定当前进度条的进度. |
||||
|
|
||||
|
### type |
||||
|
|
||||
|
指定进度条的背景颜色. 应为下列值之一: |
||||
|
|
||||
|
* `Default` (默认值) |
||||
|
* `Secondary` |
||||
|
* `Success` |
||||
|
* `Danger` |
||||
|
* `Warning` |
||||
|
* `Info` |
||||
|
* `Light` |
||||
|
* `Dark` |
||||
|
|
||||
|
### min-value |
||||
|
|
||||
|
进度条的最小值. 默认值是0。 |
||||
|
|
||||
|
### max-value |
||||
|
|
||||
|
进度条的最大值. 默认值是100. |
||||
|
|
||||
|
### strip |
||||
|
|
||||
|
指定进度条的背景样式是否被去除. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
|
|
||||
|
### animation |
||||
|
|
||||
|
指定进度条的背景样式是否为动画. 应为以下值之一: |
||||
|
|
||||
|
* `false` (默认值) |
||||
|
* `true` |
||||
@ -0,0 +1,91 @@ |
|||||
|
# 标签页 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-tab` 是从引导标签元素派生的基本标签导航内容容器. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-tabs> |
||||
|
<abp-tab title="Home"> |
||||
|
Content_Home |
||||
|
</abp-tab> |
||||
|
<abp-tab-link title="Link" href="#" /> |
||||
|
<abp-tab title="profile"> |
||||
|
Content_Profile |
||||
|
</abp-tab> |
||||
|
<abp-tab-dropdown title="Contact" name="ContactDropdown"> |
||||
|
<abp-tab title="Contact 1" parent-dropdown-name="ContactDropdown"> |
||||
|
Content_1_Content |
||||
|
</abp-tab> |
||||
|
<abp-tab title="Contact 2" parent-dropdown-name="ContactDropdown"> |
||||
|
Content_2_Content |
||||
|
</abp-tab> |
||||
|
</abp-tab-dropdown> |
||||
|
</abp-tabs> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[标签页demo页面](https://bootstrap-taghelpers.abp.io/Components/Tabs)查看示例. |
||||
|
|
||||
|
## abp-tab Attributes |
||||
|
|
||||
|
- **title**: 设置标签页菜单文字. |
||||
|
- **name:** 设置生成元素的"id"属性. 默认值为Guid. 除非使用Jquery更改或修改了选项卡,否则不需要. |
||||
|
- **active**: 设置active标签页. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-tabs name="TabId"> |
||||
|
<abp-tab name="nav-home" title="Home"> |
||||
|
Content_Home |
||||
|
</abp-tab> |
||||
|
<abp-tab name="nav-profile" active="true" title="profile"> |
||||
|
Content_Profile |
||||
|
</abp-tab> |
||||
|
<abp-tab name="nav-contact" title="Contact"> |
||||
|
Content_Contact |
||||
|
</abp-tab> |
||||
|
</abp-tabs> |
||||
|
```` |
||||
|
|
||||
|
### Pills |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-tabs tab-style="Pill"> |
||||
|
<abp-tab title="Home"> |
||||
|
Content_Home |
||||
|
</abp-tab> |
||||
|
<abp-tab title="profile"> |
||||
|
Content_Profile |
||||
|
</abp-tab> |
||||
|
<abp-tab title="Contact"> |
||||
|
Content_Contact |
||||
|
</abp-tab> |
||||
|
</abp-tabs> |
||||
|
```` |
||||
|
|
||||
|
### Vertical |
||||
|
|
||||
|
**vertical-header-size**: 设置标签标题的列宽. |
||||
|
|
||||
|
示例: |
||||
|
|
||||
|
````xml |
||||
|
<abp-tabs tab-style="PillVertical" vertical-header-size="_2" > |
||||
|
<abp-tab active="true" title="Home"> |
||||
|
Content_Home |
||||
|
</abp-tab> |
||||
|
<abp-tab title="profile"> |
||||
|
Content_Profile |
||||
|
</abp-tab> |
||||
|
<abp-tab title="Contact"> |
||||
|
Content_Contact |
||||
|
</abp-tab> |
||||
|
</abp-tabs> |
||||
|
```` |
||||
@ -0,0 +1,33 @@ |
|||||
|
# 提示工具 |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
`abp-tooltip` 是提示工具的abp标签. |
||||
|
|
||||
|
基本用法: |
||||
|
|
||||
|
````xml |
||||
|
<abp-button abp-tooltip="Tooltip"> |
||||
|
Tooltip Default |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-top="Tooltip"> |
||||
|
Tooltip on top |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-right="Tooltip"> |
||||
|
Tooltip on right |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button abp-tooltip-bottom="Tooltip"> |
||||
|
Tooltip on bottom |
||||
|
</abp-button> |
||||
|
|
||||
|
<abp-button disabled="true" abp-tooltip="Tooltip"> |
||||
|
Disabled button Tooltip |
||||
|
</abp-button> |
||||
|
```` |
||||
|
|
||||
|
## Demo |
||||
|
|
||||
|
参阅[提示工具demo页面](https://bootstrap-taghelpers.abp.io/Components/Tooltips)查看示例. |
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,37 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.ObjectExtending; |
||||
|
|
||||
|
namespace AutoMapper |
||||
|
{ |
||||
|
public static class AbpAutoMapperExtensibleDtoExtensions |
||||
|
{ |
||||
|
public static IMappingExpression<TSource, TDestination> MapExtraProperties<TSource, TDestination>( |
||||
|
this IMappingExpression<TSource, TDestination> mappingExpression, |
||||
|
MappingPropertyDefinitionChecks? definitionChecks = null) |
||||
|
where TDestination : IHasExtraProperties |
||||
|
where TSource : IHasExtraProperties |
||||
|
{ |
||||
|
return mappingExpression |
||||
|
.ForMember( |
||||
|
x => x.ExtraProperties, |
||||
|
y => y.MapFrom( |
||||
|
(source, destination, extraProps) => |
||||
|
{ |
||||
|
var result = extraProps.IsNullOrEmpty() |
||||
|
? new Dictionary<string, object>() |
||||
|
: new Dictionary<string, object>(extraProps); |
||||
|
|
||||
|
HasExtraPropertiesObjectExtendingExtensions |
||||
|
.MapExtraPropertiesTo<TSource, TDestination>( |
||||
|
source.ExtraProperties, |
||||
|
result, |
||||
|
definitionChecks |
||||
|
); |
||||
|
|
||||
|
return result; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Files; |
||||
|
using Volo.Abp.Text; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps |
||||
|
{ |
||||
|
public class ConnectionStringChangeStep : ProjectBuildPipelineStep |
||||
|
{ |
||||
|
private const string DefaultConnectionStringKey = "Default"; |
||||
|
|
||||
|
public override void Execute(ProjectBuildContext context) |
||||
|
{ |
||||
|
var appSettingsJsonFiles = context.Files.Where(f => |
||||
|
f.Name.EndsWith("appsettings.json", StringComparison.OrdinalIgnoreCase)) |
||||
|
.ToArray(); |
||||
|
|
||||
|
if (!appSettingsJsonFiles.Any()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var newConnectionString = $"\"{DefaultConnectionStringKey}\": \"{context.BuildArgs.ConnectionString}\""; |
||||
|
|
||||
|
foreach (var appSettingsJson in appSettingsJsonFiles) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var appSettingJsonContentWithoutBom = StringHelper.ConvertFromBytesWithoutBom(appSettingsJson.Bytes); |
||||
|
var jsonObject = JObject.Parse(appSettingJsonContentWithoutBom); |
||||
|
|
||||
|
var connectionStringContainer = (JContainer)jsonObject?["ConnectionStrings"]; |
||||
|
if (connectionStringContainer == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (!connectionStringContainer.Any()) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var connectionStrings = connectionStringContainer.ToList(); |
||||
|
|
||||
|
foreach (var connectionString in connectionStrings) |
||||
|
{ |
||||
|
var property = ((JProperty)connectionString); |
||||
|
var connectionStringName = property.Name; |
||||
|
|
||||
|
if (connectionStringName == DefaultConnectionStringKey) |
||||
|
{ |
||||
|
var defaultConnectionString = property.ToString(); |
||||
|
if (defaultConnectionString == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
appSettingsJson.ReplaceText(defaultConnectionString, newConnectionString); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Console.WriteLine("Cannot change the connection string in " + appSettingsJson.Name + ". Error: " + ex.Message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
public class ExceptionNotificationContext |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The exception object.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public Exception Exception { get; } |
||||
|
|
||||
|
public LogLevel LogLevel { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// True, if it is handled.
|
||||
|
/// </summary>
|
||||
|
public bool Handled { get; } |
||||
|
|
||||
|
public ExceptionNotificationContext( |
||||
|
[NotNull] Exception exception, |
||||
|
LogLevel? logLevel = null, |
||||
|
bool handled = true) |
||||
|
{ |
||||
|
Exception = Check.NotNull(exception, nameof(exception)); |
||||
|
LogLevel = logLevel ?? exception.GetLogLevel(); |
||||
|
Handled = handled; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
public class ExceptionNotifier : IExceptionNotifier, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<ExceptionNotifier> Logger { get; set; } |
||||
|
|
||||
|
protected IEnumerable<IExceptionSubscriber> ExceptionSubscribers { get; } |
||||
|
|
||||
|
public ExceptionNotifier(IEnumerable<IExceptionSubscriber> exceptionSubscribers) |
||||
|
{ |
||||
|
ExceptionSubscribers = exceptionSubscribers; |
||||
|
Logger = NullLogger<ExceptionNotifier>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task NotifyAsync([NotNull] ExceptionNotificationContext context) |
||||
|
{ |
||||
|
Check.NotNull(context, nameof(context)); |
||||
|
|
||||
|
foreach (var exceptionSubscriber in ExceptionSubscribers) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await exceptionSubscriber.HandleAsync(context); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Logger.LogWarning($"Exception subscriber of type {exceptionSubscriber.GetType().AssemblyQualifiedName} has thrown an exception!"); |
||||
|
Logger.LogException(e, LogLevel.Warning); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
public static class ExceptionNotifierExtensions |
||||
|
{ |
||||
|
public static Task NotifyAsync( |
||||
|
[NotNull] this IExceptionNotifier exceptionNotifier, |
||||
|
[NotNull] Exception exception, |
||||
|
LogLevel? logLevel = null, |
||||
|
bool handled = true) |
||||
|
{ |
||||
|
Check.NotNull(exceptionNotifier, nameof(exceptionNotifier)); |
||||
|
|
||||
|
return exceptionNotifier.NotifyAsync( |
||||
|
new ExceptionNotificationContext( |
||||
|
exception, |
||||
|
logLevel, |
||||
|
handled |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
[ExposeServices(typeof(IExceptionSubscriber))] |
||||
|
public abstract class ExceptionSubscriber : IExceptionSubscriber, ITransientDependency |
||||
|
{ |
||||
|
public abstract Task HandleAsync(ExceptionNotificationContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
public interface IExceptionNotifier |
||||
|
{ |
||||
|
Task NotifyAsync([NotNull] ExceptionNotificationContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.ExceptionHandling |
||||
|
{ |
||||
|
public interface IExceptionSubscriber |
||||
|
{ |
||||
|
Task HandleAsync([NotNull] ExceptionNotificationContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System.Text; |
||||
|
|
||||
|
namespace Volo.Abp.Text |
||||
|
{ |
||||
|
public class StringHelper |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a byte[] to string without BOM (byte order mark).
|
||||
|
/// </summary>
|
||||
|
/// <param name="bytes">The byte[] to be converted to string</param>
|
||||
|
/// <param name="encoding">The encoding to get string. Default is UTF8</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string ConvertFromBytesWithoutBom(byte[] bytes, Encoding encoding = null) |
||||
|
{ |
||||
|
if (bytes == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (encoding == null) |
||||
|
{ |
||||
|
encoding = Encoding.UTF8; |
||||
|
} |
||||
|
|
||||
|
var hasBom = bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF; |
||||
|
|
||||
|
if (hasBom) |
||||
|
{ |
||||
|
return encoding.GetString(bytes, 3, bytes.Length - 3); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return encoding.GetString(bytes); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleAuditedEntityDto<TPrimaryKey> : ExtensibleCreationAuditedEntityDto<TPrimaryKey>, IAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public DateTime? LastModificationTime { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? LastModifierId { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleAuditedEntityDto : ExtensibleCreationAuditedEntityDto, IAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public DateTime? LastModificationTime { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? LastModifierId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
||||
|
/// It has the <see cref="Creator"/> and <see cref="LastModifier"/> objects as a DTOs represent the related user.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
||||
|
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleAuditedEntityWithUserDto<TPrimaryKey, TUserDto> : ExtensibleAuditedEntityDto<TPrimaryKey>, IAuditedObject<TUserDto> |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public TUserDto Creator { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public TUserDto LastModifier { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IAuditedObject"/> interface.
|
||||
|
/// It has the <see cref="Creator"/> and <see cref="LastModifier"/> objects as a DTOs represent the related user.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleAuditedEntityWithUserDto<TUserDto> : ExtensibleAuditedEntityDto, |
||||
|
IAuditedObject<TUserDto> |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public TUserDto Creator { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public TUserDto LastModifier { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleCreationAuditedEntityDto<TPrimaryKey> : ExtensibleEntityDto<TPrimaryKey>, ICreationAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public DateTime CreationTime { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? CreatorId { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleCreationAuditedEntityDto : ExtensibleEntityDto, ICreationAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public DateTime CreationTime { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? CreatorId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject{TCreator}"/> interface.
|
||||
|
/// It has the <see cref="Creator"/> object as a DTO represents the user.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
||||
|
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleCreationAuditedEntityWithUserDto<TPrimaryKey, TUserDto> : ExtensibleCreationAuditedEntityDto<TPrimaryKey>, ICreationAuditedObject<TUserDto> |
||||
|
{ |
||||
|
public TUserDto Creator { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="ICreationAuditedObject{TCreator}"/> interface.
|
||||
|
/// It has the <see cref="Creator"/> object as a DTO represents the user.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TUserDto">Type of the User DTO</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleCreationAuditedEntityWithUserDto<TUserDto> : ExtensibleCreationAuditedEntityDto, |
||||
|
ICreationAuditedObject<TUserDto> |
||||
|
{ |
||||
|
public TUserDto Creator { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.ObjectExtending; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleEntityDto<TKey> : ExtensibleObject, IEntityDto<TKey> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Id of the entity.
|
||||
|
/// </summary>
|
||||
|
public TKey Id { get; set; } |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[DTO: {GetType().Name}] Id = {Id}"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleEntityDto : ExtensibleObject, IEntityDto |
||||
|
{ |
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[DTO: {GetType().Name}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Dtos |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPrimaryKey">Type of primary key</typeparam>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleFullAuditedEntityDto<TPrimaryKey> : ExtensibleAuditedEntityDto<TPrimaryKey>, IFullAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public bool IsDeleted { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? DeleterId { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public DateTime? DeletionTime { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This class can be inherited by DTO classes to implement <see cref="IFullAuditedObject"/> interface.
|
||||
|
/// It also implements the <see cref="IHasExtraProperties"/> interface.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public abstract class ExtensibleFullAuditedEntityDto : ExtensibleAuditedEntityDto, IFullAuditedObject |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public bool IsDeleted { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Guid? DeleterId { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public DateTime? DeletionTime { get; set; } |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue