├── src
├── assets
│ ├── css
│ │ ├── variables.less
│ │ ├── global.less
│ │ ├── antd-variables.less
│ │ └── mixin.less
│ ├── images
│ │ ├── logo.png
│ │ └── bg-smooth.jpg
│ └── iconsvg
│ │ ├── arrow-down.svg
│ │ ├── arrow-left.svg
│ │ ├── arrow-right.svg
│ │ ├── svgo.yml
│ │ ├── tick.svg
│ │ ├── refresh.svg
│ │ ├── close.svg
│ │ ├── home.svg
│ │ ├── list.svg
│ │ ├── arrow-left2.svg
│ │ ├── arrow-right2.svg
│ │ ├── icon.svg
│ │ ├── more.svg
│ │ ├── editor.svg
│ │ ├── close2.svg
│ │ ├── menu-fold.svg
│ │ ├── menu-unfold.svg
│ │ ├── chart.svg
│ │ ├── control.svg
│ │ ├── message.svg
│ │ ├── detail.svg
│ │ ├── language-outline.svg
│ │ ├── edit.svg
│ │ ├── page.svg
│ │ ├── theme.svg
│ │ ├── permissions.svg
│ │ ├── set.svg
│ │ └── components.svg
├── layouts
│ ├── IndexLayout
│ │ ├── css
│ │ │ ├── mixins.less
│ │ │ └── var.less
│ │ ├── components
│ │ │ ├── Icon.vue
│ │ │ ├── RightTopMessage.vue
│ │ │ ├── RightFooter.vue
│ │ │ ├── Left.vue
│ │ │ ├── RightTopUser.vue
│ │ │ ├── SiderMenu.vue
│ │ │ └── SiderMenuItem.vue
│ │ ├── composables
│ │ │ └── useTopMenuWidth.ts
│ │ └── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ ├── BlankLayout.vue
│ ├── UserLayout
│ │ ├── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ │ ├── routes.ts
│ │ └── index.vue
│ ├── UniversalLayout
│ │ ├── css
│ │ │ ├── mixins.less
│ │ │ └── var.less
│ │ ├── components
│ │ │ ├── RightFooter.vue
│ │ │ ├── Icon.vue
│ │ │ ├── RightTopMessage.vue
│ │ │ ├── LeftSider.vue
│ │ │ ├── SiderMenuItem.vue
│ │ │ ├── RightTopUser.vue
│ │ │ └── SiderMenu.vue
│ │ ├── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ │ └── index.vue
│ └── SecurityLayout.vue
├── components
│ ├── IconFont
│ │ ├── index.ts
│ │ └── index.vue
│ ├── ScreenTable
│ │ └── data.d.ts
│ ├── IconSvg
│ │ ├── index.ts
│ │ └── index.vue
│ ├── ALink
│ │ └── index.vue
│ ├── FormFooterToolbar
│ │ └── index.vue
│ ├── BreadCrumbs
│ │ └── index.vue
│ ├── Permission
│ │ └── index.vue
│ ├── TuiEditor
│ │ └── viewer.vue
│ └── SelectLang
│ │ └── index.vue
├── views
│ ├── 404
│ │ └── index.vue
│ ├── component
│ │ ├── icon
│ │ │ ├── svg
│ │ │ │ ├── locales
│ │ │ │ │ ├── zh-CN.ts
│ │ │ │ │ ├── zh-TW.ts
│ │ │ │ │ └── en-US.ts
│ │ │ │ └── index.vue
│ │ │ └── font
│ │ │ │ └── index.vue
│ │ └── editor
│ │ │ ├── ckeditor
│ │ │ └── index.vue
│ │ │ └── tui-editor
│ │ │ └── index.vue
│ ├── custom-breadcrumbs
│ │ ├── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ │ └── index.vue
│ ├── user
│ │ ├── login
│ │ │ ├── data.d.ts
│ │ │ ├── service.ts
│ │ │ ├── locales
│ │ │ │ ├── zh-CN.ts
│ │ │ │ ├── zh-TW.ts
│ │ │ │ └── en-US.ts
│ │ │ └── store.ts
│ │ └── register
│ │ │ ├── data.d.ts
│ │ │ ├── service.ts
│ │ │ ├── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ │ │ └── store.ts
│ ├── home
│ │ ├── components
│ │ │ ├── HotSearchCard
│ │ │ │ ├── data.d.ts
│ │ │ │ └── index.vue
│ │ │ ├── ArticleHitCard
│ │ │ │ ├── data.d.ts
│ │ │ │ └── index.vue
│ │ │ ├── WorksHitCard
│ │ │ │ ├── data.d.ts
│ │ │ │ └── index.vue
│ │ │ ├── HotTagsCard
│ │ │ │ ├── data.d.ts
│ │ │ │ └── index.vue
│ │ │ ├── PageLoading
│ │ │ │ └── index.vue
│ │ │ ├── WorksChartCard
│ │ │ │ ├── service.ts
│ │ │ │ └── data.d.ts
│ │ │ ├── LinksChartCard
│ │ │ │ ├── service.ts
│ │ │ │ └── data.d.ts
│ │ │ ├── TopicsChartCard
│ │ │ │ ├── service.ts
│ │ │ │ └── data.d.ts
│ │ │ └── ArticleChartCard
│ │ │ │ ├── service.ts
│ │ │ │ └── data.d.ts
│ │ ├── service.ts
│ │ ├── data.d.ts
│ │ ├── locales
│ │ │ ├── zh-CN.ts
│ │ │ ├── zh-TW.ts
│ │ │ └── en-US.ts
│ │ └── index.vue
│ ├── pagesample
│ │ ├── form
│ │ │ ├── complex
│ │ │ │ ├── components
│ │ │ │ │ └── TableForm
│ │ │ │ │ │ └── data.d.ts
│ │ │ │ ├── service.ts
│ │ │ │ ├── data.d.ts
│ │ │ │ └── store.ts
│ │ │ └── basic
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── store.ts
│ │ ├── detail
│ │ │ ├── basic
│ │ │ │ ├── service.ts
│ │ │ │ ├── data.d.ts
│ │ │ │ └── store.ts
│ │ │ ├── module
│ │ │ │ ├── service.ts
│ │ │ │ ├── data.d.ts
│ │ │ │ └── store.ts
│ │ │ └── table
│ │ │ │ ├── service.ts
│ │ │ │ ├── data.d.ts
│ │ │ │ └── store.ts
│ │ └── list
│ │ │ ├── basic
│ │ │ ├── data.d.ts
│ │ │ ├── service.ts
│ │ │ └── components
│ │ │ │ └── TypeSelect.vue
│ │ │ ├── table
│ │ │ ├── data.d.ts
│ │ │ ├── service.ts
│ │ │ └── components
│ │ │ │ └── TypeSelect.vue
│ │ │ ├── search
│ │ │ └── table
│ │ │ │ ├── data.d.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── components
│ │ │ │ └── TypeSelect.vue
│ │ │ └── highly-adaptive-table
│ │ │ ├── data.d.ts
│ │ │ ├── service.ts
│ │ │ └── components
│ │ │ ├── TypeSelect.vue
│ │ │ └── SearchDrawer.vue
│ ├── roles
│ │ ├── test
│ │ │ └── index.vue
│ │ └── user
│ │ │ └── index.vue
│ └── refresh
│ │ └── index.vue
├── locales
│ ├── zh-CN.ts
│ ├── zh-TW.ts
│ └── en-US.ts
├── utils
│ ├── validate.ts
│ ├── array.ts
│ ├── localToken.ts
│ ├── mock
│ │ ├── require-context.js
│ │ └── server.js
│ ├── object.ts
│ └── store.ts
├── config
│ ├── store.ts
│ ├── i18n.ts
│ ├── settings.ts
│ └── routes.ts
├── shims-vue.d.ts
├── services
│ └── user.ts
├── main.ts
├── App.vue
├── composables
│ ├── useTitle.ts
│ ├── useI18nAntdFormVaildateInfos.ts
│ └── useEcharts.ts
├── directives
│ └── permission
│ │ └── index.ts
└── store
│ ├── user.ts
│ └── global.ts
├── .browserslistrc
├── public
├── favicon.ico
└── index.html
├── babel.config.js
├── jest.config.js
├── .env.development
├── .env.production
├── .gitignore
├── tests
└── unit
│ └── example.spec.ts
├── tsconfig.json
├── mock
├── global.js
├── user.js
├── pagesample.js
└── home.js
├── .eslintrc.js
├── LICENSE
├── package.json
└── vue.config.js
/src/assets/css/variables.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/css/mixins.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 | ie 11
5 |
--------------------------------------------------------------------------------
/src/layouts/BlankLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lqsong/admin-antd-vue/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/components/IconFont/index.ts:
--------------------------------------------------------------------------------
1 | import IconFont from './index.vue';
2 |
3 | export default IconFont;
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lqsong/admin-antd-vue/HEAD/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/views/component/icon/svg/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.icon.svg.remark.title': '说明:',
3 | };
4 |
--------------------------------------------------------------------------------
/src/views/component/icon/svg/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.icon.svg.remark.title': '說明:',
3 | };
4 |
--------------------------------------------------------------------------------
/src/assets/images/bg-smooth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lqsong/admin-antd-vue/HEAD/src/assets/images/bg-smooth.jpg
--------------------------------------------------------------------------------
/src/views/component/icon/svg/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.icon.svg.remark.title': 'Remark:',
3 | };
4 |
--------------------------------------------------------------------------------
/src/views/custom-breadcrumbs/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.custom-breadcrumbs.msg': '请看上方面包屑。',
3 | };
4 |
--------------------------------------------------------------------------------
/src/views/custom-breadcrumbs/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.custom-breadcrumbs.msg': '請看上方面包屑。',
3 | };
4 |
--------------------------------------------------------------------------------
/src/views/user/login/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface LoginParamsType {
2 | username: string;
3 | password: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/views/home/components/HotSearchCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | name: string;
3 | hit: number;
4 | }
5 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'user-layout.menu.login': '登录',
3 | 'user-layout.menu.register': '注册',
4 | };
5 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'user-layout.menu.login': '登錄',
3 | 'user-layout.menu.register': '註冊',
4 | };
5 |
--------------------------------------------------------------------------------
/src/views/custom-breadcrumbs/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.custom-breadcrumbs.msg': 'Look at the crumbs on the top.',
3 | };
4 |
--------------------------------------------------------------------------------
/src/views/home/components/ArticleHitCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | id: number;
3 | title: string;
4 | hit: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/views/home/components/WorksHitCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | id: number;
3 | title: string;
4 | hit: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/views/user/register/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface RegisterParamsType {
2 | username: string;
3 | password: string;
4 | confirm: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'user-layout.menu.login': 'Login',
3 | 'user-layout.menu.register': 'Register',
4 | };
5 |
--------------------------------------------------------------------------------
/src/views/home/components/HotTagsCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListItem {
2 | id: number;
3 | name: string;
4 | hit: number;
5 | pinyin?: string;
6 | }
7 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
3 | transform: {
4 | '^.+\\.vue$': 'vue-jest'
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'empty': 'empty',
3 | 'app.global.menu.notfound': 'Not Found',
4 | 'app.global.form.validatefields.catch': '验证不通过,请检查输入',
5 | };
--------------------------------------------------------------------------------
/src/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'empty': 'empty',
3 | 'app.global.menu.notfound': 'Not Found',
4 | 'app.global.form.validatefields.catch': '驗證不通過,請檢查輸入',
5 | };
--------------------------------------------------------------------------------
/src/views/home/components/PageLoading/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/views/pagesample/form/complex/components/TableForm/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableFormDataType {
2 | key: string;
3 | name?: string;
4 | workId?: string;
5 | edit?: boolean;
6 | isNew?: boolean;
7 | }
--------------------------------------------------------------------------------
/src/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'empty': 'empty',
3 | 'app.global.menu.notfound': 'Not Found',
4 | 'app.global.form.validatefields.catch': 'The validation did not pass, please check the input',
5 | };
--------------------------------------------------------------------------------
/src/views/roles/test/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 此页面只有 test 与 admin 账号可以查看。
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/views/roles/user/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 此页面只有 user 与 admin 账号可以查看。
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | #运行环境
2 | NODE_ENV = 'development'
3 |
4 | # devServer port
5 | VUE_APP_PORT=8000
6 |
7 | # mock 是否开启 true|false , development环境有效
8 | VUE_APP_MOCK = true
9 |
10 | #api接口域名
11 | VUE_APP_APIHOST = /api
12 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/basic/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryDetail(): Promise {
4 | return request({
5 | url: '/pages/detail',
6 | method: 'get'
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/module/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryDetail(): Promise {
4 | return request({
5 | url: '/pages/detail',
6 | method: 'get'
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/table/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryDetail(): Promise {
4 | return request({
5 | url: '/pages/detail',
6 | method: 'get'
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/arrow-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/home/components/WorksChartCard/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function weeknewWorks(): Promise {
4 | return request({
5 | url: '/home/works/weeknew',
6 | method: 'get'
7 | });
8 | }
--------------------------------------------------------------------------------
/src/views/pagesample/form/basic/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface FormDataType {
2 | title: string;
3 | date: string[];
4 | select: string;
5 | radio1: string;
6 | radio2: string;
7 | checkbox: string[];
8 | remark: string;
9 | }
10 |
--------------------------------------------------------------------------------
/src/views/home/components/LinksChartCard/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function annualnewLinks(): Promise {
4 | return request({
5 | url: '/home/links/annualnew',
6 | method: 'get'
7 | });
8 | }
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | #运行环境
2 | NODE_ENV = 'production'
3 |
4 | # devServer port
5 | VUE_APP_PORT=8000
6 |
7 | # mock 是否开启 true|false , development环境有效
8 | VUE_APP_MOCK = false
9 |
10 | #api接口域名
11 | VUE_APP_APIHOST = http://yapi.liqingsong.cc/mock/11
12 |
--------------------------------------------------------------------------------
/src/utils/validate.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 判断是否是外链
3 | * @param {string} path
4 | * @returns {Boolean}
5 | * @author LiQingSong
6 | */
7 | export const isExternal = (path: string): boolean => {
8 | return /^(https?:|mailto:|tel:)/.test(path);
9 | };
10 |
--------------------------------------------------------------------------------
/src/views/home/components/TopicsChartCard/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function monthnewTopics(): Promise {
4 | return request({
5 | url: '/home/topics/monthnew',
6 | method: 'get'
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/views/home/components/ArticleChartCard/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function dailynewArticles(): Promise {
4 | return request({
5 | url: '/home/articles/dailynew',
6 | method: 'get'
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/src/views/home/components/LinksChartCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface ChartDataType {
2 | day: string[];
3 | num: number[];
4 | }
5 |
6 | export interface LinksChartDataType {
7 | total: number;
8 | num: number;
9 | chart: ChartDataType;
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/ScreenTable/data.d.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface PaginationConfig {
3 | total: number;
4 | current: number;
5 | pageSize: number;
6 | showSizeChanger: boolean;
7 | showQuickJumper: boolean;
8 | onChange: (page: number, pageSize: number) => void;
9 | }
--------------------------------------------------------------------------------
/src/views/home/components/TopicsChartCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface ChartDataType {
2 | day: string[];
3 | num: number[];
4 | }
5 |
6 | export interface TopicsChartDataType {
7 | total: number;
8 | num: number;
9 | chart: ChartDataType;
10 | }
11 |
--------------------------------------------------------------------------------
/src/views/home/components/WorksChartCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface ChartDataType {
2 | day: string[];
3 | num: number[];
4 | }
5 |
6 |
7 | export interface WorksChartDataType {
8 | total: number;
9 | num: number;
10 | chart: ChartDataType;
11 | }
12 |
--------------------------------------------------------------------------------
/src/config/store.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Store 入口
3 | * @author LiQingSong
4 | */
5 | import { createStore } from 'vuex';
6 | import { importAllStore } from '@/utils/store';
7 |
8 |
9 | export default createStore({
10 | modules: importAllStore(),
11 | getters: {}
12 | })
13 |
--------------------------------------------------------------------------------
/src/views/home/components/ArticleChartCard/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface ChartDataType {
2 | day: string[];
3 | num: number[];
4 | }
5 |
6 | export interface ArticleChartDataType {
7 | total: number;
8 | num: number;
9 | week: number;
10 | day: number;
11 | }
12 |
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
7 | declare module '@ckeditor/ckeditor5-vue';
8 | declare module '@ckeditor/ckeditor5-build-decoupled-document';
9 |
--------------------------------------------------------------------------------
/src/assets/css/global.less:
--------------------------------------------------------------------------------
1 | @import '~ant-design-vue/dist/antd.less';
2 | @import './antd-variables.less';
3 | @import './variables.less';
4 | @import './mixin.less';
5 |
6 | #nprogress .bar {
7 | background: @primary-color !important;
8 | }
9 |
10 | .text-align-right{
11 | text-align: right;
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/views/pagesample/form/basic/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { FormDataType } from './data.d';
3 |
4 | export async function createData(params: FormDataType): Promise {
5 | return request({
6 | url: '/pages/form',
7 | method: 'POST',
8 | data: params,
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/src/views/pagesample/form/complex/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { FormDataType } from './data.d';
3 |
4 | export async function createData(params: FormDataType): Promise {
5 | return request({
6 | url: '/pages/form',
7 | method: 'POST',
8 | data: params,
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/src/views/user/login/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { LoginParamsType } from './data.d';
3 |
4 | export async function accountLogin(params: LoginParamsType): Promise {
5 | return request({
6 | url: '/user/login',
7 | method: 'POST',
8 | data: params,
9 | });
10 | }
--------------------------------------------------------------------------------
/src/views/user/register/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { RegisterParamsType } from './data.d';
3 |
4 | export async function accountReg(params: RegisterParamsType): Promise {
5 | return request({
6 | url: '/user/register',
7 | method: 'POST',
8 | data: params,
9 | });
10 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/src/views/pagesample/form/complex/data.d.ts:
--------------------------------------------------------------------------------
1 | import { TableFormDataType } from "./components/TableForm/data.d";
2 | export interface FormDataType {
3 | title: string;
4 | date: string[];
5 | select: string;
6 | radio1: string;
7 | radio2: string;
8 | checkbox: string[];
9 | remark: string;
10 | users: TableFormDataType[];
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/user.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 |
3 | export async function queryCurrent(): Promise {
4 | return request({
5 | url: '/user/info',
6 | method: 'get'
7 | });
8 | }
9 |
10 | export async function queryMessage(): Promise {
11 | return request({
12 | url: '/user/message'
13 | });
14 | }
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/css/var.less:
--------------------------------------------------------------------------------
1 | // Menu
2 | @menu-collapsed-width: 54px;
3 |
4 | // dark theme
5 | @menu-dark-bg: #222834;
6 |
7 |
8 | // 主窗口背景色
9 | @mainBgColor: #f0f3f4;
10 | // 左边宽度
11 | @leftSideBarWidth: 200px;
12 | // 头部高度
13 | @headerHeight: 50px;
14 | // 头部下方面包屑高度
15 | @headerBreadcrumbHeight: 34px;
16 | // 头部面包屑下tab导航高度
17 | @headerTabNavHeight: 36px;
--------------------------------------------------------------------------------
/src/assets/iconsvg/svgo.yml:
--------------------------------------------------------------------------------
1 | # replace default config
2 |
3 | # multipass: true
4 | # full: true
5 |
6 | plugins:
7 |
8 | # - name
9 | #
10 | # or:
11 | # - name: false
12 | # - name: true
13 | #
14 | # or:
15 | # - name:
16 | # param1: 1
17 | # param2: 2
18 |
19 | - removeAttrs:
20 | attrs:
21 | - 'fill'
22 | - 'fill-rule'
23 |
--------------------------------------------------------------------------------
/src/views/404/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Back Home
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/unit/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import HelloWorld from '@/components/HelloWorld.vue'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(HelloWorld, {
8 | props: { msg }
9 | })
10 | expect(wrapper.text()).toMatch(msg)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/css/mixins.less:
--------------------------------------------------------------------------------
1 | .layout-menu() {
2 | &.ant-menu-horizontal {
3 | line-height: 48px;
4 | }
5 |
6 | }
7 |
8 | .light-menu() {
9 | --universallayout-menu-bg-color: #FFF;
10 | --universallayout-submenu-bg-color: #fafafa;
11 | --universallayout-menu-color: rgba(0,0,0,.85);
12 | --universallayout-menu-higlight-bg-color: #e6f7ff;
13 | --universallayout-menu-highlight-color: @primary-color;
14 | }
15 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/tick.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/RightFooter.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
9 |
19 |
--------------------------------------------------------------------------------
/src/views/refresh/index.vue:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/routes.ts:
--------------------------------------------------------------------------------
1 | import { RoutesDataItem } from "@/utils/routes";
2 |
3 | const UserLayoutRoutes: RoutesDataItem[] = [
4 | {
5 | title: 'user-layout.menu.login',
6 | path: 'login',
7 | component: () => import('@/views/user/login/index.vue'),
8 | },
9 | {
10 | title: 'user-layout.menu.register',
11 | path: 'register',
12 | component: () => import('@/views/user/register/index.vue'),
13 | }
14 | ];
15 |
16 | export default UserLayoutRoutes;
--------------------------------------------------------------------------------
/src/assets/iconsvg/close.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/IconSvg/index.ts:
--------------------------------------------------------------------------------
1 | import iconsvg from "./index.vue";
2 |
3 | /**
4 | * 自动导入 @/assets/iconsvg 下svg文件
5 | * @author LiQingSong
6 | */
7 | export function importAllSvg (): void {
8 | try {
9 | const requireContext: __WebpackModuleApi.RequireContext = require.context('../../assets/iconsvg', false, /\.svg$/);
10 | requireContext.keys().forEach(requireContext);
11 | } catch (error) {
12 | // eslint-disable-next-line no-console
13 | console.log(error);
14 | }
15 | }
16 |
17 | export default iconsvg;
--------------------------------------------------------------------------------
/src/assets/iconsvg/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/user/login/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.login.form-item-username': '用户名: admin or test or user',
3 | 'page.user.login.form-item-username.required': '请输入用户名',
4 | 'page.user.login.form-item-password': '密码:123456',
5 | 'page.user.login.form-item-password.required': '请输入密码',
6 | 'page.user.login.form.title': '账户登录',
7 | 'page.user.login.form.btn-submit': '登录',
8 | 'page.user.login.form.btn-jump': '还没有账户?现在注册!',
9 | 'page.user.login.form.login-error': '用户名或密码错误!',
10 | 'page.user.login.form.login-success': '登录成功!',
11 | };
12 |
--------------------------------------------------------------------------------
/src/views/user/login/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.login.form-item-username': '用戶名: admin or test or user',
3 | 'page.user.login.form-item-username.required': '請輸入用戶名',
4 | 'page.user.login.form-item-password': '密碼: 123456',
5 | 'page.user.login.form-item-password.required': '請輸入密碼',
6 | 'page.user.login.form.title': '賬戶登錄',
7 | 'page.user.login.form.btn-submit': '登錄',
8 | 'page.user.login.form.btn-jump': '還沒有賬戶?現在註冊!',
9 | 'page.user.login.form.login-error': '用戶名或密碼錯誤!',
10 | 'page.user.login.form.login-success': '登錄成功!',
11 | };
12 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 |
3 | // 全局样式
4 | import '@/assets/css/global.less';
5 |
6 | // 引入 Antd
7 | import Antd from 'ant-design-vue';
8 |
9 | // 导入 svg
10 | import { importAllSvg } from "@/components/IconSvg/index";
11 | importAllSvg();
12 |
13 | import App from '@/App.vue';
14 | import router from '@/config/routes';
15 | import store from '@/config/store';
16 | import i18n from '@/config/i18n';
17 |
18 |
19 | const app = createApp(App)
20 | app.use(store);
21 | app.use(router)
22 | app.use(Antd);
23 | app.use(i18n);
24 | app.mount('#app');
25 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/basic/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListQueryParams {
2 | page: number;
3 | per: number;
4 | }
5 |
6 | export interface PaginationConfig {
7 | total: number;
8 | current: number;
9 | pageSize: number;
10 | showSizeChanger: boolean;
11 | showQuickJumper: boolean;
12 | }
13 |
14 | export interface TableListItem {
15 | id: number;
16 | name: string;
17 | desc: string;
18 | href: string;
19 | type: string;
20 | }
21 |
22 | export interface TableDataType {
23 | list: TableListItem[];
24 | pagination: PaginationConfig;
25 | }
26 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/table/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListQueryParams {
2 | page: number;
3 | per: number;
4 | }
5 |
6 | export interface PaginationConfig {
7 | total: number;
8 | current: number;
9 | pageSize: number;
10 | showSizeChanger: boolean;
11 | showQuickJumper: boolean;
12 | }
13 |
14 | export interface TableListItem {
15 | id: number;
16 | name: string;
17 | desc: string;
18 | href: string;
19 | type: string;
20 | }
21 |
22 | export interface TableDataType {
23 | list: TableListItem[];
24 | pagination: PaginationConfig;
25 | }
26 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/search/table/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListQueryParams {
2 | page: number;
3 | per: number;
4 | }
5 |
6 | export interface PaginationConfig {
7 | total: number;
8 | current: number;
9 | pageSize: number;
10 | showSizeChanger: boolean;
11 | showQuickJumper: boolean;
12 | }
13 |
14 | export interface TableListItem {
15 | id: number;
16 | name: string;
17 | desc: string;
18 | href: string;
19 | type: string;
20 | }
21 |
22 | export interface TableDataType {
23 | list: TableListItem[];
24 | pagination: PaginationConfig;
25 | }
26 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/list.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/highly-adaptive-table/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface TableListQueryParams {
2 | page: number;
3 | per: number;
4 | }
5 |
6 | export interface PaginationConfig {
7 | total: number;
8 | current: number;
9 | pageSize: number;
10 | showSizeChanger: boolean;
11 | showQuickJumper: boolean;
12 | }
13 |
14 | export interface TableListItem {
15 | id: number;
16 | name: string;
17 | desc: string;
18 | href: string;
19 | type: string;
20 | }
21 |
22 | export interface TableDataType {
23 | list: TableListItem[];
24 | pagination: PaginationConfig;
25 | }
26 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/arrow-left2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/arrow-right2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/more.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/user/login/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.login.form-item-username': 'username: admin or test or user',
3 | 'page.user.login.form-item-username.required': 'Please input your username',
4 | 'page.user.login.form-item-password': 'password: 123465',
5 | 'page.user.login.form-item-password.required': 'Please input your password',
6 | 'page.user.login.form.title': 'Account Login',
7 | 'page.user.login.form.btn-submit': 'Sign in',
8 | 'page.user.login.form.btn-jump': 'or register now!',
9 | 'page.user.login.form.login-error': 'Wrong username or password!',
10 | 'page.user.login.form.login-success': 'Login successful!',
11 | };
12 |
--------------------------------------------------------------------------------
/src/views/user/register/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.register.form-item-username': '用户名',
3 | 'page.user.register.form-item-username.required': '请输入用户名',
4 | 'page.user.register.form-item-password': '密码',
5 | 'page.user.register.form-item-password.required': '请输入密码',
6 | 'page.user.register.form-item-confirmpassword': '确认密码',
7 | 'page.user.register.form-item-confirmpassword.compare':
8 | '您输入的两个密码不匹配!',
9 | 'page.user.register.form.title': '注册账户',
10 | 'page.user.register.form.btn-submit': '注册',
11 | 'page.user.register.form.btn-jump': '已有账户?现在登录!',
12 | 'page.user.register.form.register-success': '注册成功,请登录!',
13 | };
14 |
--------------------------------------------------------------------------------
/src/views/user/register/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.register.form-item-username': '用戶名',
3 | 'page.user.register.form-item-username.required': '請輸入用戶名',
4 | 'page.user.register.form-item-password': '密碼',
5 | 'page.user.register.form-item-password.required': '請輸入密碼',
6 | 'page.user.register.form-item-confirmpassword': '確認密碼',
7 | 'page.user.register.form-item-confirmpassword.compare':
8 | '您輸入的兩個密碼不匹配!',
9 | 'page.user.register.form.title': '註冊賬戶',
10 | 'page.user.register.form.btn-submit': '註冊',
11 | 'page.user.register.form.btn-jump': '已有賬戶?現在登錄!',
12 | 'page.user.register.form.register-success': '註冊成功,請登錄!',
13 | };
14 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/editor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/custom-breadcrumbs/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{t('page.custom-breadcrumbs.msg')}}
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/utils/array.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 数组合并并去重
3 | * @param arr1 数组1
4 | * @param arr2 数组2
5 | */
6 | export function mergeUnique(arr1: Array, arr2: Array): Array {
7 | const arr: Array = arr1;
8 | for (let index = 0, len = arr2.length; index < len; index += 1) {
9 | if (!arr.includes(arr2[index])) {
10 | arr.push(arr2[index]);
11 | }
12 | }
13 |
14 | return arr;
15 | }
16 |
17 | /**
18 | * 数组去重
19 | * @param arr 数组
20 | */
21 | export function unique(arr: Array): Array {
22 | const array: Array = [];
23 | for (let index = 0, len = arr.length; index < len; index += 1) {
24 | if (!array.includes(arr[index])) {
25 | array.push(arr[index]);
26 | }
27 | }
28 | return array;
29 | }
30 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/css/antd-variables.less:
--------------------------------------------------------------------------------
1 | // 此文件针对 ant-design 定制主题:
2 |
3 | /* @primary-color: #1890ff; // 全局主色 */
4 | @primary-color: #009688;
5 | @link-color: #1890ff; // 链接色
6 | @success-color: #52c41a; // 成功色
7 | @warning-color: #faad14; // 警告色
8 | @error-color: #f5222d; // 错误色
9 | @font-size-base: 14px; // 主字号
10 | @heading-color: rgba(0, 0, 0, 0.85); // 标题色
11 | @text-color: rgba(0, 0, 0, 0.65); // 主文本色
12 | @text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
13 | @disabled-color: rgba(0, 0, 0, 0.25); // 失效色
14 | @border-radius-base: 2px; // 组件/浮层圆角
15 | @border-color-base: #d9d9d9; // 边框色
16 | @box-shadow-base: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
17 | 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05); // 浮层阴影
18 |
19 | // Menu
20 | @menu-collapsed-width: 54px;
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/Icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
29 |
--------------------------------------------------------------------------------
/src/composables/useTitle.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 设置 html Title composables
3 | * @author LiQingSong
4 | */
5 | import { ComputedRef, onMounted, Ref, watch } from 'vue';
6 | import { useI18n } from 'vue-i18n';
7 | import settings from '@/config/settings';
8 | import { RoutesDataItem } from '@/utils/routes';
9 |
10 | export default function useTitle(route: ComputedRef | Ref): void {
11 | const{ t } = useI18n();
12 |
13 | const setTitle = (title: string): void => {
14 | document.title = `${t(title)} - ${settings.siteTitle}`;
15 | }
16 |
17 | watch(route,() => {
18 | setTitle(route.value.title || '');
19 | })
20 |
21 | onMounted(()=> {
22 | setTitle(route.value.title);
23 | })
24 |
25 | }
--------------------------------------------------------------------------------
/src/assets/iconsvg/close2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/ALink/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/css/var.less:
--------------------------------------------------------------------------------
1 | :root {
2 | // 网页背景色
3 | --universallayout-bg-color: #FFFFFF;
4 | // 网页字体色
5 | --universallayout-color: rgba(0,0,0,.85);
6 | // 主窗口背景色
7 | --universallayout-main-bg-color: #f0f3f4;
8 |
9 | // 菜单
10 | --universallayout-menu-collapsed-width: @menu-collapsed-width;
11 | --universallayout-menu-bg-color: #001529;
12 | --universallayout-submenu-bg-color: #000c17;
13 | --universallayout-menu-color: rgba(255,255,255, 0.65);
14 | --universallayout-menu-higlight-bg-color: @primary-color;
15 | --universallayout-menu-highlight-color: #FFFFFF;
16 |
17 |
18 | // 头部高度
19 | --universallayout-header-height: 48px;
20 | // 左边宽度
21 | --universallayout-left-side-bar-width: 200px;
22 | // 头部Tab导航高度
23 | --universallayout-header-tab-nav-height: 36px;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/assets/css/mixin.less:
--------------------------------------------------------------------------------
1 | .scrollbar(
2 | @thumb-background: hsla(0,0%,100%,.2),
3 | @thumb-shadow: hsla(0,0%,100%,.05),
4 | @track-background:hsla(0,0%,100%,.15),
5 | @track-shadow: rgba(37,37,37,.05)
6 | ) {
7 | ::-webkit-scrollbar {
8 | width: 6px;
9 | height: 6px;
10 | }
11 | ::-webkit-scrollbar-thumb {
12 | background: @thumb-background;
13 | border-radius: 3px;
14 | box-shadow: inset 0 0 5px @thumb-shadow;
15 | }
16 | ::-webkit-scrollbar-track {
17 | background: @track-background;
18 | border-radius: 3px;
19 | box-shadow: inset 0 0 5px rgba(37,37,37,.05);
20 | }
21 | }
22 |
23 | .scrollbar-light {
24 | .scrollbar(
25 | hsla(0,0%,0%,.2), hsla(0,0%,0%,.05),
26 | hsla(0,0%,0%,.15), rgba(255,255,255,.05)
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/views/component/editor/ckeditor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
27 |
--------------------------------------------------------------------------------
/src/views/user/register/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.user.register.form-item-username': 'Username',
3 | 'page.user.register.form-item-username.required':
4 | 'Please input your username',
5 | 'page.user.register.form-item-password': 'Password',
6 | 'page.user.register.form-item-password.required':
7 | 'Please input your password',
8 | 'page.user.register.form-item-confirmpassword': 'Confirm Password',
9 | 'page.user.register.form-item-confirmpassword.compare':
10 | 'The two passwords that you entered do not match!',
11 | 'page.user.register.form.title': 'Account Registration',
12 | 'page.user.register.form.btn-submit': 'Register',
13 | 'page.user.register.form.btn-jump': 'Already have an account?',
14 | 'page.user.register.form.register-success':
15 | 'Registered successfully, please log in!',
16 | };
17 |
--------------------------------------------------------------------------------
/src/components/FormFooterToolbar/index.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
16 |
17 |
35 |
--------------------------------------------------------------------------------
/src/utils/localToken.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 自定义 token 操作
3 | * @author LiQingSong
4 | */
5 | import localforage from 'localforage';
6 | import settings from '@/config/settings';
7 |
8 | /**
9 | * 获取本地Token
10 | */
11 | export const getToken = async (): Promise => {
12 | return await localforage.getItem(settings.siteTokenKey);
13 | };
14 |
15 | /**
16 | * 设置存储本地Token
17 | */
18 | export const setToken = async (token: string): Promise => {
19 | try {
20 | await localforage.setItem(settings.siteTokenKey, token);
21 | return true;
22 | } catch (error) {
23 | return false;
24 | }
25 | };
26 |
27 | /**
28 | * 移除本地Token
29 | */
30 | export const removeToken = async (): Promise => {
31 | try {
32 | await localforage.removeItem(settings.siteTokenKey);
33 | return true;
34 | } catch (error) {
35 | return false;
36 | }
37 | };
--------------------------------------------------------------------------------
/src/assets/iconsvg/menu-fold.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/menu-unfold.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/RightTopMessage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
25 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/chart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/component/editor/tui-editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
29 |
--------------------------------------------------------------------------------
/src/views/home/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { TableListQueryParams } from './data.d';
3 |
4 | export async function hotSearchQueryList(params?: TableListQueryParams): Promise {
5 | return request({
6 | url: '/home/searchs/keywords',
7 | method: 'get',
8 | params,
9 | });
10 | }
11 |
12 | export async function hotTagsQueryList(params?: TableListQueryParams): Promise {
13 | return request({
14 | url: '/home/tags',
15 | method: 'get',
16 | params,
17 | });
18 | }
19 |
20 | export async function articleHitQueryList(params?: TableListQueryParams): Promise {
21 | return request({
22 | url: '/home/articles',
23 | method: 'get',
24 | params,
25 | });
26 | }
27 |
28 | export async function worksHitQueryList(params?: TableListQueryParams): Promise {
29 | return request({
30 | url: '/home/works',
31 | method: 'get',
32 | params,
33 | });
34 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "experimentalDecorators": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "sourceMap": true,
14 | "noImplicitAny": false,
15 | "baseUrl": ".",
16 | "types": [
17 | "webpack-env",
18 | "jest"
19 | ],
20 | "paths": {
21 | "@/*": [
22 | "src/*"
23 | ]
24 | },
25 | "lib": [
26 | "esnext",
27 | "dom",
28 | "dom.iterable",
29 | "scripthost"
30 | ]
31 | },
32 | "include": [
33 | "src/**/*.ts",
34 | "src/**/*.tsx",
35 | "src/**/*.vue",
36 | "tests/**/*.ts",
37 | "tests/**/*.tsx"
38 | ],
39 | "exclude": [
40 | "node_modules"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/basic/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface UserInfoDataType {
2 | name: string;
3 | tel: string;
4 | courier: string;
5 | address: string;
6 | remark: string;
7 | }
8 |
9 | export interface RefundApplicationDataType {
10 | ladingNo: string;
11 | saleNo: string;
12 | state: string;
13 | childOrders: string;
14 | }
15 |
16 | export interface ReturnGoodsDataType {
17 | id: string;
18 | name?: string;
19 | barcode?: string;
20 | price?: string;
21 | num?: string | number;
22 | amount?: string | number;
23 | }
24 |
25 | export interface ReturnProgressDataType {
26 | key: string;
27 | time: string;
28 | rate: string;
29 | status: string;
30 | operator: string;
31 | cost: string;
32 | }
33 |
34 | export interface DetailDataType {
35 | userInfo: UserInfoDataType;
36 | refundApplication: RefundApplicationDataType;
37 | returnGoods: ReturnGoodsDataType[];
38 | returnProgress: ReturnProgressDataType[];
39 | }
40 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/module/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface UserInfoDataType {
2 | name: string;
3 | tel: string;
4 | courier: string;
5 | address: string;
6 | remark: string;
7 | }
8 |
9 | export interface RefundApplicationDataType {
10 | ladingNo: string;
11 | saleNo: string;
12 | state: string;
13 | childOrders: string;
14 | }
15 |
16 | export interface ReturnGoodsDataType {
17 | id: string;
18 | name?: string;
19 | barcode?: string;
20 | price?: string;
21 | num?: string | number;
22 | amount?: string | number;
23 | }
24 |
25 | export interface ReturnProgressDataType {
26 | key: string;
27 | time: string;
28 | rate: string;
29 | status: string;
30 | operator: string;
31 | cost: string;
32 | }
33 |
34 | export interface DetailDataType {
35 | userInfo: UserInfoDataType;
36 | refundApplication: RefundApplicationDataType;
37 | returnGoods: ReturnGoodsDataType[];
38 | returnProgress: ReturnProgressDataType[];
39 | }
40 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/table/data.d.ts:
--------------------------------------------------------------------------------
1 | export interface UserInfoDataType {
2 | name: string;
3 | tel: string;
4 | courier: string;
5 | address: string;
6 | remark: string;
7 | }
8 |
9 | export interface RefundApplicationDataType {
10 | ladingNo: string;
11 | saleNo: string;
12 | state: string;
13 | childOrders: string;
14 | }
15 |
16 | export interface ReturnGoodsDataType {
17 | id: string;
18 | name?: string;
19 | barcode?: string;
20 | price?: string;
21 | num?: string | number;
22 | amount?: string | number;
23 | }
24 |
25 | export interface ReturnProgressDataType {
26 | key: string;
27 | time: string;
28 | rate: string;
29 | status: string;
30 | operator: string;
31 | cost: string;
32 | }
33 |
34 | export interface DetailDataType {
35 | userInfo: UserInfoDataType;
36 | refundApplication: RefundApplicationDataType;
37 | returnGoods: ReturnGoodsDataType[];
38 | returnProgress: ReturnProgressDataType[];
39 | }
40 |
--------------------------------------------------------------------------------
/mock/global.js:
--------------------------------------------------------------------------------
1 | const { VUE_APP_APIHOST } = process.env;
2 | const mock = {};
3 |
4 | mock[`POST ${VUE_APP_APIHOST || ''}/uploads`] = (req, res) => {
5 | res.send({
6 | code: 0,
7 | data: {
8 | id: 1,
9 | url:
10 | 'http://uploads.liqingsong.cc/20200531/583057e8-8bab-4eee-b5a0-bec915089c0c.jpg',
11 | name: 'xcx.jpg',
12 | },
13 | msg: '',
14 | });
15 | };
16 |
17 | mock[`GET ${VUE_APP_APIHOST}/500`] = (req, res) => {
18 | res.status(500).send({
19 | timestamp: 1513932555104,
20 | status: 500,
21 | error: 'error',
22 | message: 'error',
23 | path: '/500',
24 | });
25 | };
26 |
27 | mock[`GET ${VUE_APP_APIHOST}/404`] = (req, res) => {
28 | res.status(404).send({
29 | timestamp: 1513932643431,
30 | status: 404,
31 | error: 'Not Found',
32 | message: 'No message available',
33 | path: '/404',
34 | });
35 | };
36 |
37 | module.exports = {
38 | ...mock
39 | };
--------------------------------------------------------------------------------
/src/composables/useI18nAntdFormVaildateInfos.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 重置 Antd Form VaildateInfos 为 I18n
3 | * @author LiQingSong
4 | */
5 | import { computed, ComputedRef } from 'vue';
6 | import { useI18n } from 'vue-i18n';
7 | import { validateInfos } from 'ant-design-vue/lib/form/useForm';
8 |
9 | export default function useI18nAntdFormVaildateInfos(infos: validateInfos): ComputedRef {
10 | const{ t } = useI18n();
11 |
12 | const infosNew = computed(() => {
13 | const vInfos: validateInfos = {};
14 | for (const index in infos) {
15 | vInfos[index] = JSON.parse(JSON.stringify(infos[index]));
16 | if(vInfos[index] && vInfos[index]['help']) {
17 | vInfos[index]['help'] = vInfos[index]['help'].map((item: any)=> typeof(item)=='string' ? t(item) : item.map((item2: any)=> item2 ? t(item2):''));
18 | }
19 | }
20 | return vInfos;
21 | });
22 |
23 | return infosNew;
24 | }
--------------------------------------------------------------------------------
/src/assets/iconsvg/control.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/message.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/basic/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { TableListQueryParams, TableListItem } from './data.d';
3 |
4 | export async function queryList(params?: TableListQueryParams): Promise {
5 | return request({
6 | url: '/pages/list',
7 | method: 'get',
8 | params,
9 | });
10 | }
11 |
12 | export async function createData(params: Omit): Promise {
13 | return request({
14 | url: '/pages/list',
15 | method: 'POST',
16 | data: params,
17 | });
18 | }
19 |
20 | export async function updateData(id: number, params: Omit): Promise {
21 | return request({
22 | url: `/pages/list/${id}`,
23 | method: 'PUT',
24 | data: params,
25 | });
26 | }
27 |
28 | export async function removeData(id: number): Promise {
29 | return request({
30 | url: `/pages/list/${id}`,
31 | method: 'delete',
32 | });
33 | }
34 |
35 | export async function detailData(id: number): Promise {
36 | return request({url: `/pages/list/${id}`});
37 | }
38 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/table/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { TableListQueryParams, TableListItem } from './data.d';
3 |
4 | export async function queryList(params?: TableListQueryParams): Promise {
5 | return request({
6 | url: '/pages/list',
7 | method: 'get',
8 | params,
9 | });
10 | }
11 |
12 | export async function createData(params: Omit): Promise {
13 | return request({
14 | url: '/pages/list',
15 | method: 'POST',
16 | data: params,
17 | });
18 | }
19 |
20 | export async function updateData(id: number, params: Omit): Promise {
21 | return request({
22 | url: `/pages/list/${id}`,
23 | method: 'PUT',
24 | data: params,
25 | });
26 | }
27 |
28 | export async function removeData(id: number): Promise {
29 | return request({
30 | url: `/pages/list/${id}`,
31 | method: 'delete',
32 | });
33 | }
34 |
35 | export async function detailData(id: number): Promise {
36 | return request({url: `/pages/list/${id}`});
37 | }
38 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/search/table/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { TableListQueryParams, TableListItem } from './data.d';
3 |
4 | export async function queryList(params?: TableListQueryParams): Promise {
5 | return request({
6 | url: '/pages/list',
7 | method: 'get',
8 | params,
9 | });
10 | }
11 |
12 | export async function createData(params: Omit): Promise {
13 | return request({
14 | url: '/pages/list',
15 | method: 'POST',
16 | data: params,
17 | });
18 | }
19 |
20 | export async function updateData(id: number, params: Omit): Promise {
21 | return request({
22 | url: `/pages/list/${id}`,
23 | method: 'PUT',
24 | data: params,
25 | });
26 | }
27 |
28 | export async function removeData(id: number): Promise {
29 | return request({
30 | url: `/pages/list/${id}`,
31 | method: 'delete',
32 | });
33 | }
34 |
35 | export async function detailData(id: number): Promise {
36 | return request({url: `/pages/list/${id}`});
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/BreadCrumbs/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{t(item.title)}}
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/highly-adaptive-table/service.ts:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request';
2 | import { TableListQueryParams, TableListItem } from './data.d';
3 |
4 | export async function queryList(params?: TableListQueryParams): Promise {
5 | return request({
6 | url: '/pages/list',
7 | method: 'get',
8 | params,
9 | });
10 | }
11 |
12 | export async function createData(params: Omit): Promise {
13 | return request({
14 | url: '/pages/list',
15 | method: 'POST',
16 | data: params,
17 | });
18 | }
19 |
20 | export async function updateData(id: number, params: Omit): Promise {
21 | return request({
22 | url: `/pages/list/${id}`,
23 | method: 'PUT',
24 | data: params,
25 | });
26 | }
27 |
28 | export async function removeData(id: number): Promise {
29 | return request({
30 | url: `/pages/list/${id}`,
31 | method: 'delete',
32 | });
33 | }
34 |
35 | export async function detailData(id: number): Promise {
36 | return request({url: `/pages/list/${id}`});
37 | }
38 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/vue3-essential',
8 | 'eslint:recommended',
9 | '@vue/typescript/recommended'
10 | ],
11 | parserOptions: {
12 | ecmaVersion: 2020
13 | },
14 | rules: {
15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
17 | '@typescript-eslint/no-explicit-any': ['off'],
18 | '@typescript-eslint/no-unused-vars': ['off'],
19 | '@typescript-eslint/ban-types': ['off']
20 | },
21 | overrides: [
22 | {
23 | files: [
24 | '**/__tests__/*.{j,t}s?(x)',
25 | '**/tests/unit/**/*.spec.{j,t}s?(x)'
26 | ],
27 | env: {
28 | jest: true
29 | }
30 | },
31 | {
32 | files: [
33 | './mock/*.{j,t}s?(x)',
34 | './src/utils/mock/*.{j,t}s?(x)'
35 | ],
36 | rules: {
37 | 'no-var': ['off'],
38 | '@typescript-eslint/no-var-requires': ['off'],
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/detail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/pagesample/form/basic/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { createData } from './service';
4 | import { FormDataType } from "./data.d";
5 |
6 | /* eslint-disable @typescript-eslint/no-empty-interface */
7 | export interface StateType {}
8 |
9 | export interface ModuleType extends StoreModuleType {
10 | state: StateType;
11 | mutations: {
12 | };
13 | actions: {
14 | create: Action;
15 | };
16 | }
17 |
18 | const initState: StateType = {};
19 |
20 | const StoreModel: ModuleType = {
21 | namespaced: true,
22 | name: 'FormBasic',
23 | state: {
24 | ...initState
25 | },
26 | mutations: {
27 | },
28 | actions: {
29 | async create({ commit }, payload: FormDataType) {
30 | try {
31 | await createData(payload);
32 | return true;
33 | } catch (error) {
34 | return false;
35 | }
36 | }
37 | }
38 | }
39 |
40 | export default StoreModel;
--------------------------------------------------------------------------------
/src/views/pagesample/form/complex/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { createData } from './service';
4 | import { FormDataType } from "./data.d";
5 |
6 | /* eslint-disable @typescript-eslint/no-empty-interface */
7 | export interface StateType {}
8 |
9 | export interface ModuleType extends StoreModuleType {
10 | state: StateType;
11 | mutations: {
12 | };
13 | actions: {
14 | create: Action;
15 | };
16 | }
17 |
18 | const initState: StateType = {};
19 |
20 | const StoreModel: ModuleType = {
21 | namespaced: true,
22 | name: 'FormComplex',
23 | state: {
24 | ...initState
25 | },
26 | mutations: {
27 | },
28 | actions: {
29 | async create({ commit }, payload: FormDataType) {
30 | try {
31 | await createData(payload);
32 | return true;
33 | } catch (error) {
34 | return false;
35 | }
36 | }
37 | }
38 | }
39 |
40 | export default StoreModel;
--------------------------------------------------------------------------------
/src/directives/permission/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 自定义指令 权限验证
3 | * @author LiQingSong
4 | * 使用Demo:
5 | * import permission from '@/directives/permission';
6 | * import { defineComponent } from "vue";
7 | * export default defineComponent({
8 | * directives: {
9 | * permission
10 | * }
11 | * })
12 | * 删除
13 | * 删除
14 | */
15 | import { Directive } from "vue";
16 | import UserModel from "@/store/user";
17 | import { hasPermissionRouteRoles } from "@/utils/routes";
18 |
19 | const permission: Directive = (el, binding, vnode, prevVNode) => {
20 | const { value } = binding;
21 | if(value) {
22 | const userRoles = UserModel.state.currentUser.roles;
23 | if(!hasPermissionRouteRoles(userRoles, value)){
24 | el.parentNode && el.parentNode.removeChild(el);
25 | }
26 | } else {
27 | throw new Error(`need roles! Like v-permission="['admin','test']" or v-permission="'test'"`);
28 | }
29 | }
30 |
31 | export default permission;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 LiQingSong
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.
22 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/basic/components/TypeSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 请选择
4 | 头部
5 | 底部
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/table/components/TypeSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 请选择
4 | 头部
5 | 底部
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/IconSvg/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
34 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/search/table/components/TypeSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 请选择
4 | 头部
5 | 底部
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/pagesample/list/highly-adaptive-table/components/TypeSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 请选择
4 | 头部
5 | 底部
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/home/data.d.ts:
--------------------------------------------------------------------------------
1 | import { TableListItem as HotSearchTableListItem } from './components/HotSearchCard/data.d';
2 | import { TableListItem as HotTagsTableListItem } from './components/HotTagsCard/data.d';
3 | import { TableListItem as ArticleHitTableListItem } from './components/ArticleHitCard/data.d';
4 | import { TableListItem as WorksHitTableListItem } from './components/WorksHitCard/data.d';
5 |
6 | export interface TableListQueryParams {
7 | page: number;
8 | per: number;
9 | sort?: number;
10 | }
11 |
12 | export interface PaginationConfig {
13 | total: number;
14 | current: number;
15 | pageSize: number;
16 | showSizeChanger: boolean;
17 | }
18 |
19 | export interface HotSearchDataType {
20 | list: HotSearchTableListItem[];
21 | pagination: PaginationConfig;
22 | }
23 |
24 | export interface HotTagsDataType {
25 | list: HotTagsTableListItem[];
26 | pagination: PaginationConfig;
27 | }
28 |
29 | export interface ArticleHitDataType {
30 | list: ArticleHitTableListItem[];
31 | pagination: PaginationConfig;
32 | }
33 |
34 | export interface WorksHitDataType {
35 | list: WorksHitTableListItem[];
36 | pagination: PaginationConfig;
37 | }
38 |
--------------------------------------------------------------------------------
/src/config/i18n.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 国际化 入口
3 | * @author LiQingSong
4 | */
5 |
6 | import { createI18n } from "vue-i18n";
7 | import { getLocale, setLocale, importAllLocales, defaultLang } from "@/utils/i18n";
8 |
9 | /**
10 | * antd 多语言 配置
11 | */
12 | import zhCN from 'ant-design-vue/es/locale/zh_CN';
13 | import zhTW from 'ant-design-vue/es/locale/zh_TW';
14 | import enUS from 'ant-design-vue/es/locale/en_US';
15 | export const antdMessages: { [key: string]: any} = {
16 | 'zh-CN': zhCN,
17 | 'zh-TW': zhTW,
18 | 'en-US': enUS,
19 | }
20 |
21 |
22 | /**
23 | * 框架 多语言 配置
24 | */
25 | export const messages = importAllLocales();
26 | const sysLocale = getLocale();
27 | const i18n = createI18n({
28 | legacy: false,
29 | locale: antdMessages[sysLocale] ? sysLocale : defaultLang,
30 | messages,
31 | });
32 |
33 |
34 | /**
35 | * 设置语言
36 | * @param locale
37 | */
38 | export function setI18nLanguage(locale: string, realReload = false): void {
39 | setLocale(locale,realReload, function() {
40 | // i18n.global.locale = locale // legacy: true
41 | i18n.global.locale.value = locale;
42 | })
43 | }
44 |
45 | export default i18n;
46 |
--------------------------------------------------------------------------------
/src/utils/mock/require-context.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 自定义 require.context
3 | * @author LiQingSong
4 | */
5 | module.exports = function(directory, recursive, regExp) {
6 | const dir = require('node-dir')
7 | const path = require('path')
8 |
9 | // Assume absolute path by default
10 | let basepath = directory
11 |
12 | if (directory[0] === '.') {
13 | // Relative path
14 | basepath = path.join(__dirname, directory)
15 | } else if (!path.isAbsolute(directory)) {
16 | // Module path
17 | basepath = require.resolve(directory)
18 | }
19 |
20 | const keys = dir
21 | .files(basepath, {
22 | sync: true,
23 | recursive: recursive || false
24 | })
25 | .filter(function(file) {
26 | return file.match(regExp || /\.(json|js)$/)
27 | })
28 | .map(function(file) {
29 | return path.join('.', file.slice(basepath.length + 1))
30 | })
31 |
32 | var context = function(key) {
33 | return require(context.resolve(key))
34 | }
35 |
36 | context.resolve = function(key) {
37 | return path.join(directory, key)
38 | }
39 |
40 | context.keys = function() {
41 | return keys
42 | }
43 |
44 | return context
45 | }
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/RightTopMessage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/utils/object.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 浅比较两个object, json的key是否一致
3 | * @param obj1
4 | * @param obj2
5 | * @returns
6 | */
7 | export function equalObjectKey(obj1: Object, obj2: Object): boolean{
8 | const obj1Keys: string[] = Object.keys(obj1);
9 | const obj2Keys: string[] = Object.keys(obj2);
10 | const obj1KeysLen: number = obj1Keys.length;
11 | if(obj1KeysLen!==obj2Keys.length) {
12 | return false;
13 | }
14 | let is = true;
15 | for (let index = 0; index < obj1KeysLen; index++) {
16 | const element: string = obj1Keys[index];
17 | if(!Object.prototype.hasOwnProperty.call(obj2, element)) {
18 | is = false;
19 | break;
20 | }
21 | }
22 | return is;
23 | }
24 |
25 | /**
26 | * 浅比较两个对象是否相等,这两个对象的值只能是数字或字符串
27 | * @param obj1
28 | * @param obj2
29 | * @returns
30 | */
31 | export function equalObject(obj1: Object, obj2: Object): boolean {
32 | const obj1Keys: string[] = Object.keys(obj1);
33 | const obj2Keys: string[] = Object.keys(obj2);
34 | const obj1KeysLen: number = obj1Keys.length;
35 | const obj2KeysLen: number = obj2Keys.length;
36 | if(obj1KeysLen!==obj2KeysLen) {
37 | return false;
38 | }
39 |
40 | if(obj1KeysLen===0 && obj2KeysLen===0) {
41 | return true;
42 | }
43 |
44 | return !obj1Keys.some(key => obj1[key] != obj2[key])
45 |
46 | }
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/composables/useTopMenuWidth.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 设置 IndexLayout TopMenuWidth
3 | * @author LiQingSong
4 | */
5 | import { ComputedRef, onMounted, Ref, ref, watch, nextTick } from 'vue';
6 |
7 | export default function useTopMenuWidth(topNavEnable: ComputedRef | Ref): {
8 | topMenuCon: Ref;
9 | topMenuWidth: Ref;
10 | } {
11 |
12 | const topMenuCon = ref(null);
13 |
14 | const topMenuWidth = ref('auto');
15 |
16 | const setWidth = async () => {
17 | await nextTick();
18 | if (topMenuCon.value && topNavEnable.value) {
19 | let width = 0;
20 | const child = topMenuCon.value.children;
21 | for (let index = 0, len = child.length; index < len; index++) {
22 | const element = child[index] as HTMLElement;
23 | width = width + element.offsetWidth + 0.5;
24 | }
25 | topMenuWidth.value = width + 'px';
26 | } else {
27 | topMenuWidth.value = 'auto';
28 | }
29 | };
30 |
31 |
32 | watch(topNavEnable,(topNavEnable)=> {
33 | setWidth();
34 | })
35 |
36 | onMounted(()=> {
37 |
38 | setWidth();
39 | })
40 |
41 |
42 | return {
43 | topMenuCon,
44 | topMenuWidth
45 | }
46 |
47 |
48 |
49 | }
--------------------------------------------------------------------------------
/src/views/component/icon/font/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 | 说明:
12 | 组件位置: @/components/IconFont
13 | 创建原因:方便统一管理使用 iconfont.cn 上 js 资源图标
14 |
15 |
16 | 使用方法:
17 |
18 | 1、iconfont.cn 上生成 js 资源。
19 |
20 |
21 | 2、@/config/settings.ts 文件中配置 iconfont.cn 生成的js文件地址。
22 |
23 | 3、使用Demo:
24 | import IconFont from '@/components/IconFont';
25 |
26 | <IconFont type="svg文件名" class="" style=""/>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/language-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/edit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Permission/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Back Home
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/views/home/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.home.text-day': '日',
3 | 'page.home.text-week': '周',
4 | 'page.home.text-month': '月',
5 | 'page.home.text-years': '年',
6 | 'page.home.text-total': '总数量',
7 | 'page.home.text-daycompare': '日同比',
8 | 'page.home.text-weekcompare': '周同比',
9 | 'page.home.articlechartcard.card-title': '随笔',
10 | 'page.home.workschartcard.card-title': '作品',
11 | 'page.home.topicschartcard.card-title': '专题',
12 | 'page.home.linkschartcard.card-title': '左邻右舍',
13 |
14 | 'page.home.hotsearchcard.card-title': '热门搜索',
15 | 'page.home.hotsearchcard.card.table-column-number': '序号',
16 | 'page.home.hotsearchcard.card.table-column-name': '关键词',
17 | 'page.home.hotsearchcard.card.table-column-hit': '次数',
18 |
19 | 'page.home.hottagscard.card-title': '热门标签',
20 | 'page.home.hottagscard.card.table-column-number': '序号',
21 | 'page.home.hottagscard.card.table-column-name': '标签',
22 | 'page.home.hottagscard.card.table-column-hit': '次数',
23 |
24 | 'page.home.articlehitcard.card-title': '随笔浏览量排行',
25 | 'page.home.articlehitcard.card.table-column-number': '序号',
26 | 'page.home.articlehitcard.card.table-column-title': '标题',
27 | 'page.home.articlehitcard.card.table-column-hit': '浏览量',
28 |
29 | 'page.home.workshitcard.card-title': '作品浏览量排行',
30 | 'page.home.workshitcard.card.table-column-number': '序号',
31 | 'page.home.workshitcard.card.table-column-title': '标题',
32 | 'page.home.workshitcard.card.table-column-hit': '浏览量',
33 | };
34 |
--------------------------------------------------------------------------------
/src/views/home/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.home.text-day': '日',
3 | 'page.home.text-week': '周',
4 | 'page.home.text-month': '月',
5 | 'page.home.text-years': '年',
6 | 'page.home.text-total': '總數量',
7 | 'page.home.text-daycompare': '日同比',
8 | 'page.home.text-weekcompare': '周同比',
9 | 'page.home.articlechartcard.card-title': '隨筆',
10 | 'page.home.workschartcard.card-title': '作品',
11 | 'page.home.topicschartcard.card-title': '專題',
12 | 'page.home.linkschartcard.card-title': '左鄰右舍',
13 |
14 | 'page.home.hotsearchcard.card-title': '熱門搜索',
15 | 'page.home.hotsearchcard.card.table-column-number': '序號',
16 | 'page.home.hotsearchcard.card.table-column-name': '關鍵詞',
17 | 'page.home.hotsearchcard.card.table-column-hit': '次數',
18 |
19 | 'page.home.hottagscard.card-title': '熱門標簽',
20 | 'page.home.hottagscard.card.table-column-number': '序號',
21 | 'page.home.hottagscard.card.table-column-name': '標簽',
22 | 'page.home.hottagscard.card.table-column-hit': '次數',
23 |
24 | 'page.home.articlehitcard.card-title': '隨筆瀏覽量排行',
25 | 'page.home.articlehitcard.card.table-column-number': '序號',
26 | 'page.home.articlehitcard.card.table-column-title': '標題',
27 | 'page.home.articlehitcard.card.table-column-hit': '瀏覽量',
28 |
29 | 'page.home.workshitcard.card-title': '作品瀏覽量排行',
30 | 'page.home.workshitcard.card.table-column-number': '序號',
31 | 'page.home.workshitcard.card.table-column-title': '標題',
32 | 'page.home.workshitcard.card.table-column-hit': '瀏覽量',
33 | };
34 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/page.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/theme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/RightFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/src/assets/iconsvg/permissions.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/TuiEditor/viewer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/views/home/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'page.home.text-day': 'Day',
3 | 'page.home.text-week': 'Weeks',
4 | 'page.home.text-month': 'Month',
5 | 'page.home.text-years': 'Years',
6 | 'page.home.text-total': 'Total',
7 | 'page.home.text-daycompare': 'Day Year-on-year',
8 | 'page.home.text-weekcompare': 'Week Year-on-year',
9 | 'page.home.articlechartcard.card-title': 'Essays',
10 | 'page.home.workschartcard.card-title': 'Works',
11 | 'page.home.topicschartcard.card-title': 'Topics',
12 | 'page.home.linkschartcard.card-title': 'Neighbors',
13 |
14 | 'page.home.hotsearchcard.card-title': 'Top search',
15 | 'page.home.hotsearchcard.card.table-column-number': 'Serial number',
16 | 'page.home.hotsearchcard.card.table-column-name': 'Keywords',
17 | 'page.home.hotsearchcard.card.table-column-hit': 'Hits',
18 |
19 | 'page.home.hottagscard.card-title': 'Hot labels',
20 | 'page.home.hottagscard.card.table-column-number': 'Serial number',
21 | 'page.home.hottagscard.card.table-column-name': 'label',
22 | 'page.home.hottagscard.card.table-column-hit': 'Hits',
23 |
24 | 'page.home.articlehitcard.card-title': 'Essay Traffic Ranking',
25 | 'page.home.articlehitcard.card.table-column-number': 'Serial number',
26 | 'page.home.articlehitcard.card.table-column-title': 'Title',
27 | 'page.home.articlehitcard.card.table-column-hit': 'Hits',
28 |
29 | 'page.home.workshitcard.card-title': 'Works Traffic Ranking',
30 | 'page.home.workshitcard.card.table-column-number': 'Serial number',
31 | 'page.home.workshitcard.card.table-column-title': 'Title',
32 | 'page.home.workshitcard.card.table-column-hit': 'Hits',
33 | };
34 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/LeftSider.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | AdminAntdVue
8 |
9 |
10 |
18 |
19 |
20 |
21 |
48 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
37 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'index-layout.topmenu.userinfo': '个人信息',
3 | 'index-layout.topmenu.logout': '退出',
4 |
5 | 'index-layout.menu.home': '首页',
6 | 'index-layout.menu.home.workplace': '工作台',
7 | 'index-layout.menu.home.custom-breadcrumbs': '自定义面包屑',
8 | 'index-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'index-layout.menu.home.docs': '使用文档',
10 |
11 | 'index-layout.menu.component': '组件',
12 | 'index-layout.menu.component.icon': '图标',
13 | 'index-layout.menu.component.icon.svg': 'IconSvg',
14 | 'index-layout.menu.component.icon.font': 'IconFont',
15 | 'index-layout.menu.component.editor': '编辑器',
16 | 'index-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'index-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'index-layout.menu.pages': '页面示例',
20 | 'index-layout.menu.pages.list': '列表页面',
21 | 'index-layout.menu.pages.list.basic': '标准列表',
22 | 'index-layout.menu.pages.list.table': '表格列表',
23 | 'index-layout.menu.pages.list.highly-adaptive-table': '高度自适应表格',
24 | 'index-layout.menu.pages.list.search': '搜索列表',
25 | 'index-layout.menu.pages.list.search.table': '查询表格',
26 | 'index-layout.menu.pages.form': '表单页面',
27 | 'index-layout.menu.pages.form.basic': '基础表单',
28 | 'index-layout.menu.pages.form.complex': '高级表单',
29 | 'index-layout.menu.pages.detail': '详情页面',
30 | 'index-layout.menu.pages.detail.basic': '基础详情',
31 | 'index-layout.menu.pages.detail.module': '模块详情',
32 | 'index-layout.menu.pages.detail.table': '表格详情',
33 |
34 | 'index-layout.menu.roles': '权限验证',
35 | 'index-layout.menu.roles.all': '用户都有权限',
36 | 'index-layout.menu.roles.user': 'Users有权限',
37 | 'index-layout.menu.roles.test': 'Tests有权限',
38 | };
39 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'index-layout.topmenu.userinfo': '個人信息',
3 | 'index-layout.topmenu.logout': '退出',
4 |
5 | 'index-layout.menu.home': '首頁',
6 | 'index-layout.menu.home.workplace': '工作臺',
7 | 'index-layout.menu.home.custom-breadcrumbs': '自定義面包屑',
8 | 'index-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'index-layout.menu.home.docs': '使用文檔',
10 |
11 | 'index-layout.menu.component': '組件',
12 | 'index-layout.menu.component.icon': '圖標',
13 | 'index-layout.menu.component.icon.svg': 'IconSvg',
14 | 'index-layout.menu.component.icon.font': 'IconFont',
15 | 'index-layout.menu.component.editor': '編輯器',
16 | 'index-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'index-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'index-layout.menu.pages': '頁面示例',
20 | 'index-layout.menu.pages.list': '列表頁面',
21 | 'index-layout.menu.pages.list.basic': '標淮列表',
22 | 'index-layout.menu.pages.list.table': '表格列表',
23 | 'index-layout.menu.pages.list.highly-adaptive-table': '高度自適應表格',
24 | 'index-layout.menu.pages.list.search': '搜索列表',
25 | 'index-layout.menu.pages.list.search.table': '查詢表格',
26 | 'index-layout.menu.pages.form': '表單頁面',
27 | 'index-layout.menu.pages.form.basic': '基礎表單',
28 | 'index-layout.menu.pages.form.complex': '高級表單',
29 | 'index-layout.menu.pages.detail': '詳情頁面',
30 | 'index-layout.menu.pages.detail.basic': '基礎詳情',
31 | 'index-layout.menu.pages.detail.module': '模塊詳情',
32 | 'index-layout.menu.pages.detail.table': '表格詳情',
33 |
34 | 'index-layout.menu.roles': '權限驗證',
35 | 'index-layout.menu.roles.all': '用戶都有權限',
36 | 'index-layout.menu.roles.user': 'Users有權限',
37 | 'index-layout.menu.roles.test': 'Tests有權限',
38 | };
39 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/SiderMenuItem.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{t(routeItem.title)}}
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {{t(routeItem.title)}}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/composables/useEcharts.ts:
--------------------------------------------------------------------------------
1 | import { onMounted, onBeforeUnmount, Ref, ShallowRef, shallowRef } from 'vue';
2 | import debounce from 'lodash.debounce';
3 | import * as echarts from 'echarts';
4 |
5 | export type EChartsOption = echarts.EChartsOption;
6 |
7 | /**
8 | * Echarts composables
9 | * @param labRef Ref HTMLDivElement ref
10 | * @param initOption EChartOption echarts option init
11 | * @param cb Function|undefined 回调函数 读取数据
12 | * @param theme string|undefined 使用的主题
13 | * @returns
14 | * @author LiQingSong
15 | */
16 | export default function useEcharts(
17 | labRef: Ref,
18 | initOption: EChartsOption,
19 | cb?: (ec:echarts.ECharts) => any,
20 | theme = ''
21 | ):{
22 | echart: ShallowRef;
23 | cb: () => void;
24 | } {
25 |
26 |
27 | const chart = shallowRef();
28 |
29 | const resizeHandler = debounce(() => {
30 | chart.value?.resize();
31 | }, 100);
32 |
33 | const callback = () => {
34 | if(typeof cb === 'function' && chart.value) {
35 | cb(chart.value)
36 | }
37 | }
38 |
39 | onMounted(()=> {
40 | if(labRef.value) {
41 | chart.value = echarts.init(labRef.value, theme);
42 | chart.value.setOption(initOption);
43 | callback()
44 | }
45 |
46 | window.addEventListener('resize', resizeHandler);
47 | })
48 |
49 | onBeforeUnmount(()=> {
50 | chart.value?.dispose();
51 | window.removeEventListener('resize', resizeHandler);
52 | });
53 |
54 | return {
55 | echart: chart,
56 | cb: callback
57 | };
58 | }
--------------------------------------------------------------------------------
/src/assets/iconsvg/set.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/basic/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { queryDetail } from './service';
4 | import { DetailDataType } from './data.d';
5 | import { ResponseData } from '@/utils/request';
6 |
7 | export interface StateType {
8 | detail: DetailDataType;
9 | }
10 |
11 | export interface ModuleType extends StoreModuleType {
12 | state: StateType;
13 | mutations: {
14 | setDetail: Mutation;
15 | };
16 | actions: {
17 | queryDetail: Action;
18 | };
19 | }
20 |
21 | const initState: StateType = {
22 | detail: {
23 | userInfo: {
24 | name: '',
25 | tel: '',
26 | courier: '',
27 | address: '',
28 | remark: '',
29 | },
30 | refundApplication: {
31 | ladingNo: '',
32 | saleNo: '',
33 | state: '',
34 | childOrders: '',
35 | },
36 | returnGoods: [],
37 | returnProgress: [],
38 | },
39 | };
40 |
41 | const StoreModel: ModuleType = {
42 | namespaced: true,
43 | name: 'DetailBasic',
44 | state: {
45 | ...initState
46 | },
47 | mutations: {
48 | setDetail(state, payload) {
49 | state.detail = payload;
50 | },
51 | },
52 | actions: {
53 | async queryDetail({ commit }) {
54 | try {
55 | const response: ResponseData = await queryDetail();
56 | const { data } = response;
57 | commit('setDetail',{
58 | ...initState.detail,
59 | ...data
60 | });
61 | return true;
62 | } catch (error) {
63 | return false;
64 | }
65 | }
66 | }
67 | };
68 |
69 | export default StoreModel;
70 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/table/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { queryDetail } from './service';
4 | import { DetailDataType } from './data.d';
5 | import { ResponseData } from '@/utils/request';
6 |
7 | export interface StateType {
8 | detail: DetailDataType;
9 | }
10 |
11 | export interface ModuleType extends StoreModuleType {
12 | state: StateType;
13 | mutations: {
14 | setDetail: Mutation;
15 | };
16 | actions: {
17 | queryDetail: Action;
18 | };
19 | }
20 |
21 | const initState: StateType = {
22 | detail: {
23 | userInfo: {
24 | name: '',
25 | tel: '',
26 | courier: '',
27 | address: '',
28 | remark: '',
29 | },
30 | refundApplication: {
31 | ladingNo: '',
32 | saleNo: '',
33 | state: '',
34 | childOrders: '',
35 | },
36 | returnGoods: [],
37 | returnProgress: [],
38 | },
39 | };
40 |
41 | const StoreModel: ModuleType = {
42 | namespaced: true,
43 | name: 'DetailTable',
44 | state: {
45 | ...initState
46 | },
47 | mutations: {
48 | setDetail(state, payload) {
49 | state.detail = payload;
50 | },
51 | },
52 | actions: {
53 | async queryDetail({ commit }) {
54 | try {
55 | const response: ResponseData = await queryDetail();
56 | const { data } = response;
57 | commit('setDetail',{
58 | ...initState.detail,
59 | ...data
60 | });
61 | return true;
62 | } catch (error) {
63 | return false;
64 | }
65 | }
66 | }
67 | };
68 |
69 | export default StoreModel;
70 |
--------------------------------------------------------------------------------
/src/views/pagesample/detail/module/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { queryDetail } from './service';
4 | import { DetailDataType } from './data.d';
5 | import { ResponseData } from '@/utils/request';
6 |
7 | export interface StateType {
8 | detail: DetailDataType;
9 | }
10 |
11 | export interface ModuleType extends StoreModuleType {
12 | state: StateType;
13 | mutations: {
14 | setDetail: Mutation;
15 | };
16 | actions: {
17 | queryDetail: Action;
18 | };
19 | }
20 |
21 | const initState: StateType = {
22 | detail: {
23 | userInfo: {
24 | name: '',
25 | tel: '',
26 | courier: '',
27 | address: '',
28 | remark: '',
29 | },
30 | refundApplication: {
31 | ladingNo: '',
32 | saleNo: '',
33 | state: '',
34 | childOrders: '',
35 | },
36 | returnGoods: [],
37 | returnProgress: [],
38 | },
39 | };
40 |
41 | const StoreModel: ModuleType = {
42 | namespaced: true,
43 | name: 'DetailModule',
44 | state: {
45 | ...initState
46 | },
47 | mutations: {
48 | setDetail(state, payload) {
49 | state.detail = payload;
50 | },
51 | },
52 | actions: {
53 | async queryDetail({ commit }) {
54 | try {
55 | const response: ResponseData = await queryDetail();
56 | const { data } = response;
57 | commit('setDetail',{
58 | ...initState.detail,
59 | ...data
60 | });
61 | return true;
62 | } catch (error) {
63 | return false;
64 | }
65 | }
66 | }
67 | };
68 |
69 | export default StoreModel;
70 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/RightTopUser.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 | {{t('universal-layout.topmenu.userinfo')}}
11 |
12 |
13 | {{t('universal-layout.topmenu.logout')}}
14 |
15 |
16 |
17 |
18 |
19 |
54 |
--------------------------------------------------------------------------------
/src/views/user/register/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { RegisterParamsType } from "./data.d";
4 | import { accountReg } from "./service";
5 |
6 | export interface StateType {
7 | errorMsg?: string;
8 | }
9 |
10 | export interface ModuleType extends StoreModuleType {
11 | state: StateType;
12 | mutations: {
13 | changeErrorMsg: Mutation;
14 | };
15 | actions: {
16 | register: Action;
17 | };
18 | }
19 |
20 | const initState: StateType = {
21 | errorMsg: undefined,
22 | };
23 |
24 | const StoreModel: ModuleType = {
25 | namespaced: true,
26 | name: 'userregister',
27 | state: {
28 | ...initState
29 | },
30 | mutations: {
31 | changeErrorMsg(state, payload) {
32 | state.errorMsg = payload;
33 | },
34 | },
35 | actions: {
36 | async register({ commit }, payload: RegisterParamsType) {
37 | let msg: string | undefined = undefined;
38 | try {
39 | await accountReg(payload);
40 | msg = '';
41 | } catch (error) {
42 | if (error.message && error.message === 'CustomError') {
43 | const { response } = error;
44 | const { data } = response;
45 | msg = data.msg || 'error';
46 | }
47 | }
48 |
49 | commit('changeErrorMsg',msg);
50 |
51 | if (msg === '') {
52 | return true; // 成功
53 | } else if (typeof msg === 'undefined') {
54 | return undefined; // 服务器错误
55 | } else {
56 | return false; // 自定义错误
57 | }
58 | }
59 | }
60 | };
61 |
62 | export default StoreModel;
63 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'universal-layout.topmenu.userinfo': '个人信息',
3 | 'universal-layout.topmenu.logout': '退出',
4 |
5 | 'universal-layout.menu.home': '首页',
6 | 'universal-layout.menu.home.workplace': '工作台',
7 | 'universal-layout.menu.home.custom-breadcrumbs': '自定义面包屑',
8 | 'universal-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'universal-layout.menu.home.docs': '使用文档',
10 |
11 | 'universal-layout.menu.component': '组件',
12 | 'universal-layout.menu.component.icon': '图标',
13 | 'universal-layout.menu.component.icon.svg': 'IconSvg',
14 | 'universal-layout.menu.component.icon.font': 'IconFont',
15 | 'universal-layout.menu.component.editor': '编辑器',
16 | 'universal-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'universal-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'universal-layout.menu.pages': '页面示例',
20 | 'universal-layout.menu.pages.list': '列表页面',
21 | 'universal-layout.menu.pages.list.basic': '标准列表',
22 | 'universal-layout.menu.pages.list.table': '表格列表',
23 | 'universal-layout.menu.pages.list.highly-adaptive-table': '高度自适应表格',
24 | 'universal-layout.menu.pages.list.search': '搜索列表',
25 | 'universal-layout.menu.pages.list.search.table': '查询表格',
26 | 'universal-layout.menu.pages.form': '表单页面',
27 | 'universal-layout.menu.pages.form.basic': '基础表单',
28 | 'universal-layout.menu.pages.form.complex': '高级表单',
29 | 'universal-layout.menu.pages.detail': '详情页面',
30 | 'universal-layout.menu.pages.detail.basic': '基础详情',
31 | 'universal-layout.menu.pages.detail.module': '模块详情',
32 | 'universal-layout.menu.pages.detail.table': '表格详情',
33 |
34 | 'universal-layout.menu.roles': '权限验证',
35 | 'universal-layout.menu.roles.all': '用户都有权限',
36 | 'universal-layout.menu.roles.user': 'Users有权限',
37 | 'universal-layout.menu.roles.test': 'Tests有权限',
38 | };
39 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/locales/zh-TW.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'universal-layout.topmenu.userinfo': '個人信息',
3 | 'universal-layout.topmenu.logout': '退出',
4 |
5 | 'universal-layout.menu.home': '首頁',
6 | 'universal-layout.menu.home.workplace': '工作臺',
7 | 'universal-layout.menu.home.custom-breadcrumbs': '自定義面包屑',
8 | 'universal-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'universal-layout.menu.home.docs': '使用文檔',
10 |
11 | 'universal-layout.menu.component': '組件',
12 | 'universal-layout.menu.component.icon': '圖標',
13 | 'universal-layout.menu.component.icon.svg': 'IconSvg',
14 | 'universal-layout.menu.component.icon.font': 'IconFont',
15 | 'universal-layout.menu.component.editor': '編輯器',
16 | 'universal-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'universal-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'universal-layout.menu.pages': '頁面示例',
20 | 'universal-layout.menu.pages.list': '列表頁面',
21 | 'universal-layout.menu.pages.list.basic': '標淮列表',
22 | 'universal-layout.menu.pages.list.table': '表格列表',
23 | 'universal-layout.menu.pages.list.highly-adaptive-table': '高度自適應表格',
24 | 'universal-layout.menu.pages.list.search': '搜索列表',
25 | 'universal-layout.menu.pages.list.search.table': '查詢表格',
26 | 'universal-layout.menu.pages.form': '表單頁面',
27 | 'universal-layout.menu.pages.form.basic': '基礎表單',
28 | 'universal-layout.menu.pages.form.complex': '高級表單',
29 | 'universal-layout.menu.pages.detail': '詳情頁面',
30 | 'universal-layout.menu.pages.detail.basic': '基礎詳情',
31 | 'universal-layout.menu.pages.detail.module': '模塊詳情',
32 | 'universal-layout.menu.pages.detail.table': '表格詳情',
33 |
34 | 'universal-layout.menu.roles': '權限驗證',
35 | 'universal-layout.menu.roles.all': '用戶都有權限',
36 | 'universal-layout.menu.roles.user': 'Users有權限',
37 | 'universal-layout.menu.roles.test': 'Tests有權限',
38 | };
39 |
--------------------------------------------------------------------------------
/src/views/user/login/store.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { ResponseData } from '@/utils/request';
4 | import { setToken } from '@/utils/localToken';
5 | import { accountLogin } from './service';
6 | import { LoginParamsType } from "./data.d";
7 |
8 | export interface StateType {
9 | loginStatus?: 'ok' | 'error';
10 | }
11 |
12 | export interface ModuleType extends StoreModuleType {
13 | state: StateType;
14 | mutations: {
15 | changeLoginStatus: Mutation;
16 | };
17 | actions: {
18 | login: Action;
19 | };
20 | }
21 |
22 | const initState: StateType = {
23 | loginStatus: undefined,
24 | }
25 |
26 | const StoreModel: ModuleType = {
27 | namespaced: true,
28 | name: 'userlogin',
29 | state: {
30 | ...initState
31 | },
32 | mutations: {
33 | changeLoginStatus(state, payload) {
34 | state.loginStatus = payload;
35 | },
36 | },
37 | actions: {
38 | async login({ commit }, payload: LoginParamsType) {
39 | let status: string | undefined = undefined;
40 | try {
41 | const response: ResponseData = await accountLogin(payload);
42 | const { data } = response;
43 | setToken(data.token || '');
44 | status = 'ok';
45 | } catch (error) {
46 | if (error.message && error.message === 'CustomError') {
47 | status = 'error';
48 | }
49 | }
50 |
51 | commit('changeLoginStatus',status);
52 |
53 | if (status === 'ok') {
54 | return true;
55 | } else if (status === 'error') {
56 | return false;
57 | }
58 | return undefined;
59 | }
60 | }
61 | }
62 |
63 | export default StoreModel;
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'index-layout.topmenu.userinfo': 'Personal Info',
3 | 'index-layout.topmenu.logout': 'Logout',
4 |
5 | 'index-layout.menu.home': 'Home',
6 | 'index-layout.menu.home.workplace': 'Workplace',
7 | 'index-layout.menu.home.custom-breadcrumbs': 'Custom bread crumbs',
8 | 'index-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'index-layout.menu.home.docs': 'Using document',
10 |
11 | 'index-layout.menu.component': 'Component',
12 | 'index-layout.menu.component.icon': 'Icon',
13 | 'index-layout.menu.component.icon.svg': 'IconSvg',
14 | 'index-layout.menu.component.icon.font': 'IconFont',
15 | 'index-layout.menu.component.editor': 'Editor',
16 | 'index-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'index-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'index-layout.menu.pages': 'Page sample',
20 | 'index-layout.menu.pages.list': 'List page',
21 | 'index-layout.menu.pages.list.basic': 'Basis list',
22 | 'index-layout.menu.pages.list.table': 'Table list',
23 | 'index-layout.menu.pages.list.highly-adaptive-table': 'Highly adaptive table',
24 | 'index-layout.menu.pages.list.search': 'Search list',
25 | 'index-layout.menu.pages.list.search.table': 'Query list',
26 | 'index-layout.menu.pages.form': 'Form page',
27 | 'index-layout.menu.pages.form.basic': 'Basis Form',
28 | 'index-layout.menu.pages.form.complex': 'Advanced Form',
29 | 'index-layout.menu.pages.detail': 'Details page',
30 | 'index-layout.menu.pages.detail.basic': 'Basic details',
31 | 'index-layout.menu.pages.detail.module': 'Module details',
32 | 'index-layout.menu.pages.detail.table': 'Table details',
33 |
34 | 'index-layout.menu.roles': 'Permission to verify',
35 | 'index-layout.menu.roles.all': 'All users have permissions',
36 | 'index-layout.menu.roles.user': 'Users have permission',
37 | 'index-layout.menu.roles.test': 'Tests have permission',
38 | };
39 |
--------------------------------------------------------------------------------
/src/utils/store.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Store utils
3 | * @author LiQingSong
4 | */
5 | import { Module, ModuleTree } from 'vuex';
6 |
7 | /**
8 | * 自定义项目 Store Module 类型
9 | * 为自动导入的 store 做类型限制
10 | * [@/store文件夹下定义的 store]与[@/views文件夹下定义的`文件store.ts`] 必须继承此类型
11 | * @author LiQingSong
12 | */
13 | export interface StoreModuleType extends Module {
14 | namespaced: true;
15 | name: string;
16 | }
17 |
18 | /**
19 | * 自动导入 Store
20 | * @author LiQingSong
21 | */
22 | export function importAllStore (): ModuleTree {
23 | const modules: ModuleTree = {};
24 | try {
25 | // 导入 @/views 下文件,包含子目录,文件名为:store.ts
26 | const viewsRequireContext: __WebpackModuleApi.RequireContext = require.context('../views', true, /[/\\]store\.ts$/);
27 | viewsRequireContext.keys().forEach(fileName => {
28 | // 获取内容
29 | const modulesConent = viewsRequireContext(fileName);
30 | if(modulesConent.default) {
31 | const { name, ...module } = modulesConent.default;
32 | // 获取 PascalCase 命名
33 | const modulesName = name || fileName.replace(/^\.\/(.*)\.\w+$/, "$1");
34 |
35 | modules[modulesName] = { ...module };
36 | }
37 |
38 | });
39 |
40 | // 导入 @/store 下文件
41 | const requireContext: __WebpackModuleApi.RequireContext = require.context('../store', false, /\.ts$/);
42 | requireContext.keys().forEach(fileName => {
43 | // 获取内容
44 | const modulesConent = requireContext(fileName);
45 | if(modulesConent.default) {
46 | const { name, ...module } = modulesConent.default;
47 | // 获取 PascalCase 命名
48 | const modulesName = name || fileName.replace(/^\.\/(.*)\.\w+$/, "$1");
49 |
50 | modules[modulesName] = { ...module };
51 | }
52 |
53 | });
54 | } catch (error) {
55 | // eslint-disable-next-line no-console
56 | console.log(error);
57 | }
58 |
59 | return modules;
60 | }
61 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'universal-layout.topmenu.userinfo': 'Personal Info',
3 | 'universal-layout.topmenu.logout': 'Logout',
4 |
5 | 'universal-layout.menu.home': 'Home',
6 | 'universal-layout.menu.home.workplace': 'Workplace',
7 | 'universal-layout.menu.home.custom-breadcrumbs': 'Custom bread crumbs',
8 | 'universal-layout.menu.home.custom-breadcrumbs.liqingsong.cc': 'liqingsong.cc',
9 | 'universal-layout.menu.home.docs': 'Using document',
10 |
11 | 'universal-layout.menu.component': 'Component',
12 | 'universal-layout.menu.component.icon': 'Icon',
13 | 'universal-layout.menu.component.icon.svg': 'IconSvg',
14 | 'universal-layout.menu.component.icon.font': 'IconFont',
15 | 'universal-layout.menu.component.editor': 'Editor',
16 | 'universal-layout.menu.component.editor.tui-editor': 'tui-editor',
17 | 'universal-layout.menu.component.editor.ckeditor': 'CKEditor',
18 |
19 | 'universal-layout.menu.pages': 'Page sample',
20 | 'universal-layout.menu.pages.list': 'List page',
21 | 'universal-layout.menu.pages.list.basic': 'Basis list',
22 | 'universal-layout.menu.pages.list.table': 'Table list',
23 | 'universal-layout.menu.pages.list.highly-adaptive-table': 'Highly adaptive table',
24 | 'universal-layout.menu.pages.list.search': 'Search list',
25 | 'universal-layout.menu.pages.list.search.table': 'Query list',
26 | 'universal-layout.menu.pages.form': 'Form page',
27 | 'universal-layout.menu.pages.form.basic': 'Basis Form',
28 | 'universal-layout.menu.pages.form.complex': 'Advanced Form',
29 | 'universal-layout.menu.pages.detail': 'Details page',
30 | 'universal-layout.menu.pages.detail.basic': 'Basic details',
31 | 'universal-layout.menu.pages.detail.module': 'Module details',
32 | 'universal-layout.menu.pages.detail.table': 'Table details',
33 |
34 | 'universal-layout.menu.roles': 'Permission to verify',
35 | 'universal-layout.menu.roles.all': 'All users have permissions',
36 | 'universal-layout.menu.roles.user': 'Users have permission',
37 | 'universal-layout.menu.roles.test': 'Tests have permission',
38 | };
39 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/Left.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | AdminAntdVue
7 |
8 |
9 |
21 |
22 |
23 |
69 |
--------------------------------------------------------------------------------
/src/layouts/SecurityLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
61 |
64 |
--------------------------------------------------------------------------------
/src/components/IconFont/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
61 |
--------------------------------------------------------------------------------
/src/layouts/UniversalLayout/components/SiderMenu.vue:
--------------------------------------------------------------------------------
1 |
53 |
54 |
71 |
72 |
--------------------------------------------------------------------------------
/src/config/settings.ts:
--------------------------------------------------------------------------------
1 | import { RoutesDataItem } from "@/utils/routes";
2 |
3 | export type Theme = 'dark' | 'light';
4 |
5 | export type NavMode = 'inline' | 'horizontal';
6 |
7 | /**
8 | * 站点配置
9 | * @author LiQingSong
10 | */
11 | export interface SettingsType {
12 | /**
13 | * 站点名称
14 | */
15 | siteTitle: string;
16 |
17 | /**
18 | * 站点首页路由
19 | */
20 | homeRouteItem: RoutesDataItem;
21 |
22 | /**
23 | * 站点本地存储Token 的 Key值
24 | */
25 | siteTokenKey: string;
26 |
27 | /**
28 | * Ajax请求头发送Token 的 Key值
29 | */
30 | ajaxHeadersTokenKey: string;
31 |
32 | /**
33 | * Ajax返回值不参加统一验证的api地址
34 | */
35 | ajaxResponseNoVerifyUrl: string[];
36 |
37 | /**
38 | * iconfont.cn 项目在线生成的 js 地址
39 | */
40 | iconfontUrl: string[];
41 |
42 | /**
43 | * Layout 头部固定开启
44 | */
45 | headFixed: boolean;
46 |
47 | /**
48 | * Layout tab菜单开启
49 | */
50 | tabNavEnable: boolean;
51 |
52 | /**
53 | * IndexLayout 顶部菜单开启
54 | */
55 | topNavEnable: boolean;
56 |
57 | /**
58 | * UniversalLayout 模板主题
59 | */
60 | theme: Theme;
61 |
62 | /**
63 | * UniversalLayout 导航模式
64 | */
65 | navMode: NavMode;
66 |
67 | /**
68 | * UniversalLayout 左侧侧边固定开启
69 | */
70 | leftSiderFixed: boolean;
71 | }
72 |
73 | const settings: SettingsType = {
74 | siteTitle: 'ADMIN-ANTD-VUE',
75 | homeRouteItem: {
76 | icon: 'control',
77 | title: 'index-layout.menu.home.workplace',
78 | path: '/home/workplace',
79 | component: ()=> import('@/views/home/index.vue')
80 | },
81 | siteTokenKey: 'admin_antd_vue_token',
82 | ajaxHeadersTokenKey: 'x-token',
83 | ajaxResponseNoVerifyUrl: [
84 | '/user/login', // 用户登录
85 | '/user/info', // 获取用户信息
86 | ],
87 | iconfontUrl: [],
88 |
89 | /* 以下是针对所有 Layout 扩展字段 */
90 | headFixed: true,
91 | tabNavEnable: true,
92 |
93 | /* 以下是针对 IndexLayout 扩展字段 */
94 | topNavEnable: true,
95 |
96 | /* 以下是针对 UniversalLayout 扩展字段 */
97 | theme: 'dark',
98 | navMode: 'inline',
99 | leftSiderFixed: true,
100 | };
101 |
102 | export default settings;
103 |
--------------------------------------------------------------------------------
/src/config/routes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 路由入口
3 | * @author LiQingSong
4 | */
5 | import NProgress from 'nprogress'; // progress bar
6 | import 'nprogress/nprogress.css'; // progress bar style
7 | NProgress.configure({ showSpinner: false, easing: 'ease', speed: 1000 }); // NProgress Configuration
8 |
9 | import { createRouter, createWebHashHistory } from 'vue-router';
10 | import { RoutesDataItem } from "@/utils/routes";
11 | import settings from "@/config/settings";
12 |
13 | import SecurityLayout from '@/layouts/SecurityLayout.vue';
14 |
15 | /* UniversalLayout 通用布局,可以与 IndexLayout 互相代替 */
16 | // import UniversalLayoutRoutes from '@/layouts/UniversalLayout/routes';
17 | // import UniversalLayout from '@/layouts/UniversalLayout/index.vue';
18 |
19 | /* IndexLayout 自定义布局,可以与 UniversalLayout 互相代替 */
20 | import IndexLayoutRoutes from '@/layouts/IndexLayout/routes';
21 | import IndexLayout from '@/layouts/IndexLayout/index.vue';
22 |
23 | import UserLayoutRoutes from '@/layouts/UserLayout/routes';
24 | import UserLayout from '@/layouts/UserLayout/index.vue';
25 |
26 | const routes: RoutesDataItem[] = [
27 | {
28 | title: 'empty',
29 | path: '/',
30 | component: SecurityLayout,
31 | children: [
32 | {
33 | title: 'empty',
34 | path: '/',
35 | redirect: settings.homeRouteItem.path,
36 | component: IndexLayout,
37 | children: IndexLayoutRoutes
38 | },
39 | {
40 | title: 'empty',
41 | path: '/refresh',
42 | component: () => import('@/views/refresh/index.vue'),
43 | },
44 | ]
45 | },
46 | {
47 | title: 'empty',
48 | path: '/user',
49 | redirect: '/user/login',
50 | component: UserLayout,
51 | children: UserLayoutRoutes
52 | },
53 | {
54 | title: 'app.global.menu.notfound',
55 | path: '/:pathMatch(.*)*',
56 | component: () => import('@/views/404/index.vue'),
57 | }
58 | ]
59 |
60 | const router = createRouter({
61 | scrollBehavior(/* to, from, savedPosition */) {
62 | return { top: 0 }
63 | },
64 | history: createWebHashHistory(process.env.BASE_URL),
65 | routes: routes,
66 | });
67 |
68 | router.beforeEach((/* to, from */) => {
69 | // start progress bar
70 | NProgress.start();
71 | });
72 |
73 | router.afterEach(() => {
74 | // finish progress bar
75 | NProgress.done();
76 | });
77 |
78 | export default router;
79 |
--------------------------------------------------------------------------------
/src/components/SelectLang/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
65 |
--------------------------------------------------------------------------------
/mock/user.js:
--------------------------------------------------------------------------------
1 | const mockjs= require('mockjs');
2 | const { VUE_APP_APIHOST } = process.env;
3 | const ajaxHeadersTokenKey = 'x-token';
4 | const mock = {};
5 |
6 | mock[`GET ${VUE_APP_APIHOST}/user/info`] = (req, res) => {
7 | const headers = req.headers;
8 | if (headers[ajaxHeadersTokenKey] === 'admin') {
9 | res.send({
10 | code: 0,
11 | data: {
12 | id: 1,
13 | name: 'Admins',
14 | avatar: '',
15 | roles: ['admin'],
16 | },
17 | });
18 | } else if (headers[ajaxHeadersTokenKey] === 'user') {
19 | res.send({
20 | code: 0,
21 | data: {
22 | id: 2,
23 | name: 'Users',
24 | avatar: '',
25 | roles: ['user'],
26 | },
27 | });
28 | } else if (headers[ajaxHeadersTokenKey] === 'test') {
29 | res.send({
30 | code: 0,
31 | data: {
32 | id: 3,
33 | name: 'Tests',
34 | avatar: '',
35 | roles: ['test'],
36 | },
37 | });
38 | } else {
39 | res.send({
40 | code: 10002,
41 | data: {},
42 | msg: '未登录',
43 | });
44 | }
45 |
46 | };
47 |
48 | mock[`GET ${VUE_APP_APIHOST || ''}/user/message`] = (req, res) => {
49 | res.send({
50 | code: 0,
51 | data: mockjs.mock('@integer(0,99)'),
52 | });
53 | };
54 |
55 | mock[`POST ${VUE_APP_APIHOST || ''}/user/login`] = (req, res) => {
56 | const { password, username } = req.body;
57 | const send = { code: 0, data: {}, msg: '' };
58 | if (username === 'admin' && password === '123456') {
59 | send['data'] = {
60 | token: 'admin',
61 | };
62 | } else if (username === 'user' && password === '123456') {
63 | send['data'] = {
64 | token: 'user',
65 | };
66 | } else if (username === 'test' && password === '123456') {
67 | send['data'] = {
68 | token: 'test',
69 | };
70 | } else {
71 | send['code'] = 201;
72 | send['msg'] = 'Wrong username or password';
73 | }
74 |
75 | res.send(send);
76 | };
77 |
78 | mock[`POST ${VUE_APP_APIHOST || ''}/user/register`] = (req, res) => {
79 | res.send({
80 | code: 0,
81 | data: '',
82 | msg: '',
83 | });
84 | };
85 |
86 |
87 | module.exports = {
88 | ...mock
89 | };
--------------------------------------------------------------------------------
/src/store/user.ts:
--------------------------------------------------------------------------------
1 | import { Mutation, Action } from 'vuex';
2 | import { StoreModuleType } from "@/utils/store";
3 | import { ResponseData } from '@/utils/request';
4 | import { queryCurrent, queryMessage } from "@/services/user";
5 | import { removeToken } from "@/utils/localToken";
6 |
7 | export interface CurrentUser {
8 | id: number;
9 | name: string;
10 | avatar: string;
11 | roles: string[];
12 | }
13 |
14 | export interface StateType {
15 | currentUser: CurrentUser;
16 | message: number;
17 | }
18 |
19 | export interface ModuleType extends StoreModuleType {
20 | state: StateType;
21 | mutations: {
22 | saveCurrentUser: Mutation;
23 | saveMessage: Mutation;
24 | };
25 | actions: {
26 | fetchCurrent: Action;
27 | fetchMessage: Action;
28 | logout: Action;
29 | };
30 | }
31 |
32 | const initState: StateType = {
33 | currentUser: {
34 | id: 0,
35 | name: '',
36 | avatar: '',
37 | roles: [],
38 | },
39 | message: 0,
40 | }
41 |
42 | const StoreModel: ModuleType = {
43 | namespaced: true,
44 | name: 'user',
45 | state: {
46 | ...initState
47 | },
48 | mutations: {
49 | saveCurrentUser(state, payload) {
50 | state.currentUser = {
51 | ...initState.currentUser,
52 | ...payload,
53 | }
54 | },
55 | saveMessage(state, payload) {
56 | state.message = payload;
57 | }
58 | },
59 | actions: {
60 | async fetchCurrent({ commit }) {
61 | try {
62 | const response: ResponseData = await queryCurrent();
63 | const { data } = response;
64 | commit('saveCurrentUser', data || {});
65 | return true;
66 | } catch (error) {
67 | return false;
68 | }
69 | },
70 | async fetchMessage({ commit }) {
71 | try {
72 | const response: ResponseData = await queryMessage();
73 | const { data } = response;
74 | commit('saveMessage', data || 0);
75 | return true;
76 | } catch (error) {
77 | return false;
78 | }
79 | },
80 | async logout({ commit }) {
81 | try {
82 | await removeToken();
83 | commit('saveCurrentUser', { ...initState.currentUser });
84 | return true;
85 | } catch (error) {
86 | return false;
87 | }
88 | }
89 | }
90 | }
91 |
92 |
93 |
94 | export default StoreModel;
95 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/RightTopUser.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | {{t('index-layout.topmenu.userinfo')}}
10 |
11 |
12 | {{t('index-layout.topmenu.logout')}}
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "admin-antd-vue",
3 | "description": "Vue3.x Antd Admin",
4 | "version": "1.0.0",
5 | "private": true,
6 | "scripts": {
7 | "serve": "vue-cli-service serve",
8 | "build": "vue-cli-service build",
9 | "test:unit": "vue-cli-service test:unit",
10 | "lint": "vue-cli-service lint",
11 | "svgo": "svgo -f src/assets/iconsvg --config=src/assets/iconsvg/svgo.yml"
12 | },
13 | "dependencies": {
14 | "@ckeditor/ckeditor5-build-decoupled-document": "^23.1.0",
15 | "@ckeditor/ckeditor5-vue": "^4.0.0",
16 | "@toast-ui/editor": "^2.5.4",
17 | "ant-design-vue": "^2.2.8",
18 | "axios": "^0.21.4",
19 | "core-js": "^3.22.4",
20 | "echarts": "^5.3.2",
21 | "localforage": "^1.10.0",
22 | "lodash.debounce": "^4.0.8",
23 | "nprogress": "^0.2.0",
24 | "vue": "^3.2.39",
25 | "vue-i18n": "~9.1.10",
26 | "vue-router": "^4.1.5",
27 | "vuex": "^4.0.2"
28 | },
29 | "devDependencies": {
30 | "@types/jest": "^24.9.1",
31 | "@types/lodash.debounce": "^4.0.7",
32 | "@typescript-eslint/eslint-plugin": "^4.33.0",
33 | "@typescript-eslint/parser": "^4.33.0",
34 | "@vue/cli-plugin-babel": "~4.5.19",
35 | "@vue/cli-plugin-eslint": "~4.5.19",
36 | "@vue/cli-plugin-router": "~4.5.19",
37 | "@vue/cli-plugin-typescript": "~4.5.19",
38 | "@vue/cli-plugin-unit-jest": "~4.5.19",
39 | "@vue/cli-plugin-vuex": "~4.5.19",
40 | "@vue/cli-service": "~4.5.19",
41 | "@vue/compiler-sfc": "^3.2.37",
42 | "@vue/eslint-config-typescript": "^7.0.0",
43 | "@vue/test-utils": "^2.0.0-rc.21",
44 | "body-parser": "^1.20.0",
45 | "chokidar": "^3.5.3",
46 | "eslint": "^6.8.0",
47 | "eslint-plugin-vue": "^7.20.0",
48 | "less": "^3.13.1",
49 | "less-loader": "^5.0.0",
50 | "lint-staged": "^9.5.0",
51 | "mockjs": "^1.1.0",
52 | "node-dir": "^0.1.17",
53 | "svg-sprite-loader": "^5.2.1",
54 | "svgo": "^1.3.2",
55 | "svgo-loader": "^2.2.2",
56 | "typescript": "~4.1.6",
57 | "vue-jest": "^5.0.0-alpha.10"
58 | },
59 | "gitHooks": {
60 | "pre-commit": "lint-staged"
61 | },
62 | "keywords": [
63 | "vue",
64 | "vue3",
65 | "vue3.0",
66 | "vue3.x",
67 | "typescript",
68 | "admin",
69 | "template",
70 | "antd",
71 | "antdv",
72 | "Ant Design",
73 | "Ant Design Vue"
74 | ],
75 | "lint-staged": {
76 | "*.{js,jsx,vue,ts,tsx}": [
77 | "vue-cli-service lint",
78 | "git add"
79 | ]
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const bodyParser = require('body-parser')
3 | const mockServer = require('./src/utils/mock/server');
4 | const { NODE_ENV, VUE_APP_PORT, VUE_APP_MOCK } = process.env;
5 | module.exports = {
6 | publicPath: '/',
7 | outputDir: 'dist',
8 | productionSourceMap: false,
9 | devServer: {
10 | port: VUE_APP_PORT || 8000,
11 | // 配置反向代理
12 | /*
13 | proxy: {
14 | '/api': {
15 | target: '',
16 | ws: true,
17 | changeOrigin: true
18 | },
19 | '/foo': {
20 | target: ''
21 | }
22 | },
23 | */
24 | before: function(app, server) {
25 | if(NODE_ENV === 'development' && VUE_APP_MOCK === 'true') {
26 | // parse app.body
27 | // https://expressjs.com/en/4x/api.html#req.body
28 | // create application/json parser
29 | app.use(bodyParser.json());
30 | // create application/x-www-form-urlencoded parser
31 | app.use(bodyParser.urlencoded({ extended: false}));
32 | mockServer(app);
33 | }
34 | }
35 | },
36 | css: {
37 | loaderOptions: {
38 | less: {
39 | javascriptEnabled: true,
40 | }
41 | }
42 | },
43 | // 修改webpack的配置
44 | configureWebpack: {
45 | // 不需要打包的插件
46 | externals: {
47 | // 'vue': 'Vue',
48 | // 'vue-router': 'VueRouter',
49 | }
50 | },
51 | chainWebpack(config) {
52 |
53 | // 内置的 svg Rule 添加 exclude
54 | config.module
55 | .rule('svg')
56 | .exclude.add(/iconsvg/)
57 | .end();
58 |
59 | // 添加 svg-sprite-loader Rule
60 | config.module
61 | .rule('svg-sprite-loader')
62 | .test(/.svg$/)
63 | .include.add(/iconsvg/)
64 | .end()
65 | .use('svg-sprite-loader')
66 | .loader('svg-sprite-loader');
67 |
68 | // 添加 svgo Rule
69 | config.module
70 | .rule('svgo')
71 | .test(/.svg$/)
72 | .include.add(/iconsvg/)
73 | .end()
74 | .use('svgo-loader')
75 | .loader('svgo-loader')
76 | .options({
77 | // externalConfig 配置特殊不是相对路径,起始路径是根目录
78 | externalConfig: './src/assets/iconsvg/svgo.yml',
79 | });
80 |
81 | }
82 | }
--------------------------------------------------------------------------------
/src/assets/iconsvg/components.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/home/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
90 |
--------------------------------------------------------------------------------
/src/views/component/icon/svg/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <icon-svg type="{{item}}" />
9 |
10 |
11 |
12 | {{item}}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{t('page.icon.svg.remark.title')}}
21 | 组件位置: @/components/IconSvg
22 | 创建原因:方便自定义使用svg图标
23 |
24 |
25 | 使用方法:
26 |
27 | 1、下载或制作svg文件,存放到 @/assets/iconsvg
28 | 目录下,自己可以对此目录下svg进行删减。
29 |
30 |
31 | 2、项目会根据 @/assets/iconsvg/svgo.yml
32 | 配置自动压缩精简svg,也可以独立运行 yarn svgo 或
33 | npm run svgo压缩精简svg
34 |
35 | 3、使用Demo:
36 | import IconSvg from '@/components/IconSvg';
37 |
38 | <IconSvg type="svg文件名" class="" style=""/>
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
75 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/SiderMenu.vue:
--------------------------------------------------------------------------------
1 |
2 | {
9 | openChange(key);
10 | }"
11 | >
12 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/layouts/IndexLayout/components/SiderMenuItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{t(item.title)}}
11 |
12 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{t(item.title)}}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/mock/pagesample.js:
--------------------------------------------------------------------------------
1 | const mockjs= require('mockjs');
2 | const { VUE_APP_APIHOST } = process.env;
3 | const mock = {};
4 |
5 |
6 | mock[`GET ${VUE_APP_APIHOST || ''}/pages/detail`] = (req, res) => {
7 | res.send({
8 | code: 0,
9 | data: mockjs.mock({
10 | userInfo: {
11 | name: '小李',
12 | tel: '13770779817',
13 | courier: '宇宙快递',
14 | address: '宇宙地球',
15 | remark: '无',
16 | },
17 | refundApplication: {
18 | ladingNo: '1000000000',
19 | saleNo: '1234123421',
20 | state: '已取货',
21 | childOrders: '3214321432',
22 | },
23 | 'returnGoods|5': [
24 | {
25 | id: '@integer(1,99999)',
26 | name: '@ctitle(5,10)',
27 | barcode: '@integer(100000000000000,999999999999999)',
28 | price: '@float(1,15,0,2)',
29 | num: '@integer(1,5)',
30 | amount: function() {
31 | return Number(this.price) * Number(this.num);
32 | },
33 | },
34 | ],
35 | 'returnProgress|5': [
36 | {
37 | key: '@integer(1,99999)',
38 | time: '@datetime',
39 | rate: '@csentence(3, 5)',
40 | statuskey: '@boolean',
41 | status: function() {
42 | return this.statuskey ? 'success' : 'processing';
43 | },
44 | operator: '取货员 ID @integer(1000,9999)',
45 | cost: '@integer(1,5) h',
46 | },
47 | ],
48 | }),
49 | });
50 | };
51 |
52 | mock[`POST ${VUE_APP_APIHOST || ''}/pages/form`] = (req, res) => {
53 | res.send({
54 | code: 0,
55 | data: '',
56 | msg: '',
57 | });
58 | };
59 |
60 | mock[`GET ${VUE_APP_APIHOST || ''}/pages/list`] = (req, res) => {
61 | res.send({
62 | code: 0,
63 | data: mockjs.mock({
64 | total: 1000,
65 | currentPage: 1,
66 | 'list|10': [
67 | {
68 | id: '@integer(1)',
69 | 'name|1': ['个人博客', '网页小功能'],
70 | 'desc|1': ['李庆松的个人博客', '原创定制最好的网页插件小功能'],
71 | 'href|1': ['http://liqingsong.cc', 'http://wyxgn.com'],
72 | 'type|1': ['header', 'footer'],
73 | },
74 | ],
75 | }),
76 | });
77 | };
78 |
79 | mock[`POST ${VUE_APP_APIHOST || ''}/pages/list`] = (req, res) => {
80 | res.send({
81 | code: 0,
82 | data: '',
83 | });
84 | };
85 |
86 | mock[`PUT ${VUE_APP_APIHOST || ''}/pages/list/*`] = (req, res) => {
87 | res.send({
88 | code: 0,
89 | data: '',
90 | });
91 | };
92 |
93 | mock[`DELETE ${VUE_APP_APIHOST || ''}/pages/list/*`] = ( req, res) => {
94 | res.send({
95 | code: 0,
96 | data: '',
97 | });
98 | };
99 |
100 | mock[`GET ${VUE_APP_APIHOST || ''}/pages/list/*`] = (req, res) => {
101 | res.send({
102 | code: 0,
103 | data: mockjs.mock({
104 | id: '@integer(1)',
105 | 'name|1': ['个人博客', '网页小功能'],
106 | 'desc|1': ['李庆松的个人博客', '原创定制最好的网页插件小功能'],
107 | 'href|1': ['http://liqingsong.cc', 'http://wyxgn.com'],
108 | 'type|1': ['header', 'footer'],
109 | }),
110 | });
111 | };
112 |
113 | module.exports = {
114 | ...mock
115 | };
--------------------------------------------------------------------------------
/src/utils/mock/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Mock Server
3 | * @author LiQingSong
4 | */
5 | const requireContext = require('./require-context');
6 | const chokidar = require('chokidar');
7 | const path = require('path');
8 |
9 | // 注册 mock 路由
10 | function registerMockRoutes(mockDir, app) {
11 | let mockLastIndex = 0;
12 | let mockRoutesLength = 0;
13 | try {
14 | // 导入 /mock 下文件
15 | const context = requireContext(mockDir, false, /\.js$/);
16 | context.keys().forEach(fileName => {
17 | // 获取内容
18 | const mocks = context(fileName);
19 | for (const key in mocks) {
20 | const keys = key.replace(/(^\s*)|(\s*$)/g, '').split(' ');
21 | let ajaxType = 'get';
22 | let ajaxUrl = '';
23 | if(keys.length<2) {
24 | ajaxUrl = keys[0];
25 | } else {
26 | ajaxType = keys[0].toLowerCase();
27 | ajaxUrl = keys[1];
28 | if(!['get','post','put','patch','delete','head','options'].includes(ajaxType)) {
29 | ajaxType = 'get';
30 | }
31 | }
32 | if(typeof mocks[key] === 'function') {
33 | app[ajaxType](ajaxUrl, mocks[key]);
34 | mockLastIndex = app._router.stack.length;
35 | mockRoutesLength = mockRoutesLength + 1;
36 | // console.log(app._router.stack.length);
37 | }
38 | }
39 | });
40 | } catch (error) {
41 | console.log(error);
42 | }
43 |
44 | return {
45 | mockRoutesLength: mockRoutesLength,
46 | mockStartIndex: mockLastIndex - mockRoutesLength
47 | }
48 |
49 | }
50 |
51 | // 删除 mock 路由
52 | function unRegisterMockRoutes(mockDir) {
53 | Object.keys(require.cache).forEach(i => {
54 | if (i.includes(mockDir)) {
55 | delete require.cache[require.resolve(i)]
56 | }
57 | })
58 | }
59 |
60 |
61 | module.exports = app => {
62 | const mockDir = path.resolve('./mock');
63 |
64 | const mockRoutes = registerMockRoutes(mockDir,app);
65 | let mockRoutesLength = mockRoutes.mockRoutesLength;
66 | let mockStartIndex = mockRoutes.mockStartIndex;
67 |
68 | // watch files, hot reload mock server
69 | chokidar.watch(mockDir, {
70 | ignoreInitial: true
71 | }).on('all', (event, path) => {
72 | if (event === 'change' || event === 'add' || event === 'unlink') {
73 | // console.log(event, path);
74 | try {
75 | // remove mock routes stack
76 | app._router.stack.splice(mockStartIndex, mockRoutesLength);
77 |
78 | // clear routes cache
79 | unRegisterMockRoutes(mockDir);
80 |
81 | // rest routes
82 | const mockRoutes = registerMockRoutes(mockDir,app);
83 | mockRoutesLength = mockRoutes.mockRoutesLength;
84 | mockStartIndex = mockRoutes.mockStartIndex;
85 |
86 |
87 | console.log(`\n > Mock Server hot reload success! changed ${path}`)
88 | } catch (error) {
89 | console.log(error);
90 | }
91 |
92 | }
93 | })
94 |
95 |
96 |
97 |
98 | }
--------------------------------------------------------------------------------
/src/views/home/components/HotTagsCard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {
14 | getList(p.current || 1);
15 | }"
16 | />
17 |
18 |
19 |
95 |
--------------------------------------------------------------------------------
/src/views/home/components/WorksHitCard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {
14 | getList(p.current || 1);
15 | }"
16 | />
17 |
18 |
19 |
95 |
--------------------------------------------------------------------------------
/src/store/global.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Mutation/* , Action */ } from 'vuex';
3 | import { StoreModuleType } from "@/utils/store";
4 | import { TabNavItem, equalTabNavRoute } from '@/utils/routes';
5 | import settings, { Theme, NavMode } from '@/config/settings';
6 | import router from '@/config/routes';
7 | import { RouteLocationRaw } from 'vue-router';
8 |
9 | export interface StateType {
10 | /* 以下是针对所有 Layout 扩展字段 */
11 | // 左侧展开收起
12 | collapsed: boolean;
13 | // 头部固定开启
14 | headFixed: boolean;
15 | // tab菜单开启
16 | tabNavEnable: boolean;
17 | // 头部tab导航列表
18 | headTabNavList: TabNavItem[];
19 |
20 | /* 以下是针对 IndexLayout 扩展字段 */
21 | // 顶部菜单开启
22 | topNavEnable: boolean;
23 |
24 | /* 以下是针对 UniversalLayout 扩展字段 */
25 | // 模板主题
26 | theme: Theme;
27 | // 头部固定开启
28 | navMode: NavMode;
29 | // 左侧侧边固定开启
30 | leftSiderFixed: boolean;
31 | }
32 |
33 | export interface ModuleType extends StoreModuleType {
34 | state: StateType;
35 | mutations: {
36 | changeLayoutCollapsed: Mutation;
37 | setHeadFixed: Mutation;
38 | setTabNavEnable: Mutation;
39 | setHeadTabNavList: Mutation;
40 | closeCurrentHeadTabNav: Mutation;
41 | setTopNavEnable: Mutation;
42 | setTheme: Mutation;
43 | setNavMode: Mutation;
44 | setLeftSiderFixed: Mutation;
45 | };
46 | actions: {
47 | };
48 | }
49 |
50 | const homeRoute = router.resolve(settings.homeRouteItem.path);
51 |
52 | const initState: StateType = {
53 | collapsed: false,
54 | headFixed: settings.headFixed,
55 | tabNavEnable: settings.tabNavEnable,
56 | headTabNavList: [
57 | {
58 | route: homeRoute,
59 | menu: settings.homeRouteItem
60 | }
61 | ],
62 | topNavEnable: settings.topNavEnable,
63 | theme: settings.theme,
64 | navMode: settings.navMode,
65 | leftSiderFixed: settings.leftSiderFixed,
66 | };
67 |
68 | const StoreModel: ModuleType = {
69 | namespaced: true,
70 | name: 'global',
71 | state: {
72 | ...initState
73 | },
74 | mutations: {
75 | changeLayoutCollapsed(state, payload) {
76 | state.collapsed = payload;
77 | },
78 | setHeadFixed(state, payload) {
79 | state.headFixed = payload;
80 | },
81 | setTabNavEnable(state, payload) {
82 | state.tabNavEnable = payload;
83 | },
84 | setHeadTabNavList(state, payload) {
85 | state.headTabNavList = payload;
86 | },
87 | /**
88 | * 关闭当前tabNav,并跳转自定义路由
89 | * @param state
90 | * @param payload RouteLocationRaw 跳转路由参数,必须
91 | */
92 | closeCurrentHeadTabNav(state, payload: RouteLocationRaw) {
93 | const navList: TabNavItem[] = state.headTabNavList.filter((item2: TabNavItem) => !equalTabNavRoute(router.currentRoute.value, item2.route, item2.menu.tabNavType))
94 | state.headTabNavList = [
95 | ...navList
96 | ]
97 |
98 | router.push(payload)
99 | },
100 | setTopNavEnable(state, payload) {
101 | state.topNavEnable = payload;
102 | },
103 | setTheme(state, payload) {
104 | state.theme = payload;
105 | },
106 | setNavMode(state, payload) {
107 | state.navMode = payload;
108 | },
109 | setLeftSiderFixed(state, payload) {
110 | state.leftSiderFixed = payload;
111 | },
112 | },
113 | actions: {}
114 | }
115 |
116 |
117 |
118 | export default StoreModel;
119 |
--------------------------------------------------------------------------------
/src/views/home/components/HotSearchCard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {
14 | getList(p.current || 1);
15 | }"
16 | />
17 |
18 |
19 |
95 |
--------------------------------------------------------------------------------
/src/views/home/components/ArticleHitCard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {
14 | getList(p.current || 1);
15 | }"
16 | />
17 |
18 |
19 |
95 |
--------------------------------------------------------------------------------
/mock/home.js:
--------------------------------------------------------------------------------
1 | const mockjs= require('mockjs');
2 | const { VUE_APP_APIHOST } = process.env;
3 | const mock = {};
4 |
5 | mock[`GET ${VUE_APP_APIHOST || ''}/home/articles/dailynew`] = (req, res) => {
6 | res.send({
7 | code: 0,
8 | data: {
9 | total: mockjs.mock('@integer(1000,10000)'),
10 | num: mockjs.mock('@integer(10,100)'),
11 | day: mockjs.mock('@float(-10,15,0,2)'),
12 | week: mockjs.mock('@float(-10,15,0,2)'),
13 | },
14 | });
15 | };
16 |
17 | mock[`GET ${VUE_APP_APIHOST || ''}/home/works/weeknew`] = (req, res) => {
18 | res.send({
19 | code: 0,
20 | data: {
21 | total: mockjs.mock('@integer(1000,10000)'),
22 | num: mockjs.mock('@integer(10,100)'),
23 | chart: mockjs.mock({
24 | 'day|7': ['03-01'],
25 | 'num|7': ['@integer(0,3)'],
26 | }),
27 | },
28 | });
29 | };
30 |
31 | mock[`GET ${VUE_APP_APIHOST || ''}/home/topics/monthnew`] = (req, res) => {
32 | res.send({
33 | code: 0,
34 | data: {
35 | total: mockjs.mock('@integer(1000,10000)'),
36 | num: mockjs.mock('@integer(10,100)'),
37 | chart: mockjs.mock({
38 | 'day|30': ['03-01'],
39 | 'num|30': ['@integer(0,2)'],
40 | }),
41 | },
42 | });
43 | };
44 |
45 | mock[`GET ${VUE_APP_APIHOST || ''}/home/links/annualnew`] = (req, res) => {
46 | res.send({
47 | code: 0,
48 | data: {
49 | total: mockjs.mock('@integer(1000,10000)'),
50 | num: mockjs.mock('@integer(10,100)'),
51 | chart: mockjs.mock({
52 | 'day|12': ['2019-03'],
53 | 'num|12': ['@integer(0,8)'],
54 | }),
55 | },
56 | });
57 | };
58 |
59 | mock[`GET ${VUE_APP_APIHOST || ''}/home/searchs/keywords`] = (req, res) => {
60 | res.send({
61 | code: 0,
62 | data: mockjs.mock({
63 | total: 1000,
64 | currentPage: 1,
65 | 'list|5': [
66 | {
67 | name: '@ctitle(4,8)',
68 | hit: '@integer(1000,10000)',
69 | },
70 | ],
71 | }),
72 | });
73 | };
74 |
75 | mock[`GET ${VUE_APP_APIHOST || ''}/home/tags`] = (req, res) => {
76 | res.send({
77 | code: 0,
78 | data: mockjs.mock({
79 | total: 1000,
80 | currentPage: 1,
81 | 'list|5': [
82 | {
83 | name: '@ctitle(4,6)',
84 | id: '@integer(1)',
85 | pinyin: '@word(10,20)',
86 | hit: '@integer(1000,10000)',
87 | },
88 | ],
89 | }),
90 | });
91 | };
92 |
93 | mock[`GET ${VUE_APP_APIHOST || ''}/home/articles`] = (req, res) => {
94 | res.send({
95 | code: 0,
96 | data: mockjs.mock({
97 | total: 1000,
98 | currentPage: 1,
99 | 'list|5': [
100 | {
101 | category: {
102 | id: '@integer(1)',
103 | alias: '@word(4)',
104 | name: '@cword(4)',
105 | },
106 | title: '@ctitle(20,30)',
107 | id: '@integer(1)',
108 | addtime: '@datetime',
109 | 'tag|0-3': '@ctitle(4,6),',
110 | hit: '@integer(100,1000)',
111 | },
112 | ],
113 | }),
114 | });
115 | };
116 |
117 | mock[`GET ${VUE_APP_APIHOST || ''}/home/works`] = (req, res) => {
118 | res.send({
119 | code: 0,
120 | data: mockjs.mock({
121 | total: 1000,
122 | currentPage: 1,
123 | 'list|5': [
124 | {
125 | title: '@ctitle(20,30)',
126 | id: '@integer(1)',
127 | addtime: '@datetime',
128 | 'tag|0-3': '@ctitle(4,6),',
129 | hit: '@integer(100,1000)',
130 | },
131 | ],
132 | }),
133 | });
134 | };
135 |
136 |
137 |
138 | module.exports = {
139 | ...mock
140 | };
--------------------------------------------------------------------------------
/src/views/pagesample/list/highly-adaptive-table/components/SearchDrawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
38 |
39 |
40 | 取消
41 |
42 |
43 | 搜索
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------