├── .env.development
├── .env.production
├── .env.staging
├── src
├── vite-env.d.ts
├── store
│ ├── keys.ts
│ ├── index.ts
│ ├── pinia.ts
│ ├── modules
│ │ ├── cached-routes.ts
│ │ ├── app-config.ts
│ │ ├── user.ts
│ │ └── permission.ts
│ ├── plugin
│ │ └── persist.ts
│ └── types.ts
├── assets
│ ├── logo.png
│ ├── bg_img.webp
│ ├── img_403.png
│ ├── img_404.png
│ ├── img_500.png
│ ├── qrcode.jpg
│ ├── the_p.png
│ ├── the_w.png
│ ├── the_x.png
│ ├── img_vue.jpeg
│ ├── icon_sex_man.png
│ ├── img_angular.jpeg
│ ├── img_avatar.gif
│ ├── img_login_bg.png
│ ├── img_react.jpeg
│ ├── img_vip_icon.png
│ ├── icon_sex_woman.png
│ ├── img_avatar_01.jpeg
│ ├── img_avatar_02.jpeg
│ ├── img_avatar_default.png
│ └── data
│ │ └── provinces.json
├── hooks
│ ├── useAxios.ts
│ ├── useGet.ts
│ ├── usePost.ts
│ ├── useAppInfo.ts
│ ├── useEcharts.ts
│ ├── useMenuWidth.ts
│ ├── useCreateScript.ts
│ └── useDialogDragger.ts
├── icons
│ ├── iconfont
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ ├── add.svg
│ ├── expand.svg
│ ├── menu.svg
│ ├── data-money.svg
│ └── data-chart.svg
├── shims-vue.d.ts
├── styles
│ ├── variables.scss
│ ├── index.ts
│ ├── index.css
│ └── transition.css
├── App.vue
├── router
│ ├── guard
│ │ ├── index.ts
│ │ ├── title.ts
│ │ ├── cached.ts
│ │ ├── permission.ts
│ │ └── visited.ts
│ ├── index.ts
│ └── routes
│ │ ├── async.ts
│ │ ├── constants.ts
│ │ └── default-routes.ts
├── components
│ ├── common
│ │ ├── TableBody.vue
│ │ ├── AddButton.vue
│ │ ├── DeleteButton.vue
│ │ ├── TableConfig.vue
│ │ ├── SearchContent.vue
│ │ ├── RichTextEditor.vue
│ │ ├── TableFooter.vue
│ │ ├── PopoverMessage.vue
│ │ ├── CitySelector.vue
│ │ ├── IconSelector.vue
│ │ ├── TableHeader.vue
│ │ ├── ModalDialog.vue
│ │ └── SortableTable.vue
│ ├── loading
│ │ └── index.vue
│ ├── footer
│ │ └── index.vue
│ ├── Main.vue
│ ├── index.ts
│ ├── navbar
│ │ └── NavBar.vue
│ ├── humburger
│ │ └── index.vue
│ ├── svg-icon
│ │ └── index.vue
│ ├── logo
│ │ └── index.vue
│ ├── header
│ │ └── index.vue
│ ├── sidebar
│ │ └── components
│ │ │ ├── HorizontalScrollerMenu.vue
│ │ │ └── ScrollerMenu.vue
│ ├── setting
│ │ └── components
│ │ │ └── StyleExample.vue
│ ├── avatar
│ │ └── VAWAvatar.vue
│ └── breadcrumb
│ │ └── index.vue
├── views
│ ├── exception
│ │ ├── 403.vue
│ │ ├── 404.vue
│ │ ├── 500.vue
│ │ └── components
│ │ │ └── ExceptionStatus.vue
│ ├── next
│ │ ├── menu1.vue
│ │ ├── menu2
│ │ │ ├── menu-2-2.vue
│ │ │ └── menu-2-1
│ │ │ │ ├── menu-2-1-2.vue
│ │ │ │ └── menu-2-1-1.vue
│ │ └── cache-next-child.vue
│ ├── redirect
│ │ └── index.vue
│ ├── other
│ │ ├── chart
│ │ │ ├── icon-selector.vue
│ │ │ ├── icon.vue
│ │ │ └── components
│ │ │ │ ├── Chart.vue
│ │ │ │ ├── IconFont.vue
│ │ │ │ └── xicons.vue
│ │ ├── big-preview.vue
│ │ ├── clipboard.vue
│ │ ├── player.vue
│ │ ├── print.vue
│ │ ├── css-animation.vue
│ │ └── city-selector.vue
│ ├── editor
│ │ ├── markdown.vue
│ │ └── rich-text.vue
│ ├── index
│ │ └── components
│ │ │ ├── TrendsItem.vue
│ │ │ ├── TodoItem.vue
│ │ │ ├── DataItem.vue
│ │ │ ├── ProjectItem.vue
│ │ │ └── chart
│ │ │ ├── OrderChart.vue
│ │ │ ├── EnrollmentChannelsChart.vue
│ │ │ ├── SalesChart.vue
│ │ │ ├── StudentChart.vue
│ │ │ └── DepartmentChart.vue
│ ├── map
│ │ ├── gaode.vue
│ │ └── baidu.vue
│ ├── result
│ │ ├── success.vue
│ │ └── fail.vue
│ ├── form
│ │ ├── step-form.vue
│ │ └── components
│ │ │ ├── ResultInfo.vue
│ │ │ └── PasswordInfo.vue
│ ├── list
│ │ ├── card-list.vue
│ │ └── list.vue
│ ├── project
│ │ └── infomation.vue
│ ├── draggable
│ │ └── card-draggable.vue
│ └── login
│ │ └── index.vue
├── api
│ ├── interceptors
│ │ └── CustomRequestInterceptor.ts
│ ├── url.ts
│ ├── axios.config.ts
│ └── http.ts
├── main.ts
├── setting
│ └── index.ts
├── types
│ └── components.ts
└── utils
│ └── index.ts
├── public
├── favicon.ico
└── static
│ ├── images
│ ├── 1.jpeg
│ ├── 2.jpeg
│ ├── 3.jpeg
│ ├── 4.jpeg
│ ├── 5.jpeg
│ ├── 6.jpeg
│ ├── 7.jpeg
│ ├── 8.jpeg
│ ├── 9.jpeg
│ └── img_avatar_01.jpeg
│ └── loading.css
├── mock
├── base.ts
├── index.ts
└── user
│ └── index.js
├── postcss.config.js
├── .gitignore
├── tailwind.config.js
├── .eslintignore
├── tsconfig.json
├── prettier.config.js
├── .vscode
└── settings.json
├── LICENSE
├── index.html
├── vite.config.ts
├── package.json
└── .eslintrc.js
/.env.development:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.env.staging:
--------------------------------------------------------------------------------
1 | NODE_ENV=production
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/store/keys.ts:
--------------------------------------------------------------------------------
1 | export const LAYOUT = () => import('@/components/Layout.vue')
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/mock/base.ts:
--------------------------------------------------------------------------------
1 | export const baseData = {
2 | code: 200,
3 | data: '',
4 | msg: '获取数据成功',
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/bg_img.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/bg_img.webp
--------------------------------------------------------------------------------
/src/assets/img_403.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_403.png
--------------------------------------------------------------------------------
/src/assets/img_404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_404.png
--------------------------------------------------------------------------------
/src/assets/img_500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_500.png
--------------------------------------------------------------------------------
/src/assets/qrcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/qrcode.jpg
--------------------------------------------------------------------------------
/src/assets/the_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/the_p.png
--------------------------------------------------------------------------------
/src/assets/the_w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/the_w.png
--------------------------------------------------------------------------------
/src/assets/the_x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/the_x.png
--------------------------------------------------------------------------------
/src/assets/img_vue.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_vue.jpeg
--------------------------------------------------------------------------------
/public/static/images/1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/1.jpeg
--------------------------------------------------------------------------------
/public/static/images/2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/2.jpeg
--------------------------------------------------------------------------------
/public/static/images/3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/3.jpeg
--------------------------------------------------------------------------------
/public/static/images/4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/4.jpeg
--------------------------------------------------------------------------------
/public/static/images/5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/5.jpeg
--------------------------------------------------------------------------------
/public/static/images/6.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/6.jpeg
--------------------------------------------------------------------------------
/public/static/images/7.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/7.jpeg
--------------------------------------------------------------------------------
/public/static/images/8.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/8.jpeg
--------------------------------------------------------------------------------
/public/static/images/9.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/9.jpeg
--------------------------------------------------------------------------------
/src/assets/icon_sex_man.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/icon_sex_man.png
--------------------------------------------------------------------------------
/src/assets/img_angular.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_angular.jpeg
--------------------------------------------------------------------------------
/src/assets/img_avatar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_avatar.gif
--------------------------------------------------------------------------------
/src/assets/img_login_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_login_bg.png
--------------------------------------------------------------------------------
/src/assets/img_react.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_react.jpeg
--------------------------------------------------------------------------------
/src/assets/img_vip_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_vip_icon.png
--------------------------------------------------------------------------------
/src/assets/icon_sex_woman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/icon_sex_woman.png
--------------------------------------------------------------------------------
/src/assets/img_avatar_01.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_avatar_01.jpeg
--------------------------------------------------------------------------------
/src/assets/img_avatar_02.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_avatar_02.jpeg
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/src/hooks/useAxios.ts:
--------------------------------------------------------------------------------
1 | import axios from '../api/axios.config'
2 | export default function () {
3 | return axios
4 | }
5 |
--------------------------------------------------------------------------------
/src/hooks/useGet.ts:
--------------------------------------------------------------------------------
1 | import { get } from '../api/http'
2 | export default function usePost() {
3 | return get
4 | }
5 |
--------------------------------------------------------------------------------
/src/hooks/usePost.ts:
--------------------------------------------------------------------------------
1 | import { post } from '../api/http'
2 | export default function usePost() {
3 | return post
4 | }
5 |
--------------------------------------------------------------------------------
/src/icons/iconfont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/icons/iconfont/iconfont.ttf
--------------------------------------------------------------------------------
/src/icons/iconfont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/icons/iconfont/iconfont.woff
--------------------------------------------------------------------------------
/src/assets/img_avatar_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/assets/img_avatar_default.png
--------------------------------------------------------------------------------
/src/icons/iconfont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/src/icons/iconfont/iconfont.woff2
--------------------------------------------------------------------------------
/public/static/images/img_avatar_01.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qingqingxuan/admin-work/HEAD/public/static/images/img_avatar_01.jpeg
--------------------------------------------------------------------------------
/src/hooks/useAppInfo.ts:
--------------------------------------------------------------------------------
1 | import packageInfo from '../../package.json'
2 | export default function useAppInfo() {
3 | return packageInfo
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist-ssr
4 | *.local
5 | /dist
6 | dist
7 | package-lock.json
8 | components.d.ts
9 | pnpm-lock.yaml
10 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | }
8 |
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | import pinia from './pinia'
3 |
4 | function useAppPinia(app: App) {
5 | app.use(pinia)
6 | }
7 |
8 | export default useAppPinia
9 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | *.sh
2 | node_modules
3 | *.md
4 | *.woff
5 | *.ttf
6 | .vscode
7 | .idea
8 | dist
9 | /public
10 | /docs
11 | .husky
12 | .local
13 | /bin
14 | Dockerfile
15 | /dist
16 |
--------------------------------------------------------------------------------
/src/store/pinia.ts:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 | import PersistPlugin from './plugin/persist'
3 |
4 | const pinia = createPinia()
5 | pinia.use(PersistPlugin)
6 |
7 | export default pinia
8 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $tabSplitMenuWidth: 65px;
2 | $menuWidth: var(--menu-width);
3 | $minMenuWidth: 65px;
4 | $transitionTime: 0.3s ease;
5 | $logoHeight: 48px;
6 | $tabHeight: 40px;
7 | $footerHeight: 45px;
8 |
--------------------------------------------------------------------------------
/mock/index.ts:
--------------------------------------------------------------------------------
1 | function useMock() {
2 | const modules = import.meta.glob('./**/*.{js,ts}', { eager: true })
3 | Object.keys(modules).forEach((it) => {
4 | modules[it]
5 | })
6 | }
7 | export default useMock
8 |
--------------------------------------------------------------------------------
/src/styles/index.ts:
--------------------------------------------------------------------------------
1 | import 'vfonts/Lato.css'
2 | import 'vfonts/FiraCode.css'
3 | import 'virtual:svg-icons-register'
4 | import './index.css'
5 | import './transition.css'
6 | import '@/icons/iconfont/iconfont.css'
7 |
--------------------------------------------------------------------------------
/src/hooks/useEcharts.ts:
--------------------------------------------------------------------------------
1 | import * as echarts from 'echarts'
2 | export default function useEcharts(dom: HTMLElement, theme?: string) {
3 | let instance = echarts.getInstanceByDom(dom)
4 | if (!instance) {
5 | instance = echarts.init(dom, theme)
6 | }
7 | return instance
8 | }
9 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
--------------------------------------------------------------------------------
/src/router/guard/index.ts:
--------------------------------------------------------------------------------
1 | import useCachedGuard from './cached'
2 | import usePermissionGuard from './permission'
3 | import usePageTitleGuard from './title'
4 | import useVisitedGuard from './visited'
5 |
6 | export default function useRouterGuard() {
7 | usePermissionGuard()
8 | useVisitedGuard()
9 | useCachedGuard()
10 | usePageTitleGuard()
11 | }
12 |
--------------------------------------------------------------------------------
/src/icons/add.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/styles/index.css:
--------------------------------------------------------------------------------
1 | /*! @import */
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 | :root {
6 | --menu-width: 210px;
7 | }
8 | html,
9 | body,
10 | #app {
11 | height: 100vh;
12 | }
13 | * {
14 | box-sizing: border-box;
15 | }
16 |
17 | td .n-button + .n-button {
18 | margin-left: 10px;
19 | }
20 |
21 | .n-card {
22 | border-radius: 10px;
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/common/TableBody.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/src/router/guard/title.ts:
--------------------------------------------------------------------------------
1 | import router from '@/router'
2 | import { projectName } from '@/setting'
3 |
4 | function usePageTitleGuard() {
5 | router.afterEach((to) => {
6 | if (to.meta && to.meta.title) {
7 | const title = to.meta.title
8 | document.title = projectName + ' | ' + title
9 | } else {
10 | document.title = projectName
11 | }
12 | })
13 | }
14 |
15 | export default usePageTitleGuard
16 |
--------------------------------------------------------------------------------
/src/views/exception/403.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/views/exception/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/views/exception/500.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/hooks/useMenuWidth.ts:
--------------------------------------------------------------------------------
1 | export function useMenuWidth() {
2 | const r = document.querySelector(':root') as HTMLElement
3 | const styles = getComputedStyle(r)
4 | const menuWith = styles.getPropertyValue('--menu-width')
5 | return parseInt(menuWith)
6 | }
7 |
8 | export function useChangeMenuWidth(width: Number) {
9 | const r = document.querySelector(':root') as HTMLElement
10 | r.style.setProperty('--menu-width', width + 'px')
11 | }
12 |
--------------------------------------------------------------------------------
/src/views/next/menu1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
--------------------------------------------------------------------------------
/src/views/next/menu2/menu-2-2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | import { mapTwoLevelRouter } from '@/store/help'
3 | import { createRouter, createWebHashHistory } from 'vue-router'
4 | import { constantRoutes } from './routes/constants'
5 |
6 | const router = createRouter({
7 | history: createWebHashHistory(),
8 | routes: mapTwoLevelRouter([...constantRoutes]),
9 | })
10 |
11 | export function useAppRouter(app: App) {
12 | app.use(router)
13 | }
14 |
15 | export default router
16 |
--------------------------------------------------------------------------------
/src/api/interceptors/CustomRequestInterceptor.ts:
--------------------------------------------------------------------------------
1 | import { useUserStoreContext } from '@/store/modules/user'
2 | import { AxiosRequestConfig } from 'axios'
3 |
4 | export default function (config: AxiosRequestConfig) {
5 | const useStore = useUserStoreContext()
6 | if (config) {
7 | if (!config.headers) {
8 | config.headers = {}
9 | }
10 | if (!config.headers['Auth']) {
11 | config.headers['Auth'] = useStore.token
12 | }
13 | }
14 | return config
15 | }
16 |
--------------------------------------------------------------------------------
/src/views/next/menu2/menu-2-1/menu-2-1-2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
--------------------------------------------------------------------------------
/src/views/redirect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
--------------------------------------------------------------------------------
/src/components/loading/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
23 |
--------------------------------------------------------------------------------
/src/views/other/chart/icon-selector.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
17 |
--------------------------------------------------------------------------------
/src/router/guard/cached.ts:
--------------------------------------------------------------------------------
1 | import { findCachedRoutes } from '@/store/help'
2 | import useCachedRouteStore from '@/store/modules/cached-routes'
3 | import router from '..'
4 |
5 | function useCachedGuard() {
6 | router.beforeEach(() => {
7 | const cachedRouteStore = useCachedRouteStore()
8 | if (cachedRouteStore.getCachedRouteName.length === 0) {
9 | cachedRouteStore.initCachedRoute(findCachedRoutes(router.getRoutes()))
10 | }
11 | return true
12 | })
13 | }
14 |
15 | export default useCachedGuard
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "esModuleInterop": true,
11 | "skipLibCheck": true,
12 | "lib": ["esnext", "dom"],
13 | "baseUrl": ".",
14 | "paths": {
15 | "@/*": ["src/*"]
16 | }
17 | },
18 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
19 | }
20 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | useTabs: false,
5 | semi: false,
6 | vueIndentScriptAndStyle: true,
7 | singleQuote: true,
8 | quoteProps: 'as-needed',
9 | bracketSpacing: true,
10 | trailingComma: 'es5',
11 | jsxBracketSameLine: false,
12 | jsxSingleQuote: false,
13 | arrowParens: 'always',
14 | insertPragma: false,
15 | requirePragma: false,
16 | proseWrap: 'never',
17 | htmlWhitespaceSensitivity: 'strict',
18 | endOfLine: 'auto',
19 | rangeStart: 0,
20 | }
21 |
--------------------------------------------------------------------------------
/src/views/next/menu2/menu-2-1/menu-2-1-1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 缓存信息
5 |
6 |
7 |
8 |
9 |
10 |
23 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import './styles'
4 | import useGlobalComponents from './components'
5 | import { useAppRouter } from './router'
6 | import useRouterGuard from './router/guard'
7 | import useAppPinia from './store'
8 | import useMock from '../mock'
9 |
10 | function vawBoot() {
11 | const app = createApp(App)
12 | useAppPinia(app)
13 | useAppRouter(app)
14 | useGlobalComponents(app)
15 | useRouterGuard()
16 | useMock()
17 | app.mount('#app')
18 | }
19 |
20 | vawBoot()
21 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.iconTheme": "material-icon-theme",
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": "explicit"
5 | },
6 | "[html]": {
7 | "editor.defaultFormatter": "esbenp.prettier-vscode"
8 | },
9 | "[javascript]": {
10 | "editor.defaultFormatter": "esbenp.prettier-vscode"
11 | },
12 | "[typescript]": {
13 | "editor.defaultFormatter": "esbenp.prettier-vscode"
14 | },
15 | "[vue]": {
16 | "editor.defaultFormatter": "Vue.volar"
17 | },
18 | "git.ignoreLimitWarning": true
19 | }
20 |
--------------------------------------------------------------------------------
/src/views/other/big-preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/views/editor/markdown.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 功能开发中……
4 |
5 |
6 |
7 |
14 |
15 |
28 |
--------------------------------------------------------------------------------
/src/hooks/useCreateScript.ts:
--------------------------------------------------------------------------------
1 | import { onMounted } from 'vue'
2 |
3 | export default function userCreateScript(src: string) {
4 | const createScriptPromise = new Promise((resolve, reject) => {
5 | onMounted(() => {
6 | const script = document.createElement('script')
7 | script.type = 'text/javascript'
8 | script.onload = () => {
9 | resolve('')
10 | }
11 | script.onerror = (error) => {
12 | reject(error)
13 | }
14 | script.src = src
15 | document.head.appendChild(script)
16 | })
17 | })
18 | return {
19 | createScriptPromise,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/views/next/cache-next-child.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/src/setting/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AppConfigState,
3 | DeviceType,
4 | LayoutMode,
5 | PageAnim,
6 | SideTheme,
7 | ThemeMode,
8 | } from '@/store/types'
9 |
10 | export const projectName = 'Admin Work'
11 |
12 | export default {
13 | theme: ThemeMode.LIGHT,
14 | sideTheme: SideTheme.WHITE,
15 | themeColor: '#409eff',
16 | layoutMode: LayoutMode.LTR,
17 | sideWidth: 210,
18 | deviceType: DeviceType.PC,
19 | pageAnim: PageAnim.OPACITY,
20 | isFixedNavBar: true,
21 | isCollapse: false,
22 | actionBar: {
23 | isShowSearch: true,
24 | isShowMessage: true,
25 | isShowRefresh: true,
26 | isShowFullScreen: true,
27 | },
28 | } as AppConfigState
29 |
--------------------------------------------------------------------------------
/src/components/common/AddButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 添加
9 |
10 |
11 |
12 |
32 |
--------------------------------------------------------------------------------
/src/components/footer/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/views/index/components/TrendsItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
28 |
--------------------------------------------------------------------------------
/src/components/common/DeleteButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 删除
9 |
10 |
11 |
12 |
32 |
--------------------------------------------------------------------------------
/src/icons/expand.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/store/modules/cached-routes.ts:
--------------------------------------------------------------------------------
1 | import { toHump } from '@/utils'
2 | import { defineStore } from 'pinia'
3 |
4 | const useCachedRouteStore = defineStore('cached-routes', {
5 | state: () => {
6 | return {
7 | cachedRoutes: [] as string[],
8 | }
9 | },
10 | getters: {
11 | getCachedRouteName(state) {
12 | return state.cachedRoutes
13 | },
14 | },
15 | actions: {
16 | initCachedRoute(routes: string[]) {
17 | this.cachedRoutes = routes.map((it) => {
18 | return toHump(it as string)
19 | })
20 | },
21 | setCachedRoutes(cachedRoutes: string[] = []) {
22 | this.cachedRoutes = cachedRoutes
23 | },
24 | resetCachedRoutes() {
25 | this.$reset()
26 | },
27 | },
28 | })
29 |
30 | export default useCachedRouteStore
31 |
--------------------------------------------------------------------------------
/src/views/other/chart/icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
--------------------------------------------------------------------------------
/src/assets/data/provinces.json:
--------------------------------------------------------------------------------
1 | [{"code":"11","name":"北京市"},{"code":"12","name":"天津市"},{"code":"13","name":"河北省"},{"code":"14","name":"山西省"},{"code":"15","name":"内蒙古自治区"},{"code":"21","name":"辽宁省"},{"code":"22","name":"吉林省"},{"code":"23","name":"黑龙江省"},{"code":"31","name":"上海市"},{"code":"32","name":"江苏省"},{"code":"33","name":"浙江省"},{"code":"34","name":"安徽省"},{"code":"35","name":"福建省"},{"code":"36","name":"江西省"},{"code":"37","name":"山东省"},{"code":"41","name":"河南省"},{"code":"42","name":"湖北省"},{"code":"43","name":"湖南省"},{"code":"44","name":"广东省"},{"code":"45","name":"广西壮族自治区"},{"code":"46","name":"海南省"},{"code":"50","name":"重庆市"},{"code":"51","name":"四川省"},{"code":"52","name":"贵州省"},{"code":"53","name":"云南省"},{"code":"54","name":"西藏自治区"},{"code":"61","name":"陕西省"},{"code":"62","name":"甘肃省"},{"code":"63","name":"青海省"},{"code":"64","name":"宁夏回族自治区"},{"code":"65","name":"新疆维吾尔自治区"}]
--------------------------------------------------------------------------------
/src/components/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
27 |
--------------------------------------------------------------------------------
/src/api/url.ts:
--------------------------------------------------------------------------------
1 | import { baseURL } from './axios.config'
2 |
3 | export const baseAddress = baseURL
4 |
5 | export const test = '/test'
6 |
7 | export const login = '/login'
8 |
9 | export const updateUserInfo = '/updateUser'
10 |
11 | export const addUserInfo = '/addUser'
12 |
13 | export const getMenuListByRoleId = '/getMenusByRoleId'
14 |
15 | export const getAllMenuByRoleId = '/getAllMenuByRoleId'
16 |
17 | export const deleteUserById = '/deleteUserById'
18 |
19 | export const getDepartmentList = '/getDepartmentList'
20 |
21 | export const addDepartment = '/addDepartment'
22 |
23 | export const getRoleList = '/getRoleList'
24 |
25 | export const getMenuList = '/getMenuList'
26 |
27 | export const getParentMenuList = '/getParentMenuList'
28 |
29 | export const getTableList = '/getTableList'
30 |
31 | export const getCardList = '/getCardList'
32 |
33 | export const getCommentList = '/getCommentList'
34 |
--------------------------------------------------------------------------------
/src/router/guard/permission.ts:
--------------------------------------------------------------------------------
1 | import useUserStore from '@/store/modules/user'
2 | import usePermissionStore from '@/store/modules/permission'
3 | import router from '..'
4 |
5 | const whiteRoutes: string[] = ['/login', '/404', '/403', '/500']
6 |
7 | function usePermissionGuard() {
8 | router.beforeEach(async (to) => {
9 | if (whiteRoutes.includes(to.path)) {
10 | return true
11 | }
12 | const userStore = useUserStore()
13 | if (userStore.isTokenExpire()) {
14 | return {
15 | path: '/login',
16 | query: { redirect: to.fullPath },
17 | }
18 | }
19 | const permissionStore = usePermissionStore()
20 | const isEmptyRoute = permissionStore.isEmptyPermissionRoute()
21 | if (isEmptyRoute) {
22 | await permissionStore.initPermissionRoute()
23 | return { ...to, replace: true }
24 | }
25 | return true
26 | })
27 | }
28 |
29 | export default usePermissionGuard
30 |
--------------------------------------------------------------------------------
/src/router/routes/async.ts:
--------------------------------------------------------------------------------
1 | import { LAYOUT } from '@/store/keys'
2 |
3 | export const asyncRoutes = [
4 | {
5 | path: '/index',
6 | component: LAYOUT,
7 | name: 'Index',
8 | meta: {
9 | title: 'Dashboard',
10 | iconPrefix: 'iconfont',
11 | icon: 'dashboard',
12 | },
13 | children: [
14 | {
15 | path: 'home',
16 | name: 'Home',
17 | component: () => import('@/views/index/main.vue'),
18 | meta: {
19 | title: '主控台',
20 | affix: true,
21 | cacheable: true,
22 | iconPrefix: 'iconfont',
23 | icon: 'menu',
24 | },
25 | },
26 | {
27 | path: 'work-place',
28 | name: 'WorkPlace',
29 | component: () => import('@/views/index/work-place.vue'),
30 | meta: {
31 | title: '工作台',
32 | affix: true,
33 | iconPrefix: 'iconfont',
34 | icon: 'menu',
35 | },
36 | },
37 | ],
38 | },
39 | ]
40 |
--------------------------------------------------------------------------------
/src/router/guard/visited.ts:
--------------------------------------------------------------------------------
1 | import { findAffixedRoutes } from '@/store/help'
2 | import useVisitedRouteStore from '@/store/modules/visited-routes'
3 | import { RouteRecordRaw } from 'vue-router'
4 | import router from '..'
5 |
6 | function useVisitedGuard() {
7 | router.beforeEach((to) => {
8 | if (['404', '500', '403', 'not-found', 'Login'].includes(to.name as string)) {
9 | return true
10 | }
11 | const visitedRouteStore = useVisitedRouteStore()
12 | if (!visitedRouteStore.isLoadAffix) {
13 | const affixRoutes = findAffixedRoutes(router.getRoutes())
14 | visitedRouteStore.initAffixRoutes(affixRoutes)
15 | }
16 | if (to.path.startsWith('/redirect')) {
17 | return true
18 | }
19 | if (to.meta.noShowTabbar) {
20 | return true
21 | }
22 | if (to.query?.noShowTabbar) {
23 | return true
24 | }
25 | visitedRouteStore.addVisitedRoute(to as unknown as RouteRecordRaw)
26 | return true
27 | })
28 | }
29 |
30 | export default useVisitedGuard
31 |
--------------------------------------------------------------------------------
/src/views/other/clipboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 复制
7 |
8 |
9 |
10 |
11 |
12 |
35 |
--------------------------------------------------------------------------------
/src/views/other/chart/components/Chart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/views/map/gaode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 清清玄
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 | Admin Work
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Admin Work
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 | import { toHump } from '../utils'
3 |
4 | function adapterNaiveCss() {
5 | const meta = document.createElement('meta')
6 | meta.name = 'naive-ui-style'
7 | document.head.appendChild(meta)
8 | }
9 |
10 | function getComponentName(key: string) {
11 | const paths = key.split('/')
12 | const name = paths
13 | .filter((it) => !!it && it !== '.')
14 | .reverse()
15 | .find(
16 | (it) =>
17 | it !== 'index.vue' &&
18 | it !== 'index.ts' &&
19 | it !== 'index.tsx' &&
20 | it !== 'index.js' &&
21 | it !== 'index.jsx'
22 | )
23 | ?.replace('.vue', '')
24 | return name || ''
25 | }
26 |
27 | export function registerComponents(app: App) {
28 | const components = import.meta.glob('./**/**.{vue,tsx}', { eager: true })
29 | Object.keys(components).forEach((it: string) => {
30 | const component = components[it] as any
31 | app.component(component.default.name || toHump(getComponentName(it)), component.default)
32 | })
33 | }
34 |
35 | function useGlobalComponents(app: App) {
36 | adapterNaiveCss()
37 | registerComponents(app)
38 | }
39 |
40 | export default useGlobalComponents
41 |
--------------------------------------------------------------------------------
/src/views/other/player.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 视频播放器(集成西瓜视频插件SDK)
5 |
6 |
9 |
10 |
11 |
12 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/views/map/baidu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
33 |
--------------------------------------------------------------------------------
/src/components/navbar/NavBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
28 |
29 |
47 |
--------------------------------------------------------------------------------
/src/components/common/TableConfig.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 表格边框
4 |
5 | 刷新页面
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
43 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
3 | import path from 'path'
4 | import vitePluginCompression from 'vite-plugin-compression'
5 | import ViteComponents from 'unplugin-vue-components/vite'
6 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
7 |
8 | import vueJsx from '@vitejs/plugin-vue-jsx'
9 |
10 | export default () => {
11 | return {
12 | base: '/',
13 | plugins: [
14 | vue(),
15 | createSvgIconsPlugin({
16 | iconDirs: [path.resolve(process.cwd(), 'src/icons')],
17 | symbolId: 'icon-[dir]-[name]',
18 | }),
19 | vitePluginCompression({
20 | threshold: 1024 * 10,
21 | }),
22 | ViteComponents({
23 | resolvers: [NaiveUiResolver()],
24 | }),
25 | vueJsx(),
26 | ],
27 | css: {
28 | preprocessorOptions: {
29 | scss: {
30 | additionalData: '@use "./src/styles/variables.scss" as *;',
31 | },
32 | },
33 | },
34 | resolve: {
35 | alias: [
36 | {
37 | find: '@/',
38 | replacement: path.resolve(process.cwd(), 'src') + '/',
39 | },
40 | ],
41 | },
42 | server: {
43 | open: true,
44 | },
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/humburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
28 |
29 |
50 |
--------------------------------------------------------------------------------
/src/api/axios.config.ts:
--------------------------------------------------------------------------------
1 | import Axios, { AxiosResponse } from 'axios'
2 | import qs from 'qs'
3 |
4 | export const baseURL = 'http://localhost:8080/'
5 |
6 | export const CONTENT_TYPE = 'Content-Type'
7 |
8 | export const FORM_URLENCODED = 'application/x-www-form-urlencoded; charset=UTF-8'
9 |
10 | export const APPLICATION_JSON = 'application/json; charset=UTF-8'
11 |
12 | export const TEXT_PLAIN = 'text/plain; charset=UTF-8'
13 |
14 | const service = Axios.create({
15 | baseURL,
16 | timeout: 10 * 60 * 1000,
17 | })
18 |
19 | service.interceptors.request.use(
20 | (config) => {
21 | !config.headers && (config.headers = {})
22 | if (!config.headers[CONTENT_TYPE]) {
23 | config.headers[CONTENT_TYPE] = APPLICATION_JSON
24 | }
25 | if (config.headers[CONTENT_TYPE] === FORM_URLENCODED) {
26 | config.data = qs.stringify(config.data)
27 | }
28 | return config
29 | },
30 | (error) => {
31 | return Promise.reject(error.response)
32 | }
33 | )
34 |
35 | service.interceptors.response.use(
36 | (response: AxiosResponse): AxiosResponse => {
37 | if (response.status === 200) {
38 | return response
39 | } else {
40 | throw new Error(response.status.toString())
41 | }
42 | },
43 | (error) => {
44 | if (import.meta.env.MODE === 'development') {
45 | console.log(error)
46 | }
47 | return Promise.reject({ code: 500, msg: '服务器异常,请稍后重试…' })
48 | }
49 | )
50 |
51 | export default service
52 |
--------------------------------------------------------------------------------
/src/components/svg-icon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
41 |
42 |
60 |
--------------------------------------------------------------------------------
/src/router/routes/constants.ts:
--------------------------------------------------------------------------------
1 | import { LAYOUT } from '@/store/keys'
2 |
3 | export const constantRoutes = [
4 | {
5 | path: '/login',
6 | name: 'Login',
7 | component: () => import('@/views/login/index.vue'),
8 | meta: {
9 | hidden: true,
10 | },
11 | },
12 | {
13 | path: '/redirect',
14 | component: LAYOUT,
15 | meta: {
16 | hidden: true,
17 | noShowTabbar: true,
18 | },
19 | children: [
20 | {
21 | path: '/redirect/:path(.*)*',
22 | component: () => import('@/views/redirect/index.vue'),
23 | },
24 | ],
25 | },
26 | {
27 | path: '/personal',
28 | name: 'personal',
29 | component: LAYOUT,
30 | meta: {
31 | title: '个人中心',
32 | hidden: true,
33 | },
34 | children: [
35 | {
36 | path: 'info',
37 | component: () => import('@/views/personal/index.vue'),
38 | meta: {
39 | title: '个人中心',
40 | },
41 | },
42 | ],
43 | },
44 | {
45 | path: '/404',
46 | name: '404',
47 | component: () => import('@/views/exception/404.vue'),
48 | meta: {
49 | hidden: true,
50 | },
51 | },
52 | {
53 | path: '/500',
54 | name: '500',
55 | component: () => import('@/views/exception/500.vue'),
56 | meta: {
57 | hidden: true,
58 | },
59 | },
60 | {
61 | path: '/403',
62 | name: '403',
63 | component: () => import('@/views/exception/403.vue'),
64 | meta: {
65 | hidden: true,
66 | },
67 | },
68 | ]
69 |
--------------------------------------------------------------------------------
/src/store/modules/app-config.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 |
3 | import defaultSetting from '@/setting'
4 | import { LayoutMode, PageAnim, SideTheme, ThemeMode, DeviceType } from '../types'
5 |
6 | import { useChangeMenuWidth } from '@/hooks/useMenuWidth'
7 | useChangeMenuWidth(defaultSetting.sideWidth)
8 |
9 | const useAppConfigStore = defineStore('app-config', {
10 | state: () => {
11 | return defaultSetting
12 | },
13 | getters: {
14 | getLayoutMode(state) {
15 | return state.layoutMode
16 | },
17 | },
18 | actions: {
19 | changeTheme(theme: ThemeMode) {
20 | this.theme = theme
21 | },
22 | changeLayoutMode(mode: LayoutMode) {
23 | this.layoutMode = mode
24 | },
25 | changeDevice(deviceType: DeviceType) {
26 | this.deviceType = deviceType
27 | },
28 | changeSideBarTheme(sideTheme: SideTheme) {
29 | this.sideTheme = sideTheme
30 | },
31 | changePageAnim(pageAnim: PageAnim) {
32 | this.pageAnim = pageAnim
33 | },
34 | changePrimaryColor(color: string) {
35 | this.themeColor = color
36 | },
37 | changeSideWith(sideWidth: number) {
38 | this.sideWidth = sideWidth
39 | const r = document.querySelector(':root') as HTMLElement
40 | r.style.setProperty('--menu-width', sideWidth + 'px')
41 | },
42 | toggleCollapse(isCollapse: boolean) {
43 | this.isCollapse = isCollapse
44 | },
45 | },
46 | presist: {
47 | enable: true,
48 | resetToState: true,
49 | },
50 | })
51 |
52 | export default useAppConfigStore
53 |
--------------------------------------------------------------------------------
/src/views/result/success.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 提交成功
10 |
11 | 提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message
12 | 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示
13 | “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。
14 |
15 |
16 | 返回列表
17 | 查看项目
18 | 打印
19 |
20 |
21 |
22 |
23 |
24 |
33 |
34 |
57 |
--------------------------------------------------------------------------------
/src/store/modules/user.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { UserState } from '../types'
3 | import store from '../pinia'
4 |
5 | import Avatar from '@/assets/img_avatar.gif'
6 |
7 | const defaultAvatar = Avatar
8 |
9 | const useUserStore = defineStore('user-info', {
10 | state: () => {
11 | return {
12 | userId: 0,
13 | roleId: 0,
14 | token: '',
15 | userName: '',
16 | nickName: '',
17 | avatar: defaultAvatar,
18 | }
19 | },
20 | actions: {
21 | saveUser(userInfo: UserState) {
22 | return new Promise((resolve) => {
23 | this.userId = userInfo.userId
24 | this.roleId = userInfo.roleId
25 | this.token = userInfo.token
26 | this.userName = userInfo.userName
27 | this.nickName = userInfo.nickName
28 | this.avatar = userInfo.avatar || defaultAvatar
29 | resolve(userInfo)
30 | })
31 | },
32 | isTokenExpire() {
33 | return !this.token
34 | },
35 | changeNickName(newNickName: string) {
36 | this.nickName = newNickName
37 | },
38 | logout() {
39 | return new Promise((resolve) => {
40 | this.$reset()
41 | localStorage.clear()
42 | sessionStorage.clear()
43 | resolve()
44 | })
45 | },
46 | },
47 | presist: {
48 | enable: true,
49 | resetToState: true,
50 | option: {
51 | exclude: ['userName'],
52 | },
53 | },
54 | })
55 |
56 | export default useUserStore
57 |
58 | export function useUserStoreContext() {
59 | return useUserStore(store)
60 | }
61 |
--------------------------------------------------------------------------------
/src/router/routes/default-routes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里的 defaultRoutes 是为了在一开始对接项目的时候,后端人员还没有准备好菜单接口,导致前端开发者不能进入主页面。
3 | * 所以这里返回默认的菜单数据,同时也向大家说明菜单数据的数据结构。后端的菜单接口一定要按这个格式去返回json数据,否则会解析菜单失败
4 | */
5 | export default [
6 | {
7 | menuUrl: '/index',
8 | menuName: 'Dashborad',
9 | routeName: 'dashborad',
10 | icon: 'icon-dashboard',
11 | parentPath: '',
12 | children: [
13 | {
14 | parentPath: '/index',
15 | menuUrl: '/index/home',
16 | menuName: '主控台',
17 | routeName: 'home',
18 | },
19 | {
20 | parentPath: '/index',
21 | menuUrl: '/index/work-place',
22 | menuName: '工作台',
23 | routeName: 'workPlace',
24 | },
25 | ],
26 | },
27 | {
28 | menuUrl: '/system',
29 | menuName: '系统管理',
30 | icon: 'icon-settings',
31 | parentPath: '',
32 | routeName: 'system',
33 | children: [
34 | {
35 | parentPath: '/system',
36 | menuUrl: '/system/department',
37 | menuName: '部门管理',
38 | routeName: 'department',
39 | localFilePath: '/system/local-path/department',
40 | },
41 | {
42 | parentPath: '/system',
43 | menuUrl: '/system/user',
44 | menuName: '用户管理',
45 | routeName: 'user',
46 | isRootPath: true,
47 | },
48 | {
49 | parentPath: '/system',
50 | menuUrl: '/system/role',
51 | menuName: '角色管理',
52 | },
53 | {
54 | parentPath: '/system',
55 | menuUrl: '/system/menu',
56 | menuName: '菜单管理',
57 | },
58 | ],
59 | },
60 | ]
61 |
--------------------------------------------------------------------------------
/src/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | {{ projectName }}
9 |
10 |
11 |
12 |
13 |
42 |
66 |
--------------------------------------------------------------------------------
/src/components/header/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
34 |
35 |
62 |
--------------------------------------------------------------------------------
/public/static/loading.css:
--------------------------------------------------------------------------------
1 | .first-loading-wrp {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | height: 90vh;
7 | min-height: 90vh;
8 | }
9 |
10 | .loading-wrp{
11 | display: flex;
12 | }
13 |
14 | .line{
15 | width: 5px;
16 | height: 50px;
17 | margin: 0 5px;
18 | border-radius: 30px;
19 | }
20 |
21 | .loading-wrp > span:nth-child(even) {
22 | background-color: #eb0f0f;
23 | animation: growAnim 0.4s linear infinite alternate-reverse;
24 | }
25 |
26 | .loading-wrp > span:nth-child(odd) {
27 | background-color: #18a058;
28 | animation: growAnimReverse 0.4s linear infinite alternate-reverse;
29 | }
30 |
31 | @keyframes growAnim {
32 | 0% {
33 | transform: scale(1, 0.3);
34 | -ms-transform: scale(1, 0.3);
35 | -webkit-transform: scale(1, 0.3);
36 | -moz-transform: scale(1, 0.3);
37 | -o-transform: scale(1, 0.3);
38 | }
39 | 100% {
40 | transform: scale(1, 1);
41 | -ms-transform: scale(1, 1);
42 | -webkit-transform: scale(1, 1);
43 | -moz-transform: scale(1, 1);
44 | -o-transform: scale(1, 1);
45 | }
46 | }
47 |
48 | @keyframes growAnimReverse {
49 | from {
50 | transform: scale(1, 1);
51 | -ms-transform: scale(1, 1);
52 | -webkit-transform: scale(1, 1);
53 | -moz-transform: scale(1, 1);
54 | -o-transform: scale(1, 1);
55 | }
56 | to {
57 | transform: scale(1, 0.3);
58 | -ms-transform: scale(1, 0.3);
59 | -webkit-transform: scale(1, 0.3);
60 | -moz-transform: scale(1, 0.3);
61 | -o-transform: scale(1, 0.3);
62 | }
63 | }
64 |
65 | .first-loading-wrp .title{
66 | font-size: 28px;
67 | font-weight: bold;
68 | text-align: center;
69 | margin-top: 50px;
70 | }
--------------------------------------------------------------------------------
/src/views/index/components/TodoItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
{{ item?.content }}
10 |
11 |
12 |
13 |
36 |
37 |
67 |
--------------------------------------------------------------------------------
/src/icons/data-money.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/index/components/DataItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ dataModel.title }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{ dataModel.data }}
17 |
18 |
19 |
20 |
21 |
22 |
23 | {{ dataModel.bottomTitle }}
24 | {{ dataModel.totalSum }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
55 |
56 |
62 |
--------------------------------------------------------------------------------
/src/components/common/SearchContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 搜索
8 |
9 |
10 |
11 |
12 |
13 |
14 |
46 |
47 |
72 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "admin-work",
3 | "version": "2.1.8",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite --host",
7 | "staging": "vite build --mode staging",
8 | "build": "vite build",
9 | "tsc": "vue-tsc --noEmit",
10 | "serve": "vite preview"
11 | },
12 | "dependencies": {
13 | "@vueuse/core": "^11.1.0",
14 | "axios": "^1.7.7",
15 | "clipboard": "^2.0.8",
16 | "echarts": "^5.1.2",
17 | "lodash-es": "^4.17.21",
18 | "mockjs": "^1.1.0",
19 | "path-browserify": "^1.0.1",
20 | "pinia": "^2.1.7",
21 | "print-js": "^1.6.0",
22 | "qrcode": "^1.4.4",
23 | "qs": "^6.10.1",
24 | "quill": "^1.3.7",
25 | "screenfull": "^5.1.0",
26 | "tiny-emitter": "^2.1.0",
27 | "vue": "^3.3.9",
28 | "vue-router": "^4.2.5",
29 | "vuedraggable": "^4.0.3",
30 | "xgplayer": "^2.28.0"
31 | },
32 | "devDependencies": {
33 | "@types/js-cookie": "^2.2.7",
34 | "@types/lodash-es": "^4.17.6",
35 | "@types/node": "*",
36 | "@types/path-browserify": "^1.0.0",
37 | "@types/qrcode": "^1.4.1",
38 | "@types/qs": "^6.9.7",
39 | "@types/quill": "^2.0.9",
40 | "@typescript-eslint/eslint-plugin": "^6.13.1",
41 | "@typescript-eslint/parser": "^6.13.1",
42 | "@vicons/ionicons5": "^0.10.0",
43 | "@vitejs/plugin-vue": "^5.0.3",
44 | "@vitejs/plugin-vue-jsx": "^3.1.0",
45 | "autoprefixer": "^10.4.7",
46 | "eslint": "^7.30.0",
47 | "eslint-config-prettier": "^8.3.0",
48 | "eslint-define-config": "^1.0.9",
49 | "eslint-plugin-prettier": "^3.4.0",
50 | "eslint-plugin-vue": "^7.13.0",
51 | "naive-ui": "^2.40.3",
52 | "postcss": "^8.4.13",
53 | "prettier": "^2.3.2",
54 | "sass": "^1.35.1",
55 | "tailwindcss": "^3.3.5",
56 | "typescript": "^5.3.2",
57 | "unplugin-vue-components": "^0.26.0",
58 | "vfonts": "^0.1.0",
59 | "vite": "^5.2.12",
60 | "vite-plugin-compression": "^0.5.1",
61 | "vite-plugin-svg-icons": "^2.0.1",
62 | "vue-tsc": "^0.3.0"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/views/exception/components/ExceptionStatus.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
{{ statusTip }}
8 |
9 | 返回首页
10 |
11 |
12 |
13 |
14 |
15 |
62 |
63 |
79 |
--------------------------------------------------------------------------------
/src/views/form/step-form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ activeStep === 1 || activeStep === 2 ? '请填写转账信息' : '转账结果' }}
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
52 |
53 |
67 |
--------------------------------------------------------------------------------
/src/components/common/RichTextEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
66 |
--------------------------------------------------------------------------------
/src/api/http.ts:
--------------------------------------------------------------------------------
1 | import { AxiosResponse } from 'axios'
2 | import { App } from 'vue'
3 | import request from './axios.config'
4 |
5 | export interface HttpOption {
6 | url: string
7 | data?: any
8 | method?: string
9 | headers?: any
10 | beforeRequest?: () => void
11 | afterRequest?: () => void
12 | }
13 |
14 | export interface Response {
15 | totalSize: number | 0
16 | code: number
17 | msg: string
18 | data: T
19 | }
20 |
21 | function http({ url, data, method, headers, beforeRequest, afterRequest }: HttpOption) {
22 | const successHandler = (res: AxiosResponse>) => {
23 | if (res.data.code === 200) {
24 | return res.data
25 | }
26 | throw new Error(res.data.msg || '请求失败,未知异常')
27 | }
28 | const failHandler = (error: Response) => {
29 | afterRequest && afterRequest()
30 | throw new Error(error.msg || '请求失败,未知异常')
31 | }
32 | beforeRequest && beforeRequest()
33 | method = method || 'GET'
34 | const params = Object.assign(typeof data === 'function' ? data() : data || {}, {})
35 | return method === 'GET'
36 | ? request.get(url, { params }).then(successHandler, failHandler)
37 | : request.post(url, params, { headers: headers }).then(successHandler, failHandler)
38 | }
39 |
40 | export function get({
41 | url,
42 | data,
43 | method = 'GET',
44 | beforeRequest,
45 | afterRequest,
46 | }: HttpOption): Promise> {
47 | return http({
48 | url,
49 | method,
50 | data,
51 | beforeRequest,
52 | afterRequest,
53 | })
54 | }
55 |
56 | export function post({
57 | url,
58 | data,
59 | method = 'POST',
60 | headers,
61 | beforeRequest,
62 | afterRequest,
63 | }: HttpOption): Promise> {
64 | return http({
65 | url,
66 | method,
67 | data,
68 | headers,
69 | beforeRequest,
70 | afterRequest,
71 | })
72 | }
73 |
74 | function install(app: App): void {
75 | app.config.globalProperties.$http = http
76 |
77 | app.config.globalProperties.$get = get
78 |
79 | app.config.globalProperties.$post = post
80 | }
81 |
82 | export default {
83 | install,
84 | get,
85 | post,
86 | }
87 |
--------------------------------------------------------------------------------
/src/store/plugin/persist.ts:
--------------------------------------------------------------------------------
1 | import { isObject } from '@vueuse/core'
2 | import { PiniaPluginContext } from 'pinia'
3 | import { toRaw } from 'vue'
4 |
5 | interface PresistType {
6 | enable: boolean
7 | option: Partial<{
8 | key: string
9 | storage: 'local' | 'session'
10 | include: (keyof S)[]
11 | exclude: (keyof S)[]
12 | }>
13 | resetToState?: ((store: Store) => void) | boolean
14 | }
15 |
16 | declare module 'pinia' {
17 | export interface DefineStoreOptionsBase {
18 | presist?: Partial>
19 | }
20 | }
21 |
22 | export default ({ options, store }: PiniaPluginContext) => {
23 | const presist = options.presist
24 | if (presist && isObject(presist) && presist.enable) {
25 | // 设置默认值
26 | !presist.option && (presist.option = {})
27 |
28 | const key = presist.option?.key || store.$id
29 | presist.option!.key = key
30 | const storage = presist.option?.storage || 'local'
31 | presist.option!.storage = storage
32 |
33 | // 恢复状态
34 | if (presist.resetToState) {
35 | if (typeof presist.resetToState === 'boolean') {
36 | const json = (window as any)[presist.option?.storage + 'Storage'].getItem(
37 | presist.option?.key
38 | )
39 | if (json) {
40 | store.$patch(JSON.parse(json))
41 | }
42 | } else if (typeof presist.resetToState === 'function') {
43 | presist.resetToState.call(presist, store)
44 | }
45 | }
46 |
47 | // 设置监听器
48 | store.$subscribe(
49 | (mutation, state) => {
50 | const toPersistObj = JSON.parse(JSON.stringify(toRaw(state)))
51 | if (presist.option?.include || presist.option?.exclude) {
52 | Object.keys(toPersistObj).forEach((it) => {
53 | if (
54 | (presist.option?.include && !presist.option?.include?.includes(it)) ||
55 | (presist.option?.exclude && presist.option?.exclude?.includes(it))
56 | ) {
57 | toPersistObj[it] = undefined
58 | }
59 | })
60 | }
61 | ;(window as any)[storage + 'Storage'].setItem(key, JSON.stringify(toPersistObj))
62 | },
63 | { detached: true }
64 | )
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/views/index/components/ProjectItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ item.title }}
6 |
7 |
{{ item.ui }}
8 |
9 | 预览地址
12 | 项目地址
13 |
14 |
15 |
16 |
17 |
18 |
50 |
51 |
85 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | node: true,
6 | es6: true,
7 | },
8 | parser: 'vue-eslint-parser',
9 | parserOptions: {
10 | parser: '@typescript-eslint/parser',
11 | ecmaVersion: 2020,
12 | sourceType: 'module',
13 | jsxPragma: 'React',
14 | ecmaFeatures: {
15 | jsx: true,
16 | },
17 | },
18 | extends: [
19 | 'plugin:vue/vue3-recommended',
20 | 'plugin:@typescript-eslint/recommended',
21 | 'prettier',
22 | 'plugin:prettier/recommended',
23 | ],
24 | rules: {
25 | '@typescript-eslint/ban-ts-ignore': 'off',
26 | '@typescript-eslint/explicit-function-return-type': 'off',
27 | '@typescript-eslint/no-explicit-any': 'off',
28 | '@typescript-eslint/no-var-requires': 'off',
29 | '@typescript-eslint/no-empty-function': 'off',
30 | 'vue/custom-event-name-casing': 'off',
31 | 'no-use-before-define': 'off',
32 | '@typescript-eslint/no-use-before-define': 'off',
33 | '@typescript-eslint/ban-ts-comment': 'off',
34 | '@typescript-eslint/ban-types': 'off',
35 | '@typescript-eslint/no-non-null-assertion': 'off',
36 | '@typescript-eslint/explicit-module-boundary-types': 'off',
37 | '@typescript-eslint/no-unused-vars': [
38 | 'error',
39 | {
40 | argsIgnorePattern: '^_',
41 | varsIgnorePattern: '^_',
42 | },
43 | ],
44 | 'no-unused-vars': [
45 | 'error',
46 | {
47 | argsIgnorePattern: '^_',
48 | varsIgnorePattern: '^_',
49 | },
50 | ],
51 | 'space-before-function-paren': 'off',
52 | 'vue/attributes-order': 'off',
53 | 'vue/one-component-per-file': 'off',
54 | 'vue/html-closing-bracket-newline': 'off',
55 | 'vue/max-attributes-per-line': 'off',
56 | 'vue/multiline-html-element-content-newline': 'off',
57 | 'vue/singleline-html-element-content-newline': 'off',
58 | 'vue/attribute-hyphenation': 'off',
59 | 'vue/require-default-prop': 'off',
60 | 'vue/script-setup-uses-vars': 'off',
61 | 'vue/html-self-closing': [
62 | 'error',
63 | {
64 | html: {
65 | void: 'always',
66 | normal: 'never',
67 | component: 'always',
68 | },
69 | svg: 'always',
70 | math: 'always',
71 | },
72 | ],
73 | },
74 | }
75 |
--------------------------------------------------------------------------------
/src/views/list/card-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 | {{ item.title }}
16 |
17 |
18 | {{ item.description }}
19 |
20 |
21 |
22 | {{ item.time }}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
67 |
68 |
73 |
--------------------------------------------------------------------------------
/src/components/common/TableFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
29 |
30 |
31 |
70 |
79 |
--------------------------------------------------------------------------------
/src/views/result/fail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 提交失败
10 | 请核对并修改以下信息后,再重新提交。
11 |
12 | 您提交的内容有如下错误:
13 | 您的账户已被冻结
15 | 立即解冻
18 |
19 | 您的账户还不具备申请资格
21 |
22 | 立即升级
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
40 |
41 |
78 |
--------------------------------------------------------------------------------
/src/styles/transition.css:
--------------------------------------------------------------------------------
1 | /* fade-transform */
2 | .fade-transform-leave-active,
3 | .fade-transform-enter-active {
4 | transition: all 0.5s;
5 | }
6 |
7 | .fade-transform-enter-from {
8 | opacity: 0;
9 | transform: translateX(-50px);
10 | }
11 |
12 | .fade-transform-leave-to {
13 | opacity: 0;
14 | transform: translateX(50px);
15 | }
16 |
17 | /* down-transform */
18 | .down-transform-leave-active,
19 | .down-transform-enter-active {
20 | transition: all 0.5s;
21 | }
22 |
23 | .down-transform-enter-from {
24 | opacity: 0;
25 | transform: translateY(-50px);
26 | }
27 |
28 | .down-transform-leave-to {
29 | opacity: 0;
30 | transform: translateY(50px);
31 | }
32 |
33 | /* scale-transform */
34 | .scale-transform-leave-active,
35 | .scale-transform-enter-active {
36 | transition: all 0.5s;
37 | }
38 |
39 | .scale-transform-enter-from {
40 | opacity: 0;
41 | transform: scale(2);
42 | }
43 |
44 | .scale-transform-leave-to {
45 | opacity: 0;
46 | transform: scale(0.5);
47 | }
48 |
49 | /* opacity-transform */
50 | .opacity-transform-leave-active,
51 | .opacity-transform-enter-active {
52 | transition: all 0.5s;
53 | }
54 |
55 | .opacity-transform-enter-from {
56 | opacity: 0;
57 | }
58 |
59 | .opacity-transform-leave-to {
60 | opacity: 0;
61 | }
62 |
63 | /* breadcrumb transition */
64 | .breadcrumb-enter-active,
65 | .breadcrumb-leave-active {
66 | transition: all 0.5s;
67 | }
68 |
69 | .breadcrumb-enter-from,
70 | .breadcrumb-leave-active {
71 | opacity: 0;
72 | transform: translateX(20px);
73 | }
74 |
75 | .breadcrumb-move {
76 | transition: all 0.5s;
77 | }
78 |
79 | .breadcrumb-leave-active {
80 | position: absolute;
81 | }
82 |
83 | /* header transition */
84 | .header-enter-active,
85 | .header-leave-active {
86 | transition: all 0.5s;
87 | }
88 |
89 | .header-enter-from,
90 | .header-leave-active {
91 | opacity: 0;
92 | transform: translateX(100%);
93 | }
94 |
95 | .header-move {
96 | transition: all 0.5s;
97 | }
98 |
99 | /* logo transition */
100 | .logo-enter-active,
101 | .logo-leave-active {
102 | transition: all 0.5s;
103 | }
104 |
105 | .logo-enter-from,
106 | .logo-leave-active {
107 | opacity: 0;
108 | transform: translateY(-100%);
109 | }
110 |
111 | .logo-move {
112 | transition: all 0.5s;
113 | }
114 |
--------------------------------------------------------------------------------
/src/types/components.ts:
--------------------------------------------------------------------------------
1 | import { MessageApi, TreeSelectOption } from 'naive-ui'
2 | import { Ref, VNode } from 'vue'
3 |
4 | export interface HeaderCellStyle {
5 | backgroundColor?: string
6 | color: string
7 | }
8 |
9 | export interface TableConfig {
10 | dataList: Array
11 | stripe: boolean
12 | border: boolean
13 | size: string
14 | headerCellStyle: HeaderCellStyle
15 | height: string | number
16 | tableLoading: boolean
17 | }
18 |
19 | export interface SelectOptionItem {
20 | label: string
21 | value: any
22 | }
23 |
24 | export interface TableSearchItem {
25 | key: string | number
26 | label: string
27 | value: any
28 | placeholder?: string
29 | associatedOption?: string
30 | onChange?: (value: any, associationItem: string) => void
31 | span?: number
32 | }
33 |
34 | export interface FormItem extends TableSearchItem {
35 | required?: boolean
36 | validator?: (value: FormItem, message: MessageApi) => boolean
37 | hidden?: boolean
38 | inputType?: string
39 | maxLength?: number
40 | rows?: number
41 | disabled?: Ref | boolean
42 | optionItems?: Array
43 | path?: string
44 | reset?: (formItem: FormItem) => void
45 | render?: (formItem: FormItem) => VNode
46 | }
47 |
48 | export interface LikeSearchModel {
49 | conditionItems: Array | null
50 | extraParams?: (() => Record) | Record
51 | }
52 |
53 | export interface TablePropsType {
54 | title: string
55 | key: string
56 | sortIndex: number
57 | checked: Ref
58 | }
59 |
60 | // export type ModalDialogType = InstanceType
61 |
62 | export type ModalDialogType = InstanceType<
63 | typeof import('../components/common/ModalDialog.vue').default
64 | >
65 |
66 | export type DataFormType = InstanceType
67 |
68 | export type TableHeaderType = InstanceType<
69 | typeof import('../components/common/TableHeader.vue').default
70 | >
71 |
72 | export type TableFooterType = InstanceType<
73 | typeof import('../components/common/TableFooter.vue').default
74 | >
75 |
76 | export type SvgIconType = InstanceType
77 |
78 | export type SearchContentType = InstanceType<
79 | typeof import('../components/common/SearchContent.vue').default
80 | >
81 |
--------------------------------------------------------------------------------
/src/hooks/useDialogDragger.ts:
--------------------------------------------------------------------------------
1 | const range = {
2 | left: 0,
3 | right: 0,
4 | top: 0,
5 | bottom: 0,
6 | }
7 |
8 | const listeners: { name: string; listener: (e: MouseEvent) => void }[] = []
9 |
10 | export function drag(wrap: HTMLElement) {
11 | wrap.style.cursor = 'move'
12 | const parent = wrap.parentElement
13 | if (!parent) {
14 | return
15 | }
16 | let startX = 0
17 | let startY = 0
18 | let status = ''
19 | wrap.addEventListener('mousedown', (e: MouseEvent) => {
20 | e.preventDefault()
21 | status = 'down'
22 | range.left = -((document.documentElement.clientWidth - parent.clientWidth) / 2)
23 | range.right = Math.abs(range.left)
24 | range.top = -(document.documentElement.clientHeight - parent.clientHeight) / 2
25 | range.bottom = Math.abs(range.top)
26 | startX = e.clientX - (parseInt(parent.style.left) || 0)
27 | startY = e.clientY - (parseInt(parent.style.top) || 0)
28 | const handleMove = (e: MouseEvent) => {
29 | if (status !== 'down') return
30 | const moveX = e.clientX
31 | const moveY = e.clientY
32 | let distX = moveX - startX
33 | let distY = moveY - startY
34 | if (distX <= range.left) {
35 | distX = range.left
36 | }
37 | if (distX >= range.right) {
38 | distX = range.right
39 | }
40 | if (distY <= range.top) {
41 | distY = range.top
42 | }
43 | if (distY >= range.bottom) {
44 | distY = range.bottom
45 | }
46 | parent.style.left = distX + 'px'
47 | parent.style.top = distY + 'px'
48 | }
49 | const handleUp = () => {
50 | status = 'up'
51 | document.removeEventListener('mousemove', handleMove)
52 | document.removeEventListener('mouseup', handleUp)
53 | }
54 | listeners.push(
55 | {
56 | name: 'mousemove',
57 | listener: handleMove,
58 | },
59 | {
60 | name: 'mouseup',
61 | listener: handleUp,
62 | }
63 | )
64 | document.addEventListener('mousemove', handleMove)
65 | document.addEventListener('mouseup', handleUp)
66 | wrap.addEventListener('mouseup', () => {
67 | handleUp()
68 | })
69 | })
70 | }
71 |
72 | export function unDrag(wrap: HTMLElement) {
73 | listeners.forEach((it: any) => {
74 | wrap.removeEventListener(it.name, it.listener)
75 | })
76 | listeners.length = 0
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/common/PopoverMessage.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
{{ item.title }}
21 |
{{ item.desc }}
22 |
23 |
24 |
25 |
26 |
27 |
28 | 清空消息
29 | 查看更多
30 |
31 |
32 |
33 |
34 |
35 |
80 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { DataTableColumn, TreeSelectOption } from 'naive-ui'
2 | import { TablePropsType } from '@/types/components'
3 |
4 | export function isExternal(path: string) {
5 | return /^(https?:|mailto:|tel:)/.test(path)
6 | }
7 |
8 | export function uuid() {
9 | const s: Array = []
10 | const hexDigits = '0123456789abcdef'
11 | for (let i = 0; i < 36; i++) {
12 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
13 | }
14 | s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
15 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
16 | s[8] = s[13] = s[18] = s[23] = '-'
17 | const uuid = s.join('')
18 | return uuid
19 | }
20 |
21 | export function randomString(length: number) {
22 | const str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
23 | let result = ''
24 | for (let i = length; i > 0; --i) {
25 | result += str[Math.floor(Math.random() * str.length)]
26 | }
27 | return result
28 | }
29 |
30 | /**
31 | * 中划线字符驼峰
32 | * @param {*} str 要转换的字符串
33 | * @returns 返回值
34 | */
35 | export function toHump(str: string): string {
36 | if (!str) return str
37 | return str
38 | .replace(/\-(\w)/g, function (all, letter) {
39 | return letter.toUpperCase()
40 | })
41 | .replace(/(\s|^)[a-z]/g, function (char) {
42 | return char.toUpperCase()
43 | })
44 | }
45 |
46 | export function sortColumns(originColumns: DataTableColumn[], newColumns: TablePropsType[]) {
47 | if (!originColumns || !newColumns) {
48 | return
49 | }
50 | if (newColumns.length === 0) {
51 | originColumns.length = 0
52 | } else {
53 | const selectionItem = originColumns.find((it) => it.type === 'selection')
54 | originColumns.length = 0
55 | if (selectionItem) {
56 | originColumns.push(selectionItem)
57 | }
58 | originColumns.push(...newColumns)
59 | }
60 | }
61 |
62 | export function transformTreeSelect(
63 | origin: any[],
64 | labelName: string,
65 | keyName: string
66 | ): TreeSelectOption[] {
67 | const tempSelections: TreeSelectOption[] = []
68 | origin.forEach((it) => {
69 | const selection = {
70 | label: it[labelName],
71 | key: it[keyName],
72 | } as TreeSelectOption
73 | if (it.children) {
74 | selection.children = transformTreeSelect(it.children, labelName, keyName)
75 | }
76 | tempSelections.push(selection)
77 | })
78 | return tempSelections
79 | }
80 |
--------------------------------------------------------------------------------
/src/store/types.ts:
--------------------------------------------------------------------------------
1 | import { Ref, UnwrapRef } from 'vue'
2 | import { RouteMeta, RouteRecordNormalized, RouteRecordRaw } from 'vue-router'
3 |
4 | export enum LayoutMode {
5 | LTR = 'ltr',
6 | LCR = 'lcr',
7 | TTB = 'ttb',
8 | }
9 |
10 | export enum DeviceType {
11 | PC = 'pc',
12 | PAD = 'pad',
13 | MOBILE = 'mobile',
14 | }
15 |
16 | export enum ThemeMode {
17 | LIGHT = 'light',
18 | DARK = 'dark',
19 | }
20 |
21 | export enum SideTheme {
22 | DARK = 'dark',
23 | WHITE = 'white',
24 | BLUE = 'blue',
25 | IMAGE = 'image',
26 | }
27 |
28 | export enum PageAnim {
29 | FADE = 'fade',
30 | OPACITY = 'opacity',
31 | DOWN = 'down',
32 | SCALE = 'scale',
33 | }
34 |
35 | export interface UserState {
36 | userId: number
37 | token: string
38 | roleId: number
39 | roles: string[] | null
40 | userName: string
41 | nickName: string
42 | avatar: string
43 | }
44 |
45 | export interface AppConfigState {
46 | projectName: string
47 | theme: ThemeMode
48 | sideTheme: SideTheme
49 | themeColor: string
50 | layoutMode: LayoutMode
51 | deviceType: DeviceType
52 | sideWidth: number
53 | pageAnim: PageAnim
54 | isFixedNavBar: boolean
55 | isCollapse: boolean
56 | actionBar: {
57 | isShowSearch: boolean
58 | isShowMessage: boolean
59 | isShowRefresh: boolean
60 | isShowFullScreen: boolean
61 | }
62 | }
63 |
64 | export interface VisitedRouteState {
65 | visitedRoutes: RouteRecordNormalized[]
66 | affixRoutes: RouteRecordNormalized[]
67 | }
68 |
69 | export interface CachedRouteState {
70 | cachedRoutes: string[]
71 | }
72 |
73 | export interface OriginRoute {
74 | parentPath?: string
75 | menuUrl: string
76 | menuName?: string
77 | routeName?: string
78 | hidden?: boolean
79 | outLink?: string
80 | affix?: boolean
81 | cacheable?: boolean
82 | isRootPath?: boolean
83 | iconPrefix?: string
84 | icon?: string
85 | badge?: string | number
86 | isSingle?: boolean
87 | localFilePath?: string
88 | children?: Array
89 | }
90 |
91 | export interface RouteMetaType extends RouteMeta {
92 | icon?: string
93 | title?: string
94 | cacheable?: boolean
95 | affix?: boolean
96 | }
97 |
98 | export interface SplitTab {
99 | label: string
100 | iconPrefix?: string | unknown
101 | icon: string
102 | fullPath: string
103 | children?: Array
104 | checked: Ref>
105 | }
106 |
--------------------------------------------------------------------------------
/src/views/other/chart/components/IconFont.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
{{ item.font_class }}
8 |
复制
9 |
10 |
11 |
12 |
13 |
14 |
15 |
43 |
44 |
82 |
--------------------------------------------------------------------------------
/src/components/sidebar/components/HorizontalScrollerMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
66 |
67 |
85 |
--------------------------------------------------------------------------------
/src/icons/data-chart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/index/components/chart/OrderChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
89 |
--------------------------------------------------------------------------------
/src/views/project/infomation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 当前版本号:{{ version }}
6 | 获取源码
7 |
8 |
9 |
10 |
16 |
17 | {{ item.value }}
18 |
19 |
20 |
21 |
22 |
28 |
33 | {{ item.value }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
关注公众号:知码前端 获取源码
47 |
48 |
49 |
50 |
51 |
52 |
53 |
78 |
--------------------------------------------------------------------------------
/src/components/common/CitySelector.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
83 |
--------------------------------------------------------------------------------
/mock/user/index.js:
--------------------------------------------------------------------------------
1 | import { getMenuListByRoleId, getAllMenuByRoleId, login, updateUserInfo } from '@/api/url'
2 | import { randomString } from '@/utils'
3 | import Mock from 'mockjs'
4 | import { baseData } from '../base.ts'
5 | import { adminRoutes, editorRoutes } from '../router'
6 |
7 | Mock.mock(RegExp(login), 'post', function (options) {
8 | const username = JSON.parse(options.body).username
9 | const data = {}
10 | if (username === 'admin') {
11 | baseData.code = 200
12 | baseData.msg = '登录成功'
13 | data.nickName = '超级管理员'
14 | data.userName = 'admin'
15 | data.userId = 1
16 | data.roleId = 1
17 | data.token = randomString(100)
18 | data.roles = [
19 | {
20 | roleCode: 'ROLE_admin',
21 | roleId: 1,
22 | roleName: '超级管理员',
23 | },
24 | ]
25 | baseData.data = data
26 | } else if (username === 'editor') {
27 | baseData.code = 200
28 | baseData.msg = '登录成功'
29 | data.nickName = '编辑员'
30 | data.userName = 'editor'
31 | data.userId = 2
32 | data.roleId = 2
33 | data.token = randomString(100)
34 | data.roles = [
35 | {
36 | roleCode: 'ROLE_editor',
37 | roleId: 2,
38 | roleName: '网站编辑人员',
39 | },
40 | ]
41 | baseData.data = data
42 | } else {
43 | baseData.code = 500
44 | baseData.data = ''
45 | baseData.msg = '用户名或密码错误'
46 | }
47 | return Mock.mock(baseData)
48 | })
49 |
50 | Mock.mock(RegExp(getAllMenuByRoleId), 'post', function (options) {
51 | const roleId = JSON.parse(options.body).roleId || ''
52 | if (!roleId) {
53 | return Mock.mock({ code: 500, data: '', msg: '获取菜单列表失败' })
54 | }
55 | const allRoutes = [...adminRoutes]
56 | allRoutes.forEach((it) => {
57 | it.isSelect = parseInt(roleId) === 1 || it.menuUrl.indexOf('authority') === -1
58 | it.children.forEach((child) => {
59 | child.isSelect = parseInt(roleId) === 1 || child.menuUrl.indexOf('authority') === -1
60 | })
61 | })
62 | return Mock.mock({ code: 200, data: allRoutes, msg: '获取菜单列表成功' })
63 | })
64 |
65 | Mock.mock(RegExp(getMenuListByRoleId), 'post', function (options) {
66 | const roleId = JSON.parse(options.body).roleId || ''
67 | if (!roleId) {
68 | return Mock.mock({ code: 500, data: '', msg: '获取菜单列表失败' })
69 | }
70 | if (parseInt(roleId) === 1) {
71 | // 超级管理员
72 | return Mock.mock({ code: 200, data: adminRoutes, msg: '获取菜单列表成功' })
73 | } else if (parseInt(roleId) === 2) {
74 | // 编辑
75 | return Mock.mock({
76 | code: 200,
77 | data: editorRoutes,
78 | msg: '获取菜单列表成功',
79 | })
80 | } else {
81 | return Mock.mock({
82 | code: 500,
83 | data: '',
84 | msg: '目前仅支持超级管理员和编辑人员菜单',
85 | })
86 | }
87 | })
88 |
89 | Mock.mock(RegExp(updateUserInfo), 'post', function () {
90 | return Mock.mock({ ...baseData, msg: '更新信息成功' })
91 | })
92 |
--------------------------------------------------------------------------------
/src/views/form/components/ResultInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
操作成功
9 |
预计两个小时到账
10 |
11 |
12 |
13 | 付款账户:
14 | {{ accountInfo.account }}
15 |
16 |
17 | 收款账户:
18 | {{ accountInfo.otherAccount }}
19 |
20 |
21 | 收款人姓名:
22 |
23 | {{ accountInfo.receiveName }}
24 |
25 |
26 |
27 | 转账金额:
28 |
29 | {{ '¥' + Number(accountInfo.money).toFixed(2) }}
30 |
31 |
32 |
33 |
34 |
35 |
36 | 再转一笔
37 | 查看订单
38 |
39 |
40 |
41 |
42 |
43 |
66 |
67 |
103 |
--------------------------------------------------------------------------------
/src/store/modules/permission.ts:
--------------------------------------------------------------------------------
1 | import { RouteRecordRaw } from 'vue-router'
2 | import { defineStore } from 'pinia'
3 | import useUserStore from './user'
4 | import router from '@/router'
5 | import { baseAddress, getMenuListByRoleId } from '@/api/url'
6 | import { post } from '@/api/http'
7 | import defaultRoutes from '@/router/routes/default-routes'
8 | import { findRootPathRoute, generatorRoutes, mapTwoLevelRouter } from '../help'
9 | import { constantRoutes } from '@/router/routes/constants'
10 |
11 | const usePermissionStore = defineStore('permission-route', {
12 | state: () => {
13 | return {
14 | permissionRoutes: [] as RouteRecordRaw[],
15 | }
16 | },
17 | getters: {
18 | getPermissionSideBar(state) {
19 | return state.permissionRoutes.filter((it) => {
20 | return it.meta && !it.meta.hidden
21 | })
22 | },
23 | getPermissionSplitTabs(state) {
24 | return state.permissionRoutes.filter((it) => {
25 | return it.meta && !it.meta.hidden && it.children && it.children.length > 0
26 | })
27 | },
28 | },
29 | actions: {
30 | async getRoutes(data: { userId: number; roleId: number }) {
31 | try {
32 | if (getMenuListByRoleId) {
33 | const res = await post({
34 | url: baseAddress + getMenuListByRoleId,
35 | // 在实际的开发中,这个地方可以换成 token,让后端解析用户信息获取 userId 和 roleId,前端可以不用传 userId 和 roleId。
36 | // 这样可以增加安全性
37 | data,
38 | })
39 | return generatorRoutes(res.data)
40 | } else {
41 | return generatorRoutes(defaultRoutes)
42 | }
43 | } catch (error) {
44 | console.log(
45 | '路由加载失败了,请清空一下Cookie和localStorage,重新登录;如果已经采用真实接口的,请确保菜单接口地址真实可用并且返回的数据格式和mock中的一样'
46 | )
47 | return []
48 | }
49 | },
50 | async initPermissionRoute() {
51 | const userStore = useUserStore()
52 | // 加载路由
53 | const accessRoutes = await this.getRoutes({
54 | roleId: userStore.roleId,
55 | userId: userStore.userId,
56 | })
57 | const mapRoutes = mapTwoLevelRouter(accessRoutes)
58 | mapRoutes.forEach((it: any) => {
59 | router.addRoute(it)
60 | })
61 | // 配置 `/` 路由的默认跳转地址
62 | router.addRoute({
63 | path: '/',
64 | redirect: findRootPathRoute(accessRoutes),
65 | meta: {
66 | hidden: true,
67 | },
68 | })
69 | // 这个路由一定要放在最后
70 | router.addRoute({
71 | path: '/:pathMatch(.*)*',
72 | redirect: '/404',
73 | meta: {
74 | hidden: true,
75 | },
76 | })
77 | this.permissionRoutes = [...constantRoutes, ...accessRoutes]
78 | },
79 | isEmptyPermissionRoute() {
80 | return !this.permissionRoutes || this.permissionRoutes.length === 0
81 | },
82 | reset() {
83 | this.$reset()
84 | },
85 | },
86 | })
87 |
88 | export default usePermissionStore
89 |
--------------------------------------------------------------------------------
/src/views/other/print.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 打印
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 | 打印
19 |
20 |
21 |
22 |
23 |
24 | 姓名
25 | 年龄
26 | 性别
27 |
28 |
29 |
30 |
31 | {{ item.name }}
32 | {{ item.age }}
33 | {{ item.gender }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
88 |
89 |
105 |
--------------------------------------------------------------------------------
/src/views/other/css-animation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{
7 | item.label
8 | }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
91 |
92 |
108 |
--------------------------------------------------------------------------------
/src/views/editor/rich-text.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
16 |
17 |
18 |
19 | 设置(回显)数据
21 |
22 | 获取HTML
23 | 获取JSON
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{ jsonContent }}
32 |
33 |
34 |
35 |
36 |
73 |
74 |
96 |
--------------------------------------------------------------------------------
/src/views/other/chart/components/xicons.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
{{ item }}
10 |
复制
11 |
12 |
13 |
14 |
15 | 加载更多
16 |
17 |
18 |
19 |
20 |
57 |
58 |
96 |
--------------------------------------------------------------------------------
/src/views/form/components/PasswordInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 | {{ accountInfo.account }}
12 |
13 |
14 | {{ accountInfo.otherAccount }}
15 |
16 |
17 |
18 | {{ accountInfo.receiveName }}
19 |
20 |
21 |
22 |
23 | {{ '¥' + Number(accountInfo.money).toFixed(2) }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 上一步
32 |
33 | 下一步
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
87 |
88 |
102 |
--------------------------------------------------------------------------------
/src/components/common/IconSelector.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ selectItem.name }}
12 |
13 |
14 |
15 |
16 |
17 |
21 |
26 | {{ item.name }}
27 |
28 |
29 |
30 |
31 |
32 |
39 |
40 |
41 |
42 |
43 |
88 |
96 |
--------------------------------------------------------------------------------
/src/components/setting/components/StyleExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
{{ tipText }}
9 |
10 |
11 |
12 |
41 |
42 |
117 |
--------------------------------------------------------------------------------
/src/components/common/TableHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 重置
49 | 搜索
50 | 关闭
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
104 |
--------------------------------------------------------------------------------
/src/views/index/components/chart/EnrollmentChannelsChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | 招生渠道分析图
11 |
12 |
13 |
19 |
20 |
21 |
22 |
99 |
100 |
108 |
--------------------------------------------------------------------------------
/src/components/common/ModalDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 取消
25 | 确定
26 |
27 |
28 |
29 |
30 |
31 |
32 |
112 |
--------------------------------------------------------------------------------
/src/views/index/components/chart/SalesChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | 一周销售额(单位:万)
11 |
12 |
13 |
19 |
20 |
21 |
22 |
101 |
102 |
110 |
--------------------------------------------------------------------------------
/src/views/index/components/chart/StudentChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | 半年招生对比图(单位:人)
11 |
12 |
13 |
19 |
20 |
21 |
22 |
102 |
103 |
111 |
--------------------------------------------------------------------------------
/src/views/other/city-selector.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 城市列表所需要的数据源在 src/assets/data/ 文件夹下面:
5 |
6 |
7 | src/assets/data/provinces.json:单独的省份列表
11 |
12 | src/assets/data/cities.json:单独的城市列表
16 |
17 | src/assets/data/areas.json:单独的地区列表
21 |
22 | src/assets/data/pc-code.json:级联的省市列表
26 |
27 | src/assets/data/pca-code.json:级联的省市区列表
31 |
32 | src/assets/data/pcas-code.json:级联的省市区、街道列表
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 当前选择的城市为:{{ JSON.stringify(currentItem1) }}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 当前选择的城市为:{{ JSON.stringify(currentItem2) }}
53 |
54 |
55 |
56 |
61 |
67 |
73 |
74 | 当前选择的城市为:{{ JSON.stringify(currentItem3) }}
75 |
76 |
77 |
78 |
79 |
97 |
--------------------------------------------------------------------------------
/src/components/avatar/VAWAvatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ userStore.nickName }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
85 |
86 |
123 |
--------------------------------------------------------------------------------
/src/views/index/components/chart/DepartmentChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | 公司各部门人员数量
11 |
12 |
13 |
19 |
20 |
21 |
22 |
108 |
109 |
117 |
--------------------------------------------------------------------------------
/src/views/draggable/card-draggable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 重置元素位置
6 |
7 |
15 |
16 |
17 |
23 | {{ element.label }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
98 |
99 |
132 |
--------------------------------------------------------------------------------
/src/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
36 |
63 |
150 |
--------------------------------------------------------------------------------
/src/views/list/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
19 |
20 |
21 |
22 |
时间
23 |
{{ item.time }}
24 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
77 |
78 |
127 |
--------------------------------------------------------------------------------
/src/components/common/SortableTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 | 全选
11 |
12 | 重置
13 |
14 |
21 |
22 |
23 |
28 | {{ element.title }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
115 |
--------------------------------------------------------------------------------
/src/components/sidebar/components/ScrollerMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
98 |
99 |
122 |
--------------------------------------------------------------------------------
/src/components/breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 | {{ item.label }}
12 |
13 |
14 |
15 |
16 |
17 | {{ item.label }}
18 |
19 |
20 |
21 |
22 |
23 |
125 |
--------------------------------------------------------------------------------