chrysalis1215
4 years ago
16 changed files with 401 additions and 131 deletions
-
10.editorconfig
-
3.gitignore
-
39.prettierrc.js
-
1commitlint.config.js
-
0docker/nginx.conf
-
31mock/index.ts
-
4src/App.vue
-
40src/components/breadcrumb.vue
-
32src/components/footer.vue
-
72src/components/sidenav.tsx
-
129src/config/routes.js
-
5src/layouts/blank.vue
-
37src/router/index.js
-
83src/style/index.less
-
7src/style/sidenav.less
-
39src/utils/request.js
@ -1,10 +1,14 @@ |
|||
# editorconfig.org |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
end_of_line = lf |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
|||
|
|||
[*.{ts,js,vue,css}] |
|||
indent_size = 2 |
@ -0,0 +1,39 @@ |
|||
module.exports = { |
|||
// 一行最多 120 字符
|
|||
printWidth: 120, |
|||
// 使用 2 个空格缩进
|
|||
tabWidth: 2, |
|||
// 不使用缩进符,而使用空格
|
|||
useTabs: false, |
|||
// 行尾需要有分号
|
|||
semi: true, |
|||
// 使用单引号
|
|||
singleQuote: true, |
|||
// 对象的 key 仅在必要时用引号
|
|||
quoteProps: 'as-needed', |
|||
// jsx 不使用单引号,而使用双引号
|
|||
jsxSingleQuote: false, |
|||
// 末尾需要有逗号
|
|||
trailingComma: 'all', |
|||
// 大括号内的首尾需要空格
|
|||
bracketSpacing: true, |
|||
// jsx 标签的反尖括号需要换行
|
|||
jsxBracketSameLine: false, |
|||
// 箭头函数,只有一个参数的时候,也需要括号
|
|||
arrowParens: 'always', |
|||
// 每个文件格式化的范围是文件的全部内容
|
|||
rangeStart: 0, |
|||
rangeEnd: Infinity, |
|||
// 不需要写文件开头的 @prettier
|
|||
requirePragma: false, |
|||
// 不需要自动在文件开头插入 @prettier
|
|||
insertPragma: false, |
|||
// 使用默认的折行标准
|
|||
proseWrap: 'preserve', |
|||
// 根据显示样式决定 html 要不要折行
|
|||
htmlWhitespaceSensitivity: 'css', |
|||
// vue 文件中的 script 和 style 内不用缩进
|
|||
vueIndentScriptAndStyle: false, |
|||
// 换行符使用 lf
|
|||
endOfLine: 'lf', |
|||
}; |
@ -0,0 +1 @@ |
|||
module.exports = { extends: ['@commitlint/config-conventional'] }; |
@ -0,0 +1,31 @@ |
|||
/* eslint-disable */ |
|||
import { MockMethod } from 'vite-plugin-mock'; |
|||
const Mock = require('mockjs'); |
|||
export default [ |
|||
{ |
|||
url: '/api/get', |
|||
method: 'get', |
|||
response: () => ({ |
|||
code: 0, |
|||
data: { |
|||
name: 'vben', |
|||
list: Mock.mock({ |
|||
'list|1-10': [{ |
|||
'id|+1': 1, |
|||
}], |
|||
}), |
|||
} |
|||
}), |
|||
}, |
|||
{ |
|||
url: '/api/post', |
|||
method: 'post', |
|||
timeout: 2000, |
|||
response: { |
|||
code: 0, |
|||
data: { |
|||
name: 'vben', |
|||
}, |
|||
}, |
|||
}, |
|||
] as MockMethod[]; |
@ -1,13 +1,39 @@ |
|||
<template> |
|||
<t-breadcrumb :maxItemWidth="'150'" class="tdesign-breadcrumb"> |
|||
<t-breadcrumbItem>页面1</t-breadcrumbItem> |
|||
<t-breadcrumbItem>页面2页面2页面</t-breadcrumbItem> |
|||
<t-breadcrumbItem :maxWidth="'160'">页面3页面3页面3页面3页面3页面3</t-breadcrumbItem> |
|||
<template v-for="item in crumbs"> |
|||
<t-breadcrumbItem :key="item.path" :to="item.path"> |
|||
{{ item.title }} |
|||
</t-breadcrumbItem> |
|||
</template> |
|||
</t-breadcrumb> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'Tdesign-breadcrumb', |
|||
props: { |
|||
isVisible: Boolean, |
|||
}, |
|||
computed: { |
|||
crumbs() { |
|||
const pathArray = this.$route.path.split('/'); |
|||
pathArray.shift(); |
|||
|
|||
const breadcrumbs = pathArray.reduce((breadcrumbArray, path, idx) => { |
|||
breadcrumbArray.push({ |
|||
path, |
|||
to: breadcrumbArray[idx - 1] ? `/${breadcrumbArray[idx - 1].path}/${path}` : `/${path}`, |
|||
title: this.$route.matched[idx].meta.title || path, |
|||
}); |
|||
return breadcrumbArray; |
|||
}, []); |
|||
return breadcrumbs; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.tdesign-breadcrumb { |
|||
margin-bottom: 24px; |
|||
} |
|||
</style>> |
|||
.tdesign-breadcrumb { |
|||
// margin-bottom: 16px; |
|||
} |
|||
</style> |
@ -1,23 +1,27 @@ |
|||
<template> |
|||
<div class="tdesign-footer"> |
|||
<span> |
|||
Copyright @ 2021-{{ new Date().getFullYear() }} Tencent. All Rights Reserved |
|||
</span> |
|||
</div> |
|||
<div :class="prefix + '-footer'"> |
|||
<span> Copyright @ 2021-{{ new Date().getFullYear() }} Tencent. All Rights Reserved </span> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { prefix } from '@/config/global'; |
|||
export default { |
|||
name: 'tdesignFooter', |
|||
} |
|||
name: `${prefix}-footer`, |
|||
data() { |
|||
return { |
|||
prefix, |
|||
}; |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.tdesign-footer { |
|||
border-top: 1px solid rgba(0, 0, 0, 0.1); |
|||
padding-top: 24px; |
|||
opacity: 0.6; |
|||
line-height: 24px; |
|||
text-align: center; |
|||
@import '@/style/index'; |
|||
|
|||
.@{prefix}-footer { |
|||
color: rgba(0, 0, 0, 0.3); |
|||
line-height: 20px; |
|||
text-align: center; |
|||
} |
|||
</style>> |
|||
</style> |
@ -1,57 +1,76 @@ |
|||
export default [ |
|||
{ |
|||
path: '/dashboard', |
|||
icon: 'chart-pie', |
|||
title: '仪表板', |
|||
component: '/src/layouts/default.vue', |
|||
{ |
|||
path: '/detail', |
|||
icon: 'chart-pie', |
|||
title: '详情页', |
|||
component: '/src/layouts/default.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础详情页', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
}, |
|||
{ |
|||
title: '高级详情页', |
|||
path: 'advance', |
|||
component: '/src/pages/demo.vue', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: '/list', |
|||
icon: 'view-module', |
|||
title: '列表页', |
|||
component: '/src/layouts/td-layout.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础列表页', |
|||
path: 'base', |
|||
component: '/src/pages/list-base.vue', |
|||
}, |
|||
{ |
|||
title: '筛选列表页', |
|||
path: 'select', |
|||
component: '/src/pages/list-select.vue', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: '/form', |
|||
icon: 'queue', |
|||
title: '表单页', |
|||
component: '/src/layouts/base.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础表单页', |
|||
path: 'base', |
|||
component: '/src/pages/list-base.vue', |
|||
}, |
|||
{ |
|||
title: '分步表单页', |
|||
path: 'select', |
|||
component: '/src/pages/list-select.vue', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: '/dashboard', |
|||
icon: 'chart-pie', |
|||
title: '仪表板', |
|||
component: '/src/layouts/default.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础仪表盘', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础仪表盘', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
children: [ { |
|||
title: '基础仪表盘', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
}] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
path: '/list', |
|||
icon: 'view-module', |
|||
title: '列表页', |
|||
component: '/src/layouts/base.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础列表页', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
}, |
|||
{ |
|||
title: '筛选列表页', |
|||
path: 'select', |
|||
component: '/src/pages/list-select.vue', |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
path: '/form', |
|||
icon: 'queue', |
|||
title: '列表页', |
|||
component: '/src/layouts/base.vue', |
|||
children: [ |
|||
{ |
|||
title: '基础列表页', |
|||
path: 'base', |
|||
component: '/src/page/list-base.vue', |
|||
}, |
|||
{ |
|||
title: '筛选列表页', |
|||
path: 'select', |
|||
component: '/src/page/list-select.vue', |
|||
} |
|||
] |
|||
}, |
|||
|
|||
] |
|||
{ |
|||
title: '基础仪表盘', |
|||
path: 'base', |
|||
component: '/src/pages/demo.vue', |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}, |
|||
]; |
@ -1,21 +1,28 @@ |
|||
import routeConfig from '@/config/routes.js'; |
|||
|
|||
const getMenuRoutes = (list, parent) => { |
|||
if (!list) { |
|||
return []; |
|||
} |
|||
return list.map( item => { |
|||
const { path = '', component } = item; |
|||
return { |
|||
path: path, |
|||
component: () => import(component), |
|||
children: getMenuRoutes(item.children, item), |
|||
} |
|||
}) |
|||
const getMenuRoutes = (list) => { |
|||
if (!list) { |
|||
return []; |
|||
} |
|||
return list.map((item) => { |
|||
const { path = '', component } = item; |
|||
return { |
|||
path, |
|||
component: () => import(component), |
|||
children: getMenuRoutes(item.children, item), |
|||
meta: { |
|||
title: item.title, |
|||
}, |
|||
}; |
|||
}); |
|||
}; |
|||
|
|||
const routes = [ |
|||
...getMenuRoutes(routeConfig) |
|||
] |
|||
...getMenuRoutes(routeConfig), |
|||
{ |
|||
path: '*', |
|||
redirect: '/dashboard/base', |
|||
}, |
|||
]; |
|||
|
|||
export default routes; |
|||
export default routes; |
@ -0,0 +1,7 @@ |
|||
@import './variables.less'; |
|||
|
|||
// .@{prefix} { |
|||
// &-sidebar{ |
|||
// width: 232px; |
|||
// } |
|||
// } |
@ -0,0 +1,39 @@ |
|||
import axios from 'axios'; |
|||
|
|||
// const CODE = {
|
|||
// LOGIN_TIMEOUT: 1000,
|
|||
// REQUEST_SUCCESS: 0,
|
|||
// REQUEST_FOBID: 1001,
|
|||
// };
|
|||
|
|||
const instance = axios.create({ |
|||
baseURL: '/api', |
|||
timeout: 1000, |
|||
withCredentials: true, |
|||
}); |
|||
|
|||
instance.interceptors.request.use((config) => config); |
|||
|
|||
instance.interceptors.response.use(undefined, (err) => { |
|||
const { config } = err; |
|||
|
|||
if (!config || !config.retry) return Promise.reject(err); |
|||
|
|||
config.retryCount = config.retryCount || 0; |
|||
|
|||
if (config.retryCount >= config.retry) { |
|||
return Promise.reject(err); |
|||
} |
|||
|
|||
config.retryCount += 1; |
|||
|
|||
const backoff = new Promise((resolve) => { |
|||
setTimeout(() => { |
|||
resolve(); |
|||
}, config.retryDelay || 1); |
|||
}); |
|||
|
|||
return backoff.then(() => instance(config)); |
|||
}); |
|||
|
|||
export default instance; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue