mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 00:26:20 +08:00
Compare commits
112 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
46540a7329 | ||
![]() |
13fd0ea16c | ||
![]() |
f7016466ee | ||
![]() |
0cd865e211 | ||
![]() |
64428b9b11 | ||
![]() |
aed31a5a4e | ||
![]() |
b3e196f001 | ||
![]() |
b2c117f544 | ||
![]() |
01391ee5a1 | ||
![]() |
3572ce1538 | ||
![]() |
d1e1256202 | ||
![]() |
b7776c5148 | ||
![]() |
2d1519eca7 | ||
![]() |
93b5618b52 | ||
![]() |
639d2e1525 | ||
![]() |
26646d42f7 | ||
![]() |
17fa8eb93b | ||
![]() |
8250894a50 | ||
![]() |
a72b8acaf9 | ||
![]() |
a46c85d77d | ||
![]() |
fdc5b02c30 | ||
![]() |
476aa068d7 | ||
![]() |
bb6057cac3 | ||
![]() |
abbbbfb955 | ||
![]() |
79c87c9f46 | ||
![]() |
f815dcf3ae | ||
![]() |
1197efea26 | ||
![]() |
2a83f1d666 | ||
![]() |
4b3d2d21ed | ||
![]() |
31f6cc6416 | ||
![]() |
5ce3a18785 | ||
![]() |
dac80703d9 | ||
![]() |
000172e482 | ||
![]() |
dbe5b33db6 | ||
![]() |
81a9accafd | ||
![]() |
bc625ee710 | ||
![]() |
d34f1fbf2f | ||
![]() |
60cffb0dec | ||
![]() |
68dbe04bef | ||
![]() |
37645f08be | ||
![]() |
ace942e2df | ||
![]() |
f9359ed0f9 | ||
![]() |
b12ff2d766 | ||
![]() |
c72747c649 | ||
![]() |
fbd23701de | ||
![]() |
a634ec3692 | ||
![]() |
df48409814 | ||
![]() |
4765158510 | ||
![]() |
161820dbc1 | ||
![]() |
56bdb8f606 | ||
![]() |
f25783933d | ||
![]() |
d574fb8b6d | ||
![]() |
0c73cf8d3f | ||
![]() |
834cb4c470 | ||
![]() |
2c22825546 | ||
![]() |
26d43ef822 | ||
![]() |
bfae9626dd | ||
![]() |
02c4014ae3 | ||
![]() |
c3d0102cda | ||
![]() |
38fe6426a2 | ||
![]() |
b8a4fba78c | ||
![]() |
d99cad35d7 | ||
![]() |
5ba3a9dec2 | ||
![]() |
10b90eae5d | ||
![]() |
29f572abd3 | ||
![]() |
d27e5eeef7 | ||
![]() |
bd6b724aaf | ||
![]() |
7bcb973d65 | ||
![]() |
9e88b8004f | ||
![]() |
76a879d4d8 | ||
![]() |
537a4b0ecb | ||
![]() |
855ac02622 | ||
![]() |
61faa1895a | ||
![]() |
8f6bf6add3 | ||
![]() |
ceb52aad7f | ||
![]() |
66c732fdee | ||
![]() |
6e67fb5fe7 | ||
![]() |
3697f6bc5a | ||
![]() |
978edb1e02 | ||
![]() |
b417ac2469 | ||
![]() |
524b9badf2 | ||
![]() |
86ed732ca8 | ||
![]() |
56e66193fc | ||
![]() |
b1636405fc | ||
![]() |
ad89ea7a75 | ||
![]() |
7b3c9d56be | ||
![]() |
1cff0b9753 | ||
![]() |
31d5f03b45 | ||
![]() |
2b65e935c1 | ||
![]() |
58f2b17bde | ||
![]() |
46a9fac38e | ||
![]() |
41612f7723 | ||
![]() |
83ecae7c4e | ||
![]() |
3332b20fd0 | ||
![]() |
2d0153a841 | ||
![]() |
95a4a85c3b | ||
![]() |
3f2dcb8281 | ||
![]() |
67f3d63066 | ||
![]() |
277e98c42c | ||
![]() |
1063b2268e | ||
![]() |
8404c12129 | ||
![]() |
a7d322019e | ||
![]() |
f23821ff46 | ||
![]() |
2b0aedbaba | ||
![]() |
071cc0dcec | ||
![]() |
7db7d6ec5f | ||
![]() |
b3e3e05990 | ||
![]() |
cc678a2b51 | ||
![]() |
7d2bcf476f | ||
![]() |
cfbe379ee4 | ||
![]() |
388e5b5cb3 | ||
![]() |
c1dfbc1ebf |
@@ -3,3 +3,5 @@ node_modules
|
|||||||
.gitignore
|
.gitignore
|
||||||
*.md
|
*.md
|
||||||
dist
|
dist
|
||||||
|
.turbo
|
||||||
|
dist.zip
|
||||||
|
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: 🐞 Bug Report
|
name: 🐞 Bug Report
|
||||||
description: Report an issue with Vben Admin to help us make it better.
|
description: Report an issue with Vben Admin to help us make it better.
|
||||||
title: "Bug: "
|
title: 'Bug: '
|
||||||
labels: ["bug: pending triage"]
|
labels: ['bug: pending triage']
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
2
.github/ISSUE_TEMPLATE/docs.yml
vendored
2
.github/ISSUE_TEMPLATE/docs.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: 📚 Documentation
|
name: 📚 Documentation
|
||||||
description: Report an issue with Vben Admin Website to help us make it better.
|
description: Report an issue with Vben Admin Website to help us make it better.
|
||||||
title: "Docs: "
|
title: 'Docs: '
|
||||||
labels: [documentation]
|
labels: [documentation]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: ✨ New Feature Proposal
|
name: ✨ New Feature Proposal
|
||||||
description: Propose a new feature to be added to Vben Admin
|
description: Propose a new feature to be added to Vben Admin
|
||||||
title: "FEATURE: "
|
title: 'FEATURE: '
|
||||||
labels: ["enhancement: pending triage"]
|
labels: ['enhancement: pending triage']
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
8
.github/actions/setup-node/action.yml
vendored
8
.github/actions/setup-node/action.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: "Setup Node"
|
name: 'Setup Node'
|
||||||
|
|
||||||
description: "Setup node and pnpm"
|
description: 'Setup node and pnpm'
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
@@ -12,7 +12,7 @@ runs:
|
|||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: .node-version
|
node-version-file: .node-version
|
||||||
cache: "pnpm"
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
groups:
|
groups:
|
||||||
@@ -9,7 +9,7 @@ updates:
|
|||||||
update-types: [minor, patch]
|
update-types: [minor, patch]
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
groups:
|
groups:
|
||||||
|
16
.github/labeler.yml
vendored
16
.github/labeler.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
# Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name
|
|
||||||
feature:
|
|
||||||
- head-branch: ["^feat", "feat"]
|
|
||||||
|
|
||||||
bug:
|
|
||||||
- head-branch: ["^fix", "fix"]
|
|
||||||
|
|
||||||
chore:
|
|
||||||
- head-branch: ["^chore", "chore"]
|
|
||||||
|
|
||||||
perf:
|
|
||||||
- head-branch: ["^perf", "perf"]
|
|
||||||
|
|
||||||
documentation:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file: ["**/*.md", "docs/**"]
|
|
64
.github/release-drafter.yml
vendored
64
.github/release-drafter.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name-template: "v$RESOLVED_VERSION"
|
name-template: 'v$RESOLVED_VERSION'
|
||||||
tag-template: "v$RESOLVED_VERSION"
|
tag-template: 'v$RESOLVED_VERSION'
|
||||||
version-template: $MAJOR.$MINOR.$PATCH
|
version-template: $MAJOR.$MINOR.$PATCH
|
||||||
change-template: "* $TITLE (#$NUMBER) @$AUTHOR"
|
change-template: '* $TITLE (#$NUMBER) @$AUTHOR'
|
||||||
template: |
|
template: |
|
||||||
# What's Changed
|
# What's Changed
|
||||||
|
|
||||||
@@ -10,52 +10,52 @@ template: |
|
|||||||
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
||||||
|
|
||||||
categories:
|
categories:
|
||||||
- title: "🚀 Features"
|
- title: '🚀 Features'
|
||||||
labels:
|
labels:
|
||||||
- "feature"
|
- 'feature'
|
||||||
- title: "🐞 Bug Fixes"
|
- title: '🐞 Bug Fixes'
|
||||||
labels:
|
labels:
|
||||||
- "bug"
|
- 'bug'
|
||||||
- title: "📈 Performance"
|
- title: '📈 Performance'
|
||||||
labels:
|
labels:
|
||||||
- "perf"
|
- 'perf'
|
||||||
- "enhancement"
|
- 'enhancement'
|
||||||
- title: 📝 Documentation
|
- title: 📝 Documentation
|
||||||
labels:
|
labels:
|
||||||
- "documentation"
|
- 'documentation'
|
||||||
- title: 👻 Maintenance
|
- title: 👻 Maintenance
|
||||||
labels:
|
labels:
|
||||||
- "chore"
|
- 'chore'
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
# collapse-after: 12
|
# collapse-after: 12
|
||||||
- title: 🚦 Tests
|
- title: 🚦 Tests
|
||||||
labels:
|
labels:
|
||||||
- "tests"
|
- 'tests'
|
||||||
- title: "Breaking"
|
- title: 'Breaking'
|
||||||
label: "breaking"
|
label: 'breaking'
|
||||||
|
|
||||||
version-resolver:
|
version-resolver:
|
||||||
major:
|
major:
|
||||||
labels:
|
labels:
|
||||||
- "major"
|
- 'major'
|
||||||
- "breaking"
|
- 'breaking'
|
||||||
minor:
|
minor:
|
||||||
labels:
|
labels:
|
||||||
- "minor"
|
- 'minor'
|
||||||
patch:
|
patch:
|
||||||
labels:
|
labels:
|
||||||
- "feature"
|
- 'feature'
|
||||||
- "patch"
|
- 'patch'
|
||||||
- "bug"
|
- 'bug'
|
||||||
- "maintenance"
|
- 'maintenance'
|
||||||
- "docs"
|
- 'docs'
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
- "security"
|
- 'security'
|
||||||
|
|
||||||
exclude-labels:
|
exclude-labels:
|
||||||
- "skip-changelog"
|
- 'skip-changelog'
|
||||||
- "no-changelog"
|
- 'no-changelog'
|
||||||
- "changelog"
|
- 'changelog'
|
||||||
- "bump versions"
|
- 'bump versions'
|
||||||
- "reverted"
|
- 'reverted'
|
||||||
- "invalid"
|
- 'invalid'
|
||||||
|
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
HUSKY: "0"
|
HUSKY: '0'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
@@ -19,8 +19,15 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
post-update:
|
post-update:
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
# if: ${{ github.actor == 'dependabot[bot]' }}
|
# if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
# - macos-latest
|
||||||
|
- windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
6
.github/workflows/changeset-version.yml
vendored
6
.github/workflows/changeset-version.yml
vendored
@@ -18,7 +18,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
version:
|
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.event.pull_request.merged || github.event_name == 'workflow_dispatch') && github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
# if: github.repository == 'vbenjs/vue-vben-admin'
|
# if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
uses: changesets/action@v1
|
uses: changesets/action@v1
|
||||||
with:
|
with:
|
||||||
version: pnpm run version
|
version: pnpm run version
|
||||||
commit: "chore: bump versions"
|
commit: 'chore: bump versions'
|
||||||
title: "chore: bump versions"
|
title: 'chore: bump versions'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -5,7 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "releases/*"
|
- 'releases/*'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -17,7 +17,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -79,6 +79,7 @@ jobs:
|
|||||||
|
|
||||||
check:
|
check:
|
||||||
name: Check
|
name: Check
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
strategy:
|
strategy:
|
||||||
@@ -108,8 +109,8 @@ jobs:
|
|||||||
|
|
||||||
ci-ok:
|
ci-ok:
|
||||||
name: CI OK
|
name: CI OK
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && always()
|
|
||||||
needs: [test, check, lint]
|
needs: [test, check, lint]
|
||||||
env:
|
env:
|
||||||
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
|
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
|
||||||
|
11
.github/workflows/codeql.yml
vendored
11
.github/workflows/codeql.yml
vendored
@@ -9,19 +9,20 @@
|
|||||||
# the `language` matrix defined below to confirm you have the correct set of
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: "CodeQL"
|
name: 'CodeQL'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ['main']
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ['main']
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "35 0 * * 0"
|
- cron: '35 0 * * 0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze (${{ matrix.language }})
|
name: Analyze (${{ matrix.language }})
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
# - https://gh.io/supported-runners-and-hardware-resources
|
||||||
@@ -90,4 +91,4 @@ jobs:
|
|||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: '/language:${{matrix.language}}'
|
||||||
|
10
.github/workflows/deploy.yml
vendored
10
.github/workflows/deploy.yml
vendored
@@ -8,7 +8,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
deploy-playground-ftp:
|
deploy-playground-ftp:
|
||||||
name: Deploy Push Playground Ftp
|
name: Deploy Push Playground Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-docs-ftp:
|
deploy-docs-ftp:
|
||||||
name: Deploy Push Docs Ftp
|
name: Deploy Push Docs Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-antd-ftp:
|
deploy-antd-ftp:
|
||||||
name: Deploy Push Antd Ftp
|
name: Deploy Push Antd Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-ele-ftp:
|
deploy-ele-ftp:
|
||||||
name: Deploy Push Element Ftp
|
name: Deploy Push Element Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -125,7 +125,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-naive-ftp:
|
deploy-naive-ftp:
|
||||||
name: Deploy Push Naive Ftp
|
name: Deploy Push Naive Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
1
.github/workflows/draft.yml
vendored
1
.github/workflows/draft.yml
vendored
@@ -17,6 +17,7 @@ jobs:
|
|||||||
# write permission is required for autolabeler
|
# write permission is required for autolabeler
|
||||||
# otherwise, read permission is required at least
|
# otherwise, read permission is required at least
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: release-drafter/release-drafter@v6
|
- uses: release-drafter/release-drafter@v6
|
||||||
|
22
.github/workflows/issue-close-require.yml
vendored
22
.github/workflows/issue-close-require.yml
vendored
@@ -3,23 +3,29 @@ name: Issue Close Require
|
|||||||
|
|
||||||
# 触发条件:每天零点
|
# 触发条件:每天零点
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
contents: write
|
contents: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
close-issues:
|
close-issues:
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# 步骤1:关闭未活动的 Issues
|
# 关闭未活动的 Issues
|
||||||
- name: Close Inactive Issues
|
- name: Close Inactive Issues
|
||||||
uses: actions-cool/issues-helper@v3
|
uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
actions: "close-issues" # 执行动作:关闭 Issues
|
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||||
token: ${{ secrets.GITHUB_TOKEN }} # GitHub Token,用于认证
|
stale-issue-label: needs-reproduction # Label that flags an issue as stale.
|
||||||
labels: "needs reproduction" # 目标标签
|
only-labels: needs-reproduction # Only process these issues
|
||||||
inactive-day: 3 # 未活动天数阈值
|
days-before-issue-close: 3
|
||||||
|
ignore-updates: true
|
||||||
|
remove-stale-when-updated: false
|
||||||
|
close-issue-message: This issue was closed because it was open for 3 days without a valid reproduction.
|
||||||
|
close-issue-label: closed-by-action
|
||||||
|
13
.github/workflows/issue-labeled.yml
vendored
13
.github/workflows/issue-labeled.yml
vendored
@@ -13,33 +13,34 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
reply-labeled:
|
reply-labeled:
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: remove enhancement pending
|
- name: remove enhancement pending
|
||||||
if: github.event.label.name == 'enhancement'
|
if: github.event.label.name == 'enhancement'
|
||||||
uses: actions-cool/issues-helper@v3
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: "remove-labels"
|
actions: 'remove-labels'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
labels: "enhancement: pending triage"
|
labels: 'enhancement: pending triage'
|
||||||
|
|
||||||
- name: remove bug pending
|
- name: remove bug pending
|
||||||
if: github.event.label.name == 'bug'
|
if: github.event.label.name == 'bug'
|
||||||
uses: actions-cool/issues-helper@v3
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: "remove-labels"
|
actions: 'remove-labels'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
labels: "bug: pending triage"
|
labels: 'bug: pending triage'
|
||||||
|
|
||||||
- name: needs reproduction
|
- name: needs reproduction
|
||||||
if: github.event.label.name == 'needs reproduction'
|
if: github.event.label.name == 'needs reproduction'
|
||||||
uses: actions-cool/issues-helper@v3
|
uses: actions-cool/issues-helper@v3
|
||||||
with:
|
with:
|
||||||
actions: "create-comment, remove-labels"
|
actions: 'create-comment, remove-labels'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
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.
|
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"
|
labels: 'bug: pending triage'
|
||||||
|
22
.github/workflows/labeler.yml
vendored
22
.github/workflows/labeler.yml
vendored
@@ -1,22 +0,0 @@
|
|||||||
name: PR Labeler
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, edited, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
label:
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Label PR based on title or file changes
|
|
||||||
uses: actions/labeler@v5
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
configuration-path: .github/labeler.yml
|
|
13
.github/workflows/lock.yml
vendored
13
.github/workflows/lock.yml
vendored
@@ -2,7 +2,7 @@ name: Lock Threads
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: '0 0 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -11,13 +11,14 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
action:
|
action:
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v5
|
- uses: dessant/lock-threads@v5
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-inactive-days: "30"
|
issue-inactive-days: '14'
|
||||||
issue-lock-reason: ""
|
issue-lock-reason: ''
|
||||||
pr-inactive-days: "30"
|
pr-inactive-days: '30'
|
||||||
pr-lock-reason: ""
|
pr-lock-reason: ''
|
||||||
process-only: "issues, prs"
|
process-only: 'issues, prs'
|
||||||
|
5
.github/workflows/release-tag.yml
vendored
5
.github/workflows/release-tag.yml
vendored
@@ -3,10 +3,10 @@ name: Create Release Tag
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*.*.*" # Push events to matching v*, i.e. v1.0, v20.15.10
|
- 'v*.*.*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||||
|
|
||||||
env:
|
env:
|
||||||
HUSKY: "0"
|
HUSKY: '0'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -15,6 +15,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
3
.github/workflows/semantic-pull-request.yml
vendored
3
.github/workflows/semantic-pull-request.yml
vendored
@@ -9,8 +9,9 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Semantic Pull Request
|
name: Semantic Pull Request
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Validate PR title
|
- name: Validate PR title
|
||||||
uses: amannn/action-semantic-pull-request@v5
|
uses: amannn/action-semantic-pull-request@v5
|
||||||
|
11
.github/workflows/stale.yml
vendored
11
.github/workflows/stale.yml
vendored
@@ -1,18 +1,19 @@
|
|||||||
name: "Close stale issues"
|
name: 'Close stale issues'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 1 * * *"
|
- cron: '0 1 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
|
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
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-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"
|
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"
|
exempt-issue-labels: 'bug,enhancement'
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
days-before-close: 7
|
days-before-close: 7
|
||||||
|
@@ -3,4 +3,4 @@ ports:
|
|||||||
onOpen: open-preview
|
onOpen: open-preview
|
||||||
tasks:
|
tasks:
|
||||||
- init: corepack enable && pnpm install
|
- init: corepack enable && pnpm install
|
||||||
command: pnpm run dev
|
command: pnpm run dev:play
|
||||||
|
28
.ls-lint.yml
28
.ls-lint.yml
@@ -1,28 +0,0 @@
|
|||||||
ls:
|
|
||||||
.js: kebab-case | pointcase
|
|
||||||
.vue: kebab-case | pointcase
|
|
||||||
.ts: kebab-case | pointcase
|
|
||||||
.tsx: kebab-case | pointcase
|
|
||||||
.jsx: kebab-case | pointcase
|
|
||||||
.css: kebab-case | pointcase
|
|
||||||
.d.ts: kebab-case | pointcase
|
|
||||||
# shadcn 自动生成文件为 PascalCase 格式
|
|
||||||
packages/@core/ui-kit/shadcn-ui/src/components/ui:
|
|
||||||
.vue: PascalCase
|
|
||||||
|
|
||||||
ignore:
|
|
||||||
- "**/*.png"
|
|
||||||
- "**/*.jpg"
|
|
||||||
- "**/*.jpeg"
|
|
||||||
- "**/*.jpeg"
|
|
||||||
- "**/*.gif"
|
|
||||||
- "**/_util.ts"
|
|
||||||
- "**/deps/**"
|
|
||||||
- "**/dist/**"
|
|
||||||
- "**/node_modules/**"
|
|
||||||
- "**/.turbo/**"
|
|
||||||
- .git
|
|
||||||
- .vscode
|
|
||||||
- .idea
|
|
||||||
- node_modules
|
|
||||||
- .cache
|
|
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -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}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -212,8 +212,7 @@
|
|||||||
"*.env": "$(capture).env.*",
|
"*.env": "$(capture).env.*",
|
||||||
"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",
|
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
|
||||||
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,.ls-lint*,cspell.json",
|
|
||||||
"tailwind.config.mjs": "postcss.*"
|
"tailwind.config.mjs": "postcss.*"
|
||||||
},
|
},
|
||||||
"commentTranslate.hover.enabled": false,
|
"commentTranslate.hover.enabled": false,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp"> </a> <br> <br>
|
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -125,11 +125,15 @@ pnpm build
|
|||||||
|
|
||||||
[@Vben](https://github.com/anncwb)
|
[@Vben](https://github.com/anncwb)
|
||||||
|
|
||||||
|
## スター歴史
|
||||||
|
|
||||||
|
[](https://star-history.com/#vbenjs/vue-vben-admin&Date)
|
||||||
|
|
||||||
## 寄付
|
## 寄付
|
||||||
|
|
||||||
このプロジェクトが役に立つと思われた場合、作者にコーヒーを一杯おごってサポートを示すことができます!
|
このプロジェクトが役に立つと思われた場合、作者にコーヒーを一杯おごってサポートを示すことができます!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp"> </a> <br> <br>
|
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -124,11 +124,15 @@ Support modern browsers, not IE
|
|||||||
|
|
||||||
[@Vben](https://github.com/anncwb)
|
[@Vben](https://github.com/anncwb)
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#vbenjs/vue-vben-admin&Date)
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
|
|
||||||
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
|
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp"> </a> <br> <br>
|
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -77,6 +77,10 @@ pnpm dev
|
|||||||
pnpm build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
||||||
|
|
||||||
## 如何贡献
|
## 如何贡献
|
||||||
|
|
||||||
非常欢迎你的加入 或者提交一个 Pull Request。
|
非常欢迎你的加入 或者提交一个 Pull Request。
|
||||||
@@ -120,18 +124,18 @@ pnpm build
|
|||||||
|
|
||||||
[@Vben](https://github.com/anncwb)
|
[@Vben](https://github.com/anncwb)
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#vbenjs/vue-vben-admin&Date)
|
||||||
|
|
||||||
## 捐赠
|
## 捐赠
|
||||||
|
|
||||||
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
## 更新日志
|
|
||||||
|
|
||||||
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
|
||||||
|
|
||||||
## Contributor
|
## Contributor
|
||||||
|
|
||||||
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
|
@@ -10,11 +10,11 @@
|
|||||||
"start": "nitro dev"
|
"start": "nitro dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "catalog:",
|
||||||
"nitropack": "^2.9.7"
|
"nitropack": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "catalog:",
|
||||||
"h3": "^1.12.0"
|
"h3": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-antd",
|
"name": "@vben/web-antd",
|
||||||
"version": "5.2.0",
|
"version": "5.3.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,11 +40,11 @@
|
|||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^11.0.3",
|
"@vueuse/core": "catalog:",
|
||||||
"ant-design-vue": "^4.2.3",
|
"ant-design-vue": "catalog:",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "catalog:",
|
||||||
"pinia": "2.2.2",
|
"pinia": "catalog:",
|
||||||
"vue": "^3.4.38",
|
"vue": "catalog:",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
138
apps/web-antd/src/adapter/form.ts
Normal file
138
apps/web-antd/src/adapter/form.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import type {
|
||||||
|
BaseFormComponentType,
|
||||||
|
VbenFormSchema as FormSchema,
|
||||||
|
VbenFormProps,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import type { Component, SetupContext } from 'vue';
|
||||||
|
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,
|
||||||
|
Textarea,
|
||||||
|
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'
|
||||||
|
| 'Textarea'
|
||||||
|
| 'TimePicker'
|
||||||
|
| 'TreeSelect'
|
||||||
|
| 'Upload'
|
||||||
|
| BaseFormComponentType;
|
||||||
|
|
||||||
|
const withDefaultPlaceholder = <T extends Component>(
|
||||||
|
component: T,
|
||||||
|
type: 'input' | 'select',
|
||||||
|
) => {
|
||||||
|
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||||
|
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
|
||||||
|
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化表单组件,并注册到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: withDefaultPlaceholder(Input, 'input'),
|
||||||
|
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||||
|
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||||
|
Mentions: withDefaultPlaceholder(Mentions, 'input'),
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
RangePicker,
|
||||||
|
Rate,
|
||||||
|
Select: withDefaultPlaceholder(Select, 'select'),
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Textarea: withDefaultPlaceholder(Textarea, 'input'),
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
|
||||||
|
Upload,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
// ant design vue组件库默认都是 v-model:value
|
||||||
|
baseModelPropName: 'value',
|
||||||
|
|
||||||
|
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||||
|
modelPropNameMap: {
|
||||||
|
Checkbox: 'checked',
|
||||||
|
Radio: 'checked',
|
||||||
|
Switch: 'checked',
|
||||||
|
Upload: 'fileList',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
// 输入项目必填国际化适配
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
|
return $t('formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 选择项目必填国际化适配
|
||||||
|
selectRequired: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return $t('formRules.selectRequired', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVbenForm = useForm<FormComponentType>;
|
||||||
|
|
||||||
|
export { useVbenForm, z };
|
||||||
|
|
||||||
|
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||||
|
export type { VbenFormProps };
|
1
apps/web-antd/src/adapter/index.ts
Normal file
1
apps/web-antd/src/adapter/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './form';
|
@@ -10,10 +10,6 @@ export namespace AuthApi {
|
|||||||
/** 登录接口返回值 */
|
/** 登录接口返回值 */
|
||||||
export interface LoginResult {
|
export interface LoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
desc: string;
|
|
||||||
realName: string;
|
|
||||||
userId: string;
|
|
||||||
username: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RefreshTokenResult {
|
export interface RefreshTokenResult {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
|
import type { HttpResponse } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import {
|
import {
|
||||||
@@ -68,7 +70,7 @@ function createRequestClient(baseURL: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// response数据解构
|
// response数据解构
|
||||||
client.addResponseInterceptor({
|
client.addResponseInterceptor<HttpResponse>({
|
||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
@@ -93,7 +95,10 @@ function createRequestClient(baseURL: string) {
|
|||||||
|
|
||||||
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
|
errorMessageResponseInterceptor((msg: string, _error) => {
|
||||||
|
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
|
||||||
|
message.error(msg);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
|
23
apps/web-antd/src/layouts/auth.vue
Normal file
23
apps/web-antd/src/layouts/auth.vue
Normal file
@@ -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>
|
@@ -13,11 +13,12 @@ import {
|
|||||||
UserDropdown,
|
UserDropdown,
|
||||||
} from '@vben/layouts';
|
} from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { storeToRefs, useAccessStore, useUserStore } from '@vben/stores';
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
import { openWindow } from '@vben/utils';
|
import { openWindow } from '@vben/utils';
|
||||||
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
@@ -87,8 +88,6 @@ const menus = computed(() => [
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { loginLoading } = storeToRefs(authStore);
|
|
||||||
|
|
||||||
const avatar = computed(() => {
|
const avatar = computed(() => {
|
||||||
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||||
});
|
});
|
||||||
@@ -130,11 +129,9 @@ function handleMakeAll() {
|
|||||||
<AuthenticationLoginExpiredModal
|
<AuthenticationLoginExpiredModal
|
||||||
v-model:open="accessStore.loginExpired"
|
v-model:open="accessStore.loginExpired"
|
||||||
:avatar
|
:avatar
|
||||||
:loading="loginLoading"
|
>
|
||||||
password-placeholder="123456"
|
<LoginForm />
|
||||||
username-placeholder="vben"
|
</AuthenticationLoginExpiredModal>
|
||||||
@submit="authStore.authLogin"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #lock-screen>
|
<template #lock-screen>
|
||||||
<LockScreen :avatar @to-login="handleLogout" />
|
<LockScreen :avatar @to-login="handleLogout" />
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
const BasicLayout = () => import('./basic.vue');
|
const BasicLayout = () => import('./basic.vue');
|
||||||
|
const AuthPageLayout = () => import('./auth.vue');
|
||||||
|
|
||||||
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||||
|
|
||||||
const AuthPageLayout = () =>
|
|
||||||
import('@vben/layouts').then((m) => m.AuthPageLayout);
|
|
||||||
|
|
||||||
export { AuthPageLayout, BasicLayout, IFrameView };
|
export { AuthPageLayout, BasicLayout, IFrameView };
|
||||||
|
@@ -19,7 +19,12 @@ const router = createRouter({
|
|||||||
: createWebHistory(import.meta.env.VITE_BASE),
|
: createWebHistory(import.meta.env.VITE_BASE),
|
||||||
// 应该添加到路由的初始路由列表。
|
// 应该添加到路由的初始路由列表。
|
||||||
routes,
|
routes,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: (to, _from, savedPosition) => {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition;
|
||||||
|
}
|
||||||
|
return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
|
||||||
|
},
|
||||||
// 是否应该禁止尾部斜杠。
|
// 是否应该禁止尾部斜杠。
|
||||||
// strict: true,
|
// strict: true,
|
||||||
});
|
});
|
||||||
|
@@ -29,6 +29,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
path: '/workspace',
|
path: '/workspace',
|
||||||
component: () => import('#/views/dashboard/workspace/index.vue'),
|
component: () => import('#/views/dashboard/workspace/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
|
icon: 'carbon:workspace',
|
||||||
title: $t('page.dashboard.workspace'),
|
title: $t('page.dashboard.workspace'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -58,6 +58,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:naiveui',
|
||||||
link: VBEN_NAIVE_PREVIEW_URL,
|
link: VBEN_NAIVE_PREVIEW_URL,
|
||||||
title: $t('page.vben.naive-ui'),
|
title: $t('page.vben.naive-ui'),
|
||||||
},
|
},
|
||||||
@@ -68,6 +69,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:element',
|
||||||
link: VBEN_ELE_PREVIEW_URL,
|
link: VBEN_ELE_PREVIEW_URL,
|
||||||
title: $t('page.vben.element-plus'),
|
title: $t('page.vben.element-plus'),
|
||||||
},
|
},
|
||||||
|
@@ -76,7 +76,11 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function logout(redirect: boolean = true) {
|
async function logout(redirect: boolean = true) {
|
||||||
await logoutApi();
|
try {
|
||||||
|
await logoutApi();
|
||||||
|
} catch {
|
||||||
|
// 不做任何处理
|
||||||
|
}
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
|
@@ -1,15 +1,49 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginCodeParams } from '@vben/common-ui';
|
import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationCodeLogin } from '@vben/common-ui';
|
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'CodeLogin' });
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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
|
* Asynchronously handle the login process
|
||||||
@@ -23,8 +57,8 @@ async function handleLogin(values: LoginCodeParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationCodeLogin
|
<AuthenticationCodeLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleLogin"
|
@submit="handleLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,13 +1,32 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { AuthenticationForgetPassword } from '@vben/common-ui';
|
import { computed, ref } from 'vue';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
|
||||||
|
import { AuthenticationForgetPassword, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'ForgetPassword' });
|
defineOptions({ name: 'ForgetPassword' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: string) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
@@ -16,8 +35,8 @@ function handleSubmit(value: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationForgetPassword
|
<AuthenticationForgetPassword
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,18 +1,98 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { AuthenticationLogin } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { BasicOption } from '@vben/types';
|
||||||
|
|
||||||
|
import { computed, markRaw } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
defineOptions({ name: 'Login' });
|
defineOptions({ name: 'Login' });
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const MOCK_USER_OPTIONS: BasicOption[] = [
|
||||||
|
{
|
||||||
|
label: 'Super',
|
||||||
|
value: 'vben',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Admin',
|
||||||
|
value: 'admin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'User',
|
||||||
|
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') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: markRaw(SliderCaptcha),
|
||||||
|
fieldName: 'captcha',
|
||||||
|
rules: z.boolean().refine((value) => value, {
|
||||||
|
message: $t('authentication.verifyRequiredTip'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationLogin
|
<AuthenticationLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="authStore.loginLoading"
|
:loading="authStore.loginLoading"
|
||||||
password-placeholder="123456"
|
|
||||||
username-placeholder="vben"
|
|
||||||
@submit="authStore.authLogin"
|
@submit="authStore.authLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,15 +1,91 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationRegister } from '@vben/common-ui';
|
import { AuthenticationRegister, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'Register' });
|
defineOptions({ name: 'Register' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: LoginAndRegisterParams) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
@@ -18,8 +94,8 @@ function handleSubmit(value: LoginAndRegisterParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationRegister
|
<AuthenticationRegister
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -55,12 +55,27 @@ onMounted(() => {
|
|||||||
},
|
},
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
|
// xAxis: {
|
||||||
|
// axisTick: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// boundaryGap: false,
|
||||||
|
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
// type: 'category',
|
||||||
|
// },
|
||||||
xAxis: {
|
xAxis: {
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
type: 'category',
|
type: 'category',
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
@@ -69,7 +84,10 @@ onMounted(() => {
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
max: 80_000,
|
max: 80_000,
|
||||||
|
splitArea: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
splitNumber: 4,
|
||||||
type: 'value',
|
type: 'value',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
|
|||||||
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-2/5">
|
<div class="w-full lg:w-2/5">
|
||||||
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
|
<WorkbenchQuickNav
|
||||||
|
:items="quickNavItems"
|
||||||
|
class="mt-5 lg:mt-0"
|
||||||
|
title="快捷导航"
|
||||||
|
/>
|
||||||
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
||||||
<AnalysisChartCard class="mt-5" title="访问来源">
|
<AnalysisChartCard class="mt-5" title="访问来源">
|
||||||
<AnalyticsVisitsSource />
|
<AnalyticsVisitsSource />
|
||||||
|
@@ -37,7 +37,7 @@ function notify(type: NotificationType) {
|
|||||||
description="支持多语言,主题功能集成切换等"
|
description="支持多语言,主题功能集成切换等"
|
||||||
title="Ant Design Vue组件使用演示"
|
title="Ant Design Vue组件使用演示"
|
||||||
>
|
>
|
||||||
<Card title="按钮">
|
<Card class="mb-5" title="按钮">
|
||||||
<Space>
|
<Space>
|
||||||
<Button>Default</Button>
|
<Button>Default</Button>
|
||||||
<Button type="primary"> Primary </Button>
|
<Button type="primary"> Primary </Button>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-ele",
|
"name": "@vben/web-ele",
|
||||||
"version": "5.2.0",
|
"version": "5.3.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,14 +40,14 @@
|
|||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^11.0.3",
|
"@vueuse/core": "catalog:",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "catalog:",
|
||||||
"element-plus": "^2.8.1",
|
"element-plus": "catalog:",
|
||||||
"pinia": "2.2.2",
|
"pinia": "catalog:",
|
||||||
"vue": "^3.4.38",
|
"vue": "catalog:",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"unplugin-element-plus": "^0.8.0"
|
"unplugin-element-plus": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
apps/web-ele/src/adapter/form.ts
Normal file
106
apps/web-ele/src/adapter/form.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import type {
|
||||||
|
BaseFormComponentType,
|
||||||
|
VbenFormSchema as FormSchema,
|
||||||
|
VbenFormProps,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import type { Component, SetupContext } from 'vue';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ElButton,
|
||||||
|
ElCheckbox,
|
||||||
|
ElCheckboxGroup,
|
||||||
|
ElDivider,
|
||||||
|
ElInput,
|
||||||
|
ElInputNumber,
|
||||||
|
ElRadioGroup,
|
||||||
|
ElSelect,
|
||||||
|
ElSpace,
|
||||||
|
ElSwitch,
|
||||||
|
ElTimePicker,
|
||||||
|
ElTreeSelect,
|
||||||
|
ElUpload,
|
||||||
|
} from 'element-plus';
|
||||||
|
// 业务表单组件适配
|
||||||
|
|
||||||
|
export type FormComponentType =
|
||||||
|
| 'Checkbox'
|
||||||
|
| 'CheckboxGroup'
|
||||||
|
| 'DatePicker'
|
||||||
|
| 'Divider'
|
||||||
|
| 'Input'
|
||||||
|
| 'InputNumber'
|
||||||
|
| 'RadioGroup'
|
||||||
|
| 'Select'
|
||||||
|
| 'Space'
|
||||||
|
| 'Switch'
|
||||||
|
| 'TimePicker'
|
||||||
|
| 'TreeSelect'
|
||||||
|
| 'Upload'
|
||||||
|
| BaseFormComponentType;
|
||||||
|
|
||||||
|
const withDefaultPlaceholder = <T extends Component>(
|
||||||
|
component: T,
|
||||||
|
type: 'input' | 'select',
|
||||||
|
) => {
|
||||||
|
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||||
|
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
|
||||||
|
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化表单组件,并注册到form组件内部
|
||||||
|
setupVbenForm<FormComponentType>({
|
||||||
|
components: {
|
||||||
|
Checkbox: ElCheckbox,
|
||||||
|
CheckboxGroup: ElCheckboxGroup,
|
||||||
|
// 自定义默认的重置按钮
|
||||||
|
DefaultResetActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(ElButton, { ...props, attrs, type: 'info' }, slots);
|
||||||
|
},
|
||||||
|
// 自定义默认的提交按钮
|
||||||
|
DefaultSubmitActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
|
||||||
|
},
|
||||||
|
Divider: ElDivider,
|
||||||
|
Input: withDefaultPlaceholder(ElInput, 'input'),
|
||||||
|
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
||||||
|
RadioGroup: ElRadioGroup,
|
||||||
|
Select: withDefaultPlaceholder(ElSelect, 'select'),
|
||||||
|
Space: ElSpace,
|
||||||
|
Switch: ElSwitch,
|
||||||
|
TimePicker: ElTimePicker,
|
||||||
|
TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
|
||||||
|
Upload: ElUpload,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
modelPropNameMap: {
|
||||||
|
Upload: 'fileList',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
|
return $t('formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
selectRequired: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return $t('formRules.selectRequired', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVbenForm = useForm<FormComponentType>;
|
||||||
|
|
||||||
|
export { useVbenForm, z };
|
||||||
|
|
||||||
|
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||||
|
export type { VbenFormProps };
|
1
apps/web-ele/src/adapter/index.ts
Normal file
1
apps/web-ele/src/adapter/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './form';
|
@@ -10,10 +10,6 @@ export namespace AuthApi {
|
|||||||
/** 登录接口返回值 */
|
/** 登录接口返回值 */
|
||||||
export interface LoginResult {
|
export interface LoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
desc: string;
|
|
||||||
realName: string;
|
|
||||||
userId: string;
|
|
||||||
username: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RefreshTokenResult {
|
export interface RefreshTokenResult {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
|
import type { HttpResponse } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import {
|
import {
|
||||||
@@ -68,7 +70,7 @@ function createRequestClient(baseURL: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// response数据解构
|
// response数据解构
|
||||||
client.addResponseInterceptor({
|
client.addResponseInterceptor<HttpResponse>({
|
||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
@@ -93,7 +95,10 @@ function createRequestClient(baseURL: string) {
|
|||||||
|
|
||||||
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
errorMessageResponseInterceptor((msg: string) => ElMessage.error(msg)),
|
errorMessageResponseInterceptor((msg: string, _error) => {
|
||||||
|
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
|
||||||
|
ElMessage.error(msg);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
|
23
apps/web-ele/src/layouts/auth.vue
Normal file
23
apps/web-ele/src/layouts/auth.vue
Normal file
@@ -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>
|
@@ -13,11 +13,12 @@ import {
|
|||||||
UserDropdown,
|
UserDropdown,
|
||||||
} from '@vben/layouts';
|
} from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { storeToRefs, useAccessStore, useUserStore } from '@vben/stores';
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
import { openWindow } from '@vben/utils';
|
import { openWindow } from '@vben/utils';
|
||||||
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
@@ -87,8 +88,6 @@ const menus = computed(() => [
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { loginLoading } = storeToRefs(authStore);
|
|
||||||
|
|
||||||
const avatar = computed(() => {
|
const avatar = computed(() => {
|
||||||
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||||
});
|
});
|
||||||
@@ -130,11 +129,9 @@ function handleMakeAll() {
|
|||||||
<AuthenticationLoginExpiredModal
|
<AuthenticationLoginExpiredModal
|
||||||
v-model:open="accessStore.loginExpired"
|
v-model:open="accessStore.loginExpired"
|
||||||
:avatar
|
:avatar
|
||||||
:loading="loginLoading"
|
>
|
||||||
password-placeholder="123456"
|
<LoginForm />
|
||||||
username-placeholder="vben"
|
</AuthenticationLoginExpiredModal>
|
||||||
@submit="authStore.authLogin"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #lock-screen>
|
<template #lock-screen>
|
||||||
<LockScreen :avatar @to-login="handleLogout" />
|
<LockScreen :avatar @to-login="handleLogout" />
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
const BasicLayout = () => import('./basic.vue');
|
const BasicLayout = () => import('./basic.vue');
|
||||||
|
const AuthPageLayout = () => import('./auth.vue');
|
||||||
|
|
||||||
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||||
|
|
||||||
const AuthPageLayout = () =>
|
|
||||||
import('@vben/layouts').then((m) => m.AuthPageLayout);
|
|
||||||
|
|
||||||
export { AuthPageLayout, BasicLayout, IFrameView };
|
export { AuthPageLayout, BasicLayout, IFrameView };
|
||||||
|
@@ -19,7 +19,12 @@ const router = createRouter({
|
|||||||
: createWebHistory(import.meta.env.VITE_BASE),
|
: createWebHistory(import.meta.env.VITE_BASE),
|
||||||
// 应该添加到路由的初始路由列表。
|
// 应该添加到路由的初始路由列表。
|
||||||
routes,
|
routes,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: (to, _from, savedPosition) => {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition;
|
||||||
|
}
|
||||||
|
return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
|
||||||
|
},
|
||||||
// 是否应该禁止尾部斜杠。
|
// 是否应该禁止尾部斜杠。
|
||||||
// strict: true,
|
// strict: true,
|
||||||
});
|
});
|
||||||
|
@@ -29,6 +29,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
path: '/workspace',
|
path: '/workspace',
|
||||||
component: () => import('#/views/dashboard/workspace/index.vue'),
|
component: () => import('#/views/dashboard/workspace/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
|
icon: 'carbon:workspace',
|
||||||
title: $t('page.dashboard.workspace'),
|
title: $t('page.dashboard.workspace'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
VBEN_LOGO_URL,
|
VBEN_LOGO_URL,
|
||||||
VBEN_NAIVE_PREVIEW_URL,
|
VBEN_NAIVE_PREVIEW_URL,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
|
import { SvgAntdvLogoIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { BasicLayout, IFrameView } from '#/layouts';
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -58,6 +59,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:naiveui',
|
||||||
link: VBEN_NAIVE_PREVIEW_URL,
|
link: VBEN_NAIVE_PREVIEW_URL,
|
||||||
title: $t('page.vben.naive-ui'),
|
title: $t('page.vben.naive-ui'),
|
||||||
},
|
},
|
||||||
@@ -68,6 +70,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: SvgAntdvLogoIcon,
|
||||||
link: VBEN_ANT_PREVIEW_URL,
|
link: VBEN_ANT_PREVIEW_URL,
|
||||||
title: $t('page.vben.antdv'),
|
title: $t('page.vben.antdv'),
|
||||||
},
|
},
|
||||||
|
@@ -77,7 +77,11 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function logout(redirect: boolean = true) {
|
async function logout(redirect: boolean = true) {
|
||||||
await logoutApi();
|
try {
|
||||||
|
await logoutApi();
|
||||||
|
} catch {
|
||||||
|
// 不做任何处理
|
||||||
|
}
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
|
@@ -1,15 +1,49 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginCodeParams } from '@vben/common-ui';
|
import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationCodeLogin } from '@vben/common-ui';
|
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'CodeLogin' });
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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
|
* Asynchronously handle the login process
|
||||||
@@ -23,8 +57,8 @@ async function handleLogin(values: LoginCodeParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationCodeLogin
|
<AuthenticationCodeLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleLogin"
|
@submit="handleLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,13 +1,32 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { AuthenticationForgetPassword } from '@vben/common-ui';
|
import { computed, ref } from 'vue';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
|
||||||
|
import { AuthenticationForgetPassword, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'ForgetPassword' });
|
defineOptions({ name: 'ForgetPassword' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: string) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
@@ -16,8 +35,8 @@ function handleSubmit(value: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationForgetPassword
|
<AuthenticationForgetPassword
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,18 +1,98 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { AuthenticationLogin } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { BasicOption } from '@vben/types';
|
||||||
|
|
||||||
|
import { computed, markRaw } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
defineOptions({ name: 'Login' });
|
defineOptions({ name: 'Login' });
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const MOCK_USER_OPTIONS: BasicOption[] = [
|
||||||
|
{
|
||||||
|
label: 'Super',
|
||||||
|
value: 'vben',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Admin',
|
||||||
|
value: 'admin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'User',
|
||||||
|
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') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: markRaw(SliderCaptcha),
|
||||||
|
fieldName: 'captcha',
|
||||||
|
rules: z.boolean().refine((value) => value, {
|
||||||
|
message: $t('authentication.verifyRequiredTip'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationLogin
|
<AuthenticationLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="authStore.loginLoading"
|
:loading="authStore.loginLoading"
|
||||||
password-placeholder="123456"
|
|
||||||
username-placeholder="vben"
|
|
||||||
@submit="authStore.authLogin"
|
@submit="authStore.authLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,15 +1,91 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationRegister } from '@vben/common-ui';
|
import { AuthenticationRegister, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'Register' });
|
defineOptions({ name: 'Register' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: LoginAndRegisterParams) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
@@ -18,8 +94,8 @@ function handleSubmit(value: LoginAndRegisterParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationRegister
|
<AuthenticationRegister
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -55,12 +55,27 @@ onMounted(() => {
|
|||||||
},
|
},
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
|
// xAxis: {
|
||||||
|
// axisTick: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// boundaryGap: false,
|
||||||
|
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
// type: 'category',
|
||||||
|
// },
|
||||||
xAxis: {
|
xAxis: {
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
type: 'category',
|
type: 'category',
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
@@ -69,7 +84,10 @@ onMounted(() => {
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
max: 80_000,
|
max: 80_000,
|
||||||
|
splitArea: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
splitNumber: 4,
|
||||||
type: 'value',
|
type: 'value',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
|
|||||||
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-2/5">
|
<div class="w-full lg:w-2/5">
|
||||||
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
|
<WorkbenchQuickNav
|
||||||
|
:items="quickNavItems"
|
||||||
|
class="mt-5 lg:mt-0"
|
||||||
|
title="快捷导航"
|
||||||
|
/>
|
||||||
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
||||||
<AnalysisChartCard class="mt-5" title="访问来源">
|
<AnalysisChartCard class="mt-5" title="访问来源">
|
||||||
<AnalyticsVisitsSource />
|
<AnalyticsVisitsSource />
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
ElMessage,
|
ElMessage,
|
||||||
ElNotification,
|
ElNotification,
|
||||||
ElSpace,
|
ElSpace,
|
||||||
|
ElTable,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
|
|
||||||
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
||||||
@@ -38,6 +39,14 @@ function notify(type: NotificationType) {
|
|||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const tableData = [
|
||||||
|
{ prop1: '1', prop2: 'A' },
|
||||||
|
{ prop1: '2', prop2: 'B' },
|
||||||
|
{ prop1: '3', prop2: 'C' },
|
||||||
|
{ prop1: '4', prop2: 'D' },
|
||||||
|
{ prop1: '5', prop2: 'E' },
|
||||||
|
{ prop1: '6', prop2: 'F' },
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -74,5 +83,11 @@ function notify(type: NotificationType) {
|
|||||||
<ElButton type="success" @click="notify('success')"> 成功 </ElButton>
|
<ElButton type="success" @click="notify('success')"> 成功 </ElButton>
|
||||||
</ElSpace>
|
</ElSpace>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
|
<ElCard class="mb-5">
|
||||||
|
<ElTable :data="tableData" stripe>
|
||||||
|
<ElTable.TableColumn label="测试列1" prop="prop1" />
|
||||||
|
<ElTable.TableColumn label="测试列2" prop="prop2" />
|
||||||
|
</ElTable>
|
||||||
|
</ElCard>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-naive",
|
"name": "@vben/web-naive",
|
||||||
"version": "5.2.0",
|
"version": "5.3.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,10 +40,10 @@
|
|||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^11.0.3",
|
"@vueuse/core": "catalog:",
|
||||||
"naive-ui": "^2.39.0",
|
"naive-ui": "catalog:",
|
||||||
"pinia": "2.2.2",
|
"pinia": "catalog:",
|
||||||
"vue": "^3.4.38",
|
"vue": "catalog:",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
apps/web-naive/src/adapter/form.ts
Normal file
116
apps/web-naive/src/adapter/form.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import type {
|
||||||
|
BaseFormComponentType,
|
||||||
|
VbenFormSchema as FormSchema,
|
||||||
|
VbenFormProps,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import type { Component, SetupContext } from 'vue';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import {
|
||||||
|
NButton,
|
||||||
|
NCheckbox,
|
||||||
|
NCheckboxGroup,
|
||||||
|
NDatePicker,
|
||||||
|
NDivider,
|
||||||
|
NInput,
|
||||||
|
NInputNumber,
|
||||||
|
NRadioGroup,
|
||||||
|
NSelect,
|
||||||
|
NSpace,
|
||||||
|
NSwitch,
|
||||||
|
NTimePicker,
|
||||||
|
NTreeSelect,
|
||||||
|
NUpload,
|
||||||
|
} from 'naive-ui';
|
||||||
|
// 业务表单组件适配
|
||||||
|
|
||||||
|
export type FormComponentType =
|
||||||
|
| 'Checkbox'
|
||||||
|
| 'CheckboxGroup'
|
||||||
|
| 'DatePicker'
|
||||||
|
| 'Divider'
|
||||||
|
| 'Input'
|
||||||
|
| 'InputNumber'
|
||||||
|
| 'RadioGroup'
|
||||||
|
| 'Select'
|
||||||
|
| 'Space'
|
||||||
|
| 'Switch'
|
||||||
|
| 'TimePicker'
|
||||||
|
| 'TreeSelect'
|
||||||
|
| 'Upload'
|
||||||
|
| BaseFormComponentType;
|
||||||
|
|
||||||
|
const withDefaultPlaceholder = <T extends Component>(
|
||||||
|
component: T,
|
||||||
|
type: 'input' | 'select',
|
||||||
|
) => {
|
||||||
|
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||||
|
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
|
||||||
|
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化表单组件,并注册到form组件内部
|
||||||
|
setupVbenForm<FormComponentType>({
|
||||||
|
components: {
|
||||||
|
Checkbox: NCheckbox,
|
||||||
|
CheckboxGroup: NCheckboxGroup,
|
||||||
|
DatePicker: NDatePicker,
|
||||||
|
// 自定义默认的重置按钮
|
||||||
|
DefaultResetActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(NButton, { ...props, attrs, text: false, type: 'info' }, slots);
|
||||||
|
},
|
||||||
|
// 自定义默认的提交按钮
|
||||||
|
DefaultSubmitActionButton: (props, { attrs, slots }) => {
|
||||||
|
return h(
|
||||||
|
NButton,
|
||||||
|
{ ...props, attrs, text: false, type: 'primary' },
|
||||||
|
slots,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Divider: NDivider,
|
||||||
|
Input: withDefaultPlaceholder(NInput, 'input'),
|
||||||
|
InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
|
||||||
|
RadioGroup: NRadioGroup,
|
||||||
|
Select: withDefaultPlaceholder(NSelect, 'select'),
|
||||||
|
Space: NSpace,
|
||||||
|
Switch: NSwitch,
|
||||||
|
TimePicker: NTimePicker,
|
||||||
|
TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'),
|
||||||
|
Upload: NUpload,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
disabledOnChangeListener: true,
|
||||||
|
baseModelPropName: 'value',
|
||||||
|
modelPropNameMap: {
|
||||||
|
Checkbox: 'checked',
|
||||||
|
Radio: 'checked',
|
||||||
|
Upload: 'fileList',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
|
return $t('formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
selectRequired: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return $t('formRules.selectRequired', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVbenForm = useForm<FormComponentType>;
|
||||||
|
|
||||||
|
export { useVbenForm, z };
|
||||||
|
|
||||||
|
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||||
|
export type { VbenFormProps };
|
2
apps/web-naive/src/adapter/index.ts
Normal file
2
apps/web-naive/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './form';
|
||||||
|
export * from './naive';
|
@@ -10,10 +10,6 @@ export namespace AuthApi {
|
|||||||
/** 登录接口返回值 */
|
/** 登录接口返回值 */
|
||||||
export interface LoginResult {
|
export interface LoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
desc: string;
|
|
||||||
realName: string;
|
|
||||||
userId: string;
|
|
||||||
username: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RefreshTokenResult {
|
export interface RefreshTokenResult {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
|
import type { HttpResponse } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import {
|
import {
|
||||||
@@ -10,7 +12,7 @@ import {
|
|||||||
} from '@vben/request';
|
} from '@vben/request';
|
||||||
import { useAccessStore } from '@vben/stores';
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
import { message } from '#/naive';
|
import { message } from '#/adapter';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
import { refreshTokenApi } from './core';
|
import { refreshTokenApi } from './core';
|
||||||
@@ -67,7 +69,7 @@ function createRequestClient(baseURL: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// response数据解构
|
// response数据解构
|
||||||
client.addResponseInterceptor({
|
client.addResponseInterceptor<HttpResponse>({
|
||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
@@ -92,7 +94,10 @@ function createRequestClient(baseURL: string) {
|
|||||||
|
|
||||||
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
|
errorMessageResponseInterceptor((msg: string, _error) => {
|
||||||
|
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
|
||||||
|
message.error(msg);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
|
23
apps/web-naive/src/layouts/auth.vue
Normal file
23
apps/web-naive/src/layouts/auth.vue
Normal file
@@ -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>
|
@@ -13,11 +13,12 @@ import {
|
|||||||
UserDropdown,
|
UserDropdown,
|
||||||
} from '@vben/layouts';
|
} from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { storeToRefs, useAccessStore, useUserStore } from '@vben/stores';
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
import { openWindow } from '@vben/utils';
|
import { openWindow } from '@vben/utils';
|
||||||
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
@@ -87,8 +88,6 @@ const menus = computed(() => [
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { loginLoading } = storeToRefs(authStore);
|
|
||||||
|
|
||||||
const avatar = computed(() => {
|
const avatar = computed(() => {
|
||||||
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||||
});
|
});
|
||||||
@@ -130,11 +129,9 @@ function handleMakeAll() {
|
|||||||
<AuthenticationLoginExpiredModal
|
<AuthenticationLoginExpiredModal
|
||||||
v-model:open="accessStore.loginExpired"
|
v-model:open="accessStore.loginExpired"
|
||||||
:avatar
|
:avatar
|
||||||
:loading="loginLoading"
|
>
|
||||||
password-placeholder="123456"
|
<LoginForm />
|
||||||
username-placeholder="vben"
|
</AuthenticationLoginExpiredModal>
|
||||||
@submit="authStore.authLogin"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #lock-screen>
|
<template #lock-screen>
|
||||||
<LockScreen :avatar @to-login="handleLogout" />
|
<LockScreen :avatar @to-login="handleLogout" />
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
const BasicLayout = () => import('./basic.vue');
|
const BasicLayout = () => import('./basic.vue');
|
||||||
|
const AuthPageLayout = () => import('./auth.vue');
|
||||||
|
|
||||||
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||||
|
|
||||||
const AuthPageLayout = () =>
|
|
||||||
import('@vben/layouts').then((m) => m.AuthPageLayout);
|
|
||||||
|
|
||||||
export { AuthPageLayout, BasicLayout, IFrameView };
|
export { AuthPageLayout, BasicLayout, IFrameView };
|
||||||
|
@@ -6,10 +6,10 @@ import type {
|
|||||||
import { generateAccessible } from '@vben/access';
|
import { generateAccessible } from '@vben/access';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { message } from '#/adapter';
|
||||||
import { getAllMenusApi } from '#/api';
|
import { getAllMenusApi } from '#/api';
|
||||||
import { BasicLayout, IFrameView } from '#/layouts';
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { message } from '#/naive';
|
|
||||||
|
|
||||||
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
||||||
|
|
||||||
|
@@ -19,7 +19,12 @@ const router = createRouter({
|
|||||||
: createWebHistory(import.meta.env.VITE_BASE),
|
: createWebHistory(import.meta.env.VITE_BASE),
|
||||||
// 应该添加到路由的初始路由列表。
|
// 应该添加到路由的初始路由列表。
|
||||||
routes,
|
routes,
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: (to, _from, savedPosition) => {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition;
|
||||||
|
}
|
||||||
|
return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
|
||||||
|
},
|
||||||
// 是否应该禁止尾部斜杠。
|
// 是否应该禁止尾部斜杠。
|
||||||
// strict: true,
|
// strict: true,
|
||||||
});
|
});
|
||||||
|
@@ -29,6 +29,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
path: '/workspace',
|
path: '/workspace',
|
||||||
component: () => import('#/views/dashboard/workspace/index.vue'),
|
component: () => import('#/views/dashboard/workspace/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
|
icon: 'carbon:workspace',
|
||||||
title: $t('page.dashboard.workspace'),
|
title: $t('page.dashboard.workspace'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -25,7 +25,6 @@ const routes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:shield-key-outline',
|
|
||||||
title: $t('page.demos.table'),
|
title: $t('page.demos.table'),
|
||||||
},
|
},
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
VBEN_GITHUB_URL,
|
VBEN_GITHUB_URL,
|
||||||
VBEN_LOGO_URL,
|
VBEN_LOGO_URL,
|
||||||
} from '@vben/constants';
|
} from '@vben/constants';
|
||||||
|
import { SvgAntdvLogoIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { BasicLayout, IFrameView } from '#/layouts';
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -58,6 +59,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: SvgAntdvLogoIcon,
|
||||||
link: VBEN_ANT_PREVIEW_URL,
|
link: VBEN_ANT_PREVIEW_URL,
|
||||||
title: $t('page.vben.antdv'),
|
title: $t('page.vben.antdv'),
|
||||||
},
|
},
|
||||||
@@ -68,6 +70,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: IFrameView,
|
component: IFrameView,
|
||||||
meta: {
|
meta: {
|
||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
|
icon: 'logos:element',
|
||||||
link: VBEN_ELE_PREVIEW_URL,
|
link: VBEN_ELE_PREVIEW_URL,
|
||||||
title: $t('page.vben.element-plus'),
|
title: $t('page.vben.element-plus'),
|
||||||
},
|
},
|
||||||
|
@@ -9,9 +9,9 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
|||||||
|
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { notification } from '#/adapter';
|
||||||
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { notification } from '#/naive';
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
@@ -77,7 +77,11 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function logout(redirect: boolean = true) {
|
async function logout(redirect: boolean = true) {
|
||||||
await logoutApi();
|
try {
|
||||||
|
await logoutApi();
|
||||||
|
} catch {
|
||||||
|
// 不做任何处理
|
||||||
|
}
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
|
@@ -1,15 +1,49 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginCodeParams } from '@vben/common-ui';
|
import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationCodeLogin } from '@vben/common-ui';
|
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'CodeLogin' });
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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
|
* Asynchronously handle the login process
|
||||||
@@ -23,8 +57,8 @@ async function handleLogin(values: LoginCodeParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationCodeLogin
|
<AuthenticationCodeLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleLogin"
|
@submit="handleLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,13 +1,32 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { AuthenticationForgetPassword } from '@vben/common-ui';
|
import { computed, ref } from 'vue';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
|
||||||
|
import { AuthenticationForgetPassword, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'ForgetPassword' });
|
defineOptions({ name: 'ForgetPassword' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: string) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
@@ -16,8 +35,8 @@ function handleSubmit(value: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationForgetPassword
|
<AuthenticationForgetPassword
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,18 +1,98 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { AuthenticationLogin } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { BasicOption } from '@vben/types';
|
||||||
|
|
||||||
|
import { computed, markRaw } from 'vue';
|
||||||
|
|
||||||
|
import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
defineOptions({ name: 'Login' });
|
defineOptions({ name: 'Login' });
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const MOCK_USER_OPTIONS: BasicOption[] = [
|
||||||
|
{
|
||||||
|
label: 'Super',
|
||||||
|
value: 'vben',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Admin',
|
||||||
|
value: 'admin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'User',
|
||||||
|
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') }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: markRaw(SliderCaptcha),
|
||||||
|
fieldName: 'captcha',
|
||||||
|
rules: z.boolean().refine((value) => value, {
|
||||||
|
message: $t('authentication.verifyRequiredTip'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationLogin
|
<AuthenticationLogin
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="authStore.loginLoading"
|
:loading="authStore.loginLoading"
|
||||||
password-placeholder="123456"
|
|
||||||
username-placeholder="vben"
|
|
||||||
@submit="authStore.authLogin"
|
@submit="authStore.authLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -1,15 +1,91 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationRegister } from '@vben/common-ui';
|
import { AuthenticationRegister, z } from '@vben/common-ui';
|
||||||
import { LOGIN_PATH } from '@vben/constants';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
defineOptions({ name: 'Register' });
|
defineOptions({ name: 'Register' });
|
||||||
|
|
||||||
const loading = ref(false);
|
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) {
|
function handleSubmit(value: LoginAndRegisterParams) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
@@ -18,8 +94,8 @@ function handleSubmit(value: LoginAndRegisterParams) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationRegister
|
<AuthenticationRegister
|
||||||
|
:form-schema="formSchema"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:login-path="LOGIN_PATH"
|
|
||||||
@submit="handleSubmit"
|
@submit="handleSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -55,12 +55,27 @@ onMounted(() => {
|
|||||||
},
|
},
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
|
// xAxis: {
|
||||||
|
// axisTick: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// boundaryGap: false,
|
||||||
|
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
// type: 'category',
|
||||||
|
// },
|
||||||
xAxis: {
|
xAxis: {
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'solid',
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
type: 'category',
|
type: 'category',
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
@@ -69,7 +84,10 @@ onMounted(() => {
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
max: 80_000,
|
max: 80_000,
|
||||||
|
splitArea: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
splitNumber: 4,
|
||||||
type: 'value',
|
type: 'value',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
|
|||||||
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-2/5">
|
<div class="w-full lg:w-2/5">
|
||||||
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
|
<WorkbenchQuickNav
|
||||||
|
:items="quickNavItems"
|
||||||
|
class="mt-5 lg:mt-0"
|
||||||
|
title="快捷导航"
|
||||||
|
/>
|
||||||
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
|
||||||
<AnalysisChartCard class="mt-5" title="访问来源">
|
<AnalysisChartCard class="mt-5" title="访问来源">
|
||||||
<AnalyticsVisitsSource />
|
<AnalyticsVisitsSource />
|
||||||
|
@@ -46,7 +46,10 @@
|
|||||||
"vite",
|
"vite",
|
||||||
"echarts",
|
"echarts",
|
||||||
"sortablejs",
|
"sortablejs",
|
||||||
"etag"
|
"etag",
|
||||||
|
"naiveui",
|
||||||
|
"uicons",
|
||||||
|
"iconoir"
|
||||||
],
|
],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"**/node_modules/**",
|
"**/node_modules/**",
|
||||||
|
@@ -24,14 +24,16 @@ const parsedFiles = computed(() => {
|
|||||||
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
|
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
|
||||||
>
|
>
|
||||||
<div class="flex w-full max-w-[700px] px-2">
|
<div class="flex w-full max-w-[700px] px-2">
|
||||||
<slot v-if="parsedFiles.length > 0"></slot>
|
<ClientOnly>
|
||||||
<div v-else class="text-destructive text-sm">
|
<slot v-if="parsedFiles.length > 0"></slot>
|
||||||
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
|
<div v-else class="text-destructive text-sm">
|
||||||
ERROR:
|
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
|
||||||
</span>
|
ERROR:
|
||||||
The preview directory does not exist. Please check the 'dir'
|
</span>
|
||||||
parameter.
|
The preview directory does not exist. Please check the 'dir'
|
||||||
</div>
|
parameter.
|
||||||
|
</div>
|
||||||
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PreviewGroup v-if="parsedFiles.length > 0" :files="parsedFiles">
|
<PreviewGroup v-if="parsedFiles.length > 0" :files="parsedFiles">
|
||||||
|
@@ -133,12 +133,19 @@ function sidebarCommercial(): DefaultTheme.SidebarItem[] {
|
|||||||
function nav(): DefaultTheme.NavItem[] {
|
function nav(): DefaultTheme.NavItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
activeMatch: '^/en/(guide|components)/',
|
||||||
text: 'Doc',
|
text: 'Doc',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
activeMatch: '^/en/guide/',
|
||||||
link: '/en/guide/introduction/vben',
|
link: '/en/guide/introduction/vben',
|
||||||
text: 'Guide',
|
text: 'Guide',
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// activeMatch: '^/en/components/',
|
||||||
|
// link: '/en/components/introduction',
|
||||||
|
// text: 'Components',
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
text: 'Historical Versions',
|
text: 'Historical Versions',
|
||||||
items: [
|
items: [
|
||||||
|
@@ -47,7 +47,10 @@ export const demoPreviewPlugin = (md: MarkdownRenderer) => {
|
|||||||
const regex = /<DemoPreview[^>]*\sdir="([^"]*)"/g;
|
const regex = /<DemoPreview[^>]*\sdir="([^"]*)"/g;
|
||||||
// Iterate through the Markdown content and replace the pattern
|
// Iterate through the Markdown content and replace the pattern
|
||||||
state.src = state.src.replaceAll(regex, (_match, dir) => {
|
state.src = state.src.replaceAll(regex, (_match, dir) => {
|
||||||
const componentDir = join(process.cwd(), 'src', dir);
|
const componentDir = join(process.cwd(), 'src', dir).replaceAll(
|
||||||
|
'\\',
|
||||||
|
'/',
|
||||||
|
);
|
||||||
|
|
||||||
let childFiles: string[] = [];
|
let childFiles: string[] = [];
|
||||||
let dirExists = true;
|
let dirExists = true;
|
||||||
@@ -80,7 +83,12 @@ export const demoPreviewPlugin = (md: MarkdownRenderer) => {
|
|||||||
if (!state.tokens[index]) {
|
if (!state.tokens[index]) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
const firstString = 'index.vue';
|
||||||
|
childFiles = childFiles.sort((a, b) => {
|
||||||
|
if (a === firstString) return -1;
|
||||||
|
if (b === firstString) return 1;
|
||||||
|
return a.localeCompare(b, 'en', { sensitivity: 'base' });
|
||||||
|
});
|
||||||
state.tokens[index].content =
|
state.tokens[index].content =
|
||||||
`<DemoPreview files="${encodeURIComponent(JSON.stringify(childFiles))}" ><${ComponentName}/>
|
`<DemoPreview files="${encodeURIComponent(JSON.stringify(childFiles))}" ><${ComponentName}/>
|
||||||
`;
|
`;
|
||||||
|
@@ -11,6 +11,10 @@ import {
|
|||||||
} from '@nolebase/vitepress-plugin-git-changelog/vite';
|
} from '@nolebase/vitepress-plugin-git-changelog/vite';
|
||||||
import tailwind from 'tailwindcss';
|
import tailwind from 'tailwindcss';
|
||||||
import { defineConfig, postcssIsolateStyles } from 'vitepress';
|
import { defineConfig, postcssIsolateStyles } from 'vitepress';
|
||||||
|
import {
|
||||||
|
groupIconMdPlugin,
|
||||||
|
groupIconVitePlugin,
|
||||||
|
} from 'vitepress-plugin-group-icons';
|
||||||
|
|
||||||
import { demoPreviewPlugin } from './plugins/demo-preview';
|
import { demoPreviewPlugin } from './plugins/demo-preview';
|
||||||
import { search as zhSearch } from './zh.mts';
|
import { search as zhSearch } from './zh.mts';
|
||||||
@@ -21,13 +25,14 @@ export const shared = defineConfig({
|
|||||||
markdown: {
|
markdown: {
|
||||||
preConfig(md) {
|
preConfig(md) {
|
||||||
md.use(demoPreviewPlugin);
|
md.use(demoPreviewPlugin);
|
||||||
|
md.use(groupIconMdPlugin);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pwa: pwa(),
|
pwa: pwa(),
|
||||||
srcDir: 'src',
|
srcDir: 'src',
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
i18nRouting: true,
|
i18nRouting: true,
|
||||||
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp',
|
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||||
search: {
|
search: {
|
||||||
options: {
|
options: {
|
||||||
locales: {
|
locales: {
|
||||||
@@ -79,6 +84,7 @@ export const shared = defineConfig({
|
|||||||
}),
|
}),
|
||||||
GitChangelogMarkdownSection(),
|
GitChangelogMarkdownSection(),
|
||||||
viteArchiverPlugin({ outputDir: '.vitepress' }),
|
viteArchiverPlugin({ outputDir: '.vitepress' }),
|
||||||
|
groupIconVitePlugin(),
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
fs: {
|
fs: {
|
||||||
@@ -132,12 +138,12 @@ function pwa(): PwaOptions {
|
|||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
sizes: '192x192',
|
sizes: '192x192',
|
||||||
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-192.png',
|
src: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/pwa-icon-192.png',
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sizes: '512x512',
|
sizes: '512x512',
|
||||||
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-512.png',
|
src: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/pwa-icon-512.png',
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -154,11 +154,19 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
link: 'common-ui/vben-modal',
|
link: 'common-ui/vben-modal',
|
||||||
text: 'Vben Modal 模态框',
|
text: 'Modal 模态框',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: 'common-ui/vben-drawer',
|
link: 'common-ui/vben-drawer',
|
||||||
text: 'Vben Drawer 抽屉',
|
text: 'Drawer 抽屉',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'common-ui/vben-form',
|
||||||
|
text: 'Form 表单',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'common-ui/vben-count-to-animator',
|
||||||
|
text: 'CountToAnimator 数字动画',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -168,13 +176,16 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
|||||||
function nav(): DefaultTheme.NavItem[] {
|
function nav(): DefaultTheme.NavItem[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
activeMatch: '^/(guide|components)/',
|
||||||
text: '文档',
|
text: '文档',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
activeMatch: '^/guide/',
|
||||||
link: '/guide/introduction/vben',
|
link: '/guide/introduction/vben',
|
||||||
text: '指南',
|
text: '指南',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
activeMatch: '^/components/',
|
||||||
link: '/components/introduction',
|
link: '/components/introduction',
|
||||||
text: '组件',
|
text: '组件',
|
||||||
},
|
},
|
||||||
|
@@ -1,30 +1,92 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, watch } from 'vue';
|
import {
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
// import { useAntdDesignTokens } from '@vben/hooks';
|
||||||
|
// import { initPreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { ConfigProvider, theme } from 'ant-design-vue';
|
||||||
import mediumZoom from 'medium-zoom';
|
import mediumZoom from 'medium-zoom';
|
||||||
import { useRoute } from 'vitepress';
|
import { useRoute } from 'vitepress';
|
||||||
import DefaultTheme from 'vitepress/theme';
|
import DefaultTheme from 'vitepress/theme';
|
||||||
|
|
||||||
const { Layout } = DefaultTheme;
|
const { Layout } = DefaultTheme;
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
// const { tokens } = useAntdDesignTokens();
|
||||||
|
|
||||||
const initZoom = () => {
|
const initZoom = () => {
|
||||||
// mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' });
|
// mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' });
|
||||||
mediumZoom('.VPContent img', { background: 'var(--vp-c-bg)' });
|
mediumZoom('.VPContent img', { background: 'var(--vp-c-bg)' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isDark = ref(true);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
() => nextTick(() => initZoom()),
|
() => nextTick(() => initZoom()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// initPreferences({
|
||||||
|
// namespace: 'docs',
|
||||||
|
// });
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initZoom();
|
initZoom();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 使用该函数
|
||||||
|
const observer = watchDarkModeChange((dark) => {
|
||||||
|
isDark.value = dark;
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
observer?.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
function watchDarkModeChange(callback: (isDark: boolean) => void) {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const htmlElement = document.documentElement;
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const isDark = htmlElement.classList.contains('dark');
|
||||||
|
callback(isDark);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(htmlElement, {
|
||||||
|
attributeFilter: ['class'],
|
||||||
|
attributes: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialIsDark = htmlElement.classList.contains('dark');
|
||||||
|
callback(initialIsDark);
|
||||||
|
|
||||||
|
return observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenTheme = computed(() => {
|
||||||
|
const algorithm = isDark.value
|
||||||
|
? [theme.darkAlgorithm]
|
||||||
|
: [theme.defaultAlgorithm];
|
||||||
|
|
||||||
|
return {
|
||||||
|
algorithm,
|
||||||
|
// token: tokens,
|
||||||
|
};
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout />
|
<ConfigProvider :theme="tokenTheme">
|
||||||
|
<Layout />
|
||||||
|
</ConfigProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@@ -11,6 +11,7 @@ import { initHmPlugin } from './plugins/hm';
|
|||||||
|
|
||||||
import './styles';
|
import './styles';
|
||||||
|
|
||||||
|
import 'virtual:group-icons.css';
|
||||||
import '@nolebase/vitepress-plugin-git-changelog/client/style.css';
|
import '@nolebase/vitepress-plugin-git-changelog/client/style.css';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@@ -5,3 +5,18 @@ html.dark {
|
|||||||
.dark .VPContent {
|
.dark .VPContent {
|
||||||
/* background-color: #14161a; */
|
/* background-color: #14161a; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-valid-error p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部导航栏选中项样式 */
|
||||||
|
.VPNavBarMenuLink,
|
||||||
|
.VPNavBarMenuGroup {
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBarMenuLink.active,
|
||||||
|
.VPNavBarMenuGroup.active {
|
||||||
|
border-bottom-color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
@@ -1,25 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/docs",
|
"name": "@vben/docs",
|
||||||
"version": "5.2.0",
|
"version": "5.3.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vitepress build",
|
"build": "vitepress build",
|
||||||
"dev": "vitepress dev",
|
"dev": "vitepress dev",
|
||||||
"docs:preview": "vitepress preview"
|
"docs:preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
|
"imports": {
|
||||||
|
"#/*": "./src/_env/*"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben-core/shadcn-ui": "workspace:*",
|
"@vben-core/shadcn-ui": "workspace:*",
|
||||||
"@vben/common-ui": "workspace:*",
|
"@vben/common-ui": "workspace:*",
|
||||||
|
"@vben/hooks": "workspace:*",
|
||||||
|
"@vben/locales": "workspace:*",
|
||||||
|
"@vben/preferences": "workspace:*",
|
||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"lucide-vue-next": "^0.436.0",
|
"ant-design-vue": "catalog:",
|
||||||
"medium-zoom": "^1.1.0",
|
"lucide-vue-next": "catalog:",
|
||||||
"radix-vue": "^1.9.5"
|
"medium-zoom": "catalog:",
|
||||||
|
"radix-vue": "catalog:",
|
||||||
|
"vitepress-plugin-group-icons": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nolebase/vitepress-plugin-git-changelog": "^2.4.0",
|
"@nolebase/vitepress-plugin-git-changelog": "catalog:",
|
||||||
"@vben/vite-config": "workspace:*",
|
"@vben/vite-config": "workspace:*",
|
||||||
"@vite-pwa/vitepress": "^0.5.0",
|
"@vite-pwa/vitepress": "catalog:",
|
||||||
"vitepress": "^1.3.4",
|
"vitepress": "catalog:",
|
||||||
"vue": "^3.4.38"
|
"vue": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
docs/src/_env/adapter/form.ts
Normal file
127
docs/src/_env/adapter/form.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
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,
|
||||||
|
Textarea,
|
||||||
|
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'
|
||||||
|
| 'Textarea'
|
||||||
|
| '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,
|
||||||
|
Textarea,
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect,
|
||||||
|
Upload,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
// ant design vue组件库默认都是 v-model:value
|
||||||
|
baseModelPropName: 'value',
|
||||||
|
|
||||||
|
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||||
|
modelPropNameMap: {
|
||||||
|
Checkbox: 'checked',
|
||||||
|
Radio: 'checked',
|
||||||
|
Switch: 'checked',
|
||||||
|
Upload: 'fileList',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
// 输入项目必填国际化适配
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
|
return $t('formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 选择项目必填国际化适配
|
||||||
|
selectRequired: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return $t('formRules.selectRequired', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVbenForm = useForm<FormComponentType>;
|
||||||
|
|
||||||
|
export { useVbenForm, z };
|
||||||
|
|
||||||
|
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||||
|
export type { VbenFormProps };
|
1
docs/src/_env/adapter/index.ts
Normal file
1
docs/src/_env/adapter/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './form';
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user