Merge remote-tracking branch 'upstream/main' into develop
This commit is contained in:
commit
6e1222943e
|
@ -0,0 +1,5 @@
|
||||||
|
# Changesets
|
||||||
|
|
||||||
|
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||||
|
|
||||||
|
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||||
|
"changelog": [
|
||||||
|
"@changesets/changelog-github",
|
||||||
|
{ "repo": "vbenjs/vue-vben-admin" }
|
||||||
|
],
|
||||||
|
"commit": false,
|
||||||
|
"fixed": [["@vben-core/*", "@vben/*"]],
|
||||||
|
"snapshot": {
|
||||||
|
"prereleaseTemplate": "{tag}-{datetime}"
|
||||||
|
},
|
||||||
|
"privatePackages": { "version": true, "tag": true },
|
||||||
|
"linked": [],
|
||||||
|
"access": "public",
|
||||||
|
"baseBranch": "main",
|
||||||
|
"updateInternalDependencies": "patch",
|
||||||
|
"ignore": []
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
# default onwer
|
||||||
|
* anncwb@126.com vince292007@gmail.com
|
||||||
|
|
||||||
|
# vben core onwer
|
||||||
|
/.github/ anncwb@126.com vince292007@gmail.com
|
||||||
|
/.vscode/ anncwb@126.com vince292007@gmail.com
|
||||||
|
/packages/ anncwb@126.com vince292007@gmail.com
|
||||||
|
/packages/@core/ anncwb@126.com vince292007@gmail.com
|
||||||
|
/internal/ anncwb@126.com vince292007@gmail.com
|
||||||
|
/scripts/ anncwb@126.com vince292007@gmail.com
|
||||||
|
|
||||||
|
# vben team onwer
|
||||||
|
apps/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
||||||
|
docs/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
|
@ -0,0 +1,74 @@
|
||||||
|
name: 🐞 Bug Report
|
||||||
|
description: Report an issue with Vben Admin to help us make it better.
|
||||||
|
title: "Bug: "
|
||||||
|
labels: ["bug: pending triage"]
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: What version of our software are you running?
|
||||||
|
options:
|
||||||
|
- Vben Admin V5
|
||||||
|
- Vben Admin V2
|
||||||
|
default: 0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: bug-desc
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug?
|
||||||
|
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||||
|
placeholder: Bug Description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Reproduction
|
||||||
|
description: Please provide a link to [StackBlitz](https://stackblitz.com/fork/github/vitest-dev/vitest/tree/main/examples/basic?initialPath=__vitest__/) (you can also use [examples](https://github.com/vitest-dev/vitest/tree/main/examples)) or a github repo that can reproduce the problem you ran into. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed.
|
||||||
|
placeholder: Reproduction
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: system-info
|
||||||
|
attributes:
|
||||||
|
label: System Info
|
||||||
|
description: Output of `npx envinfo --system --npmPackages '{vue}' --binaries --browsers`
|
||||||
|
render: shell
|
||||||
|
placeholder: System, Binaries, Browsers
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant log output
|
||||||
|
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Validations
|
||||||
|
description: Before submitting the issue, please make sure you do the following
|
||||||
|
# description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com).
|
||||||
|
options:
|
||||||
|
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||||
|
required: true
|
||||||
|
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [existing issues](https://github.com/vbenjs/vue-vben-admin/issues) and checked that my issue does not duplicate any existing issues.
|
||||||
|
required: true
|
||||||
|
- label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vbenjs/vue-vben-admin/discussions) or join our [Discord Chat Server](https://discord.gg/8GuAdwDhj6).
|
||||||
|
required: true
|
||||||
|
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
|
||||||
|
required: true
|
|
@ -0,0 +1,38 @@
|
||||||
|
name: 📚 Documentation
|
||||||
|
description: Report an issue with Vben Admin Website to help us make it better.
|
||||||
|
title: "Docs: "
|
||||||
|
labels: [documentation]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this issue!
|
||||||
|
- type: checkboxes
|
||||||
|
id: documentation_is
|
||||||
|
attributes:
|
||||||
|
label: Documentation is
|
||||||
|
options:
|
||||||
|
- label: Missing
|
||||||
|
- label: Outdated
|
||||||
|
- label: Confusing
|
||||||
|
- label: Not sure?
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Explain in Detail
|
||||||
|
description: A clear and concise description of your suggestion. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||||
|
placeholder: The description of ... page is not clear. I thought it meant ... but it wasn't.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: suggestion
|
||||||
|
attributes:
|
||||||
|
label: Your Suggestion for Changes
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction-steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use.
|
||||||
|
placeholder: Run `pnpm install` followed by `pnpm run docs:dev`
|
|
@ -0,0 +1,70 @@
|
||||||
|
name: ✨ New Feature Proposal
|
||||||
|
description: Propose a new feature to be added to Vben Admin
|
||||||
|
title: "FEATURE: "
|
||||||
|
labels: ["enhancement: pending triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thank you for suggesting a feature for our project! Please fill out the information below to help us understand and implement your request!
|
||||||
|
- type: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: What version of our software are you running?
|
||||||
|
options:
|
||||||
|
- Vben Admin V5
|
||||||
|
- Vben Admin V2
|
||||||
|
default: 0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A detailed description of the feature request.
|
||||||
|
placeholder: Please describe the feature you would like to see, and why it would be useful.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: proposed-solution
|
||||||
|
attributes:
|
||||||
|
label: Proposed Solution
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
placeholder: Describe the solution you'd like to see
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives Considered
|
||||||
|
description: |
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
placeholder: Describe any alternative solutions or features you've considered
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context or screenshots about the feature request here.
|
||||||
|
placeholder: Any additional information
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Validations
|
||||||
|
description: Before submitting the issue, please make sure you do the following
|
||||||
|
options:
|
||||||
|
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||||
|
required: true
|
||||||
|
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [existing issues](https://github.com/vbenjs/vue-vben-admin/issues) and checked that my issue does not duplicate any existing issues.
|
||||||
|
required: true
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: "Setup Node"
|
||||||
|
|
||||||
|
description: "Setup node and pnpm"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: .node-version
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
name: Setup pnpm cache
|
||||||
|
if: ${{ github.ref_name == 'main' }}
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- uses: actions/cache/restore@v4
|
||||||
|
if: ${{ github.ref_name != 'main' }}
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash
|
||||||
|
run: pnpm install --frozen-lockfile
|
|
@ -0,0 +1,89 @@
|
||||||
|
## Git Commit Message Convention
|
||||||
|
|
||||||
|
> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
|
||||||
|
|
||||||
|
#### TL;DR:
|
||||||
|
|
||||||
|
Messages must be matched by the following regex:
|
||||||
|
|
||||||
|
```js
|
||||||
|
/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|types|wip): .{1,50}/;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
Appears under "Features" header, `dev` subheader:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(dev): add 'comments' option
|
||||||
|
```
|
||||||
|
|
||||||
|
Appears under "Bug Fixes" header, `dev` subheader, with a link to issue #28:
|
||||||
|
|
||||||
|
```
|
||||||
|
fix(dev): fix dev error
|
||||||
|
|
||||||
|
close #28
|
||||||
|
```
|
||||||
|
|
||||||
|
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
||||||
|
|
||||||
|
```
|
||||||
|
perf(build): remove 'foo' option
|
||||||
|
|
||||||
|
BREAKING CHANGE: The 'foo' option has been removed.
|
||||||
|
```
|
||||||
|
|
||||||
|
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
||||||
|
|
||||||
|
```
|
||||||
|
revert: feat(compiler): add 'comments' option
|
||||||
|
|
||||||
|
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full Message Format
|
||||||
|
|
||||||
|
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The **header** is mandatory and the **scope** of the header is optional.
|
||||||
|
|
||||||
|
### Revert
|
||||||
|
|
||||||
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
|
### Type
|
||||||
|
|
||||||
|
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
|
||||||
|
|
||||||
|
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
The scope could be anything specifying the place of the commit change. For example `dev`, `build`, `workflow`, `cli` etc...
|
||||||
|
|
||||||
|
### Subject
|
||||||
|
|
||||||
|
The subject contains a succinct description of the change:
|
||||||
|
|
||||||
|
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
|
- don't capitalize the first letter
|
||||||
|
- no dot (.) at the end
|
||||||
|
|
||||||
|
### Body
|
||||||
|
|
||||||
|
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||||
|
|
||||||
|
### Footer
|
||||||
|
|
||||||
|
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
|
||||||
|
|
||||||
|
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Prevent issues being created without using the template
|
||||||
|
blank_issues_enabled: false
|
||||||
|
checkIssueTemplate: true
|
||||||
|
checkPullRequestTemplate: true
|
||||||
|
|
||||||
|
contact_links:
|
||||||
|
- name: 💬 Discord Chat
|
||||||
|
url: https://discord.gg/8GuAdwDhj6
|
||||||
|
about: Ask questions and discuss with other Vben users in real time.
|
||||||
|
|
||||||
|
- name: ❓ Questions & Discussions
|
||||||
|
url: https://github.com/@vbenjs/vue-vben-admin/discussions
|
||||||
|
about: Use GitHub discussions for message-board style questions and discussions.
|
||||||
|
|
||||||
|
# Comment to be posted to on PRs from first time contributors in your repository
|
||||||
|
newPRWelcomeComment: |
|
||||||
|
💖 Thanks for opening this pull request! 💖
|
||||||
|
Please be patient and we will get back to you as soon as we can.
|
||||||
|
|
||||||
|
# Comment to be posted to on pull requests merged by a first time user
|
||||||
|
firstPRMergeComment: >
|
||||||
|
Thanks for your contribution! 🎉🎉🎉
|
||||||
|
|
||||||
|
|
||||||
|
# Comment to be posted to on first time issues
|
||||||
|
newIssueWelcomeComment: >
|
||||||
|
Thanks for opening your first issue! Be sure to follow the issue template and provide every bit of information to help the developers!
|
||||||
|
|
||||||
|
|
||||||
|
# *OPTIONAL* default titles to check against for lack of descriptiveness
|
||||||
|
# MUST BE ALL LOWERCASE
|
||||||
|
requestInfoDefaultTitles:
|
||||||
|
- update readme.md
|
||||||
|
- updates
|
||||||
|
|
||||||
|
# *Required* Comment to reply with
|
||||||
|
requestInfoReplyComment: >
|
||||||
|
Thanks for filing this issue/PR! It would be much appreciated if you could provide us with more information so we can effectively analyze the situation in context.
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Vben Admin Contributing Guide
|
||||||
|
|
||||||
|
Hi! We're really excited that you are interested in contributing to Vben Admin. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
|
||||||
|
|
||||||
|
- [Pull Request Guidelines](#pull-request-guidelines)
|
||||||
|
|
||||||
|
## Contributor Code of Conduct
|
||||||
|
|
||||||
|
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||||
|
|
||||||
|
We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||||
|
|
||||||
|
## Pull Request Guidelines
|
||||||
|
|
||||||
|
- Checkout a topic branch from the relevant branch, e.g. main, and merge back against that branch.
|
||||||
|
|
||||||
|
- If adding a new feature:
|
||||||
|
|
||||||
|
- Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it.
|
||||||
|
|
||||||
|
- If fixing bug:
|
||||||
|
|
||||||
|
- Provide a detailed description of the bug in the PR. Live demo preferred.
|
||||||
|
|
||||||
|
- It's OK to have multiple small commits as you work on the PR - GitHub can automatically squash them before merging.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
You will need [pnpm](https://pnpm.io/)
|
||||||
|
|
||||||
|
After cloning the repo, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install the dependencies of the project
|
||||||
|
$ pnpm install
|
||||||
|
# start the project
|
||||||
|
$ pnpm run dev
|
||||||
|
```
|
|
@ -0,0 +1,17 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
groups:
|
||||||
|
non-breaking-changes:
|
||||||
|
update-types: [minor, patch]
|
||||||
|
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
groups:
|
||||||
|
non-breaking-changes:
|
||||||
|
update-types: [minor, patch]
|
|
@ -0,0 +1,33 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- Please describe the change as necessary. If it's a feature or enhancement please be as detailed as possible. If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- You can also add additional context here -->
|
||||||
|
|
||||||
|
## Type of change
|
||||||
|
|
||||||
|
Please delete options that are not relevant.
|
||||||
|
|
||||||
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
|
- [ ] This change requires a documentation update
|
||||||
|
- [ ] Please, don't make changes to `pnpm-lock.yaml` unless you introduce a new test example.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
> ℹ️ Check all checkboxes - this will indicate that you have done everything in accordance with the rules in [CONTRIBUTING](contributing.md).
|
||||||
|
|
||||||
|
- [ ] If you introduce new functionality, document it. You can run documentation with `pnpm run docs:dev` command.
|
||||||
|
- [ ] Run the tests with `pnpm test`.
|
||||||
|
- [ ] Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with `feat:`, `fix:`, `perf:`, `docs:`, or `chore:`.
|
||||||
|
- [ ] My code follows the style guidelines of this project
|
||||||
|
- [ ] I have performed a self-review of my own code
|
||||||
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||||
|
- [ ] I have made corresponding changes to the documentation
|
||||||
|
- [ ] My changes generate no new warnings
|
||||||
|
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||||
|
- [ ] New and existing unit tests pass locally with my changes
|
||||||
|
- [ ] Any dependent changes have been merged and published in downstream modules
|
|
@ -0,0 +1,61 @@
|
||||||
|
name-template: "v$RESOLVED_VERSION"
|
||||||
|
tag-template: "v$RESOLVED_VERSION"
|
||||||
|
version-template: $MAJOR.$MINOR.$PATCH
|
||||||
|
change-template: "* $TITLE (#$NUMBER) @$AUTHOR"
|
||||||
|
template: |
|
||||||
|
# What's Changed
|
||||||
|
|
||||||
|
$CHANGES
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- title: "🚀 Features"
|
||||||
|
labels:
|
||||||
|
- "feature"
|
||||||
|
- title: "🐞 Bug Fixes"
|
||||||
|
labels:
|
||||||
|
- "bug"
|
||||||
|
- title: "📈 Performance"
|
||||||
|
labels:
|
||||||
|
- "perf"
|
||||||
|
- "enhancement"
|
||||||
|
- title: 📝 Documentation
|
||||||
|
labels:
|
||||||
|
- "documentation"
|
||||||
|
- title: 👻 Maintenance
|
||||||
|
labels:
|
||||||
|
- "chore"
|
||||||
|
- "dependencies"
|
||||||
|
# collapse-after: 12
|
||||||
|
- title: 🚦 Tests
|
||||||
|
labels:
|
||||||
|
- "tests"
|
||||||
|
- title: "Breaking"
|
||||||
|
label: "breaking"
|
||||||
|
|
||||||
|
version-resolver:
|
||||||
|
major:
|
||||||
|
labels:
|
||||||
|
- "major"
|
||||||
|
- "breaking"
|
||||||
|
minor:
|
||||||
|
labels:
|
||||||
|
- "minor"
|
||||||
|
patch:
|
||||||
|
labels:
|
||||||
|
- "feature"
|
||||||
|
- "patch"
|
||||||
|
- "bug"
|
||||||
|
- "maintenance"
|
||||||
|
- "docs"
|
||||||
|
- "dependencies"
|
||||||
|
- "security"
|
||||||
|
|
||||||
|
exclude-labels:
|
||||||
|
- "skip-changelog"
|
||||||
|
- "no-changelog"
|
||||||
|
- "changelog"
|
||||||
|
- "bump versions"
|
||||||
|
- "reverted"
|
||||||
|
- "invalid"
|
|
@ -0,0 +1,13 @@
|
||||||
|
titleAndCommits: true
|
||||||
|
types:
|
||||||
|
- feat
|
||||||
|
- fix
|
||||||
|
- docs
|
||||||
|
- chore
|
||||||
|
- style
|
||||||
|
- refactor
|
||||||
|
- perf
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- ci
|
||||||
|
- revert
|
|
@ -0,0 +1,47 @@
|
||||||
|
# name: Dependabot post-update
|
||||||
|
name: Build detection
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
HUSKY: "0"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
post-update:
|
||||||
|
# if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
# - macos-latest
|
||||||
|
- windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout out pull request
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr checkout ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
pnpm run build
|
|
@ -0,0 +1,42 @@
|
||||||
|
# https://github.com/changesets/action
|
||||||
|
name: Changeset version
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
version:
|
||||||
|
if: (github.event.pull_request.merged || github.event_name == 'workflow_dispatch') && github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
# if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Create Release Pull Request
|
||||||
|
uses: changesets/action@v1
|
||||||
|
with:
|
||||||
|
version: pnpm run version
|
||||||
|
commit: "chore: bump versions"
|
||||||
|
title: "chore: bump versions"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -0,0 +1,124 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- "releases/*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
# - macos-latest
|
||||||
|
- windows-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
# - name: Check Git version
|
||||||
|
# run: git --version
|
||||||
|
|
||||||
|
# - name: Setup mock Git user
|
||||||
|
# run: git config --global user.email "you@example.com" && git config --global user.name "Your Name"
|
||||||
|
|
||||||
|
- name: Vitest tests
|
||||||
|
run: pnpm run test:unit
|
||||||
|
|
||||||
|
# - name: Upload coverage
|
||||||
|
# uses: codecov/codecov-action@v4
|
||||||
|
# with:
|
||||||
|
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
# - macos-latest
|
||||||
|
- windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: pnpm run lint
|
||||||
|
|
||||||
|
check:
|
||||||
|
name: Check
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
# - macos-latest
|
||||||
|
- windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: pnpm check:type
|
||||||
|
|
||||||
|
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
||||||
|
- name: Check workflow files
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||||
|
./actionlint -color -shellcheck=""
|
||||||
|
|
||||||
|
ci-ok:
|
||||||
|
name: CI OK
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && always()
|
||||||
|
needs: [test, check, lint]
|
||||||
|
env:
|
||||||
|
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
|
||||||
|
steps:
|
||||||
|
- name: Check for failure
|
||||||
|
run: |
|
||||||
|
echo $FAILURE
|
||||||
|
if [ "$FAILURE" = "false" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -0,0 +1,93 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
schedule:
|
||||||
|
- cron: "35 0 * * 0"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze (${{ matrix.language }})
|
||||||
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||||
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||||
|
# - https://gh.io/supported-runners-and-hardware-resources
|
||||||
|
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||||
|
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||||
|
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||||
|
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||||
|
permissions:
|
||||||
|
# required for all workflows
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
# required to fetch internal or private CodeQL packs
|
||||||
|
packages: read
|
||||||
|
|
||||||
|
# only required for workflows in private repositories
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- language: javascript-typescript
|
||||||
|
build-mode: none
|
||||||
|
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||||
|
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||||
|
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||||
|
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||||
|
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||||
|
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||||
|
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||||
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: ${{ matrix.build-mode }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
# If the analyze step fails for one of the languages you are analyzing with
|
||||||
|
# "We were unable to automatically build your code", modify the matrix above
|
||||||
|
# to set the build mode to "manual" for that language. Then modify this step
|
||||||
|
# to build your code.
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
- if: matrix.build-mode == 'manual'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||||
|
'languages you are analyzing, replace this with the commands to build' \
|
||||||
|
'your code, for example:'
|
||||||
|
echo ' make bootstrap'
|
||||||
|
echo ' make release'
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v3
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
|
@ -0,0 +1,155 @@
|
||||||
|
name: Deploy Website on push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-playground-ftp:
|
||||||
|
name: Deploy Push Playground Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Sed Config Base
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./playground/.env.production
|
||||||
|
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./playground/.env.production
|
||||||
|
cat ./playground/.env.production
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build:play
|
||||||
|
|
||||||
|
- name: Sync Playground files
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
server: ${{ secrets.PRO_FTP_HOST }}
|
||||||
|
username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }}
|
||||||
|
password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }}
|
||||||
|
local-dir: ./playground/dist/
|
||||||
|
|
||||||
|
deploy-docs-ftp:
|
||||||
|
name: Deploy Push Docs Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build:docs
|
||||||
|
|
||||||
|
- name: Sync Docs files
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
server: ${{ secrets.PRO_FTP_HOST }}
|
||||||
|
username: ${{ secrets.WEBSITE_FTP_ACCOUNT }}
|
||||||
|
password: ${{ secrets.WEBSITE_FTP_PASSWORD }}
|
||||||
|
local-dir: ./docs/.vitepress/dist/
|
||||||
|
|
||||||
|
deploy-antd-ftp:
|
||||||
|
name: Deploy Push Antd Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Sed Config Base
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-antd/.env.production
|
||||||
|
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-antd/.env.production
|
||||||
|
cat ./apps/web-antd/.env.production
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build:antd
|
||||||
|
|
||||||
|
- name: Sync files
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
server: ${{ secrets.PRO_FTP_HOST }}
|
||||||
|
username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }}
|
||||||
|
password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }}
|
||||||
|
local-dir: ./apps/web-antd/dist/
|
||||||
|
|
||||||
|
deploy-ele-ftp:
|
||||||
|
name: Deploy Push Element Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Sed Config Base
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-ele/.env.production
|
||||||
|
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-ele/.env.production
|
||||||
|
cat ./apps/web-ele/.env.production
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build:ele
|
||||||
|
|
||||||
|
- name: Sync files
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
server: ${{ secrets.PRO_FTP_HOST }}
|
||||||
|
username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }}
|
||||||
|
password: ${{ secrets.WEB_ELE_FTP_PASSWORD }}
|
||||||
|
local-dir: ./apps/web-ele/dist/
|
||||||
|
|
||||||
|
deploy-naive-ftp:
|
||||||
|
name: Deploy Push Naive Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Sed Config Base
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-naive/.env.production
|
||||||
|
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-naive/.env.production
|
||||||
|
cat ./apps/web-naive/.env.production
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build:naive
|
||||||
|
|
||||||
|
- name: Sync files
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
server: ${{ secrets.PRO_FTP_HOST }}
|
||||||
|
username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }}
|
||||||
|
password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }}
|
||||||
|
local-dir: ./apps/web-naive/dist/
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: Release Drafter
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_release_draft:
|
||||||
|
permissions:
|
||||||
|
# write permission is required to create a github release
|
||||||
|
contents: write
|
||||||
|
# write permission is required for autolabeler
|
||||||
|
# otherwise, read permission is required at least
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: release-drafter/release-drafter@v6
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -0,0 +1,25 @@
|
||||||
|
# 每天零点运行一次,它会检查所有带有 "need reproduction" 标签的 Issues。如果这些 Issues 在过去的 3 天内没有任何活动,它们将会被自动关闭。这有助于保持 Issue 列表的整洁,并且提醒用户在必要时提供更多的信息。
|
||||||
|
name: Issue Close Require
|
||||||
|
|
||||||
|
# 触发条件:每天零点
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
close-issues:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# 步骤1:关闭未活动的 Issues
|
||||||
|
- name: Close Inactive Issues
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: "close-issues" # 执行动作:关闭 Issues
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }} # GitHub Token,用于认证
|
||||||
|
labels: "needs reproduction" # 目标标签
|
||||||
|
inactive-day: 3 # 未活动天数阈值
|
|
@ -0,0 +1,45 @@
|
||||||
|
name: Label Based Actions
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [labeled]
|
||||||
|
# pull_request:
|
||||||
|
# types: [labeled]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
reply-labeled:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: remove enhancement pending
|
||||||
|
if: github.event.label.name == 'enhancement'
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: "remove-labels"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
labels: "enhancement: pending triage"
|
||||||
|
|
||||||
|
- name: remove bug pending
|
||||||
|
if: github.event.label.name == 'bug'
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: "remove-labels"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
labels: "bug: pending triage"
|
||||||
|
|
||||||
|
- name: needs reproduction
|
||||||
|
if: github.event.label.name == 'needs reproduction'
|
||||||
|
uses: actions-cool/issues-helper@v3
|
||||||
|
with:
|
||||||
|
actions: "create-comment, remove-labels"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-number: ${{ github.event.issue.number }}
|
||||||
|
body: |
|
||||||
|
Hello @${{ github.event.issue.user.login }}. Please provide the complete reproduction steps and code. Issues labeled by `needs reproduction` will be closed if no activities in 3 days.
|
||||||
|
labels: "bug: pending triage"
|
|
@ -0,0 +1,23 @@
|
||||||
|
name: Lock Threads
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
action:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v5
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
issue-inactive-days: "30"
|
||||||
|
issue-lock-reason: ""
|
||||||
|
pr-inactive-days: "30"
|
||||||
|
pr-lock-reason: ""
|
||||||
|
process-only: "issues, prs"
|
|
@ -0,0 +1,79 @@
|
||||||
|
name: Create Release Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*" # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||||
|
|
||||||
|
env:
|
||||||
|
HUSKY: "0"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# - name: Checkout code
|
||||||
|
# uses: actions/checkout@v4
|
||||||
|
# with:
|
||||||
|
# fetch-depth: 0
|
||||||
|
|
||||||
|
# - name: Install pnpm
|
||||||
|
# uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
# - name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
# uses: actions/setup-node@v4
|
||||||
|
# with:
|
||||||
|
# node-version: ${{ matrix.node-version }}
|
||||||
|
# cache: "pnpm"
|
||||||
|
|
||||||
|
# - name: Install dependencies
|
||||||
|
# run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# - name: Test and Build
|
||||||
|
# run: |
|
||||||
|
# pnpm run test
|
||||||
|
# pnpm run build
|
||||||
|
|
||||||
|
- name: version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
tag=${GITHUB_REF/refs\/tags\//}
|
||||||
|
version=${tag#v}
|
||||||
|
major=${version%%.*}
|
||||||
|
echo "tag=${tag}" >> $GITHUB_OUTPUT
|
||||||
|
echo "version=${version}" >> $GITHUB_OUTPUT
|
||||||
|
echo "major=${major}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: release-drafter/release-drafter@v6
|
||||||
|
with:
|
||||||
|
version: ${{ steps.version.outputs.version }}
|
||||||
|
publish: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# - name: force update major tag
|
||||||
|
# run: |
|
||||||
|
# git tag v${{ steps.version.outputs.major }} ${{ steps.version.outputs.tag }} -f
|
||||||
|
# git push origin refs/tags/v${{ steps.version.outputs.major }} -f
|
||||||
|
|
||||||
|
# - name: Create Release for Tag
|
||||||
|
# id: release_tag
|
||||||
|
# uses: ncipollo/release-action@v1
|
||||||
|
# with:
|
||||||
|
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# generateReleaseNotes: "true"
|
||||||
|
# body: |
|
||||||
|
# > Please refer to [CHANGELOG.md](https://github.com/vbenjs/vue-vben-admin/blob/main/CHANGELOG.md) for details.
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: Semantic Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- synchronize
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Semantic Pull Request
|
||||||
|
steps:
|
||||||
|
- name: Validate PR title
|
||||||
|
uses: amannn/action-semantic-pull-request@v5
|
||||||
|
with:
|
||||||
|
wip: true
|
||||||
|
subjectPattern: ^(?![A-Z]).+$
|
||||||
|
subjectPatternError: |
|
||||||
|
The subject "{subject}" found in the pull request title "{title}"
|
||||||
|
didn't match the configured pattern. Please ensure that the subject
|
||||||
|
doesn't start with an uppercase character.
|
||||||
|
requireScope: false
|
||||||
|
types: |
|
||||||
|
fix
|
||||||
|
feat
|
||||||
|
docs
|
||||||
|
style
|
||||||
|
refactor
|
||||||
|
perf
|
||||||
|
test
|
||||||
|
build
|
||||||
|
ci
|
||||||
|
chore
|
||||||
|
revert
|
||||||
|
types
|
||||||
|
release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -0,0 +1,18 @@
|
||||||
|
name: "Close stale issues"
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 1 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
stale-issue-message: "This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days"
|
||||||
|
stale-pr-message: "This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days"
|
||||||
|
exempt-issue-labels: "bug,enhancement"
|
||||||
|
days-before-stale: 60
|
||||||
|
days-before-close: 7
|
|
@ -1,7 +1,4 @@
|
||||||
export default {
|
export default {
|
||||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
|
||||||
'prettier --cache --write--parser json',
|
|
||||||
],
|
|
||||||
'*.{js,jsx,ts,tsx}': [
|
'*.{js,jsx,ts,tsx}': [
|
||||||
'prettier --cache --ignore-unknown --write',
|
'prettier --cache --ignore-unknown --write',
|
||||||
'eslint --cache --fix',
|
'eslint --cache --fix',
|
||||||
|
@ -16,5 +13,8 @@ export default {
|
||||||
'eslint --cache --fix',
|
'eslint --cache --fix',
|
||||||
'stylelint --fix --allow-empty-input',
|
'stylelint --fix --allow-empty-input',
|
||||||
],
|
],
|
||||||
|
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||||
|
'prettier --cache --write--parser json',
|
||||||
|
],
|
||||||
'package.json': ['prettier --cache --write'],
|
'package.json': ['prettier --cache --write'],
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"url": "http://localhost:5555",
|
"url": "http://localhost:5555",
|
||||||
"env": { "NODE_ENV": "development" },
|
"env": { "NODE_ENV": "development" },
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"webRoot": "${workspaceFolder}/playground/src"
|
"webRoot": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
"url": "http://localhost:5666",
|
"url": "http://localhost:5666",
|
||||||
"env": { "NODE_ENV": "development" },
|
"env": { "NODE_ENV": "development" },
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"webRoot": "${workspaceFolder}/apps/web-antd/src"
|
"webRoot": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"url": "http://localhost:5777",
|
"url": "http://localhost:5777",
|
||||||
"env": { "NODE_ENV": "development" },
|
"env": { "NODE_ENV": "development" },
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"webRoot": "${workspaceFolder}/apps/web-ele/src"
|
"webRoot": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"url": "http://localhost:5888",
|
"url": "http://localhost:5888",
|
||||||
"env": { "NODE_ENV": "development" },
|
"env": { "NODE_ENV": "development" },
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"webRoot": "${workspaceFolder}/apps/web-naive/src"
|
"webRoot": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
"editor.defaultFormatter": "Vue.volar"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
// extensions
|
// extensions
|
||||||
"extensions.ignoreRecommendations": true,
|
"extensions.ignoreRecommendations": true,
|
||||||
|
@ -213,16 +213,12 @@
|
||||||
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
||||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
||||||
"Dockerfile": "Dockerfile,.docker*,docker-entrypoint.sh,build-local-docker*,nginx.conf",
|
"Dockerfile": "Dockerfile,.docker*,docker-entrypoint.sh,build-local-docker*,nginx.conf",
|
||||||
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,.ls-lint*,cspell.json",
|
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
|
||||||
"tailwind.config.mjs": "postcss.*"
|
"tailwind.config.mjs": "postcss.*"
|
||||||
},
|
},
|
||||||
"commentTranslate.hover.enabled": false,
|
"commentTranslate.hover.enabled": false,
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
"commentTranslate.multiLineMerge": true,
|
"commentTranslate.multiLineMerge": true,
|
||||||
"vue.server.hybridMode": true,
|
"vue.server.hybridMode": true,
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
"vue3snippets.enable-compile-vue-file-on-did-save-code": false,
|
|
||||||
"[typescriptreact]": {
|
|
||||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# 应用标题
|
||||||
|
VITE_APP_TITLE=Vben Admin Antd
|
||||||
|
|
||||||
|
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||||
|
VITE_APP_NAMESPACE=vben-web-antd
|
|
@ -0,0 +1,7 @@
|
||||||
|
# public path
|
||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# Basic interface address SPA
|
||||||
|
VITE_GLOB_API_URL=/api
|
||||||
|
|
||||||
|
VITE_VISUALIZER=true
|
|
@ -0,0 +1,16 @@
|
||||||
|
# 端口号
|
||||||
|
VITE_PORT=5666
|
||||||
|
|
||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_GLOB_API_URL=/api
|
||||||
|
|
||||||
|
# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
|
||||||
|
VITE_NITRO_MOCK=true
|
||||||
|
|
||||||
|
# 是否打开 devtools,true 为打开,false 为关闭
|
||||||
|
VITE_DEVTOOLS=false
|
||||||
|
|
||||||
|
# 是否注入全局loading
|
||||||
|
VITE_INJECT_APP_LOADING=true
|
|
@ -0,0 +1,19 @@
|
||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
|
||||||
|
|
||||||
|
# 是否开启压缩,可以设置为 none, brotli, gzip
|
||||||
|
VITE_COMPRESS=none
|
||||||
|
|
||||||
|
# 是否开启 PWA
|
||||||
|
VITE_PWA=false
|
||||||
|
|
||||||
|
# vue-router 的模式
|
||||||
|
VITE_ROUTER_HISTORY=hash
|
||||||
|
|
||||||
|
# 是否注入全局loading
|
||||||
|
VITE_INJECT_APP_LOADING=true
|
||||||
|
|
||||||
|
# 打包后是否生成dist.zip
|
||||||
|
VITE_ARCHIVER=true
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<meta name="renderer" content="webkit" />
|
||||||
|
<meta name="description" content="A Modern Back-end Management System" />
|
||||||
|
<meta name="keywords" content="Vben Admin Vue3 Vite" />
|
||||||
|
<meta name="author" content="Vben" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||||
|
/>
|
||||||
|
<!-- 由 vite 注入 VITE_APP_TITLE 变量,在 .env 文件内配置 -->
|
||||||
|
<title><%= VITE_APP_TITLE %></title>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<script>
|
||||||
|
// 生产环境下注入百度统计
|
||||||
|
if (window._VBEN_ADMIN_PRO_APP_CONF_) {
|
||||||
|
var _hmt = _hmt || [];
|
||||||
|
(function () {
|
||||||
|
var hm = document.createElement('script');
|
||||||
|
hm.src =
|
||||||
|
'https://hm.baidu.com/hm.js?b38e689f40558f20a9a686d7f6f33edf';
|
||||||
|
var s = document.getElementsByTagName('script')[0];
|
||||||
|
s.parentNode.insertBefore(hm, s);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "@vben/web-antd",
|
||||||
|
"version": "5.3.0-beta.2",
|
||||||
|
"homepage": "https://vben.pro",
|
||||||
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||||
|
"directory": "apps/web-antd"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "vben",
|
||||||
|
"email": "ann.vben@gmail.com",
|
||||||
|
"url": "https://github.com/anncwb"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm vite build --mode production",
|
||||||
|
"build:analyze": "pnpm vite build --mode analyze",
|
||||||
|
"dev": "pnpm vite --mode development",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"#/*": "./src/*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vben/access": "workspace:*",
|
||||||
|
"@vben/common-ui": "workspace:*",
|
||||||
|
"@vben/constants": "workspace:*",
|
||||||
|
"@vben/hooks": "workspace:*",
|
||||||
|
"@vben/icons": "workspace:*",
|
||||||
|
"@vben/layouts": "workspace:*",
|
||||||
|
"@vben/locales": "workspace:*",
|
||||||
|
"@vben/plugins": "workspace:*",
|
||||||
|
"@vben/preferences": "workspace:*",
|
||||||
|
"@vben/request": "workspace:*",
|
||||||
|
"@vben/stores": "workspace:*",
|
||||||
|
"@vben/styles": "workspace:*",
|
||||||
|
"@vben/types": "workspace:*",
|
||||||
|
"@vben/utils": "workspace:*",
|
||||||
|
"@vueuse/core": "^11.0.3",
|
||||||
|
"ant-design-vue": "^4.2.3",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"pinia": "2.2.2",
|
||||||
|
"vue": "^3.5.4",
|
||||||
|
"vue-router": "^4.4.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from '@vben/tailwind-config/postcss';
|
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -0,0 +1,114 @@
|
||||||
|
import type {
|
||||||
|
BaseFormComponentType,
|
||||||
|
VbenFormSchema as FormSchema,
|
||||||
|
VbenFormProps,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AutoComplete,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
DatePicker,
|
||||||
|
Divider,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
InputPassword,
|
||||||
|
Mentions,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
RangePicker,
|
||||||
|
Rate,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect,
|
||||||
|
Upload,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
// 业务表单组件适配
|
||||||
|
|
||||||
|
export type FormComponentType =
|
||||||
|
| 'AutoComplete'
|
||||||
|
| 'Checkbox'
|
||||||
|
| 'CheckboxGroup'
|
||||||
|
| 'DatePicker'
|
||||||
|
| 'Divider'
|
||||||
|
| 'Input'
|
||||||
|
| 'InputNumber'
|
||||||
|
| 'InputPassword'
|
||||||
|
| 'Mentions'
|
||||||
|
| 'Radio'
|
||||||
|
| 'RadioGroup'
|
||||||
|
| 'RangePicker'
|
||||||
|
| 'Rate'
|
||||||
|
| 'Select'
|
||||||
|
| 'Space'
|
||||||
|
| 'Switch'
|
||||||
|
| 'TimePicker'
|
||||||
|
| 'TreeSelect'
|
||||||
|
| 'Upload'
|
||||||
|
| BaseFormComponentType;
|
||||||
|
|
||||||
|
// 初始化表单组件,并注册到form组件内部
|
||||||
|
setupVbenForm<FormComponentType>({
|
||||||
|
components: {
|
||||||
|
AutoComplete,
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
DatePicker,
|
||||||
|
// 自定义默认的重置按钮
|
||||||
|
DefaultResetActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||||
|
},
|
||||||
|
// 自定义默认的提交按钮
|
||||||
|
DefaultSubmitActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||||
|
},
|
||||||
|
Divider,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
InputPassword,
|
||||||
|
Mentions,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
RangePicker,
|
||||||
|
Rate,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect,
|
||||||
|
Upload,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
baseModelPropName: 'value',
|
||||||
|
modelPropNameMap: {
|
||||||
|
Checkbox: 'checked',
|
||||||
|
Radio: 'checked',
|
||||||
|
Switch: 'checked',
|
||||||
|
Upload: 'fileList',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if ((!value && value !== 0) || value.length === 0) {
|
||||||
|
return $t('formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVbenForm = useForm<FormComponentType>;
|
||||||
|
|
||||||
|
export { useVbenForm, z };
|
||||||
|
|
||||||
|
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||||
|
export type { VbenFormProps };
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './form';
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { baseRequestClient, requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace AuthApi {
|
||||||
|
/** 登录接口参数 */
|
||||||
|
export interface LoginParams {
|
||||||
|
password: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 登录接口返回值 */
|
||||||
|
export interface LoginResult {
|
||||||
|
accessToken: string;
|
||||||
|
desc: string;
|
||||||
|
realName: string;
|
||||||
|
userId: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefreshTokenResult {
|
||||||
|
data: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*/
|
||||||
|
export async function loginApi(data: AuthApi.LoginParams) {
|
||||||
|
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新accessToken
|
||||||
|
*/
|
||||||
|
export async function refreshTokenApi() {
|
||||||
|
return baseRequestClient.post<AuthApi.RefreshTokenResult>('/auth/refresh', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*/
|
||||||
|
export async function logoutApi() {
|
||||||
|
return baseRequestClient.post('/auth/logout', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户权限码
|
||||||
|
*/
|
||||||
|
export async function getAccessCodesApi() {
|
||||||
|
return requestClient.get<string[]>('/auth/codes');
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './auth';
|
||||||
|
export * from './menu';
|
||||||
|
export * from './user';
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { RouteRecordStringComponent } from '@vben/types';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户所有菜单
|
||||||
|
*/
|
||||||
|
export async function getAllMenusApi() {
|
||||||
|
return requestClient.get<RouteRecordStringComponent[]>('/menu/all');
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { UserInfo } from '@vben/types';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
export async function getUserInfoApi() {
|
||||||
|
return requestClient.get<UserInfo>('/user/info');
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './core';
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
|
*/
|
||||||
|
import { useAppConfig } from '@vben/hooks';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
import {
|
||||||
|
authenticateResponseInterceptor,
|
||||||
|
errorMessageResponseInterceptor,
|
||||||
|
RequestClient,
|
||||||
|
} from '@vben/request';
|
||||||
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
|
import { refreshTokenApi } from './core';
|
||||||
|
|
||||||
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
|
function createRequestClient(baseURL: string) {
|
||||||
|
const client = new RequestClient({
|
||||||
|
baseURL,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新认证逻辑
|
||||||
|
*/
|
||||||
|
async function doReAuthenticate() {
|
||||||
|
console.warn('Access token or refresh token is invalid or expired. ');
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
accessStore.setAccessToken(null);
|
||||||
|
if (
|
||||||
|
preferences.app.loginExpiredMode === 'modal' &&
|
||||||
|
accessStore.isAccessChecked
|
||||||
|
) {
|
||||||
|
accessStore.setLoginExpired(true);
|
||||||
|
} else {
|
||||||
|
await authStore.logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新token逻辑
|
||||||
|
*/
|
||||||
|
async function doRefreshToken() {
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const resp = await refreshTokenApi();
|
||||||
|
const newToken = resp.data;
|
||||||
|
accessStore.setAccessToken(newToken);
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatToken(token: null | string) {
|
||||||
|
return token ? `Bearer ${token}` : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求头处理
|
||||||
|
client.addRequestInterceptor({
|
||||||
|
fulfilled: async (config) => {
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
|
config.headers.Authorization = formatToken(accessStore.accessToken);
|
||||||
|
config.headers['Accept-Language'] = preferences.app.locale;
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// response数据解构
|
||||||
|
client.addResponseInterceptor({
|
||||||
|
fulfilled: (response) => {
|
||||||
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
|
const { code, data, message: msg } = responseData;
|
||||||
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
throw new Error(`Error ${status}: ${msg}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// token过期的处理
|
||||||
|
client.addResponseInterceptor(
|
||||||
|
authenticateResponseInterceptor({
|
||||||
|
client,
|
||||||
|
doReAuthenticate,
|
||||||
|
doRefreshToken,
|
||||||
|
enableRefreshToken: preferences.app.enableRefreshToken,
|
||||||
|
formatToken,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
||||||
|
client.addResponseInterceptor(
|
||||||
|
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requestClient = createRequestClient(apiURL);
|
||||||
|
|
||||||
|
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
|
@ -0,0 +1,39 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { useAntdDesignTokens } from '@vben/hooks';
|
||||||
|
import { preferences, usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { App, ConfigProvider, theme } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { antdLocale } from '#/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'App' });
|
||||||
|
|
||||||
|
const { isDark } = usePreferences();
|
||||||
|
const { tokens } = useAntdDesignTokens();
|
||||||
|
|
||||||
|
const tokenTheme = computed(() => {
|
||||||
|
const algorithm = isDark.value
|
||||||
|
? [theme.darkAlgorithm]
|
||||||
|
: [theme.defaultAlgorithm];
|
||||||
|
|
||||||
|
// antd 紧凑模式算法
|
||||||
|
if (preferences.app.compact) {
|
||||||
|
algorithm.push(theme.compactAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
algorithm,
|
||||||
|
token: tokens,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ConfigProvider :locale="antdLocale" :theme="tokenTheme">
|
||||||
|
<App>
|
||||||
|
<RouterView />
|
||||||
|
</App>
|
||||||
|
</ConfigProvider>
|
||||||
|
</template>
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
|
||||||
|
import { registerAccessDirective } from '@vben/access';
|
||||||
|
import { initStores } from '@vben/stores';
|
||||||
|
import '@vben/styles';
|
||||||
|
import '@vben/styles/antd';
|
||||||
|
|
||||||
|
import { setupI18n } from '#/locales';
|
||||||
|
|
||||||
|
import App from './app.vue';
|
||||||
|
import { router } from './router';
|
||||||
|
|
||||||
|
async function bootstrap(namespace: string) {
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
// 国际化 i18n 配置
|
||||||
|
await setupI18n(app);
|
||||||
|
|
||||||
|
// 配置 pinia-tore
|
||||||
|
await initStores(app, { namespace });
|
||||||
|
|
||||||
|
// 安装权限指令
|
||||||
|
registerAccessDirective(app);
|
||||||
|
|
||||||
|
// 配置路由及路由守卫
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
app.mount('#app');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { bootstrap };
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { AuthPageLayout } from '@vben/layouts';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const appName = computed(() => preferences.app.name);
|
||||||
|
const logo = computed(() => preferences.logo.source);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthPageLayout
|
||||||
|
:app-name="appName"
|
||||||
|
:logo="logo"
|
||||||
|
:page-description="$t('authentication.pageDesc')"
|
||||||
|
:page-title="$t('authentication.pageTitle')"
|
||||||
|
>
|
||||||
|
<!-- 自定义工具栏 -->
|
||||||
|
<!-- <template #toolbar></template> -->
|
||||||
|
</AuthPageLayout>
|
||||||
|
</template>
|
|
@ -0,0 +1,140 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { NotificationItem } from '@vben/layouts';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||||
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
|
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
||||||
|
import {
|
||||||
|
BasicLayout,
|
||||||
|
LockScreen,
|
||||||
|
Notification,
|
||||||
|
UserDropdown,
|
||||||
|
} from '@vben/layouts';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
|
import { openWindow } from '@vben/utils';
|
||||||
|
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
import { useAuthStore } from '#/store';
|
||||||
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
|
const notifications = ref<NotificationItem[]>([
|
||||||
|
{
|
||||||
|
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
||||||
|
date: '3小时前',
|
||||||
|
isRead: true,
|
||||||
|
message: '描述信息描述信息描述信息',
|
||||||
|
title: '收到了 14 份新周报',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
|
date: '刚刚',
|
||||||
|
isRead: false,
|
||||||
|
message: '描述信息描述信息描述信息',
|
||||||
|
title: '朱偏右 回复了你',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
|
date: '2024-01-01',
|
||||||
|
isRead: false,
|
||||||
|
message: '描述信息描述信息描述信息',
|
||||||
|
title: '曲丽丽 评论了你',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://avatar.vercel.sh/satori',
|
||||||
|
date: '1天前',
|
||||||
|
isRead: false,
|
||||||
|
message: '描述信息描述信息描述信息',
|
||||||
|
title: '代办提醒',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const showDot = computed(() =>
|
||||||
|
notifications.value.some((item) => !item.isRead),
|
||||||
|
);
|
||||||
|
|
||||||
|
const menus = computed(() => [
|
||||||
|
{
|
||||||
|
handler: () => {
|
||||||
|
openWindow(VBEN_DOC_URL, {
|
||||||
|
target: '_blank',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: BookOpenText,
|
||||||
|
text: $t('widgets.document'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
handler: () => {
|
||||||
|
openWindow(VBEN_GITHUB_URL, {
|
||||||
|
target: '_blank',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: MdiGithub,
|
||||||
|
text: 'GitHub',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
handler: () => {
|
||||||
|
openWindow(`${VBEN_GITHUB_URL}/issues`, {
|
||||||
|
target: '_blank',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: CircleHelp,
|
||||||
|
text: $t('widgets.qa'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const avatar = computed(() => {
|
||||||
|
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleLogout() {
|
||||||
|
await authStore.logout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNoticeClear() {
|
||||||
|
notifications.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMakeAll() {
|
||||||
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
||||||
|
<template #user-dropdown>
|
||||||
|
<UserDropdown
|
||||||
|
:avatar
|
||||||
|
:menus
|
||||||
|
:text="userStore.userInfo?.realName"
|
||||||
|
description="ann.vben@gmail.com"
|
||||||
|
tag-text="Pro"
|
||||||
|
@logout="handleLogout"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #notification>
|
||||||
|
<Notification
|
||||||
|
:dot="showDot"
|
||||||
|
:notifications="notifications"
|
||||||
|
@clear="handleNoticeClear"
|
||||||
|
@make-all="handleMakeAll"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<AuthenticationLoginExpiredModal
|
||||||
|
v-model:open="accessStore.loginExpired"
|
||||||
|
:avatar
|
||||||
|
>
|
||||||
|
<LoginForm />
|
||||||
|
</AuthenticationLoginExpiredModal>
|
||||||
|
</template>
|
||||||
|
<template #lock-screen>
|
||||||
|
<LockScreen :avatar @to-login="handleLogout" />
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
</template>
|
|
@ -0,0 +1,6 @@
|
||||||
|
const BasicLayout = () => import('./basic.vue');
|
||||||
|
const AuthPageLayout = () => import('./auth.vue');
|
||||||
|
|
||||||
|
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||||
|
|
||||||
|
export { AuthPageLayout, BasicLayout, IFrameView };
|
|
@ -0,0 +1,3 @@
|
||||||
|
# locale
|
||||||
|
|
||||||
|
每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。
|
|
@ -0,0 +1,94 @@
|
||||||
|
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||||
|
import type { Locale } from 'ant-design-vue/es/locale';
|
||||||
|
|
||||||
|
import type { App } from 'vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import antdEnLocale from 'ant-design-vue/es/locale/en_US';
|
||||||
|
import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const antdLocale = ref<Locale>(antdDefaultLocale);
|
||||||
|
|
||||||
|
const modules = import.meta.glob('./langs/*.json');
|
||||||
|
|
||||||
|
const localesMap = loadLocalesMap(modules);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载应用特有的语言包
|
||||||
|
* 这里也可以改造为从服务端获取翻译数据
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
async function loadMessages(lang: SupportedLanguagesType) {
|
||||||
|
const [appLocaleMessages] = await Promise.all([
|
||||||
|
localesMap[lang]?.(),
|
||||||
|
loadThirdPartyMessage(lang),
|
||||||
|
]);
|
||||||
|
return appLocaleMessages?.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载第三方组件库的语言包
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
||||||
|
await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载dayjs的语言包
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||||
|
let locale;
|
||||||
|
switch (lang) {
|
||||||
|
case 'zh-CN': {
|
||||||
|
locale = await import('dayjs/locale/zh-cn');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'en-US': {
|
||||||
|
locale = await import('dayjs/locale/en');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 默认使用英语
|
||||||
|
default: {
|
||||||
|
locale = await import('dayjs/locale/en');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (locale) {
|
||||||
|
dayjs.locale(locale);
|
||||||
|
} else {
|
||||||
|
console.error(`Failed to load dayjs locale for ${lang}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载antd的语言包
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
async function loadAntdLocale(lang: SupportedLanguagesType) {
|
||||||
|
switch (lang) {
|
||||||
|
case 'zh-CN': {
|
||||||
|
antdLocale.value = antdDefaultLocale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'en-US': {
|
||||||
|
antdLocale.value = antdEnLocale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
|
||||||
|
await coreSetup(app, {
|
||||||
|
defaultLocale: preferences.app.locale,
|
||||||
|
loadMessages,
|
||||||
|
missingWarn: !import.meta.env.PROD,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { $t, antdLocale, setupI18n };
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"page": {
|
||||||
|
"demos": {
|
||||||
|
"title": "Demos",
|
||||||
|
"antd": "Ant Design Vue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"page": {
|
||||||
|
"demos": {
|
||||||
|
"title": "演示",
|
||||||
|
"antd": "Ant Design Vue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { initPreferences } from '@vben/preferences';
|
||||||
|
import { unmountGlobalLoading } from '@vben/utils';
|
||||||
|
|
||||||
|
import { overridesPreferences } from './preferences';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用初始化完成之后再进行页面加载渲染
|
||||||
|
*/
|
||||||
|
async function initApplication() {
|
||||||
|
// name用于指定项目唯一标识
|
||||||
|
// 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据
|
||||||
|
const env = import.meta.env.PROD ? 'prod' : 'dev';
|
||||||
|
const appVersion = import.meta.env.VITE_APP_VERSION;
|
||||||
|
const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
|
||||||
|
|
||||||
|
// app偏好设置初始化
|
||||||
|
await initPreferences({
|
||||||
|
namespace,
|
||||||
|
overrides: overridesPreferences,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动应用并挂载
|
||||||
|
// vue应用主要逻辑及视图
|
||||||
|
const { bootstrap } = await import('./bootstrap');
|
||||||
|
await bootstrap(namespace);
|
||||||
|
|
||||||
|
// 移除并销毁loading
|
||||||
|
unmountGlobalLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
initApplication();
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { defineOverridesPreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 项目配置文件
|
||||||
|
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
|
||||||
|
*/
|
||||||
|
export const overridesPreferences = defineOverridesPreferences({
|
||||||
|
// overrides
|
||||||
|
app: {
|
||||||
|
name: import.meta.env.VITE_APP_TITLE,
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
import type {
|
||||||
|
ComponentRecordType,
|
||||||
|
GenerateMenuAndRoutesOptions,
|
||||||
|
} from '@vben/types';
|
||||||
|
|
||||||
|
import { generateAccessible } from '@vben/access';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { getAllMenusApi } from '#/api';
|
||||||
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
||||||
|
|
||||||
|
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||||
|
const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue');
|
||||||
|
|
||||||
|
const layoutMap: ComponentRecordType = {
|
||||||
|
BasicLayout,
|
||||||
|
IFrameView,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await generateAccessible(preferences.app.accessMode, {
|
||||||
|
...options,
|
||||||
|
fetchMenuListAsync: async () => {
|
||||||
|
message.loading({
|
||||||
|
content: `${$t('common.loadingMenu')}...`,
|
||||||
|
duration: 1.5,
|
||||||
|
});
|
||||||
|
return await getAllMenusApi();
|
||||||
|
},
|
||||||
|
// 可以指定没有权限跳转403页面
|
||||||
|
forbiddenComponent,
|
||||||
|
// 如果 route.meta.menuVisibleWithForbidden = true
|
||||||
|
layoutMap,
|
||||||
|
pageMap,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { generateAccess };
|
|
@ -0,0 +1,137 @@
|
||||||
|
import type { Router } from 'vue-router';
|
||||||
|
|
||||||
|
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
|
import { startProgress, stopProgress } from '@vben/utils';
|
||||||
|
|
||||||
|
import { useTitle } from '@vueuse/core';
|
||||||
|
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
import { coreRouteNames, dynamicRoutes } from '#/router/routes';
|
||||||
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
|
import { generateAccess } from './access';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用守卫配置
|
||||||
|
* @param router
|
||||||
|
*/
|
||||||
|
function setupCommonGuard(router: Router) {
|
||||||
|
// 记录已经加载的页面
|
||||||
|
const loadedPaths = new Set<string>();
|
||||||
|
|
||||||
|
router.beforeEach(async (to) => {
|
||||||
|
to.meta.loaded = loadedPaths.has(to.path);
|
||||||
|
|
||||||
|
// 页面加载进度条
|
||||||
|
if (!to.meta.loaded && preferences.transition.progress) {
|
||||||
|
startProgress();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.afterEach((to) => {
|
||||||
|
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
||||||
|
|
||||||
|
if (preferences.tabbar.enable) {
|
||||||
|
loadedPaths.add(to.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭页面加载进度条
|
||||||
|
if (preferences.transition.progress) {
|
||||||
|
stopProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态修改标题
|
||||||
|
if (preferences.app.dynamicTitle) {
|
||||||
|
const { title } = to.meta;
|
||||||
|
// useTitle(`${$t(title)} - ${preferences.app.name}`);
|
||||||
|
useTitle(`${$t(title)} - ${preferences.app.name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限访问守卫配置
|
||||||
|
* @param router
|
||||||
|
*/
|
||||||
|
function setupAccessGuard(router: Router) {
|
||||||
|
router.beforeEach(async (to, from) => {
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
// 基本路由,这些路由不需要进入权限拦截
|
||||||
|
if (coreRouteNames.includes(to.name as string)) {
|
||||||
|
if (to.path === LOGIN_PATH && accessStore.accessToken) {
|
||||||
|
return decodeURIComponent(
|
||||||
|
(to.query?.redirect as string) || DEFAULT_HOME_PATH,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// accessToken 检查
|
||||||
|
if (!accessStore.accessToken) {
|
||||||
|
// 明确声明忽略权限访问权限,则可以访问
|
||||||
|
if (to.meta.ignoreAccess) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有访问权限,跳转登录页面
|
||||||
|
if (to.fullPath !== LOGIN_PATH) {
|
||||||
|
return {
|
||||||
|
path: LOGIN_PATH,
|
||||||
|
// 如不需要,直接删除 query
|
||||||
|
query: { redirect: encodeURIComponent(to.fullPath) },
|
||||||
|
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||||
|
replace: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否已经生成过动态路由
|
||||||
|
if (accessStore.isAccessChecked) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成路由表
|
||||||
|
// 当前登录用户拥有的角色标识列表
|
||||||
|
const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
|
||||||
|
const userRoles = userInfo.roles ?? [];
|
||||||
|
|
||||||
|
// 生成菜单和路由
|
||||||
|
const { accessibleMenus, accessibleRoutes } = await generateAccess({
|
||||||
|
roles: userRoles,
|
||||||
|
router,
|
||||||
|
// 则会在菜单中显示,但是访问会被重定向到403
|
||||||
|
routes: dynamicRoutes,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存菜单信息和路由信息
|
||||||
|
accessStore.setAccessMenus(accessibleMenus);
|
||||||
|
accessStore.setAccessRoutes(accessibleRoutes);
|
||||||
|
accessStore.setIsAccessChecked(true);
|
||||||
|
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...router.resolve(decodeURIComponent(redirectPath)),
|
||||||
|
replace: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目守卫配置
|
||||||
|
* @param router
|
||||||
|
*/
|
||||||
|
function createRouterGuard(router: Router) {
|
||||||
|
/** 通用 */
|
||||||
|
setupCommonGuard(router);
|
||||||
|
/** 权限访问 */
|
||||||
|
setupAccessGuard(router);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createRouterGuard };
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {
|
||||||
|
createRouter,
|
||||||
|
createWebHashHistory,
|
||||||
|
createWebHistory,
|
||||||
|
} from 'vue-router';
|
||||||
|
|
||||||
|
import { resetStaticRoutes } from '@vben/utils';
|
||||||
|
|
||||||
|
import { createRouterGuard } from './guard';
|
||||||
|
import { routes } from './routes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @zh_CN 创建vue-router实例
|
||||||
|
*/
|
||||||
|
const router = createRouter({
|
||||||
|
history:
|
||||||
|
import.meta.env.VITE_ROUTER_HISTORY === 'hash'
|
||||||
|
? createWebHashHistory(import.meta.env.VITE_BASE)
|
||||||
|
: createWebHistory(import.meta.env.VITE_BASE),
|
||||||
|
// 应该添加到路由的初始路由列表。
|
||||||
|
routes,
|
||||||
|
scrollBehavior: (to, _from, savedPosition) => {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition;
|
||||||
|
}
|
||||||
|
return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
|
||||||
|
},
|
||||||
|
// 是否应该禁止尾部斜杠。
|
||||||
|
// strict: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetRoutes = () => resetStaticRoutes(router, routes);
|
||||||
|
|
||||||
|
// 创建路由守卫
|
||||||
|
createRouterGuard(router);
|
||||||
|
|
||||||
|
export { resetRoutes, router };
|
|
@ -0,0 +1,86 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { DEFAULT_HOME_PATH } from '@vben/constants';
|
||||||
|
|
||||||
|
import { AuthPageLayout } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
import Login from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
|
/** 全局404页面 */
|
||||||
|
const fallbackNotFoundRoute: RouteRecordRaw = {
|
||||||
|
component: () => import('#/views/_core/fallback/not-found.vue'),
|
||||||
|
meta: {
|
||||||
|
hideInBreadcrumb: true,
|
||||||
|
hideInMenu: true,
|
||||||
|
hideInTab: true,
|
||||||
|
title: '404',
|
||||||
|
},
|
||||||
|
name: 'FallbackNotFound',
|
||||||
|
path: '/:path(.*)*',
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 基本路由,这些路由是必须存在的 */
|
||||||
|
const coreRoutes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
title: 'Root',
|
||||||
|
},
|
||||||
|
name: 'Root',
|
||||||
|
path: '/',
|
||||||
|
redirect: DEFAULT_HOME_PATH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: AuthPageLayout,
|
||||||
|
meta: {
|
||||||
|
title: 'Authentication',
|
||||||
|
},
|
||||||
|
name: 'Authentication',
|
||||||
|
path: '/auth',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'Login',
|
||||||
|
path: 'login',
|
||||||
|
component: Login,
|
||||||
|
meta: {
|
||||||
|
title: $t('page.core.login'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CodeLogin',
|
||||||
|
path: 'code-login',
|
||||||
|
component: () => import('#/views/_core/authentication/code-login.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('page.core.codeLogin'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'QrCodeLogin',
|
||||||
|
path: 'qrcode-login',
|
||||||
|
component: () =>
|
||||||
|
import('#/views/_core/authentication/qrcode-login.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('page.core.qrcodeLogin'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ForgetPassword',
|
||||||
|
path: 'forget-password',
|
||||||
|
component: () =>
|
||||||
|
import('#/views/_core/authentication/forget-password.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('page.core.forgetPassword'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Register',
|
||||||
|
path: 'register',
|
||||||
|
component: () => import('#/views/_core/authentication/register.vue'),
|
||||||
|
meta: {
|
||||||
|
title: $t('page.core.register'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export { coreRoutes, fallbackNotFoundRoute };
|
|
@ -0,0 +1,31 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { mergeRouteModules, traverseTreeValues } from '@vben/utils';
|
||||||
|
|
||||||
|
import { coreRoutes, fallbackNotFoundRoute } from './core';
|
||||||
|
|
||||||
|
const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
|
||||||
|
eager: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 有需要可以自行打开注释,并创建文件夹
|
||||||
|
// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true });
|
||||||
|
|
||||||
|
/** 动态路由 */
|
||||||
|
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
|
||||||
|
|
||||||
|
/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统 */
|
||||||
|
// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
|
||||||
|
const externalRoutes: RouteRecordRaw[] = [];
|
||||||
|
|
||||||
|
/** 路由列表,由基本路由+静态路由组成 */
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
...coreRoutes,
|
||||||
|
...externalRoutes,
|
||||||
|
fallbackNotFoundRoute,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
||||||
|
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||||
|
|
||||||
|
export { coreRouteNames, dynamicRoutes, routes };
|
|
@ -0,0 +1,40 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { BasicLayout } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
component: BasicLayout,
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:layout-dashboard',
|
||||||
|
order: -1,
|
||||||
|
title: $t('page.dashboard.title'),
|
||||||
|
},
|
||||||
|
name: 'Dashboard',
|
||||||
|
path: '/',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'Analytics',
|
||||||
|
path: '/analytics',
|
||||||
|
component: () => import('#/views/dashboard/analytics/index.vue'),
|
||||||
|
meta: {
|
||||||
|
affixTab: true,
|
||||||
|
icon: 'lucide:area-chart',
|
||||||
|
title: $t('page.dashboard.analytics'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Workspace',
|
||||||
|
path: '/workspace',
|
||||||
|
component: () => import('#/views/dashboard/workspace/index.vue'),
|
||||||
|
meta: {
|
||||||
|
icon: 'carbon:workspace',
|
||||||
|
title: $t('page.dashboard.workspace'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
|
@ -0,0 +1,30 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { BasicLayout } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
component: BasicLayout,
|
||||||
|
meta: {
|
||||||
|
icon: 'ic:baseline-view-in-ar',
|
||||||
|
keepAlive: true,
|
||||||
|
order: 1000,
|
||||||
|
title: $t('page.demos.title'),
|
||||||
|
},
|
||||||
|
name: 'Demos',
|
||||||
|
path: '/demos',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
meta: {
|
||||||
|
title: $t('page.demos.antd'),
|
||||||
|
},
|
||||||
|
name: 'AntDesignDemos',
|
||||||
|
path: '/demos/ant-design',
|
||||||
|
component: () => import('#/views/demos/antd/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
|
@ -0,0 +1,81 @@
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import {
|
||||||
|
VBEN_DOC_URL,
|
||||||
|
VBEN_ELE_PREVIEW_URL,
|
||||||
|
VBEN_GITHUB_URL,
|
||||||
|
VBEN_LOGO_URL,
|
||||||
|
VBEN_NAIVE_PREVIEW_URL,
|
||||||
|
} from '@vben/constants';
|
||||||
|
|
||||||
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
component: BasicLayout,
|
||||||
|
meta: {
|
||||||
|
badgeType: 'dot',
|
||||||
|
icon: VBEN_LOGO_URL,
|
||||||
|
order: 9999,
|
||||||
|
title: $t('page.vben.title'),
|
||||||
|
},
|
||||||
|
name: 'VbenProject',
|
||||||
|
path: '/vben-admin',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'VbenAbout',
|
||||||
|
path: '/vben-admin/about',
|
||||||
|
component: () => import('#/views/_core/about/index.vue'),
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:copyright',
|
||||||
|
title: $t('page.vben.about'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VbenDocument',
|
||||||
|
path: '/vben-admin/document',
|
||||||
|
component: IFrameView,
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:book-open-text',
|
||||||
|
link: VBEN_DOC_URL,
|
||||||
|
title: $t('page.vben.document'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VbenGithub',
|
||||||
|
path: '/vben-admin/github',
|
||||||
|
component: IFrameView,
|
||||||
|
meta: {
|
||||||
|
icon: 'mdi:github',
|
||||||
|
link: VBEN_GITHUB_URL,
|
||||||
|
title: 'Github',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VbenNaive',
|
||||||
|
path: '/vben-admin/naive',
|
||||||
|
component: IFrameView,
|
||||||
|
meta: {
|
||||||
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:naiveui',
|
||||||
|
link: VBEN_NAIVE_PREVIEW_URL,
|
||||||
|
title: $t('page.vben.naive-ui'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VbenElementPlus',
|
||||||
|
path: '/vben-admin/ele',
|
||||||
|
component: IFrameView,
|
||||||
|
meta: {
|
||||||
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:element',
|
||||||
|
link: VBEN_ELE_PREVIEW_URL,
|
||||||
|
title: $t('page.vben.element-plus'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
|
@ -0,0 +1,112 @@
|
||||||
|
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
||||||
|
import type { UserInfo } from '@vben/types';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||||
|
import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
||||||
|
|
||||||
|
import { notification } from 'ant-design-vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const loginLoading = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步处理登录操作
|
||||||
|
* Asynchronously handle the login process
|
||||||
|
* @param params 登录表单数据
|
||||||
|
*/
|
||||||
|
async function authLogin(
|
||||||
|
params: LoginAndRegisterParams,
|
||||||
|
onSuccess?: () => Promise<void> | void,
|
||||||
|
) {
|
||||||
|
// 异步处理用户登录操作并获取 accessToken
|
||||||
|
let userInfo: null | UserInfo = null;
|
||||||
|
try {
|
||||||
|
loginLoading.value = true;
|
||||||
|
const { accessToken } = await loginApi(params);
|
||||||
|
|
||||||
|
// 如果成功获取到 accessToken
|
||||||
|
if (accessToken) {
|
||||||
|
accessStore.setAccessToken(accessToken);
|
||||||
|
|
||||||
|
// 获取用户信息并存储到 accessStore 中
|
||||||
|
const [fetchUserInfoResult, accessCodes] = await Promise.all([
|
||||||
|
fetchUserInfo(),
|
||||||
|
getAccessCodesApi(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
userInfo = fetchUserInfoResult;
|
||||||
|
|
||||||
|
userStore.setUserInfo(userInfo);
|
||||||
|
accessStore.setAccessCodes(accessCodes);
|
||||||
|
|
||||||
|
if (accessStore.loginExpired) {
|
||||||
|
accessStore.setLoginExpired(false);
|
||||||
|
} else {
|
||||||
|
onSuccess
|
||||||
|
? await onSuccess?.()
|
||||||
|
: await router.push(userInfo.homePath || DEFAULT_HOME_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo?.realName) {
|
||||||
|
notification.success({
|
||||||
|
description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`,
|
||||||
|
duration: 3,
|
||||||
|
message: $t('authentication.loginSuccess'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loginLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout(redirect: boolean = true) {
|
||||||
|
await logoutApi();
|
||||||
|
resetAllStores();
|
||||||
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
|
// 回登陆页带上当前路由地址
|
||||||
|
await router.replace({
|
||||||
|
path: LOGIN_PATH,
|
||||||
|
query: redirect
|
||||||
|
? {
|
||||||
|
redirect: encodeURIComponent(router.currentRoute.value.fullPath),
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUserInfo() {
|
||||||
|
let userInfo: null | UserInfo = null;
|
||||||
|
userInfo = await getUserInfoApi();
|
||||||
|
userStore.setUserInfo(userInfo);
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function $reset() {
|
||||||
|
loginLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
$reset,
|
||||||
|
authLogin,
|
||||||
|
fetchUserInfo,
|
||||||
|
loginLoading,
|
||||||
|
logout,
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './auth';
|
|
@ -0,0 +1,3 @@
|
||||||
|
# \_core
|
||||||
|
|
||||||
|
此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { About } from '@vben/common-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'About' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<About />
|
||||||
|
</template>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'VbenInput',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('authentication.mobile'),
|
||||||
|
},
|
||||||
|
fieldName: 'phoneNumber',
|
||||||
|
label: $t('authentication.mobile'),
|
||||||
|
rules: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: $t('authentication.mobileTip') })
|
||||||
|
.refine((v) => /^\d{11}$/.test(v), {
|
||||||
|
message: $t('authentication.mobileErrortip'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenPinInput',
|
||||||
|
componentProps: {
|
||||||
|
createText: (countdown: number) => {
|
||||||
|
const text =
|
||||||
|
countdown > 0
|
||||||
|
? $t('authentication.sendText', [countdown])
|
||||||
|
: $t('authentication.sendCode');
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
placeholder: $t('authentication.code'),
|
||||||
|
},
|
||||||
|
fieldName: 'code',
|
||||||
|
label: $t('authentication.code'),
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.codeTip') }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 异步处理登录操作
|
||||||
|
* Asynchronously handle the login process
|
||||||
|
* @param values 登录表单数据
|
||||||
|
*/
|
||||||
|
async function handleLogin(values: LoginCodeParams) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(values);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthenticationCodeLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
|
:loading="loading"
|
||||||
|
@submit="handleLogin"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,42 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationForgetPassword, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'ForgetPassword' });
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'VbenInput',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: 'example@example.com',
|
||||||
|
},
|
||||||
|
fieldName: 'email',
|
||||||
|
label: $t('authentication.email'),
|
||||||
|
rules: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: $t('authentication.emailTip') })
|
||||||
|
.email($t('authentication.emailValidErrorTip')),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSubmit(value: string) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('reset email:', value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthenticationForgetPassword
|
||||||
|
:form-schema="formSchema"
|
||||||
|
:loading="loading"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,91 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { BasicOption } from '@vben/types';
|
||||||
|
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationLogin, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
|
defineOptions({ name: 'Login' });
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const MOCK_USER_OPTIONS: BasicOption[] = [
|
||||||
|
{
|
||||||
|
label: '超级管理员',
|
||||||
|
value: 'vben',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '管理员',
|
||||||
|
value: 'admin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '用户',
|
||||||
|
value: 'jack',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'VbenSelect',
|
||||||
|
componentProps: {
|
||||||
|
options: MOCK_USER_OPTIONS,
|
||||||
|
placeholder: $t('authentication.selectAccount'),
|
||||||
|
},
|
||||||
|
fieldName: 'selectAccount',
|
||||||
|
label: $t('authentication.selectAccount'),
|
||||||
|
rules: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: $t('authentication.selectAccount') })
|
||||||
|
.optional()
|
||||||
|
.default('vben'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenInput',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('authentication.usernameTip'),
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
trigger(values, form) {
|
||||||
|
if (values.selectAccount) {
|
||||||
|
const findUser = MOCK_USER_OPTIONS.find(
|
||||||
|
(item) => item.value === values.selectAccount,
|
||||||
|
);
|
||||||
|
if (findUser) {
|
||||||
|
form.setValues({
|
||||||
|
password: '123456',
|
||||||
|
username: findUser.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
triggerFields: ['selectAccount'],
|
||||||
|
},
|
||||||
|
fieldName: 'username',
|
||||||
|
label: $t('authentication.username'),
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenInputPassword',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('authentication.password'),
|
||||||
|
},
|
||||||
|
fieldName: 'password',
|
||||||
|
label: $t('authentication.password'),
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthenticationLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
|
:loading="authStore.loginLoading"
|
||||||
|
@submit="authStore.authLogin"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { AuthenticationQrCodeLogin } from '@vben/common-ui';
|
||||||
|
import { LOGIN_PATH } from '@vben/constants';
|
||||||
|
|
||||||
|
defineOptions({ name: 'QrCodeLogin' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthenticationQrCodeLogin :login-path="LOGIN_PATH" />
|
||||||
|
</template>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationRegister, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
defineOptions({ name: 'Register' });
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'VbenInput',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('authentication.usernameTip'),
|
||||||
|
},
|
||||||
|
fieldName: 'username',
|
||||||
|
label: $t('authentication.username'),
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenInputPassword',
|
||||||
|
componentProps: {
|
||||||
|
passwordStrength: true,
|
||||||
|
placeholder: $t('authentication.password'),
|
||||||
|
},
|
||||||
|
fieldName: 'password',
|
||||||
|
label: $t('authentication.password'),
|
||||||
|
renderComponentContent() {
|
||||||
|
return {
|
||||||
|
strengthText: () => $t('authentication.passwordStrength'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenInputPassword',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('authentication.confirmPassword'),
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
rules(values) {
|
||||||
|
const { password } = values;
|
||||||
|
return z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: $t('authentication.passwordTip') })
|
||||||
|
.refine((value) => value === password, {
|
||||||
|
message: $t('authentication.confirmPasswordTip'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
triggerFields: ['password'],
|
||||||
|
},
|
||||||
|
fieldName: 'confirmPassword',
|
||||||
|
label: $t('authentication.confirmPassword'),
|
||||||
|
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'VbenCheckbox',
|
||||||
|
fieldName: 'agreePolicy',
|
||||||
|
renderComponentContent: () => ({
|
||||||
|
default: () =>
|
||||||
|
h('span', [
|
||||||
|
$t('authentication.agree'),
|
||||||
|
h(
|
||||||
|
'a',
|
||||||
|
{
|
||||||
|
class:
|
||||||
|
'cursor-pointer text-primary ml-1 hover:text-primary-hover',
|
||||||
|
href: '',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
$t('authentication.privacyPolicy'),
|
||||||
|
'&',
|
||||||
|
$t('authentication.terms'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
rules: z.boolean().refine((value) => !!value, {
|
||||||
|
message: $t('authentication.agreeTip'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleSubmit(value: LoginAndRegisterParams) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('register submit:', value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AuthenticationRegister
|
||||||
|
:form-schema="formSchema"
|
||||||
|
:loading="loading"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback status="coming-soon" />
|
||||||
|
</template>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'Fallback403Demo' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback status="403" />
|
||||||
|
</template>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'Fallback500Demo' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback status="500" />
|
||||||
|
</template>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'Fallback404Demo' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback status="404" />
|
||||||
|
</template>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'FallbackOfflineDemo' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback status="offline" />
|
||||||
|
</template>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EchartsUI,
|
||||||
|
type EchartsUIType,
|
||||||
|
useEcharts,
|
||||||
|
} from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
renderEcharts({
|
||||||
|
grid: {
|
||||||
|
bottom: 0,
|
||||||
|
containLabel: true,
|
||||||
|
left: '1%',
|
||||||
|
right: '1%',
|
||||||
|
top: '2 %',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
areaStyle: {},
|
||||||
|
data: [
|
||||||
|
111, 2000, 6000, 16_000, 33_333, 55_555, 64_000, 33_333, 18_000,
|
||||||
|
36_000, 70_000, 42_444, 23_222, 13_000, 8000, 4000, 1200, 333, 222,
|
||||||
|
111,
|
||||||
|
],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#5ab1ef',
|
||||||
|
},
|
||||||
|
smooth: true,
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
areaStyle: {},
|
||||||
|
data: [
|
||||||
|
33, 66, 88, 333, 3333, 6200, 20_000, 3000, 1200, 13_000, 22_000,
|
||||||
|
11_000, 2221, 1201, 390, 198, 60, 30, 22, 11,
|
||||||
|
],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#019680',
|
||||||
|
},
|
||||||
|
smooth: true,
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
axisPointer: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#019680',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
trigger: 'axis',
|
||||||
|
},
|
||||||
|
// xAxis: {
|
||||||
|
// axisTick: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// boundaryGap: false,
|
||||||
|
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
// type: 'category',
|
||||||
|
// },
|
||||||
|
xAxis: {
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
boundaryGap: false,
|
||||||
|
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
type: 'category',
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
max: 80_000,
|
||||||
|
splitArea: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
splitNumber: 4,
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EchartsUI ref="chartRef" />
|
||||||
|
</template>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EchartsUI,
|
||||||
|
type EchartsUIType,
|
||||||
|
useEcharts,
|
||||||
|
} from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
renderEcharts({
|
||||||
|
legend: {
|
||||||
|
bottom: 0,
|
||||||
|
data: ['访问', '趋势'],
|
||||||
|
},
|
||||||
|
radar: {
|
||||||
|
indicator: [
|
||||||
|
{
|
||||||
|
name: '网页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '移动端',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Ipad',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '客户端',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '第三方',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '其它',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
radius: '60%',
|
||||||
|
splitNumber: 8,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 1,
|
||||||
|
shadowBlur: 0,
|
||||||
|
shadowColor: 'rgba(0,0,0,.2)',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 10,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
itemStyle: {
|
||||||
|
color: '#b6a2de',
|
||||||
|
},
|
||||||
|
name: '访问',
|
||||||
|
value: [90, 50, 86, 40, 50, 20],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemStyle: {
|
||||||
|
color: '#5ab1ef',
|
||||||
|
},
|
||||||
|
name: '趋势',
|
||||||
|
value: [70, 75, 70, 76, 20, 85],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
itemStyle: {
|
||||||
|
// borderColor: '#fff',
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
symbolSize: 0,
|
||||||
|
type: 'radar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EchartsUI ref="chartRef" />
|
||||||
|
</template>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EchartsUI,
|
||||||
|
type EchartsUIType,
|
||||||
|
useEcharts,
|
||||||
|
} from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
renderEcharts({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
animationDelay() {
|
||||||
|
return Math.random() * 400;
|
||||||
|
},
|
||||||
|
animationEasing: 'exponentialInOut',
|
||||||
|
animationType: 'scale',
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
|
||||||
|
data: [
|
||||||
|
{ name: '外包', value: 500 },
|
||||||
|
{ name: '定制', value: 310 },
|
||||||
|
{ name: '技术支持', value: 274 },
|
||||||
|
{ name: '远程', value: 400 },
|
||||||
|
].sort((a, b) => {
|
||||||
|
return a.value - b.value;
|
||||||
|
}),
|
||||||
|
name: '商业占比',
|
||||||
|
radius: '80%',
|
||||||
|
roseType: 'radius',
|
||||||
|
type: 'pie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EchartsUI ref="chartRef" />
|
||||||
|
</template>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EchartsUI,
|
||||||
|
type EchartsUIType,
|
||||||
|
useEcharts,
|
||||||
|
} from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
renderEcharts({
|
||||||
|
legend: {
|
||||||
|
bottom: '2%',
|
||||||
|
left: 'center',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
animationDelay() {
|
||||||
|
return Math.random() * 100;
|
||||||
|
},
|
||||||
|
animationEasing: 'exponentialInOut',
|
||||||
|
animationType: 'scale',
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
|
||||||
|
data: [
|
||||||
|
{ name: '搜索引擎', value: 1048 },
|
||||||
|
{ name: '直接访问', value: 735 },
|
||||||
|
{ name: '邮件营销', value: 580 },
|
||||||
|
{ name: '联盟广告', value: 484 },
|
||||||
|
],
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
fontSize: '12',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
// borderColor: '#fff',
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
position: 'center',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
name: '访问来源',
|
||||||
|
radius: ['40%', '65%'],
|
||||||
|
type: 'pie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EchartsUI ref="chartRef" />
|
||||||
|
</template>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EchartsUI,
|
||||||
|
type EchartsUIType,
|
||||||
|
useEcharts,
|
||||||
|
} from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts } = useEcharts(chartRef);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
renderEcharts({
|
||||||
|
grid: {
|
||||||
|
bottom: 0,
|
||||||
|
containLabel: true,
|
||||||
|
left: '1%',
|
||||||
|
right: '1%',
|
||||||
|
top: '2 %',
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
barMaxWidth: 80,
|
||||||
|
// color: '#4f69fd',
|
||||||
|
data: [
|
||||||
|
3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000,
|
||||||
|
3200, 4800,
|
||||||
|
],
|
||||||
|
type: 'bar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
axisPointer: {
|
||||||
|
lineStyle: {
|
||||||
|
// color: '#4f69fd',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
trigger: 'axis',
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: Array.from({ length: 12 }).map((_item, index) => `${index + 1}月`),
|
||||||
|
type: 'category',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
max: 8000,
|
||||||
|
splitNumber: 4,
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<EchartsUI ref="chartRef" />
|
||||||
|
</template>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { AnalysisOverviewItem } from '@vben/common-ui';
|
||||||
|
import type { TabOption } from '@vben/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AnalysisChartCard,
|
||||||
|
AnalysisChartsTabs,
|
||||||
|
AnalysisOverview,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
import {
|
||||||
|
SvgBellIcon,
|
||||||
|
SvgCakeIcon,
|
||||||
|
SvgCardIcon,
|
||||||
|
SvgDownloadIcon,
|
||||||
|
} from '@vben/icons';
|
||||||
|
|
||||||
|
import AnalyticsTrends from './analytics-trends.vue';
|
||||||
|
import AnalyticsVisits from './analytics-visits.vue';
|
||||||
|
import AnalyticsVisitsData from './analytics-visits-data.vue';
|
||||||
|
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
|
||||||
|
import AnalyticsVisitsSource from './analytics-visits-source.vue';
|
||||||
|
|
||||||
|
const overviewItems: AnalysisOverviewItem[] = [
|
||||||
|
{
|
||||||
|
icon: SvgCardIcon,
|
||||||
|
title: '用户量',
|
||||||
|
totalTitle: '总用户量',
|
||||||
|
totalValue: 120_000,
|
||||||
|
value: 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: SvgCakeIcon,
|
||||||
|
title: '访问量',
|
||||||
|
totalTitle: '总访问量',
|
||||||
|
totalValue: 500_000,
|
||||||
|
value: 20_000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: SvgDownloadIcon,
|
||||||
|
title: '下载量',
|
||||||
|
totalTitle: '总下载量',
|
||||||
|
totalValue: 120_000,
|
||||||
|
value: 8000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: SvgBellIcon,
|
||||||
|
title: '使用量',
|
||||||
|
totalTitle: '总使用量',
|
||||||
|
totalValue: 50_000,
|
||||||
|
value: 5000,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const chartTabs: TabOption[] = [
|
||||||
|
{
|
||||||
|
label: '流量趋势',
|
||||||
|
value: 'trends',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '月访问量',
|
||||||
|
value: 'visits',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-5">
|
||||||
|
<AnalysisOverview :items="overviewItems" />
|
||||||
|
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
|
||||||
|
<template #trends>
|
||||||
|
<AnalyticsTrends />
|
||||||
|
</template>
|
||||||
|
<template #visits>
|
||||||
|
<AnalyticsVisits />
|
||||||
|
</template>
|
||||||
|
</AnalysisChartsTabs>
|
||||||
|
|
||||||
|
<div class="mt-5 w-full md:flex">
|
||||||
|
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量">
|
||||||
|
<AnalyticsVisitsData />
|
||||||
|
</AnalysisChartCard>
|
||||||
|
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问来源">
|
||||||
|
<AnalyticsVisitsSource />
|
||||||
|
</AnalysisChartCard>
|
||||||
|
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源">
|
||||||
|
<AnalyticsVisitsSales />
|
||||||
|
</AnalysisChartCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,229 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type {
|
||||||
|
WorkbenchProjectItem,
|
||||||
|
WorkbenchQuickNavItem,
|
||||||
|
WorkbenchTodoItem,
|
||||||
|
WorkbenchTrendItem,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AnalysisChartCard,
|
||||||
|
WorkbenchHeader,
|
||||||
|
WorkbenchProject,
|
||||||
|
WorkbenchQuickNav,
|
||||||
|
WorkbenchTodo,
|
||||||
|
WorkbenchTrends,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
import { preferences } from '@vben/preferences';
|
||||||
|
import { useUserStore } from '@vben/stores';
|
||||||
|
|
||||||
|
import AnalyticsVisitsSource from '../analytics/analytics-visits-source.vue';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const projectItems: WorkbenchProjectItem[] = [
|
||||||
|
{
|
||||||
|
color: '',
|
||||||
|
content: '不要等待机会,而要创造机会。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: '开源组',
|
||||||
|
icon: 'carbon:logo-github',
|
||||||
|
title: 'Github',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#3fb27f',
|
||||||
|
content: '现在的你决定将来的你。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: '算法组',
|
||||||
|
icon: 'ion:logo-vue',
|
||||||
|
title: 'Vue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#e18525',
|
||||||
|
content: '没有什么才能比努力更重要。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: '上班摸鱼',
|
||||||
|
icon: 'ion:logo-html5',
|
||||||
|
title: 'Html5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#bf0c2c',
|
||||||
|
content: '热情和欲望可以突破一切难关。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: 'UI',
|
||||||
|
icon: 'ion:logo-angular',
|
||||||
|
title: 'Angular',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#00d8ff',
|
||||||
|
content: '健康的身体是实现目标的基石。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: '技术牛',
|
||||||
|
icon: 'bx:bxl-react',
|
||||||
|
title: 'React',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#EBD94E',
|
||||||
|
content: '路是走出来的,而不是空想出来的。',
|
||||||
|
date: '2021-04-01',
|
||||||
|
group: '架构组',
|
||||||
|
icon: 'ion:logo-javascript',
|
||||||
|
title: 'Js',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const quickNavItems: WorkbenchQuickNavItem[] = [
|
||||||
|
{
|
||||||
|
color: '#1fdaca',
|
||||||
|
icon: 'ion:home-outline',
|
||||||
|
title: '首页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#bf0c2c',
|
||||||
|
icon: 'ion:grid-outline',
|
||||||
|
title: '仪表盘',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#e18525',
|
||||||
|
icon: 'ion:layers-outline',
|
||||||
|
title: '组件',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#3fb27f',
|
||||||
|
icon: 'ion:settings-outline',
|
||||||
|
title: '系统管理',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#4daf1bc9',
|
||||||
|
icon: 'ion:key-outline',
|
||||||
|
title: '权限管理',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: '#00d8ff',
|
||||||
|
icon: 'ion:bar-chart-outline',
|
||||||
|
title: '图表',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const todoItems = ref<WorkbenchTodoItem[]>([
|
||||||
|
{
|
||||||
|
completed: false,
|
||||||
|
content: `审查最近提交到Git仓库的前端代码,确保代码质量和规范。`,
|
||||||
|
date: '2024-07-30 11:00:00',
|
||||||
|
title: '审查前端代码提交',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
completed: true,
|
||||||
|
content: `检查并优化系统性能,降低CPU使用率。`,
|
||||||
|
date: '2024-07-30 11:00:00',
|
||||||
|
title: '系统性能优化',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
completed: false,
|
||||||
|
content: `进行系统安全检查,确保没有安全漏洞或未授权的访问。 `,
|
||||||
|
date: '2024-07-30 11:00:00',
|
||||||
|
title: '安全检查',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
completed: false,
|
||||||
|
content: `更新项目中的所有npm依赖包,确保使用最新版本。`,
|
||||||
|
date: '2024-07-30 11:00:00',
|
||||||
|
title: '更新项目依赖',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
completed: false,
|
||||||
|
content: `修复用户报告的页面UI显示问题,确保在不同浏览器中显示一致。 `,
|
||||||
|
date: '2024-07-30 11:00:00',
|
||||||
|
title: '修复UI显示问题',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const trendItems: WorkbenchTrendItem[] = [
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-1',
|
||||||
|
content: `在 <a>开源组</a> 创建了项目 <a>Vue</a>`,
|
||||||
|
date: '刚刚',
|
||||||
|
title: '威廉',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-2',
|
||||||
|
content: `关注了 <a>威廉</a> `,
|
||||||
|
date: '1个小时前',
|
||||||
|
title: '艾文',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-3',
|
||||||
|
content: `发布了 <a>个人动态</a> `,
|
||||||
|
date: '1天前',
|
||||||
|
title: '克里斯',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-4',
|
||||||
|
content: `发表文章 <a>如何编写一个Vite插件</a> `,
|
||||||
|
date: '2天前',
|
||||||
|
title: 'Vben',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-1',
|
||||||
|
content: `回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>`,
|
||||||
|
date: '3天前',
|
||||||
|
title: '皮特',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-2',
|
||||||
|
content: `关闭了问题 <a>如何运行项目</a> `,
|
||||||
|
date: '1周前',
|
||||||
|
title: '杰克',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-3',
|
||||||
|
content: `发布了 <a>个人动态</a> `,
|
||||||
|
date: '1周前',
|
||||||
|
title: '威廉',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-4',
|
||||||
|
content: `推送了代码到 <a>Github</a>`,
|
||||||
|
date: '2021-04-01 20:00',
|
||||||
|
title: '威廉',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'svg:avatar-4',
|
||||||
|
content: `发表文章 <a>如何编写使用 Admin Vben</a> `,
|
||||||
|
date: '2021-03-01 20:00',
|
||||||
|
title: 'Vben',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-5">
|
||||||
|
<WorkbenchHeader
|
||||||
|
:avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
|
||||||
|
</template>
|
||||||
|
<template #description> 今日晴,20℃ - 32℃! </template>
|
||||||
|
</WorkbenchHeader>
|
||||||
|
|
||||||
|
<div class="mt-5 flex flex-col lg:flex-row">
|
||||||
|
<div class="mr-4 w-full lg:w-3/5">
|
||||||
|
<WorkbenchProject :items="projectItems" title="项目" />
|
||||||
|
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full lg:w-2/5">
|
||||||
|
<WorkbenchQuickNav
|
||||||
|
:items="quickNavItems"
|
||||||
|
class="mt-5 lg:mt-0"
|
||||||
|
title="快捷导航"
|
||||||
|
/>
|
||||||
|
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
||||||
|
<AnalysisChartCard class="mt-5" title="访问来源">
|
||||||
|
<AnalyticsVisitsSource />
|
||||||
|
</AnalysisChartCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { Button, Card, message, notification, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
|
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
||||||
|
|
||||||
|
function info() {
|
||||||
|
message.info('How many roads must a man walk down');
|
||||||
|
}
|
||||||
|
|
||||||
|
function error() {
|
||||||
|
message.error({
|
||||||
|
content: 'Once upon a time you dressed so fine',
|
||||||
|
duration: 2500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function warning() {
|
||||||
|
message.warning('How many roads must a man walk down');
|
||||||
|
}
|
||||||
|
function success() {
|
||||||
|
message.success('Cause you walked hand in hand With another man in my place');
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(type: NotificationType) {
|
||||||
|
notification[type]({
|
||||||
|
duration: 2500,
|
||||||
|
message: '说点啥呢',
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page
|
||||||
|
description="支持多语言,主题功能集成切换等"
|
||||||
|
title="Ant Design Vue组件使用演示"
|
||||||
|
>
|
||||||
|
<Card class="mb-5" title="按钮">
|
||||||
|
<Space>
|
||||||
|
<Button>Default</Button>
|
||||||
|
<Button type="primary"> Primary </Button>
|
||||||
|
<Button> Info </Button>
|
||||||
|
<Button danger> Error </Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
<Card class="mb-5" title="Message">
|
||||||
|
<Space>
|
||||||
|
<Button @click="info"> 信息 </Button>
|
||||||
|
<Button danger @click="error"> 错误 </Button>
|
||||||
|
<Button @click="warning"> 警告 </Button>
|
||||||
|
<Button @click="success"> 成功 </Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card class="mb-5" title="Notification">
|
||||||
|
<Space>
|
||||||
|
<Button @click="notify('info')"> 信息 </Button>
|
||||||
|
<Button danger @click="notify('error')"> 错误 </Button>
|
||||||
|
<Button @click="notify('warning')"> 警告 </Button>
|
||||||
|
<Button @click="notify('success')"> 成功 </Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
</Page>
|
||||||
|
</template>
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from '@vben/tailwind-config';
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "@vben/tsconfig/web-app.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"#/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }],
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "@vben/tsconfig/node.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"noEmit": false
|
||||||
|
},
|
||||||
|
"include": ["vite.config.mts"]
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { defineConfig } from '@vben/vite-config';
|
||||||
|
|
||||||
|
export default defineConfig(async () => {
|
||||||
|
return {
|
||||||
|
application: {},
|
||||||
|
vite: {
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
// mock代理目标地址
|
||||||
|
target: 'http://localhost:5320/api',
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/commitlint-config",
|
"name": "@vben/commitlint-config",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
|
|
@ -27,29 +27,29 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-config-turbo": "^2.1.0",
|
"eslint-config-turbo": "^2.1.1",
|
||||||
"eslint-plugin-command": "^0.2.3",
|
"eslint-plugin-command": "^0.2.4",
|
||||||
"eslint-plugin-import-x": "^4.1.0"
|
"eslint-plugin-import-x": "^4.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.1",
|
"@eslint/js": "^9.10.0",
|
||||||
"@types/eslint": "^9.6.1",
|
"@types/eslint": "^9.6.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
||||||
"@typescript-eslint/parser": "^8.3.0",
|
"@typescript-eslint/parser": "^8.5.0",
|
||||||
"eslint": "^9.9.1",
|
"eslint": "^9.10.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||||
"eslint-plugin-jsdoc": "^50.2.2",
|
"eslint-plugin-jsdoc": "^50.2.2",
|
||||||
"eslint-plugin-jsonc": "^2.16.0",
|
"eslint-plugin-jsonc": "^2.16.0",
|
||||||
"eslint-plugin-n": "^17.10.2",
|
"eslint-plugin-n": "^17.10.2",
|
||||||
"eslint-plugin-no-only-tests": "^3.3.0",
|
"eslint-plugin-no-only-tests": "^3.3.0",
|
||||||
"eslint-plugin-perfectionist": "^3.3.0",
|
"eslint-plugin-perfectionist": "^3.5.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"eslint-plugin-regexp": "^2.6.0",
|
"eslint-plugin-regexp": "^2.6.0",
|
||||||
"eslint-plugin-unicorn": "^55.0.0",
|
"eslint-plugin-unicorn": "^55.0.0",
|
||||||
"eslint-plugin-unused-imports": "^4.1.3",
|
"eslint-plugin-unused-imports": "^4.1.3",
|
||||||
"eslint-plugin-vitest": "^0.5.4",
|
"eslint-plugin-vitest": "^0.5.4",
|
||||||
"eslint-plugin-vue": "^9.27.0",
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"jsonc-eslint-parser": "^2.4.0",
|
"jsonc-eslint-parser": "^2.4.0",
|
||||||
"vue-eslint-parser": "^9.4.3"
|
"vue-eslint-parser": "^9.4.3"
|
||||||
|
|
|
@ -68,7 +68,7 @@ export async function perfectionist(): Promise<Linter.Config[]> {
|
||||||
ignorePattern: ['children'],
|
ignorePattern: ['children'],
|
||||||
order: 'asc',
|
order: 'asc',
|
||||||
partitionByComment: 'Part:**',
|
partitionByComment: 'Part:**',
|
||||||
type: 'alphabetical',
|
type: 'natural',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'perfectionist/sort-vue-attributes': [
|
'perfectionist/sort-vue-attributes': [
|
||||||
|
|
|
@ -15,12 +15,14 @@ export async function unicorn(): Promise<Linter.Config[]> {
|
||||||
rules: {
|
rules: {
|
||||||
...pluginUnicorn.configs.recommended.rules,
|
...pluginUnicorn.configs.recommended.rules,
|
||||||
|
|
||||||
|
'unicorn/better-regex': 'off',
|
||||||
'unicorn/consistent-destructuring': 'off',
|
'unicorn/consistent-destructuring': 'off',
|
||||||
'unicorn/consistent-function-scoping': 'off',
|
'unicorn/consistent-function-scoping': 'off',
|
||||||
'unicorn/filename-case': 'off',
|
'unicorn/filename-case': 'off',
|
||||||
'unicorn/import-style': 'off',
|
'unicorn/import-style': 'off',
|
||||||
'unicorn/no-array-for-each': 'off',
|
'unicorn/no-array-for-each': 'off',
|
||||||
'unicorn/no-null': 'off',
|
'unicorn/no-null': 'off',
|
||||||
|
'unicorn/no-useless-undefined': 'off',
|
||||||
'unicorn/prefer-at': 'off',
|
'unicorn/prefer-at': 'off',
|
||||||
'unicorn/prefer-dom-node-text-content': 'off',
|
'unicorn/prefer-dom-node-text-content': 'off',
|
||||||
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }],
|
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/stylelint-config",
|
"name": "@vben/stylelint-config",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"stylelint-scss": "^6.5.1"
|
"stylelint-scss": "^6.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.45",
|
||||||
"postcss-html": "^1.7.0",
|
"postcss-html": "^1.7.0",
|
||||||
"postcss-scss": "^4.0.9",
|
"postcss-scss": "^4.0.9",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/node-utils",
|
"name": "@vben/node-utils",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@changesets/git": "^3.0.0",
|
"@changesets/git": "^3.0.1",
|
||||||
"@manypkg/get-packages": "^2.2.2",
|
"@manypkg/get-packages": "^2.2.2",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"consola": "^3.2.3",
|
"consola": "^3.2.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/tailwind-config",
|
"name": "@vben/tailwind-config",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -46,17 +46,17 @@
|
||||||
"tailwindcss": "^3.4.3"
|
"tailwindcss": "^3.4.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/json": "^2.2.242",
|
"@iconify/json": "^2.2.246",
|
||||||
"@iconify/tailwind": "^1.1.3",
|
"@iconify/tailwind": "^1.1.3",
|
||||||
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
|
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"cssnano": "^7.0.5",
|
"cssnano": "^7.0.6",
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.45",
|
||||||
"postcss-antd-fixes": "^0.2.0",
|
"postcss-antd-fixes": "^0.2.0",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-preset-env": "^10.0.2",
|
"postcss-preset-env": "^10.0.3",
|
||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.11",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -190,8 +190,8 @@ export default {
|
||||||
},
|
},
|
||||||
float: {
|
float: {
|
||||||
'0%': { transform: 'translateY(0)' },
|
'0%': { transform: 'translateY(0)' },
|
||||||
'100%': { transform: 'translateY(0)' },
|
|
||||||
'50%': { transform: 'translateY(-20px)' },
|
'50%': { transform: 'translateY(-20px)' },
|
||||||
|
'100%': { transform: 'translateY(0)' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zIndex: {
|
zIndex: {
|
||||||
|
@ -228,11 +228,11 @@ function createColorsPalette(name: string) {
|
||||||
// • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。
|
// • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
50: `hsl(var(--${name}-50))`,
|
||||||
100: `hsl(var(--${name}-100))`,
|
100: `hsl(var(--${name}-100))`,
|
||||||
200: `hsl(var(--${name}-200))`,
|
200: `hsl(var(--${name}-200))`,
|
||||||
300: `hsl(var(--${name}-300))`,
|
300: `hsl(var(--${name}-300))`,
|
||||||
400: `hsl(var(--${name}-400))`,
|
400: `hsl(var(--${name}-400))`,
|
||||||
50: `hsl(var(--${name}-50))`,
|
|
||||||
500: `hsl(var(--${name}-500))`,
|
500: `hsl(var(--${name}-500))`,
|
||||||
600: `hsl(var(--${name}-600))`,
|
600: `hsl(var(--${name}-600))`,
|
||||||
700: `hsl(var(--${name}-700))`,
|
700: `hsl(var(--${name}-700))`,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/tsconfig",
|
"name": "@vben/tsconfig",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -20,6 +20,6 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"vite": "^5.4.2"
|
"vite": "^5.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@vben/vite-config",
|
"name": "@vben/vite-config",
|
||||||
"version": "5.1.2",
|
"version": "5.3.0-beta.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@ -28,29 +28,31 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||||
"@jspm/generator": "^2.1.3",
|
"@jspm/generator": "^2.3.0",
|
||||||
|
"archiver": "^7.0.1",
|
||||||
"cheerio": "1.0.0",
|
"cheerio": "1.0.0",
|
||||||
"get-port": "^7.1.0",
|
"get-port": "^7.1.0",
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"nitropack": "^2.9.7",
|
"nitropack": "^2.9.7",
|
||||||
"resolve.exports": "^2.0.2",
|
"resolve.exports": "^2.0.2",
|
||||||
"vite-plugin-lib-inject-css": "^2.1.1",
|
"vite-plugin-lib-inject-css": "^2.1.1",
|
||||||
"vite-plugin-pwa": "^0.20.1",
|
"vite-plugin-pwa": "^0.20.5",
|
||||||
"vite-plugin-vue-devtools": "^7.3.9"
|
"vite-plugin-vue-devtools": "^7.4.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/archiver": "^6.0.2",
|
||||||
"@types/html-minifier-terser": "^7.0.2",
|
"@types/html-minifier-terser": "^7.0.2",
|
||||||
"@vben/node-utils": "workspace:*",
|
"@vben/node-utils": "workspace:*",
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"rollup": "^4.21.1",
|
"rollup": "^4.21.2",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.78.0",
|
||||||
"vite": "^5.4.2",
|
"vite": "^5.4.3",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-dts": "4.0.3",
|
"vite-plugin-dts": "4.2.1",
|
||||||
"vite-plugin-html": "^3.2.2"
|
"vite-plugin-html": "^3.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue