mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-12-23 01:56:33 +08:00
Merge branch 'develop' into fix/base-url
This commit is contained in:
commit
f05f54a8af
|
@ -11,6 +11,10 @@
|
|||
"jest": true,
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"defineProps": "readonly",
|
||||
"defineEmits": "readonly"
|
||||
},
|
||||
"plugins": [
|
||||
"vue",
|
||||
"@typescript-eslint"
|
||||
|
|
79
.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml
vendored
Normal file
79
.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
name: 反馈 Bug
|
||||
description: 通过 github 模板进行 Bug 反馈。
|
||||
title: "[组件名称] 描述问题的标题"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# 欢迎你的参与
|
||||
tdesign-vue-next-starter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区:<img width="80px" src="https://user-images.githubusercontent.com/15634204/157386871-bf84c2ea-a02f-4c1c-b6fd-577450cdbcf7.png" />
|
||||
|
||||
在发布一个 Issue 前,请确保:
|
||||
- 在 [常见问题](https://tdesign.tencent.com/about/faq)、[更新日志](https://github.com/Tencent/tdesign-vue-next-starter/blob/main/CHANGELOG.md) 和 [旧Issue列表](https://github.com/Tencent/tdesign-vue-next-starter/issues?q=is%3Aissue) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正)
|
||||
- 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: tdesign-vue-next-starter 版本
|
||||
description: 请检查在最新项目版本中能否重现此 issue。
|
||||
placeholder: 请填写
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: 重现链接
|
||||
description: 请提供尽可能精简的 CodePen、CodeSandbox 或 GitHub 仓库的链接。请不要填无关链接,否则你的 Issue 将被关闭。
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: reproduceSteps
|
||||
attributes:
|
||||
label: 重现步骤
|
||||
description: 请清晰的描述重现该 Issue 的步骤,这能帮助我们快速定位问题。没有清晰重现步骤将不会被修复,标有 'need reproduction' 的 Issue 在 7 天内不提供相关步骤,将被关闭。
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: expect
|
||||
attributes:
|
||||
label: 期望结果
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: actual
|
||||
attributes:
|
||||
label: 实际结果
|
||||
placeholder: 请填写
|
||||
|
||||
- type: input
|
||||
id: frameworkVersion
|
||||
attributes:
|
||||
label: 框架版本
|
||||
placeholder: Vue(3.2.0)
|
||||
|
||||
- type: input
|
||||
id: browsersVersion
|
||||
attributes:
|
||||
label: 浏览器版本
|
||||
placeholder: Chrome(8.213.231.123)
|
||||
|
||||
- type: input
|
||||
id: systemVersion
|
||||
attributes:
|
||||
label: 系统版本
|
||||
placeholder: MacOS(11.2.3)
|
||||
|
||||
- type: input
|
||||
id: nodeVersion
|
||||
attributes:
|
||||
label: Node版本
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: remarks
|
||||
attributes:
|
||||
label: 补充说明
|
||||
description: 可以是遇到这个 bug 的业务场景、上下文等信息。
|
||||
placeholder: 请填写
|
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe Environment**
|
||||
|
||||
Add browser version here
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 使用 issue-helper 新建
|
||||
url: https://Tencent.github.io/tdesign/issue-helper/?lang=zh-CN&repo=Tencent/tdesign-vue-next-starter
|
||||
about: 使用 https://Tencent.github.io/tdesign/issue-helper/ 创建 issue,其中包含 bug 和 feature,表单提交更加严格。
|
30
.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
name: 反馈新功能
|
||||
description: 通过 github 模板进行新功能反馈。
|
||||
title: "[组件名称] 描述问题的标题"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# 欢迎你的参与
|
||||
tdesign-vue-next-starter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区:<img width="80px" src="https://user-images.githubusercontent.com/15634204/157386871-bf84c2ea-a02f-4c1c-b6fd-577450cdbcf7.png" />
|
||||
|
||||
在发布一个 Issue 前,请确保:
|
||||
- 在 [常见问题](https://tdesign.tencent.com/about/faq)、[更新日志](https://github.com/Tencent/tdesign-vue-next-starter/blob/main/CHANGELOG.md) 和 [旧Issue列表](https://github.com/Tencent/tdesign-vue-next-starter/issues?q=is%3Aissue) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正)
|
||||
- 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。
|
||||
|
||||
- type: textarea
|
||||
id: functionContent
|
||||
attributes:
|
||||
label: 这个功能解决了什么问题
|
||||
description: 请详尽说明这个需求的用例和场景。最重要的是:解释清楚是怎样的用户体验需求催生了这个功能上的需求。我们将考虑添加在现有 API 无法轻松实现的功能。新功能的用例也应当足够常见。
|
||||
placeholder: 请填写
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: functionalExpectations
|
||||
attributes:
|
||||
label: 你建议的方案是什么
|
||||
placeholder: 请填写
|
||||
validations:
|
||||
required: true
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
52
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
52
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
首先,感谢你的贡献!😄
|
||||
请阅读并遵循 [TDesign 贡献指南](https://github.com/Tencent/tdesign/blob/main/docs/contributing.md),填写以下 pull request 的信息。
|
||||
PR 在维护者审核通过后会合并,谢谢!
|
||||
-->
|
||||
|
||||
### 🤔 这个 PR 的性质是?
|
||||
|
||||
- [ ] 日常 bug 修复
|
||||
- [ ] 新特性提交
|
||||
- [ ] 文档改进
|
||||
- [ ] 演示代码改进
|
||||
- [ ] 组件样式/交互改进
|
||||
- [ ] CI/CD 改进
|
||||
- [ ] 重构
|
||||
- [ ] 代码风格优化
|
||||
- [ ] 测试用例
|
||||
- [ ] 分支合并
|
||||
- [ ] 其他
|
||||
|
||||
### 🔗 相关 Issue
|
||||
|
||||
<!--
|
||||
1. 描述相关需求的来源,如相关的 issue 讨论链接。
|
||||
-->
|
||||
|
||||
### 💡 需求背景和解决方案
|
||||
|
||||
<!--
|
||||
1. 要解决的具体问题。
|
||||
2. 列出最终的 API 实现和用法。
|
||||
3. 涉及UI/交互变动需要有截图或 GIF。
|
||||
-->
|
||||
|
||||
### 📝 更新日志
|
||||
|
||||
<!--
|
||||
从用户角度描述具体变化,以及可能的 breaking change 和其他风险。
|
||||
-->
|
||||
|
||||
- fix(组件名称): 处理问题或特性描述 ...
|
||||
|
||||
- [ ] 本条 PR 不需要纳入 Changelog
|
||||
|
||||
### ☑️ 请求合并前的自查清单
|
||||
|
||||
⚠️ 请自检并全部**勾选全部选项**。⚠️
|
||||
|
||||
- [ ] 文档已补充或无须补充
|
||||
- [ ] 代码演示已提供或无须提供
|
||||
- [ ] TypeScript 定义已补充或无须补充
|
||||
- [ ] Changelog 已提供或无须提供
|
52
.github/workflows/issue-assignees.temp.yml
vendored
Normal file
52
.github/workflows/issue-assignees.temp.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
# force copy from tencent/tdesign
|
||||
name: Issue Add Assigness
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
mark-duplicate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: wow-actions/auto-comment@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
issuesOpened: |
|
||||
👋 @{{ author }},感谢给 TDesign 提出了 issue。
|
||||
请根据 issue 模版确保背景信息的完善,我们将调查并尽快回复你。
|
||||
|
||||
# https://docs.github.com/cn/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#issues
|
||||
- uses: 94dreamer/issue-assignees@main
|
||||
id: assignees
|
||||
with:
|
||||
project_name: ${{github.event.repository.name}}
|
||||
issue_title: ${{github.event.issue.title}}
|
||||
|
||||
- run: echo ${{ steps.assignees.outputs.contributors }}
|
||||
- name: Add assigness
|
||||
if: steps.assignees.outputs.contributors != ''
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'add-assignees'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
assignees: ${{ steps.assignees.outputs.contributors }}
|
||||
|
||||
- run: |
|
||||
contributors=${{ steps.assignees.outputs.contributors }}
|
||||
contributorstring=${contributors//,/ @}
|
||||
echo "::set-output name=string::@$contributorstring"
|
||||
id: contributors
|
||||
|
||||
- name: 通知贡献者
|
||||
if: steps.assignees.outputs.contributors != ''
|
||||
uses: actions-cool/maintain-one-comment@v2.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
♥️ 有劳 ${{ steps.contributors.outputs.string }} 尽快确认问题。
|
||||
确认有效后将下一步计划和可能需要的时间回复给 @${{ github.event.issue.user.login }} 。
|
||||
<!-- AUTO_ASSIGENEES_NOTIFY_HOOK -->
|
||||
number: ${{ github.event.issue.number }}
|
||||
body-include: "<!-- AUTO_ASSIGENEES_NOTIFY_HOOK -->"
|
22
.github/workflows/issue-help-wanted.temp.yml
vendored
Normal file
22
.github/workflows/issue-help-wanted.temp.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
# force copy from tencent/tdesign
|
||||
name: Issue Help wanted
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
jobs:
|
||||
add-comment:
|
||||
if: github.event.label.name == 'help wanted'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Add comment
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
任何人都可以处理此问题。
|
||||
**请务必在您的 `pull request` 中引用此问题。** :sparkles:
|
||||
感谢你的贡献! :sparkles:
|
||||
reactions: heart
|
19
.github/workflows/issue-mark-duplicate.temp.yml
vendored
Normal file
19
.github/workflows/issue-mark-duplicate.temp.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# force copy from tencent/tdesign
|
||||
# 当在 issue 的 comment 回复类似 `Duplicate of #111` 这样的话,issue 将被自动打上 重复提交标签 并且 cloese
|
||||
name: Issue Mark Duplicate
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
mark-duplicate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: mark-duplicate
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: "mark-duplicate"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
duplicate-labels: "duplicate"
|
||||
close-issue: true
|
21
.github/workflows/issue-reply.temp.yml
vendored
Normal file
21
.github/workflows/issue-reply.temp.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# force copy from tencent/tdesign
|
||||
# 当被打上 Need Reproduce 标签时候,自动提示需要重现实例
|
||||
|
||||
name: ISSUE_REPLY
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
issue-reply:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Need Reproduce
|
||||
if: github.event.label.name == 'Need Reproduce'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
你好 @${{ github.event.issue.user.login }}, 我们需要你提供一个在线的重现实例以便于我们帮你排查问题。你可以通过点击 [此处](https://codesandbox.io/) 创建一个 codesandbox 或者提供一个最小化的 GitHub 仓库。请确保选择准确的版本。
|
27
.github/workflows/issue-stale-close.temp.yml
vendored
Normal file
27
.github/workflows/issue-stale-close.temp.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# force copy from tencent/tdesign
|
||||
# 国际标准时间+8
|
||||
name: Close stale issues and PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: "50 5 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
stale-issue-message: "这个 Issue 被标记为了过时 stale ,因为它已经持续 30 天没有任何活动了。删除 stale 标签或评论,否则将在 7 天内关闭。"
|
||||
stale-pr-message: '这个 PR 已经过时了,因为它已经持续 45 天没有任何活动了。 删除 stale 的标签或评论,否则将在 10 天内关闭。'
|
||||
close-issue-message: "此 Issue 被自动关闭,因为它自被标记为过时 stale 以来已闲置 7 天。"
|
||||
close-pr-message: "此 PR 被自动关闭,因为它已经 stable 停滞了 10 天,没有任何活动。"
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
days-before-pr-stale: 45
|
||||
days-before-pr-close: 10
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-labels: 'WIP'
|
||||
exempt-pr-labels: 'WIP'
|
17
.github/workflows/issue-synchronize.temp.yml
vendored
Normal file
17
.github/workflows/issue-synchronize.temp.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# force copy from tencent/tdesign
|
||||
name: Issue Add Assigness
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
mark-duplicate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# https://docs.github.com/cn/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#issues
|
||||
- uses: 94dreamer/create-report@main
|
||||
with:
|
||||
wxhook: ${{ secrets.WX_HOOK_URL }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
type: 'issue'
|
15
.github/workflows/preview-publish.yml
vendored
Normal file
15
.github/workflows/preview-publish.yml
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# 文件名建议统一为 preview-publish
|
||||
# 应用 preview.yml 的 demo
|
||||
name: PREVIEW_PUBLISH
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["MAIN_PULL_REQUEST"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
call-preview:
|
||||
uses: Tencent/tdesign/.github/workflows/preview.yml@main
|
||||
secrets:
|
||||
TDESIGN_SURGE_TOKEN: ${{ secrets.TDESIGN_SURGE_TOKEN }}
|
29
.github/workflows/pull-request.yml
vendored
29
.github/workflows/pull-request.yml
vendored
|
@ -1,28 +1,15 @@
|
|||
# 文件名建议统一为 pull-request.yml
|
||||
# 应用 test-build.yml 的 demo
|
||||
|
||||
name: MAIN_PULL_REQUEST
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [develop, main]
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
MAIN_PULL_REQUEST:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache nodemodules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-nodemodules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm install
|
||||
shell: bash
|
||||
- run: npm run lint
|
||||
call-test-build:
|
||||
uses: Tencent/tdesign/.github/workflows/test-build.yml@main
|
||||
|
||||
# install lint
|
24
.github/workflows/push.yml
vendored
24
.github/workflows/push.yml
vendored
|
@ -1,24 +0,0 @@
|
|||
name: All_ON_PUSH
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
All_ON_PUSH:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: npm install
|
||||
shell: bash
|
||||
- run: echo '${{ github.ref }} ... ${{ github.sha }}'
|
||||
- name: build-site
|
||||
run: npm run build
|
||||
- name: upload surge service and generate preview URL
|
||||
id: deploy
|
||||
run: |
|
||||
export GITHUB_SHA=${{ github.sha }}
|
||||
export GITHUB_SHA_SUB=${GITHUB_SHA: 0: 7 }
|
||||
export DEPLOY_DOMAIN=https://preview-$GITHUB_SHA_SUB-tdesign-vue-next-starter.surge.sh
|
||||
npx surge --project ./dist --domain $DEPLOY_DOMAIN --token ${{ secrets.TDESIGN_SURGE_TOKEN }}
|
||||
echo the preview URL is $DEPLOY_DOMAIN
|
||||
if: ${{ success() }}
|
||||
- run: echo "🚀 This job's status is ${{ job.status }}."
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,3 +20,4 @@ yarn-error.log
|
|||
.stylelintcache
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
### 项目简介
|
||||
|
||||
TDesign Vue Next Starter 是一个基于 TDesign。使用 `Vue3`、`Vite2`、`TypeScript` 开发,可进行个性化主题配置,旨在提供项目开箱即用的、配置式的中后台项目。
|
||||
TDesign Vue Next Starter 是一个基于 TDesign。使用 `Vue3`、`Vite2`、`Pinia`、`TypeScript` 开发,可进行个性化主题配置,旨在提供项目开箱即用的、配置式的中后台项目。
|
||||
|
||||
<p>
|
||||
<a href="http://tdesign.tencent.com/starter/vue-next/">在线预览</a>
|
||||
|
@ -108,9 +108,9 @@ npm run stylelint:fix
|
|||
|
||||
### 兼容性
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions |
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Edge >=84 | Firefox >=83 | Chrome >=84 | Safari >=14.1 |
|
||||
|
||||
### License
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
# 选择一个 Base 镜像
|
||||
FROM node:16
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /space
|
||||
|
||||
# 将 by 中的文件列表 COPY 过来
|
||||
COPY . .
|
||||
|
||||
# 根据 COPY 过来的文件进行依赖的安装
|
||||
RUN npm i
|
||||
|
||||
# 设置好需要的环境变量
|
||||
ENV NODE_PATH=/space/node_modules
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
server {
|
||||
if ($request_method = HEAD) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
location / {
|
||||
alias /usr/share/nginx/html/;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
error_log /var/log/nginx/error.log error;
|
||||
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
19
package.json
19
package.json
|
@ -1,9 +1,10 @@
|
|||
{
|
||||
"name": "tdesign-vue-next-starter",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev:mock": "vite --open --mode mock",
|
||||
"dev": "vite --open --mode development",
|
||||
"dev:linux": "vite --mode development",
|
||||
"build:test": "vite build --mode test",
|
||||
"build": "vue-tsc --noEmit && vite build --mode release",
|
||||
"preview": "vite preview",
|
||||
|
@ -11,21 +12,25 @@
|
|||
"lint:fix": "eslint --ext .vue,.js,jsx,.ts,.tsx ./ --max-warnings 0 --fix",
|
||||
"stylelint": "stylelint src/**/*.{html,vue,sass,less}",
|
||||
"stylelint:fix": "stylelint --fix src/**/*.{html,vue,vss,sass,less}",
|
||||
"prepare": "husky install"
|
||||
"prepare": "husky install",
|
||||
"site:preview": "npm run build && cp -r dist _site",
|
||||
"test": "echo \"no test specified,work in process\""
|
||||
},
|
||||
"dependencies": {
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"echarts": "~5.1.2",
|
||||
"hex-to-hsl": "^1.0.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.11",
|
||||
"qrcode.vue": "^3.2.2",
|
||||
"tdesign-vue-next": "^0.5.0",
|
||||
"tdesign-icons-vue-next": "^0.0.6",
|
||||
"tdesign-vue-next": "0.10.1",
|
||||
"tvision-color": "^1.3.1",
|
||||
"vue": "^3.1.5",
|
||||
"vue": "^3.2.31",
|
||||
"vue-color-kit": "^1.0.5",
|
||||
"vue-router": "^4.0.11",
|
||||
"vue3-clipboard": "^1.0.0",
|
||||
"vuex": "^4.0.2"
|
||||
"vue3-clipboard": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^15.0.0",
|
||||
|
@ -37,7 +42,7 @@
|
|||
"@vitejs/plugin-vue": "^1.3.0",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.7",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"axios": "^0.24.0",
|
||||
"axios": "^0.26.0",
|
||||
"commitizen": "^4.2.4",
|
||||
"compressorjs": "^1.0.7",
|
||||
"eslint": "^7.32.0",
|
||||
|
|
23
src/App.vue
23
src/App.vue
|
@ -1,22 +1,19 @@
|
|||
<template>
|
||||
<router-view :class="[mode]" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue';
|
||||
import config from '@/config/style';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const store = useSettingStore();
|
||||
|
||||
const mode = computed(() => {
|
||||
return store.getters['setting/mode'];
|
||||
});
|
||||
const mode = computed(() => {
|
||||
return store.displayMode;
|
||||
});
|
||||
|
||||
return {
|
||||
mode,
|
||||
};
|
||||
},
|
||||
onMounted(() => {
|
||||
store.updateConfig({ ...config });
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
|
|
@ -9,21 +9,15 @@
|
|||
<user-avatar-icon v-if="product.type === 4" />
|
||||
<laptop-icon v-if="product.type === 5" />
|
||||
</div>
|
||||
<t-tag :theme="product.isSetup ? 'success' : 'default'" :disabled="!product.isSetup">{{
|
||||
product.isSetup ? '已启用' : '已停用'
|
||||
}}</t-tag>
|
||||
<t-tag :theme="product.isSetup ? 'success' : 'default'" :disabled="!product.isSetup">
|
||||
{{ product.isSetup ? '已启用' : '已停用' }}
|
||||
</t-tag>
|
||||
</t-row>
|
||||
<p class="list-card-item_detail--name">
|
||||
{{ product.name }}
|
||||
</p>
|
||||
<p class="list-card-item_detail--desc">
|
||||
{{ product.description }}
|
||||
</p>
|
||||
<p class="list-card-item_detail--name">{{ product.name }}</p>
|
||||
<p class="list-card-item_detail--desc">{{ product.description }}</p>
|
||||
<t-row justify="space-between" align="middle" :class="cardControlClass">
|
||||
<div>
|
||||
<t-button shape="circle" :disabled="!product.isSetup">
|
||||
{{ typeMap[product.type - 1] }}
|
||||
</t-button>
|
||||
<t-button shape="circle" :disabled="!product.isSetup">{{ typeMap[product.type - 1] }}</t-button>
|
||||
<t-button shape="circle" :disabled="!product.isSetup">
|
||||
<add-icon />
|
||||
</t-button>
|
||||
|
@ -52,8 +46,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType } from 'vue';
|
||||
import {
|
||||
ShopIcon,
|
||||
CalendarIcon,
|
||||
|
@ -71,63 +65,35 @@ export interface CardProductType {
|
|||
name: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListCardComponent',
|
||||
components: {
|
||||
ShopIcon,
|
||||
CalendarIcon,
|
||||
ServiceIcon,
|
||||
UserAvatarIcon,
|
||||
LaptopIcon,
|
||||
MoreIcon,
|
||||
AddIcon,
|
||||
},
|
||||
props: {
|
||||
product: {
|
||||
type: Object as PropType<CardProductType>,
|
||||
default: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
emits: ['manage-product', 'delete-item'],
|
||||
setup(props, ctx) {
|
||||
const { emit } = ctx;
|
||||
const cardClass = computed(() => [
|
||||
'list-card-item',
|
||||
{
|
||||
'list-card-item__disabled': !props.product.isSetup,
|
||||
},
|
||||
]);
|
||||
|
||||
const cardLogoClass = computed(() => [
|
||||
'list-card-item_detail--logo',
|
||||
{
|
||||
'list-card-item_detail--logo__disabled': !props.product.isSetup,
|
||||
},
|
||||
]);
|
||||
|
||||
const cardControlClass = computed(() => [
|
||||
'list-card-item_detail--control',
|
||||
{
|
||||
'list-card-item_detail--control__disabled': !props.product.isSetup,
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
cardClass,
|
||||
cardLogoClass,
|
||||
cardControlClass,
|
||||
typeMap: ['A', 'B', 'C', 'D', 'E'],
|
||||
handleClickManage(product) {
|
||||
emit('manage-product', product);
|
||||
},
|
||||
handleClickDelete(product) {
|
||||
emit('delete-item', product);
|
||||
},
|
||||
};
|
||||
const props = defineProps({
|
||||
product: {
|
||||
type: Object as PropType<CardProductType>,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['manage-product', 'delete-item']);
|
||||
|
||||
const cardClass = computed(() => ['list-card-item', { 'list-card-item__disabled': !props.product.isSetup }]);
|
||||
|
||||
const cardLogoClass = computed(() => [
|
||||
'list-card-item_detail--logo',
|
||||
{ 'list-card-item_detail--logo__disabled': !props.product.isSetup },
|
||||
]);
|
||||
|
||||
const cardControlClass = computed(() => [
|
||||
'list-card-item_detail--control',
|
||||
{ 'list-card-item_detail--control__disabled': !props.product.isSetup },
|
||||
]);
|
||||
|
||||
const typeMap = ['A', 'B', 'C', 'D', 'E'];
|
||||
|
||||
const handleClickManage = (product: CardProductType) => {
|
||||
emit('manage-product', product);
|
||||
};
|
||||
|
||||
const handleClickDelete = (product: CardProductType) => {
|
||||
emit('delete-item', product);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
|
@ -16,70 +16,51 @@
|
|||
<div v-if="size !== 'small'" class="card-spacer-bottom" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Card',
|
||||
props: {
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
subtitle: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
compact: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
describe: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<string>,
|
||||
default: 'default',
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
subtitle: String,
|
||||
describe: String,
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
setup(props) {
|
||||
const containerCls = computed(() => {
|
||||
const { compact, border } = props;
|
||||
return ['card-container', { 'card-container-compact': compact, 'card-container--border': border }];
|
||||
});
|
||||
|
||||
const titleCls = computed(() => {
|
||||
const { size } = props;
|
||||
return [
|
||||
'card-title',
|
||||
{
|
||||
'card-title--small': size === 'small',
|
||||
'card-title--default': size !== 'small',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const titleTextCls = computed(() => {
|
||||
const { size } = props;
|
||||
return [
|
||||
{
|
||||
'card-title__text--small': size === 'small',
|
||||
'card-title__text--default': size !== 'small',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
return {
|
||||
containerCls,
|
||||
titleCls,
|
||||
titleTextCls,
|
||||
};
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const containerCls = computed(() => {
|
||||
const { compact, border } = props;
|
||||
return ['card-container', { 'card-container-compact': compact, 'card-container--border': border }];
|
||||
});
|
||||
|
||||
const titleCls = computed(() => {
|
||||
const { size } = props;
|
||||
return [
|
||||
'card-title',
|
||||
{
|
||||
'card-title--small': size === 'small',
|
||||
'card-title--default': size !== 'small',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const titleTextCls = computed(() => {
|
||||
const { size } = props;
|
||||
return [
|
||||
{
|
||||
'card-title__text--small': size === 'small',
|
||||
'card-title__text--default': size !== 'small',
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
|
|
@ -1,37 +1,28 @@
|
|||
<template>
|
||||
<div :style="style" class="color-container" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getBrandColor } from '@/config/color';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
const store = useSettingStore();
|
||||
|
||||
const panelColor =
|
||||
'conic-gradient(from 90deg at 50% 50%, #FF0000 -19.41deg, #FF0000 18.76deg, #FF8A00 59.32deg, #FFE600 99.87deg, #14FF00 141.65deg, #00A3FF 177.72deg, #0500FF 220.23deg, #AD00FF 260.13deg, #FF00C7 300.69deg, #FF0000 340.59deg, #FF0000 378.76deg)';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Color',
|
||||
props: {
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: 'default',
|
||||
},
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
});
|
||||
|
||||
const style = computed(() => {
|
||||
const { value } = props;
|
||||
const { colorList } = store.state.setting;
|
||||
return {
|
||||
background: value !== 'dynamic' ? getBrandColor(value, colorList)['@brand-color'] : panelColor,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
style,
|
||||
};
|
||||
},
|
||||
const style = computed(() => {
|
||||
const { value } = props;
|
||||
const { colorList } = store;
|
||||
return {
|
||||
background: value !== 'dynamic' ? getBrandColor(value, colorList)['@brand-color'] : panelColor,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -8,55 +8,36 @@
|
|||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import Result403Icon from '@/assets/assets-result-403.svg?component';
|
||||
import Result404Icon from '@/assets/assets-result-404.svg?component';
|
||||
import Result500Icon from '@/assets/assets-result-500.svg?component';
|
||||
import ResultIeIcon from '@/assets/assets-result-ie.svg?component';
|
||||
import ResultWifiIcon from '@/assets/assets-result-wifi.svg?component';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Result',
|
||||
props: {
|
||||
bgUrl: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
tip: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const dynamicComponent = computed(() => {
|
||||
switch (props.type) {
|
||||
case '403':
|
||||
return Result403Icon;
|
||||
case '404':
|
||||
return Result404Icon;
|
||||
case '500':
|
||||
return Result500Icon;
|
||||
case 'ie':
|
||||
return ResultIeIcon;
|
||||
case 'wifi':
|
||||
return ResultWifiIcon;
|
||||
default:
|
||||
return Result403Icon;
|
||||
}
|
||||
});
|
||||
return {
|
||||
dynamicComponent,
|
||||
};
|
||||
},
|
||||
const props = defineProps({
|
||||
bgUrl: String,
|
||||
title: String,
|
||||
tip: String,
|
||||
type: String,
|
||||
});
|
||||
|
||||
const dynamicComponent = computed(() => {
|
||||
switch (props.type) {
|
||||
case '403':
|
||||
return Result403Icon;
|
||||
case '404':
|
||||
return Result404Icon;
|
||||
case '500':
|
||||
return Result500Icon;
|
||||
case 'ie':
|
||||
return ResultIeIcon;
|
||||
case 'wifi':
|
||||
return ResultWifiIcon;
|
||||
default:
|
||||
return Result403Icon;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -1,35 +1,27 @@
|
|||
<template>
|
||||
<img :class="className" :src="url" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Thumbnail',
|
||||
props: {
|
||||
url: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<string>,
|
||||
default: 'layout',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const className = computed(() => {
|
||||
const { type } = props;
|
||||
return [
|
||||
'thumbnail-container',
|
||||
{
|
||||
'thumbnail-circle': type === 'circle',
|
||||
'thumbnail-layout': type === 'layout',
|
||||
},
|
||||
];
|
||||
});
|
||||
return { className };
|
||||
const props = defineProps({
|
||||
url: String,
|
||||
type: {
|
||||
type: String,
|
||||
default: 'layout',
|
||||
},
|
||||
});
|
||||
|
||||
const className = computed(() => {
|
||||
const { type } = props;
|
||||
return [
|
||||
'thumbnail-container',
|
||||
{
|
||||
'thumbnail-circle': type === 'circle',
|
||||
'thumbnail-layout': type === 'layout',
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('@/style/index.less');
|
||||
|
|
|
@ -20,46 +20,31 @@
|
|||
<span>{{ describe }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Trend',
|
||||
props: {
|
||||
type: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
describe: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: '',
|
||||
},
|
||||
isReverseColor: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const containerCls = computed(() => {
|
||||
const { isReverseColor, type } = props;
|
||||
return [
|
||||
'trend-container',
|
||||
{
|
||||
'trend-container__reverse': isReverseColor,
|
||||
'trend-container__up': !isReverseColor && type === 'up',
|
||||
'trend-container__down': !isReverseColor && type === 'down',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const iconCls = computed(() => ['trend-icon-container']);
|
||||
|
||||
return {
|
||||
containerCls,
|
||||
iconCls,
|
||||
};
|
||||
const props = defineProps({
|
||||
type: String,
|
||||
describe: [String, Number],
|
||||
isReverseColor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const containerCls = computed(() => {
|
||||
const { isReverseColor, type } = props;
|
||||
return [
|
||||
'trend-container',
|
||||
{
|
||||
'trend-container__reverse': isReverseColor,
|
||||
'trend-container__up': !isReverseColor && type === 'up',
|
||||
'trend-container__down': !isReverseColor && type === 'down',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const iconCls = computed(() => ['trend-icon-container']);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import hexToHsl from 'hex-to-hsl';
|
||||
/* eslint-disable indent */
|
||||
export type ColorToken = Record<string, string>;
|
||||
export type ColorSeries = Record<string, ColorToken>;
|
||||
export type TColorToken = Record<string, string>;
|
||||
export type TColorSeries = Record<string, TColorToken>;
|
||||
|
||||
export const defaultLightColor = [
|
||||
'#0052d9',
|
||||
|
@ -24,7 +24,7 @@ export const defaultDarkColor = [
|
|||
'#ab87d5',
|
||||
];
|
||||
|
||||
export const BACKGROUND_TOKEN: ColorSeries = {
|
||||
export const BACKGROUND_TOKEN: TColorSeries = {
|
||||
BLUE_GREY: {
|
||||
'@gray-color-1': '#F1F2F5',
|
||||
'@gray-color-2': '#EBEDF1',
|
||||
|
@ -60,7 +60,7 @@ export const BACKGROUND_TOKEN: ColorSeries = {
|
|||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const NEUTRAL_GREY_TOKEN: ColorToken = {
|
||||
export const NEUTRAL_GREY_TOKEN: TColorToken = {
|
||||
'@gray-color-1': '#F3F3F3',
|
||||
'@gray-color-2': '#EEEEEE',
|
||||
'@gray-color-3': '#E7E7E7',
|
||||
|
@ -77,7 +77,7 @@ export const NEUTRAL_GREY_TOKEN: ColorToken = {
|
|||
'@gray-color-14': '#181818',
|
||||
};
|
||||
|
||||
export const COLOR_TOKEN: ColorSeries = {
|
||||
export const COLOR_TOKEN: TColorSeries = {
|
||||
DEFAULT: {
|
||||
'@brand-color': '#0052D9',
|
||||
'@brand-color-1': '#e0ebff',
|
||||
|
@ -185,35 +185,37 @@ export const COLOR_TOKEN: ColorSeries = {
|
|||
},
|
||||
};
|
||||
|
||||
export const LIGHT_CHART_COLORS: ColorToken = {
|
||||
export const LIGHT_CHART_COLORS = {
|
||||
textColor: 'rgba(0, 0, 0, 0.9)',
|
||||
placeholderColor: 'rgba(0, 0, 0, 0.35)',
|
||||
borderColor: '#dcdcdc',
|
||||
containerColor: '#fff',
|
||||
};
|
||||
|
||||
export const DARK_CHART_COLORS: ColorToken = {
|
||||
export const DARK_CHART_COLORS = {
|
||||
textColor: 'rgba(255, 255, 255, 0.9)',
|
||||
placeholderColor: 'rgba(255, 255, 255, 0.35)',
|
||||
borderColor: '#5e5e5e',
|
||||
containerColor: '#242424',
|
||||
};
|
||||
|
||||
export type TChartColor = typeof LIGHT_CHART_COLORS;
|
||||
|
||||
function toUnderline(name: string): string {
|
||||
return name.replace(/([A-Z])/g, '_$1').toUpperCase();
|
||||
}
|
||||
|
||||
export function getGreyColor(type: string): ColorToken {
|
||||
export function getGreyColor(type: string): TColorToken {
|
||||
const name = toUnderline(type);
|
||||
return BACKGROUND_TOKEN[name] || {};
|
||||
}
|
||||
|
||||
export function getBrandColor(type: string, colorList: ColorSeries): ColorToken {
|
||||
export function getBrandColor(type: string, colorList: TColorSeries): TColorToken {
|
||||
const name = /^#[A-F\d]{6}$/i.test(type) ? type : toUnderline(type);
|
||||
return colorList[name || 'DEFAULT'];
|
||||
}
|
||||
|
||||
export function getColorList(colorArray: Array<ColorToken>): Array<string> {
|
||||
export function getColorList(colorArray: Array<TColorToken>): Array<string> {
|
||||
const pureColorList = [];
|
||||
colorArray.map((colorToken) => Object.keys(colorToken).map((key) => pureColorList.push(colorToken[key])));
|
||||
|
||||
|
@ -262,7 +264,7 @@ export function generateColorMap(theme: string, colorPalette: Array<string>, mod
|
|||
};
|
||||
return colorMap;
|
||||
}
|
||||
export function insertThemeStylesheet(theme: string, colorMap: ColorToken, mode: 'light' | 'dark') {
|
||||
export function insertThemeStylesheet(theme: string, colorMap: TColorToken, mode: 'light' | 'dark') {
|
||||
const isDarkMode = mode === 'dark';
|
||||
const root = !isDarkMode ? `:root[theme-color='${theme}']` : `:root[theme-color='${theme}'][theme-mode='dark']`;
|
||||
|
||||
|
|
|
@ -35,5 +35,5 @@ export interface NotificationItem {
|
|||
status: boolean;
|
||||
collected: boolean;
|
||||
date: string;
|
||||
quality: 'high' | 'low' | 'middle';
|
||||
quality: string;
|
||||
}
|
||||
|
|
|
@ -6,37 +6,25 @@
|
|||
</t-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TdesignStarterBreadcrumb',
|
||||
props: {
|
||||
isVisible: Boolean,
|
||||
},
|
||||
setup() {
|
||||
const crumbs = computed(() => {
|
||||
const route = useRoute();
|
||||
const crumbs = computed(() => {
|
||||
const route = useRoute();
|
||||
|
||||
const pathArray = route.path.split('/');
|
||||
pathArray.shift();
|
||||
const pathArray = route.path.split('/');
|
||||
pathArray.shift();
|
||||
|
||||
const breadcrumbs = pathArray.reduce((breadcrumbArray, path, idx) => {
|
||||
breadcrumbArray.push({
|
||||
path,
|
||||
to: breadcrumbArray[idx - 1] ? `/${breadcrumbArray[idx - 1].path}/${path}` : `/${path}`,
|
||||
title: route.matched[idx].meta.title || path,
|
||||
});
|
||||
return breadcrumbArray;
|
||||
}, []);
|
||||
return breadcrumbs;
|
||||
const breadcrumbs = pathArray.reduce((breadcrumbArray, path, idx) => {
|
||||
breadcrumbArray.push({
|
||||
path,
|
||||
to: breadcrumbArray[idx - 1] ? `/${breadcrumbArray[idx - 1].path}/${path}` : `/${path}`,
|
||||
title: route.matched[idx].meta.title || path,
|
||||
});
|
||||
|
||||
return {
|
||||
crumbs,
|
||||
};
|
||||
},
|
||||
return breadcrumbArray;
|
||||
}, []);
|
||||
return breadcrumbs;
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
|
|
|
@ -2,18 +2,8 @@
|
|||
<div :class="prefix + '-footer'">Copyright @ 2021-{{ new Date().getFullYear() }} Tencent. All Rights Reserved</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { prefix } from '@/config/global';
|
||||
|
||||
export default defineComponent({
|
||||
name: `${prefix}-footer`,
|
||||
setup() {
|
||||
return {
|
||||
prefix,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<search :layout="layout" />
|
||||
</div>
|
||||
</template>
|
||||
<menu-content v-show="layout !== 'side'" class="header-menu" :nav-data="menu" />
|
||||
<MenuContent v-show="layout !== 'side'" class="header-menu" :nav-data="menu" />
|
||||
<template #operations>
|
||||
<div class="operations-container">
|
||||
<!-- 搜索框 -->
|
||||
|
@ -63,11 +63,10 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed, ref } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { PropType, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import { useSettingStore } from '@/store';
|
||||
import { prefix } from '@/config/global';
|
||||
import tLogoFull from '@/assets/assets-logo-full.svg?component';
|
||||
import { MenuRoute } from '@/interface';
|
||||
|
@ -76,126 +75,96 @@ import Notice from './Notice.vue';
|
|||
import Search from './Search.vue';
|
||||
import MenuContent from './MenuContent';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
tLogoFull,
|
||||
Notice,
|
||||
Search,
|
||||
MenuContent,
|
||||
const props = defineProps({
|
||||
theme: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
props: {
|
||||
theme: {
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
layout: {
|
||||
type: String as PropType<string>,
|
||||
default: 'top',
|
||||
},
|
||||
showLogo: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
menu: {
|
||||
type: Array as PropType<MenuRoute[]>,
|
||||
default: () => [],
|
||||
},
|
||||
isFixed: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
isCompact: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
maxLevel: {
|
||||
type: Number as PropType<number>,
|
||||
default: 3,
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'top',
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
|
||||
const toggleSettingPanel = () => {
|
||||
store.commit('setting/toggleSettingPanel', true);
|
||||
};
|
||||
|
||||
const active = computed(() => {
|
||||
const route = useRoute();
|
||||
if (!route.path) {
|
||||
return '';
|
||||
}
|
||||
return route.path
|
||||
.split('/')
|
||||
.filter((item, index) => index <= props.maxLevel && index > 0)
|
||||
.map((item) => `/${item}`)
|
||||
.join('');
|
||||
});
|
||||
|
||||
const showMenu = computed(() => !(props.layout === 'mix' && props.showLogo));
|
||||
|
||||
const layoutCls = computed(() => [`${prefix}-header-layout`]);
|
||||
|
||||
const menuCls = computed(() => {
|
||||
const { isFixed, layout, isCompact } = props;
|
||||
return [
|
||||
{
|
||||
[`${prefix}-header-menu`]: !isFixed,
|
||||
[`${prefix}-header-menu-fixed`]: isFixed,
|
||||
[`${prefix}-header-menu-fixed-side`]: layout === 'side' && isFixed,
|
||||
[`${prefix}-header-menu-fixed-side-compact`]: layout === 'side' && isFixed && isCompact,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const userVisible = ref(false);
|
||||
const userVisibleChange = (value: boolean) => {
|
||||
userVisible.value = value;
|
||||
};
|
||||
|
||||
const changeCollapsed = () => {
|
||||
store.commit('setting/toggleSidebarCompact');
|
||||
};
|
||||
const isSidebarCompact = computed(() => store.state.setting.isSidebarCompact);
|
||||
|
||||
const handleNav = (url) => {
|
||||
router.push(url);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
router.push(`/login?redirect=${router.currentRoute.value.fullPath}`);
|
||||
};
|
||||
|
||||
const navToGitHub = () => {
|
||||
window.open('https://github.com/tencent/tdesign-vue-next-starter');
|
||||
};
|
||||
|
||||
const navToHelper = () => {
|
||||
window.open('http://tdesign.tencent.com/starter/docs/get-started');
|
||||
};
|
||||
|
||||
return {
|
||||
isSidebarCompact,
|
||||
toggleSettingPanel,
|
||||
active,
|
||||
showMenu,
|
||||
layoutCls,
|
||||
userVisible,
|
||||
userVisibleChange,
|
||||
menuCls,
|
||||
changeCollapsed,
|
||||
handleNav,
|
||||
handleLogout,
|
||||
navToGitHub,
|
||||
navToHelper,
|
||||
};
|
||||
showLogo: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
menu: {
|
||||
type: Array as PropType<MenuRoute[]>,
|
||||
default: () => [],
|
||||
},
|
||||
isFixed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCompact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxLevel: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const toggleSettingPanel = () => {
|
||||
settingStore.updateConfig({
|
||||
showSettingPanel: true,
|
||||
});
|
||||
};
|
||||
|
||||
const active = computed(() => {
|
||||
const route = useRoute();
|
||||
if (!route.path) {
|
||||
return '';
|
||||
}
|
||||
return route.path
|
||||
.split('/')
|
||||
.filter((item, index) => index <= props.maxLevel && index > 0)
|
||||
.map((item) => `/${item}`)
|
||||
.join('');
|
||||
});
|
||||
|
||||
const layoutCls = computed(() => [`${prefix}-header-layout`]);
|
||||
|
||||
const menuCls = computed(() => {
|
||||
const { isFixed, layout, isCompact } = props;
|
||||
return [
|
||||
{
|
||||
[`${prefix}-header-menu`]: !isFixed,
|
||||
[`${prefix}-header-menu-fixed`]: isFixed,
|
||||
[`${prefix}-header-menu-fixed-side`]: layout === 'side' && isFixed,
|
||||
[`${prefix}-header-menu-fixed-side-compact`]: layout === 'side' && isFixed && isCompact,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const changeCollapsed = () => {
|
||||
settingStore.updateConfig({
|
||||
isSidebarCompact: !settingStore.isSidebarCompact,
|
||||
});
|
||||
};
|
||||
|
||||
const handleNav = (url) => {
|
||||
router.push(url);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
router.push(`/login?redirect=${router.currentRoute.value.fullPath}`);
|
||||
};
|
||||
|
||||
const navToGitHub = () => {
|
||||
window.open('https://github.com/tencent/tdesign-vue-next-starter');
|
||||
};
|
||||
|
||||
const navToHelper = () => {
|
||||
window.open('http://tdesign.tencent.com/starter/docs/get-started');
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '@/style/variables.less';
|
||||
|
||||
.@{prefix}-header {
|
||||
&-layout {
|
||||
height: 64px;
|
||||
|
@ -225,7 +194,6 @@ export default defineComponent({
|
|||
height: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-menu {
|
||||
flex: 1 1 1;
|
||||
display: inline-flex;
|
||||
|
@ -236,7 +204,7 @@ export default defineComponent({
|
|||
align-items: center;
|
||||
margin-right: 12px;
|
||||
|
||||
.t-popup-reference {
|
||||
.t-popup__reference {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -260,7 +228,8 @@ export default defineComponent({
|
|||
.header-operate-left {
|
||||
display: flex;
|
||||
margin-left: 20px;
|
||||
align-items: center;
|
||||
align-items: normal;
|
||||
line-height: 0;
|
||||
|
||||
.collapsed-icon {
|
||||
font-size: 20px;
|
||||
|
|
|
@ -6,17 +6,19 @@ const getMenuList = (list: MenuRoute[], basePath?: string): MenuRoute[] => {
|
|||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
return list.map((item) => {
|
||||
const path = basePath ? `${basePath}/${item.path}` : item.path;
|
||||
return {
|
||||
path,
|
||||
title: item.meta?.title,
|
||||
icon: item.meta?.icon || '',
|
||||
children: getMenuList(item.children, path),
|
||||
meta: item.meta,
|
||||
redirect: item.redirect,
|
||||
};
|
||||
});
|
||||
return list
|
||||
.map((item) => {
|
||||
const path = basePath ? `${basePath}/${item.path}` : item.path;
|
||||
return {
|
||||
path,
|
||||
title: item.meta?.title,
|
||||
icon: item.meta?.icon || '',
|
||||
children: getMenuList(item.children, path),
|
||||
meta: item.meta,
|
||||
redirect: item.redirect,
|
||||
};
|
||||
})
|
||||
.filter((item) => item.meta && item.meta.hidden !== true);
|
||||
};
|
||||
|
||||
const renderIcon = (item) => {
|
||||
|
@ -35,6 +37,19 @@ const renderIcon = (item) => {
|
|||
const useRenderNav = (list: Array<MenuRoute>) => {
|
||||
return list.map((item) => {
|
||||
if (!item.children || !item.children.length || item.meta?.single) {
|
||||
const href = item.path.match(/(http|https):\/\/([\w.]+\/?)\S*/);
|
||||
if (href) {
|
||||
return (
|
||||
<t-menu-item
|
||||
href={href?.[0]}
|
||||
name={item.path}
|
||||
value={item.meta?.single ? item.redirect : item.path}
|
||||
icon={renderIcon(item)}
|
||||
>
|
||||
{item.title}
|
||||
</t-menu-item>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<t-menu-item
|
||||
name={item.path}
|
||||
|
|
|
@ -45,47 +45,35 @@
|
|||
</t-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useNotificationStore } from '@/store';
|
||||
import { NotificationItem } from '@/interface';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const { msgData } = store.state.notification;
|
||||
const router = useRouter();
|
||||
const store = useNotificationStore();
|
||||
const { msgData, unreadMsg } = storeToRefs(store);
|
||||
|
||||
const unreadMsg = computed(() => store.getters['notification/unreadMsg']);
|
||||
|
||||
const setRead = (type: string, item?: NotificationItem) => {
|
||||
const changeMsg = msgData;
|
||||
if (type === 'all') {
|
||||
changeMsg.forEach((e: NotificationItem) => {
|
||||
e.status = false;
|
||||
});
|
||||
} else {
|
||||
changeMsg.forEach((e: NotificationItem) => {
|
||||
if (e.id === item?.id) {
|
||||
e.status = false;
|
||||
}
|
||||
});
|
||||
const setRead = (type: string, item?: NotificationItem) => {
|
||||
const changeMsg = msgData.value;
|
||||
if (type === 'all') {
|
||||
changeMsg.forEach((e) => {
|
||||
e.status = false;
|
||||
});
|
||||
} else {
|
||||
changeMsg.forEach((e) => {
|
||||
if (e.id === item?.id) {
|
||||
e.status = false;
|
||||
}
|
||||
store.commit('notification/setMsgData', changeMsg);
|
||||
};
|
||||
});
|
||||
}
|
||||
store.setMsgData(changeMsg);
|
||||
};
|
||||
|
||||
const goDetail = () => {
|
||||
const router = useRouter();
|
||||
router.push('/detail/secondary');
|
||||
};
|
||||
|
||||
return {
|
||||
goDetail,
|
||||
unreadMsg,
|
||||
setRead,
|
||||
};
|
||||
},
|
||||
});
|
||||
const goDetail = () => {
|
||||
router.push('/detail/secondary');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -36,31 +36,21 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
layout: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const isSearchFocus = ref(false);
|
||||
const searchData = ref('');
|
||||
const changeSearchFocus = (value: boolean) => {
|
||||
if (!value) {
|
||||
searchData.value = '';
|
||||
}
|
||||
isSearchFocus.value = value;
|
||||
};
|
||||
return {
|
||||
isSearchFocus,
|
||||
searchData,
|
||||
changeSearchFocus,
|
||||
};
|
||||
},
|
||||
defineProps({
|
||||
layout: String,
|
||||
});
|
||||
|
||||
const isSearchFocus = ref(false);
|
||||
const searchData = ref('');
|
||||
const changeSearchFocus = (value: boolean) => {
|
||||
if (!value) {
|
||||
searchData.value = '';
|
||||
}
|
||||
isSearchFocus.value = value;
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '@/style/variables.less';
|
||||
|
@ -81,13 +71,22 @@ export default defineComponent({
|
|||
font-size: 20px !important;
|
||||
color: @text-color-primary !important;
|
||||
}
|
||||
.t-input__inner {
|
||||
.t-input {
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
transform: background @anim-duration-base linear;
|
||||
transition: background @anim-duration-base linear;
|
||||
.t-input__inner {
|
||||
transition: background @anim-duration-base linear;
|
||||
}
|
||||
.t-input__inner {
|
||||
background: none;
|
||||
}
|
||||
&:hover {
|
||||
background: @bg-color-secondarycontainer;
|
||||
.t-input__inner {
|
||||
background: @bg-color-secondarycontainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +94,7 @@ export default defineComponent({
|
|||
.header-search {
|
||||
width: 200px;
|
||||
transition: width @anim-duration-base @anim-time-fn-easing;
|
||||
.t-input__inner {
|
||||
.t-input {
|
||||
border: 0;
|
||||
padding-left: 40px;
|
||||
&:focus {
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
import { defineComponent, PropType, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import { prefix } from '@/config/global';
|
||||
import pgk from '../../../package.json';
|
||||
import MenuContent from './MenuContent';
|
||||
import tLogo from '@/assets/assets-t-logo.svg?component';
|
||||
import tLogoFull from '@/assets/assets-logo-full.svg?component';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
const MIN_POINT = 992 - 1;
|
||||
|
||||
const useComputed = (props) => {
|
||||
const store = useStore();
|
||||
|
||||
const collapsed = computed(() => store.state.setting.isSidebarCompact);
|
||||
const collapsed = computed(() => useSettingStore().isSidebarCompact);
|
||||
|
||||
const sideNavCls = computed(() => {
|
||||
const { isCompact } = props;
|
||||
|
@ -52,7 +50,6 @@ const useComputed = (props) => {
|
|||
export default defineComponent({
|
||||
name: 'SideNav',
|
||||
components: {
|
||||
MenuContent,
|
||||
tLogoFull,
|
||||
tLogo,
|
||||
},
|
||||
|
@ -87,16 +84,20 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const changeCollapsed = () => {
|
||||
store.commit('setting/toggleSidebarCompact');
|
||||
settingStore.updateConfig({
|
||||
isSidebarCompact: !settingStore.isSidebarCompact,
|
||||
});
|
||||
};
|
||||
|
||||
const autoCollapsed = () => {
|
||||
const isCompact = window.innerWidth <= MIN_POINT;
|
||||
store.commit('setting/showSidebarCompact', isCompact);
|
||||
settingStore.updateConfig({
|
||||
isSidebarCompact: isCompact,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -118,12 +119,6 @@ export default defineComponent({
|
|||
.join('');
|
||||
};
|
||||
|
||||
const routerChange = (path: string) => {
|
||||
router.push({
|
||||
path,
|
||||
});
|
||||
};
|
||||
|
||||
const goHome = () => {
|
||||
router.push('/dashboard/base');
|
||||
};
|
||||
|
@ -134,7 +129,6 @@ export default defineComponent({
|
|||
autoCollapsed,
|
||||
changeCollapsed,
|
||||
getActiveName,
|
||||
routerChange,
|
||||
goHome,
|
||||
};
|
||||
},
|
||||
|
@ -165,7 +159,7 @@ export default defineComponent({
|
|||
),
|
||||
}}
|
||||
>
|
||||
<menu-content navData={this.menu} />
|
||||
<MenuContent navData={this.menu} />
|
||||
</t-menu>
|
||||
<div class={`${prefix}-side-nav-placeholder${this.collapsed ? '-hidden' : ''}`}></div>
|
||||
</div>
|
||||
|
|
|
@ -1,135 +1,128 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import TdesignHeader from './components/Header.vue';
|
||||
import TdesignBreadcrumb from './components/Breadcrumb.vue';
|
||||
import TdesignFooter from './components/Footer.vue';
|
||||
import TdesignSideNav from './components/SideNav';
|
||||
import TdesignContent from './components/Content.vue';
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { usePermissionStore, useSettingStore } from '@/store';
|
||||
|
||||
import TDesignHeader from './components/Header.vue';
|
||||
import TDesignBreadcrumb from './components/Breadcrumb.vue';
|
||||
import TDesignFooter from './components/Footer.vue';
|
||||
import TDesignSideNav from './components/SideNav';
|
||||
import TDesignContent from './components/Content.vue';
|
||||
|
||||
import { prefix } from '@/config/global';
|
||||
import TdesignSetting from './setting.vue';
|
||||
import { SettingType, ClassName } from '@/interface';
|
||||
import '@/style/layout.less';
|
||||
|
||||
const name = `${prefix}-base-layout`;
|
||||
|
||||
export default defineComponent({
|
||||
name,
|
||||
components: {
|
||||
TdesignHeader,
|
||||
TdesignFooter,
|
||||
TdesignSideNav,
|
||||
TdesignSetting,
|
||||
TdesignBreadcrumb,
|
||||
TdesignContent,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
showSidebar: 'setting/showSidebar',
|
||||
showHeader: 'setting/showHeader',
|
||||
showHeaderLogo: 'setting/showHeaderLogo',
|
||||
showSidebarLogo: 'setting/showSidebarLogo',
|
||||
showFooter: 'setting/showFooter',
|
||||
mode: 'setting/mode',
|
||||
menuRouters: 'permission/routers',
|
||||
}),
|
||||
setting(): SettingType {
|
||||
return this.$store.state.setting;
|
||||
},
|
||||
mainLayoutCls(): ClassName {
|
||||
return [
|
||||
{
|
||||
't-layout-has-sider': this.showSidebar,
|
||||
},
|
||||
];
|
||||
},
|
||||
headerMenu() {
|
||||
const { layout, splitMenu } = this.$store.state.setting;
|
||||
const { menuRouters } = this;
|
||||
if (layout === 'mix') {
|
||||
if (splitMenu) {
|
||||
return menuRouters.map((menu) => ({
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const permissionStore = usePermissionStore();
|
||||
const settingStore = useSettingStore();
|
||||
const { routers: menuRouters } = storeToRefs(permissionStore);
|
||||
const setting = storeToRefs(settingStore);
|
||||
|
||||
const mainLayoutCls = computed(() => [
|
||||
{
|
||||
't-layout--with-sider': settingStore.showSidebar,
|
||||
},
|
||||
]);
|
||||
|
||||
const headerMenu = computed(() => {
|
||||
if (settingStore.layout === 'mix') {
|
||||
if (settingStore.splitMenu) {
|
||||
console.log(menuRouters);
|
||||
return menuRouters.value.map((menu) => ({
|
||||
...menu,
|
||||
children: [],
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return menuRouters;
|
||||
},
|
||||
sideMenu() {
|
||||
const { layout, splitMenu } = this.$store.state.setting;
|
||||
let { menuRouters } = this;
|
||||
return menuRouters.value;
|
||||
});
|
||||
|
||||
const sideMenu = computed(() => {
|
||||
const { layout, splitMenu } = settingStore;
|
||||
let newMenuRouters = menuRouters.value;
|
||||
if (layout === 'mix' && splitMenu) {
|
||||
menuRouters.forEach((menu) => {
|
||||
if (this.$route.path.indexOf(menu.path) === 0) {
|
||||
menuRouters = menu.children.map((subMenu) => ({ ...subMenu, path: `${menu.path}/${subMenu.path}` }));
|
||||
newMenuRouters.forEach((menu) => {
|
||||
if (route.path.indexOf(menu.path) === 0) {
|
||||
newMenuRouters = menu.children.map((subMenu) => ({ ...subMenu, path: `${menu.path}/${subMenu.path}` }));
|
||||
}
|
||||
});
|
||||
}
|
||||
return menuRouters;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
renderSidebar() {
|
||||
return newMenuRouters;
|
||||
});
|
||||
|
||||
const renderSidebar = () => {
|
||||
return (
|
||||
this.showSidebar && (
|
||||
<tdesign-side-nav
|
||||
showLogo={this.showSidebarLogo}
|
||||
layout={this.setting.layout}
|
||||
isFixed={this.setting.isSidebarFixed}
|
||||
menu={this.sideMenu}
|
||||
theme={this.mode}
|
||||
isCompact={this.setting.isSidebarCompact}
|
||||
settingStore.showSidebar && (
|
||||
<TDesignSideNav
|
||||
showLogo={settingStore.showSidebarLogo}
|
||||
layout={settingStore.layout}
|
||||
isFixed={settingStore.isSidebarFixed}
|
||||
menu={sideMenu.value}
|
||||
theme={settingStore.displayMode}
|
||||
isCompact={settingStore.isSidebarCompact}
|
||||
/>
|
||||
)
|
||||
);
|
||||
},
|
||||
renderHeader() {
|
||||
};
|
||||
|
||||
const renderHeader = () => {
|
||||
return (
|
||||
this.showHeader && (
|
||||
<tdesign-header
|
||||
showLogo={this.showHeaderLogo}
|
||||
theme={this.mode}
|
||||
layout={this.setting.layout}
|
||||
isFixed={this.setting.isHeaderFixed}
|
||||
menu={this.headerMenu}
|
||||
isCompact={this.setting.isSidebarCompact}
|
||||
settingStore.showHeader && (
|
||||
<TDesignHeader
|
||||
showLogo={settingStore.showHeaderLogo}
|
||||
theme={settingStore.displayMode}
|
||||
layout={settingStore.layout}
|
||||
isFixed={settingStore.isHeaderFixed}
|
||||
menu={headerMenu.value}
|
||||
isCompact={settingStore.isSidebarCompact}
|
||||
/>
|
||||
)
|
||||
);
|
||||
},
|
||||
renderContent() {
|
||||
const { showBreadcrumb } = this.setting;
|
||||
const { showFooter } = this;
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<t-footer class={`${prefix}-footer-layout`}>
|
||||
<TDesignFooter />
|
||||
</t-footer>
|
||||
);
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
const { showBreadcrumb, showFooter } = settingStore;
|
||||
return (
|
||||
<t-layout class={[`${prefix}-layout`]}>
|
||||
<t-content class={`${prefix}-content-layout`}>
|
||||
{showBreadcrumb && <tdesign-breadcrumb />}
|
||||
<TdesignContent />
|
||||
{showBreadcrumb && <TDesignBreadcrumb />}
|
||||
<TDesignContent />
|
||||
</t-content>
|
||||
{showFooter && this.renderFooter()}
|
||||
{showFooter && renderFooter()}
|
||||
</t-layout>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
renderFooter() {
|
||||
return (
|
||||
<t-footer class={`${prefix}-footer-layout`}>
|
||||
<tdesign-footer />
|
||||
</t-footer>
|
||||
);
|
||||
},
|
||||
return {
|
||||
setting,
|
||||
mainLayoutCls,
|
||||
renderSidebar,
|
||||
renderHeader,
|
||||
renderContent,
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
const { layout } = this.setting;
|
||||
const header = this.renderHeader();
|
||||
const sidebar = this.renderSidebar();
|
||||
const content = this.renderContent();
|
||||
|
||||
return (
|
||||
<div class={`${prefix}-wrapper`}>
|
||||
<div>
|
||||
{layout === 'side' ? (
|
||||
<t-layout class={this.mainLayoutCls} key="side">
|
||||
<t-aside>{sidebar}</t-aside>
|
||||
|
@ -141,7 +134,7 @@ export default defineComponent({
|
|||
<t-layout class={this.mainLayoutCls}>{[sidebar, content]}</t-layout>
|
||||
</t-layout>
|
||||
)}
|
||||
<tdesign-setting />
|
||||
<TdesignSetting />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
v-model:visible="showSettingPanel"
|
||||
size="408px"
|
||||
:footer="false"
|
||||
value="medium"
|
||||
header="页面配置"
|
||||
:close-btn="true"
|
||||
class="setting-drawer-container"
|
||||
|
@ -50,7 +49,7 @@
|
|||
:value="COLOR_OPTIONS[COLOR_OPTIONS.length - 1]"
|
||||
class="setting-layout-color-group dynamic-color-btn"
|
||||
>
|
||||
<color-container :value="COLOR_OPTIONS[COLOR_OPTIONS.length - 1]" />
|
||||
<ColorContainer :value="COLOR_OPTIONS[COLOR_OPTIONS.length - 1]" />
|
||||
</t-radio-button>
|
||||
</t-popup>
|
||||
</div>
|
||||
|
@ -60,7 +59,7 @@
|
|||
<t-radio-group v-model="formData.layout" default-vaule="top">
|
||||
<div v-for="(item, index) in LAYOUT_OPTION" :key="index" class="setting-layout-drawer">
|
||||
<t-radio-button :key="index" :value="item">
|
||||
<thumbnail :src="getThumbnailUrl(item)" />
|
||||
<Thumbnail :src="getThumbnailUrl(item)" />
|
||||
</t-radio-button>
|
||||
</div>
|
||||
</t-radio-group>
|
||||
|
@ -69,10 +68,7 @@
|
|||
<t-switch v-model="formData.splitMenu" />
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item v-show="formData.layout !== 'side'" label="固定 Header" name="isHeaderFixed">
|
||||
<t-switch v-model="formData.isHeaderFixed" />
|
||||
</t-form-item>
|
||||
<t-form-item v-show="formData.layout !== 'top'" label="固定 Sidebar" name="isSidebarFixed">
|
||||
<t-form-item v-show="formData.layout === 'mix'" label="固定 Sidebar" name="isSidebarFixed">
|
||||
<t-switch v-model="formData.isSidebarFixed" />
|
||||
</t-form-item>
|
||||
|
||||
|
@ -86,35 +82,35 @@
|
|||
<t-form-item label="显示 Footer" name="showFooter">
|
||||
<t-switch v-model="formData.showFooter" />
|
||||
</t-form-item>
|
||||
<t-form-item v-show="formData.showFooter && !formData.isSidebarFixed" label="footer 内收" name="footerPosition">
|
||||
<t-switch v-model="formData.isFooterAside" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
<div class="setting-info">
|
||||
<p>请复制后手动修改配置文件: /src/config/style.js</p>
|
||||
<p>请复制后手动修改配置文件: /src/config/style.ts</p>
|
||||
<t-button theme="primary" variant="text" @click="handleCopy"> 复制配置项 </t-button>
|
||||
</div>
|
||||
</div>
|
||||
</t-drawer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, watch, onMounted } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, watchEffect } from 'vue';
|
||||
import { ColorPicker } from 'vue-color-kit';
|
||||
import { MessagePlugin, PopupVisibleChangeContext } from 'tdesign-vue-next';
|
||||
import { Color } from 'tvision-color';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
import { useSettingStore } from '@/store';
|
||||
import Thumbnail from '@/components/thumbnail/index.vue';
|
||||
import ColorContainer from '@/components/color/index.vue';
|
||||
|
||||
import 'vue-color-kit/dist/vue-color-kit.css';
|
||||
|
||||
import STYLE_CONFIG from '@/config/style';
|
||||
import { insertThemeStylesheet, generateColorMap } from '@/config/color';
|
||||
|
||||
import Thumbnail from '@/components/thumbnail/index.vue';
|
||||
import ColorContainer from '@/components/color/index.vue';
|
||||
|
||||
import SettingDarkIcon from '@/assets/assets-setting-dark.svg';
|
||||
import SettingLightIcon from '@/assets/assets-setting-light.svg';
|
||||
import SettingAutoIcon from '@/assets/assets-setting-auto.svg';
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const LAYOUT_OPTION = ['side', 'top', 'mix'];
|
||||
const COLOR_OPTIONS = ['default', 'cyan', 'green', 'yellow', 'orange', 'red', 'pink', 'purple', 'dynamic'];
|
||||
const MODE_OPTIONS = [
|
||||
|
@ -123,123 +119,113 @@ const MODE_OPTIONS = [
|
|||
{ type: 'auto', text: '跟随系统' },
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutSetting',
|
||||
components: { Thumbnail, ColorContainer, ColorPicker },
|
||||
setup() {
|
||||
const formData = ref({ ...STYLE_CONFIG });
|
||||
const store = useStore();
|
||||
const colors = ref();
|
||||
const isColoPickerDisplay = ref(false);
|
||||
const formData = ref({ ...STYLE_CONFIG });
|
||||
const colors = ref();
|
||||
const isColoPickerDisplay = ref(false);
|
||||
|
||||
const showSettingPanel = computed({
|
||||
get() {
|
||||
return store.state.setting.showSettingPanel;
|
||||
},
|
||||
set(newVal) {
|
||||
store.commit('setting/toggleSettingPanel', newVal);
|
||||
},
|
||||
const showSettingPanel = computed({
|
||||
get() {
|
||||
return settingStore.showSettingPanel;
|
||||
},
|
||||
set(newVal: boolean) {
|
||||
settingStore.updateConfig({
|
||||
showSettingPanel: newVal,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const mode = computed(() => {
|
||||
return store.getters['setting/mode'];
|
||||
const mode = computed(() => {
|
||||
return settingStore.displayMode;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => colors.value,
|
||||
(newColor) => {
|
||||
const { hex } = newColor;
|
||||
|
||||
// hex 主题色
|
||||
const newPalette = Color.getPaletteByGradation({
|
||||
colors: [hex],
|
||||
step: 10,
|
||||
})[0];
|
||||
const { mode } = settingStore;
|
||||
const colorMap = generateColorMap(hex, newPalette, mode as 'light' | 'dark');
|
||||
|
||||
insertThemeStylesheet(hex, colorMap, mode as 'light' | 'dark');
|
||||
|
||||
settingStore.updateConfig({
|
||||
[hex]: colorMap,
|
||||
});
|
||||
settingStore.changeBrandTheme(hex);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => colors.value,
|
||||
(newColor) => {
|
||||
const { hex } = newColor;
|
||||
const { setting } = store.state;
|
||||
const changeColor = (val) => {
|
||||
const { hex } = val;
|
||||
// hex 主题色
|
||||
const newPalette = Color.getPaletteByGradation({
|
||||
colors: [hex],
|
||||
step: 10,
|
||||
})[0];
|
||||
const { mode } = settingStore;
|
||||
const colorMap = generateColorMap(hex, newPalette, mode as 'light' | 'dark');
|
||||
|
||||
// hex 主题色
|
||||
const newPalette = Color.getPaletteByGradation({
|
||||
colors: [hex],
|
||||
step: 10,
|
||||
})[0];
|
||||
const { mode } = store.state.setting;
|
||||
const colorMap = generateColorMap(hex, newPalette, mode);
|
||||
settingStore.updateConfig({
|
||||
[hex]: colorMap,
|
||||
});
|
||||
|
||||
store.commit('setting/addColor', { [hex]: colorMap });
|
||||
insertThemeStylesheet(hex, colorMap, mode as 'light' | 'dark');
|
||||
|
||||
insertThemeStylesheet(hex, colorMap, mode);
|
||||
settingStore.changeBrandTheme(hex);
|
||||
};
|
||||
|
||||
store.dispatch('setting/changeTheme', { ...setting, brandTheme: hex });
|
||||
},
|
||||
);
|
||||
const changeColor = (val) => {
|
||||
const { hex } = val;
|
||||
const { setting } = store.state;
|
||||
onMounted(() => {
|
||||
document.querySelector('.dynamic-color-btn').addEventListener('click', () => {
|
||||
isColoPickerDisplay.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
// hex 主题色
|
||||
const newPalette = Color.getPaletteByGradation({
|
||||
colors: [hex],
|
||||
step: 10,
|
||||
})[0];
|
||||
const { mode } = store.state.setting;
|
||||
const colorMap = generateColorMap(hex, newPalette, mode);
|
||||
const onPopupVisibleChange = (visible: boolean, context: PopupVisibleChangeContext) => {
|
||||
if (!visible && context.trigger === 'document') {
|
||||
isColoPickerDisplay.value = visible;
|
||||
}
|
||||
};
|
||||
|
||||
store.commit('setting/addColor', { [hex]: colorMap });
|
||||
|
||||
insertThemeStylesheet(hex, colorMap, mode);
|
||||
|
||||
store.dispatch('setting/changeTheme', { ...setting, brandTheme: hex });
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.querySelector('.dynamic-color-btn').addEventListener('click', () => {
|
||||
isColoPickerDisplay.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
const onPopupVisibleChange = (visible: boolean, context: PopupVisibleChangeContext) => {
|
||||
if (!visible && context.trigger === 'document') {
|
||||
isColoPickerDisplay.value = visible;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
const handleCopy = () => {
|
||||
const text = JSON.stringify(formData.value, null, 4);
|
||||
const { toClipboard } = useClipboard();
|
||||
toClipboard(text)
|
||||
.then(() => {
|
||||
MessagePlugin.closeAll();
|
||||
MessagePlugin.success('复制成功');
|
||||
};
|
||||
const getModeIcon = (mode: string) => {
|
||||
if (mode === 'light') {
|
||||
return SettingLightIcon;
|
||||
}
|
||||
if (mode === 'dark') {
|
||||
return SettingDarkIcon;
|
||||
}
|
||||
return SettingAutoIcon;
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
MessagePlugin.closeAll();
|
||||
MessagePlugin.error('复制失败');
|
||||
});
|
||||
};
|
||||
const getModeIcon = (mode: string) => {
|
||||
if (mode === 'light') {
|
||||
return SettingLightIcon;
|
||||
}
|
||||
if (mode === 'dark') {
|
||||
return SettingDarkIcon;
|
||||
}
|
||||
return SettingAutoIcon;
|
||||
};
|
||||
|
||||
const handleCloseDrawer = () => {
|
||||
store.commit('setting/toggleSettingPanel', false);
|
||||
};
|
||||
return {
|
||||
mode,
|
||||
changeColor,
|
||||
isColoPickerDisplay,
|
||||
onPopupVisibleChange,
|
||||
MODE_OPTIONS,
|
||||
LAYOUT_OPTION,
|
||||
COLOR_OPTIONS,
|
||||
formData,
|
||||
showSettingPanel,
|
||||
handleCopy,
|
||||
getModeIcon,
|
||||
handleCloseDrawer,
|
||||
getThumbnailUrl(name: string): string {
|
||||
return `https://tdesign.gtimg.com/tdesign-pro/setting/${name}.png`;
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
formData: {
|
||||
handler(newVal) {
|
||||
this.$store.dispatch('setting/changeTheme', newVal);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
const handleCloseDrawer = () => {
|
||||
settingStore.updateConfig({
|
||||
showSettingPanel: false,
|
||||
});
|
||||
};
|
||||
|
||||
const getThumbnailUrl = (name: string): string => {
|
||||
return `https://tdesign.gtimg.com/tdesign-pro/setting/${name}.png`;
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
settingStore.updateConfig(formData.value);
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
@ -331,7 +317,7 @@ export default defineComponent({
|
|||
.setting-container {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.t-radio-group.t-radio-group-medium {
|
||||
.t-radio-group.t-size-m {
|
||||
min-height: 32px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
@ -360,12 +346,12 @@ export default defineComponent({
|
|||
border: 2px solid @brand-color !important;
|
||||
}
|
||||
|
||||
.t-form__controls--content {
|
||||
.t-form__controls-content {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
||||
.t-form__controls--content {
|
||||
.t-form__controls-content {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { createApp } from 'vue';
|
||||
|
||||
import TDesign from 'tdesign-vue-next';
|
||||
import 'tdesign-vue-next/es/style/index.css';
|
||||
import VueClipboard from 'vue3-clipboard';
|
||||
import App from './App.vue';
|
||||
|
||||
import { store } from './store';
|
||||
import router from './router';
|
||||
import '@/style/index.less';
|
||||
import './permission';
|
||||
import App from './App.vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import dayjs from 'dayjs';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { Color } from 'tvision-color';
|
||||
import { getBrandColor, defaultLightColor, defaultDarkColor } from '@/config/color';
|
||||
import store from '@/store';
|
||||
|
||||
const { state } = store;
|
||||
import { getBrandColor, defaultLightColor, defaultDarkColor, TChartColor } from '@/config/color';
|
||||
import { getSettingStore } from '@/store';
|
||||
|
||||
/**
|
||||
* 依据主题类型获取颜色
|
||||
|
@ -14,8 +12,8 @@ const { state } = store;
|
|||
* @returns {}
|
||||
*/
|
||||
export function getColorFromTheme(theme: string) {
|
||||
const { setting } = state as any;
|
||||
const { colorList, mode } = setting;
|
||||
const settingStore = getSettingStore();
|
||||
const { colorList, mode } = settingStore;
|
||||
const isDarkMode = mode === 'dark';
|
||||
let themeColorList = [];
|
||||
const themeColor = getBrandColor(theme, colorList);
|
||||
|
@ -41,8 +39,9 @@ export function getColorFromTheme(theme: string) {
|
|||
|
||||
/** 图表颜色 */
|
||||
function chartListColor(): Array<string> {
|
||||
const { setting } = state as any;
|
||||
const res = getColorFromTheme(setting.brandTheme);
|
||||
const settingStore = getSettingStore();
|
||||
const { brandTheme } = settingStore;
|
||||
const res = getColorFromTheme(brandTheme);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -149,19 +148,19 @@ export function constructInitDashboardDataset(type: string) {
|
|||
{
|
||||
value: 135,
|
||||
itemStyle: {
|
||||
color: chartListColor()[1],
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 118,
|
||||
itemStyle: {
|
||||
color: chartListColor()[1],
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 60,
|
||||
itemStyle: {
|
||||
color: chartListColor()[1],
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -178,7 +177,7 @@ export function constructInitDataset({
|
|||
dateTime = [],
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { dateTime: Array<string> } & Record<string, string>) {
|
||||
}: { dateTime: Array<string> } & TChartColor) {
|
||||
// const dataset: Array<Array<string>> = [['时间'], ['入库'], ['出库']];
|
||||
const divideNum = 10;
|
||||
const timeArray = [];
|
||||
|
@ -276,7 +275,7 @@ export function getSmoothLineDataSet({
|
|||
dateTime = [],
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { dateTime?: Array<string> } & Record<string, string>) {
|
||||
}: { dateTime?: Array<string> } & TChartColor) {
|
||||
let dateArray: Array<string> = ['00:00', '02:00', '04:00', '06:00'];
|
||||
if (dateTime.length > 0) {
|
||||
const divideNum = 7;
|
||||
|
@ -383,7 +382,7 @@ export function getFolderLineDataSet({
|
|||
dateTime = [],
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { dateTime?: Array<string> } & Record<string, string>) {
|
||||
}: { dateTime?: Array<string> } & TChartColor) {
|
||||
let dateArray: Array<string> = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
||||
if (dateTime.length > 0) {
|
||||
const divideNum = 7;
|
||||
|
@ -543,7 +542,7 @@ export function getLineChartDataSet({
|
|||
dateTime = [],
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { dateTime?: Array<string> } & Record<string, string>) {
|
||||
}: { dateTime?: Array<string> } & TChartColor) {
|
||||
const divideNum = 10;
|
||||
const timeArray = [];
|
||||
const inArray = [];
|
||||
|
@ -679,7 +678,7 @@ export function getScatterDataSet({
|
|||
dateTime = [],
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { dateTime?: Array<string> } & Record<string, string>): any {
|
||||
}: { dateTime?: Array<string> } & TChartColor): any {
|
||||
const divideNum = 40;
|
||||
const timeArray = [];
|
||||
const inArray = [];
|
||||
|
@ -975,7 +974,7 @@ export function get2ColBarChartDataSet({
|
|||
isMonth = false,
|
||||
placeholderColor,
|
||||
borderColor,
|
||||
}: { isMonth?: boolean } & Record<string, string>) {
|
||||
}: { isMonth?: boolean } & TChartColor) {
|
||||
let lastYearListCopy = lastYearList.concat([]);
|
||||
let thisYearListCopy = lastYearList.concat([]);
|
||||
|
||||
|
@ -1077,7 +1076,8 @@ export function getPieChartDataSet({
|
|||
radius = 42,
|
||||
textColor,
|
||||
placeholderColor,
|
||||
}: { radius: number } & Record<string, string>) {
|
||||
containerColor,
|
||||
}: { radius?: number } & Record<string, string>) {
|
||||
return {
|
||||
color: chartListColor(),
|
||||
tooltip: {
|
||||
|
@ -1110,6 +1110,10 @@ export function getPieChartDataSet({
|
|||
selectedMode: true,
|
||||
hoverAnimation: true,
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
borderColor: containerColor,
|
||||
borderWidth: 1,
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'center',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<t-row :gutter="[16, 16]">
|
||||
<t-col v-for="(item, index) in PANE_LIST" :key="item.title" :xs="6" :xl="3">
|
||||
<card :subtitle="item.title" :style="{ height: '168px' }" :class="{ 'main-color': index == 0 }" size="small">
|
||||
<card :subtitle="item.title" size="small" :style="{ height: '168px' }" :class="{ 'main-color': index == 0 }">
|
||||
<div class="dashboard-item">
|
||||
<div class="dashboard-item-top">
|
||||
<span :style="{ fontSize: `${resizeTime * 36}px` }">{{ item.number }}</span>
|
||||
|
@ -202,14 +202,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, watch, ref, onUnmounted } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { onMounted, watch, ref, onUnmounted, nextTick, computed } from 'vue';
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
import { TooltipComponent, LegendComponent, GridComponent } from 'echarts/components';
|
||||
import { PieChart, LineChart, BarChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { useSettingStore } from '@/store';
|
||||
import { LAST_7_DAYS } from '@/utils/date';
|
||||
|
||||
// 导入样式
|
||||
|
@ -228,7 +228,7 @@ import { PANE_LIST, SALE_TEND_LIST, BUY_TEND_LIST, SALE_COLUMNS, BUY_COLUMNS } f
|
|||
echarts.use([TooltipComponent, LegendComponent, PieChart, GridComponent, LineChart, BarChart, CanvasRenderer]);
|
||||
|
||||
const getThisMonth = (checkedValues?: string[]) => {
|
||||
let date;
|
||||
let date: Date;
|
||||
if (!checkedValues || checkedValues.length === 0) {
|
||||
date = new Date();
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}`;
|
||||
|
@ -241,167 +241,150 @@ const getThisMonth = (checkedValues?: string[]) => {
|
|||
return `${date.getFullYear()}-${startMonth} 至 ${date2.getFullYear()}-${endMonth}`;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardBase',
|
||||
components: {
|
||||
Card,
|
||||
Trend,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const resizeTime = ref(1);
|
||||
const store = useSettingStore();
|
||||
const resizeTime = ref(1);
|
||||
|
||||
const { chartColors } = store.state.setting;
|
||||
const chartColors = computed(() => store.chartColors);
|
||||
|
||||
// moneyCharts
|
||||
let moneyContainer: HTMLElement;
|
||||
let moneyChart: echarts.ECharts;
|
||||
const renderMoneyChart = () => {
|
||||
if (!moneyContainer) {
|
||||
moneyContainer = document.getElementById('moneyContainer');
|
||||
}
|
||||
moneyChart = echarts.init(moneyContainer);
|
||||
moneyChart.setOption(constructInitDashboardDataset('line'));
|
||||
};
|
||||
// moneyCharts
|
||||
let moneyContainer: HTMLElement;
|
||||
let moneyChart: echarts.ECharts;
|
||||
const renderMoneyChart = () => {
|
||||
if (!moneyContainer) {
|
||||
moneyContainer = document.getElementById('moneyContainer');
|
||||
}
|
||||
moneyChart = echarts.init(moneyContainer);
|
||||
moneyChart.setOption(constructInitDashboardDataset('line'));
|
||||
};
|
||||
|
||||
// refundCharts
|
||||
let refundContainer: HTMLElement;
|
||||
let refundChart: echarts.ECharts;
|
||||
const renderRefundChart = () => {
|
||||
if (!refundContainer) {
|
||||
refundContainer = document.getElementById('refundContainer');
|
||||
}
|
||||
refundChart = echarts.init(refundContainer);
|
||||
refundChart.setOption(constructInitDashboardDataset('bar'));
|
||||
};
|
||||
// refundCharts
|
||||
let refundContainer: HTMLElement;
|
||||
let refundChart: echarts.ECharts;
|
||||
const renderRefundChart = () => {
|
||||
if (!refundContainer) {
|
||||
refundContainer = document.getElementById('refundContainer');
|
||||
}
|
||||
refundChart = echarts.init(refundContainer);
|
||||
refundChart.setOption(constructInitDashboardDataset('bar'));
|
||||
};
|
||||
|
||||
// stokeCharts
|
||||
let stokeContainer: HTMLElement;
|
||||
let stokeChart: echarts.ECharts;
|
||||
const renderStokeChart = () => {
|
||||
if (!stokeContainer) {
|
||||
stokeContainer = document.getElementById('stokeContainer');
|
||||
}
|
||||
stokeChart = echarts.init(stokeContainer);
|
||||
stokeChart.setOption(constructInitDataset({ dateTime: LAST_7_DAYS, ...chartColors }));
|
||||
};
|
||||
// stokeCharts
|
||||
let stokeContainer: HTMLElement;
|
||||
let stokeChart: echarts.ECharts;
|
||||
const renderStokeChart = () => {
|
||||
if (!stokeContainer) {
|
||||
stokeContainer = document.getElementById('stokeContainer');
|
||||
}
|
||||
stokeChart = echarts.init(stokeContainer);
|
||||
stokeChart.setOption(constructInitDataset({ dateTime: LAST_7_DAYS, ...chartColors.value }));
|
||||
};
|
||||
|
||||
// monitorChart
|
||||
let monitorContainer: HTMLElement;
|
||||
let monitorChart: echarts.ECharts;
|
||||
const renderMonitorChart = () => {
|
||||
if (!monitorContainer) {
|
||||
monitorContainer = document.getElementById('monitorContainer');
|
||||
}
|
||||
monitorChart = echarts.init(monitorContainer);
|
||||
monitorChart.setOption(getLineChartDataSet({ ...chartColors }));
|
||||
};
|
||||
// monitorChart
|
||||
let monitorContainer: HTMLElement;
|
||||
let monitorChart: echarts.ECharts;
|
||||
const renderMonitorChart = () => {
|
||||
if (!monitorContainer) {
|
||||
monitorContainer = document.getElementById('monitorContainer');
|
||||
}
|
||||
monitorChart = echarts.init(monitorContainer);
|
||||
monitorChart.setOption(getLineChartDataSet({ ...chartColors.value }));
|
||||
};
|
||||
|
||||
// monitorChart
|
||||
let countContainer: HTMLElement;
|
||||
let countChart: echarts.ECharts;
|
||||
const renderCountChart = () => {
|
||||
if (!countContainer) {
|
||||
countContainer = document.getElementById('countContainer');
|
||||
}
|
||||
countChart = echarts.init(countContainer);
|
||||
countChart.setOption(getPieChartDataSet(chartColors));
|
||||
};
|
||||
// monitorChart
|
||||
let countContainer: HTMLElement;
|
||||
let countChart: echarts.ECharts;
|
||||
const renderCountChart = () => {
|
||||
if (!countContainer) {
|
||||
countContainer = document.getElementById('countContainer');
|
||||
}
|
||||
countChart = echarts.init(countContainer);
|
||||
countChart.setOption(getPieChartDataSet(chartColors.value));
|
||||
};
|
||||
|
||||
const renderCharts = () => {
|
||||
renderMoneyChart();
|
||||
renderRefundChart();
|
||||
renderStokeChart();
|
||||
renderMonitorChart();
|
||||
renderCountChart();
|
||||
};
|
||||
const renderCharts = () => {
|
||||
renderMoneyChart();
|
||||
renderRefundChart();
|
||||
renderStokeChart();
|
||||
renderMonitorChart();
|
||||
renderCountChart();
|
||||
};
|
||||
|
||||
// chartSize update
|
||||
const updateContainer = () => {
|
||||
if (document.documentElement.clientWidth >= 1400 && document.documentElement.clientWidth < 1920) {
|
||||
resizeTime.value = Number((document.documentElement.clientWidth / 2080).toFixed(2));
|
||||
} else if (document.documentElement.clientWidth < 1080) {
|
||||
resizeTime.value = Number((document.documentElement.clientWidth / 1080).toFixed(2));
|
||||
} else {
|
||||
resizeTime.value = 1;
|
||||
}
|
||||
moneyChart.resize({
|
||||
width: resizeTime.value * 120,
|
||||
height: resizeTime.value * 66,
|
||||
});
|
||||
refundChart.resize({
|
||||
width: resizeTime.value * 120,
|
||||
height: resizeTime.value * 66,
|
||||
});
|
||||
stokeChart.resize({
|
||||
width: stokeContainer.clientWidth,
|
||||
height: stokeContainer.clientHeight,
|
||||
});
|
||||
monitorChart.resize({
|
||||
width: monitorContainer.clientWidth,
|
||||
height: resizeTime.value * 326,
|
||||
});
|
||||
countChart.resize({
|
||||
width: resizeTime.value * 326,
|
||||
height: resizeTime.value * 326,
|
||||
});
|
||||
};
|
||||
// chartSize update
|
||||
const updateContainer = () => {
|
||||
if (document.documentElement.clientWidth >= 1400 && document.documentElement.clientWidth < 1920) {
|
||||
resizeTime.value = Number((document.documentElement.clientWidth / 2080).toFixed(2));
|
||||
} else if (document.documentElement.clientWidth < 1080) {
|
||||
resizeTime.value = Number((document.documentElement.clientWidth / 1080).toFixed(2));
|
||||
} else {
|
||||
resizeTime.value = 1;
|
||||
}
|
||||
moneyChart.resize({
|
||||
width: resizeTime.value * 120,
|
||||
height: resizeTime.value * 66,
|
||||
});
|
||||
refundChart.resize({
|
||||
width: resizeTime.value * 120,
|
||||
height: resizeTime.value * 42,
|
||||
});
|
||||
stokeChart.resize({
|
||||
width: stokeContainer.clientWidth,
|
||||
height: stokeContainer.clientHeight,
|
||||
});
|
||||
monitorChart.resize({
|
||||
width: monitorContainer.clientWidth,
|
||||
height: resizeTime.value * 326,
|
||||
});
|
||||
countChart.resize({
|
||||
width: resizeTime.value * 326,
|
||||
height: resizeTime.value * 326,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
renderCharts();
|
||||
updateContainer();
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
const currentMonth = ref(getThisMonth());
|
||||
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([refundChart, stokeChart, monitorChart, countChart]);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => store.state.setting.mode,
|
||||
() => {
|
||||
[moneyChart, refundChart, stokeChart, monitorChart, countChart].forEach((item) => {
|
||||
item.dispose();
|
||||
});
|
||||
|
||||
renderCharts();
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
resizeTime,
|
||||
currentMonth,
|
||||
LAST_7_DAYS,
|
||||
PANE_LIST,
|
||||
BUY_TEND_LIST,
|
||||
SALE_TEND_LIST,
|
||||
SALE_COLUMNS,
|
||||
BUY_COLUMNS,
|
||||
onCurrencyChange(checkedValues: string[]) {
|
||||
currentMonth.value = getThisMonth(checkedValues);
|
||||
monitorChart.setOption(getLineChartDataSet({ dateTime: checkedValues, ...chartColors }));
|
||||
},
|
||||
onStokeDataChange(checkedValues: string[]) {
|
||||
stokeChart.setOption(constructInitDataset({ dateTime: checkedValues, ...chartColors }));
|
||||
},
|
||||
rehandleClickOp(val: MouseEvent) {
|
||||
console.log(val);
|
||||
},
|
||||
getRankClass(index: number) {
|
||||
return ['dashboard-rank', { 'dashboard-rank__top': index < 3 }];
|
||||
},
|
||||
};
|
||||
},
|
||||
onMounted(() => {
|
||||
renderCharts();
|
||||
nextTick(() => {
|
||||
updateContainer();
|
||||
});
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
const currentMonth = ref(getThisMonth());
|
||||
|
||||
watch(
|
||||
() => store.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([refundChart, stokeChart, monitorChart, countChart]);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => store.mode,
|
||||
() => {
|
||||
[moneyChart, refundChart, stokeChart, monitorChart, countChart].forEach((item) => {
|
||||
item.dispose();
|
||||
});
|
||||
|
||||
renderCharts();
|
||||
},
|
||||
);
|
||||
|
||||
const onCurrencyChange = (checkedValues: string[]) => {
|
||||
currentMonth.value = getThisMonth(checkedValues);
|
||||
monitorChart.setOption(getLineChartDataSet({ dateTime: checkedValues, ...chartColors.value }));
|
||||
};
|
||||
const onStokeDataChange = (checkedValues: string[]) => {
|
||||
stokeChart.setOption(constructInitDataset({ dateTime: checkedValues, ...chartColors.value }));
|
||||
};
|
||||
const rehandleClickOp = (val: MouseEvent) => {
|
||||
console.log(val);
|
||||
};
|
||||
const getRankClass = (index: number) => {
|
||||
return ['dashboard-rank', { 'dashboard-rank__top': index < 3 }];
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import './index.less';
|
||||
|
|
|
@ -57,107 +57,97 @@
|
|||
</card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, onUnmounted, watch, computed } from 'vue';
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
|
||||
import { LineChart, ScatterChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import ProductCard from '@/pages/list/card/components/Card.vue';
|
||||
import ProductCard from '@/components/card/Card.vue';
|
||||
|
||||
import { changeChartsTheme, getFolderLineDataSet, getScatterDataSet } from '../base/index';
|
||||
import { PANE_LIST_DATA, PRODUCT_LIST } from './constants';
|
||||
import { LAST_7_DAYS } from '@/utils/date';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
import Trend from '@/components/trend/index.vue';
|
||||
import Card from '@/components/card/index.vue';
|
||||
|
||||
echarts.use([GridComponent, LegendComponent, TooltipComponent, LineChart, ScatterChart, CanvasRenderer]);
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DashboardDetail',
|
||||
components: {
|
||||
Card,
|
||||
Trend,
|
||||
ProductCard,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const { chartColors } = store.state.setting;
|
||||
// lineChart logic
|
||||
let lineContainer: HTMLElement;
|
||||
let lineChart: echarts.ECharts;
|
||||
const renderLineChart = () => {
|
||||
lineContainer = document.getElementById('lineContainer');
|
||||
lineChart = echarts.init(lineContainer);
|
||||
lineChart.setOption(getFolderLineDataSet({ ...chartColors }));
|
||||
};
|
||||
const store = useSettingStore();
|
||||
const chartColors = computed(() => store.chartColors);
|
||||
|
||||
// scatterChart logic
|
||||
let scatterContainer: HTMLElement;
|
||||
let scatterChart: echarts.ECharts;
|
||||
const renderScatterChart = () => {
|
||||
scatterContainer = document.getElementById('scatterContainer');
|
||||
scatterChart = echarts.init(scatterContainer);
|
||||
scatterChart.setOption(getScatterDataSet({ ...chartColors }));
|
||||
};
|
||||
// lineChart logic
|
||||
let lineContainer: HTMLElement;
|
||||
let lineChart: echarts.ECharts;
|
||||
const renderLineChart = () => {
|
||||
lineContainer = document.getElementById('lineContainer');
|
||||
lineChart = echarts.init(lineContainer);
|
||||
lineChart.setOption(getFolderLineDataSet({ ...chartColors.value }));
|
||||
};
|
||||
|
||||
// chartSize update
|
||||
const updateContainer = () => {
|
||||
lineChart?.resize({
|
||||
width: lineContainer.clientWidth,
|
||||
height: lineContainer.clientHeight,
|
||||
});
|
||||
scatterChart?.resize({
|
||||
width: scatterContainer.clientWidth,
|
||||
height: scatterContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
// scatterChart logic
|
||||
let scatterContainer: HTMLElement;
|
||||
let scatterChart: echarts.ECharts;
|
||||
const renderScatterChart = () => {
|
||||
scatterContainer = document.getElementById('scatterContainer');
|
||||
scatterChart = echarts.init(scatterContainer);
|
||||
scatterChart.setOption(getScatterDataSet({ ...chartColors.value }));
|
||||
};
|
||||
|
||||
const renderCharts = () => {
|
||||
renderScatterChart();
|
||||
renderLineChart();
|
||||
};
|
||||
// chartSize update
|
||||
const updateContainer = () => {
|
||||
lineChart?.resize({
|
||||
width: lineContainer.clientWidth,
|
||||
height: lineContainer.clientHeight,
|
||||
});
|
||||
scatterChart?.resize({
|
||||
width: scatterContainer.clientWidth,
|
||||
height: scatterContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
renderCharts();
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
const renderCharts = () => {
|
||||
renderScatterChart();
|
||||
renderLineChart();
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.state.setting.mode,
|
||||
() => {
|
||||
renderCharts();
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([lineChart, scatterChart]);
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
LAST_7_DAYS,
|
||||
PRODUCT_LIST,
|
||||
PANE_LIST_DATA,
|
||||
onSatisfyChange() {
|
||||
scatterChart.setOption(getScatterDataSet({ ...chartColors }));
|
||||
},
|
||||
onMaterialChange(value: string[]) {
|
||||
const { chartColors } = store.state.setting;
|
||||
lineChart.setOption(getFolderLineDataSet({ dateTime: value, ...chartColors }));
|
||||
},
|
||||
};
|
||||
},
|
||||
onMounted(() => {
|
||||
renderCharts();
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
nextTick(() => {
|
||||
updateContainer();
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.mode,
|
||||
() => {
|
||||
renderCharts();
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => store.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([lineChart, scatterChart]);
|
||||
},
|
||||
);
|
||||
|
||||
const onSatisfyChange = () => {
|
||||
scatterChart.setOption(getScatterDataSet({ ...chartColors.value }));
|
||||
};
|
||||
|
||||
const onMaterialChange = (value: string[]) => {
|
||||
const chartColors = computed(() => store.chartColors);
|
||||
lineChart.setOption(getFolderLineDataSet({ dateTime: value, ...chartColors.value }));
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -109,91 +109,71 @@
|
|||
</t-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { prefix } from '@/config/global';
|
||||
import { BASE_INFO_DATA, TABLE_COLUMNS_DATA, PRODUCT_LIST } from './constants';
|
||||
import { BASE_INFO_DATA, TABLE_COLUMNS_DATA as columns, PRODUCT_LIST } from './constants';
|
||||
import request from '@/utils/request';
|
||||
import { ResDataType } from '@/interface';
|
||||
|
||||
import Card from '@/components/card/index.vue';
|
||||
import Product from './components/Product.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DetailAdvanced',
|
||||
components: {
|
||||
Card,
|
||||
Product,
|
||||
},
|
||||
setup() {
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 10,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const updateCurrent = ref(0);
|
||||
|
||||
const stepUpdate = () => {
|
||||
setInterval(() => {
|
||||
if (updateCurrent.value > 5) {
|
||||
updateCurrent.value = -1;
|
||||
}
|
||||
updateCurrent.value += 1;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-purchase-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
stepUpdate();
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
return {
|
||||
BASE_INFO_DATA,
|
||||
prefix,
|
||||
PRODUCT_LIST,
|
||||
columns: TABLE_COLUMNS_DATA,
|
||||
data,
|
||||
pagination,
|
||||
visible,
|
||||
updateCurrent,
|
||||
sortChange(val) {
|
||||
console.log(val);
|
||||
},
|
||||
rehandleChange(changeParams, triggerAndData) {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
},
|
||||
listClick() {
|
||||
visible.value = true;
|
||||
},
|
||||
deleteClickOp(columns) {
|
||||
data.value.splice(columns.index, 1);
|
||||
},
|
||||
onConfirm() {
|
||||
visible.value = false;
|
||||
},
|
||||
};
|
||||
},
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 10,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const updateCurrent = ref(0);
|
||||
|
||||
const stepUpdate = () => {
|
||||
setInterval(() => {
|
||||
if (updateCurrent.value > 5) {
|
||||
updateCurrent.value = -1;
|
||||
}
|
||||
updateCurrent.value += 1;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-purchase-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
stepUpdate();
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
const sortChange = (val) => {
|
||||
console.log(val);
|
||||
};
|
||||
const rehandleChange = (changeParams, triggerAndData) => {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
};
|
||||
const listClick = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const deleteClickOp = (columns) => {
|
||||
data.value.splice(columns.rowIndex, 1);
|
||||
};
|
||||
const onConfirm = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
</card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Card from '@/components/card/index.vue';
|
||||
|
||||
const BASE_INFO_DATA = [
|
||||
|
@ -108,16 +107,6 @@ const BASE_INFO_DATA = [
|
|||
type: null,
|
||||
},
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListBase',
|
||||
components: { Card },
|
||||
data() {
|
||||
return {
|
||||
BASE_INFO_DATA,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -68,17 +68,17 @@
|
|||
</t-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
import { TitleComponent, ToolboxComponent, TooltipComponent, GridComponent, LegendComponent } from 'echarts/components';
|
||||
import { BarChart, LineChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
import { changeChartsTheme, getSmoothLineDataSet, get2ColBarChartDataSet } from '../../dashboard/base/index';
|
||||
import { BASE_INFO_DATA, TABLE_COLUMNS } from './constants';
|
||||
import { BASE_INFO_DATA, TABLE_COLUMNS as columns } from './constants';
|
||||
|
||||
import { prefix } from '@/config/global';
|
||||
import Card from '@/components/card/index.vue';
|
||||
|
@ -96,118 +96,104 @@ echarts.use([
|
|||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DetailDeploy',
|
||||
components: { Card },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const store = useSettingStore();
|
||||
|
||||
const { chartColors } = store.state.setting;
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 10,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-project-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
const visible = ref(false);
|
||||
|
||||
// monitorChart logic
|
||||
let monitorContainer: HTMLElement;
|
||||
let monitorChart: echarts.ECharts;
|
||||
onMounted(() => {
|
||||
monitorContainer = document.getElementById('monitorContainer');
|
||||
monitorChart = echarts.init(monitorContainer);
|
||||
monitorChart.setOption(getSmoothLineDataSet({ ...chartColors }));
|
||||
setInterval(() => {
|
||||
monitorChart.setOption(getSmoothLineDataSet({ ...chartColors }));
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// dataChart logic
|
||||
let dataContainer: HTMLElement;
|
||||
let dataChart: echarts.ECharts;
|
||||
onMounted(() => {
|
||||
dataContainer = document.getElementById('dataContainer');
|
||||
dataChart = echarts.init(dataContainer);
|
||||
dataChart.setOption(get2ColBarChartDataSet({ ...chartColors }));
|
||||
});
|
||||
|
||||
const intervalTimer = null;
|
||||
|
||||
/// / chartSize update
|
||||
const updateContainer = () => {
|
||||
monitorChart.resize({
|
||||
width: monitorContainer.clientWidth,
|
||||
height: monitorContainer.clientHeight,
|
||||
});
|
||||
dataChart.resize({
|
||||
width: dataContainer.clientWidth,
|
||||
height: dataContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
clearInterval(intervalTimer);
|
||||
});
|
||||
|
||||
const onAlertChange = () => {
|
||||
dataChart.setOption(get2ColBarChartDataSet({ ...chartColors }));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([monitorChart, dataChart]);
|
||||
},
|
||||
);
|
||||
return {
|
||||
prefix,
|
||||
BASE_INFO_DATA,
|
||||
columns: TABLE_COLUMNS,
|
||||
data,
|
||||
pagination,
|
||||
visible,
|
||||
sortChange(val) {
|
||||
console.log(val);
|
||||
},
|
||||
rehandleChange(changeParams, triggerAndData) {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
},
|
||||
listClick() {
|
||||
visible.value = true;
|
||||
},
|
||||
onConfirm() {
|
||||
visible.value = false;
|
||||
},
|
||||
deleteClickOp(e) {
|
||||
data.value.splice(e.index, 1);
|
||||
},
|
||||
onAlertChange,
|
||||
};
|
||||
},
|
||||
const chartColors = computed(() => store.chartColors);
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 10,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-project-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
const visible = ref(false);
|
||||
|
||||
// monitorChart logic
|
||||
let monitorContainer: HTMLElement;
|
||||
let monitorChart: echarts.ECharts;
|
||||
onMounted(() => {
|
||||
monitorContainer = document.getElementById('monitorContainer');
|
||||
monitorChart = echarts.init(monitorContainer);
|
||||
monitorChart.setOption(getSmoothLineDataSet({ ...chartColors.value }));
|
||||
setInterval(() => {
|
||||
monitorChart.setOption(getSmoothLineDataSet({ ...chartColors.value }));
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// dataChart logic
|
||||
let dataContainer: HTMLElement;
|
||||
let dataChart: echarts.ECharts;
|
||||
onMounted(() => {
|
||||
dataContainer = document.getElementById('dataContainer');
|
||||
dataChart = echarts.init(dataContainer);
|
||||
dataChart.setOption(get2ColBarChartDataSet({ ...chartColors.value }));
|
||||
});
|
||||
|
||||
const intervalTimer = null;
|
||||
|
||||
/// / chartSize update
|
||||
const updateContainer = () => {
|
||||
monitorChart.resize({
|
||||
width: monitorContainer.clientWidth,
|
||||
height: monitorContainer.clientHeight,
|
||||
});
|
||||
dataChart.resize({
|
||||
width: dataContainer.clientWidth,
|
||||
height: dataContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
clearInterval(intervalTimer);
|
||||
});
|
||||
|
||||
const onAlertChange = () => {
|
||||
dataChart.setOption(get2ColBarChartDataSet({ ...chartColors.value }));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([monitorChart, dataChart]);
|
||||
},
|
||||
);
|
||||
|
||||
const sortChange = (val) => {
|
||||
console.log(val);
|
||||
};
|
||||
const rehandleChange = (changeParams, triggerAndData) => {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
};
|
||||
const listClick = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const onConfirm = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
const deleteClickOp = (e) => {
|
||||
data.value.splice(e.rowIndex, 1);
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('../base/index.less');
|
||||
|
|
|
@ -49,12 +49,13 @@
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, ComputedRef } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { NOTIFICATION_TYPES } from '@/constants';
|
||||
import { NotificationItem } from '@/interface';
|
||||
import EmptyIcon from '@/assets/assets-empty.svg?component';
|
||||
import { useNotificationStore } from '@/store';
|
||||
|
||||
const TAB_LIST = [
|
||||
{
|
||||
|
@ -71,68 +72,47 @@ const TAB_LIST = [
|
|||
},
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DetailSecondary',
|
||||
components: {
|
||||
EmptyIcon,
|
||||
},
|
||||
setup() {
|
||||
const tabValue = ref('msgData');
|
||||
const tabValue = ref('msgData');
|
||||
|
||||
const visible = ref(false);
|
||||
const selectedItem = ref<NotificationItem>();
|
||||
const visible = ref(false);
|
||||
const selectedItem = ref<NotificationItem>();
|
||||
|
||||
const store = useStore();
|
||||
const store = useNotificationStore();
|
||||
const { msgData, unreadMsg, readMsg } = storeToRefs(store);
|
||||
|
||||
const { msgData } = store.state.notification;
|
||||
|
||||
const msgDataList: ComputedRef<NotificationItem[]> = computed(() => {
|
||||
if (tabValue.value === 'msgData') return msgData;
|
||||
if (tabValue.value === 'unreadMsg') return store.getters['notification/unreadMsg'];
|
||||
if (tabValue.value === 'readMsg') return store.getters['notification/readMsg'];
|
||||
return [];
|
||||
});
|
||||
|
||||
const handleClickDeleteBtn = (item: NotificationItem) => {
|
||||
visible.value = true;
|
||||
selectedItem.value = item;
|
||||
};
|
||||
|
||||
const setReadStatus = (item: NotificationItem) => {
|
||||
const changeMsg = msgData;
|
||||
changeMsg.forEach((e: NotificationItem) => {
|
||||
if (e.id === item.id) {
|
||||
if (e.status) e.status = false;
|
||||
}
|
||||
});
|
||||
store.commit('notification/setMsgData', changeMsg);
|
||||
};
|
||||
|
||||
const deleteMsg = () => {
|
||||
const item = selectedItem.value;
|
||||
const changeMsg = msgData;
|
||||
changeMsg.forEach((e: NotificationItem, index: number) => {
|
||||
if (e.id === item?.id) {
|
||||
changeMsg.splice(index, 1);
|
||||
}
|
||||
});
|
||||
visible.value = false;
|
||||
store.commit('notification/setMsgData', changeMsg);
|
||||
};
|
||||
|
||||
return {
|
||||
TAB_LIST,
|
||||
NOTIFICATION_TYPES,
|
||||
visible,
|
||||
selectedItem,
|
||||
tabValue,
|
||||
msgDataList,
|
||||
handleClickDeleteBtn,
|
||||
setReadStatus,
|
||||
deleteMsg,
|
||||
};
|
||||
},
|
||||
const msgDataList = computed(() => {
|
||||
if (tabValue.value === 'msgData') return msgData.value;
|
||||
if (tabValue.value === 'unreadMsg') return unreadMsg.value;
|
||||
if (tabValue.value === 'readMsg') return readMsg.value;
|
||||
return [];
|
||||
});
|
||||
|
||||
const handleClickDeleteBtn = (item: NotificationItem) => {
|
||||
visible.value = true;
|
||||
selectedItem.value = item;
|
||||
};
|
||||
|
||||
const setReadStatus = (item: NotificationItem) => {
|
||||
const changeMsg = msgData.value;
|
||||
changeMsg.forEach((e: NotificationItem) => {
|
||||
if (e.id === item.id) {
|
||||
if (e.status) e.status = false;
|
||||
}
|
||||
});
|
||||
store.setMsgData(changeMsg);
|
||||
};
|
||||
|
||||
const deleteMsg = () => {
|
||||
const item = selectedItem.value;
|
||||
const changeMsg = msgData.value;
|
||||
changeMsg.forEach((e: NotificationItem, index: number) => {
|
||||
if (e.id === item?.id) {
|
||||
changeMsg.splice(index, 1);
|
||||
}
|
||||
});
|
||||
visible.value = false;
|
||||
store.setMsgData(changeMsg);
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -1,167 +1,148 @@
|
|||
<template>
|
||||
<div>
|
||||
<t-form
|
||||
ref="form"
|
||||
class="base-form"
|
||||
:data="formData"
|
||||
:rules="FORM_RULES"
|
||||
label-align="top"
|
||||
:label-width="100"
|
||||
@reset="onReset"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<div class="form-basic-container">
|
||||
<div class="form-basic-item">
|
||||
<div class="form-basic-container-title">合同信息</div>
|
||||
<!-- 表单内容 -->
|
||||
<t-form
|
||||
ref="form"
|
||||
class="base-form"
|
||||
:data="formData"
|
||||
:rules="FORM_RULES"
|
||||
label-align="top"
|
||||
:label-width="100"
|
||||
@reset="onReset"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<!-- 合同名称,合同类型 -->
|
||||
<t-row class="row-gap" :gutter="[16, 24]">
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同名称" name="name">
|
||||
<t-input v-model="formData.name" :style="{ width: '322px' }" placeholder="请输入内容" />
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同类型" name="type">
|
||||
<t-select
|
||||
v-model="formData.type"
|
||||
:style="{ width: '322px' }"
|
||||
placeholder="请选择类型"
|
||||
class="demo-select-base"
|
||||
clearable
|
||||
>
|
||||
<t-option v-for="(item, index) in TYPE_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
|
||||
<!-- 合同收付类型 -->
|
||||
<t-col :span="8">
|
||||
<t-form-item label="合同收付类型" name="payment">
|
||||
<t-radio-group v-model="formData.payment">
|
||||
<t-radio value="1"> 收款 </t-radio>
|
||||
<t-radio value="2"> 付款 </t-radio>
|
||||
</t-radio-group>
|
||||
<span class="space-item" />
|
||||
<!-- 合同名称,合同类型 -->
|
||||
<t-row class="row-gap" :gutter="[16, 24]">
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同名称" name="name">
|
||||
<t-input v-model="formData.name" :style="{ width: '322px' }" placeholder="请输入内容" />
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同类型" name="type">
|
||||
<t-select
|
||||
v-model="formData.type"
|
||||
:style="{ width: '322px' }"
|
||||
placeholder="请选择类型"
|
||||
class="demo-select-base"
|
||||
clearable
|
||||
>
|
||||
<t-option v-for="(item, index) in TYPE_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
|
||||
<!-- 合同收付类型 -->
|
||||
<t-col :span="8">
|
||||
<t-form-item label="合同收付类型" name="payment">
|
||||
<t-radio-group v-model="formData.payment">
|
||||
<t-radio value="1"> 收款 </t-radio>
|
||||
<t-radio value="2"> 付款 </t-radio>
|
||||
</t-radio-group>
|
||||
<span class="space-item" />
|
||||
<div>
|
||||
<t-input placeholder="请输入金额" :style="{ width: '160px' }" />
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
</div>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
|
||||
<t-col :span="6">
|
||||
<t-form-item label="甲方" name="partyA">
|
||||
<t-select
|
||||
v-model="formData.partyA"
|
||||
:style="{ width: '322px' }"
|
||||
class="demo-select-base"
|
||||
placeholder="请选择类型"
|
||||
clearable
|
||||
>
|
||||
<t-option
|
||||
v-for="(item, index) in PARTY_A_OPTIONS"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="乙方" name="partyB">
|
||||
<t-select
|
||||
v-model="formData.partyB"
|
||||
:style="{ width: '322px' }"
|
||||
placeholder="请选择类型"
|
||||
class="demo-select-base"
|
||||
clearable
|
||||
>
|
||||
<t-option
|
||||
v-for="(item, index) in PARTY_B_OPTIONS"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同签订日期" name="signDate">
|
||||
<t-date-picker
|
||||
v-model="formData.signDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同生效日期" name="startDate">
|
||||
<t-date-picker
|
||||
v-model="formData.startDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同结束日期" name="endDate">
|
||||
<t-date-picker
|
||||
v-model="formData.endDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="" name="files">
|
||||
<t-upload
|
||||
v-model="formData.files"
|
||||
action="https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
|
||||
tips="请上传pdf文件,大小在60M以内"
|
||||
:size-limit="{ size: 60, unit: 'MB' }"
|
||||
:format-response="formatResponse"
|
||||
:before-upload="beforeUpload"
|
||||
@fail="handleFail"
|
||||
>
|
||||
<t-button class="form-submit-upload-btn" variant="outline"> 上传合同文件 </t-button>
|
||||
</t-upload>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
</t-row>
|
||||
</t-form>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="甲方" name="partyA">
|
||||
<t-select
|
||||
v-model="formData.partyA"
|
||||
:style="{ width: '322px' }"
|
||||
class="demo-select-base"
|
||||
placeholder="请选择类型"
|
||||
clearable
|
||||
>
|
||||
<t-option v-for="(item, index) in PARTY_A_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="乙方" name="partyB">
|
||||
<t-select
|
||||
v-model="formData.partyB"
|
||||
:style="{ width: '322px' }"
|
||||
placeholder="请选择类型"
|
||||
class="demo-select-base"
|
||||
clearable
|
||||
>
|
||||
<t-option v-for="(item, index) in PARTY_B_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||
{{ item.label }}
|
||||
</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同签订日期" name="signDate">
|
||||
<t-date-picker
|
||||
v-model="formData.signDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同生效日期" name="startDate">
|
||||
<t-date-picker
|
||||
v-model="formData.startDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="合同结束日期" name="endDate">
|
||||
<t-date-picker
|
||||
v-model="formData.endDate"
|
||||
:style="{ width: '322px' }"
|
||||
theme="primary"
|
||||
mode="date"
|
||||
separator="/"
|
||||
/>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
<t-col :span="6">
|
||||
<t-form-item label="" name="files">
|
||||
<t-upload
|
||||
v-model="formData.files"
|
||||
action="https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
|
||||
tips="请上传pdf文件,大小在60M以内"
|
||||
:size-limit="{ size: 60, unit: 'MB' }"
|
||||
:format-response="formatResponse"
|
||||
:before-upload="beforeUpload"
|
||||
@fail="handleFail"
|
||||
>
|
||||
<t-button class="form-submit-upload-btn" variant="outline"> 上传合同文件 </t-button>
|
||||
</t-upload>
|
||||
</t-form-item>
|
||||
</t-col>
|
||||
</t-row>
|
||||
|
||||
<div class="form-basic-container-title form-title-gap">其它信息</div>
|
||||
<t-form
|
||||
ref="form"
|
||||
class="base-form"
|
||||
:data="formData"
|
||||
:rules="FORM_RULES"
|
||||
label-align="top"
|
||||
:label-width="100"
|
||||
@reset="onReset"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<t-form-item label="备注" name="comment">
|
||||
<t-textarea v-model="formData.comment" :height="124" placeholder="请输入备注" />
|
||||
</t-form-item>
|
||||
<t-form-item label="公证人">
|
||||
<t-avatar-group>
|
||||
<t-avatar>D</t-avatar>
|
||||
<t-avatar>S</t-avatar>
|
||||
<t-avatar>+</t-avatar>
|
||||
</t-avatar-group>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
|
||||
<t-form-item label="备注" name="comment">
|
||||
<t-textarea v-model="formData.comment" :height="124" placeholder="请输入备注" />
|
||||
</t-form-item>
|
||||
<t-form-item label="公证人">
|
||||
<t-avatar-group>
|
||||
<t-avatar>D</t-avatar>
|
||||
<t-avatar>S</t-avatar>
|
||||
<t-avatar>+</t-avatar>
|
||||
</t-avatar-group>
|
||||
</t-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -173,53 +154,41 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t-form>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { FORM_RULES, INITIAL_DATA, TYPE_OPTIONS, PARTY_A_OPTIONS, PARTY_B_OPTIONS } from './constants';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FormBase',
|
||||
setup() {
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
|
||||
return {
|
||||
TYPE_OPTIONS,
|
||||
PARTY_A_OPTIONS,
|
||||
PARTY_B_OPTIONS,
|
||||
FORM_RULES,
|
||||
formData,
|
||||
onReset() {
|
||||
MessagePlugin.warning('取消新建');
|
||||
},
|
||||
onSubmit({ validateResult }) {
|
||||
if (validateResult === true) {
|
||||
MessagePlugin.success('新建成功');
|
||||
}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!/\.(pdf)$/.test(file.name)) {
|
||||
MessagePlugin.warning('请上传pdf文件');
|
||||
return false;
|
||||
}
|
||||
if (file.size > 60 * 1024 * 1024) {
|
||||
MessagePlugin.warning('上传文件不能大于60M');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleFail({ file }) {
|
||||
MessagePlugin.error(`文件 ${file.name} 上传失败`);
|
||||
},
|
||||
// 用于格式化接口响应值,error 会被用于上传失败的提示文字;url 表示文件/图片地址
|
||||
formatResponse(res) {
|
||||
return { ...res, error: '上传失败,请重试', url: res.url };
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
const onReset = () => {
|
||||
MessagePlugin.warning('取消新建');
|
||||
};
|
||||
const onSubmit = ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
MessagePlugin.success('新建成功');
|
||||
}
|
||||
};
|
||||
const beforeUpload = (file) => {
|
||||
if (!/\.(pdf)$/.test(file.name)) {
|
||||
MessagePlugin.warning('请上传pdf文件');
|
||||
return false;
|
||||
}
|
||||
if (file.size > 60 * 1024 * 1024) {
|
||||
MessagePlugin.warning('上传文件不能大于60M');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const handleFail = ({ file }) => {
|
||||
MessagePlugin.error(`文件 ${file.name} 上传失败`);
|
||||
};
|
||||
// 用于格式化接口响应值,error 会被用于上传失败的提示文字;url 表示文件/图片地址
|
||||
const formatResponse = (res) => {
|
||||
return { ...res, error: '上传失败,请重试', url: res.url };
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -139,8 +139,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ValidateResultContext } from 'tdesign-vue-next';
|
||||
import Card from '@/components/card/index.vue';
|
||||
|
@ -155,53 +155,36 @@ import {
|
|||
INITIAL_DATA3,
|
||||
} from './constants';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FormStep',
|
||||
components: { Card },
|
||||
setup() {
|
||||
const formData1 = ref({ ...INITIAL_DATA1 });
|
||||
const formData2 = ref({ ...INITIAL_DATA2 });
|
||||
const formData3 = ref({ ...INITIAL_DATA3 });
|
||||
const activeForm = ref(0);
|
||||
const formData1 = ref({ ...INITIAL_DATA1 });
|
||||
const formData2 = ref({ ...INITIAL_DATA2 });
|
||||
const formData3 = ref({ ...INITIAL_DATA3 });
|
||||
const activeForm = ref(0);
|
||||
|
||||
const amount = computed(() => {
|
||||
if (formData1.value.name === '1') {
|
||||
return '565421';
|
||||
}
|
||||
if (formData1.value.name === '2') {
|
||||
return '278821';
|
||||
}
|
||||
if (formData1.value.name === '3') {
|
||||
return '109824';
|
||||
}
|
||||
return '--';
|
||||
});
|
||||
|
||||
return {
|
||||
NAME_OPTIONS,
|
||||
TYPE_OPTIONS,
|
||||
ADDRESS_OPTIONS,
|
||||
FORM_RULES,
|
||||
formData1,
|
||||
formData2,
|
||||
formData3,
|
||||
activeForm,
|
||||
amount,
|
||||
onSubmit(result: ValidateResultContext<FormData>, val: number) {
|
||||
if (result.validateResult === true) {
|
||||
activeForm.value = val;
|
||||
}
|
||||
},
|
||||
onReset(val: number) {
|
||||
activeForm.value = val;
|
||||
},
|
||||
complete() {
|
||||
const router = useRouter();
|
||||
router.replace({ path: '/detail/advanced' });
|
||||
},
|
||||
};
|
||||
},
|
||||
const amount = computed(() => {
|
||||
if (formData1.value.name === '1') {
|
||||
return '565421';
|
||||
}
|
||||
if (formData1.value.name === '2') {
|
||||
return '278821';
|
||||
}
|
||||
if (formData1.value.name === '3') {
|
||||
return '109824';
|
||||
}
|
||||
return '--';
|
||||
});
|
||||
|
||||
const onSubmit = (result: ValidateResultContext<FormData>, val: number) => {
|
||||
if (result.validateResult === true) {
|
||||
activeForm.value = val;
|
||||
}
|
||||
};
|
||||
const onReset = (val: number) => {
|
||||
activeForm.value = val;
|
||||
};
|
||||
const complete = () => {
|
||||
const router = useRouter();
|
||||
router.replace({ path: '/detail/advanced' });
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
<t-button variant="base" theme="default" :disabled="!selectedRowKeys.length"> 导出合同 </t-button>
|
||||
<p v-if="!!selectedRowKeys.length" class="selected-count">已选{{ selectedRowKeys.length }}项</p>
|
||||
</div>
|
||||
<t-input v-model="searchValue" class="search-input" placeholder="请输入你需要搜索的内容" clearable>
|
||||
<template #suffix-icon>
|
||||
<search-icon size="20px" />
|
||||
</template>
|
||||
</t-input>
|
||||
<div class="search-input">
|
||||
<t-input v-model="searchValue" placeholder="请输入你需要搜索的内容" clearable>
|
||||
<template #suffix-icon>
|
||||
<search-icon size="20px" />
|
||||
</template>
|
||||
</t-input>
|
||||
</div>
|
||||
</t-row>
|
||||
|
||||
<t-table
|
||||
|
@ -64,8 +66,8 @@
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, computed } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { SearchIcon } from 'tdesign-icons-vue-next';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
|
@ -78,121 +80,96 @@ import request from '@/utils/request';
|
|||
|
||||
import { COLUMNS } from './constants';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListBaseCard',
|
||||
components: {
|
||||
Card,
|
||||
SearchIcon,
|
||||
Trend,
|
||||
},
|
||||
setup() {
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 20,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const searchValue = ref('');
|
||||
|
||||
const dataLoading = ref(false);
|
||||
const fetchData = async () => {
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIdx = ref(-1);
|
||||
const confirmBody = computed(() => {
|
||||
if (deleteIdx.value > -1) {
|
||||
const { name } = data.value[deleteIdx.value];
|
||||
return `删除后,${name}的所有合同信息将被清空,且无法恢复`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const confirmVisible = ref(false);
|
||||
|
||||
const selectedRowKeys = ref([1, 2]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const resetIdx = () => {
|
||||
deleteIdx.value = -1;
|
||||
};
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
// 真实业务请发起请求
|
||||
data.value.splice(deleteIdx.value - 1, 1);
|
||||
pagination.value.total = data.value.length;
|
||||
const selectedIdx = selectedRowKeys.value.indexOf(deleteIdx.value);
|
||||
if (selectedIdx > -1) {
|
||||
selectedRowKeys.value.splice(selectedIdx, 1);
|
||||
}
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
return {
|
||||
CONTRACT_STATUS,
|
||||
CONTRACT_TYPES,
|
||||
CONTRACT_PAYMENT_TYPES,
|
||||
COLUMNS,
|
||||
data,
|
||||
searchValue,
|
||||
dataLoading,
|
||||
pagination,
|
||||
confirmBody,
|
||||
confirmVisible,
|
||||
rowKey: 'index',
|
||||
onConfirmDelete,
|
||||
onCancel,
|
||||
selectedRowKeys,
|
||||
rehandleSelectChange(val: number[]) {
|
||||
selectedRowKeys.value = val;
|
||||
},
|
||||
rehandlePageChange(curr, pageInfo) {
|
||||
console.log('分页变化', curr, pageInfo);
|
||||
},
|
||||
rehandleChange(changeParams, triggerAndData) {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
},
|
||||
handleClickDetail() {
|
||||
router.push('/detail/base');
|
||||
},
|
||||
handleSetupContract() {
|
||||
router.push('/form/base');
|
||||
},
|
||||
handleClickDelete({ row }) {
|
||||
deleteIdx.value = row.index;
|
||||
confirmVisible.value = true;
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {},
|
||||
const data = ref([]);
|
||||
const pagination = ref({
|
||||
defaultPageSize: 20,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
|
||||
const searchValue = ref('');
|
||||
|
||||
const dataLoading = ref(false);
|
||||
const fetchData = async () => {
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIdx = ref(-1);
|
||||
const confirmBody = computed(() => {
|
||||
if (deleteIdx.value > -1) {
|
||||
const { name } = data.value[deleteIdx.value];
|
||||
return `删除后,${name}的所有合同信息将被清空,且无法恢复`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const confirmVisible = ref(false);
|
||||
|
||||
const selectedRowKeys = ref([1, 2]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const resetIdx = () => {
|
||||
deleteIdx.value = -1;
|
||||
};
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
// 真实业务请发起请求
|
||||
data.value.splice(deleteIdx.value, 1);
|
||||
pagination.value.total = data.value.length;
|
||||
const selectedIdx = selectedRowKeys.value.indexOf(deleteIdx.value);
|
||||
if (selectedIdx > -1) {
|
||||
selectedRowKeys.value.splice(selectedIdx, 1);
|
||||
}
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
const rowKey = 'index';
|
||||
|
||||
const rehandleSelectChange = (val: number[]) => {
|
||||
selectedRowKeys.value = val;
|
||||
};
|
||||
const rehandlePageChange = (curr, pageInfo) => {
|
||||
console.log('分页变化', curr, pageInfo);
|
||||
};
|
||||
const rehandleChange = (changeParams, triggerAndData) => {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
};
|
||||
const handleClickDetail = () => {
|
||||
router.push('/detail/base');
|
||||
};
|
||||
const handleSetupContract = () => {
|
||||
router.push('/form/base');
|
||||
};
|
||||
const handleClickDelete = (row: { rowIndex: any }) => {
|
||||
deleteIdx.value = row.rowIndex;
|
||||
confirmVisible.value = true;
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables';
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
</t-form-item>
|
||||
<t-form-item label="产品状态" name="status">
|
||||
<t-radio-group v-model="formData.status">
|
||||
<t-radio value="0"> 已停用 </t-radio>
|
||||
<t-radio value="1"> 已启用 </t-radio>
|
||||
<t-radio value="0">已停用</t-radio>
|
||||
<t-radio value="1">已启用</t-radio>
|
||||
</t-radio-group>
|
||||
</t-form-item>
|
||||
<t-form-item label="产品描述" name="description">
|
||||
|
@ -26,16 +26,16 @@
|
|||
<t-textarea v-model="textareaValue" :style="{ width: '480px' }" placeholder="请输入内容" name="description" />
|
||||
</t-form-item>
|
||||
<t-form-item style="float: right">
|
||||
<t-button variant="outline" @click="onClickCloseBtn"> 取消 </t-button>
|
||||
<t-button theme="primary" type="submit"> 确定 </t-button>
|
||||
<t-button variant="outline" @click="onClickCloseBtn">取消</t-button>
|
||||
<t-button theme="primary" type="submit">确定</t-button>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</template>
|
||||
</t-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
|
||||
const INITIAL_DATA = {
|
||||
|
@ -53,72 +53,61 @@ const SELECT_OPTIONS = [
|
|||
{ label: 'CVM', value: '3' },
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const formVisible = ref(false);
|
||||
const formData = ref(props.data);
|
||||
const textareaValue = ref('');
|
||||
|
||||
const onSubmit = ({ result, firstError }) => {
|
||||
if (!firstError) {
|
||||
MessagePlugin.success('提交成功');
|
||||
formVisible.value = false;
|
||||
} else {
|
||||
console.log('Errors: ', result);
|
||||
MessagePlugin.warning(firstError);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickCloseBtn = () => {
|
||||
formVisible.value = false;
|
||||
formData.value = { ...INITIAL_DATA };
|
||||
};
|
||||
|
||||
watch(
|
||||
() => formVisible.value,
|
||||
(val) => {
|
||||
const { emit } = ctx;
|
||||
emit('update:visible', val);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
formVisible.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(val) => {
|
||||
formData.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
SELECT_OPTIONS,
|
||||
formVisible,
|
||||
formData,
|
||||
textareaValue,
|
||||
onSubmit,
|
||||
onClickCloseBtn,
|
||||
rules: {
|
||||
name: [{ required: true, message: '请输入产品名称', type: 'error' }],
|
||||
},
|
||||
};
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formVisible = ref(false);
|
||||
const formData = ref(props.data);
|
||||
const textareaValue = ref('');
|
||||
|
||||
const onSubmit = ({ result, firstError }) => {
|
||||
if (!firstError) {
|
||||
MessagePlugin.success('提交成功');
|
||||
formVisible.value = false;
|
||||
} else {
|
||||
console.log('Errors: ', result);
|
||||
MessagePlugin.warning(firstError);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickCloseBtn = () => {
|
||||
formVisible.value = false;
|
||||
formData.value = { ...INITIAL_DATA };
|
||||
};
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
watch(
|
||||
() => formVisible.value,
|
||||
(val) => {
|
||||
emit('update:visible', val);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
formVisible.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(val) => {
|
||||
formData.value = val;
|
||||
},
|
||||
);
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入产品名称', type: 'error' }],
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
<div>
|
||||
<div class="list-card-operation">
|
||||
<t-button @click="formDialogVisible = true"> 新建产品 </t-button>
|
||||
<t-input v-model="searchValue" class="search-input" placeholder="请输入你需要搜索的内容" clearable>
|
||||
<template #suffix-icon>
|
||||
<search-icon v-if="searchValue === ''" size="20px" />
|
||||
</template>
|
||||
</t-input>
|
||||
<div class="search-input">
|
||||
<t-input v-model="searchValue" placeholder="请输入你需要搜索的内容" clearable>
|
||||
<template #suffix-icon>
|
||||
<search-icon v-if="searchValue === ''" size="20px" />
|
||||
</template>
|
||||
</t-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dialog-form v-model:visible="formDialogVisible" :data="formData" />
|
||||
|
@ -58,11 +60,11 @@
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, onMounted } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { SearchIcon } from 'tdesign-icons-vue-next';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import Card from './components/Card.vue';
|
||||
import Card from '@/components/card/Card.vue';
|
||||
import DialogForm from './components/DialogForm.vue';
|
||||
import request from '@/utils/request';
|
||||
import { ResDataType } from '@/interface';
|
||||
|
@ -76,88 +78,68 @@ const INITIAL_DATA = {
|
|||
amount: 0,
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListBase',
|
||||
components: {
|
||||
SearchIcon,
|
||||
Card,
|
||||
DialogForm,
|
||||
},
|
||||
setup() {
|
||||
const pagination = ref({ current: 1, pageSize: 12, total: 0 });
|
||||
const deleteProduct = ref(undefined);
|
||||
const pagination = ref({ current: 1, pageSize: 12, total: 0 });
|
||||
const deleteProduct = ref(undefined);
|
||||
|
||||
const productList = ref([]);
|
||||
const dataLoading = ref(true);
|
||||
const productList = ref([]);
|
||||
const dataLoading = ref(true);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-card-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
productList.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-card-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
productList.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const confirmBody = computed(() =>
|
||||
deleteProduct.value ? `确认删除后${deleteProduct.value.name}的所有产品信息将被清空, 且无法恢复` : '',
|
||||
);
|
||||
const confirmBody = computed(() =>
|
||||
deleteProduct.value ? `确认删除后${deleteProduct.value.name}的所有产品信息将被清空, 且无法恢复` : '',
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const formDialogVisible = ref(false);
|
||||
const searchValue = ref('');
|
||||
const confirmVisible = ref(false);
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
|
||||
return {
|
||||
pagination,
|
||||
productList,
|
||||
dataLoading,
|
||||
formDialogVisible,
|
||||
confirmBody,
|
||||
searchValue,
|
||||
confirmVisible,
|
||||
formData,
|
||||
onPageSizeChange(size: number) {
|
||||
pagination.value.pageSize = size;
|
||||
pagination.value.current = 1;
|
||||
},
|
||||
onCurrentChange(current: number) {
|
||||
pagination.value.current = current;
|
||||
},
|
||||
handleDeleteItem(product) {
|
||||
confirmVisible.value = true;
|
||||
deleteProduct.value = product;
|
||||
},
|
||||
onConfirmDelete() {
|
||||
const { index } = deleteProduct.value;
|
||||
productList.value.splice(index - 1, 1);
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
},
|
||||
onCancel() {
|
||||
deleteProduct.value = undefined;
|
||||
formData.value = { ...INITIAL_DATA };
|
||||
},
|
||||
handleManageProduct(product) {
|
||||
formDialogVisible.value = true;
|
||||
formData.value = { ...product, status: product?.isSetup ? '1' : '0' };
|
||||
},
|
||||
};
|
||||
},
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const formDialogVisible = ref(false);
|
||||
const searchValue = ref('');
|
||||
const confirmVisible = ref(false);
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
|
||||
const onPageSizeChange = (size: number) => {
|
||||
pagination.value.pageSize = size;
|
||||
pagination.value.current = 1;
|
||||
};
|
||||
const onCurrentChange = (current: number) => {
|
||||
pagination.value.current = current;
|
||||
};
|
||||
const handleDeleteItem = (product) => {
|
||||
confirmVisible.value = true;
|
||||
deleteProduct.value = product;
|
||||
};
|
||||
const onConfirmDelete = () => {
|
||||
const { index } = deleteProduct.value;
|
||||
productList.value.splice(index - 1, 1);
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
};
|
||||
const onCancel = () => {
|
||||
deleteProduct.value = undefined;
|
||||
formData.value = { ...INITIAL_DATA };
|
||||
};
|
||||
const handleManageProduct = (product) => {
|
||||
formDialogVisible.value = true;
|
||||
formData.value = { ...product, status: product?.isSetup ? '1' : '0' };
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables.less';
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<t-form-item label="合同状态" name="status">
|
||||
<t-select
|
||||
v-model="formData.status"
|
||||
class="form-item-content`"
|
||||
class="form-item-content"
|
||||
:options="CONTRACT_STATUS_OPTIONS"
|
||||
placeholder="请选择合同状态"
|
||||
/>
|
||||
|
@ -39,7 +39,7 @@
|
|||
<t-form-item label="合同类型" name="type">
|
||||
<t-select
|
||||
v-model="formData.type"
|
||||
class="form-item-content`"
|
||||
class="form-item-content"
|
||||
:options="CONTRACT_TYPE_OPTIONS"
|
||||
placeholder="请选择合同类型"
|
||||
/>
|
||||
|
@ -94,7 +94,7 @@
|
|||
</t-table>
|
||||
<t-dialog
|
||||
v-model:visible="confirmVisible"
|
||||
header="是否确认删除该产品"
|
||||
header="确认删除当前所选合同?"
|
||||
:body="confirmBody"
|
||||
:on-cancel="onCancel"
|
||||
@confirm="onConfirmDelete"
|
||||
|
@ -102,8 +102,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, onMounted } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import Trend from '@/components/trend/index.vue';
|
||||
import request from '@/utils/request';
|
||||
|
@ -123,7 +123,6 @@ const COLUMNS = [
|
|||
fixed: 'left',
|
||||
minWidth: '300',
|
||||
align: 'left',
|
||||
ellipsis: true,
|
||||
colKey: 'name',
|
||||
},
|
||||
{ title: '合同状态', colKey: 'status', width: 200, cell: { col: 'status' } },
|
||||
|
@ -167,115 +166,89 @@ const searchForm = {
|
|||
type: '',
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListTable',
|
||||
components: {
|
||||
Trend,
|
||||
},
|
||||
setup() {
|
||||
const formData = ref({ ...searchForm });
|
||||
const tableConfig = {
|
||||
rowKey: 'index',
|
||||
verticalAlign: 'top',
|
||||
hover: true,
|
||||
};
|
||||
const pagination = ref({
|
||||
defaultPageSize: 20,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
const confirmVisible = ref(false);
|
||||
const formData = ref({ ...searchForm });
|
||||
const rowKey = 'index';
|
||||
const verticalAlign = 'top';
|
||||
const hover = true;
|
||||
|
||||
const data = ref([]);
|
||||
|
||||
const dataLoading = ref(false);
|
||||
const fetchData = async () => {
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIdx = ref(-1);
|
||||
const confirmBody = computed(() => {
|
||||
if (deleteIdx.value > -1) {
|
||||
const { no, name } = data.value[deleteIdx.value];
|
||||
return `产品编号:${no}, 产品名称: ${name}`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const resetIdx = () => {
|
||||
deleteIdx.value = -1;
|
||||
};
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
// 真实业务请发起请求
|
||||
data.value.splice(deleteIdx.value - 1, 1);
|
||||
pagination.value.total = data.value.length;
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
COLUMNS,
|
||||
CONTRACT_STATUS,
|
||||
CONTRACT_STATUS_OPTIONS,
|
||||
CONTRACT_TYPES,
|
||||
CONTRACT_TYPE_OPTIONS,
|
||||
CONTRACT_PAYMENT_TYPES,
|
||||
formData,
|
||||
pagination,
|
||||
confirmVisible,
|
||||
confirmBody,
|
||||
...tableConfig,
|
||||
onConfirmDelete,
|
||||
onCancel,
|
||||
dataLoading,
|
||||
handleClickDelete({ row }) {
|
||||
deleteIdx.value = row.index;
|
||||
confirmVisible.value = true;
|
||||
},
|
||||
onReset(val) {
|
||||
console.log(val);
|
||||
},
|
||||
onSubmit(val) {
|
||||
console.log(val);
|
||||
},
|
||||
rehandlePageChange(curr, pageInfo) {
|
||||
console.log('分页变化', curr, pageInfo);
|
||||
},
|
||||
rehandleChange(changeParams, triggerAndData) {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
},
|
||||
rehandleClickOp({ text, row }) {
|
||||
console.log(text, row);
|
||||
},
|
||||
};
|
||||
},
|
||||
const pagination = ref({
|
||||
defaultPageSize: 20,
|
||||
total: 100,
|
||||
defaultCurrent: 1,
|
||||
});
|
||||
const confirmVisible = ref(false);
|
||||
|
||||
const data = ref([]);
|
||||
|
||||
const dataLoading = ref(false);
|
||||
const fetchData = async () => {
|
||||
dataLoading.value = true;
|
||||
try {
|
||||
const res: ResDataType = await request.get('/api/get-list');
|
||||
if (res.code === 0) {
|
||||
const { list = [] } = res.data;
|
||||
data.value = list;
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
total: list.length,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
dataLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIdx = ref(-1);
|
||||
const confirmBody = computed(() => {
|
||||
if (deleteIdx.value > -1) {
|
||||
const { name } = data.value[deleteIdx.value];
|
||||
return `删除后,${name}的所有合同信息将被清空,且无法恢复`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const resetIdx = () => {
|
||||
deleteIdx.value = -1;
|
||||
};
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
// 真实业务请发起请求
|
||||
data.value.splice(deleteIdx.value, 1);
|
||||
pagination.value.total = data.value.length;
|
||||
confirmVisible.value = false;
|
||||
MessagePlugin.success('删除成功');
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
resetIdx();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
const handleClickDelete = ({ row }) => {
|
||||
deleteIdx.value = row.rowIndex;
|
||||
confirmVisible.value = true;
|
||||
};
|
||||
const onReset = (val) => {
|
||||
console.log(val);
|
||||
};
|
||||
const onSubmit = (val) => {
|
||||
console.log(val);
|
||||
};
|
||||
const rehandlePageChange = (curr, pageInfo) => {
|
||||
console.log('分页变化', curr, pageInfo);
|
||||
};
|
||||
const rehandleChange = (changeParams, triggerAndData) => {
|
||||
console.log('统一Change', changeParams, triggerAndData);
|
||||
};
|
||||
const rehandleClickOp = ({ text, row }) => {
|
||||
console.log(text, row);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
<template>
|
||||
<common-table />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import CommonTable from '../components/CommonTable.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListBase',
|
||||
components: {
|
||||
CommonTable,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -15,38 +15,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { SearchIcon } from 'tdesign-icons-vue-next';
|
||||
import { prefix } from '@/config/global';
|
||||
|
||||
import { TREE_DATA } from './constants';
|
||||
import CommonTable from '../components/CommonTable.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListTree',
|
||||
components: {
|
||||
SearchIcon,
|
||||
CommonTable,
|
||||
},
|
||||
setup() {
|
||||
const filterByText = ref();
|
||||
const filterText = ref();
|
||||
return {
|
||||
prefix,
|
||||
TREE_DATA,
|
||||
expanded: ['0', '0-0', '0-1', '0-2', '0-3', '0-4'],
|
||||
filterText,
|
||||
filterByText,
|
||||
onInput() {
|
||||
filterByText.value = (node) => {
|
||||
const rs = node.label.indexOf(filterText.value) >= 0;
|
||||
return rs;
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
const filterByText = ref();
|
||||
const filterText = ref();
|
||||
|
||||
const expanded = ['0', '0-0', '0-1', '0-2', '0-3', '0-4'];
|
||||
|
||||
const onInput = () => {
|
||||
filterByText.value = (node) => {
|
||||
const rs = node.label.indexOf(filterText.value) >= 0;
|
||||
return rs;
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables.less';
|
||||
|
|
|
@ -15,34 +15,24 @@
|
|||
</header>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import LogoFullIcon from '@/assets/assets-logo-full.svg?component';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
components: { LogoFullIcon },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const toggleSettingPanel = () => {
|
||||
store.commit('setting/toggleSettingPanel', true);
|
||||
};
|
||||
const settingStore = useSettingStore();
|
||||
const toggleSettingPanel = () => {
|
||||
settingStore.updateConfig({
|
||||
showSettingPanel: true,
|
||||
});
|
||||
};
|
||||
|
||||
const navToGitHub = () => {
|
||||
window.open('https://github.com/tencent/tdesign-vue-next-starter');
|
||||
};
|
||||
const navToGitHub = () => {
|
||||
window.open('https://github.com/tencent/tdesign-vue-next-starter');
|
||||
};
|
||||
|
||||
const navToHelper = () => {
|
||||
window.open('http://tdesign.tencent.com/starter/docs/get-started');
|
||||
};
|
||||
|
||||
return {
|
||||
toggleSettingPanel,
|
||||
navToGitHub,
|
||||
navToHelper,
|
||||
};
|
||||
},
|
||||
});
|
||||
const navToHelper = () => {
|
||||
window.open('http://tdesign.tencent.com/starter/docs/get-started');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -70,13 +70,15 @@
|
|||
</t-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import QrcodeVue from 'qrcode.vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { useCounter } from '@/hooks';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const INITIAL_DATA = {
|
||||
phone: '',
|
||||
|
@ -93,48 +95,32 @@ const FORM_RULES = {
|
|||
verifyCode: [{ required: true, message: '验证码必填', type: 'error' }],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { QrcodeVue },
|
||||
setup() {
|
||||
const type = ref('password');
|
||||
const type = ref('password');
|
||||
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
const showPsw = ref(false);
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
const showPsw = ref(false);
|
||||
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
|
||||
const switchType = (val: string) => {
|
||||
type.value = val;
|
||||
};
|
||||
const switchType = (val: string) => {
|
||||
type.value = val;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit = async ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
try {
|
||||
await store.dispatch('user/login', formData.value);
|
||||
MessagePlugin.success('登陆成功');
|
||||
router.push({
|
||||
path: '/dashboard/base',
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
MessagePlugin.error(e.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
const onSubmit = async ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
try {
|
||||
await userStore.login(formData.value);
|
||||
|
||||
return {
|
||||
FORM_RULES,
|
||||
formData,
|
||||
showPsw,
|
||||
type,
|
||||
switchType,
|
||||
countDown,
|
||||
handleCounter,
|
||||
onSubmit,
|
||||
};
|
||||
},
|
||||
});
|
||||
MessagePlugin.success('登陆成功');
|
||||
router.push({
|
||||
path: '/dashboard/base',
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
MessagePlugin.error(e.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -70,8 +70,8 @@
|
|||
</t-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { useCounter } from '@/hooks';
|
||||
|
||||
|
@ -93,47 +93,30 @@ const FORM_RULES = {
|
|||
verifyCode: [{ required: true, message: '验证码必填', type: 'error' }],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
setup(props, ctx) {
|
||||
const type = ref('phone');
|
||||
const emailOptions = ref([]);
|
||||
const type = ref('phone');
|
||||
|
||||
const form = ref();
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
const form = ref();
|
||||
const formData = ref({ ...INITIAL_DATA });
|
||||
|
||||
const showPsw = ref(false);
|
||||
const showPsw = ref(false);
|
||||
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
const [countDown, handleCounter] = useCounter();
|
||||
|
||||
const onSubmit = ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
if (!formData.value.checked) {
|
||||
MessagePlugin.error('请同意TDesign服务协议和TDesign 隐私声明');
|
||||
return;
|
||||
}
|
||||
MessagePlugin.success('注册成功');
|
||||
const { emit } = ctx;
|
||||
emit('registerSuccess');
|
||||
}
|
||||
};
|
||||
const emit = defineEmits(['registerSuccess']);
|
||||
|
||||
const switchType = (val) => {
|
||||
form.value.reset();
|
||||
type.value = val;
|
||||
};
|
||||
const onSubmit = ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
if (!formData.value.checked) {
|
||||
MessagePlugin.error('请同意TDesign服务协议和TDesign 隐私声明');
|
||||
return;
|
||||
}
|
||||
MessagePlugin.success('注册成功');
|
||||
emit('registerSuccess');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
FORM_RULES,
|
||||
formData,
|
||||
showPsw,
|
||||
form,
|
||||
type,
|
||||
emailOptions,
|
||||
countDown,
|
||||
handleCounter,
|
||||
onSubmit,
|
||||
switchType,
|
||||
};
|
||||
},
|
||||
});
|
||||
const switchType = (val) => {
|
||||
form.value.reset();
|
||||
type.value = val;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
background-position: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,34 +22,18 @@
|
|||
<footer class="copyright">Copyright @ 2021-2022 Tencent. All Rights Reserved</footer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import Login from './components/Login.vue';
|
||||
import Register from './components/Register.vue';
|
||||
import LoginHeader from './components/Header.vue';
|
||||
import TdesignSetting from '@/layouts/setting.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LoginIndex',
|
||||
components: {
|
||||
Login,
|
||||
Register,
|
||||
LoginHeader,
|
||||
TdesignSetting,
|
||||
},
|
||||
setup() {
|
||||
const type = ref('login');
|
||||
const switchType = (val: string) => {
|
||||
type.value = val;
|
||||
};
|
||||
|
||||
return {
|
||||
type,
|
||||
switchType,
|
||||
};
|
||||
},
|
||||
});
|
||||
const type = ref('login');
|
||||
const switchType = (val: string) => {
|
||||
type.value = val;
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
</result>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Result from '@/components/result/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Result403',
|
||||
components: { Result },
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
</result>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Result from '@/components/result/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Result404',
|
||||
components: { Result },
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
</result>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Result from '@/components/result/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Result500',
|
||||
components: { Result },
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -19,15 +19,9 @@
|
|||
</result>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Result from '@/components/result/index.vue';
|
||||
import Thumbnail from '@/components/thumbnail/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ResultBrowserIncompatible',
|
||||
components: { Result, Thumbnail },
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables.less';
|
||||
|
|
|
@ -4,20 +4,12 @@
|
|||
<div class="result-success-title">项目创建失败</div>
|
||||
<div class="result-success-describe">企业微信联系检查创建者权限,或返回修改</div>
|
||||
<div>
|
||||
<t-button theme="default" @click="() => $router.push('/form/base')">返回首页</t-button>
|
||||
<t-button @click="() => $router.push('/form/base')"> 返回修改 </t-button>
|
||||
<t-button @click="() => $router.push('/form/base')">返回修改</t-button>
|
||||
<t-button theme="default" @click="() => $router.push('/dashboard/base')">返回首页</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ResultSuccess',
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables.less';
|
||||
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
</result>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Result from '@/components/result/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ResultNetworkError',
|
||||
components: { Result },
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -10,14 +10,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ResultSuccess',
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '@/style/variables.less';
|
||||
|
||||
|
|
|
@ -89,13 +89,13 @@
|
|||
</t-col>
|
||||
</t-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, onUnmounted, watch, computed } from 'vue';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { useSettingStore } from '@/store';
|
||||
|
||||
import { LAST_7_DAYS } from '@/utils/date';
|
||||
import { USER_INFO_LIST, TEAM_MEMBERS, PRODUCT_LIST } from './constants';
|
||||
|
@ -109,84 +109,68 @@ import Card from '@/components/card/index.vue';
|
|||
|
||||
echarts.use([GridComponent, TooltipComponent, LineChart, CanvasRenderer, LegendComponent]);
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Card,
|
||||
},
|
||||
setup() {
|
||||
let lineContainer: HTMLElement;
|
||||
let lineChart: echarts.ECharts;
|
||||
const store = useStore();
|
||||
const { chartColors } = store.state.setting;
|
||||
let lineContainer: HTMLElement;
|
||||
let lineChart: echarts.ECharts;
|
||||
const store = useSettingStore();
|
||||
const chartColors = computed(() => store.chartColors);
|
||||
|
||||
const onLineChange = (value) => {
|
||||
lineChart.setOption(getFolderLineDataSet(value));
|
||||
};
|
||||
const onLineChange = (value) => {
|
||||
lineChart.setOption(getFolderLineDataSet(value));
|
||||
};
|
||||
|
||||
const initChart = () => {
|
||||
lineContainer = document.getElementById('lineContainer');
|
||||
lineChart = echarts.init(lineContainer);
|
||||
lineChart.setOption({
|
||||
grid: {
|
||||
x: 30, // 默认是80px
|
||||
y: 30, // 默认是60px
|
||||
x2: 10, // 默认80px
|
||||
y2: 30, // 默认60px
|
||||
},
|
||||
...getFolderLineDataSet({ ...chartColors }),
|
||||
});
|
||||
};
|
||||
const initChart = () => {
|
||||
lineContainer = document.getElementById('lineContainer');
|
||||
lineChart = echarts.init(lineContainer);
|
||||
lineChart.setOption({
|
||||
grid: {
|
||||
x: 30, // 默认是80px
|
||||
y: 30, // 默认是60px
|
||||
x2: 10, // 默认80px
|
||||
y2: 30, // 默认60px
|
||||
},
|
||||
...getFolderLineDataSet({ ...chartColors.value }),
|
||||
});
|
||||
};
|
||||
|
||||
const updateContainer = () => {
|
||||
lineChart?.resize({
|
||||
width: lineContainer.clientWidth,
|
||||
height: lineContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
const updateContainer = () => {
|
||||
lineChart?.resize({
|
||||
width: lineContainer.clientWidth,
|
||||
height: lineContainer.clientHeight,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
const getIcon = (type) => {
|
||||
switch (type) {
|
||||
case 'a':
|
||||
return ProductAIcon;
|
||||
case 'b':
|
||||
return ProductBIcon;
|
||||
case 'c':
|
||||
return ProductCIcon;
|
||||
case 'd':
|
||||
return ProductDIcon;
|
||||
default:
|
||||
return ProductAIcon;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => store.state.setting.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([lineChart]);
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
LAST_7_DAYS,
|
||||
USER_INFO_LIST,
|
||||
TEAM_MEMBERS,
|
||||
PRODUCT_LIST,
|
||||
onLineChange,
|
||||
getIcon,
|
||||
};
|
||||
},
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initChart();
|
||||
});
|
||||
window.addEventListener('resize', updateContainer, false);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateContainer);
|
||||
});
|
||||
|
||||
const getIcon = (type) => {
|
||||
switch (type) {
|
||||
case 'a':
|
||||
return ProductAIcon;
|
||||
case 'b':
|
||||
return ProductBIcon;
|
||||
case 'c':
|
||||
return ProductCIcon;
|
||||
case 'd':
|
||||
return ProductDIcon;
|
||||
default:
|
||||
return ProductAIcon;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => store.brandTheme,
|
||||
() => {
|
||||
changeChartsTheme([lineChart]);
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import url('./index.less');
|
||||
|
|
|
@ -2,42 +2,47 @@ import { MessagePlugin } from 'tdesign-vue-next';
|
|||
import NProgress from 'nprogress'; // progress bar
|
||||
import 'nprogress/nprogress.css'; // progress bar style
|
||||
|
||||
import store from '@/store';
|
||||
import { getPermissionStore, getUserStore } from '@/store';
|
||||
import router from '@/router';
|
||||
|
||||
const permissionStore = getPermissionStore();
|
||||
const userStore = getUserStore();
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
|
||||
const whiteListRouters = store.getters['permission/whiteListRouters'];
|
||||
const { whiteListRouters } = permissionStore;
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start();
|
||||
|
||||
const token = store.getters['user/token'];
|
||||
|
||||
const { token } = userStore;
|
||||
if (token) {
|
||||
if (to.path === '/login') {
|
||||
setTimeout(() => {
|
||||
store.dispatch('user/logout');
|
||||
store.dispatch('permission/restore');
|
||||
});
|
||||
userStore.logout();
|
||||
permissionStore.restore();
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
const roles = store.getters['user/roles'];
|
||||
const { roles } = userStore;
|
||||
|
||||
if (roles && roles.length > 0) {
|
||||
next();
|
||||
} else {
|
||||
try {
|
||||
await store.dispatch('user/getUserInfo');
|
||||
await userStore.getUserInfo();
|
||||
|
||||
await store.dispatch('permission/initRoutes', store.getters['user/roles']);
|
||||
const { roles } = userStore;
|
||||
|
||||
next({ ...to });
|
||||
await permissionStore.initRoutes(roles);
|
||||
|
||||
if (router.hasRoute(to.name)) {
|
||||
next();
|
||||
} else {
|
||||
next(`/`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
MessagePlugin.error(error);
|
||||
await store.commit('user/removeToken');
|
||||
next(`/login?redirect=${to.path}`);
|
||||
NProgress.done();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import baseRouters from './modules/base';
|
|||
import componentsRouters from './modules/components';
|
||||
import othersRouters from './modules/others';
|
||||
|
||||
// 关于单层路由,meta 中设置 { single: true } 即可为单层路由,{ hidden: true } 即可在侧边栏隐藏该路由
|
||||
|
||||
// 存放动态路由
|
||||
export const asyncRouterList: Array<RouteRecordRaw> = [...baseRouters, ...componentsRouters, ...othersRouters];
|
||||
|
||||
|
@ -19,17 +21,18 @@ const defaultRouterList: Array<RouteRecordRaw> = [
|
|||
redirect: '/dashboard/base',
|
||||
component: () => import('@/layouts/blank.vue'),
|
||||
},
|
||||
{
|
||||
path: '/:w+',
|
||||
name: '404Page',
|
||||
redirect: '/result/404',
|
||||
},
|
||||
];
|
||||
|
||||
export const page404 = {
|
||||
path: '/:w+',
|
||||
name: '404Page',
|
||||
redirect: '/result/404',
|
||||
};
|
||||
export const allRoutes = [...defaultRouterList, ...asyncRouterList];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: defaultRouterList,
|
||||
routes: allRoutes,
|
||||
scrollBehavior() {
|
||||
return {
|
||||
el: '#app',
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import { createStore } from 'vuex';
|
||||
import user from './modules/user';
|
||||
import notification from './modules/notification';
|
||||
import setting from './modules/setting';
|
||||
import permission from './modules/permission';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
export const store = createStore({
|
||||
modules: {
|
||||
user,
|
||||
setting,
|
||||
notification,
|
||||
permission,
|
||||
},
|
||||
});
|
||||
const store = createPinia();
|
||||
|
||||
export { store };
|
||||
|
||||
export * from './modules/notification';
|
||||
export * from './modules/permission';
|
||||
export * from './modules/user';
|
||||
export * from './modules/setting';
|
||||
|
||||
export default store;
|
||||
|
|
|
@ -1,85 +1,76 @@
|
|||
// 定义的state初始值
|
||||
import { defineStore } from 'pinia';
|
||||
import { NotificationItem } from '@/interface';
|
||||
|
||||
const state = {
|
||||
msgData: [
|
||||
{
|
||||
id: '123',
|
||||
content: '腾讯大厦一楼改造施工项目 已通过审核!',
|
||||
type: '合同动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'high',
|
||||
},
|
||||
{
|
||||
id: '124',
|
||||
content: '三季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '125',
|
||||
content: '2021-01-01 10:00的【国家电网线下签约】会议即将开始,请提前10分钟前往 会议室1 进行签到!',
|
||||
type: '会议通知',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'middle',
|
||||
},
|
||||
{
|
||||
id: '126',
|
||||
content: '一季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '127',
|
||||
content: '二季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '128',
|
||||
content: '三季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
type NotificationStateType = typeof state;
|
||||
type MsgDataType = typeof state.msgData;
|
||||
|
||||
const mutations = {
|
||||
setMsgData(state: NotificationStateType, data: MsgDataType) {
|
||||
state.msgData = data;
|
||||
const msgData = [
|
||||
{
|
||||
id: '123',
|
||||
content: '腾讯大厦一楼改造施工项目 已通过审核!',
|
||||
type: '合同动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'high',
|
||||
},
|
||||
};
|
||||
{
|
||||
id: '124',
|
||||
content: '三季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '125',
|
||||
content: '2021-01-01 10:00的【国家电网线下签约】会议即将开始,请提前10分钟前往 会议室1 进行签到!',
|
||||
type: '会议通知',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'middle',
|
||||
},
|
||||
{
|
||||
id: '126',
|
||||
content: '一季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '127',
|
||||
content: '二季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
{
|
||||
id: '128',
|
||||
content: '三季度生产原材料采购项目 开票成功!',
|
||||
type: '票务动态',
|
||||
status: true,
|
||||
collected: false,
|
||||
date: '2021-01-01 08:00',
|
||||
quality: 'low',
|
||||
},
|
||||
];
|
||||
|
||||
const getters = {
|
||||
unreadMsg: (state: NotificationStateType) => state.msgData.filter((item: NotificationItem) => item.status),
|
||||
readMsg: (state: NotificationStateType) => state.msgData.filter((item: NotificationItem) => !item.status),
|
||||
};
|
||||
type MsgDataType = typeof msgData;
|
||||
|
||||
const actions = {};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
};
|
||||
export const useNotificationStore = defineStore('notification', {
|
||||
state: () => ({
|
||||
msgData,
|
||||
}),
|
||||
getters: {
|
||||
unreadMsg: (state) => state.msgData.filter((item: NotificationItem) => item.status),
|
||||
readMsg: (state) => state.msgData.filter((item: NotificationItem) => !item.status),
|
||||
},
|
||||
actions: {
|
||||
setMsgData(data: MsgDataType) {
|
||||
this.msgData = data;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import router, { asyncRouterList, page404 } from '@/router';
|
||||
import { defineStore } from 'pinia';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import router, { asyncRouterList } from '@/router';
|
||||
import { store } from '@/store';
|
||||
|
||||
function filterPermissionsRouters(routes, roles) {
|
||||
function filterPermissionsRouters(routes: Array<RouteRecordRaw>, roles: Array<unknown>) {
|
||||
const res = [];
|
||||
const removeRoutes = [];
|
||||
routes.forEach((route) => {
|
||||
const children = [];
|
||||
route.children?.forEach((childRouter) => {
|
||||
const roleCode = childRouter.meta?.roleCode || childRouter.name;
|
||||
if (roles.indexOf(roleCode) !== -1) {
|
||||
children.push(childRouter);
|
||||
} else {
|
||||
removeRoutes.push(childRouter);
|
||||
}
|
||||
});
|
||||
if (children.length > 0) {
|
||||
|
@ -15,60 +21,46 @@ function filterPermissionsRouters(routes, roles) {
|
|||
res.push(route);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
return { accessedRouters: res, removeRoutes };
|
||||
}
|
||||
|
||||
const state = {
|
||||
whiteListRouters: ['/login'],
|
||||
routers: [],
|
||||
};
|
||||
export const usePermissionStore = defineStore('permission', {
|
||||
state: () => ({
|
||||
whiteListRouters: ['/login'],
|
||||
routers: [],
|
||||
removeRoutes: [],
|
||||
}),
|
||||
actions: {
|
||||
async initRoutes(roles: Array<unknown>) {
|
||||
let accessedRouters = [];
|
||||
|
||||
const mutations = {
|
||||
setRouters: (state, routers) => {
|
||||
state.routers = routers;
|
||||
let removeRoutes = [];
|
||||
// special token
|
||||
if (roles.includes('all')) {
|
||||
accessedRouters = asyncRouterList;
|
||||
} else {
|
||||
const res = filterPermissionsRouters(asyncRouterList, roles);
|
||||
accessedRouters = res.accessedRouters;
|
||||
removeRoutes = res.removeRoutes;
|
||||
}
|
||||
|
||||
this.routers = accessedRouters;
|
||||
this.removeRoutes = removeRoutes;
|
||||
|
||||
removeRoutes.forEach((item: RouteRecordRaw) => {
|
||||
if (router.hasRoute(item.name)) {
|
||||
router.removeRoute(item.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
async restore() {
|
||||
this.removeRoutes.forEach((item: RouteRecordRaw) => {
|
||||
router.addRoute(item);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const getters = {
|
||||
routers: (state) => {
|
||||
return state.routers;
|
||||
},
|
||||
whiteListRouters: (state) => {
|
||||
return state.whiteListRouters;
|
||||
},
|
||||
};
|
||||
|
||||
const actions = {
|
||||
async initRoutes({ commit }, roles) {
|
||||
let accessedRouters;
|
||||
|
||||
// special token
|
||||
if (roles.includes('ALL_ROUTERS')) {
|
||||
accessedRouters = asyncRouterList;
|
||||
} else {
|
||||
accessedRouters = filterPermissionsRouters(asyncRouterList, roles);
|
||||
}
|
||||
|
||||
commit('setRouters', accessedRouters);
|
||||
|
||||
// register routers
|
||||
state.routers.concat(page404).forEach((item) => {
|
||||
router.addRoute(item);
|
||||
});
|
||||
},
|
||||
async restore({ commit, state }) {
|
||||
// remove routers
|
||||
state.routers.concat(page404).forEach((item) => {
|
||||
router.removeRoute(item.name);
|
||||
});
|
||||
commit('setRouters', []);
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
};
|
||||
export function getPermissionStore() {
|
||||
return usePermissionStore(store);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { COLOR_TOKEN, LIGHT_CHART_COLORS, DARK_CHART_COLORS } from '@/config/color';
|
||||
import STYLE_CONFIG from '@/config/style';
|
||||
import { COLOR_TOKEN, ColorSeries, ColorToken, LIGHT_CHART_COLORS, DARK_CHART_COLORS } from '@/config/color';
|
||||
import { store } from '@/store';
|
||||
|
||||
// 定义的state初始值
|
||||
const state = {
|
||||
...STYLE_CONFIG,
|
||||
showSettingPanel: false,
|
||||
|
@ -9,102 +10,62 @@ const state = {
|
|||
chartColors: LIGHT_CHART_COLORS,
|
||||
};
|
||||
|
||||
type IInitStateType = typeof state;
|
||||
export type TState = typeof state;
|
||||
|
||||
interface IStateType extends IInitStateType {
|
||||
isAsideFooter: boolean;
|
||||
showSettingPanel: boolean;
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
update(state: IStateType, payload: IStateType) {
|
||||
state.showBreadcrumb = payload.showBreadcrumb;
|
||||
state.mode = payload.mode;
|
||||
state.layout = payload.layout;
|
||||
state.isSidebarCompact = payload.isSidebarCompact;
|
||||
state.splitMenu = payload.splitMenu;
|
||||
state.isFooterAside = payload.isFooterAside;
|
||||
state.isSidebarFixed = payload.isSidebarFixed;
|
||||
state.isHeaderFixed = payload.isHeaderFixed;
|
||||
state.showHeader = payload.showHeader;
|
||||
state.backgroundTheme = payload.backgroundTheme;
|
||||
state.brandTheme = payload.brandTheme;
|
||||
},
|
||||
toggleSidebarCompact(state: IStateType) {
|
||||
state.isSidebarCompact = !state.isSidebarCompact;
|
||||
},
|
||||
showSidebarCompact(state: IStateType, payload: boolean) {
|
||||
state.isSidebarCompact = payload;
|
||||
},
|
||||
|
||||
toggleSettingPanel(state: IStateType, payload: boolean) {
|
||||
state.showSettingPanel = payload;
|
||||
},
|
||||
addColor(state: IStateType, payload: ColorSeries) {
|
||||
state.colorList = { ...state.colorList, ...payload };
|
||||
},
|
||||
changeChartColor(state: IStateType, payload: ColorToken) {
|
||||
state.chartColors = { ...payload };
|
||||
},
|
||||
};
|
||||
|
||||
const getters = {
|
||||
showHeader: (state: IStateType) => state.showHeader,
|
||||
showSidebar: (state: IStateType) => state.layout !== 'top',
|
||||
showSidebarLogo: (state: IStateType) => state.layout === 'side',
|
||||
showHeaderLogo: (state: IStateType) => state.layout !== 'side',
|
||||
showFooter: (state: IStateType) => state.showFooter,
|
||||
mode: (state: IStateType) => {
|
||||
if (state.mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
return 'dark';
|
||||
export const useSettingStore = defineStore('setting', {
|
||||
state: () => state,
|
||||
getters: {
|
||||
showSidebar: (state) => state.layout !== 'top',
|
||||
showSidebarLogo: (state) => state.layout === 'side',
|
||||
showHeaderLogo: (state) => state.layout !== 'side',
|
||||
displayMode: (state) => {
|
||||
if (state.mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
}
|
||||
return 'light';
|
||||
}
|
||||
return state.mode;
|
||||
return state.mode;
|
||||
},
|
||||
},
|
||||
};
|
||||
actions: {
|
||||
async changeMode(mode: 'dark' | 'light' | 'auto') {
|
||||
let theme = mode;
|
||||
|
||||
const actions = {
|
||||
async changeTheme({ commit, dispatch }, payload) {
|
||||
dispatch('changeMode', payload);
|
||||
dispatch('changeBrandTheme', payload);
|
||||
commit('update', payload);
|
||||
},
|
||||
async changeMode({ commit, state }, payload: IStateType) {
|
||||
let theme = payload.mode;
|
||||
|
||||
if (payload.mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
theme = 'dark';
|
||||
} else {
|
||||
theme = 'light';
|
||||
if (mode === 'auto') {
|
||||
const media = window.matchMedia('(prefers-color-scheme:dark)');
|
||||
if (media.matches) {
|
||||
theme = 'dark';
|
||||
} else {
|
||||
theme = 'light';
|
||||
}
|
||||
}
|
||||
}
|
||||
const isDarkMode = theme === 'dark';
|
||||
if (theme !== state.mode) {
|
||||
const isDarkMode = theme === 'dark';
|
||||
|
||||
document.documentElement.setAttribute('theme-mode', isDarkMode ? 'dark' : '');
|
||||
}
|
||||
|
||||
commit('changeChartColor', isDarkMode ? DARK_CHART_COLORS : LIGHT_CHART_COLORS);
|
||||
this.chartColor = isDarkMode ? DARK_CHART_COLORS : LIGHT_CHART_COLORS;
|
||||
},
|
||||
changeBrandTheme(brandTheme: string) {
|
||||
document.documentElement.setAttribute('theme-color', brandTheme);
|
||||
},
|
||||
updateConfig(payload: Partial<TState>) {
|
||||
for (const key in payload) {
|
||||
if (payload[key] !== undefined) {
|
||||
this[key] = payload[key];
|
||||
}
|
||||
if (key === 'mode') {
|
||||
this.changeMode(payload[key]);
|
||||
}
|
||||
if (key === 'brandTheme') {
|
||||
this.changeBrandTheme(payload[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
changeBrandTheme({ state }: { state: IStateType }, payload: IStateType) {
|
||||
const { brandTheme, mode } = payload;
|
||||
if (brandTheme !== state.brandTheme) {
|
||||
document.documentElement.setAttribute(
|
||||
'theme-color',
|
||||
/^#([a-fA-F\d]{6}|[a-fA-F\d]{3})$/.test(brandTheme) && mode === 'dark' ? `${brandTheme}` : brandTheme,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
};
|
||||
export function getSettingStore() {
|
||||
return useSettingStore(store);
|
||||
}
|
||||
|
|
|
@ -1,102 +1,86 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { TOKEN_NAME } from '@/config/global';
|
||||
import { store } from '@/store';
|
||||
|
||||
const InitUserInfo = {
|
||||
roles: [],
|
||||
};
|
||||
|
||||
// 定义的state初始值
|
||||
const state = {
|
||||
token: localStorage.getItem(TOKEN_NAME) || 'main_token', // 默认token不走权限
|
||||
userInfo: InitUserInfo,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setToken(state, token) {
|
||||
localStorage.setItem(TOKEN_NAME, token);
|
||||
state.token = token;
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
token: localStorage.getItem(TOKEN_NAME) || 'main_token', // 默认token不走权限
|
||||
userInfo: InitUserInfo,
|
||||
}),
|
||||
getters: {
|
||||
roles: (state) => {
|
||||
return state.userInfo?.roles;
|
||||
},
|
||||
},
|
||||
removeToken(state) {
|
||||
localStorage.removeItem(TOKEN_NAME);
|
||||
state.token = '';
|
||||
},
|
||||
setUserInfo(state, userInfo) {
|
||||
state.userInfo = userInfo;
|
||||
},
|
||||
};
|
||||
|
||||
const getters = {
|
||||
token: (state) => {
|
||||
return state.token;
|
||||
},
|
||||
roles: (state) => {
|
||||
return state.userInfo?.roles;
|
||||
},
|
||||
};
|
||||
|
||||
const actions = {
|
||||
async login({ commit }, userInfo) {
|
||||
const mockLogin = async (userInfo) => {
|
||||
// 登录请求流程
|
||||
console.log(userInfo);
|
||||
// const { account, password } = userInfo;
|
||||
// if (account !== 'td') {
|
||||
// return {
|
||||
// code: 401,
|
||||
// message: '账号不存在',
|
||||
// };
|
||||
// }
|
||||
// if (['main_', 'dev_'].indexOf(password) === -1) {
|
||||
// return {
|
||||
// code: 401,
|
||||
// message: '密码错误',
|
||||
// };
|
||||
// }
|
||||
// const token = {
|
||||
// main_: 'main_token',
|
||||
// dev_: 'dev_token',
|
||||
// }[password];
|
||||
return {
|
||||
code: 200,
|
||||
message: '登陆成功',
|
||||
data: 'main_token',
|
||||
};
|
||||
};
|
||||
|
||||
const res = await mockLogin(userInfo);
|
||||
if (res.code === 200) {
|
||||
commit('setToken', res.data);
|
||||
} else {
|
||||
throw res;
|
||||
}
|
||||
},
|
||||
async getUserInfo({ commit, state }) {
|
||||
const mockRemoteUserInfo = async (token) => {
|
||||
if (token === 'main_token') {
|
||||
actions: {
|
||||
async login(userInfo: Record<string, unknown>) {
|
||||
const mockLogin = async (userInfo: Record<string, unknown>) => {
|
||||
// 登录请求流程
|
||||
console.log(userInfo);
|
||||
// const { account, password } = userInfo;
|
||||
// if (account !== 'td') {
|
||||
// return {
|
||||
// code: 401,
|
||||
// message: '账号不存在',
|
||||
// };
|
||||
// }
|
||||
// if (['main_', 'dev_'].indexOf(password) === -1) {
|
||||
// return {
|
||||
// code: 401,
|
||||
// message: '密码错误',
|
||||
// };
|
||||
// }
|
||||
// const token = {
|
||||
// main_: 'main_token',
|
||||
// dev_: 'dev_token',
|
||||
// }[password];
|
||||
return {
|
||||
name: 'td_main',
|
||||
roles: ['ALL_ROUTERS'],
|
||||
code: 200,
|
||||
message: '登陆成功',
|
||||
data: 'main_token',
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: 'td_dev',
|
||||
roles: ['userIndex', 'dashboardBase', 'login'],
|
||||
};
|
||||
};
|
||||
|
||||
const res = await mockRemoteUserInfo(state.token);
|
||||
const res = await mockLogin(userInfo);
|
||||
if (res.code === 200) {
|
||||
this.token = res.data;
|
||||
} else {
|
||||
throw res;
|
||||
}
|
||||
},
|
||||
async getUserInfo() {
|
||||
const mockRemoteUserInfo = async (token: string) => {
|
||||
if (token === 'main_token') {
|
||||
return {
|
||||
name: 'td_main',
|
||||
roles: ['all'],
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: 'td_dev',
|
||||
roles: ['userIndex', 'dashboardBase', 'login'],
|
||||
};
|
||||
};
|
||||
|
||||
commit('setUserInfo', res);
|
||||
const res = await mockRemoteUserInfo(this.token);
|
||||
|
||||
this.userInfo = res;
|
||||
},
|
||||
async logout() {
|
||||
localStorage.removeItem(TOKEN_NAME);
|
||||
this.token = '';
|
||||
this.userInfo = InitUserInfo;
|
||||
},
|
||||
async removeToken() {
|
||||
this.token = '';
|
||||
},
|
||||
},
|
||||
async logout({ commit }) {
|
||||
commit('removeToken');
|
||||
commit('setUserInfo', InitUserInfo);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
};
|
||||
export function getUserStore() {
|
||||
return useUserStore(store);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// layout rewrite
|
||||
|
||||
.t-layout--sider {
|
||||
.t-layout__sider {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,7 @@
|
|||
margin-left: @spacer;
|
||||
}
|
||||
|
||||
.t-layout.t-layout-has-sider {
|
||||
|
||||
.t-layout.t-layout--with-sider {
|
||||
> .t-layout {
|
||||
flex: 1;
|
||||
min-width: 760px;
|
||||
|
@ -88,7 +87,7 @@
|
|||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
z-index: 200;
|
||||
transition: all 0.3s;
|
||||
min-height: 100%;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user