├── README.md
├── js-i18n
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── index.html
├── mock
│ ├── table.js
│ └── user.js
├── mockProdServer.js
├── package.json
├── public
│ └── favicon.ico
├── src
│ ├── App.vue
│ ├── api
│ │ ├── table.js
│ │ └── user.js
│ ├── assets
│ │ ├── images
│ │ │ ├── 401.gif
│ │ │ ├── 404.png
│ │ │ └── 404_cloud.png
│ │ ├── logo.png
│ │ └── style
│ │ │ ├── common.scss
│ │ │ └── transition.scss
│ ├── components
│ │ ├── layer
│ │ │ └── index.vue
│ │ ├── menu
│ │ │ └── index.vue
│ │ └── table
│ │ │ └── index.vue
│ ├── config
│ │ └── index.js
│ ├── directive
│ │ └── drag
│ │ │ └── index.js
│ ├── layout
│ │ ├── Header
│ │ │ ├── Breadcrumb.vue
│ │ │ ├── functionList
│ │ │ │ ├── fullscreen.vue
│ │ │ │ ├── sizeChange.vue
│ │ │ │ ├── theme.vue
│ │ │ │ ├── theme
│ │ │ │ │ ├── theme-color.vue
│ │ │ │ │ └── theme-icon.vue
│ │ │ │ └── word.vue
│ │ │ ├── index.vue
│ │ │ └── passwordLayer.vue
│ │ ├── Logo
│ │ │ └── index.vue
│ │ ├── Menu
│ │ │ ├── Link.vue
│ │ │ ├── MenuItem.vue
│ │ │ └── index.vue
│ │ ├── Tabs
│ │ │ ├── index.vue
│ │ │ ├── item.vue
│ │ │ └── tabsHook.js
│ │ └── index.vue
│ ├── locale
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── en.js
│ │ │ ├── en
│ │ │ ├── common.js
│ │ │ ├── menu.js
│ │ │ └── system.js
│ │ │ ├── zh-cn.js
│ │ │ └── zh-cn
│ │ │ ├── common.js
│ │ │ ├── menu.js
│ │ │ └── system.js
│ ├── main.js
│ ├── router
│ │ ├── createNode.js
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── dashboard.js
│ │ │ ├── pages.js
│ │ │ └── system.js
│ │ └── reload.vue
│ ├── store
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── app.js
│ │ │ ├── keepAlive.js
│ │ │ └── user.js
│ │ └── plugins
│ │ │ └── persistent.js
│ ├── theme
│ │ ├── index.js
│ │ ├── index.scss
│ │ └── modules
│ │ │ └── dark.scss
│ ├── utils
│ │ └── system
│ │ │ ├── nprogress.js
│ │ │ ├── request.js
│ │ │ ├── statistics.js
│ │ │ └── title.js
│ └── views
│ │ ├── main
│ │ ├── dashboard
│ │ │ └── index.vue
│ │ └── pages
│ │ │ ├── categoryTable
│ │ │ ├── category.vue
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ └── my-table.vue
│ │ │ ├── crudTable
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ └── layer.vue
│ │ │ └── treeTable
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ ├── my-table.vue
│ │ │ └── tree.vue
│ │ └── system
│ │ ├── 401.vue
│ │ ├── 404.vue
│ │ ├── login.vue
│ │ └── redirect.vue
└── vite.config.js
├── js
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── index.html
├── mock
│ ├── table.js
│ └── user.js
├── mockProdServer.js
├── package.json
├── public
│ └── favicon.ico
├── src
│ ├── App.vue
│ ├── api
│ │ ├── table.js
│ │ └── user.js
│ ├── assets
│ │ ├── images
│ │ │ ├── 401.gif
│ │ │ ├── 404.png
│ │ │ └── 404_cloud.png
│ │ ├── logo.png
│ │ └── style
│ │ │ ├── common.scss
│ │ │ └── transition.scss
│ ├── components
│ │ ├── layer
│ │ │ └── index.vue
│ │ ├── menu
│ │ │ └── index.vue
│ │ └── table
│ │ │ └── index.vue
│ ├── config
│ │ └── index.js
│ ├── directive
│ │ └── drag
│ │ │ └── index.js
│ ├── layout
│ │ ├── Header
│ │ │ ├── Breadcrumb.vue
│ │ │ ├── functionList
│ │ │ │ ├── fullscreen.vue
│ │ │ │ ├── sizeChange.vue
│ │ │ │ ├── theme.vue
│ │ │ │ └── theme
│ │ │ │ │ ├── theme-color.vue
│ │ │ │ │ └── theme-icon.vue
│ │ │ ├── index.vue
│ │ │ └── passwordLayer.vue
│ │ ├── Logo
│ │ │ └── index.vue
│ │ ├── Menu
│ │ │ ├── Link.vue
│ │ │ ├── MenuItem.vue
│ │ │ └── index.vue
│ │ ├── Tabs
│ │ │ ├── index.vue
│ │ │ ├── item.vue
│ │ │ └── tabsHook.js
│ │ └── index.vue
│ ├── main.js
│ ├── router
│ │ ├── createNode.js
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── dashboard.js
│ │ │ ├── pages.js
│ │ │ └── system.js
│ │ └── reload.vue
│ ├── store
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── app.js
│ │ │ ├── keepAlive.js
│ │ │ └── user.js
│ │ └── plugins
│ │ │ └── persistent.js
│ ├── theme
│ │ ├── index.js
│ │ ├── index.scss
│ │ └── modules
│ │ │ └── dark.scss
│ ├── utils
│ │ └── system
│ │ │ ├── nprogress.js
│ │ │ ├── request.js
│ │ │ ├── statistics.js
│ │ │ └── title.js
│ └── views
│ │ ├── main
│ │ ├── dashboard
│ │ │ └── index.vue
│ │ └── pages
│ │ │ ├── categoryTable
│ │ │ ├── category.vue
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ └── my-table.vue
│ │ │ ├── crudTable
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ └── layer.vue
│ │ │ └── treeTable
│ │ │ ├── enum.js
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ ├── my-table.vue
│ │ │ └── tree.vue
│ │ └── system
│ │ ├── 401.vue
│ │ ├── 404.vue
│ │ ├── login.vue
│ │ └── redirect.vue
└── vite.config.js
├── ts-i18n
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── index.html
├── mock
│ ├── table.ts
│ └── user.ts
├── mockProdServer.ts
├── package.json
├── public
│ └── favicon.ico
├── src
│ ├── App.vue
│ ├── api
│ │ ├── table.ts
│ │ └── user.ts
│ ├── assets
│ │ ├── images
│ │ │ ├── 401.gif
│ │ │ ├── 404.png
│ │ │ └── 404_cloud.png
│ │ ├── logo.png
│ │ └── style
│ │ │ ├── common.scss
│ │ │ └── transition.scss
│ ├── components
│ │ ├── layer
│ │ │ └── index.vue
│ │ ├── menu
│ │ │ └── index.vue
│ │ └── table
│ │ │ ├── index.vue
│ │ │ └── type.ts
│ ├── config
│ │ └── index.ts
│ ├── directive
│ │ └── drag
│ │ │ └── index.ts
│ ├── layout
│ │ ├── Header
│ │ │ ├── Breadcrumb.vue
│ │ │ ├── functionList
│ │ │ │ ├── fullscreen.vue
│ │ │ │ ├── sizeChange.vue
│ │ │ │ ├── theme.vue
│ │ │ │ ├── theme
│ │ │ │ │ ├── theme-color.vue
│ │ │ │ │ └── theme-icon.vue
│ │ │ │ └── word.vue
│ │ │ ├── index.vue
│ │ │ └── passwordLayer.vue
│ │ ├── Logo
│ │ │ └── index.vue
│ │ ├── Menu
│ │ │ ├── Link.vue
│ │ │ ├── MenuItem.vue
│ │ │ └── index.vue
│ │ ├── Tabs
│ │ │ ├── index.vue
│ │ │ ├── item.vue
│ │ │ └── tabsHook.ts
│ │ └── index.vue
│ ├── locale
│ │ ├── index.ts
│ │ └── modules
│ │ │ ├── en.ts
│ │ │ ├── en
│ │ │ ├── common.ts
│ │ │ ├── menu.ts
│ │ │ └── system.ts
│ │ │ ├── zh-cn.ts
│ │ │ └── zh-cn
│ │ │ ├── common.ts
│ │ │ ├── menu.ts
│ │ │ └── system.ts
│ ├── main.ts
│ ├── router
│ │ ├── createNode.ts
│ │ ├── index.ts
│ │ ├── modules
│ │ │ ├── dashboard.ts
│ │ │ ├── pages.ts
│ │ │ └── system.ts
│ │ └── reload.vue
│ ├── shims-vue.d.ts
│ ├── store
│ │ ├── index.ts
│ │ ├── modules
│ │ │ ├── app.ts
│ │ │ ├── keepAlive.ts
│ │ │ └── user.ts
│ │ └── plugins
│ │ │ └── persistent.ts
│ ├── theme
│ │ ├── index.scss
│ │ ├── index.ts
│ │ └── modules
│ │ │ └── dark.scss
│ ├── utils
│ │ └── system
│ │ │ ├── nprogress.ts
│ │ │ ├── request.ts
│ │ │ ├── statistics.ts
│ │ │ └── title.ts
│ └── views
│ │ ├── main
│ │ ├── dashboard
│ │ │ └── index.vue
│ │ └── pages
│ │ │ ├── categoryTable
│ │ │ ├── category.vue
│ │ │ ├── enum.ts
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ └── my-table.vue
│ │ │ ├── crudTable
│ │ │ ├── enum.ts
│ │ │ ├── index.vue
│ │ │ └── layer.vue
│ │ │ └── treeTable
│ │ │ ├── enum.ts
│ │ │ ├── index.vue
│ │ │ ├── layer.vue
│ │ │ ├── my-table.vue
│ │ │ └── tree.vue
│ │ └── system
│ │ ├── 401.vue
│ │ ├── 404.vue
│ │ ├── login.vue
│ │ └── redirect.vue
├── tsconfig.json
└── vite.config.ts
└── ts
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── index.html
├── mock
├── table.ts
└── user.ts
├── mockProdServer.ts
├── package.json
├── public
└── favicon.ico
├── src
├── App.vue
├── api
│ ├── table.ts
│ └── user.ts
├── assets
│ ├── images
│ │ ├── 401.gif
│ │ ├── 404.png
│ │ └── 404_cloud.png
│ ├── logo.png
│ └── style
│ │ ├── common.scss
│ │ └── transition.scss
├── components
│ ├── layer
│ │ └── index.vue
│ ├── menu
│ │ └── index.vue
│ └── table
│ │ ├── index.vue
│ │ └── type.ts
├── config
│ └── index.ts
├── directive
│ └── drag
│ │ └── index.ts
├── layout
│ ├── Header
│ │ ├── Breadcrumb.vue
│ │ ├── functionList
│ │ │ ├── fullscreen.vue
│ │ │ ├── sizeChange.vue
│ │ │ ├── theme.vue
│ │ │ └── theme
│ │ │ │ ├── theme-color.vue
│ │ │ │ └── theme-icon.vue
│ │ ├── index.vue
│ │ └── passwordLayer.vue
│ ├── Logo
│ │ └── index.vue
│ ├── Menu
│ │ ├── Link.vue
│ │ ├── MenuItem.vue
│ │ └── index.vue
│ ├── Tabs
│ │ ├── index.vue
│ │ ├── item.vue
│ │ └── tabsHook.ts
│ └── index.vue
├── main.ts
├── router
│ ├── createNode.ts
│ ├── index.ts
│ ├── modules
│ │ ├── dashboard.ts
│ │ ├── pages.ts
│ │ └── system.ts
│ └── reload.vue
├── shims-vue.d.ts
├── store
│ ├── index.ts
│ ├── modules
│ │ ├── app.ts
│ │ ├── keepAlive.ts
│ │ └── user.ts
│ └── plugins
│ │ └── persistent.ts
├── theme
│ ├── index.scss
│ ├── index.ts
│ └── modules
│ │ └── dark.scss
├── utils
│ └── system
│ │ ├── nprogress.ts
│ │ ├── request.ts
│ │ ├── statistics.ts
│ │ └── title.ts
└── views
│ ├── main
│ ├── dashboard
│ │ └── index.vue
│ └── pages
│ │ ├── categoryTable
│ │ ├── category.vue
│ │ ├── enum.ts
│ │ ├── index.vue
│ │ ├── layer.vue
│ │ └── my-table.vue
│ │ ├── crudTable
│ │ ├── enum.ts
│ │ ├── index.vue
│ │ └── layer.vue
│ │ └── treeTable
│ │ ├── enum.ts
│ │ ├── index.vue
│ │ ├── layer.vue
│ │ ├── my-table.vue
│ │ └── tree.vue
│ └── system
│ ├── 401.vue
│ ├── 404.vue
│ ├── login.vue
│ └── redirect.vue
├── tsconfig.json
└── vite.config.ts
/README.md:
--------------------------------------------------------------------------------
1 | # vue-admin-box-template
2 | vue-admin-box中后台开源框架基础模板,包括了四个基本模板(ts版本/js版本)
3 |
4 | - ts-i18n —— ts版本+国际化
5 | - ts —— ts版本,无国际化
6 | - js —— js版本,无国际化
7 | - Js-i18n—— js版本,国际化
--------------------------------------------------------------------------------
/js-i18n/.env.development:
--------------------------------------------------------------------------------
1 | ENV = 'development'
2 |
3 | VITE_BASE_URL = '/dev-api'
--------------------------------------------------------------------------------
/js-i18n/.env.production:
--------------------------------------------------------------------------------
1 | ENV = 'production'
2 |
3 | VITE_BASE_URL = '/pro-api'
--------------------------------------------------------------------------------
/js-i18n/.env.staging:
--------------------------------------------------------------------------------
1 | ENV = 'staging'
2 |
3 | VITE_BASE_URL = '/test-api'
--------------------------------------------------------------------------------
/js-i18n/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | yarn.lock
7 | yarn-error.log
8 | .vscode
9 |
--------------------------------------------------------------------------------
/js-i18n/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 罗茜
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/js-i18n/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/js-i18n/mock/user.js:
--------------------------------------------------------------------------------
1 | const users = [
2 | { name: 'admin', password: '123456', token: 'admin', info: {
3 | name: '系统管理员'
4 | }},
5 | { name: 'editor', password: '123456', token: 'editor', info: {
6 | name: '编辑人员'
7 | }},
8 | { name: 'test', password: '123456', token: 'test', info: {
9 | name: '测试人员'
10 | }},
11 | ]
12 | export default [
13 | {
14 | url: `/mock/user/login`,
15 | method: 'post',
16 | response: ({ body }) => {
17 | const user = users.find(user => {
18 | return body.name === user.name && body.password === user.password
19 | })
20 | if (user) {
21 | return {
22 | code: 200,
23 | data: {
24 | token: user.token,
25 | },
26 | };
27 | } else {
28 | return {
29 | code: 401,
30 | data: {},
31 | msg: '用户名或密码错误'
32 | };
33 | }
34 |
35 | }
36 | },
37 | {
38 | url: `/mock/user/info`,
39 | method: 'post',
40 | response: ({ body }) => {
41 | const { token } = body
42 | const info = users.find(user => {
43 | return user.token === token
44 | }).info
45 | if (info) {
46 | return {
47 | code: 200,
48 | data: {
49 | info: info
50 | },
51 | };
52 | } else {
53 | return {
54 | code: 403,
55 | data: {},
56 | msg: '无访问权限'
57 | };
58 | }
59 |
60 | }
61 | },
62 | {
63 | url: `/mock/user/out`,
64 | method: 'post',
65 | response: () => {
66 | return {
67 | code: 200,
68 | data: {},
69 | msg: 'success'
70 | };
71 | }
72 | },
73 | {
74 | url: `/mock/user/passwordChange`,
75 | method: 'post',
76 | response: () => {
77 | return {
78 | code: 200,
79 | data: {},
80 | msg: 'success'
81 | };
82 | }
83 | },
84 | ]
--------------------------------------------------------------------------------
/js-i18n/mockProdServer.js:
--------------------------------------------------------------------------------
1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
2 | import userModule from './mock/user'
3 | import tableModule from './mock/table'
4 |
5 | export function setupProdMockServer() {
6 | createProdMockServer([
7 | ...userModule,
8 | ...tableModule
9 | ]);
10 | }
11 |
--------------------------------------------------------------------------------
/js-i18n/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "init",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "start": "vite",
7 | "build": "vite build --mode=production",
8 | "build:stag": "vite build --mode=staging",
9 | "serve": "vite preview"
10 | },
11 | "dependencies": {
12 | "@vueuse/core": "^4.10.0",
13 | "axios": "^0.21.1",
14 | "element-plus": "^1.0.2-beta.48",
15 | "mockjs": "^1.1.0",
16 | "normalize.css": "^8.0.1",
17 | "nprogress": "^0.2.0",
18 | "throttle-debounce": "^3.0.1",
19 | "vue": "^3.1.2",
20 | "vue-i18n": "^9.1.6",
21 | "vue-router": "4",
22 | "vuex": "^4.0.0"
23 | },
24 | "devDependencies": {
25 | "@types/node": "^15.0.3",
26 | "@vitejs/plugin-vue": "^1.2.2",
27 | "@vue/compiler-sfc": "^3.0.5",
28 | "sass": "^1.32.12",
29 | "vite": "2.3.7",
30 | "vite-plugin-mock": "2.8.0",
31 | "vue-tsc": "^0.0.24"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/js-i18n/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js-i18n/public/favicon.ico
--------------------------------------------------------------------------------
/js-i18n/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
27 |
38 |
--------------------------------------------------------------------------------
/js-i18n/src/api/table.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 获取数据api
4 | export function getData(data) {
5 | return request({
6 | url: '/table/list',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取分类数据
14 | export function getCategory(data) {
15 | return request({
16 | url: '/table/category',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 获取树组织数据
24 | export function getTree(data) {
25 | return request({
26 | url: '/table/tree',
27 | method: 'post',
28 | baseURL: '/mock',
29 | data
30 | })
31 | }
32 |
33 | // 新增
34 | export function add(data) {
35 | return request({
36 | url: '/table/add',
37 | method: 'post',
38 | baseURL: '/mock',
39 | data
40 | })
41 | }
42 |
43 | // 编辑
44 | export function update(data) {
45 | return request({
46 | url: '/table/update',
47 | method: 'post',
48 | baseURL: '/mock',
49 | data
50 | })
51 | }
52 |
53 | // 删除
54 | export function del(data) {
55 | return request({
56 | url: '/table/del',
57 | method: 'post',
58 | baseURL: '/mock',
59 | data
60 | })
61 | }
--------------------------------------------------------------------------------
/js-i18n/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 登录api
4 | export function loginApi(data) {
5 | return request({
6 | url: '/user/login',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取用户信息Api
14 | export function getInfoApi(data) {
15 | return request({
16 | url: '/user/info',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 退出登录Api
24 | export function loginOutApi() {
25 | return request({
26 | url: '/user/out',
27 | method: 'post',
28 | baseURL: '/mock'
29 | })
30 | }
31 |
32 | // 获取用户信息Api
33 | export function passwordChange(data) {
34 | return request({
35 | url: '/user/passwordChange',
36 | method: 'post',
37 | baseURL: '/mock',
38 | data
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/js-i18n/src/assets/images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js-i18n/src/assets/images/401.gif
--------------------------------------------------------------------------------
/js-i18n/src/assets/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js-i18n/src/assets/images/404.png
--------------------------------------------------------------------------------
/js-i18n/src/assets/images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js-i18n/src/assets/images/404_cloud.png
--------------------------------------------------------------------------------
/js-i18n/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js-i18n/src/assets/logo.png
--------------------------------------------------------------------------------
/js-i18n/src/assets/style/common.scss:
--------------------------------------------------------------------------------
1 | @import "./transition.scss";
2 | @import "@/theme/index.scss";
3 | .layout-container {
4 | background-color: var(--system-container-main-background);
5 | width: calc(100% - 30px);
6 | height: calc(100% - 30px);
7 | margin: 15px;
8 | display: flex;
9 | flex-direction: column;
10 | overflow-y: auto;
11 | &-form {
12 | display: flex;
13 | justify-content: space-between;
14 | padding: 15px 15px 0;
15 | &-handle {
16 | display: flex;
17 | justify-content: flex-start;
18 | }
19 | &-search {
20 | display: flex;
21 | justify-content: flex-end;
22 | .search-btn {
23 | margin-left: 15px;
24 | }
25 | }
26 | .el-form-item {
27 | margin-bottom: 0;
28 | }
29 | }
30 | &-table {
31 | flex: 1;
32 | height: 100%;
33 | padding: 15px;
34 | overflow: auto;
35 | }
36 | }
37 | .flex-box {
38 | display: flex;
39 | flex-direction: column;
40 | width: 100%;
41 | height: 100%;
42 | padding: 15px;
43 | box-sizing: border-box;
44 | }
45 | .flex {
46 | display: flex;
47 | }
48 | .center {
49 | justify-content: center;
50 | align-items: center;
51 | text-align: center;
52 | }
53 | a {
54 | text-decoration: none;
55 | }
56 |
57 | /** element-plus **/
58 | .el-icon{
59 | text-align: center;
60 | }
61 |
62 | /** 用于提示信息 **/
63 | .my-tip {
64 | background-color: #f1f1f1;
65 | padding: 5px 10px;
66 | text-align: left;
67 | border-radius: 4px;
68 | }
69 | .system-scrollbar {
70 | &::-webkit-scrollbar {
71 | display: none;
72 | width: 6px;
73 | }
74 | &::-webkit-scrollbar-thumb {
75 | border-radius: 10px;
76 | background: rgba(144, 147, 153, 0.3);
77 | }
78 | &:hover {
79 | &::-webkit-scrollbar {
80 | display: block;
81 | }
82 | &::-webkit-scrollbar-thumb {
83 | border-radius: 10px;
84 | background: rgba(144, 147, 153, 0.3);
85 | &:hover {
86 | background: rgba(144, 147, 153, 0.5);
87 | }
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/js-i18n/src/assets/style/transition.scss:
--------------------------------------------------------------------------------
1 | /* fade-transform */
2 | .fade-transform-leave-active,
3 | .fade-transform-enter-active {
4 | transition: all .2s;
5 | }
6 |
7 | .fade-transform-enter-from {
8 | opacity: 0;
9 | transform: translateX(-30px);
10 | transition: all .2s;
11 | }
12 |
13 | .fade-transform-leave-to {
14 | opacity: 0;
15 | transform: translateX(30px);
16 | transition: all .2s;
17 | }
18 |
19 | /* breadcrumb transition */
20 | .breadcrumb-enter-active,
21 | .breadcrumb-leave-active {
22 | transition: all .2s;
23 | }
24 |
25 | .breadcrumb-enter,
26 | .breadcrumb-leave-active {
27 | opacity: 0;
28 | transform: translateX(80px);
29 | }
30 |
31 | .breadcrumb-move {
32 | transition: all .5s;
33 | }
34 |
35 | .breadcrumb-leave-active {
36 | position: absolute;
37 | }
--------------------------------------------------------------------------------
/js-i18n/src/components/layer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 | 确认
14 | 取消
15 |
16 |
17 |
18 |
19 |
20 |
21 |
57 |
58 |
--------------------------------------------------------------------------------
/js-i18n/src/components/menu/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/js-i18n/src/config/index.js:
--------------------------------------------------------------------------------
1 | const showLogo = true; // 是否显示Logo顶部模块
2 | const systemTitle = 'message.system.title' // 系统名称,用于显示在左上角模块,以及浏览器标题上使用,使用配置项
3 | export {
4 | systemTitle
5 | }
--------------------------------------------------------------------------------
/js-i18n/src/layout/Header/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $t(item.meta.title) }}
9 |
10 | {{ $t(item.meta.title) }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
50 |
51 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Header/functionList/fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Header/functionList/sizeChange.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ $t(d.name) }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
60 |
61 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Header/functionList/theme/theme-color.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
48 |
49 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Header/functionList/word.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ locale.message.language }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
46 |
47 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ $t(systemTitle) }}
5 |
6 |
7 |
8 |
23 |
24 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Menu/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
39 |
--------------------------------------------------------------------------------
/js-i18n/src/layout/Tabs/tabsHook.js:
--------------------------------------------------------------------------------
1 | const tabsHook = {
2 | setItem: function(arr) {
3 | localStorage.setItem('tabs', JSON.stringify(arr))
4 | },
5 | getItem: function() {
6 | return JSON.parse(localStorage.getItem('tabs') || '[]')
7 | }
8 | }
9 | export default tabsHook
10 |
--------------------------------------------------------------------------------
/js-i18n/src/locale/index.js:
--------------------------------------------------------------------------------
1 | // 提示信息仅在开发环境生效
2 | import { createI18n } from 'vue-i18n'
3 | import store from '@/store'
4 |
5 | const files= import.meta.globEager('./modules/*.js')
6 |
7 | let messages = {}
8 | Object.keys(files).forEach((c) => {
9 | const module = files[c].default
10 | const moduleName = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | messages[moduleName] = module
12 | })
13 |
14 | const lang = store.state.app.lang || navigator.userLanguage || navigator.language // 初次进入,采用浏览器当前设置的语言,默认采用中文
15 | const locale = lang.indexOf('en') !== -1 ? 'en' : 'zh-cn'
16 |
17 | const i18n = createI18n({
18 | __VUE_I18N_LEGACY_API__: false,
19 | __VUE_I18N_FULL_INSTALL__: false,
20 | locale: locale,
21 | fallbackLocale: 'zh-cn',
22 | messages
23 | })
24 | document.querySelector('html').setAttribute('lang', locale)
25 |
26 | export default i18n
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/en.js:
--------------------------------------------------------------------------------
1 | import enLocale from 'element-plus/lib/locale/lang/en'
2 | import system from './en/system'
3 | import common from './en/common'
4 | import menu from './en/menu'
5 | const lang = {
6 | el: enLocale.el, // element-plus i18 setting
7 | message: {
8 | language: 'English',
9 | ...system,
10 | ...common,
11 | ...menu
12 | }
13 | }
14 |
15 | export default lang
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/en/common.js:
--------------------------------------------------------------------------------
1 | export default {
2 | common: {
3 | search: 'search',
4 | searchTip: 'please input keyword',
5 | add: 'add',
6 | update: 'update',
7 | del: 'delete',
8 | delBat: 'delete choose',
9 | delTip: 'Are you sure delete the selection data ?',
10 | handle: 'handle'
11 | },
12 | }
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/en/menu.js:
--------------------------------------------------------------------------------
1 | export default {
2 | menu: {
3 | dashboard: {
4 | name: 'dashboard',
5 | index: 'index'
6 | },
7 | system: {
8 | name: 'system',
9 | redirect: 'redirect',
10 | '404': '404',
11 | '401': '401'
12 | },
13 | page: {
14 | name: 'page',
15 | crudTable: 'crudTable',
16 | categoryTable: 'categoryTable',
17 | treeTable: 'treeTable',
18 | },
19 | },
20 | }
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/en/system.js:
--------------------------------------------------------------------------------
1 | export default {
2 | system: {
3 | title: 'backendsystem',
4 | login: 'login',
5 | userName: 'userName',
6 | password: 'password',
7 | contentScreen: 'content full screen',
8 | fullScreen: 'fullscreen',
9 | fullScreenBack: 'back fullscreen',
10 | github: 'visit github',
11 | changePassword: 'change password',
12 | loginOut: 'login out',
13 | user: 'admin',
14 | size: {
15 | default: 'default',
16 | medium: 'medium',
17 | small: 'small',
18 | mini: 'mini'
19 | },
20 | setting: {
21 | name: 'setting',
22 |
23 | style: {
24 | name: 'full style setting',
25 | default: 'default menu style',
26 | light: 'light menu style',
27 | dark: 'dark menu style'
28 | },
29 | primaryColor: {
30 | name: 'primary color',
31 | blue: 'default blue',
32 | red: 'rose red',
33 | violet: 'grace violet',
34 | green: 'story green',
35 | cyan: 'cyan',
36 | black: 'geek black'
37 | },
38 | other: {
39 | name: 'other setting',
40 | showLogo: 'show logo',
41 | showBreadcrumb: 'show breadcrumb',
42 | keepOnlyOneMenu: 'keep only one menu open',
43 | }
44 | },
45 | tab: {
46 | reload: 'refresh',
47 | closeAll: 'close all tags',
48 | closeOther: 'close other tags',
49 | closeCurrent: 'close current tag'
50 | }
51 | },
52 | }
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/zh-cn.js:
--------------------------------------------------------------------------------
1 | import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
2 | import system from './zh-cn/system'
3 | import common from './zh-cn/common'
4 | import menu from './zh-cn/menu'
5 | const lang = {
6 | el: zhLocale.el, // element内部国际化
7 | message: {
8 | language: '中文',
9 | ...system,
10 | ...common,
11 | ...menu
12 | }
13 | }
14 |
15 | export default lang
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/zh-cn/common.js:
--------------------------------------------------------------------------------
1 | export default {
2 | common: {
3 | search: '搜索',
4 | searchTip: '请输入关键词进行检索',
5 | add: '新增',
6 | update: '编辑',
7 | del: '删除',
8 | delBat: '批量删除',
9 | delTip: '确定删除选中的数据吗?',
10 | handle: '操作'
11 | },
12 | }
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/zh-cn/menu.js:
--------------------------------------------------------------------------------
1 | export default {
2 | menu: {
3 | dashboard: {
4 | name: 'dashboard',
5 | index: '首页'
6 | },
7 | system: {
8 | name: '系统目录',
9 | redirect: '重定向页面',
10 | '404': '404',
11 | '401': '401'
12 | },
13 | page: {
14 | name: '页面',
15 | crudTable: '业务表格',
16 | categoryTable: '分类联动表格',
17 | treeTable: '树联动表格'
18 | }
19 | },
20 | }
--------------------------------------------------------------------------------
/js-i18n/src/locale/modules/zh-cn/system.js:
--------------------------------------------------------------------------------
1 | export default {
2 | system: {
3 | title: '后台管理系统',
4 | login: '登录',
5 | userName: '用户名',
6 | password: '密码',
7 | contentScreen: '内容全屏',
8 | fullScreen: '全屏',
9 | fullScreenBack: '退出全屏',
10 | github: '访问github地址',
11 | changePassword: '修改密码',
12 | loginOut: '退出登录',
13 | user: '管理员',
14 | size: {
15 | default: '默认',
16 | medium: '中',
17 | small: '小',
18 | mini: '迷你'
19 | },
20 | setting: {
21 | name: '系统设置',
22 |
23 | style: {
24 | name: '整体风格设置',
25 | default: '默认菜单风格',
26 | light: '亮色菜单风格',
27 | dark: '暗色菜单风格'
28 | },
29 | primaryColor: {
30 | name: '主题色',
31 | blue: '默认蓝',
32 | red: '玫瑰红',
33 | violet: '优雅紫',
34 | green: '故事绿',
35 | cyan: '明青',
36 | black: '极客黑'
37 | },
38 | other: {
39 | name: '其他设置',
40 | showLogo: '显示logo',
41 | showBreadcrumb: '显示面包屑导航',
42 | keepOnlyOneMenu: '保持一个菜单展开',
43 | }
44 | },
45 | tab: {
46 | reload: '重新加载',
47 | closeAll: '关闭所有标签',
48 | closeOther: '关闭其他标签',
49 | closeCurrent: '关闭当前标签'
50 | }
51 | },
52 | }
--------------------------------------------------------------------------------
/js-i18n/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import ElementPlus from 'element-plus'
3 | import { baidu } from './utils/system/statistics'
4 | import 'element-plus/lib/theme-chalk/index.css'
5 | import 'element-plus/lib/theme-chalk/display.css' // 引入基于断点的隐藏类
6 | import 'normalize.css' // css初始化
7 | import './assets/style/common.scss' // 公共css
8 | import App from './App.vue'
9 | import store from './store'
10 | import router from './router'
11 | import i18n from './locale'
12 | // if (import.meta.env.MODE !== 'development') { // 非开发环境调用百度统计
13 | // baidu()
14 | // }
15 | const app = createApp(App)
16 | app.use(ElementPlus, { size: store.state.app.elementSize, i18n: i18n.global.t })
17 | app.use(store)
18 | app.use(router)
19 | app.use(i18n)
20 | // app.config.performance = true
21 | app.mount('#app')
22 |
--------------------------------------------------------------------------------
/js-i18n/src/router/createNode.js:
--------------------------------------------------------------------------------
1 | // 1. 用于解决keep-alive需要name的问题,动态生成随机name供keep-alive使用
2 | // 2. 用于解决transition动画内部结点只能为根元素的问题,单文件可写多结点
3 | import { defineComponent, h, createVNode, ref, nextTick } from 'vue'
4 | import reload from './reload.vue'
5 | import NProgress from '@/utils/system/nprogress'
6 |
7 | export function createNameComponent(component) {
8 | return () => {
9 | return new Promise((res) => {
10 | component().then((comm) => {
11 | const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now();
12 | const tempComm = defineComponent({
13 | name,
14 | setup() {
15 | const isReload = ref(false);
16 | let timeOut = null;
17 | const handleReload = () => {
18 | isReload.value = true;
19 | timeOut && clearTimeout(timeOut);
20 | NProgress.start();
21 | timeOut = setTimeout(() => {
22 | nextTick(() => {
23 | NProgress.done();
24 | isReload.value = false;
25 | });
26 | }, 260);
27 | };
28 | return {
29 | isReload,
30 | handleReload
31 | };
32 | },
33 | render: function () {
34 | if (this.isReload) {
35 | return h('div', { class: 'el-main-box' }, [h(reload)]);
36 | } else {
37 | return h('div', { class: 'el-main-box' }, [createVNode(comm.default)]);
38 | }
39 | }
40 | });
41 | res(tempComm);
42 | });
43 | });
44 | };
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/js-i18n/src/router/modules/dashboard.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/',
6 | component: Layout,
7 | redirect: '/dashboard',
8 | meta: { title: 'message.menu.dashboard.name', icon: 'el-icon-menu' },
9 | children: [
10 | {
11 | path: 'dashboard',
12 | component: createNameComponent(() => import('@/views/main/dashboard/index.vue')),
13 | meta: { title: 'message.menu.dashboard.index', icon: 'el-icon-menu', hideClose: true }
14 | }
15 | ]
16 | }
17 | ]
18 |
19 | export default route
--------------------------------------------------------------------------------
/js-i18n/src/router/modules/pages.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/pages',
6 | component: Layout,
7 | redirect: '/pages/crudTable',
8 | meta: { title: 'message.menu.page.name', icon: 'el-icon-document-copy' },
9 | alwayShow: true,
10 | children: [
11 | {
12 | path: 'crudTable',
13 | component: createNameComponent(() => import('@/views/main/pages/crudTable/index.vue')),
14 | meta: { title: 'message.menu.page.crudTable', cache: false, roles: ['admin', 'editor'] }
15 | },
16 | {
17 | path: 'categoryTable',
18 | component: createNameComponent(() => import('@/views/main/pages/categoryTable/index.vue')),
19 | meta: { title: 'message.menu.page.categoryTable', cache: true, roles: ['admin'] }
20 | },
21 | {
22 | path: 'treeTable',
23 | component: createNameComponent(() => import('@/views/main/pages/treeTable/index.vue')),
24 | meta: { title: 'message.menu.page.treeTable', cache: true }
25 | }
26 | ]
27 | }
28 | ]
29 |
30 | export default route
--------------------------------------------------------------------------------
/js-i18n/src/router/modules/system.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/system',
6 | component: Layout,
7 | redirect: '/404',
8 | hideMenu: true,
9 | meta: { title: 'message.menu.system.name' },
10 | children: [
11 | {
12 | path: '/404',
13 | component: createNameComponent(() => import('@/views/system/404.vue')),
14 | meta: { title: 'message.menu.system.404', hideTabs: true }
15 | },
16 | {
17 | path: '/401',
18 | component: createNameComponent(() => import('@/views/system/401.vue')),
19 | meta: { title: 'message.menu.system.401', hideTabs: true }
20 | },
21 | {
22 | path: '/redirect/:path(.*)',
23 | component: createNameComponent(() => import('@/views/system/redirect.vue')),
24 | meta: { title: 'message.menu.system.redirect', hideTabs: true }
25 | }
26 | ]
27 | },
28 | {
29 | path: '/login',
30 | component: createNameComponent(() => import('@/views/system/login.vue')),
31 | hideMenu: true,
32 | meta: { title: 'message.system.login', hideTabs: true }
33 | },
34 | {
35 | // 找不到路由重定向到404页面
36 | path: "/:pathMatch(.*)",
37 | component: Layout,
38 | redirect: "/404",
39 | hideMenu: true
40 | },
41 | ]
42 |
43 | export default route
--------------------------------------------------------------------------------
/js-i18n/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, createLogger } from 'vuex'
2 | import Presistent from './plugins/persistent'
3 | const debug = process.env.NODE_ENV !== 'production'
4 |
5 | const files= import.meta.globEager('./modules/*.js')
6 |
7 | let modules = {}
8 | Object.keys(files).forEach((c) => {
9 | const module = files[c].default
10 | const moduleName = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | modules[moduleName] = module
12 | })
13 |
14 | const presistent = Presistent({ key: 'vuex', modules, modulesKeys: {
15 | local: Object.keys(modules),
16 | session: []
17 | } })
18 |
19 | export default createStore({
20 | modules: {
21 | ...modules
22 | },
23 | strict: debug,
24 | plugins: debug ? [createLogger(), presistent] : [presistent]
25 | })
--------------------------------------------------------------------------------
/js-i18n/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | const state = () => ({
2 | isCollapse: false, // 侧边栏是否收缩展示
3 | contentFullScreen: false, // 内容是否可全屏展示
4 | showLogo: true, // 是否显示Logo
5 | fixedTop: false, // 是否固定顶部, todo,暂未使用
6 | showTabs: true, // 是否显示导航历史
7 | expandOneMenu: true, // 一次是否只能展开一个菜单
8 | elementSize: 'mini', // element默认尺寸,支持官网四个大小参数
9 | lang: 'zh', // 默认采用的国际化方案
10 | theme: {
11 | state: {
12 | style: 'default',
13 | primaryColor: '#409eff',
14 | menuType: 'side'
15 | }
16 | }
17 | })
18 |
19 | // mutations
20 | const mutations = {
21 | isCollapseChange(state, type) {
22 | state.isCollapse = type
23 | },
24 | contentFullScreenChange(state, type) {
25 | state.contentFullScreen = type
26 | },
27 | menuListChange(state, arr) {
28 | state.menuList = arr
29 | },
30 | stateChange(state, option) {
31 | state[option.name] = option.value
32 | }
33 | }
34 |
35 | // actions
36 | const actions = {
37 |
38 | }
39 |
40 | export default {
41 | namespaced: true,
42 | state,
43 | actions,
44 | mutations
45 | }
--------------------------------------------------------------------------------
/js-i18n/src/store/modules/keepAlive.js:
--------------------------------------------------------------------------------
1 |
2 | const state = () => ({
3 | keepAliveComponentsName: [] // 需要缓存的组件名称
4 | })
5 |
6 | // mutations
7 | const mutations = {
8 | // 重置,Push, splice keep-alive对象
9 | setKeepAliveComponentsName(state, nameArr) {
10 | state.keepAliveComponentsName = nameArr
11 | },
12 | addKeepAliveComponentsName(state, name) {
13 | state.keepAliveComponentsName.push(name)
14 | },
15 | delKeepAliveComponentsName(state, name) {
16 | const key = state.keepAliveComponentsName.indexOf(name)
17 | if (key !== -1) {
18 | state.keepAliveComponentsName.splice(key, 1)
19 | console.log(state.keepAliveComponentsName)
20 | }
21 | }
22 | }
23 |
24 | const getters = {
25 | keepAliveComponentsName(state) {
26 | return state.keepAliveComponentsName
27 | }
28 | }
29 |
30 | // actions
31 | const actions = {
32 |
33 | }
34 |
35 | export default {
36 | namespaced: true,
37 | state,
38 | getters,
39 | actions,
40 | mutations
41 | }
--------------------------------------------------------------------------------
/js-i18n/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { loginApi, getInfoApi, loginOutApi } from '@/api/user'
2 |
3 | const state = () => ({
4 | token: '', // 登录token
5 | info: {}, // 用户信息
6 | })
7 |
8 | // getters
9 | const getters = {
10 | token(state) {
11 | return state.token
12 | }
13 | }
14 |
15 | // mutations
16 | const mutations = {
17 | tokenChange(state, token) {
18 | state.token = token
19 | },
20 | infoChange(state, info) {
21 | state.info = info
22 | }
23 | }
24 |
25 | // actions
26 | const actions = {
27 | // login by login.vue
28 | login({ commit, dispatch }, params) {
29 | return new Promise((resolve, reject) => {
30 | loginApi(params)
31 | .then(res => {
32 | commit('tokenChange', res.data.token)
33 | dispatch('getInfo', { token: res.data.token })
34 | .then(infoRes => {
35 | resolve(res.data.token)
36 | })
37 | })
38 | })
39 | },
40 | // get user info after user logined
41 | getInfo({ commit }, params) {
42 | return new Promise((resolve, reject) => {
43 | getInfoApi(params)
44 | .then(res => {
45 | commit('infoChange', res.data.info)
46 | resolve(res.data.info)
47 | })
48 | })
49 | },
50 |
51 | // login out the system after user click the loginOut button
52 | loginOut({ commit }) {
53 | loginOutApi()
54 | .then(res => {
55 |
56 | })
57 | .catch(error => {
58 |
59 | })
60 | .finally(() => {
61 | localStorage.removeItem('tabs')
62 | localStorage.removeItem('vuex')
63 | location.reload()
64 | })
65 | }
66 | }
67 |
68 | export default {
69 | namespaced: true,
70 | state,
71 | actions,
72 | getters,
73 | mutations
74 | }
--------------------------------------------------------------------------------
/js-i18n/src/store/plugins/persistent.js:
--------------------------------------------------------------------------------
1 | const exclude = ['actions', 'getters', 'mutations', 'namespaced']
2 | export default function Presistent({ key, modules, modulesKeys }) {
3 | return (store) => {
4 | const localOldState = JSON.parse(localStorage.getItem(key) || '{}')
5 | const sessionOldState = JSON.parse(sessionStorage.getItem(key) || '{}')
6 | let oldState = {}
7 | Object.assign(oldState, localOldState, sessionOldState)
8 | if (Object.keys(oldState).length > 0) {
9 | for (const oldKey in oldState) {
10 | modules[oldKey] = oldState[oldKey]
11 | }
12 | store.replaceState(modules)
13 | }
14 | store.subscribe((mutation, state) => {
15 | // 判断是否需要缓存数据至localStorage
16 | if (modulesKeys.local.length > 0) {
17 | const localData = setData(store.state, modulesKeys.local)
18 | localStorage.setItem(key, JSON.stringify(localData))
19 | } else {
20 | localStorage.removeItem(key)
21 | }
22 | // 判断是否需要缓存数据至sessionStorage
23 | if (modulesKeys.session.length > 0) {
24 | const sessionData = setData(store.state, modulesKeys.session)
25 | sessionStorage.setItem(key, JSON.stringify(sessionData))
26 | } else {
27 | sessionStorage.removeItem(key)
28 | }
29 | })
30 | }
31 | }
32 |
33 | function setData(state, module) {
34 | let data = {}
35 | for (const i of module) {
36 | data[i] = state[i]
37 | }
38 | return data
39 | }
--------------------------------------------------------------------------------
/js-i18n/src/theme/index.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | // 主题色
3 | --system-primary-color: #409eff; // 可做背景色和文本色,用做背景色时,需要和--system-primary-text-color配合使用,避免文件颜色和主题色冲突
4 | --system-primary-text-color: #fff; // 主题色作为背景色时使用
5 |
6 | // logo颜色相关
7 | --system-logo-color: #f1f1f1;
8 | --system-logo-background: #263445;
9 |
10 | // 菜单颜色相关
11 | --system-menu-text-color: #bfcbd9;
12 | --system-menu-background: #28415a;
13 | --system-menu-children-background: #1f2d3d;
14 | --system-menu-submenu-active-color: #fff;
15 | --system-menu-hover-background: #203448;
16 |
17 | // header区域
18 | --system-header-background: #fff;
19 | --system-header-text-color: #bbb;
20 | --system-header-breadcrumb-text-color: #97a8be;
21 | --system-header-item-hover-color: #000;
22 | --system-header-border-color: #d8dce5;
23 | --system-header-tab-background: #fff;
24 |
25 | // contaier区域,父框架
26 | --system-container-background: #f0f2f5;
27 | --system-container-main-background: #fff;
28 |
29 | // 页面区域, 这一块是你在自己写的文件中使用主题,核心需要关注的地方
30 | --system-page-background: #fff; // 主背景
31 | --system-page-color: #303133; // 主要的文本颜色
32 | --system-page-tip-color: rgba(0, 0, 0, 0.45); // 协助展示的文本颜色
33 | --system-page-border-color: #000; // 通用的边框配置色,便于主题扩展
34 |
35 | // element主题色修改
36 | --el-color-primary: var(--system-primary-color);
37 | }
38 |
39 | // 进度条颜色修改为主题色
40 | body #nprogress .bar {
41 | background-color: var(--system-primary-color);
42 | }
43 | body #nprogress .peg {
44 | box-shadow: 0 0 10px var(--system-primary-color), 0 0 5px var(--system-primary-color);
45 | }
46 | body #nprogress .spinner-icon {
47 | border-top-color: var(--system-primary-color);
48 | border-left-color: var(--system-primary-color);
49 | }
50 |
51 | @import './modules/dark.scss';
52 |
--------------------------------------------------------------------------------
/js-i18n/src/theme/modules/dark.scss:
--------------------------------------------------------------------------------
1 | .dark {
2 | // 通用
3 | p, h1, h2, h3, h4, h5, h6, article {
4 | color: var(--system-page-color);
5 | }
6 | .el-tree {
7 | background-color: var(--system-page-background);
8 | .el-tree-node__content:hover {
9 | background-color: #272727;
10 | }
11 | --el-color-primary-light-9: #272727;
12 | }
13 | .el-card {
14 | background-color: var(--system-page-background);
15 | color: var(--system-page-color);
16 | border-color: var(--system-page-border-color);
17 | .el-card__header {
18 | border-color: var(--system-page-border-color);
19 | }
20 | }
21 | // 页面内部样式修改
22 |
23 | }
--------------------------------------------------------------------------------
/js-i18n/src/utils/system/nprogress.js:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress"
2 | import "nprogress/nprogress.css"
3 |
4 | NProgress.configure({
5 | easing: 'ease', // 动画方式
6 | speed: 500, // 递增进度条的速度
7 | showSpinner: true, // 是否显示加载ico
8 | trickleSpeed: 200, // 自动递增间隔
9 | minimum: 0.3 // 初始化时的最小百分比
10 | })
11 |
12 | export default NProgress
--------------------------------------------------------------------------------
/js-i18n/src/utils/system/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import store from '@/store'
3 | import { ElMessage } from 'element-plus'
4 | const baseURL = import.meta.env.VITE_BASE_URL
5 |
6 | const service = axios.create({
7 | baseURL: baseURL,
8 | timeout: 5000
9 | })
10 |
11 | // 请求前的统一处理
12 | service.interceptors.request.use(
13 | (config) => {
14 | // JWT鉴权处理
15 | if (store.getters['user/token']) {
16 | config.headers['token'] = store.state.user.token
17 | }
18 | return config
19 | },
20 | (error) => {
21 | console.log(error) // for debug
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | service.interceptors.response.use(
27 | (response) => {
28 | const res = response.data
29 | if (res.code === 200) {
30 | return res
31 | } else {
32 | showError(res)
33 | return Promise.reject(res)
34 | }
35 | },
36 | (error)=> {
37 | console.log(error) // for debug
38 | const badMessage = error.message || error
39 | const code = parseInt(badMessage.toString().replace('Error: Request failed with status code ', ''))
40 | showError({ code, message: badMessage })
41 | return Promise.reject(error)
42 | }
43 | )
44 |
45 | function showError(error) {
46 | if (error.code === 403) {
47 | // to re-login
48 | store.dispatch('user/loginOut')
49 | } else {
50 | ElMessage({
51 | message: error.msg || error.message || '服务异常',
52 | type: 'error',
53 | duration: 3 * 1000
54 | })
55 | }
56 |
57 | }
58 |
59 | export default service
--------------------------------------------------------------------------------
/js-i18n/src/utils/system/statistics.js:
--------------------------------------------------------------------------------
1 | // 百度统计代码,需自行更换
2 | export function baidu() {
3 | const script = document.createElement('script')
4 | script.type = 'text/javascript'
5 | script.text = `
6 | var _hmt = _hmt || [];
7 | (function() {
8 | var hm = document.createElement("script");
9 | hm.src = "https://hm.baidu.com/hm.js?bd78bc908e66174e7dde385bf37cb4c1";
10 | var s = document.getElementsByTagName("script")[0];
11 | s.parentNode.insertBefore(hm, s);
12 | })();
13 | `
14 | document.getElementsByTagName('head')[0].appendChild(script)
15 | }
--------------------------------------------------------------------------------
/js-i18n/src/utils/system/title.js:
--------------------------------------------------------------------------------
1 | import i18n from '@/locale'
2 | import { systemTitle } from '@/config'
3 | const { t } = i18n.global
4 |
5 | export function changeTitle(name) {
6 | document.title = `${t(name)}-${t(systemTitle)}`
7 | }
8 |
--------------------------------------------------------------------------------
/js-i18n/src/views/main/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我是首页
4 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/js-i18n/src/views/main/pages/categoryTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js-i18n/src/views/main/pages/categoryTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
28 |
29 |
--------------------------------------------------------------------------------
/js-i18n/src/views/main/pages/crudTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js-i18n/src/views/main/pages/treeTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js-i18n/src/views/main/pages/treeTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
--------------------------------------------------------------------------------
/js-i18n/src/views/system/redirect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/js-i18n/vite.config.js:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 | import { viteMockServe } from 'vite-plugin-mock'
3 | import { resolve } from 'path'
4 |
5 | const pathResolve = (dir) => {
6 | return resolve(__dirname, ".", dir)
7 | }
8 |
9 | const alias = {
10 | '@': pathResolve("src")
11 | }
12 |
13 | // https://vitejs.dev/config/
14 | export default ({ command }) => {
15 | const prodMock = true;
16 | return {
17 | base: './',
18 | resolve: {
19 | alias
20 | },
21 | server: {
22 | port: 3003,
23 | host: '0.0.0.0',
24 | open: true,
25 | proxy: { // 代理配置
26 | '/dev': 'https://www.fastmock.site/mock/48cab8545e64d93ff9ba66a87ad04f6b/'
27 | },
28 | },
29 | build: {
30 | rollupOptions: {
31 | output: {
32 | manualChunks: {
33 |
34 | }
35 | }
36 | }
37 | },
38 | plugins: [
39 | vue(),
40 | viteMockServe({
41 | mockPath: 'mock',
42 | localEnabled: command === 'serve',
43 | prodEnabled: command !== 'serve' && prodMock,
44 | watchFiles: true,
45 | injectCode: `
46 | import { setupProdMockServer } from '../mockProdServer';
47 | setupProdMockServer();
48 | `,
49 | logger: true,
50 | }),
51 | ]
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/js/.env.development:
--------------------------------------------------------------------------------
1 | ENV = 'development'
2 |
3 | VITE_BASE_URL = '/dev-api'
--------------------------------------------------------------------------------
/js/.env.production:
--------------------------------------------------------------------------------
1 | ENV = 'production'
2 |
3 | VITE_BASE_URL = '/pro-api'
--------------------------------------------------------------------------------
/js/.env.staging:
--------------------------------------------------------------------------------
1 | ENV = 'staging'
2 |
3 | VITE_BASE_URL = '/test-api'
--------------------------------------------------------------------------------
/js/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | yarn.lock
7 | yarn-error.log
8 | .vscode
9 |
--------------------------------------------------------------------------------
/js/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 罗茜
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/js/VERSION.md:
--------------------------------------------------------------------------------
1 | # 版本更新日志
2 |
3 | ## 0.41版本
4 | 1. 【修改】菜单过长时,滚动条问题
5 | 2. 【新增】主题配置功能,核心编辑代码编写
6 |
7 | ## 0.40版本
8 | 1. 【优化】路由keep-alive状态保存与删除
9 | 2. 【新增】当前页刷新,缓存页刷新机制
10 | 3. 【新增】关闭标签栏时的缓存页对应状态变化
11 |
12 | ## 0.32版本
13 | 1. 【优化】左侧菜单栏复杂情况下的示例应用
14 | 2. 【新增】分类联动表格的实现
15 | 3. 【新增】树联动表格的实现
16 | 4. 【BUG】表格+开发BUG修复
17 |
18 | ## 0.31版本
19 | 1. 【新增】指令集模块完成
20 | 2. 【新增】echarts图表模块完成
21 |
22 | ## 0.24版本
23 | ```
24 | 1. 【新增】弹窗截图功能
25 | ```
26 |
27 | ## 0.23版本
28 | ```
29 | 1. 【新增】代码编辑器
30 | 2. 【新增】JSON编辑器
31 | 3. 【新增】可拖拽面板
32 | 4. 【新增】地图组件
33 | ```
34 |
35 | ## 0.22版本
36 | ```
37 | 1. 【新增】MD编辑器
38 | ```
39 |
40 | ## 0.21版本
41 | ```
42 | 1. 【统计】统计大部分需要实现的功能页面,并展示于左侧菜单中
43 | 2. 【新增】新增大部分需要实现的路由,并确保,大部分能于7月1日之前完全实现
44 | ```
45 |
46 | ## 0.11版本
47 | ```
48 | 1. 【新增】新增组件栏目,并补充按钮组进入
49 | 2. 【优化】多级菜单跳转demo
50 | 3. 【优化】侧边栏功能优化
51 | ```
52 |
53 | ## 0.10版本
54 | ```
55 | 1. 【新增】实现多级菜单Demo
56 | 2. 【优化】当菜单数据量过高时,优化显示
57 | ```
58 |
59 | ## 0.9版本
60 | ```
61 | 1. 【优化】弹窗组件可拖拽
62 | 2. 【优化】弹窗组件内部暴露逻辑,供外部使用
63 | ```
64 |
65 | ## 0.8版本
66 | ```
67 | 1. 【优化】axios提示配置
68 | 2. 【优化】公用组件内部逻辑及外部调用方法
69 | 3. 【补充】业务表格模块的数据调用方式
70 | ```
71 |
72 | ## 0.7版本
73 | ```
74 | 1. 【优化】element-ui国际化配置指南
75 | 2. 【优化】国际化配置本地存储
76 | ```
77 |
78 | ## 0.6版本
79 | ```
80 | 1. 【新增】按钮尺寸调整功能,针对element-plus的全局尺寸调整
81 | 2. 【新增】首页,图表类展示
82 | 3. 【新增】页面模块,目前包含了业务表格,主要为较为完善的crud写法,正在完善中
83 | 4. 【新增】基于echarts封装的业务chart组件,用户可快速利用此组件生成需要的图表
84 | ```
85 |
86 | ## 0.5.1版本
87 | ```
88 | 1. 【修复】mock地址,尽量不要使用较短的url,否则,打包上线后,较长的URL容易被冲突掉,示例:登录url和退出登录URL
89 | ```
90 |
91 | ## 0.5版本
92 | ```
93 | 1. 【优化】本地mock地址使用细节补充及细节优化
94 | 2. 【新增】登录页鉴权
95 | 3. 【新增】退出登录功能
96 | 4. 【优化】本地封装的axios插件优化使用方案
97 | ```
98 |
99 | ## 0.4版本
100 |
101 | ```
102 | 1. 【新增】登录页面,及手机自适应处理
103 | 2. 【新增】axios插件,请求处理机制建立
104 | 3. 【优化】全局/@/替换为@/
105 | 4. 【新增】本地mock地址模拟
106 | ```
107 |
108 | ## 0.3.1版本
109 |
110 | ```
111 | 1. 【实现】面包屑导航三种关闭功能实现
112 | ```
113 |
114 | ## 0.3版本
115 |
116 | ```
117 | 1. 【新增】github跳转链接
118 | 2. 【新增】主题配置功能[页面制作实现]
119 | 3. 【新增】vuex持久化插件,手写版
120 | ```
121 |
122 | ## 0.2版本
123 |
124 | ```
125 | 1. 【新增】框架国际化处理
126 | 2. 【BUG】打包上线上路由跳转功能异常,已修复
127 | ```
128 |
129 |
130 |
131 | ## 0.1版本
132 |
133 | ```
134 | 1. 集成vue-router,vuex,@vueuse/core,element-plus等核心插件
135 | 2. 全局状态管理方案实现
136 | 3. 全局路由管理方案实现
137 | 4. 核心组件库引入处理
138 | 5. 全局layout制作,及自适应处理
139 | 6. 菜单解决方案实现
140 | 7. 面包屑导航实现
141 | 8. 全屏功能实现
142 | ```
143 |
144 |
--------------------------------------------------------------------------------
/js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/js/mock/user.js:
--------------------------------------------------------------------------------
1 | const users = [
2 | { name: 'admin', password: '123456', token: 'admin', info: {
3 | name: '系统管理员'
4 | }},
5 | { name: 'editor', password: '123456', token: 'editor', info: {
6 | name: '编辑人员'
7 | }},
8 | { name: 'test', password: '123456', token: 'test', info: {
9 | name: '测试人员'
10 | }},
11 | ]
12 | export default [
13 | {
14 | url: `/mock/user/login`,
15 | method: 'post',
16 | response: ({ body }) => {
17 | const user = users.find(user => {
18 | return body.name === user.name && body.password === user.password
19 | })
20 | if (user) {
21 | return {
22 | code: 200,
23 | data: {
24 | token: user.token,
25 | },
26 | };
27 | } else {
28 | return {
29 | code: 401,
30 | data: {},
31 | msg: '用户名或密码错误'
32 | };
33 | }
34 |
35 | }
36 | },
37 | {
38 | url: `/mock/user/info`,
39 | method: 'post',
40 | response: ({ body }) => {
41 | const { token } = body
42 | const info = users.find(user => {
43 | return user.token === token
44 | }).info
45 | if (info) {
46 | return {
47 | code: 200,
48 | data: {
49 | info: info
50 | },
51 | };
52 | } else {
53 | return {
54 | code: 403,
55 | data: {},
56 | msg: '无访问权限'
57 | };
58 | }
59 |
60 | }
61 | },
62 | {
63 | url: `/mock/user/out`,
64 | method: 'post',
65 | response: () => {
66 | return {
67 | code: 200,
68 | data: {},
69 | msg: 'success'
70 | };
71 | }
72 | },
73 | {
74 | url: `/mock/user/passwordChange`,
75 | method: 'post',
76 | response: () => {
77 | return {
78 | code: 200,
79 | data: {},
80 | msg: 'success'
81 | };
82 | }
83 | },
84 | ]
--------------------------------------------------------------------------------
/js/mockProdServer.js:
--------------------------------------------------------------------------------
1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
2 | import userModule from './mock/user'
3 | import tableModule from './mock/table'
4 |
5 | export function setupProdMockServer() {
6 | createProdMockServer([
7 | ...userModule,
8 | ...tableModule
9 | ]);
10 | }
11 |
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "init",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "start": "vite",
7 | "build": "vite build --mode=production",
8 | "build:stag": "vite build --mode=staging",
9 | "serve": "vite preview"
10 | },
11 | "dependencies": {
12 | "@vueuse/core": "^4.10.0",
13 | "axios": "^0.21.1",
14 | "element-plus": "^1.0.2-beta.48",
15 | "mockjs": "^1.1.0",
16 | "normalize.css": "^8.0.1",
17 | "nprogress": "^0.2.0",
18 | "throttle-debounce": "^3.0.1",
19 | "vue": "^3.1.2",
20 | "vue-router": "4",
21 | "vuex": "^4.0.0"
22 | },
23 | "devDependencies": {
24 | "@types/node": "^15.0.3",
25 | "@vitejs/plugin-vue": "^1.2.2",
26 | "@vue/compiler-sfc": "^3.0.5",
27 | "sass": "^1.32.12",
28 | "vite": "2.3.7",
29 | "vite-plugin-mock": "2.8.0",
30 | "vue-tsc": "^0.0.24"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/js/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js/public/favicon.ico
--------------------------------------------------------------------------------
/js/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
31 |
--------------------------------------------------------------------------------
/js/src/api/table.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 获取数据api
4 | export function getData(data) {
5 | return request({
6 | url: '/table/list',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取分类数据
14 | export function getCategory(data) {
15 | return request({
16 | url: '/table/category',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 获取树组织数据
24 | export function getTree(data) {
25 | return request({
26 | url: '/table/tree',
27 | method: 'post',
28 | baseURL: '/mock',
29 | data
30 | })
31 | }
32 |
33 | // 新增
34 | export function add(data) {
35 | return request({
36 | url: '/table/add',
37 | method: 'post',
38 | baseURL: '/mock',
39 | data
40 | })
41 | }
42 |
43 | // 编辑
44 | export function update(data) {
45 | return request({
46 | url: '/table/update',
47 | method: 'post',
48 | baseURL: '/mock',
49 | data
50 | })
51 | }
52 |
53 | // 删除
54 | export function del(data) {
55 | return request({
56 | url: '/table/del',
57 | method: 'post',
58 | baseURL: '/mock',
59 | data
60 | })
61 | }
--------------------------------------------------------------------------------
/js/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 登录api
4 | export function loginApi(data) {
5 | return request({
6 | url: '/user/login',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取用户信息Api
14 | export function getInfoApi(data) {
15 | return request({
16 | url: '/user/info',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 退出登录Api
24 | export function loginOutApi() {
25 | return request({
26 | url: '/user/out',
27 | method: 'post',
28 | baseURL: '/mock'
29 | })
30 | }
31 |
32 | // 获取用户信息Api
33 | export function passwordChange(data) {
34 | return request({
35 | url: '/user/passwordChange',
36 | method: 'post',
37 | baseURL: '/mock',
38 | data
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/js/src/assets/images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js/src/assets/images/401.gif
--------------------------------------------------------------------------------
/js/src/assets/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js/src/assets/images/404.png
--------------------------------------------------------------------------------
/js/src/assets/images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js/src/assets/images/404_cloud.png
--------------------------------------------------------------------------------
/js/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/js/src/assets/logo.png
--------------------------------------------------------------------------------
/js/src/assets/style/common.scss:
--------------------------------------------------------------------------------
1 | @import "./transition.scss";
2 | @import "@/theme/index.scss";
3 | .layout-container {
4 | background-color: var(--system-container-main-background);
5 | width: calc(100% - 30px);
6 | height: calc(100% - 30px);
7 | margin: 15px;
8 | display: flex;
9 | flex-direction: column;
10 | overflow-y: auto;
11 | &-form {
12 | display: flex;
13 | justify-content: space-between;
14 | padding: 15px 15px 0;
15 | &-handle {
16 | display: flex;
17 | justify-content: flex-start;
18 | }
19 | &-search {
20 | display: flex;
21 | justify-content: flex-end;
22 | .search-btn {
23 | margin-left: 15px;
24 | }
25 | }
26 | .el-form-item {
27 | margin-bottom: 0;
28 | }
29 | }
30 | &-table {
31 | flex: 1;
32 | height: 100%;
33 | padding: 15px;
34 | overflow: auto;
35 | }
36 | }
37 | .flex-box {
38 | display: flex;
39 | flex-direction: column;
40 | width: 100%;
41 | height: 100%;
42 | padding: 15px;
43 | box-sizing: border-box;
44 | }
45 | .flex {
46 | display: flex;
47 | }
48 | .center {
49 | justify-content: center;
50 | align-items: center;
51 | text-align: center;
52 | }
53 | a {
54 | text-decoration: none;
55 | }
56 |
57 | /** element-plus **/
58 | .el-icon{
59 | text-align: center;
60 | }
61 |
62 | /** 用于提示信息 **/
63 | .my-tip {
64 | background-color: #f1f1f1;
65 | padding: 5px 10px;
66 | text-align: left;
67 | border-radius: 4px;
68 | }
69 | .system-scrollbar {
70 | &::-webkit-scrollbar {
71 | display: none;
72 | width: 6px;
73 | }
74 | &::-webkit-scrollbar-thumb {
75 | border-radius: 10px;
76 | background: rgba(144, 147, 153, 0.3);
77 | }
78 | &:hover {
79 | &::-webkit-scrollbar {
80 | display: block;
81 | }
82 | &::-webkit-scrollbar-thumb {
83 | border-radius: 10px;
84 | background: rgba(144, 147, 153, 0.3);
85 | &:hover {
86 | background: rgba(144, 147, 153, 0.5);
87 | }
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/js/src/assets/style/transition.scss:
--------------------------------------------------------------------------------
1 | /* fade-transform */
2 | .fade-transform-leave-active,
3 | .fade-transform-enter-active {
4 | transition: all .2s;
5 | }
6 |
7 | .fade-transform-enter-from {
8 | opacity: 0;
9 | transform: translateX(-30px);
10 | transition: all .2s;
11 | }
12 |
13 | .fade-transform-leave-to {
14 | opacity: 0;
15 | transform: translateX(30px);
16 | transition: all .2s;
17 | }
18 |
19 | /* breadcrumb transition */
20 | .breadcrumb-enter-active,
21 | .breadcrumb-leave-active {
22 | transition: all .2s;
23 | }
24 |
25 | .breadcrumb-enter,
26 | .breadcrumb-leave-active {
27 | opacity: 0;
28 | transform: translateX(80px);
29 | }
30 |
31 | .breadcrumb-move {
32 | transition: all .5s;
33 | }
34 |
35 | .breadcrumb-leave-active {
36 | position: absolute;
37 | }
--------------------------------------------------------------------------------
/js/src/components/layer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 | 确认
14 | 取消
15 |
16 |
17 |
18 |
19 |
20 |
21 |
57 |
58 |
--------------------------------------------------------------------------------
/js/src/components/menu/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/js/src/config/index.js:
--------------------------------------------------------------------------------
1 | const showLogo = true; // 是否显示Logo顶部模块
2 | const systemTitle = '后台管理系统' // 系统名称,用于显示在左上角模块,以及浏览器标题上使用,使用配置项
3 | export {
4 | systemTitle
5 | }
--------------------------------------------------------------------------------
/js/src/layout/Header/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ item.meta.title }}
9 |
10 | {{ item.meta.title }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
50 |
51 |
--------------------------------------------------------------------------------
/js/src/layout/Header/functionList/fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/js/src/layout/Header/functionList/sizeChange.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ d.name }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
60 |
61 |
--------------------------------------------------------------------------------
/js/src/layout/Header/functionList/theme/theme-color.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
48 |
49 |
--------------------------------------------------------------------------------
/js/src/layout/Logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ systemTitle }}
5 |
6 |
7 |
8 |
23 |
24 |
--------------------------------------------------------------------------------
/js/src/layout/Menu/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
39 |
--------------------------------------------------------------------------------
/js/src/layout/Tabs/tabsHook.js:
--------------------------------------------------------------------------------
1 | const tabsHook = {
2 | setItem: function(arr) {
3 | localStorage.setItem('tabs', JSON.stringify(arr))
4 | },
5 | getItem: function() {
6 | return JSON.parse(localStorage.getItem('tabs') || '[]')
7 | }
8 | }
9 | export default tabsHook
10 |
--------------------------------------------------------------------------------
/js/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import ElementPlus from 'element-plus'
3 | import { baidu } from './utils/system/statistics'
4 | import 'element-plus/lib/theme-chalk/index.css'
5 | import 'element-plus/lib/theme-chalk/display.css' // 引入基于断点的隐藏类
6 | import 'normalize.css' // css初始化
7 | import './assets/style/common.scss' // 公共css
8 | import App from './App.vue'
9 | import store from './store'
10 | import router from './router'
11 | // if (import.meta.env.MODE !== 'development') { // 非开发环境调用百度统计
12 | // baidu()
13 | // }
14 | const app = createApp(App)
15 | app.use(ElementPlus, { size: store.state.app.elementSize })
16 | app.use(store)
17 | app.use(router)
18 | // app.config.performance = true
19 | app.mount('#app')
20 |
--------------------------------------------------------------------------------
/js/src/router/createNode.js:
--------------------------------------------------------------------------------
1 | // 1. 用于解决keep-alive需要name的问题,动态生成随机name供keep-alive使用
2 | // 2. 用于解决transition动画内部结点只能为根元素的问题,单文件可写多结点
3 | import { defineComponent, h, createVNode, ref, nextTick } from 'vue'
4 | import reload from './reload.vue'
5 | import NProgress from '@/utils/system/nprogress'
6 |
7 | export function createNameComponent(component) {
8 | return () => {
9 | return new Promise((res) => {
10 | component().then((comm) => {
11 | const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now();
12 | const tempComm = defineComponent({
13 | name,
14 | setup() {
15 | const isReload = ref(false);
16 | let timeOut = null;
17 | const handleReload = () => {
18 | isReload.value = true;
19 | timeOut && clearTimeout(timeOut);
20 | NProgress.start();
21 | timeOut = setTimeout(() => {
22 | nextTick(() => {
23 | NProgress.done();
24 | isReload.value = false;
25 | });
26 | }, 260);
27 | };
28 | return {
29 | isReload,
30 | handleReload
31 | };
32 | },
33 | render: function () {
34 | if (this.isReload) {
35 | return h('div', { class: 'el-main-box' }, [h(reload)]);
36 | } else {
37 | return h('div', { class: 'el-main-box' }, [createVNode(comm.default)]);
38 | }
39 | }
40 | });
41 | res(tempComm);
42 | });
43 | });
44 | };
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/js/src/router/modules/dashboard.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/',
6 | component: Layout,
7 | redirect: '/dashboard',
8 | meta: { title: 'dashboard', icon: 'el-icon-menu' },
9 | children: [
10 | {
11 | path: 'dashboard',
12 | component: createNameComponent(() => import('@/views/main/dashboard/index.vue')),
13 | meta: { title: '首页', icon: 'el-icon-menu', hideClose: true }
14 | }
15 | ]
16 | }
17 | ]
18 |
19 | export default route
--------------------------------------------------------------------------------
/js/src/router/modules/pages.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/pages',
6 | component: Layout,
7 | redirect: '/pages/crudTable',
8 | meta: { title: '页面', icon: 'el-icon-document-copy' },
9 | alwayShow: true,
10 | children: [
11 | {
12 | path: 'crudTable',
13 | component: createNameComponent(() => import('@/views/main/pages/crudTable/index.vue')),
14 | meta: { title: '业务表格', cache: false, roles: ['admin', 'editor'] }
15 | },
16 | {
17 | path: 'categoryTable',
18 | component: createNameComponent(() => import('@/views/main/pages/categoryTable/index.vue')),
19 | meta: { title: '分类联动表格', cache: true, roles: ['admin'] }
20 | },
21 | {
22 | path: 'treeTable',
23 | component: createNameComponent(() => import('@/views/main/pages/treeTable/index.vue')),
24 | meta: { title: '树联动表格', cache: true }
25 | }
26 | ]
27 | }
28 | ]
29 |
30 | export default route
--------------------------------------------------------------------------------
/js/src/router/modules/system.js:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/system',
6 | component: Layout,
7 | redirect: '/404',
8 | hideMenu: true,
9 | meta: { title: '系统目录' },
10 | children: [
11 | {
12 | path: '/404',
13 | component: createNameComponent(() => import('@/views/system/404.vue')),
14 | meta: { title: '404', hideTabs: true }
15 | },
16 | {
17 | path: '/401',
18 | component: createNameComponent(() => import('@/views/system/401.vue')),
19 | meta: { title: '401', hideTabs: true }
20 | },
21 | {
22 | path: '/redirect/:path(.*)',
23 | component: createNameComponent(() => import('@/views/system/redirect.vue')),
24 | meta: { title: 'redirect', hideTabs: true }
25 | }
26 | ]
27 | },
28 | {
29 | path: '/login',
30 | component: createNameComponent(() => import('@/views/system/login.vue')),
31 | hideMenu: true,
32 | meta: { title: '登录', hideTabs: true }
33 | },
34 | {
35 | // 找不到路由重定向到404页面
36 | path: "/:pathMatch(.*)",
37 | component: Layout,
38 | redirect: "/404",
39 | hideMenu: true
40 | },
41 | ]
42 |
43 | export default route
--------------------------------------------------------------------------------
/js/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, createLogger } from 'vuex'
2 | import Presistent from './plugins/persistent'
3 | const debug = process.env.NODE_ENV !== 'production'
4 |
5 | const files= import.meta.globEager('./modules/*.js')
6 |
7 | let modules = {}
8 | Object.keys(files).forEach((c) => {
9 | const module = files[c].default
10 | const moduleName = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | modules[moduleName] = module
12 | })
13 |
14 | const presistent = Presistent({ key: 'vuex', modules, modulesKeys: {
15 | local: Object.keys(modules),
16 | session: []
17 | } })
18 |
19 | export default createStore({
20 | modules: {
21 | ...modules
22 | },
23 | strict: debug,
24 | plugins: debug ? [createLogger(), presistent] : [presistent]
25 | })
--------------------------------------------------------------------------------
/js/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | const state = () => ({
2 | isCollapse: false, // 侧边栏是否收缩展示
3 | contentFullScreen: false, // 内容是否可全屏展示
4 | showLogo: true, // 是否显示Logo
5 | fixedTop: false, // 是否固定顶部, todo,暂未使用
6 | showTabs: true, // 是否显示导航历史
7 | expandOneMenu: true, // 一次是否只能展开一个菜单
8 | elementSize: 'mini', // element默认尺寸,支持官网四个大小参数
9 | theme: {
10 | state: {
11 | style: 'default',
12 | primaryColor: '#409eff',
13 | menuType: 'side'
14 | }
15 | }
16 | })
17 |
18 | // mutations
19 | const mutations = {
20 | isCollapseChange(state, type) {
21 | state.isCollapse = type
22 | },
23 | contentFullScreenChange(state, type) {
24 | state.contentFullScreen = type
25 | },
26 | menuListChange(state, arr) {
27 | state.menuList = arr
28 | },
29 | stateChange(state, option) {
30 | state[option.name] = option.value
31 | }
32 | }
33 |
34 | // actions
35 | const actions = {
36 |
37 | }
38 |
39 | export default {
40 | namespaced: true,
41 | state,
42 | actions,
43 | mutations
44 | }
--------------------------------------------------------------------------------
/js/src/store/modules/keepAlive.js:
--------------------------------------------------------------------------------
1 |
2 | const state = () => ({
3 | keepAliveComponentsName: [] // 需要缓存的组件名称
4 | })
5 |
6 | // mutations
7 | const mutations = {
8 | // 重置,Push, splice keep-alive对象
9 | setKeepAliveComponentsName(state, nameArr) {
10 | state.keepAliveComponentsName = nameArr
11 | },
12 | addKeepAliveComponentsName(state, name) {
13 | state.keepAliveComponentsName.push(name)
14 | },
15 | delKeepAliveComponentsName(state, name) {
16 | const key = state.keepAliveComponentsName.indexOf(name)
17 | if (key !== -1) {
18 | state.keepAliveComponentsName.splice(key, 1)
19 | console.log(state.keepAliveComponentsName)
20 | }
21 | }
22 | }
23 |
24 | const getters = {
25 | keepAliveComponentsName(state) {
26 | return state.keepAliveComponentsName
27 | }
28 | }
29 |
30 | // actions
31 | const actions = {
32 |
33 | }
34 |
35 | export default {
36 | namespaced: true,
37 | state,
38 | getters,
39 | actions,
40 | mutations
41 | }
--------------------------------------------------------------------------------
/js/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { loginApi, getInfoApi, loginOutApi } from '@/api/user'
2 |
3 | const state = () => ({
4 | token: '', // 登录token
5 | info: {}, // 用户信息
6 | })
7 |
8 | // getters
9 | const getters = {
10 | token(state) {
11 | return state.token
12 | }
13 | }
14 |
15 | // mutations
16 | const mutations = {
17 | tokenChange(state, token) {
18 | state.token = token
19 | },
20 | infoChange(state, info) {
21 | state.info = info
22 | }
23 | }
24 |
25 | // actions
26 | const actions = {
27 | // login by login.vue
28 | login({ commit, dispatch }, params) {
29 | return new Promise((resolve, reject) => {
30 | loginApi(params)
31 | .then(res => {
32 | commit('tokenChange', res.data.token)
33 | dispatch('getInfo', { token: res.data.token })
34 | .then(infoRes => {
35 | resolve(res.data.token)
36 | })
37 | })
38 | })
39 | },
40 | // get user info after user logined
41 | getInfo({ commit }, params) {
42 | return new Promise((resolve, reject) => {
43 | getInfoApi(params)
44 | .then(res => {
45 | commit('infoChange', res.data.info)
46 | resolve(res.data.info)
47 | })
48 | })
49 | },
50 |
51 | // login out the system after user click the loginOut button
52 | loginOut({ commit }) {
53 | loginOutApi()
54 | .then(res => {
55 |
56 | })
57 | .catch(error => {
58 |
59 | })
60 | .finally(() => {
61 | localStorage.removeItem('tabs')
62 | localStorage.removeItem('vuex')
63 | location.reload()
64 | })
65 | }
66 | }
67 |
68 | export default {
69 | namespaced: true,
70 | state,
71 | actions,
72 | getters,
73 | mutations
74 | }
--------------------------------------------------------------------------------
/js/src/store/plugins/persistent.js:
--------------------------------------------------------------------------------
1 | const exclude = ['actions', 'getters', 'mutations', 'namespaced']
2 | export default function Presistent({ key, modules, modulesKeys }) {
3 | return (store) => {
4 | const localOldState = JSON.parse(localStorage.getItem(key) || '{}')
5 | const sessionOldState = JSON.parse(sessionStorage.getItem(key) || '{}')
6 | let oldState = {}
7 | Object.assign(oldState, localOldState, sessionOldState)
8 | if (Object.keys(oldState).length > 0) {
9 | for (const oldKey in oldState) {
10 | modules[oldKey] = oldState[oldKey]
11 | }
12 | store.replaceState(modules)
13 | }
14 | store.subscribe((mutation, state) => {
15 | // 判断是否需要缓存数据至localStorage
16 | if (modulesKeys.local.length > 0) {
17 | const localData = setData(store.state, modulesKeys.local)
18 | localStorage.setItem(key, JSON.stringify(localData))
19 | } else {
20 | localStorage.removeItem(key)
21 | }
22 | // 判断是否需要缓存数据至sessionStorage
23 | if (modulesKeys.session.length > 0) {
24 | const sessionData = setData(store.state, modulesKeys.session)
25 | sessionStorage.setItem(key, JSON.stringify(sessionData))
26 | } else {
27 | sessionStorage.removeItem(key)
28 | }
29 | })
30 | }
31 | }
32 |
33 | function setData(state, module) {
34 | let data = {}
35 | for (const i of module) {
36 | data[i] = state[i]
37 | }
38 | return data
39 | }
--------------------------------------------------------------------------------
/js/src/theme/index.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | // 主题色
3 | --system-primary-color: #409eff; // 可做背景色和文本色,用做背景色时,需要和--system-primary-text-color配合使用,避免文件颜色和主题色冲突
4 | --system-primary-text-color: #fff; // 主题色作为背景色时使用
5 |
6 | // logo颜色相关
7 | --system-logo-color: #f1f1f1;
8 | --system-logo-background: #263445;
9 |
10 | // 菜单颜色相关
11 | --system-menu-text-color: #bfcbd9;
12 | --system-menu-background: #28415a;
13 | --system-menu-children-background: #1f2d3d;
14 | --system-menu-submenu-active-color: #fff;
15 | --system-menu-hover-background: #203448;
16 |
17 | // header区域
18 | --system-header-background: #fff;
19 | --system-header-text-color: #bbb;
20 | --system-header-breadcrumb-text-color: #97a8be;
21 | --system-header-item-hover-color: #000;
22 | --system-header-border-color: #d8dce5;
23 | --system-header-tab-background: #fff;
24 |
25 | // contaier区域,父框架
26 | --system-container-background: #f0f2f5;
27 | --system-container-main-background: #fff;
28 |
29 | // 页面区域, 这一块是你在自己写的文件中使用主题,核心需要关注的地方
30 | --system-page-background: #fff; // 主背景
31 | --system-page-color: #303133; // 主要的文本颜色
32 | --system-page-tip-color: rgba(0, 0, 0, 0.45); // 协助展示的文本颜色
33 | --system-page-border-color: #000; // 通用的边框配置色,便于主题扩展
34 |
35 | // element主题色修改
36 | --el-color-primary: var(--system-primary-color);
37 | }
38 |
39 | // 进度条颜色修改为主题色
40 | body #nprogress .bar {
41 | background-color: var(--system-primary-color);
42 | }
43 | body #nprogress .peg {
44 | box-shadow: 0 0 10px var(--system-primary-color), 0 0 5px var(--system-primary-color);
45 | }
46 | body #nprogress .spinner-icon {
47 | border-top-color: var(--system-primary-color);
48 | border-left-color: var(--system-primary-color);
49 | }
50 |
51 | @import './modules/dark.scss';
52 |
--------------------------------------------------------------------------------
/js/src/theme/modules/dark.scss:
--------------------------------------------------------------------------------
1 | .dark {
2 | // 通用
3 | p, h1, h2, h3, h4, h5, h6, article {
4 | color: var(--system-page-color);
5 | }
6 | .el-tree {
7 | background-color: var(--system-page-background);
8 | .el-tree-node__content:hover {
9 | background-color: #272727;
10 | }
11 | --el-color-primary-light-9: #272727;
12 | }
13 | .el-card {
14 | background-color: var(--system-page-background);
15 | color: var(--system-page-color);
16 | border-color: var(--system-page-border-color);
17 | .el-card__header {
18 | border-color: var(--system-page-border-color);
19 | }
20 | }
21 | // 页面内部样式修改
22 |
23 | }
--------------------------------------------------------------------------------
/js/src/utils/system/nprogress.js:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress"
2 | import "nprogress/nprogress.css"
3 |
4 | NProgress.configure({
5 | easing: 'ease', // 动画方式
6 | speed: 500, // 递增进度条的速度
7 | showSpinner: true, // 是否显示加载ico
8 | trickleSpeed: 200, // 自动递增间隔
9 | minimum: 0.3 // 初始化时的最小百分比
10 | })
11 |
12 | export default NProgress
--------------------------------------------------------------------------------
/js/src/utils/system/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import store from '@/store'
3 | import { ElMessage } from 'element-plus'
4 | const baseURL = import.meta.env.VITE_BASE_URL
5 |
6 | const service = axios.create({
7 | baseURL: baseURL,
8 | timeout: 5000
9 | })
10 |
11 | // 请求前的统一处理
12 | service.interceptors.request.use(
13 | (config) => {
14 | // JWT鉴权处理
15 | if (store.getters['user/token']) {
16 | config.headers['token'] = store.state.user.token
17 | }
18 | return config
19 | },
20 | (error) => {
21 | console.log(error) // for debug
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | service.interceptors.response.use(
27 | (response) => {
28 | const res = response.data
29 | if (res.code === 200) {
30 | return res
31 | } else {
32 | showError(res)
33 | return Promise.reject(res)
34 | }
35 | },
36 | (error)=> {
37 | console.log(error) // for debug
38 | const badMessage = error.message || error
39 | const code = parseInt(badMessage.toString().replace('Error: Request failed with status code ', ''))
40 | showError({ code, message: badMessage })
41 | return Promise.reject(error)
42 | }
43 | )
44 |
45 | function showError(error) {
46 | if (error.code === 403) {
47 | // to re-login
48 | store.dispatch('user/loginOut')
49 | } else {
50 | ElMessage({
51 | message: error.msg || error.message || '服务异常',
52 | type: 'error',
53 | duration: 3 * 1000
54 | })
55 | }
56 |
57 | }
58 |
59 | export default service
--------------------------------------------------------------------------------
/js/src/utils/system/statistics.js:
--------------------------------------------------------------------------------
1 | // 百度统计代码,需自行更换
2 | export function baidu() {
3 | const script = document.createElement('script')
4 | script.type = 'text/javascript'
5 | script.text = `
6 | var _hmt = _hmt || [];
7 | (function() {
8 | var hm = document.createElement("script");
9 | hm.src = "https://hm.baidu.com/hm.js?bd78bc908e66174e7dde385bf37cb4c1";
10 | var s = document.getElementsByTagName("script")[0];
11 | s.parentNode.insertBefore(hm, s);
12 | })();
13 | `
14 | document.getElementsByTagName('head')[0].appendChild(script)
15 | }
--------------------------------------------------------------------------------
/js/src/utils/system/title.js:
--------------------------------------------------------------------------------
1 | import { systemTitle } from '@/config'
2 |
3 | export function changeTitle(name) {
4 | document.title = `${name}-${systemTitle}`
5 | }
6 |
--------------------------------------------------------------------------------
/js/src/views/main/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我是首页
4 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/js/src/views/main/pages/categoryTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js/src/views/main/pages/categoryTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
28 |
29 |
--------------------------------------------------------------------------------
/js/src/views/main/pages/crudTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js/src/views/main/pages/treeTable/enum.js:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/js/src/views/main/pages/treeTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
--------------------------------------------------------------------------------
/js/src/views/system/redirect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/js/vite.config.js:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 | import { viteMockServe } from 'vite-plugin-mock'
3 | import { resolve } from 'path'
4 |
5 | const pathResolve = (dir) => {
6 | return resolve(__dirname, ".", dir)
7 | }
8 |
9 | const alias = {
10 | '@': pathResolve("src")
11 | }
12 |
13 | // https://vitejs.dev/config/
14 | export default ({ command }) => {
15 | const prodMock = true;
16 | return {
17 | base: './',
18 | resolve: {
19 | alias
20 | },
21 | server: {
22 | port: 3002,
23 | host: '0.0.0.0',
24 | open: true,
25 | proxy: { // 代理配置
26 | '/dev': 'https://www.fastmock.site/mock/48cab8545e64d93ff9ba66a87ad04f6b/'
27 | },
28 | },
29 | build: {
30 | rollupOptions: {
31 | output: {
32 | manualChunks: {
33 |
34 | }
35 | }
36 | }
37 | },
38 | plugins: [
39 | vue(),
40 | viteMockServe({
41 | mockPath: 'mock',
42 | localEnabled: command === 'serve',
43 | prodEnabled: command !== 'serve' && prodMock,
44 | watchFiles: true,
45 | injectCode: `
46 | import { setupProdMockServer } from '../mockProdServer';
47 | setupProdMockServer();
48 | `,
49 | logger: true,
50 | }),
51 | ]
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/ts-i18n/.env.development:
--------------------------------------------------------------------------------
1 | ENV = 'development'
2 |
3 | VITE_BASE_URL = '/dev-api'
--------------------------------------------------------------------------------
/ts-i18n/.env.production:
--------------------------------------------------------------------------------
1 | ENV = 'production'
2 |
3 | VITE_BASE_URL = '/pro-api'
--------------------------------------------------------------------------------
/ts-i18n/.env.staging:
--------------------------------------------------------------------------------
1 | ENV = 'staging'
2 |
3 | VITE_BASE_URL = '/test-api'
--------------------------------------------------------------------------------
/ts-i18n/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | yarn.lock
7 | yarn-error.log
8 | .vscode
9 |
--------------------------------------------------------------------------------
/ts-i18n/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 罗茜
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/ts-i18n/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ts-i18n/mock/user.ts:
--------------------------------------------------------------------------------
1 | import { MockMethod } from 'vite-plugin-mock'
2 | const users = [
3 | { name: 'admin', password: '123456', token: 'admin', info: {
4 | name: '系统管理员'
5 | }},
6 | { name: 'editor', password: '123456', token: 'editor', info: {
7 | name: '编辑人员'
8 | }},
9 | { name: 'test', password: '123456', token: 'test', info: {
10 | name: '测试人员'
11 | }},
12 | ]
13 | export default [
14 | {
15 | url: `/mock/user/login`,
16 | method: 'post',
17 | response: ({ body }) => {
18 | const user = users.find(user => {
19 | return body.name === user.name && body.password === user.password
20 | })
21 | if (user) {
22 | return {
23 | code: 200,
24 | data: {
25 | token: user.token,
26 | },
27 | };
28 | } else {
29 | return {
30 | code: 401,
31 | data: {},
32 | msg: '用户名或密码错误'
33 | };
34 | }
35 |
36 | }
37 | },
38 | {
39 | url: `/mock/user/info`,
40 | method: 'post',
41 | response: ({ body }) => {
42 | const { token } = body
43 | const info = users.find(user => {
44 | return user.token === token
45 | }).info
46 | if (info) {
47 | return {
48 | code: 200,
49 | data: {
50 | info: info
51 | },
52 | };
53 | } else {
54 | return {
55 | code: 403,
56 | data: {},
57 | msg: '无访问权限'
58 | };
59 | }
60 |
61 | }
62 | },
63 | {
64 | url: `/mock/user/out`,
65 | method: 'post',
66 | response: () => {
67 | return {
68 | code: 200,
69 | data: {},
70 | msg: 'success'
71 | };
72 | }
73 | },
74 | {
75 | url: `/mock/user/passwordChange`,
76 | method: 'post',
77 | response: () => {
78 | return {
79 | code: 200,
80 | data: {},
81 | msg: 'success'
82 | };
83 | }
84 | },
85 | ] as MockMethod[]
--------------------------------------------------------------------------------
/ts-i18n/mockProdServer.ts:
--------------------------------------------------------------------------------
1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
2 | import userModule from './mock/user'
3 | import tableModule from './mock/table'
4 |
5 | export function setupProdMockServer() {
6 | createProdMockServer([
7 | ...userModule,
8 | ...tableModule
9 | ]);
10 | }
11 |
--------------------------------------------------------------------------------
/ts-i18n/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "init",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "start": "vite",
7 | "build": "vite build --mode=production",
8 | "build:stag": "vite build --mode=staging",
9 | "serve": "vite preview"
10 | },
11 | "dependencies": {
12 | "@vueuse/core": "^4.10.0",
13 | "axios": "^0.21.1",
14 | "element-plus": "^1.0.2-beta.48",
15 | "mockjs": "^1.1.0",
16 | "normalize.css": "^8.0.1",
17 | "nprogress": "^0.2.0",
18 | "throttle-debounce": "^3.0.1",
19 | "vue": "^3.1.2",
20 | "vue-i18n": "^9.1.6",
21 | "vue-router": "4",
22 | "vuex": "^4.0.0"
23 | },
24 | "devDependencies": {
25 | "@types/node": "^15.0.3",
26 | "@vitejs/plugin-vue": "^1.2.2",
27 | "@vue/compiler-sfc": "^3.0.5",
28 | "sass": "^1.32.12",
29 | "typescript": "^4.1.3",
30 | "vite": "2.3.7",
31 | "vite-plugin-mock": "2.8.0",
32 | "vue-tsc": "^0.0.24"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ts-i18n/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts-i18n/public/favicon.ico
--------------------------------------------------------------------------------
/ts-i18n/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
27 |
38 |
--------------------------------------------------------------------------------
/ts-i18n/src/api/table.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 获取数据api
4 | export function getData(data: object) {
5 | return request({
6 | url: '/table/list',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取分类数据
14 | export function getCategory(data: object) {
15 | return request({
16 | url: '/table/category',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 获取树组织数据
24 | export function getTree(data: object) {
25 | return request({
26 | url: '/table/tree',
27 | method: 'post',
28 | baseURL: '/mock',
29 | data
30 | })
31 | }
32 |
33 | // 新增
34 | export function add(data: object) {
35 | return request({
36 | url: '/table/add',
37 | method: 'post',
38 | baseURL: '/mock',
39 | data
40 | })
41 | }
42 |
43 | // 编辑
44 | export function update(data: object) {
45 | return request({
46 | url: '/table/update',
47 | method: 'post',
48 | baseURL: '/mock',
49 | data
50 | })
51 | }
52 |
53 | // 删除
54 | export function del(data: object) {
55 | return request({
56 | url: '/table/del',
57 | method: 'post',
58 | baseURL: '/mock',
59 | data
60 | })
61 | }
--------------------------------------------------------------------------------
/ts-i18n/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 登录api
4 | export function loginApi(data: object) {
5 | return request({
6 | url: '/user/login',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取用户信息Api
14 | export function getInfoApi(data: object) {
15 | return request({
16 | url: '/user/info',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 退出登录Api
24 | export function loginOutApi() {
25 | return request({
26 | url: '/user/out',
27 | method: 'post',
28 | baseURL: '/mock'
29 | })
30 | }
31 |
32 | // 获取用户信息Api
33 | export function passwordChange(data: object) {
34 | return request({
35 | url: '/user/passwordChange',
36 | method: 'post',
37 | baseURL: '/mock',
38 | data
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/ts-i18n/src/assets/images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts-i18n/src/assets/images/401.gif
--------------------------------------------------------------------------------
/ts-i18n/src/assets/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts-i18n/src/assets/images/404.png
--------------------------------------------------------------------------------
/ts-i18n/src/assets/images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts-i18n/src/assets/images/404_cloud.png
--------------------------------------------------------------------------------
/ts-i18n/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts-i18n/src/assets/logo.png
--------------------------------------------------------------------------------
/ts-i18n/src/assets/style/common.scss:
--------------------------------------------------------------------------------
1 | @import "./transition.scss";
2 | @import "@/theme/index.scss";
3 | .layout-container {
4 | background-color: var(--system-container-main-background);
5 | width: calc(100% - 30px);
6 | height: calc(100% - 30px);
7 | margin: 15px;
8 | display: flex;
9 | flex-direction: column;
10 | overflow-y: auto;
11 | &-form {
12 | display: flex;
13 | justify-content: space-between;
14 | padding: 15px 15px 0;
15 | &-handle {
16 | display: flex;
17 | justify-content: flex-start;
18 | }
19 | &-search {
20 | display: flex;
21 | justify-content: flex-end;
22 | .search-btn {
23 | margin-left: 15px;
24 | }
25 | }
26 | .el-form-item {
27 | margin-bottom: 0;
28 | }
29 | }
30 | &-table {
31 | flex: 1;
32 | height: 100%;
33 | padding: 15px;
34 | overflow: auto;
35 | }
36 | }
37 | .flex-box {
38 | display: flex;
39 | flex-direction: column;
40 | width: 100%;
41 | height: 100%;
42 | padding: 15px;
43 | box-sizing: border-box;
44 | }
45 | .flex {
46 | display: flex;
47 | }
48 | .center {
49 | justify-content: center;
50 | align-items: center;
51 | text-align: center;
52 | }
53 | a {
54 | text-decoration: none;
55 | }
56 |
57 | /** element-plus **/
58 | .el-icon{
59 | text-align: center;
60 | }
61 |
62 | /** 用于提示信息 **/
63 | .my-tip {
64 | background-color: #f1f1f1;
65 | padding: 5px 10px;
66 | text-align: left;
67 | border-radius: 4px;
68 | }
69 | .system-scrollbar {
70 | &::-webkit-scrollbar {
71 | display: none;
72 | width: 6px;
73 | }
74 | &::-webkit-scrollbar-thumb {
75 | border-radius: 10px;
76 | background: rgba(144, 147, 153, 0.3);
77 | }
78 | &:hover {
79 | &::-webkit-scrollbar {
80 | display: block;
81 | }
82 | &::-webkit-scrollbar-thumb {
83 | border-radius: 10px;
84 | background: rgba(144, 147, 153, 0.3);
85 | &:hover {
86 | background: rgba(144, 147, 153, 0.5);
87 | }
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/ts-i18n/src/assets/style/transition.scss:
--------------------------------------------------------------------------------
1 | /* fade-transform */
2 | .fade-transform-leave-active,
3 | .fade-transform-enter-active {
4 | transition: all .2s;
5 | }
6 |
7 | .fade-transform-enter-from {
8 | opacity: 0;
9 | transform: translateX(-30px);
10 | transition: all .2s;
11 | }
12 |
13 | .fade-transform-leave-to {
14 | opacity: 0;
15 | transform: translateX(30px);
16 | transition: all .2s;
17 | }
18 |
19 | /* breadcrumb transition */
20 | .breadcrumb-enter-active,
21 | .breadcrumb-leave-active {
22 | transition: all .2s;
23 | }
24 |
25 | .breadcrumb-enter,
26 | .breadcrumb-leave-active {
27 | opacity: 0;
28 | transform: translateX(80px);
29 | }
30 |
31 | .breadcrumb-move {
32 | transition: all .5s;
33 | }
34 |
35 | .breadcrumb-leave-active {
36 | position: absolute;
37 | }
--------------------------------------------------------------------------------
/ts-i18n/src/components/layer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 | 确认
14 | 取消
15 |
16 |
17 |
18 |
19 |
20 |
21 |
72 |
73 |
--------------------------------------------------------------------------------
/ts-i18n/src/components/menu/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ts-i18n/src/components/table/type.ts:
--------------------------------------------------------------------------------
1 | export interface Page{
2 | index: Number,
3 | size: Number,
4 | total: Number
5 | }
--------------------------------------------------------------------------------
/ts-i18n/src/config/index.ts:
--------------------------------------------------------------------------------
1 | const showLogo: Boolean = true; // 是否显示Logo顶部模块
2 | const systemTitle = 'message.system.title' // 系统名称,用于显示在左上角模块,以及浏览器标题上使用,使用配置项
3 | export {
4 | systemTitle
5 | }
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Header/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $t(item.meta.title) }}
9 |
10 | {{ $t(item.meta.title) }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
50 |
51 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Header/functionList/fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Header/functionList/sizeChange.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ $t(d.name) }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
60 |
61 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Header/functionList/theme/theme-color.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
48 |
49 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Header/functionList/word.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ locale.message.language }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
46 |
47 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ $t(systemTitle) }}
5 |
6 |
7 |
8 |
23 |
24 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Menu/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
39 |
--------------------------------------------------------------------------------
/ts-i18n/src/layout/Tabs/tabsHook.ts:
--------------------------------------------------------------------------------
1 | const tabsHook = {
2 | setItem: function(arr: object[]) {
3 | localStorage.setItem('tabs', JSON.stringify(arr))
4 | },
5 | getItem: function() {
6 | return JSON.parse(localStorage.getItem('tabs') || '[]')
7 | }
8 | }
9 | export default tabsHook
10 |
--------------------------------------------------------------------------------
/ts-i18n/src/locale/index.ts:
--------------------------------------------------------------------------------
1 | // 提示信息仅在开发环境生效
2 | import { createI18n, LocaleMessages, VueMessageType } from 'vue-i18n'
3 | import store from '@/store'
4 |
5 | const files= import.meta.globEager('./modules/*.ts')
6 |
7 | let messages: LocaleMessages = {}
8 | Object.keys(files).forEach((c: string) => {
9 | const module = files[c].default
10 | const moduleName: string = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | messages[moduleName] = module
12 | })
13 |
14 | const lang = store.state.app.lang || navigator.userLanguage || navigator.language // 初次进入,采用浏览器当前设置的语言,默认采用中文
15 | const locale = lang.indexOf('en') !== -1 ? 'en' : 'zh-cn'
16 |
17 | const i18n = createI18n({
18 | __VUE_I18N_LEGACY_API__: false,
19 | __VUE_I18N_FULL_INSTALL__: false,
20 | locale: locale,
21 | fallbackLocale: 'zh-cn',
22 | messages
23 | })
24 |
25 | document.querySelector('html')!.setAttribute('lang', locale)
26 |
27 | export default i18n
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/en.ts:
--------------------------------------------------------------------------------
1 | import enLocale from 'element-plus/lib/locale/lang/en'
2 | import system from './en/system'
3 | import common from './en/common'
4 | import menu from './en/menu'
5 | const lang = {
6 | el: enLocale.el, // element-plus i18 setting
7 | message: {
8 | language: 'English',
9 | ...system,
10 | ...common,
11 | ...menu
12 | }
13 | }
14 |
15 | export default lang
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/en/common.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | common: {
3 | search: 'search',
4 | searchTip: 'please input keyword',
5 | add: 'add',
6 | update: 'update',
7 | del: 'delete',
8 | delBat: 'delete choose',
9 | delTip: 'Are you sure delete the selection data ?',
10 | handle: 'handle'
11 | },
12 | }
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/en/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | menu: {
3 | dashboard: {
4 | name: 'dashboard',
5 | index: 'index'
6 | },
7 | system: {
8 | name: 'system',
9 | redirect: 'redirect',
10 | '404': '404',
11 | '401': '401'
12 | },
13 | page: {
14 | name: 'page',
15 | crudTable: 'crudTable',
16 | categoryTable: 'categoryTable',
17 | treeTable: 'treeTable',
18 | },
19 | },
20 | }
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/en/system.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | system: {
3 | title: 'backendsystem',
4 | login: 'login',
5 | userName: 'userName',
6 | password: 'password',
7 | contentScreen: 'content full screen',
8 | fullScreen: 'fullscreen',
9 | fullScreenBack: 'back fullscreen',
10 | github: 'visit github',
11 | changePassword: 'change password',
12 | loginOut: 'login out',
13 | user: 'admin',
14 | size: {
15 | default: 'default',
16 | medium: 'medium',
17 | small: 'small',
18 | mini: 'mini'
19 | },
20 | setting: {
21 | name: 'setting',
22 |
23 | style: {
24 | name: 'full style setting',
25 | default: 'default menu style',
26 | light: 'light menu style',
27 | dark: 'dark menu style'
28 | },
29 | primaryColor: {
30 | name: 'primary color',
31 | blue: 'default blue',
32 | red: 'rose red',
33 | violet: 'grace violet',
34 | green: 'story green',
35 | cyan: 'cyan',
36 | black: 'geek black'
37 | },
38 | other: {
39 | name: 'other setting',
40 | showLogo: 'show logo',
41 | showBreadcrumb: 'show breadcrumb',
42 | keepOnlyOneMenu: 'keep only one menu open',
43 | }
44 | },
45 | tab: {
46 | reload: 'refresh',
47 | closeAll: 'close all tags',
48 | closeOther: 'close other tags',
49 | closeCurrent: 'close current tag'
50 | }
51 | },
52 | }
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/zh-cn.ts:
--------------------------------------------------------------------------------
1 | import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
2 | import system from './zh-cn/system'
3 | import common from './zh-cn/common'
4 | import menu from './zh-cn/menu'
5 | const lang = {
6 | el: zhLocale.el, // element内部国际化
7 | message: {
8 | language: '中文',
9 | ...system,
10 | ...common,
11 | ...menu
12 | }
13 | }
14 |
15 | export default lang
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/zh-cn/common.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | common: {
3 | search: '搜索',
4 | searchTip: '请输入关键词进行检索',
5 | add: '新增',
6 | update: '编辑',
7 | del: '删除',
8 | delBat: '批量删除',
9 | delTip: '确定删除选中的数据吗?',
10 | handle: '操作'
11 | },
12 | }
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/zh-cn/menu.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | menu: {
3 | dashboard: {
4 | name: 'dashboard',
5 | index: '首页'
6 | },
7 | system: {
8 | name: '系统目录',
9 | redirect: '重定向页面',
10 | '404': '404',
11 | '401': '401'
12 | },
13 | page: {
14 | name: '页面',
15 | crudTable: '业务表格',
16 | categoryTable: '分类联动表格',
17 | treeTable: '树联动表格'
18 | }
19 | },
20 | }
--------------------------------------------------------------------------------
/ts-i18n/src/locale/modules/zh-cn/system.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | system: {
3 | title: '后台管理系统',
4 | login: '登录',
5 | userName: '用户名',
6 | password: '密码',
7 | contentScreen: '内容全屏',
8 | fullScreen: '全屏',
9 | fullScreenBack: '退出全屏',
10 | github: '访问github地址',
11 | changePassword: '修改密码',
12 | loginOut: '退出登录',
13 | user: '管理员',
14 | size: {
15 | default: '默认',
16 | medium: '中',
17 | small: '小',
18 | mini: '迷你'
19 | },
20 | setting: {
21 | name: '系统设置',
22 |
23 | style: {
24 | name: '整体风格设置',
25 | default: '默认菜单风格',
26 | light: '亮色菜单风格',
27 | dark: '暗色菜单风格'
28 | },
29 | primaryColor: {
30 | name: '主题色',
31 | blue: '默认蓝',
32 | red: '玫瑰红',
33 | violet: '优雅紫',
34 | green: '故事绿',
35 | cyan: '明青',
36 | black: '极客黑'
37 | },
38 | other: {
39 | name: '其他设置',
40 | showLogo: '显示logo',
41 | showBreadcrumb: '显示面包屑导航',
42 | keepOnlyOneMenu: '保持一个菜单展开',
43 | }
44 | },
45 | tab: {
46 | reload: '重新加载',
47 | closeAll: '关闭所有标签',
48 | closeOther: '关闭其他标签',
49 | closeCurrent: '关闭当前标签'
50 | }
51 | },
52 | }
--------------------------------------------------------------------------------
/ts-i18n/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import ElementPlus from 'element-plus'
3 | import { baidu } from './utils/system/statistics'
4 | import 'element-plus/lib/theme-chalk/index.css'
5 | import 'element-plus/lib/theme-chalk/display.css' // 引入基于断点的隐藏类
6 | import 'normalize.css' // css初始化
7 | import './assets/style/common.scss' // 公共css
8 | import App from './App.vue'
9 | import store from './store'
10 | import router from './router'
11 | import i18n from './locale'
12 | // if (import.meta.env.MODE !== 'development') { // 非开发环境调用百度统计
13 | // baidu()
14 | // }
15 | const app = createApp(App)
16 | app.use(ElementPlus, { size: store.state.app.elementSize })
17 | app.use(store)
18 | app.use(router)
19 | app.use(i18n)
20 | // app.config.performance = true
21 | app.mount('#app')
22 |
--------------------------------------------------------------------------------
/ts-i18n/src/router/createNode.ts:
--------------------------------------------------------------------------------
1 | // 1. 用于解决keep-alive需要name的问题,动态生成随机name供keep-alive使用
2 | // 2. 用于解决transition动画内部结点只能为根元素的问题,单文件可写多结点
3 | import { defineComponent, h, createVNode, ref, nextTick } from 'vue'
4 | import reload from './reload.vue'
5 | import NProgress from '@/utils/system/nprogress'
6 |
7 | export function createNameComponent(component: any) {
8 | return () => {
9 | return new Promise((res) => {
10 | component().then((comm: any) => {
11 | const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now();
12 | const tempComm = defineComponent({
13 | name,
14 | setup() {
15 | const isReload = ref(false);
16 | let timeOut: any = null;
17 | const handleReload = () => {
18 | isReload.value = true;
19 | timeOut && clearTimeout(timeOut);
20 | NProgress.start();
21 | timeOut = setTimeout(() => {
22 | nextTick(() => {
23 | NProgress.done();
24 | isReload.value = false;
25 | });
26 | }, 260);
27 | };
28 | return {
29 | isReload,
30 | handleReload
31 | };
32 | },
33 | render: function () {
34 | if (this.isReload) {
35 | return h('div', { class: 'el-main-box' }, [h(reload)]);
36 | } else {
37 | return h('div', { class: 'el-main-box' }, [createVNode(comm.default)]);
38 | }
39 | }
40 | });
41 | res(tempComm);
42 | });
43 | });
44 | };
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/ts-i18n/src/router/modules/dashboard.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/',
6 | component: Layout,
7 | redirect: '/dashboard',
8 | meta: { title: 'message.menu.dashboard.name', icon: 'el-icon-menu' },
9 | children: [
10 | {
11 | path: 'dashboard',
12 | component: createNameComponent(() => import('@/views/main/dashboard/index.vue')),
13 | meta: { title: 'message.menu.dashboard.index', icon: 'el-icon-menu', hideClose: true }
14 | }
15 | ]
16 | }
17 | ]
18 |
19 | export default route
--------------------------------------------------------------------------------
/ts-i18n/src/router/modules/pages.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/pages',
6 | component: Layout,
7 | redirect: '/pages/crudTable',
8 | meta: { title: 'message.menu.page.name', icon: 'el-icon-document-copy' },
9 | alwayShow: true,
10 | children: [
11 | {
12 | path: 'crudTable',
13 | component: createNameComponent(() => import('@/views/main/pages/crudTable/index.vue')),
14 | meta: { title: 'message.menu.page.crudTable', cache: false, roles: ['admin', 'editor'] }
15 | },
16 | {
17 | path: 'categoryTable',
18 | component: createNameComponent(() => import('@/views/main/pages/categoryTable/index.vue')),
19 | meta: { title: 'message.menu.page.categoryTable', cache: true, roles: ['admin'] }
20 | },
21 | {
22 | path: 'treeTable',
23 | component: createNameComponent(() => import('@/views/main/pages/treeTable/index.vue')),
24 | meta: { title: 'message.menu.page.treeTable', cache: true }
25 | }
26 | ]
27 | }
28 | ]
29 |
30 | export default route
--------------------------------------------------------------------------------
/ts-i18n/src/router/modules/system.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/system',
6 | component: Layout,
7 | redirect: '/404',
8 | hideMenu: true,
9 | meta: { title: 'message.menu.system.name' },
10 | children: [
11 | {
12 | path: '/404',
13 | component: createNameComponent(() => import('@/views/system/404.vue')),
14 | meta: { title: 'message.menu.system.404', hideTabs: true }
15 | },
16 | {
17 | path: '/401',
18 | component: createNameComponent(() => import('@/views/system/401.vue')),
19 | meta: { title: 'message.menu.system.401', hideTabs: true }
20 | },
21 | {
22 | path: '/redirect/:path(.*)',
23 | component: createNameComponent(() => import('@/views/system/redirect.vue')),
24 | meta: { title: 'message.menu.system.redirect', hideTabs: true }
25 | }
26 | ]
27 | },
28 | {
29 | path: '/login',
30 | component: createNameComponent(() => import('@/views/system/login.vue')),
31 | hideMenu: true,
32 | meta: { title: 'message.system.login', hideTabs: true }
33 | },
34 | {
35 | // 找不到路由重定向到404页面
36 | path: "/:pathMatch(.*)",
37 | component: Layout,
38 | redirect: "/404",
39 | hideMenu: true
40 | },
41 | ]
42 |
43 | export default route
--------------------------------------------------------------------------------
/ts-i18n/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/ts-i18n/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { createStore, createLogger } from 'vuex'
2 | import Presistent from './plugins/persistent'
3 | const debug = process.env.NODE_ENV !== 'production'
4 |
5 | const files= import.meta.globEager('./modules/*.ts')
6 |
7 | let modules: any = {}
8 | Object.keys(files).forEach((c: string) => {
9 | const module = files[c].default
10 | const moduleName: string = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | modules[moduleName] = module
12 | })
13 |
14 | const presistent = Presistent({ key: 'vuex', modules, modulesKeys: {
15 | local: Object.keys(modules),
16 | session: []
17 | } })
18 |
19 | export default createStore({
20 | modules: {
21 | ...modules
22 | },
23 | strict: debug,
24 | plugins: debug ? [createLogger(), presistent] : [presistent]
25 | })
--------------------------------------------------------------------------------
/ts-i18n/src/store/modules/app.ts:
--------------------------------------------------------------------------------
1 | interface Option {
2 | name: string,
3 | value: any
4 | }
5 | interface State {
6 | isCollapse: boolean,
7 | contentFullScreen: boolean,
8 | showLogo: boolean,
9 | fixedTop: boolean,
10 | showTabs: boolean,
11 | expandOneMenu: boolean,
12 | elementSize: string,
13 | lang: string,
14 | theme: {
15 | primaryColor: string
16 | }
17 | }
18 | const state = () => ({
19 | isCollapse: false, // 侧边栏是否收缩展示
20 | contentFullScreen: false, // 内容是否可全屏展示
21 | showLogo: true, // 是否显示Logo
22 | fixedTop: false, // 是否固定顶部, todo,暂未使用
23 | showTabs: true, // 是否显示导航历史
24 | expandOneMenu: true, // 一次是否只能展开一个菜单
25 | elementSize: 'mini', // element默认尺寸,支持官网四个大小参数
26 | lang: 'zh', // 默认采用的国际化方案
27 | theme: {
28 | state: {
29 | style: 'default',
30 | primaryColor: '#409eff',
31 | menuType: 'side'
32 | }
33 | }
34 | })
35 |
36 | // mutations
37 | const mutations = {
38 | isCollapseChange(state: any, type: boolean) {
39 | state.isCollapse = type
40 | },
41 | contentFullScreenChange(state: any, type: boolean) {
42 | state.contentFullScreen = type
43 | },
44 | menuListChange(state: any, arr: []) {
45 | state.menuList = arr
46 | },
47 | stateChange(state: any, option: Option) {
48 | state[option.name] = option.value
49 | }
50 | }
51 |
52 | // actions
53 | const actions = {
54 |
55 | }
56 |
57 | export default {
58 | namespaced: true,
59 | state,
60 | actions,
61 | mutations
62 | }
--------------------------------------------------------------------------------
/ts-i18n/src/store/modules/keepAlive.ts:
--------------------------------------------------------------------------------
1 | interface Option {
2 | name: string,
3 | value: any
4 | }
5 |
6 | interface State {
7 | keepAliveComponentsName: []
8 | }
9 |
10 | const state = () => ({
11 | keepAliveComponentsName: [] // 需要缓存的组件名称
12 | })
13 |
14 | // mutations
15 | const mutations = {
16 | // 重置,Push, splice keep-alive对象
17 | setKeepAliveComponentsName(state: any, nameArr: []) {
18 | state.keepAliveComponentsName = nameArr
19 | },
20 | addKeepAliveComponentsName(state: any, name: string) {
21 | state.keepAliveComponentsName.push(name)
22 | },
23 | delKeepAliveComponentsName(state: any, name: string) {
24 | const key = state.keepAliveComponentsName.indexOf(name)
25 | if (key !== -1) {
26 | state.keepAliveComponentsName.splice(key, 1)
27 | console.log(state.keepAliveComponentsName)
28 | }
29 | }
30 | }
31 |
32 | const getters = {
33 | keepAliveComponentsName(state: State) {
34 | return state.keepAliveComponentsName
35 | }
36 | }
37 |
38 | // actions
39 | const actions = {
40 |
41 | }
42 |
43 | export default {
44 | namespaced: true,
45 | state,
46 | getters,
47 | actions,
48 | mutations
49 | }
--------------------------------------------------------------------------------
/ts-i18n/src/store/modules/user.ts:
--------------------------------------------------------------------------------
1 | import { loginApi, getInfoApi, loginOutApi } from '@/api/user'
2 | import { ActionContext } from 'vuex'
3 |
4 | interface State {
5 | token: string,
6 | info: object
7 | }
8 | const state = (): State => ({
9 | token: '', // 登录token
10 | info: {}, // 用户信息
11 | })
12 |
13 | // getters
14 | const getters = {
15 | token(state: State) {
16 | return state.token
17 | }
18 | }
19 |
20 | // mutations
21 | const mutations = {
22 | tokenChange(state: State, token: string) {
23 | state.token = token
24 | },
25 | infoChange(state: State, info: object) {
26 | state.info = info
27 | }
28 | }
29 |
30 | // actions
31 | const actions = {
32 | // login by login.vue
33 | login({ commit, dispatch }: ActionContext, params: any) {
34 | return new Promise((resolve, reject) => {
35 | loginApi(params)
36 | .then(res => {
37 | commit('tokenChange', res.data.token)
38 | dispatch('getInfo', { token: res.data.token })
39 | .then(infoRes => {
40 | resolve(res.data.token)
41 | })
42 | })
43 | })
44 | },
45 | // get user info after user logined
46 | getInfo({ commit }: ActionContext, params: any) {
47 | return new Promise((resolve, reject) => {
48 | getInfoApi(params)
49 | .then(res => {
50 | commit('infoChange', res.data.info)
51 | resolve(res.data.info)
52 | })
53 | })
54 | },
55 |
56 | // login out the system after user click the loginOut button
57 | loginOut({ commit }: ActionContext) {
58 | loginOutApi()
59 | .then(res => {
60 |
61 | })
62 | .catch(error => {
63 |
64 | })
65 | .finally(() => {
66 | localStorage.removeItem('tabs')
67 | localStorage.removeItem('vuex')
68 | location.reload()
69 | })
70 | }
71 | }
72 |
73 | export default {
74 | namespaced: true,
75 | state,
76 | actions,
77 | getters,
78 | mutations
79 | }
--------------------------------------------------------------------------------
/ts-i18n/src/store/plugins/persistent.ts:
--------------------------------------------------------------------------------
1 | interface Socket {
2 | key: string,
3 | modules: Modules,
4 | modulesKeys: ModulesKeys
5 | }
6 |
7 | interface Modules {
8 | [propName: string]: any
9 | }
10 |
11 | interface ModulesKeys {
12 | local: string[],
13 | session: string[]
14 | }
15 |
16 | interface Mutation {
17 | type: any,
18 | payload: any
19 | }
20 | const exclude = ['actions', 'getters', 'mutations', 'namespaced']
21 | export default function Presistent({ key, modules, modulesKeys }: Socket) {
22 | return (store: any) => {
23 | const localOldState = JSON.parse(localStorage.getItem(key) || '{}')
24 | const sessionOldState = JSON.parse(sessionStorage.getItem(key) || '{}')
25 | let oldState: Modules = {}
26 | Object.assign(oldState, localOldState, sessionOldState)
27 | if (Object.keys(oldState).length > 0) {
28 | for (const oldKey in oldState) {
29 | modules[oldKey] = oldState[oldKey]
30 | }
31 | store.replaceState(modules)
32 | }
33 | store.subscribe((mutation: Mutation, state: any) => {
34 | // 判断是否需要缓存数据至localStorage
35 | if (modulesKeys.local.length > 0) {
36 | const localData = setData(store.state, modulesKeys.local)
37 | localStorage.setItem(key, JSON.stringify(localData))
38 | } else {
39 | localStorage.removeItem(key)
40 | }
41 | // 判断是否需要缓存数据至sessionStorage
42 | if (modulesKeys.session.length > 0) {
43 | const sessionData = setData(store.state, modulesKeys.session)
44 | sessionStorage.setItem(key, JSON.stringify(sessionData))
45 | } else {
46 | sessionStorage.removeItem(key)
47 | }
48 | })
49 | }
50 | }
51 |
52 | function setData(state: any, module: string[]) {
53 | let data: Modules = {}
54 | for (const i of module) {
55 | data[i] = state[i]
56 | }
57 | return data
58 | }
--------------------------------------------------------------------------------
/ts-i18n/src/theme/index.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | // 主题色
3 | --system-primary-color: #409eff; // 可做背景色和文本色,用做背景色时,需要和--system-primary-text-color配合使用,避免文件颜色和主题色冲突
4 | --system-primary-text-color: #fff; // 主题色作为背景色时使用
5 |
6 | // logo颜色相关
7 | --system-logo-color: #f1f1f1;
8 | --system-logo-background: #263445;
9 |
10 | // 菜单颜色相关
11 | --system-menu-text-color: #bfcbd9;
12 | --system-menu-background: #28415a;
13 | --system-menu-children-background: #1f2d3d;
14 | --system-menu-submenu-active-color: #fff;
15 | --system-menu-hover-background: #203448;
16 |
17 | // header区域
18 | --system-header-background: #fff;
19 | --system-header-text-color: #bbb;
20 | --system-header-breadcrumb-text-color: #97a8be;
21 | --system-header-item-hover-color: #000;
22 | --system-header-border-color: #d8dce5;
23 | --system-header-tab-background: #fff;
24 |
25 | // contaier区域,父框架
26 | --system-container-background: #f0f2f5;
27 | --system-container-main-background: #fff;
28 |
29 | // 页面区域, 这一块是你在自己写的文件中使用主题,核心需要关注的地方
30 | --system-page-background: #fff; // 主背景
31 | --system-page-color: #303133; // 主要的文本颜色
32 | --system-page-tip-color: rgba(0, 0, 0, 0.45); // 协助展示的文本颜色
33 | --system-page-border-color: #000; // 通用的边框配置色,便于主题扩展
34 |
35 | // element主题色修改
36 | --el-color-primary: var(--system-primary-color);
37 | }
38 |
39 | // 进度条颜色修改为主题色
40 | body #nprogress .bar {
41 | background-color: var(--system-primary-color);
42 | }
43 | body #nprogress .peg {
44 | box-shadow: 0 0 10px var(--system-primary-color), 0 0 5px var(--system-primary-color);
45 | }
46 | body #nprogress .spinner-icon {
47 | border-top-color: var(--system-primary-color);
48 | border-left-color: var(--system-primary-color);
49 | }
50 |
51 | @import './modules/dark.scss';
52 |
--------------------------------------------------------------------------------
/ts-i18n/src/theme/modules/dark.scss:
--------------------------------------------------------------------------------
1 | .dark {
2 | // 通用
3 | p, h1, h2, h3, h4, h5, h6, article {
4 | color: var(--system-page-color);
5 | }
6 | .el-tree {
7 | background-color: var(--system-page-background);
8 | .el-tree-node__content:hover {
9 | background-color: #272727;
10 | }
11 | --el-color-primary-light-9: #272727;
12 | }
13 | .el-card {
14 | background-color: var(--system-page-background);
15 | color: var(--system-page-color);
16 | border-color: var(--system-page-border-color);
17 | .el-card__header {
18 | border-color: var(--system-page-border-color);
19 | }
20 | }
21 | // 页面内部样式修改
22 |
23 | }
--------------------------------------------------------------------------------
/ts-i18n/src/utils/system/nprogress.ts:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress"
2 | import "nprogress/nprogress.css"
3 |
4 | NProgress.configure({
5 | easing: 'ease', // 动画方式
6 | speed: 500, // 递增进度条的速度
7 | showSpinner: true, // 是否显示加载ico
8 | trickleSpeed: 200, // 自动递增间隔
9 | minimum: 0.3 // 初始化时的最小百分比
10 | })
11 |
12 | export default NProgress
--------------------------------------------------------------------------------
/ts-i18n/src/utils/system/request.ts:
--------------------------------------------------------------------------------
1 | import axios , { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'
2 | import store from '@/store'
3 | import { ElMessage } from 'element-plus'
4 | const baseURL: any = import.meta.env.VITE_BASE_URL
5 |
6 | const service: AxiosInstance = axios.create({
7 | baseURL: baseURL,
8 | timeout: 5000
9 | })
10 |
11 | // 请求前的统一处理
12 | service.interceptors.request.use(
13 | (config: AxiosRequestConfig) => {
14 | // JWT鉴权处理
15 | if (store.getters['user/token']) {
16 | config.headers['token'] = store.state.user.token
17 | }
18 | return config
19 | },
20 | (error: AxiosError) => {
21 | console.log(error) // for debug
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | service.interceptors.response.use(
27 | (response: AxiosResponse) => {
28 | const res = response.data
29 | if (res.code === 200) {
30 | return res
31 | } else {
32 | showError(res)
33 | return Promise.reject(res)
34 | }
35 | },
36 | (error: AxiosError)=> {
37 | console.log(error) // for debug
38 | const badMessage: any = error.message || error
39 | const code = parseInt(badMessage.toString().replace('Error: Request failed with status code ', ''))
40 | showError({ code, message: badMessage })
41 | return Promise.reject(error)
42 | }
43 | )
44 |
45 | function showError(error: any) {
46 | if (error.code === 403) {
47 | // to re-login
48 | store.dispatch('user/loginOut')
49 | } else {
50 | ElMessage({
51 | message: error.msg || error.message || '服务异常',
52 | type: 'error',
53 | duration: 3 * 1000
54 | })
55 | }
56 |
57 | }
58 |
59 | export default service
--------------------------------------------------------------------------------
/ts-i18n/src/utils/system/statistics.ts:
--------------------------------------------------------------------------------
1 | // 百度统计代码,需自行更换
2 | export function baidu() {
3 | const script = document.createElement('script')
4 | script.type = 'text/javascript'
5 | script.text = `
6 | var _hmt = _hmt || [];
7 | (function() {
8 | var hm = document.createElement("script");
9 | hm.src = "https://hm.baidu.com/hm.js?bd78bc908e66174e7dde385bf37cb4c1";
10 | var s = document.getElementsByTagName("script")[0];
11 | s.parentNode.insertBefore(hm, s);
12 | })();
13 | `
14 | document.getElementsByTagName('head')[0].appendChild(script)
15 | }
--------------------------------------------------------------------------------
/ts-i18n/src/utils/system/title.ts:
--------------------------------------------------------------------------------
1 | import i18n from '@/locale'
2 | import { systemTitle } from '@/config'
3 | const { t } = i18n.global
4 |
5 | export function changeTitle(name: any) {
6 | document.title = `${t(name)}-${t(systemTitle)}`
7 | }
8 |
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我是首页
4 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/pages/categoryTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/pages/categoryTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
28 |
29 |
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/pages/crudTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/pages/treeTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts-i18n/src/views/main/pages/treeTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
--------------------------------------------------------------------------------
/ts-i18n/src/views/system/redirect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ts-i18n/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "esModuleInterop": true,
11 | "lib": ["esnext", "dom"],
12 | "types": ["vite/client"],
13 | "baseUrl": "./",
14 | "paths": {
15 | "@/*": ["src/*"]
16 | }
17 | },
18 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
19 | }
20 |
--------------------------------------------------------------------------------
/ts-i18n/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { ConfigEnv, UserConfigExport } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { viteMockServe } from 'vite-plugin-mock'
4 | import { resolve } from 'path'
5 |
6 | const pathResolve = (dir: string): any => {
7 | return resolve(__dirname, ".", dir)
8 | }
9 |
10 | const alias: Record = {
11 | '@': pathResolve("src")
12 | }
13 |
14 | // https://vitejs.dev/config/
15 | export default ({ command }: ConfigEnv): UserConfigExport => {
16 | const prodMock = true;
17 | return {
18 | base: './',
19 | resolve: {
20 | alias
21 | },
22 | server: {
23 | port: 3005,
24 | host: '0.0.0.0',
25 | open: true,
26 | proxy: { // 代理配置
27 | '/dev': 'https://www.fastmock.site/mock/48cab8545e64d93ff9ba66a87ad04f6b/'
28 | },
29 | },
30 | build: {
31 | rollupOptions: {
32 | output: {
33 | manualChunks: {
34 |
35 | }
36 | }
37 | }
38 | },
39 | plugins: [
40 | vue(),
41 | viteMockServe({
42 | mockPath: 'mock',
43 | localEnabled: command === 'serve',
44 | prodEnabled: command !== 'serve' && prodMock,
45 | watchFiles: true,
46 | injectCode: `
47 | import { setupProdMockServer } from '../mockProdServer';
48 | setupProdMockServer();
49 | `,
50 | logger: true,
51 | }),
52 | ]
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/ts/.env.development:
--------------------------------------------------------------------------------
1 | ENV = 'development'
2 |
3 | VITE_BASE_URL = '/dev-api'
--------------------------------------------------------------------------------
/ts/.env.production:
--------------------------------------------------------------------------------
1 | ENV = 'production'
2 |
3 | VITE_BASE_URL = '/pro-api'
--------------------------------------------------------------------------------
/ts/.env.staging:
--------------------------------------------------------------------------------
1 | ENV = 'staging'
2 |
3 | VITE_BASE_URL = '/test-api'
--------------------------------------------------------------------------------
/ts/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | yarn.lock
7 | yarn-error.log
8 | .vscode
9 |
--------------------------------------------------------------------------------
/ts/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 罗茜
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/ts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ts/mock/user.ts:
--------------------------------------------------------------------------------
1 | import { MockMethod } from 'vite-plugin-mock'
2 | const users = [
3 | { name: 'admin', password: '123456', token: 'admin', info: {
4 | name: '系统管理员'
5 | }},
6 | { name: 'editor', password: '123456', token: 'editor', info: {
7 | name: '编辑人员'
8 | }},
9 | { name: 'test', password: '123456', token: 'test', info: {
10 | name: '测试人员'
11 | }},
12 | ]
13 | export default [
14 | {
15 | url: `/mock/user/login`,
16 | method: 'post',
17 | response: ({ body }) => {
18 | const user = users.find(user => {
19 | return body.name === user.name && body.password === user.password
20 | })
21 | if (user) {
22 | return {
23 | code: 200,
24 | data: {
25 | token: user.token,
26 | },
27 | };
28 | } else {
29 | return {
30 | code: 401,
31 | data: {},
32 | msg: '用户名或密码错误'
33 | };
34 | }
35 |
36 | }
37 | },
38 | {
39 | url: `/mock/user/info`,
40 | method: 'post',
41 | response: ({ body }) => {
42 | const { token } = body
43 | const info = users.find(user => {
44 | return user.token === token
45 | }).info
46 | if (info) {
47 | return {
48 | code: 200,
49 | data: {
50 | info: info
51 | },
52 | };
53 | } else {
54 | return {
55 | code: 403,
56 | data: {},
57 | msg: '无访问权限'
58 | };
59 | }
60 |
61 | }
62 | },
63 | {
64 | url: `/mock/user/out`,
65 | method: 'post',
66 | response: () => {
67 | return {
68 | code: 200,
69 | data: {},
70 | msg: 'success'
71 | };
72 | }
73 | },
74 | {
75 | url: `/mock/user/passwordChange`,
76 | method: 'post',
77 | response: () => {
78 | return {
79 | code: 200,
80 | data: {},
81 | msg: 'success'
82 | };
83 | }
84 | },
85 | ] as MockMethod[]
--------------------------------------------------------------------------------
/ts/mockProdServer.ts:
--------------------------------------------------------------------------------
1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
2 | import userModule from './mock/user'
3 | import tableModule from './mock/table'
4 |
5 | export function setupProdMockServer() {
6 | createProdMockServer([
7 | ...userModule,
8 | ...tableModule
9 | ]);
10 | }
11 |
--------------------------------------------------------------------------------
/ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "init",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "start": "vite",
7 | "build": "vite build --mode=production",
8 | "build:stag": "vite build --mode=staging",
9 | "serve": "vite preview"
10 | },
11 | "dependencies": {
12 | "@vueuse/core": "^4.10.0",
13 | "axios": "^0.21.1",
14 | "element-plus": "^1.0.2-beta.48",
15 | "mockjs": "^1.1.0",
16 | "normalize.css": "^8.0.1",
17 | "nprogress": "^0.2.0",
18 | "throttle-debounce": "^3.0.1",
19 | "vue": "^3.1.2",
20 | "vue-router": "4",
21 | "vuex": "^4.0.0"
22 | },
23 | "devDependencies": {
24 | "@types/node": "^15.0.3",
25 | "@vitejs/plugin-vue": "^1.2.2",
26 | "@vue/compiler-sfc": "^3.0.5",
27 | "sass": "^1.32.12",
28 | "typescript": "^4.1.3",
29 | "vite": "2.3.7",
30 | "vite-plugin-mock": "2.8.0",
31 | "vue-tsc": "^0.0.24"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ts/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts/public/favicon.ico
--------------------------------------------------------------------------------
/ts/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
31 |
--------------------------------------------------------------------------------
/ts/src/api/table.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 获取数据api
4 | export function getData(data: object) {
5 | return request({
6 | url: '/table/list',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取分类数据
14 | export function getCategory(data: object) {
15 | return request({
16 | url: '/table/category',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 获取树组织数据
24 | export function getTree(data: object) {
25 | return request({
26 | url: '/table/tree',
27 | method: 'post',
28 | baseURL: '/mock',
29 | data
30 | })
31 | }
32 |
33 | // 新增
34 | export function add(data: object) {
35 | return request({
36 | url: '/table/add',
37 | method: 'post',
38 | baseURL: '/mock',
39 | data
40 | })
41 | }
42 |
43 | // 编辑
44 | export function update(data: object) {
45 | return request({
46 | url: '/table/update',
47 | method: 'post',
48 | baseURL: '/mock',
49 | data
50 | })
51 | }
52 |
53 | // 删除
54 | export function del(data: object) {
55 | return request({
56 | url: '/table/del',
57 | method: 'post',
58 | baseURL: '/mock',
59 | data
60 | })
61 | }
--------------------------------------------------------------------------------
/ts/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/system/request'
2 |
3 | // 登录api
4 | export function loginApi(data: object) {
5 | return request({
6 | url: '/user/login',
7 | method: 'post',
8 | baseURL: '/mock',
9 | data
10 | })
11 | }
12 |
13 | // 获取用户信息Api
14 | export function getInfoApi(data: object) {
15 | return request({
16 | url: '/user/info',
17 | method: 'post',
18 | baseURL: '/mock',
19 | data
20 | })
21 | }
22 |
23 | // 退出登录Api
24 | export function loginOutApi() {
25 | return request({
26 | url: '/user/out',
27 | method: 'post',
28 | baseURL: '/mock'
29 | })
30 | }
31 |
32 | // 获取用户信息Api
33 | export function passwordChange(data: object) {
34 | return request({
35 | url: '/user/passwordChange',
36 | method: 'post',
37 | baseURL: '/mock',
38 | data
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/ts/src/assets/images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts/src/assets/images/401.gif
--------------------------------------------------------------------------------
/ts/src/assets/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts/src/assets/images/404.png
--------------------------------------------------------------------------------
/ts/src/assets/images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts/src/assets/images/404_cloud.png
--------------------------------------------------------------------------------
/ts/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmdparkour/vue-admin-box-template/705a4e3ad103552638d3f4b0fdb95ab96d1c1b88/ts/src/assets/logo.png
--------------------------------------------------------------------------------
/ts/src/assets/style/common.scss:
--------------------------------------------------------------------------------
1 | @import "./transition.scss";
2 | @import "@/theme/index.scss";
3 | .layout-container {
4 | background-color: var(--system-container-main-background);
5 | width: calc(100% - 30px);
6 | height: calc(100% - 30px);
7 | margin: 15px;
8 | display: flex;
9 | flex-direction: column;
10 | overflow-y: auto;
11 | &-form {
12 | display: flex;
13 | justify-content: space-between;
14 | padding: 15px 15px 0;
15 | &-handle {
16 | display: flex;
17 | justify-content: flex-start;
18 | }
19 | &-search {
20 | display: flex;
21 | justify-content: flex-end;
22 | .search-btn {
23 | margin-left: 15px;
24 | }
25 | }
26 | .el-form-item {
27 | margin-bottom: 0;
28 | }
29 | }
30 | &-table {
31 | flex: 1;
32 | height: 100%;
33 | padding: 15px;
34 | overflow: auto;
35 | }
36 | }
37 | .flex-box {
38 | display: flex;
39 | flex-direction: column;
40 | width: 100%;
41 | height: 100%;
42 | padding: 15px;
43 | box-sizing: border-box;
44 | }
45 | .flex {
46 | display: flex;
47 | }
48 | .center {
49 | justify-content: center;
50 | align-items: center;
51 | text-align: center;
52 | }
53 | a {
54 | text-decoration: none;
55 | }
56 |
57 | /** element-plus **/
58 | .el-icon{
59 | text-align: center;
60 | }
61 |
62 | /** 用于提示信息 **/
63 | .my-tip {
64 | background-color: #f1f1f1;
65 | padding: 5px 10px;
66 | text-align: left;
67 | border-radius: 4px;
68 | }
69 | .system-scrollbar {
70 | &::-webkit-scrollbar {
71 | display: none;
72 | width: 6px;
73 | }
74 | &::-webkit-scrollbar-thumb {
75 | border-radius: 10px;
76 | background: rgba(144, 147, 153, 0.3);
77 | }
78 | &:hover {
79 | &::-webkit-scrollbar {
80 | display: block;
81 | }
82 | &::-webkit-scrollbar-thumb {
83 | border-radius: 10px;
84 | background: rgba(144, 147, 153, 0.3);
85 | &:hover {
86 | background: rgba(144, 147, 153, 0.5);
87 | }
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/ts/src/assets/style/transition.scss:
--------------------------------------------------------------------------------
1 | /* fade-transform */
2 | .fade-transform-leave-active,
3 | .fade-transform-enter-active {
4 | transition: all .2s;
5 | }
6 |
7 | .fade-transform-enter-from {
8 | opacity: 0;
9 | transform: translateX(-30px);
10 | transition: all .2s;
11 | }
12 |
13 | .fade-transform-leave-to {
14 | opacity: 0;
15 | transform: translateX(30px);
16 | transition: all .2s;
17 | }
18 |
19 | /* breadcrumb transition */
20 | .breadcrumb-enter-active,
21 | .breadcrumb-leave-active {
22 | transition: all .2s;
23 | }
24 |
25 | .breadcrumb-enter,
26 | .breadcrumb-leave-active {
27 | opacity: 0;
28 | transform: translateX(80px);
29 | }
30 |
31 | .breadcrumb-move {
32 | transition: all .5s;
33 | }
34 |
35 | .breadcrumb-leave-active {
36 | position: absolute;
37 | }
--------------------------------------------------------------------------------
/ts/src/components/layer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 | 确认
14 | 取消
15 |
16 |
17 |
18 |
19 |
20 |
21 |
72 |
73 |
--------------------------------------------------------------------------------
/ts/src/components/menu/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ts/src/components/table/type.ts:
--------------------------------------------------------------------------------
1 | export interface Page{
2 | index: Number,
3 | size: Number,
4 | total: Number
5 | }
--------------------------------------------------------------------------------
/ts/src/config/index.ts:
--------------------------------------------------------------------------------
1 | const showLogo: Boolean = true; // 是否显示Logo顶部模块
2 | const systemTitle = '后台管理系统' // 系统名称,用于显示在左上角模块,以及浏览器标题上使用,使用配置项
3 | export {
4 | systemTitle
5 | }
--------------------------------------------------------------------------------
/ts/src/layout/Header/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ item.meta.title }}
9 |
10 | {{ item.meta.title }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
50 |
51 |
--------------------------------------------------------------------------------
/ts/src/layout/Header/functionList/fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/ts/src/layout/Header/functionList/sizeChange.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 | {{ d.name }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
60 |
61 |
--------------------------------------------------------------------------------
/ts/src/layout/Header/functionList/theme/theme-color.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
48 |
49 |
--------------------------------------------------------------------------------
/ts/src/layout/Logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ systemTitle }}
5 |
6 |
7 |
8 |
23 |
24 |
--------------------------------------------------------------------------------
/ts/src/layout/Menu/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
39 |
--------------------------------------------------------------------------------
/ts/src/layout/Tabs/tabsHook.ts:
--------------------------------------------------------------------------------
1 | const tabsHook = {
2 | setItem: function(arr: object[]) {
3 | localStorage.setItem('tabs', JSON.stringify(arr))
4 | },
5 | getItem: function() {
6 | return JSON.parse(localStorage.getItem('tabs') || '[]')
7 | }
8 | }
9 | export default tabsHook
10 |
--------------------------------------------------------------------------------
/ts/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import ElementPlus from 'element-plus'
3 | import { baidu } from './utils/system/statistics'
4 | import 'element-plus/lib/theme-chalk/index.css'
5 | import 'element-plus/lib/theme-chalk/display.css' // 引入基于断点的隐藏类
6 | import 'normalize.css' // css初始化
7 | import './assets/style/common.scss' // 公共css
8 | import App from './App.vue'
9 | import store from './store'
10 | import router from './router'
11 | // if (import.meta.env.MODE !== 'development') { // 非开发环境调用百度统计
12 | // baidu()
13 | // }
14 | const app = createApp(App)
15 | app.use(ElementPlus, { size: store.state.app.elementSize })
16 | app.use(store)
17 | app.use(router)
18 | // app.config.performance = true
19 | app.mount('#app')
20 |
--------------------------------------------------------------------------------
/ts/src/router/createNode.ts:
--------------------------------------------------------------------------------
1 | // 1. 用于解决keep-alive需要name的问题,动态生成随机name供keep-alive使用
2 | // 2. 用于解决transition动画内部结点只能为根元素的问题,单文件可写多结点
3 | import { defineComponent, h, createVNode, ref, nextTick } from 'vue'
4 | import reload from './reload.vue'
5 | import NProgress from '@/utils/system/nprogress'
6 |
7 | export function createNameComponent(component: any) {
8 | return () => {
9 | return new Promise((res) => {
10 | component().then((comm: any) => {
11 | const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now();
12 | const tempComm = defineComponent({
13 | name,
14 | setup() {
15 | const isReload = ref(false);
16 | let timeOut: any = null;
17 | const handleReload = () => {
18 | isReload.value = true;
19 | timeOut && clearTimeout(timeOut);
20 | NProgress.start();
21 | timeOut = setTimeout(() => {
22 | nextTick(() => {
23 | NProgress.done();
24 | isReload.value = false;
25 | });
26 | }, 260);
27 | };
28 | return {
29 | isReload,
30 | handleReload
31 | };
32 | },
33 | render: function () {
34 | if (this.isReload) {
35 | return h('div', { class: 'el-main-box' }, [h(reload)]);
36 | } else {
37 | return h('div', { class: 'el-main-box' }, [createVNode(comm.default)]);
38 | }
39 | }
40 | });
41 | res(tempComm);
42 | });
43 | });
44 | };
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/ts/src/router/modules/dashboard.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/',
6 | component: Layout,
7 | redirect: '/dashboard',
8 | meta: { title: 'dashboard', icon: 'el-icon-menu' },
9 | children: [
10 | {
11 | path: 'dashboard',
12 | component: createNameComponent(() => import('@/views/main/dashboard/index.vue')),
13 | meta: { title: '首页', icon: 'el-icon-menu', hideClose: true }
14 | }
15 | ]
16 | }
17 | ]
18 |
19 | export default route
--------------------------------------------------------------------------------
/ts/src/router/modules/pages.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/pages',
6 | component: Layout,
7 | redirect: '/pages/crudTable',
8 | meta: { title: '页面', icon: 'el-icon-document-copy' },
9 | alwayShow: true,
10 | children: [
11 | {
12 | path: 'crudTable',
13 | component: createNameComponent(() => import('@/views/main/pages/crudTable/index.vue')),
14 | meta: { title: '业务表格', cache: false, roles: ['admin', 'editor'] }
15 | },
16 | {
17 | path: 'categoryTable',
18 | component: createNameComponent(() => import('@/views/main/pages/categoryTable/index.vue')),
19 | meta: { title: '分类联动表格', cache: true, roles: ['admin'] }
20 | },
21 | {
22 | path: 'treeTable',
23 | component: createNameComponent(() => import('@/views/main/pages/treeTable/index.vue')),
24 | meta: { title: '树联动表格', cache: true }
25 | }
26 | ]
27 | }
28 | ]
29 |
30 | export default route
--------------------------------------------------------------------------------
/ts/src/router/modules/system.ts:
--------------------------------------------------------------------------------
1 | import Layout from '@/layout/index.vue'
2 | import { createNameComponent } from '../createNode'
3 | const route = [
4 | {
5 | path: '/system',
6 | component: Layout,
7 | redirect: '/404',
8 | hideMenu: true,
9 | meta: { title: '系统目录' },
10 | children: [
11 | {
12 | path: '/404',
13 | component: createNameComponent(() => import('@/views/system/404.vue')),
14 | meta: { title: '404', hideTabs: true }
15 | },
16 | {
17 | path: '/401',
18 | component: createNameComponent(() => import('@/views/system/401.vue')),
19 | meta: { title: '401', hideTabs: true }
20 | },
21 | {
22 | path: '/redirect/:path(.*)',
23 | component: createNameComponent(() => import('@/views/system/redirect.vue')),
24 | meta: { title: 'redirect', hideTabs: true }
25 | }
26 | ]
27 | },
28 | {
29 | path: '/login',
30 | component: createNameComponent(() => import('@/views/system/login.vue')),
31 | hideMenu: true,
32 | meta: { title: '登录', hideTabs: true }
33 | },
34 | {
35 | // 找不到路由重定向到404页面
36 | path: "/:pathMatch(.*)",
37 | component: Layout,
38 | redirect: "/404",
39 | hideMenu: true
40 | },
41 | ]
42 |
43 | export default route
--------------------------------------------------------------------------------
/ts/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/ts/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { createStore, createLogger } from 'vuex'
2 | import Presistent from './plugins/persistent'
3 | const debug = process.env.NODE_ENV !== 'production'
4 |
5 | const files= import.meta.globEager('./modules/*.ts')
6 |
7 | let modules: any = {}
8 | Object.keys(files).forEach((c: string) => {
9 | const module = files[c].default
10 | const moduleName: string = c.replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
11 | modules[moduleName] = module
12 | })
13 |
14 | const presistent = Presistent({ key: 'vuex', modules, modulesKeys: {
15 | local: Object.keys(modules),
16 | session: []
17 | } })
18 |
19 | export default createStore({
20 | modules: {
21 | ...modules
22 | },
23 | strict: debug,
24 | plugins: debug ? [createLogger(), presistent] : [presistent]
25 | })
--------------------------------------------------------------------------------
/ts/src/store/modules/app.ts:
--------------------------------------------------------------------------------
1 | interface Option {
2 | name: string,
3 | value: any
4 | }
5 | interface State {
6 | isCollapse: boolean,
7 | contentFullScreen: boolean,
8 | showLogo: boolean,
9 | fixedTop: boolean,
10 | showTabs: boolean,
11 | expandOneMenu: boolean,
12 | elementSize: string,
13 | lang: string,
14 | theme: {
15 | primaryColor: string
16 | }
17 | }
18 | const state = () => ({
19 | isCollapse: false, // 侧边栏是否收缩展示
20 | contentFullScreen: false, // 内容是否可全屏展示
21 | showLogo: true, // 是否显示Logo
22 | fixedTop: false, // 是否固定顶部, todo,暂未使用
23 | showTabs: true, // 是否显示导航历史
24 | expandOneMenu: true, // 一次是否只能展开一个菜单
25 | elementSize: 'mini', // element默认尺寸,支持官网四个大小参数
26 | lang: 'zh', // 默认采用的国际化方案
27 | theme: {
28 | state: {
29 | style: 'default',
30 | primaryColor: '#409eff',
31 | menuType: 'side'
32 | }
33 | }
34 | })
35 |
36 | // mutations
37 | const mutations = {
38 | isCollapseChange(state: any, type: boolean) {
39 | state.isCollapse = type
40 | },
41 | contentFullScreenChange(state: any, type: boolean) {
42 | state.contentFullScreen = type
43 | },
44 | menuListChange(state: any, arr: []) {
45 | state.menuList = arr
46 | },
47 | stateChange(state: any, option: Option) {
48 | state[option.name] = option.value
49 | }
50 | }
51 |
52 | // actions
53 | const actions = {
54 |
55 | }
56 |
57 | export default {
58 | namespaced: true,
59 | state,
60 | actions,
61 | mutations
62 | }
--------------------------------------------------------------------------------
/ts/src/store/modules/keepAlive.ts:
--------------------------------------------------------------------------------
1 | interface Option {
2 | name: string,
3 | value: any
4 | }
5 |
6 | interface State {
7 | keepAliveComponentsName: []
8 | }
9 |
10 | const state = () => ({
11 | keepAliveComponentsName: [] // 需要缓存的组件名称
12 | })
13 |
14 | // mutations
15 | const mutations = {
16 | // 重置,Push, splice keep-alive对象
17 | setKeepAliveComponentsName(state: any, nameArr: []) {
18 | state.keepAliveComponentsName = nameArr
19 | },
20 | addKeepAliveComponentsName(state: any, name: string) {
21 | state.keepAliveComponentsName.push(name)
22 | },
23 | delKeepAliveComponentsName(state: any, name: string) {
24 | const key = state.keepAliveComponentsName.indexOf(name)
25 | if (key !== -1) {
26 | state.keepAliveComponentsName.splice(key, 1)
27 | console.log(state.keepAliveComponentsName)
28 | }
29 | }
30 | }
31 |
32 | const getters = {
33 | keepAliveComponentsName(state: State) {
34 | return state.keepAliveComponentsName
35 | }
36 | }
37 |
38 | // actions
39 | const actions = {
40 |
41 | }
42 |
43 | export default {
44 | namespaced: true,
45 | state,
46 | getters,
47 | actions,
48 | mutations
49 | }
--------------------------------------------------------------------------------
/ts/src/store/modules/user.ts:
--------------------------------------------------------------------------------
1 | import { loginApi, getInfoApi, loginOutApi } from '@/api/user'
2 | import { ActionContext } from 'vuex'
3 |
4 | interface State {
5 | token: string,
6 | info: object
7 | }
8 | const state = (): State => ({
9 | token: '', // 登录token
10 | info: {}, // 用户信息
11 | })
12 |
13 | // getters
14 | const getters = {
15 | token(state: State) {
16 | return state.token
17 | }
18 | }
19 |
20 | // mutations
21 | const mutations = {
22 | tokenChange(state: State, token: string) {
23 | state.token = token
24 | },
25 | infoChange(state: State, info: object) {
26 | state.info = info
27 | }
28 | }
29 |
30 | // actions
31 | const actions = {
32 | // login by login.vue
33 | login({ commit, dispatch }: ActionContext, params: any) {
34 | return new Promise((resolve, reject) => {
35 | loginApi(params)
36 | .then(res => {
37 | commit('tokenChange', res.data.token)
38 | dispatch('getInfo', { token: res.data.token })
39 | .then(infoRes => {
40 | resolve(res.data.token)
41 | })
42 | })
43 | })
44 | },
45 | // get user info after user logined
46 | getInfo({ commit }: ActionContext, params: any) {
47 | return new Promise((resolve, reject) => {
48 | getInfoApi(params)
49 | .then(res => {
50 | commit('infoChange', res.data.info)
51 | resolve(res.data.info)
52 | })
53 | })
54 | },
55 |
56 | // login out the system after user click the loginOut button
57 | loginOut({ commit }: ActionContext) {
58 | loginOutApi()
59 | .then(res => {
60 |
61 | })
62 | .catch(error => {
63 |
64 | })
65 | .finally(() => {
66 | localStorage.removeItem('tabs')
67 | localStorage.removeItem('vuex')
68 | location.reload()
69 | })
70 | }
71 | }
72 |
73 | export default {
74 | namespaced: true,
75 | state,
76 | actions,
77 | getters,
78 | mutations
79 | }
--------------------------------------------------------------------------------
/ts/src/store/plugins/persistent.ts:
--------------------------------------------------------------------------------
1 | interface Socket {
2 | key: string,
3 | modules: Modules,
4 | modulesKeys: ModulesKeys
5 | }
6 |
7 | interface Modules {
8 | [propName: string]: any
9 | }
10 |
11 | interface ModulesKeys {
12 | local: string[],
13 | session: string[]
14 | }
15 |
16 | interface Mutation {
17 | type: any,
18 | payload: any
19 | }
20 | const exclude = ['actions', 'getters', 'mutations', 'namespaced']
21 | export default function Presistent({ key, modules, modulesKeys }: Socket) {
22 | return (store: any) => {
23 | const localOldState = JSON.parse(localStorage.getItem(key) || '{}')
24 | const sessionOldState = JSON.parse(sessionStorage.getItem(key) || '{}')
25 | let oldState: Modules = {}
26 | Object.assign(oldState, localOldState, sessionOldState)
27 | if (Object.keys(oldState).length > 0) {
28 | for (const oldKey in oldState) {
29 | modules[oldKey] = oldState[oldKey]
30 | }
31 | store.replaceState(modules)
32 | }
33 | store.subscribe((mutation: Mutation, state: any) => {
34 | // 判断是否需要缓存数据至localStorage
35 | if (modulesKeys.local.length > 0) {
36 | const localData = setData(store.state, modulesKeys.local)
37 | localStorage.setItem(key, JSON.stringify(localData))
38 | } else {
39 | localStorage.removeItem(key)
40 | }
41 | // 判断是否需要缓存数据至sessionStorage
42 | if (modulesKeys.session.length > 0) {
43 | const sessionData = setData(store.state, modulesKeys.session)
44 | sessionStorage.setItem(key, JSON.stringify(sessionData))
45 | } else {
46 | sessionStorage.removeItem(key)
47 | }
48 | })
49 | }
50 | }
51 |
52 | function setData(state: any, module: string[]) {
53 | let data: Modules = {}
54 | for (const i of module) {
55 | data[i] = state[i]
56 | }
57 | return data
58 | }
--------------------------------------------------------------------------------
/ts/src/theme/index.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | // 主题色
3 | --system-primary-color: #409eff; // 可做背景色和文本色,用做背景色时,需要和--system-primary-text-color配合使用,避免文件颜色和主题色冲突
4 | --system-primary-text-color: #fff; // 主题色作为背景色时使用
5 |
6 | // logo颜色相关
7 | --system-logo-color: #f1f1f1;
8 | --system-logo-background: #263445;
9 |
10 | // 菜单颜色相关
11 | --system-menu-text-color: #bfcbd9;
12 | --system-menu-background: #28415a;
13 | --system-menu-children-background: #1f2d3d;
14 | --system-menu-submenu-active-color: #fff;
15 | --system-menu-hover-background: #203448;
16 |
17 | // header区域
18 | --system-header-background: #fff;
19 | --system-header-text-color: #bbb;
20 | --system-header-breadcrumb-text-color: #97a8be;
21 | --system-header-item-hover-color: #000;
22 | --system-header-border-color: #d8dce5;
23 | --system-header-tab-background: #fff;
24 |
25 | // contaier区域,父框架
26 | --system-container-background: #f0f2f5;
27 | --system-container-main-background: #fff;
28 |
29 | // 页面区域, 这一块是你在自己写的文件中使用主题,核心需要关注的地方
30 | --system-page-background: #fff; // 主背景
31 | --system-page-color: #303133; // 主要的文本颜色
32 | --system-page-tip-color: rgba(0, 0, 0, 0.45); // 协助展示的文本颜色
33 | --system-page-border-color: #000; // 通用的边框配置色,便于主题扩展
34 |
35 | // element主题色修改
36 | --el-color-primary: var(--system-primary-color);
37 | }
38 |
39 | // 进度条颜色修改为主题色
40 | body #nprogress .bar {
41 | background-color: var(--system-primary-color);
42 | }
43 | body #nprogress .peg {
44 | box-shadow: 0 0 10px var(--system-primary-color), 0 0 5px var(--system-primary-color);
45 | }
46 | body #nprogress .spinner-icon {
47 | border-top-color: var(--system-primary-color);
48 | border-left-color: var(--system-primary-color);
49 | }
50 |
51 | @import './modules/dark.scss';
52 |
--------------------------------------------------------------------------------
/ts/src/theme/modules/dark.scss:
--------------------------------------------------------------------------------
1 | .dark {
2 | // 通用
3 | p, h1, h2, h3, h4, h5, h6, article {
4 | color: var(--system-page-color);
5 | }
6 | .el-tree {
7 | background-color: var(--system-page-background);
8 | .el-tree-node__content:hover {
9 | background-color: #272727;
10 | }
11 | --el-color-primary-light-9: #272727;
12 | }
13 | .el-card {
14 | background-color: var(--system-page-background);
15 | color: var(--system-page-color);
16 | border-color: var(--system-page-border-color);
17 | .el-card__header {
18 | border-color: var(--system-page-border-color);
19 | }
20 | }
21 | // 页面内部样式修改
22 |
23 | }
--------------------------------------------------------------------------------
/ts/src/utils/system/nprogress.ts:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress"
2 | import "nprogress/nprogress.css"
3 |
4 | NProgress.configure({
5 | easing: 'ease', // 动画方式
6 | speed: 500, // 递增进度条的速度
7 | showSpinner: true, // 是否显示加载ico
8 | trickleSpeed: 200, // 自动递增间隔
9 | minimum: 0.3 // 初始化时的最小百分比
10 | })
11 |
12 | export default NProgress
--------------------------------------------------------------------------------
/ts/src/utils/system/request.ts:
--------------------------------------------------------------------------------
1 | import axios , { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'
2 | import store from '@/store'
3 | import { ElMessage } from 'element-plus'
4 | const baseURL: any = import.meta.env.VITE_BASE_URL
5 |
6 | const service: AxiosInstance = axios.create({
7 | baseURL: baseURL,
8 | timeout: 5000
9 | })
10 |
11 | // 请求前的统一处理
12 | service.interceptors.request.use(
13 | (config: AxiosRequestConfig) => {
14 | // JWT鉴权处理
15 | if (store.getters['user/token']) {
16 | config.headers['token'] = store.state.user.token
17 | }
18 | return config
19 | },
20 | (error: AxiosError) => {
21 | console.log(error) // for debug
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | service.interceptors.response.use(
27 | (response: AxiosResponse) => {
28 | const res = response.data
29 | if (res.code === 200) {
30 | return res
31 | } else {
32 | showError(res)
33 | return Promise.reject(res)
34 | }
35 | },
36 | (error: AxiosError)=> {
37 | console.log(error) // for debug
38 | const badMessage: any = error.message || error
39 | const code = parseInt(badMessage.toString().replace('Error: Request failed with status code ', ''))
40 | showError({ code, message: badMessage })
41 | return Promise.reject(error)
42 | }
43 | )
44 |
45 | function showError(error: any) {
46 | if (error.code === 403) {
47 | // to re-login
48 | store.dispatch('user/loginOut')
49 | } else {
50 | ElMessage({
51 | message: error.msg || error.message || '服务异常',
52 | type: 'error',
53 | duration: 3 * 1000
54 | })
55 | }
56 |
57 | }
58 |
59 | export default service
--------------------------------------------------------------------------------
/ts/src/utils/system/statistics.ts:
--------------------------------------------------------------------------------
1 | // 百度统计代码,需自行更换
2 | export function baidu() {
3 | const script = document.createElement('script')
4 | script.type = 'text/javascript'
5 | script.text = `
6 | var _hmt = _hmt || [];
7 | (function() {
8 | var hm = document.createElement("script");
9 | hm.src = "https://hm.baidu.com/hm.js?bd78bc908e66174e7dde385bf37cb4c1";
10 | var s = document.getElementsByTagName("script")[0];
11 | s.parentNode.insertBefore(hm, s);
12 | })();
13 | `
14 | document.getElementsByTagName('head')[0].appendChild(script)
15 | }
--------------------------------------------------------------------------------
/ts/src/utils/system/title.ts:
--------------------------------------------------------------------------------
1 | import i18n from '@/locale'
2 | import { systemTitle } from '@/config'
3 |
4 | export function changeTitle(name: any) {
5 | document.title = `${name}-${systemTitle}`
6 | }
7 |
--------------------------------------------------------------------------------
/ts/src/views/main/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我是首页
4 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/ts/src/views/main/pages/categoryTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts/src/views/main/pages/categoryTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
28 |
29 |
--------------------------------------------------------------------------------
/ts/src/views/main/pages/crudTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts/src/views/main/pages/treeTable/enum.ts:
--------------------------------------------------------------------------------
1 | export const selectData = [
2 | { value: 1, label: '运动' },
3 | { value: 2, label: '健身' },
4 | { value: 3, label: '跑酷' },
5 | { value: 4, label: '街舞' }
6 | ]
7 |
8 | export const radioData = [
9 | { value: 1, label: '今天' },
10 | { value: 2, label: '明天' },
11 | { value: 3, label: '后天' },
12 | ]
--------------------------------------------------------------------------------
/ts/src/views/main/pages/treeTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
--------------------------------------------------------------------------------
/ts/src/views/system/redirect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "esModuleInterop": true,
11 | "lib": ["esnext", "dom"],
12 | "types": ["vite/client"],
13 | "baseUrl": "./",
14 | "paths": {
15 | "@/*": ["src/*"]
16 | }
17 | },
18 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
19 | }
20 |
--------------------------------------------------------------------------------
/ts/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { ConfigEnv, UserConfigExport } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { viteMockServe } from 'vite-plugin-mock'
4 | import { resolve } from 'path'
5 |
6 | const pathResolve = (dir: string): any => {
7 | return resolve(__dirname, ".", dir)
8 | }
9 |
10 | const alias: Record = {
11 | '@': pathResolve("src")
12 | }
13 |
14 | // https://vitejs.dev/config/
15 | export default ({ command }: ConfigEnv): UserConfigExport => {
16 | const prodMock = true;
17 | return {
18 | base: './',
19 | resolve: {
20 | alias
21 | },
22 | server: {
23 | port: 3004,
24 | host: '0.0.0.0',
25 | open: true,
26 | proxy: { // 代理配置
27 | '/dev': 'https://www.fastmock.site/mock/48cab8545e64d93ff9ba66a87ad04f6b/'
28 | },
29 | },
30 | build: {
31 | rollupOptions: {
32 | output: {
33 | manualChunks: {
34 |
35 | }
36 | }
37 | }
38 | },
39 | plugins: [
40 | vue(),
41 | viteMockServe({
42 | mockPath: 'mock',
43 | localEnabled: command === 'serve',
44 | prodEnabled: command !== 'serve' && prodMock,
45 | watchFiles: true,
46 | injectCode: `
47 | import { setupProdMockServer } from '../mockProdServer';
48 | setupProdMockServer();
49 | `,
50 | logger: true,
51 | }),
52 | ]
53 | };
54 | }
55 |
--------------------------------------------------------------------------------