├── server
├── myapp
│ ├── __init__.py
│ ├── auth
│ │ ├── __init__.py
│ │ └── authentication.py
│ ├── middlewares
│ │ ├── __init__.py
│ │ └── LogMiddleware.py
│ ├── permission
│ │ ├── __init__.py
│ │ └── permission.py
│ ├── views
│ │ ├── __init__.py
│ │ └── admin
│ │ │ ├── opLog.py
│ │ │ ├── errorLog.py
│ │ │ ├── __init__.py
│ │ │ ├── loginLog.py
│ │ │ ├── banner.py
│ │ │ ├── notice.py
│ │ │ ├── worker.py
│ │ │ ├── family.py
│ │ │ ├── medical.py
│ │ │ ├── tag.py
│ │ │ ├── classification.py
│ │ │ ├── overview.py
│ │ │ └── user.py
│ ├── tests.py
│ ├── apps.py
│ ├── admin.py
│ ├── handler.py
│ ├── utils.py
│ ├── urls.py
│ └── serializers.py
├── upload
│ ├── cover
│ │ ├── 1.jpeg
│ │ └── 1.jpg
│ ├── ad
│ │ ├── 1674045266113.jpeg
│ │ ├── 1674045282581.jpeg
│ │ ├── 1674045308177.png
│ │ ├── 1674045324510.jpeg
│ │ ├── 1684565423182.jpeg
│ │ ├── 1684565863904.jpeg
│ │ └── 1684565876995.png
│ └── banner
│ │ └── 1673963977440.jpeg
├── server
│ ├── __init__.py
│ ├── asgi.py
│ ├── wsgi.py
│ ├── urls.py
│ └── settings.py
├── requirements.txt
├── manage.py
└── readme.md
├── web
├── src
│ ├── assets
│ │ ├── styles
│ │ │ └── base.less
│ │ ├── icons
│ │ │ ├── logo.png
│ │ │ └── svg
│ │ │ │ ├── marks.svg
│ │ │ │ ├── twitter.svg
│ │ │ │ ├── github.svg
│ │ │ │ ├── test.svg
│ │ │ │ └── ts.svg
│ │ ├── images
│ │ │ ├── Play.png
│ │ │ ├── login.png
│ │ │ ├── logo.png
│ │ │ ├── avatar.jpg
│ │ │ ├── k-logo.png
│ │ │ ├── banner-02.webp
│ │ │ ├── qunerweima.jpg
│ │ │ ├── login-banner.png
│ │ │ ├── tel-icon.svg
│ │ │ ├── address-right-icon.svg
│ │ │ ├── searchIcon.svg
│ │ │ ├── search-icon.svg
│ │ │ ├── add.svg
│ │ │ ├── order-point-icon.svg
│ │ │ ├── pwd-icon.svg
│ │ │ ├── setting-msg-icon.svg
│ │ │ ├── register-name.svg
│ │ │ ├── clear-search.svg
│ │ │ ├── mail-icon.svg
│ │ │ ├── order-address-icon.svg
│ │ │ ├── message-icon.svg
│ │ │ ├── delete-icon.svg
│ │ │ ├── ebook-download-icon.svg
│ │ │ ├── code-icon.svg
│ │ │ ├── read-online-icon.svg
│ │ │ ├── share-icon.svg
│ │ │ ├── ali-pay-icon.svg
│ │ │ ├── wx-pay-icon.svg
│ │ │ ├── order-thing-icon.svg
│ │ │ ├── order-icon.svg
│ │ │ ├── want-read-hover.svg
│ │ │ ├── pwd-hidden.svg
│ │ │ ├── setting-icon.svg
│ │ │ ├── setting-safe-icon.svg
│ │ │ ├── cart-icon.svg
│ │ │ ├── recommend-hover.svg
│ │ │ ├── setting-card-icon.svg
│ │ │ ├── wb-share.svg
│ │ │ ├── banner2.svg
│ │ │ └── setting-push-icon.svg
│ │ └── fonts
│ │ │ ├── Blimone-Light.woff
│ │ │ ├── Blimone-Regular.woff
│ │ │ ├── Blimone-ExtraBold.woff
│ │ │ └── Blimone-ExtraLight.woff
│ ├── styles
│ │ ├── index.less
│ │ └── reset.less
│ ├── store
│ │ ├── modules
│ │ │ ├── user
│ │ │ │ ├── types.ts
│ │ │ │ └── index.ts
│ │ │ └── app
│ │ │ │ ├── types.ts
│ │ │ │ └── index.ts
│ │ ├── index.js
│ │ └── constants.js
│ ├── App.vue
│ ├── main.js
│ ├── utils
│ │ ├── http
│ │ │ └── axios
│ │ │ │ ├── type.ts
│ │ │ │ ├── status.ts
│ │ │ │ └── index.ts
│ │ ├── auth.ts
│ │ ├── result.ts
│ │ └── index.ts
│ ├── api
│ │ ├── overview.ts
│ │ ├── log.ts
│ │ ├── classification.ts
│ │ ├── family.ts
│ │ ├── notice.ts
│ │ ├── worker.ts
│ │ ├── medical.ts
│ │ ├── tag.ts
│ │ ├── thing.ts
│ │ ├── comment.ts
│ │ └── user.ts
│ ├── core
│ │ └── bootstrap.js
│ ├── router
│ │ ├── index.js
│ │ └── root.js
│ └── views
│ │ ├── sys-info.vue
│ │ ├── login-log.vue
│ │ ├── error-log.vue
│ │ ├── op-log.vue
│ │ ├── admin-login.vue
│ │ ├── main.vue
│ │ ├── classification.vue
│ │ └── tag.vue
├── .stylelintignore
├── postcss.config.js
├── public
│ ├── favicon.ico
│ └── images
│ │ ├── bg2.jpg
│ │ ├── demo.jpg
│ │ └── admin-login-bg.jpg
├── .eslintignore
├── build
│ ├── constant.ts
│ └── vite
│ │ └── plugins
│ │ ├── progress.ts
│ │ ├── unocss.ts
│ │ ├── restart.ts
│ │ ├── visualizer.ts
│ │ ├── compress.ts
│ │ ├── autoImport.ts
│ │ ├── imagemin.ts
│ │ ├── component.ts
│ │ └── index.ts
├── prettier.config.js
├── types
│ ├── env.d.ts
│ ├── components.d.ts
│ └── auto-imports.d.ts
├── .gitignore
├── index.html
├── tsconfig.json
├── vite.config.ts
├── package.json
├── .eslintrc.js
├── README.md
└── stylelint.config.js
├── .gitignore
└── Readme.md
/server/myapp/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/myapp/auth/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/styles/base.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/myapp/middlewares/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/myapp/permission/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/.stylelintignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | /public/*
3 | public/*
4 |
--------------------------------------------------------------------------------
/server/myapp/views/__init__.py:
--------------------------------------------------------------------------------
1 | from myapp.views.admin import *
2 |
--------------------------------------------------------------------------------
/web/src/styles/index.less:
--------------------------------------------------------------------------------
1 | //自定义css
2 | a {
3 | color: #1890ff;
4 | }
5 |
--------------------------------------------------------------------------------
/server/myapp/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/public/favicon.ico
--------------------------------------------------------------------------------
/server/upload/cover/1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/cover/1.jpeg
--------------------------------------------------------------------------------
/server/upload/cover/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/cover/1.jpg
--------------------------------------------------------------------------------
/web/public/images/bg2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/public/images/bg2.jpg
--------------------------------------------------------------------------------
/web/public/images/demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/public/images/demo.jpg
--------------------------------------------------------------------------------
/web/src/assets/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/icons/logo.png
--------------------------------------------------------------------------------
/web/src/assets/images/Play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/Play.png
--------------------------------------------------------------------------------
/web/src/assets/images/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/login.png
--------------------------------------------------------------------------------
/web/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/logo.png
--------------------------------------------------------------------------------
/web/src/assets/images/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/avatar.jpg
--------------------------------------------------------------------------------
/web/src/assets/images/k-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/k-logo.png
--------------------------------------------------------------------------------
/server/upload/ad/1674045266113.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1674045266113.jpeg
--------------------------------------------------------------------------------
/server/upload/ad/1674045282581.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1674045282581.jpeg
--------------------------------------------------------------------------------
/server/upload/ad/1674045308177.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1674045308177.png
--------------------------------------------------------------------------------
/server/upload/ad/1674045324510.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1674045324510.jpeg
--------------------------------------------------------------------------------
/server/upload/ad/1684565423182.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1684565423182.jpeg
--------------------------------------------------------------------------------
/server/upload/ad/1684565863904.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1684565863904.jpeg
--------------------------------------------------------------------------------
/server/upload/ad/1684565876995.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/ad/1684565876995.png
--------------------------------------------------------------------------------
/web/public/images/admin-login-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/public/images/admin-login-bg.jpg
--------------------------------------------------------------------------------
/web/src/assets/images/banner-02.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/banner-02.webp
--------------------------------------------------------------------------------
/web/src/assets/images/qunerweima.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/qunerweima.jpg
--------------------------------------------------------------------------------
/server/server/__init__.py:
--------------------------------------------------------------------------------
1 | import pymysql
2 | pymysql.install_as_MySQLdb()
3 |
4 | print("===============install pymysql==============")
--------------------------------------------------------------------------------
/web/src/assets/images/login-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/images/login-banner.png
--------------------------------------------------------------------------------
/server/upload/banner/1673963977440.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/server/upload/banner/1673963977440.jpeg
--------------------------------------------------------------------------------
/web/src/assets/fonts/Blimone-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/fonts/Blimone-Light.woff
--------------------------------------------------------------------------------
/web/src/assets/fonts/Blimone-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/fonts/Blimone-Regular.woff
--------------------------------------------------------------------------------
/web/src/assets/fonts/Blimone-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/fonts/Blimone-ExtraBold.woff
--------------------------------------------------------------------------------
/web/src/assets/fonts/Blimone-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geeeeeeeek/python_yanglao/HEAD/web/src/assets/fonts/Blimone-ExtraLight.woff
--------------------------------------------------------------------------------
/server/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==3.2.11
2 | PyMySQL==1.0.2
3 | djangorestframework==3.13.0
4 | django-cors-headers==3.13.0
5 | Pillow==9.1.1
6 | psutil==5.9.4
--------------------------------------------------------------------------------
/server/myapp/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MyappConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'myapp'
7 |
--------------------------------------------------------------------------------
/web/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | *.sh
3 | node_modules
4 | *.md
5 | *.woff
6 | *.ttf
7 | .vscode
8 | .idea
9 | dist
10 | /public
11 | /docs
12 | .husky
13 | .local
14 | /bin
15 | Dockerfile
16 |
--------------------------------------------------------------------------------
/web/build/constant.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name Config
3 | * @description 项目配置
4 | */
5 |
6 | // 本地服务端口
7 | export const VITE_PORT = 8008;
8 |
9 | // 包依赖分析
10 | export const ANALYSIS = true;
11 |
12 | // 代码压缩
13 | export const COMPRESSION = true;
14 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/progress.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name ConfigProgressPlugin
3 | * @description 构建显示进度条
4 | */
5 |
6 | import progress from 'vite-plugin-progress';
7 | export const ConfigProgressPlugin = () => {
8 | return progress();
9 | };
10 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/unocss.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name ConfigUnocssPlugin
3 | * @description 监听配置文件修改自动重启Vite
4 | */
5 |
6 | // Unocss
7 | import Unocss from 'unocss/vite';
8 |
9 | export const ConfigUnocssPlugin = () => {
10 | return Unocss();
11 | };
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /server/myapp/migrations
2 | /server/myapp/views/__pycache__/
3 | /server/.idea
4 | /web/.idea
5 | /web/dist
6 | /web/node_modules
7 | /server/.idea/
8 | /.idea/
9 | __pycache__
10 | .idea
11 | server/.idea
12 | .DS_Store
13 | server/.DS_Store
14 | web/.DS_Store
--------------------------------------------------------------------------------
/web/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 140,
3 | semi: true,
4 | vueIndentScriptAndStyle: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | proseWrap: 'never',
8 | htmlWhitespaceSensitivity: 'strict',
9 | endOfLine: 'auto',
10 | };
11 |
--------------------------------------------------------------------------------
/web/src/store/modules/user/types.ts:
--------------------------------------------------------------------------------
1 | export type RoleType = '' | '*' | 'admin' | 'user';
2 | export interface UserState {
3 | user_id: any;
4 | user_name: any;
5 | user_token: any;
6 |
7 | admin_user_id: any;
8 | admin_user_name: any;
9 | admin_user_token: any;
10 | }
11 |
--------------------------------------------------------------------------------
/server/myapp/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 | from myapp.models import Classification, Thing, Tag, User
5 |
6 | admin.site.register(Classification)
7 | admin.site.register(Tag)
8 | admin.site.register(Thing)
9 | admin.site.register(User)
10 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/restart.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name ConfigRestartPlugin
3 | * @description 监听配置文件修改自动重启Vite
4 | */
5 | import ViteRestart from 'vite-plugin-restart';
6 | export const ConfigRestartPlugin = () => {
7 | return ViteRestart({
8 | restart: ['*.config.[jt]s', '**/config/*.[jt]s'],
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/web/src/store/modules/app/types.ts:
--------------------------------------------------------------------------------
1 | export interface AppState {
2 | theme: string;
3 | colorWeek: boolean;
4 | navbar: boolean;
5 | menu: boolean;
6 | menuCollapse: boolean;
7 | footer: boolean;
8 | themeColor: string;
9 | menuWidth: number;
10 | globalSettings: boolean;
11 | [key: string]: unknown;
12 | }
13 |
--------------------------------------------------------------------------------
/web/types/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import { DefineComponent } from 'vue';
5 | const component: DefineComponent<{}, {}, any>;
6 | export default component;
7 | }
8 |
9 | declare module 'virtual:*' {
10 | const result: any;
11 | export default result;
12 | }
13 |
--------------------------------------------------------------------------------
/web/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
19 |
--------------------------------------------------------------------------------
/web/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia';
2 | import { useAppStore } from './modules/app';
3 | import { useUserStore } from './modules/user';
4 |
5 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
6 |
7 | const pinia = createPinia();
8 | pinia.use(piniaPluginPersistedstate);
9 |
10 | export { useAppStore, useUserStore };
11 | export default pinia;
12 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/visualizer.ts:
--------------------------------------------------------------------------------
1 | import visualizer from 'rollup-plugin-visualizer';
2 | import { ANALYSIS } from '../../constant';
3 |
4 | export function ConfigVisualizerConfig() {
5 | if (ANALYSIS) {
6 | return visualizer({
7 | filename: 'dist/report.html',
8 | open: true,
9 | gzipSize: true,
10 | emitFile: false
11 | });
12 | }
13 | return [];
14 | }
15 |
--------------------------------------------------------------------------------
/server/myapp/permission/permission.py:
--------------------------------------------------------------------------------
1 | from myapp.models import User
2 |
3 |
4 | def isDemoAdminUser(request):
5 | adminToken = request.META.get("HTTP_ADMINTOKEN")
6 | users = User.objects.filter(admin_token=adminToken)
7 | if len(users) > 0:
8 | user = users[0]
9 | if user.role == '3': # (角色3)表示演示帐号
10 | print('演示帐号===>')
11 | return True
12 | return False
13 |
--------------------------------------------------------------------------------
/server/server/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for server project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/server/server/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for server project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | .local
6 | .history
7 | # local env files
8 | .env.local
9 | .env.*.local
10 | .eslintcache
11 | .github
12 | .husky
13 | .vscode
14 |
15 | # Log files
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 | pnpm-debug.log*
20 | pnpm-lock.yaml*
21 |
22 | # Editor directories and files
23 | .idea
24 | # .vscode
25 | *.suo
26 | *.ntvs*
27 | *.njsproj
28 | *.sln
29 | *.sw?
30 | ./packages
31 | ./history
32 |
--------------------------------------------------------------------------------
/web/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | import router from './router';
4 | import piniaStore from './store';
5 |
6 | import bootstrap from './core/bootstrap';
7 | import '/@/styles/reset.less';
8 | import '/@/styles/index.less';
9 | import Antd from 'ant-design-vue';
10 |
11 | const app = createApp(App);
12 |
13 |
14 | app.use(Antd);
15 | app.use(router);
16 | app.use(piniaStore);
17 | app.use(bootstrap)
18 | app.mount('#app');
19 |
--------------------------------------------------------------------------------
/web/src/utils/http/axios/type.ts:
--------------------------------------------------------------------------------
1 | export interface RequestOptions {
2 | // Whether to process the request result
3 | isTransformResponse?: boolean;
4 | }
5 |
6 | // 返回res.data的interface
7 | export interface IResponse {
8 | code: number | string;
9 | result: T;
10 | message: string;
11 | status: string | number;
12 | }
13 |
14 | /**用户登录 */
15 | export interface ILogin {
16 | /** 账户名称 */
17 | username: string;
18 | /** 账户密码 */
19 | password: string;
20 | }
21 |
--------------------------------------------------------------------------------
/web/src/api/overview.ts:
--------------------------------------------------------------------------------
1 | import {get, post} from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/overview/count',
5 | sysInfo= '/myapp/admin/overview/sysInfo',
6 | }
7 |
8 | const listApi = async (params: any) =>
9 | get({url: URL.list, params: params, data: {}, headers: {}});
10 |
11 |
12 | const sysInfoApi = async (params: any) =>
13 | get({url: URL.sysInfo, params: params, data: {}, headers: {}});
14 |
15 | export {listApi, sysInfoApi};
16 |
--------------------------------------------------------------------------------
/web/src/store/constants.js:
--------------------------------------------------------------------------------
1 | const BASE_URL = 'http://127.0.0.1:8000';
2 | // const BASE_URL = 'https://yanglao.gitapp.cn/api/';
3 |
4 | const USER_ID = 'user_id';
5 | const USER_NAME = 'user_name';
6 | const USER_TOKEN = 'user_token';
7 |
8 | const ADMIN_USER_ID = 'admin_user_id';
9 | const ADMIN_USER_NAME = 'admin_user_name';
10 | const ADMIN_USER_TOKEN = 'admin_user_token';
11 |
12 | export { BASE_URL, USER_TOKEN, USER_NAME, USER_ID, ADMIN_USER_ID, ADMIN_USER_NAME, ADMIN_USER_TOKEN };
13 |
--------------------------------------------------------------------------------
/web/src/utils/auth.ts:
--------------------------------------------------------------------------------
1 | const TokenKey = 'fast-token';
2 | const TokenPrefix = 'Bearer ';
3 | const isLogin = () => {
4 | return !!localStorage.getItem(TokenKey);
5 | };
6 | const getToken = () => {
7 | return localStorage.getItem(TokenKey);
8 | };
9 | const setToken = (token: string) => {
10 | localStorage.setItem(TokenKey, token);
11 | };
12 | const clearToken = () => {
13 | localStorage.removeItem(TokenKey);
14 | };
15 | export { TokenPrefix, isLogin, getToken, setToken, clearToken };
16 |
--------------------------------------------------------------------------------
/web/src/styles/reset.less:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 |
5 | *,
6 | ::before,
7 | ::after {
8 | margin: 0;
9 | padding: 0;
10 | box-sizing: inherit;
11 | }
12 |
13 |
14 | a:hover,
15 | a:link,
16 | a:visited,
17 | a:active {
18 | text-decoration: none;
19 | }
20 |
21 | ol,
22 | ul {
23 | list-style: none;
24 | }
25 |
26 | input,
27 | textarea {
28 | outline: none;
29 | border: none;
30 | resize: none;
31 | }
32 |
33 | body {
34 | font-size: 14px;
35 | font-weight: 400;
36 | }
37 |
--------------------------------------------------------------------------------
/web/src/assets/images/tel-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/opLog.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view
3 |
4 | from myapp.handler import APIResponse
5 | from myapp.models import OpLog
6 | from myapp.serializers import OpLogSerializer
7 |
8 |
9 | @api_view(['GET'])
10 | def list_api(request):
11 | if request.method == 'GET':
12 | opLogs = OpLog.objects.all().order_by('-re_time')[:100]
13 | serializer = OpLogSerializer(opLogs, many=True)
14 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
15 |
--------------------------------------------------------------------------------
/web/src/assets/icons/svg/marks.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/images/address-right-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/errorLog.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view
3 |
4 | from myapp.handler import APIResponse
5 | from myapp.models import ErrorLog
6 | from myapp.serializers import ErrorLogSerializer
7 |
8 |
9 | @api_view(['GET'])
10 | def list_api(request):
11 | if request.method == 'GET':
12 | errorLogs = ErrorLog.objects.all().order_by('-log_time')
13 | serializer = ErrorLogSerializer(errorLogs, many=True)
14 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
15 |
--------------------------------------------------------------------------------
/server/myapp/handler.py:
--------------------------------------------------------------------------------
1 | from rest_framework.response import Response
2 |
3 |
4 | class APIResponse(Response):
5 | def __init__(self, code=0, msg='', data=None, status=200, headers=None, content_type=None, **kwargs):
6 | dic = {'code': code, 'msg': msg}
7 | if data is not None:
8 | dic['data'] = data
9 |
10 | dic.update(kwargs) # 这里使用update
11 | super().__init__(data=dic, status=status,
12 | template_name=None, headers=headers,
13 | exception=False, content_type=content_type)
14 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/__init__.py:
--------------------------------------------------------------------------------
1 | from myapp.views.admin.thing import *
2 | from myapp.views.admin.classification import *
3 | from myapp.views.admin.tag import *
4 | from myapp.views.admin.user import *
5 | from myapp.views.admin.overview import *
6 | from myapp.views.admin.loginLog import *
7 | from myapp.views.admin.opLog import *
8 | from myapp.views.admin.errorLog import *
9 | from myapp.views.admin.banner import *
10 | from myapp.views.admin.notice import *
11 | from myapp.views.admin.worker import *
12 | from myapp.views.admin.family import *
13 | from myapp.views.admin.medical import *
14 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/compress.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name ConfigCompressPlugin
3 | * @description 开启.gz压缩
4 | */
5 | import viteCompression from 'vite-plugin-compression';
6 | import { COMPRESSION } from '../../constant';
7 |
8 | export const ConfigCompressPlugin = () => {
9 | if (COMPRESSION) {
10 | return viteCompression({
11 | verbose: true, // 默认即可
12 | disable: false, //开启压缩(不禁用),默认即可
13 | deleteOriginFile: false, //删除源文件
14 | threshold: 10240, //压缩前最小文件大小
15 | algorithm: 'gzip', //压缩算法
16 | ext: '.gz', //文件类型
17 | });
18 | }
19 | return [];
20 | };
21 |
--------------------------------------------------------------------------------
/web/src/assets/icons/svg/twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/images/searchIcon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/assets/images/search-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/assets/images/add.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/icons/svg/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/assets/images/order-point-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/images/pwd-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/images/setting-msg-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/autoImport.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name AutoImportDeps
3 | * @description 按需加载,自动引入
4 | */
5 | import AutoImport from 'unplugin-auto-import/vite';
6 | // import { AntDesignVueResolver} from 'unplugin-vue-components/resolvers';
7 |
8 | export const AutoImportDeps = () => {
9 | return AutoImport({
10 | dts: 'types/auto-imports.d.ts',
11 | imports: [
12 | 'vue',
13 | 'pinia',
14 | 'vue-router',
15 | {
16 | '@vueuse/core': [],
17 | },
18 | {
19 | 'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
20 | },
21 | ],
22 | resolvers: [
23 | // AntDesignVueResolver(),
24 | ],
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/web/src/api/log.ts:
--------------------------------------------------------------------------------
1 | import {get, post} from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | loginLogList = '/myapp/admin/loginLog/list',
5 | opLogList = '/myapp/admin/opLog/list',
6 | errorLogList = '/myapp/admin/errorLog/list',
7 | }
8 |
9 | const listLoginLogApi = async (params: any) =>
10 | get({url: URL.loginLogList, params: params, data: {}, headers: {}});
11 | const listOpLogListApi = async (params: any) =>
12 | get({url: URL.opLogList, params: params, data: {}, headers: {}});
13 | const listErrorLogListApi = async (params: any) =>
14 | get({url: URL.errorLogList, params: params, data: {}, headers: {}});
15 |
16 | export {listLoginLogApi, listOpLogListApi, listErrorLogListApi};
17 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/imagemin.ts:
--------------------------------------------------------------------------------
1 | import viteImagemin from 'vite-plugin-imagemin';
2 |
3 | export function ConfigImageminPlugin() {
4 | const plugin = viteImagemin({
5 | gifsicle: {
6 | optimizationLevel: 7,
7 | interlaced: false,
8 | },
9 | mozjpeg: {
10 | quality: 20,
11 | },
12 | optipng: {
13 | optimizationLevel: 7,
14 | },
15 | pngquant: {
16 | quality: [0.8, 0.9],
17 | speed: 4,
18 | },
19 | svgo: {
20 | plugins: [
21 | {
22 | name: 'removeViewBox',
23 | },
24 | {
25 | name: 'removeEmptyAttrs',
26 | active: false,
27 | },
28 | ],
29 | },
30 | });
31 | return plugin;
32 | }
33 |
--------------------------------------------------------------------------------
/web/src/assets/images/register-name.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/server/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/web/src/assets/images/clear-search.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/images/mail-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/core/bootstrap.js:
--------------------------------------------------------------------------------
1 | // localStorage恢复到内存
2 |
3 | import {useUserStore} from "/@/store";
4 | import {USER_ID, USER_NAME, USER_TOKEN, ADMIN_USER_ID, ADMIN_USER_NAME, ADMIN_USER_TOKEN} from "/@/store/constants";
5 |
6 | export default function Initializer () {
7 | const userStore = useUserStore()
8 | userStore.$patch((state)=>{
9 | state.user_id = localStorage.getItem(USER_ID)
10 | state.user_name = localStorage.getItem(USER_NAME)
11 | state.user_token = localStorage.getItem(USER_TOKEN)
12 |
13 | state.admin_user_id = localStorage.getItem(ADMIN_USER_ID)
14 | state.admin_user_name = localStorage.getItem(ADMIN_USER_NAME)
15 | state.admin_user_token = localStorage.getItem(ADMIN_USER_TOKEN)
16 | console.log('恢复store完毕==>', state)
17 | })
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/server/myapp/auth/authentication.py:
--------------------------------------------------------------------------------
1 | from rest_framework import exceptions
2 | from rest_framework.authentication import BaseAuthentication
3 |
4 | from myapp.models import User
5 |
6 |
7 | # 接口认证
8 | class AdminTokenAuthtication(BaseAuthentication):
9 | def authenticate(self, request):
10 | adminToken = request.META.get("HTTP_ADMINTOKEN")
11 | # print("检查adminToken==>" + adminToken)
12 |
13 | users = User.objects.filter(admin_token=adminToken)
14 | """
15 | 判定条件:
16 | 1. 传了adminToken
17 | 2. 查到了该帐号
18 | 3. 该帐号是管理员或演示帐号
19 | """
20 | if not adminToken or len(users) == 0 or users[0].role == '2':
21 | raise exceptions.AuthenticationFailed("AUTH_FAIL_END")
22 | else:
23 | print('adminToken验证通过')
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/web/src/assets/icons/svg/test.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/api/classification.ts:
--------------------------------------------------------------------------------
1 | import { get, post } from '/@/utils/http/axios';
2 | enum URL {
3 | list = '/myapp/admin/classification/list',
4 | create = '/myapp/admin/classification/create',
5 | update = '/myapp/admin/classification/update',
6 | delete = '/myapp/admin/classification/delete',
7 | }
8 |
9 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
10 | const createApi = async (data: any) =>
11 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
12 | const updateApi = async (params: any, data: any) =>
13 | post({ url: URL.update, params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
14 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
15 |
16 | export { listApi, createApi, updateApi, deleteApi };
17 |
--------------------------------------------------------------------------------
/web/src/api/family.ts:
--------------------------------------------------------------------------------
1 | import { get, post } from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/family/list',
5 | create = '/myapp/admin/family/create',
6 | update = '/myapp/admin/family/update',
7 | delete = '/myapp/admin/family/delete',
8 | }
9 |
10 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
11 | const createApi = async (data: any) =>
12 | post({
13 | url: URL.create,
14 | params: {},
15 | data: data,
16 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
17 | });
18 | const updateApi = async (params: any, data: any) =>
19 | post({
20 | url: URL.update,
21 | params: params,
22 | data: data,
23 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
24 | });
25 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
26 |
27 | export { listApi, createApi, updateApi, deleteApi };
28 |
--------------------------------------------------------------------------------
/web/src/api/notice.ts:
--------------------------------------------------------------------------------
1 | import { get, post } from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/notice/list',
5 | create = '/myapp/admin/notice/create',
6 | update = '/myapp/admin/notice/update',
7 | delete = '/myapp/admin/notice/delete',
8 | }
9 |
10 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
11 | const createApi = async (data: any) =>
12 | post({
13 | url: URL.create,
14 | params: {},
15 | data: data,
16 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
17 | });
18 | const updateApi = async (params: any, data: any) =>
19 | post({
20 | url: URL.update,
21 | params: params,
22 | data: data,
23 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
24 | });
25 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
26 |
27 | export { listApi, createApi, updateApi, deleteApi };
28 |
--------------------------------------------------------------------------------
/web/src/api/worker.ts:
--------------------------------------------------------------------------------
1 | import { get, post } from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/worker/list',
5 | create = '/myapp/admin/worker/create',
6 | update = '/myapp/admin/worker/update',
7 | delete = '/myapp/admin/worker/delete',
8 | }
9 |
10 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
11 | const createApi = async (data: any) =>
12 | post({
13 | url: URL.create,
14 | params: {},
15 | data: data,
16 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
17 | });
18 | const updateApi = async (params: any, data: any) =>
19 | post({
20 | url: URL.update,
21 | params: params,
22 | data: data,
23 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
24 | });
25 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
26 |
27 | export { listApi, createApi, updateApi, deleteApi };
28 |
--------------------------------------------------------------------------------
/web/src/api/medical.ts:
--------------------------------------------------------------------------------
1 | import { get, post } from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/medical/list',
5 | create = '/myapp/admin/medical/create',
6 | update = '/myapp/admin/medical/update',
7 | delete = '/myapp/admin/medical/delete',
8 | }
9 |
10 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
11 | const createApi = async (data: any) =>
12 | post({
13 | url: URL.create,
14 | params: {},
15 | data: data,
16 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
17 | });
18 | const updateApi = async (params: any, data: any) =>
19 | post({
20 | url: URL.update,
21 | params: params,
22 | data: data,
23 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
24 | });
25 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
26 |
27 | export { listApi, createApi, updateApi, deleteApi };
28 |
--------------------------------------------------------------------------------
/web/src/api/tag.ts:
--------------------------------------------------------------------------------
1 | import {get, post} from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/tag/list',
5 | create = '/myapp/admin/tag/create',
6 | update = '/myapp/admin/tag/update',
7 | delete = '/myapp/admin/tag/delete',
8 | }
9 |
10 | const listApi = async (params: any) =>
11 | get({url: URL.list, params: params, data: {}, headers: {}});
12 | const createApi = async (data: any) =>
13 | post({
14 | url: URL.create,
15 | params: {},
16 | data: data,
17 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'}
18 | });
19 | const updateApi = async (params: any, data: any) =>
20 | post({
21 | url: URL.update,
22 | params: params,
23 | data: data,
24 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'}
25 | });
26 | const deleteApi = async (params: any) =>
27 | post({url: URL.delete, params: params, headers: {}});
28 |
29 | export {listApi, createApi, updateApi, deleteApi};
30 |
--------------------------------------------------------------------------------
/web/src/utils/http/axios/status.ts:
--------------------------------------------------------------------------------
1 | export const showMessage = (status: number | string): string => {
2 | let message = '';
3 | switch (status) {
4 | case 400:
5 | message = '请求错误(400)';
6 | break;
7 | case 401:
8 | message = '未授权,请重新登录(401)';
9 | break;
10 | case 403:
11 | message = '拒绝访问(403)';
12 | break;
13 | case 404:
14 | message = '请求出错(404)';
15 | break;
16 | case 408:
17 | message = '请求超时(408)';
18 | break;
19 | case 500:
20 | message = '服务器错误(500)';
21 | break;
22 | case 501:
23 | message = '服务未实现(501)';
24 | break;
25 | case 502:
26 | message = '网络错误(502)';
27 | break;
28 | case 503:
29 | message = '服务不可用(503)';
30 | break;
31 | case 504:
32 | message = '网络超时(504)';
33 | break;
34 | case 505:
35 | message = 'HTTP版本不受支持(505)';
36 | break;
37 | default:
38 | message = `连接出错(${status})!`;
39 | }
40 | return `${message},请检查网络或联系管理员!`;
41 | };
42 |
--------------------------------------------------------------------------------
/server/server/urls.py:
--------------------------------------------------------------------------------
1 | """server URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls.static import static
17 | from django.contrib import admin
18 | from django.urls import path, include
19 |
20 | from server import settings
21 |
22 | urlpatterns = [
23 | path('admin/', admin.site.urls),
24 | path('myapp/', include('myapp.urls')),
25 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
26 |
--------------------------------------------------------------------------------
/web/src/assets/images/order-address-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/images/message-icon.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/web/src/assets/images/delete-icon.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/component.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name AutoRegistryComponents
3 | * @description 按需加载,自动引入组件
4 | */
5 | import Components from 'unplugin-vue-components/vite';
6 | import {
7 | ElementPlusResolver,
8 | VueUseComponentsResolver,
9 | AntDesignVueResolver,
10 | TDesignResolver,
11 | NaiveUiResolver,
12 | } from 'unplugin-vue-components/resolvers';
13 | export const AutoRegistryComponents = () => {
14 | return Components({
15 | dirs: ['src/components'],
16 | extensions: ['vue'],
17 | deep: true,
18 | dts: 'types/components.d.ts',
19 | directoryAsNamespace: false,
20 | globalNamespaces: [],
21 | directives: true,
22 | importPathTransform: (v) => v,
23 | allowOverrides: false,
24 | include: [/\.vue$/, /\.vue\?vue/],
25 | exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
26 | resolvers: [
27 | ElementPlusResolver(),
28 | VueUseComponentsResolver(),
29 | AntDesignVueResolver(),
30 | TDesignResolver({
31 | library: 'vue-next',
32 | }),
33 | NaiveUiResolver(),
34 | ],
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/web/src/assets/images/ebook-download-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 养老管理系统演示
9 |
10 |
11 |
13 |
14 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/web/src/store/modules/app/index.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia';
2 | import piniaStore from '/@/store/index';
3 | import { AppState } from './types';
4 |
5 | export const useAppStore = defineStore(
6 | // 唯一ID
7 | 'app',
8 | {
9 | state: () => ({
10 | title: 'FastVue3, 一个快速开箱即用的Vue3+Vite模板',
11 | h1: 'Vue3 + Vite3.x + TypeScript + Pinia大厂开发必备',
12 | theme: '',
13 | }),
14 | getters: {},
15 | actions: {
16 | updateSettings(partial: Partial) {
17 | this.$patch(partial);
18 | },
19 |
20 | // Change theme color
21 | toggleTheme(dark: boolean) {
22 | if (dark) {
23 | this.theme = 'dark';
24 | document.documentElement.classList.add('dark');
25 | } else {
26 | this.theme = 'light';
27 | document.documentElement.classList.remove('dark');
28 | }
29 | },
30 | },
31 | persist: {
32 | key: 'theme',
33 | storage: localStorage,
34 | paths: ['theme'],
35 | },
36 | },
37 | );
38 |
39 | export function useAppOutsideStore() {
40 | return useAppStore(piniaStore);
41 | }
42 |
--------------------------------------------------------------------------------
/web/src/assets/images/code-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/assets/images/read-online-icon.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/web/src/assets/images/share-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/src/api/thing.ts:
--------------------------------------------------------------------------------
1 | // 权限问题后期增加
2 | import { get, post } from '/@/utils/http/axios';
3 | import { UserState } from '/@/store/modules/user/types';
4 | // import axios from 'axios';
5 | enum URL {
6 | list = '/myapp/admin/thing/list',
7 | create = '/myapp/admin/thing/create',
8 | update = '/myapp/admin/thing/update',
9 | delete = '/myapp/admin/thing/delete',
10 | detail = '/api/thing/detail',
11 | }
12 |
13 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
14 | const createApi = async (data: any) =>
15 | post({ url: URL.create, params: {}, data: data, timeout:20000, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
16 | const updateApi = async (params:any, data: any) =>
17 | post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
18 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
19 | const detailApi = async (params: any) => get({ url: URL.detail, params: params, headers: {} });
20 |
21 | export { listApi, createApi, updateApi, deleteApi, detailApi };
22 |
--------------------------------------------------------------------------------
/web/src/assets/images/ali-pay-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/assets/images/wx-pay-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/assets/images/order-thing-icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "noLib": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "allowSyntheticDefaultImports": true,
10 | "strictFunctionTypes": false,
11 | "jsx": "preserve",
12 | "baseUrl": ".",
13 | "allowJs": true,
14 | "sourceMap": true,
15 | "esModuleInterop": true,
16 | "resolveJsonModule": true,
17 | "noUnusedLocals": false,
18 | "noUnusedParameters": false,
19 | "experimentalDecorators": true,
20 | "lib": ["dom", "esnext"],
21 | "noImplicitAny": false,
22 | "skipLibCheck": true,
23 | "types": ["vite/client"],
24 | "removeComments": true,
25 | "paths": {
26 | "/@/*": ["src/*"],
27 | "/#/*": ["types/*"]
28 | }
29 | },
30 | "include": [
31 | "tests/**/*.ts",
32 | "src/**/*.ts",
33 | "src/**/*.d.ts",
34 | "src/**/*.tsx",
35 | "src/**/*.vue",
36 | "types/**/*.d.ts",
37 | "types/**/*.ts",
38 | "build/**/*.ts",
39 | "build/**/*.d.ts",
40 | "mock/**/*.ts",
41 | "vite.config.ts"
42 | ],
43 | "exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"]
44 | }
45 |
--------------------------------------------------------------------------------
/web/src/api/comment.ts:
--------------------------------------------------------------------------------
1 | import {get, post} from '/@/utils/http/axios';
2 |
3 | enum URL {
4 | list = '/myapp/admin/comment/list',
5 | create = '/myapp/admin/comment/create',
6 | delete = '/myapp/admin/comment/delete',
7 | listThingComments = '/api/comment/listThingComments',
8 | listUserComments = '/api/comment/listUserComments',
9 | like = '/api/comment/like'
10 | }
11 |
12 | const listApi = async (params: any) => get({url: URL.list, params: params, data: {}, headers: {}});
13 | const createApi = async (data: any) => post({
14 | url: URL.create,
15 | params: {},
16 | data: data,
17 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'}
18 | });
19 | const deleteApi = async (params: any) => post({url: URL.delete, params: params, headers: {}});
20 | const listThingCommentsApi = async (params: any) => get({url: URL.listThingComments, params: params, data: {}, headers: {}});
21 | const listUserCommentsApi = async (params: any) => get({url: URL.listUserComments, params: params, data: {}, headers: {}});
22 | const likeApi = async (params: any) => post({url: URL.like, params: params, headers: {}});
23 |
24 | export {listApi, createApi, deleteApi, listThingCommentsApi, listUserCommentsApi, likeApi};
25 |
--------------------------------------------------------------------------------
/web/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router';
2 | import root from './root';
3 |
4 | import { ADMIN_USER_TOKEN, USER_TOKEN } from '/@/store/constants';
5 |
6 | // 路由权限白名单
7 | const allowList = ['adminLogin', 'login', 'register', 'portal', 'search', 'detail', '403', '404'];
8 | // 前台登录地址
9 | const loginRoutePath = '/index/login';
10 | // 后台登录地址
11 | const adminLoginRoutePath = '/adminLogin';
12 |
13 | const router = createRouter({
14 | history: createWebHistory(),
15 | routes: root,
16 | });
17 |
18 | router.beforeEach(async (to, from, next) => {
19 | console.log(to, from);
20 |
21 | /** 路由 **/
22 | if (to.path.startsWith('/admin')) {
23 | if (localStorage.getItem(ADMIN_USER_TOKEN)) {
24 | if (to.path === adminLoginRoutePath) {
25 | next({ path: '/' });
26 | } else {
27 | next();
28 | }
29 | } else {
30 | if (allowList.includes(to.name)) {
31 | // 在免登录名单,直接进入
32 | next();
33 | } else {
34 | next({ path: adminLoginRoutePath, query: { redirect: to.fullPath } });
35 | }
36 | }
37 |
38 | // next()
39 | }
40 | });
41 |
42 | router.afterEach((_to) => {
43 | // 回到顶部
44 | let html = document.getElementById('html');
45 | if (html) {
46 | html.scrollTo(0, 0);
47 | }
48 | });
49 |
50 | export default router;
51 |
--------------------------------------------------------------------------------
/web/src/assets/images/order-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/assets/images/want-read-hover.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/web/src/assets/images/pwd-hidden.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/web/build/vite/plugins/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name createVitePlugins
3 | * @description 封装plugins数组统一调用
4 | */
5 | import {PluginOption} from 'vite';
6 | import vue from '@vitejs/plugin-vue';
7 | import vueJsx from '@vitejs/plugin-vue-jsx';
8 | import {AutoImportDeps} from './autoImport';
9 | import {ConfigCompressPlugin} from './compress';
10 | import {ConfigRestartPlugin} from './restart';
11 | import {ConfigProgressPlugin} from './progress';
12 | import {ConfigVisualizerConfig} from "./visualizer";
13 |
14 | export function createVitePlugins(isBuild: boolean) {
15 | const vitePlugins = [
16 | // vue支持
17 | vue(),
18 | // JSX支持
19 | vueJsx(),
20 | // setup语法糖组件名支持
21 | // vueSetupExtend(),
22 | // 提供https证书
23 | // VitePluginCertificate({
24 | // source: 'coding',
25 | // }) as PluginOption,
26 | ];
27 |
28 | // 自动按需引入组件
29 | // vitePlugins.push(AutoRegistryComponents());
30 |
31 | // 自动按需引入依赖
32 | vitePlugins.push(AutoImportDeps());
33 |
34 | // 自动生成路由
35 | // vitePlugins.push(ConfigPagesPlugin());
36 |
37 | // 开启.gz压缩 rollup-plugin-gzip
38 | vitePlugins.push(ConfigCompressPlugin());
39 |
40 | // 监听配置文件改动重启
41 | vitePlugins.push(ConfigRestartPlugin());
42 |
43 | // 构建时显示进度条
44 | vitePlugins.push(ConfigProgressPlugin());
45 |
46 | // 构建时显示进度条
47 | vitePlugins.push(ConfigVisualizerConfig());
48 |
49 |
50 | return vitePlugins;
51 | }
52 |
--------------------------------------------------------------------------------
/web/src/assets/images/setting-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/src/router/root.js:
--------------------------------------------------------------------------------
1 | // 路由表
2 | const constantRouterMap = [
3 |
4 | {
5 | path: '/',
6 | redirect: '/admin',
7 | },
8 | {
9 | path: '/adminLogin',
10 | name: 'adminLogin',
11 | component: () => import('/@/views/admin-login.vue'),
12 | },
13 | {
14 | path: '/admin',
15 | name: 'admin',
16 | redirect: '/admin/thing',
17 | component: () => import('/@/views/main.vue'),
18 | children: [
19 | { path: 'thing', name: 'thing', component: () => import('/@/views/thing.vue') },
20 | { path: 'worker', name: 'worker', component: () => import('/@/views/worker.vue') },
21 | { path: 'family', name: 'family', component: () => import('/@/views/family.vue') },
22 | { path: 'medical', name: 'medical', component: () => import('/@/views/medical.vue') },
23 | { path: 'user', name: 'user', component: () => import('/@/views/user.vue') },
24 | { path: 'classification', name: 'classification', component: () => import('/@/views/classification.vue') },
25 | { path: 'tag', name: 'tag', component: () => import('/@/views/tag.vue') },
26 | { path: 'loginLog', name: 'loginLog', component: () => import('/@/views/login-log.vue') },
27 | { path: 'opLog', name: 'opLog', component: () => import('/@/views/op-log.vue') },
28 | { path: 'errorLog', name: 'errorLog', component: () => import('/@/views/error-log.vue') },
29 | { path: 'sysInfo', name: 'sysInfo', component: () => import('/@/views/sys-info.vue') },
30 | ]
31 | },
32 | ];
33 |
34 | export default constantRouterMap;
35 |
--------------------------------------------------------------------------------
/web/src/assets/images/setting-safe-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/web/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { UserConfig, ConfigEnv } from 'vite';
2 | import { createVitePlugins } from './build/vite/plugins';
3 | import { resolve } from 'path';
4 | import { VITE_PORT } from './build/constant';
5 |
6 | function pathResolve(dir: string) {
7 | return resolve(process.cwd(), '.', dir);
8 | }
9 |
10 | // https://vitejs.dev/config/
11 | export default ({ command }: ConfigEnv): UserConfig => {
12 | const isBuild = command === 'build';
13 | let base: string;
14 | if (command === 'build') {
15 | base = '/';
16 | } else {
17 | base = '/';
18 | }
19 | return {
20 | base,
21 | publicDir: "public", //静态资源服务的文件夹
22 | resolve: {
23 | alias: [
24 | {
25 | find: 'vue-i18n',
26 | replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
27 | },
28 | // 别名 /@/xxxx => src/xxxx
29 | {
30 | find: '/@',
31 | replacement: pathResolve('src') + '/',
32 | },
33 | ],
34 | },
35 | // plugins
36 | plugins: createVitePlugins(isBuild),
37 |
38 | // css
39 | css: {},
40 |
41 | // server
42 | server: {
43 | hmr: { overlay: false }, // 禁用或配置 HMR 连接 设置 server.hmr.overlay 为 false 可以禁用服务器错误遮罩层
44 | // 服务配置
45 | port: VITE_PORT, // 类型: number 指定服务器端口;
46 | open: false, // 类型: boolean | string在服务器启动时自动在浏览器中打开应用程序;
47 | cors: true, // 类型: boolean | CorsOptions 为开发服务器配置 CORS。默认启用并允许任何源
48 | host: '0.0.0.0', // IP配置,支持从IP启动
49 | https: false, // 禁用https
50 | // proxy,
51 | },
52 | };
53 | };
54 |
--------------------------------------------------------------------------------
/web/src/utils/result.ts:
--------------------------------------------------------------------------------
1 | import { Recoverable } from 'repl';
2 |
3 | // 返回统一格式的接口数据类型定义
4 | export function successResult(result: T, { message = 'Request success' } = {}) {
5 | return {
6 | code: 200,
7 | result,
8 | message,
9 | status: 'ok',
10 | };
11 | }
12 | export function errorResult(message = 'Request failed', { code = -1, result = null } = {}) {
13 | return {
14 | code,
15 | result,
16 | message,
17 | status: 'fail',
18 | };
19 | }
20 |
21 | //返回分页数据
22 | export function pageSuccessResult(page: number, pageSize: number, list: T[], { message = 'ok' } = {}) {
23 | const pageData = pagination(page, pageSize, list);
24 | return {
25 | ...successResult({
26 | items: pageData,
27 | total: list.length,
28 | }),
29 | message,
30 | };
31 | }
32 |
33 | // 封装分页数据
34 | export function pagination(pageNo: number, pageSize: number, array: T[]): T[] {
35 | const offset = (pageNo - 1) * Number(pageSize);
36 | const res =
37 | offset + Number(pageSize) >= array.length ? array.slice(offset, array.length) : array.slice(offset, offset + Number(pageSize));
38 | return res;
39 | }
40 |
41 | // 返回参数类型定义
42 | export interface requestParams {
43 | method: string;
44 | body: any;
45 | headers?: { authorization?: string };
46 | query: any;
47 | }
48 |
49 | /**
50 | * @name getRequestToken
51 | * @description 通过request数据中获取token,具体情况根据接口规范修改
52 | */
53 | export function getRequestToken({ headers }: requestParams): string | undefined {
54 | return headers?.authorization;
55 | }
56 |
--------------------------------------------------------------------------------
/server/myapp/middlewares/LogMiddleware.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import time
3 | import json
4 |
5 | from django.utils.deprecation import MiddlewareMixin
6 |
7 | from myapp import utils
8 | from myapp.serializers import OpLogSerializer
9 |
10 |
11 | class OpLogs(MiddlewareMixin):
12 |
13 | def __init__(self, *args):
14 | super(OpLogs, self).__init__(*args)
15 |
16 | self.start_time = None # 开始时间
17 | self.end_time = None # 响应时间
18 | self.data = {} # dict数据
19 |
20 | def process_request(self, request):
21 |
22 | self.start_time = time.time() # 开始时间
23 |
24 | re_ip = utils.get_ip(request)
25 | re_method = request.method
26 | re_content = request.GET if re_method == 'GET' else request.POST
27 | if re_content:
28 | re_content = json.dumps(re_content)
29 | else:
30 | re_content = None
31 |
32 | self.data.update(
33 | {
34 | 're_url': request.path,
35 | 're_method': re_method,
36 | 're_ip': re_ip,
37 | # 're_content': re_content,
38 | }
39 | )
40 | # print(self.data)
41 |
42 | def process_response(self, request, response):
43 |
44 | # 耗时毫秒/ms
45 | self.end_time = time.time() # 响应时间
46 | access_time = self.end_time - self.start_time
47 | self.data['access_time'] = round(access_time * 1000)
48 |
49 | # 入库
50 | serializer = OpLogSerializer(data=self.data)
51 | if serializer.is_valid():
52 | serializer.save()
53 |
54 | return response
55 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | > 一直想做一款管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。现将部分源码开源,如需完整源码,可以联系客服微信购买:lengqin1024
2 |
3 | ### 功能介绍
4 |
5 | 平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。
6 |
7 | 功能包括:老人管理、护工管理、亲属管理、病史管理、房间管理、活动管理、用户管理、日志管理、系统信息模块。
8 |
9 |
10 | ### 演示地址
11 |
12 | http://yanglao.gitapp.cn
13 |
14 | 演示帐号:
15 |
16 | 用户名:admin123
17 | 密码:admin123
18 |
19 | ### 主要使用技术
20 |
21 | 环境需要
22 |
23 | - 1.运行环境:python3.8
24 | - 2.IDE环境:pycharm+mysql5.7
25 | - 3.数据库工具:Navicat15
26 | - 4.硬件环境:windows 10/11 8G内存以上;或者 Mac OS;
27 | - 5.数据库:MySql 5.7版本;
28 |
29 | 技术栈
30 | - 后端:python+django
31 | - 前端:vue+CSS+JavaScript+jQuery+antdesign
32 |
33 |
34 |
35 |
36 | ### 代码结构
37 |
38 | - server目录是后端代码
39 | - web目录是前端代码
40 |
41 | ### 部署运行
42 |
43 | #### 后端运行步骤
44 |
45 | (1) 安装python 3.8
46 |
47 | (2) 安装依赖。进入server目录下,执行 pip install -r requirements.txt
48 |
49 | (3) 安装mysql 5.7数据库,并创建数据库,命名为xxx,创建SQL如下:
50 | ```
51 | CREATE DATABASE IF NOT EXISTS python_yanglao DEFAULT CHARSET utf8 COLLATE utf8_general_ci
52 | ```
53 | (4) 恢复xxx.sql数据。在mysql下依次执行如下命令:
54 |
55 | ```
56 | mysql> use python_yanglao;
57 | mysql> source D:/xxx/xxx/xxx.sql;
58 | ```
59 |
60 | (5) 启动django服务。在server目录下执行:
61 | ```
62 | python manage.py runserver
63 | ```
64 |
65 | #### 前端运行步骤
66 |
67 | (1) 安装node 16.14
68 |
69 | (2) 进入web目录下,安装依赖,执行:
70 | ```
71 | npm install
72 | ```
73 | (3) 运行项目
74 | ```
75 | npm run dev
76 | ```
77 |
78 | #### 常见问题
79 |
80 | - 连接后端失败怎么办?
81 |
82 | 编辑前端的constants.js文件,将base_url设置为你自己电脑的ip和端口
83 |
84 | - 需要什么数据库版本?
85 |
86 | 本系统采用的是mysql5.7开发的,理论上5.7以上都支持
87 |
88 | - pip安装依赖失败怎么样?
89 |
90 | 建议使用国内镜像源安装
91 |
92 |
93 | ### 付费咨询
94 |
95 | 微信(lengqin1024)
96 |
97 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-web-app",
3 | "version": "0.1.2",
4 | "author": "lengqin1024",
5 | "scripts": {
6 | "dev": "vite --mode development",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@ant-design/icons-vue": "^6.1.0",
12 | "@vueuse/components": "^9.10.0",
13 | "@vueuse/core": "^9.10.0",
14 | "ant-design-vue": "^3.2.20",
15 | "axios": "^1.2.2",
16 | "pinia": "^2.0.28",
17 | "pinia-plugin-persistedstate": "^3.0.2",
18 | "qs": "^6.11.0",
19 | "vue": "^3.2.45",
20 | "vue-router": "^4.1.6"
21 | },
22 | "devDependencies": {
23 | "@types/qs": "^6.9.7",
24 | "@typescript-eslint/eslint-plugin": "^5.48.0",
25 | "@typescript-eslint/parser": "^5.48.0",
26 | "@vitejs/plugin-vue": "^4.0.0",
27 | "@vitejs/plugin-vue-jsx": "^3.0.0",
28 | "autoprefixer": "^10.4.13",
29 | "eslint": "8.22.0",
30 | "eslint-config-prettier": "^8.6.0",
31 | "eslint-define-config": "^1.13.0",
32 | "eslint-plugin-prettier": "^4.2.1",
33 | "eslint-plugin-vue": "^9.8.0",
34 | "less": "^4.1.3",
35 | "less-loader": "^11.1.0",
36 | "postcss": "^8.4.21",
37 | "postcss-html": "^1.5.0",
38 | "postcss-less": "^6.0.0",
39 | "prettier": "^2.8.3",
40 | "rollup-plugin-visualizer": "^5.9.0",
41 | "stylelint": "^14.16.1",
42 | "stylelint-config-standard": "^29.0.0",
43 | "stylelint-order": "^6.0.1",
44 | "typescript": "4.9.4",
45 | "unplugin-auto-import": "^0.12.2",
46 | "vite": "^4.0.3",
47 | "vite-plugin-compression": "^0.5.1",
48 | "vite-plugin-progress": "^0.0.6",
49 | "vite-plugin-restart": "^0.3.1"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/web/src/assets/images/cart-icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/web/src/assets/icons/svg/ts.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | // import { resolve } from 'path';
2 | // const fs = require('fs');
3 | //
4 | // function pathResolve(dir: string) {
5 | // return resolve(process.cwd(), '.', dir);
6 | // }
7 | //
8 | // export const getFolder = (path: any) => {
9 | // const components: Array = [];
10 | // const files = fs.readdirSync(path);
11 | // files.forEach(function (item: string) {
12 | // const stat = fs.lstatSync(path + '/' + item);
13 | // if (stat.isDirectory() === true && item != 'components') {
14 | // components.push(path + '/' + item);
15 | // components.push(pathResolve(path + '/' + item));
16 | // }
17 | // });
18 | // return components;
19 | // };
20 |
21 | export function getFormatTime(dateTime,flag) {
22 | if(dateTime != null ) {
23 | //若传入的dateTime为字符串类型,需要进行转换成数值,若不是无需下面注释代码
24 | var time = parseInt(dateTime)
25 | var date = new Date(time);
26 | //获取年份
27 | var YY = date.getFullYear();
28 | //获取月份
29 | var MM = (date.getMonth() + 1 < 10 ? '0'+(date.getMonth() + 1) : date.getMonth() + 1);
30 | //获取日期
31 | var DD = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate());
32 | if(flag) { //flag为true,显示时分秒格式
33 | //获取小时
34 | var hh = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours());
35 | //获取分
36 | var mm = (date.getMinutes() < 10 ? '0'+date.getMinutes() : date.getMinutes());
37 | ///获取秒
38 | var ss = (date.getSeconds() < 10 ? '0'+date.getSeconds() : date.getSeconds());
39 | //返回时间格式: 2020-11-09 13:14:52
40 | return YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss;
41 | } else {
42 | //返回时间格式: 2020-11-09
43 | return YY + '-' + MM + '-' + DD;
44 | }
45 | } else {
46 | return "";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/readme.md:
--------------------------------------------------------------------------------
1 | ## 后端部署步骤
2 |
3 |
4 |
5 | 1. 安装mysql数据库,启动服务
6 | 2. 打开cmd命令行,进入mysql,并新建数据库
7 | ```
8 | mysql -u root -p
9 | CREATE DATABASE IF NOT EXISTS python_yanglao DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
10 | ```
11 | 3. 恢复sql数据
12 | ```
13 | use python_yanglao
14 | source python_yanglao.sql
15 | ```
16 | 4. 修改settings.py中的配置信息(数据库密码等)
17 | 5. 复制资源,将upload文件夹复制到server目录下
18 | 6. 安装python 3.8
19 | 7. 安装依赖包
20 | ```
21 | pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
22 | ```
23 | 运行项目
24 | ```
25 | python manage.py runserver
26 | ```
27 | 7. 后期维护改动
28 |
29 | 将修改的py文件覆盖服务器的py文件即可,重启django
30 |
31 | ## 删除数据库
32 |
33 | drop database if exists xxxx;
34 |
35 | ## 创建数据库
36 |
37 | CREATE DATABASE IF NOT EXISTS xxxx DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
38 |
39 |
40 | ## 迁移数据库表(或使用source命令导入)
41 |
42 | ```
43 | python manage.py makemigrations;
44 |
45 | python manage.py migrate;
46 |
47 | python manage.py makemigrations myapp;
48 |
49 | python manage.py migrate myapp;
50 | ```
51 |
52 |
53 | ## 跨域配置
54 |
55 | django-cors-headers
56 |
57 | ## 多对多技术参考
58 |
59 | https://www.cnblogs.com/SunshineKimi/p/14140900.html
60 |
61 | ## 二级分类设计
62 | https://blog.csdn.net/weixin_47971206/article/details/124199978
63 |
64 | ## 常见问题
65 |
66 | 多对多的查询可通过related_name别名查询
67 | join查询
68 | ForeignKey的时候字段会自动加_id后缀
69 | 学习SerializerMethodField
70 | 跨域配置 django-cors-headers
71 | 数据库备份命令:
72 | mysqldump -u root -p --databases 数据库名称 > xxx.sql
73 | 数据库还原命令:
74 | source D:/xxx/xxx/xxx.sql;
75 | 创建管理员命令:
76 | insert into b_user(username,password,role,status) values('admin111',md5('admin111'),1,'0');
77 |
78 | 接口请求频次限制
79 |
80 |
81 | ## 登录接口
82 |
83 | 调login -> 生成token
84 |
85 |
86 | ## 注意
87 |
88 | update接口的时候,如果model里面存在多对多字段,则需要设置explode
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/web/src/assets/images/recommend-hover.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/web/src/store/modules/user/index.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia';
2 | import {loginApi as adminLogin} from '/@/api/user';
3 | import { setToken, clearToken } from '/@/utils/auth';
4 | import { UserState } from './types';
5 | import {USER_ID, USER_NAME, USER_TOKEN, ADMIN_USER_ID,ADMIN_USER_NAME,ADMIN_USER_TOKEN} from "/@/store/constants";
6 |
7 | export const useUserStore = defineStore('user', {
8 | state: (): UserState => ({
9 | user_id: undefined,
10 | user_name: undefined,
11 | user_token: undefined,
12 |
13 | admin_user_id: undefined,
14 | admin_user_name: undefined,
15 | admin_user_token: undefined,
16 | }),
17 | getters: {},
18 | actions: {
19 |
20 | // 管理员登录
21 | async adminLogin(loginForm) {
22 | const result = await adminLogin(loginForm);
23 | console.log('result==>', result)
24 |
25 | if(result.code === 0) {
26 | this.$patch((state)=>{
27 | state.admin_user_id = result.data.id
28 | state.admin_user_name = result.data.username
29 | state.admin_user_token = result.data.admin_token
30 | console.log('state==>', state)
31 | })
32 |
33 | localStorage.setItem(ADMIN_USER_TOKEN, result.data.admin_token)
34 | localStorage.setItem(ADMIN_USER_NAME, result.data.username)
35 | localStorage.setItem(ADMIN_USER_ID, result.data.id)
36 | }
37 |
38 | return result;
39 | },
40 | // 管理员登出
41 | async adminLogout() {
42 | // await userLogout();
43 | this.$patch((state)=>{
44 | localStorage.removeItem(ADMIN_USER_ID)
45 | localStorage.removeItem(ADMIN_USER_NAME)
46 | localStorage.removeItem(ADMIN_USER_TOKEN)
47 |
48 | state.admin_user_id = undefined
49 | state.admin_user_name = undefined
50 | state.admin_user_token = undefined
51 | })
52 | },
53 | },
54 | });
55 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/loginLog.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import LoginLog
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import LoginLogSerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | loginLogs = LoginLog.objects.all().order_by('-log_time')
15 | serializer = LoginLogSerializer(loginLogs, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | def create(request):
21 |
22 | serializer = LoginLogSerializer(data=request.data)
23 | if serializer.is_valid():
24 | serializer.save()
25 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
26 |
27 | return APIResponse(code=1, msg='创建失败')
28 |
29 |
30 | @api_view(['POST'])
31 | @authentication_classes([AdminTokenAuthtication])
32 | def update(request):
33 | try:
34 | pk = request.GET.get('id', -1)
35 | loginLogs = LoginLog.objects.get(pk=pk)
36 | except LoginLog.DoesNotExist:
37 | return APIResponse(code=1, msg='对象不存在')
38 |
39 | serializer = LoginLogSerializer(loginLogs, data=request.data)
40 | if serializer.is_valid():
41 | serializer.save()
42 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
43 |
44 | return APIResponse(code=1, msg='更新失败')
45 |
46 |
47 | @api_view(['POST'])
48 | @authentication_classes([AdminTokenAuthtication])
49 | def delete(request):
50 | if isDemoAdminUser(request):
51 | return APIResponse(code=1, msg='演示帐号无法操作')
52 |
53 | try:
54 | ids = request.GET.get('ids')
55 | ids_arr = ids.split(',')
56 | LoginLog.objects.filter(id__in=ids_arr).delete()
57 | except LoginLog.DoesNotExist:
58 | return APIResponse(code=1, msg='对象不存在')
59 |
60 | return APIResponse(code=0, msg='删除成功')
61 |
--------------------------------------------------------------------------------
/web/.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: ['plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
19 | rules: {
20 | 'vue/script-setup-uses-vars': 'error',
21 | '@typescript-eslint/ban-ts-ignore': 'off',
22 | '@typescript-eslint/explicit-function-return-type': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | '@typescript-eslint/no-var-requires': 'off',
25 | '@typescript-eslint/no-empty-function': 'off',
26 | 'vue/custom-event-name-casing': 'off',
27 | 'no-use-before-define': 'off',
28 | '@typescript-eslint/no-use-before-define': 'off',
29 | '@typescript-eslint/ban-ts-comment': 'off',
30 | '@typescript-eslint/ban-types': 'off',
31 | '@typescript-eslint/no-non-null-assertion': 'off',
32 | '@typescript-eslint/explicit-module-boundary-types': 'off',
33 | '@typescript-eslint/no-unused-vars': 'off',
34 | 'no-unused-vars': 'off',
35 | 'space-before-function-paren': 'off',
36 |
37 | 'vue/attributes-order': 'off',
38 | 'vue/one-component-per-file': 'off',
39 | 'vue/html-closing-bracket-newline': 'off',
40 | 'vue/max-attributes-per-line': 'off',
41 | 'vue/multiline-html-element-content-newline': 'off',
42 | 'vue/singleline-html-element-content-newline': 'off',
43 | 'vue/attribute-hyphenation': 'off',
44 | 'vue/require-default-prop': 'off',
45 | 'vue/require-explicit-emits': 'off',
46 | 'vue/html-self-closing': [
47 | 'error',
48 | {
49 | html: {
50 | void: 'always',
51 | normal: 'never',
52 | component: 'always',
53 | },
54 | svg: 'always',
55 | math: 'always',
56 | },
57 | ],
58 | 'vue/multi-word-component-names': 'off',
59 | },
60 | };
61 |
--------------------------------------------------------------------------------
/server/myapp/utils.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import hashlib
3 | import time
4 |
5 | from rest_framework.views import exception_handler
6 |
7 | from myapp.serializers import ErrorLogSerializer
8 |
9 | def get_timestamp():
10 | return int(round(time.time() * 1000))
11 |
12 | def md5value(key):
13 | input_name = hashlib.md5()
14 | input_name.update(key.encode("utf-8"))
15 | md5str = (input_name.hexdigest()).lower()
16 | print('计算md5:', md5str)
17 | return md5str
18 |
19 |
20 | def dict_fetchall(cursor): # cursor是执行sql_str后的记录,作入参
21 | columns = [col[0] for col in cursor.description] # 得到域的名字col[0],组成List
22 | return [
23 | dict(zip(columns, row)) for row in cursor.fetchall()
24 | ]
25 |
26 |
27 | def get_ip(request):
28 | """
29 | 获取请求者的IP信息
30 | """
31 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
32 | if x_forwarded_for:
33 | ip = x_forwarded_for.split(',')[0]
34 | else:
35 | ip = request.META.get('REMOTE_ADDR')
36 | return ip
37 |
38 |
39 | def get_ua(request):
40 | """
41 | 获取请求者的IP信息
42 | """
43 | ua = request.META.get('HTTP_USER_AGENT')
44 | return ua[0:200]
45 |
46 |
47 | def getWeekDays():
48 | """
49 | 获取近一周的日期
50 | """
51 | week_days = []
52 | now = datetime.datetime.now()
53 | for i in range(7):
54 | day = now - datetime.timedelta(days=i)
55 | week_days.append(day.strftime('%Y-%m-%d %H:%M:%S.%f')[:10])
56 | week_days.reverse() # 逆序
57 | return week_days
58 |
59 |
60 | def get_monday():
61 | """
62 | 获取本周周一日期
63 | """
64 | now = datetime.datetime.now()
65 | monday = now - datetime.timedelta(now.weekday())
66 | return monday.strftime('%Y-%m-%d %H:%M:%S.%f')[:10]
67 |
68 |
69 | def log_error(request, content):
70 | """
71 | 记录错误日志
72 | """
73 | ip = get_ip(request)
74 | method = request.method
75 | url = request.path
76 |
77 | data = {
78 | 'ip': ip,
79 | 'method': method,
80 | 'url': url,
81 | 'content': content
82 | }
83 |
84 | # 入库
85 | serializer = ErrorLogSerializer(data=data)
86 | if serializer.is_valid():
87 | serializer.save()
88 |
--------------------------------------------------------------------------------
/web/src/assets/images/setting-card-icon.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/web/src/assets/images/wb-share.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/banner.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import Banner
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import BannerSerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | banners = Banner.objects.all().order_by('-create_time')
15 | serializer = BannerSerializer(banners, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | @authentication_classes([AdminTokenAuthtication])
21 | def create(request):
22 | if isDemoAdminUser(request):
23 | return APIResponse(code=1, msg='演示帐号无法操作')
24 |
25 | serializer = BannerSerializer(data=request.data)
26 | if serializer.is_valid():
27 | serializer.save()
28 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
29 |
30 | return APIResponse(code=1, msg='创建失败')
31 |
32 |
33 | @api_view(['POST'])
34 | @authentication_classes([AdminTokenAuthtication])
35 | def update(request):
36 | if isDemoAdminUser(request):
37 | return APIResponse(code=1, msg='演示帐号无法操作')
38 |
39 | try:
40 | pk = request.GET.get('id', -1)
41 | banner = Banner.objects.get(pk=pk)
42 | except Banner.DoesNotExist:
43 | return APIResponse(code=1, msg='对象不存在')
44 |
45 | serializer = BannerSerializer(banner, data=request.data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
49 | else:
50 | print(serializer.errors)
51 |
52 | return APIResponse(code=1, msg='更新失败')
53 |
54 |
55 | @api_view(['POST'])
56 | @authentication_classes([AdminTokenAuthtication])
57 | def delete(request):
58 | if isDemoAdminUser(request):
59 | return APIResponse(code=1, msg='演示帐号无法操作')
60 |
61 | try:
62 | ids = request.GET.get('ids')
63 | ids_arr = ids.split(',')
64 | Banner.objects.filter(id__in=ids_arr).delete()
65 | except Banner.DoesNotExist:
66 | return APIResponse(code=1, msg='对象不存在')
67 |
68 | return APIResponse(code=0, msg='删除成功')
69 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/notice.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import Notice
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import NoticeSerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | notices = Notice.objects.all().order_by('-create_time')
15 | serializer = NoticeSerializer(notices, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | @authentication_classes([AdminTokenAuthtication])
21 | def create(request):
22 | if isDemoAdminUser(request):
23 | return APIResponse(code=1, msg='演示帐号无法操作')
24 |
25 | serializer = NoticeSerializer(data=request.data)
26 | if serializer.is_valid():
27 | serializer.save()
28 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
29 |
30 | return APIResponse(code=1, msg='创建失败')
31 |
32 |
33 | @api_view(['POST'])
34 | @authentication_classes([AdminTokenAuthtication])
35 | def update(request):
36 | if isDemoAdminUser(request):
37 | return APIResponse(code=1, msg='演示帐号无法操作')
38 |
39 | try:
40 | pk = request.GET.get('id', -1)
41 | notice = Notice.objects.get(pk=pk)
42 | except Notice.DoesNotExist:
43 | return APIResponse(code=1, msg='对象不存在')
44 |
45 | serializer = NoticeSerializer(notice, data=request.data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
49 | else:
50 | print(serializer.errors)
51 |
52 | return APIResponse(code=1, msg='更新失败')
53 |
54 |
55 | @api_view(['POST'])
56 | @authentication_classes([AdminTokenAuthtication])
57 | def delete(request):
58 | if isDemoAdminUser(request):
59 | return APIResponse(code=1, msg='演示帐号无法操作')
60 |
61 | try:
62 | ids = request.GET.get('ids')
63 | ids_arr = ids.split(',')
64 | Notice.objects.filter(id__in=ids_arr).delete()
65 | except Notice.DoesNotExist:
66 | return APIResponse(code=1, msg='对象不存在')
67 |
68 | return APIResponse(code=0, msg='删除成功')
69 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/worker.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import Worker
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import WorkerSerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | workers = Worker.objects.all().order_by('-create_time')
15 | serializer = WorkerSerializer(workers, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | @authentication_classes([AdminTokenAuthtication])
21 | def create(request):
22 | if isDemoAdminUser(request):
23 | return APIResponse(code=1, msg='演示帐号无法操作')
24 |
25 | serializer = WorkerSerializer(data=request.data)
26 | if serializer.is_valid():
27 | serializer.save()
28 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
29 |
30 | return APIResponse(code=1, msg='创建失败')
31 |
32 |
33 | @api_view(['POST'])
34 | @authentication_classes([AdminTokenAuthtication])
35 | def update(request):
36 | if isDemoAdminUser(request):
37 | return APIResponse(code=1, msg='演示帐号无法操作')
38 |
39 | try:
40 | pk = request.GET.get('id', -1)
41 | worker = Worker.objects.get(pk=pk)
42 | except Worker.DoesNotExist:
43 | return APIResponse(code=1, msg='对象不存在')
44 |
45 | serializer = WorkerSerializer(worker, data=request.data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
49 | else:
50 | print(serializer.errors)
51 |
52 | return APIResponse(code=1, msg='更新失败')
53 |
54 |
55 | @api_view(['POST'])
56 | @authentication_classes([AdminTokenAuthtication])
57 | def delete(request):
58 | if isDemoAdminUser(request):
59 | return APIResponse(code=1, msg='演示帐号无法操作')
60 |
61 | try:
62 | ids = request.GET.get('ids')
63 | ids_arr = ids.split(',')
64 | Worker.objects.filter(id__in=ids_arr).delete()
65 | except Worker.DoesNotExist:
66 | return APIResponse(code=1, msg='对象不存在')
67 |
68 | return APIResponse(code=0, msg='删除成功')
69 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/family.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import Family
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import FamilySerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | families = Family.objects.all().order_by('-create_time')
15 | serializer = FamilySerializer(families, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | @authentication_classes([AdminTokenAuthtication])
21 | def create(request):
22 | if isDemoAdminUser(request):
23 | return APIResponse(code=1, msg='演示帐号无法操作')
24 |
25 | serializer = FamilySerializer(data=request.data)
26 | if serializer.is_valid():
27 | serializer.save()
28 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
29 |
30 | return APIResponse(code=1, msg='创建失败')
31 |
32 |
33 | @api_view(['POST'])
34 | @authentication_classes([AdminTokenAuthtication])
35 | def update(request):
36 | if isDemoAdminUser(request):
37 | return APIResponse(code=1, msg='演示帐号无法操作')
38 |
39 | try:
40 | pk = request.GET.get('id', -1)
41 | family = Family.objects.get(pk=pk)
42 | except Family.DoesNotExist:
43 | return APIResponse(code=1, msg='对象不存在')
44 |
45 | serializer = FamilySerializer(family, data=request.data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
49 | else:
50 | print(serializer.errors)
51 |
52 | return APIResponse(code=1, msg='更新失败')
53 |
54 |
55 | @api_view(['POST'])
56 | @authentication_classes([AdminTokenAuthtication])
57 | def delete(request):
58 | if isDemoAdminUser(request):
59 | return APIResponse(code=1, msg='演示帐号无法操作')
60 |
61 | try:
62 | ids = request.GET.get('ids')
63 | ids_arr = ids.split(',')
64 | Family.objects.filter(id__in=ids_arr).delete()
65 | except Family.DoesNotExist:
66 | return APIResponse(code=1, msg='对象不存在')
67 |
68 | return APIResponse(code=0, msg='删除成功')
69 |
--------------------------------------------------------------------------------
/web/src/api/user.ts:
--------------------------------------------------------------------------------
1 | // 权限问题后期增加
2 | import { get, post } from '/@/utils/http/axios';
3 | import { UserState } from '/@/store/modules/user/types';
4 | // import axios from 'axios';
5 | enum URL {
6 | login = '/myapp/admin/adminLogin',
7 | userList = '/myapp/admin/user/list',
8 | detail = '/api/user/detail',
9 | create = '/myapp/admin/user/create',
10 | update = '/myapp/admin/user/update',
11 | delete = '/myapp/admin/user/delete',
12 | userLogin = '/api/user/userLogin',
13 | userRegister = '/api/user/userRegister',
14 | updateUserPwd = '/api/user/updatePwd',
15 | updateUserInfo = '/api/user/updateUserInfo'
16 | }
17 | interface LoginRes {
18 | token: string;
19 | }
20 |
21 | export interface LoginData {
22 | username: string;
23 | password: string;
24 | }
25 |
26 | const loginApi = async (data: LoginData) => post({ url: URL.login, data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
27 | const listApi = async (params: any) => get({ url: URL.userList, params: params, data: {}, headers: {} });
28 | const detailApi = async (params: any) => get({ url: URL.detail, params: params, data: {}, headers: {} });
29 | const createApi = async (data: any) => post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
30 | const updateApi = async (params: any, data: any) => post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
31 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
32 | const userLoginApi = async (data: LoginData) => post({ url: URL.userLogin, data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
33 | const userRegisterApi = async (data: any) => post({ url: URL.userRegister, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
34 | const updateUserPwdApi = async (params: any) => post({ url: URL.updateUserPwd, params: params });
35 | const updateUserInfoApi = async (data: any) => post({ url: URL.updateUserInfo, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } });
36 |
37 | export { loginApi, listApi, detailApi, createApi, updateApi, deleteApi, userLoginApi, userRegisterApi, updateUserPwdApi, updateUserInfoApi};
38 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/medical.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp.auth.authentication import AdminTokenAuthtication
5 | from myapp.handler import APIResponse
6 | from myapp.models import Medical
7 | from myapp.permission.permission import isDemoAdminUser
8 | from myapp.serializers import MedicalSerializer
9 |
10 |
11 | @api_view(['GET'])
12 | def list_api(request):
13 | if request.method == 'GET':
14 | medicals = Medical.objects.all().order_by('-create_time')
15 | serializer = MedicalSerializer(medicals, many=True)
16 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
17 |
18 |
19 | @api_view(['POST'])
20 | @authentication_classes([AdminTokenAuthtication])
21 | def create(request):
22 | if isDemoAdminUser(request):
23 | return APIResponse(code=1, msg='演示帐号无法操作')
24 |
25 | serializer = MedicalSerializer(data=request.data)
26 | if serializer.is_valid():
27 | serializer.save()
28 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
29 |
30 | return APIResponse(code=1, msg='创建失败')
31 |
32 |
33 | @api_view(['POST'])
34 | @authentication_classes([AdminTokenAuthtication])
35 | def update(request):
36 | if isDemoAdminUser(request):
37 | return APIResponse(code=1, msg='演示帐号无法操作')
38 |
39 | try:
40 | pk = request.GET.get('id', -1)
41 | medical = Medical.objects.get(pk=pk)
42 | except Medical.DoesNotExist:
43 | return APIResponse(code=1, msg='对象不存在')
44 |
45 | serializer = MedicalSerializer(medical, data=request.data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
49 | else:
50 | print(serializer.errors)
51 |
52 | return APIResponse(code=1, msg='更新失败')
53 |
54 |
55 | @api_view(['POST'])
56 | @authentication_classes([AdminTokenAuthtication])
57 | def delete(request):
58 | if isDemoAdminUser(request):
59 | return APIResponse(code=1, msg='演示帐号无法操作')
60 |
61 | try:
62 | ids = request.GET.get('ids')
63 | ids_arr = ids.split(',')
64 | Medical.objects.filter(id__in=ids_arr).delete()
65 | except Medical.DoesNotExist:
66 | return APIResponse(code=1, msg='对象不存在')
67 |
68 | return APIResponse(code=0, msg='删除成功')
69 |
--------------------------------------------------------------------------------
/web/src/utils/http/axios/index.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios';
3 | import { showMessage } from './status';
4 | import { IResponse } from './type';
5 | import { getToken } from '/@/utils/auth';
6 | import { TokenPrefix } from '/@/utils/auth';
7 | import {ADMIN_USER_TOKEN, USER_TOKEN, BASE_URL} from '/@/store/constants'
8 |
9 | const service: AxiosInstance = axios.create({
10 | baseURL: BASE_URL + '',
11 | timeout: 15000,
12 | });
13 |
14 | // axios实例拦截请求
15 | service.interceptors.request.use(
16 | (config: InternalAxiosRequestConfig) => {
17 |
18 | config.headers.ADMINTOKEN = localStorage.getItem(ADMIN_USER_TOKEN)
19 | config.headers.TOKEN = localStorage.getItem(USER_TOKEN)
20 |
21 | return config;
22 | },
23 | (error: AxiosError) => {
24 | return Promise.reject(error);
25 | },
26 | );
27 |
28 | // axios实例拦截响应
29 | service.interceptors.response.use(
30 | (response: AxiosResponse) => {
31 | if(response.status == 200) {
32 | if(response.data.code == 0 || response.data.code == 200) {
33 | return response
34 | }else {
35 | return Promise.reject(response.data)
36 | }
37 | } else {
38 | return Promise.reject(response.data)
39 | }
40 | },
41 | // 请求失败
42 | (error: any) => {
43 | console.log(error.response.status)
44 | if(error.response.status == 404) {
45 | // todo
46 | } else if(error.response.status == 403) {
47 | // todo
48 | }
49 | return Promise.reject(error)
50 | },
51 | );
52 |
53 |
54 |
55 | const request = (config: AxiosRequestConfig): Promise => {
56 | const conf = config;
57 | return new Promise((resolve, reject) => {
58 | service.request>(conf).then((res: AxiosResponse) => {
59 | const data = res.data
60 | resolve(data as T);
61 | }).catch(err => {
62 | reject(err)
63 | });
64 | });
65 | };
66 |
67 | export function get(config: AxiosRequestConfig): Promise {
68 | return request({ ...config, method: 'GET' });
69 | }
70 |
71 | export function post(config: AxiosRequestConfig): Promise {
72 | return request({ ...config, method: 'POST' });
73 | }
74 |
75 | export default request;
76 |
77 | export type { AxiosInstance, AxiosResponse };
78 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/tag.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from rest_framework.decorators import api_view, authentication_classes
3 |
4 | from myapp import utils
5 | from myapp.auth.authentication import AdminTokenAuthtication
6 | from myapp.handler import APIResponse
7 | from myapp.models import Tag
8 | from myapp.permission.permission import isDemoAdminUser
9 | from myapp.serializers import TagSerializer
10 |
11 |
12 | @api_view(['GET'])
13 | def list_api(request):
14 | if request.method == 'GET':
15 | tags = Tag.objects.all().order_by('-create_time')
16 | serializer = TagSerializer(tags, many=True)
17 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
18 |
19 |
20 | @api_view(['POST'])
21 | @authentication_classes([AdminTokenAuthtication])
22 | def create(request):
23 | if isDemoAdminUser(request):
24 | return APIResponse(code=1, msg='演示帐号无法操作')
25 |
26 | tags = Tag.objects.filter(title=request.data['title'])
27 | if len(tags) > 0:
28 | return APIResponse(code=1, msg='该名称已存在')
29 |
30 | serializer = TagSerializer(data=request.data)
31 | if serializer.is_valid():
32 | serializer.save()
33 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
34 | else:
35 | utils.log_error(request, '参数错误')
36 |
37 | return APIResponse(code=1, msg='创建失败')
38 |
39 |
40 | @api_view(['POST'])
41 | @authentication_classes([AdminTokenAuthtication])
42 | def update(request):
43 | if isDemoAdminUser(request):
44 | return APIResponse(code=1, msg='演示帐号无法操作')
45 |
46 | try:
47 | pk = request.GET.get('id', -1)
48 | tags = Tag.objects.get(pk=pk)
49 | except Tag.DoesNotExist:
50 | return APIResponse(code=1, msg='对象不存在')
51 |
52 | serializer = TagSerializer(tags, data=request.data)
53 | if serializer.is_valid():
54 | serializer.save()
55 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
56 | else:
57 | utils.log_error(request, '参数错误')
58 |
59 | return APIResponse(code=1, msg='更新失败')
60 |
61 |
62 | @api_view(['POST'])
63 | @authentication_classes([AdminTokenAuthtication])
64 | def delete(request):
65 | if isDemoAdminUser(request):
66 | return APIResponse(code=1, msg='演示帐号无法操作')
67 |
68 | try:
69 | ids = request.GET.get('ids')
70 | ids_arr = ids.split(',')
71 | Tag.objects.filter(id__in=ids_arr).delete()
72 | except Tag.DoesNotExist:
73 | return APIResponse(code=1, msg='对象不存在')
74 |
75 | return APIResponse(code=0, msg='删除成功')
76 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # 前端文档
2 |
3 |
4 | ### 运行步骤
5 |
6 | 1. npm install
7 | 2. npm run dev
8 |
9 | ### 部署步骤
10 |
11 | 1. 修改constants.ts中的BASE_URL
12 | 2. vite build
13 | 3. 将dist部署到nginx
14 |
15 | ### 开发步骤
16 |
17 | 共分三步:开发接口、开发view、开发路由
18 |
19 | 第一步:开发接口
20 |
21 | 在api文件夹下,新建请求文件,然后写入api请求代码,如下
22 |
23 | ```
24 | import { get, post } from '/@/utils/http/axios';
25 |
26 | enum URL {
27 | list = '/myapp/admin/notice/list',
28 | create = '/myapp/admin/notice/create',
29 | update = '/myapp/admin/notice/update',
30 | delete = '/myapp/admin/notice/delete',
31 | }
32 |
33 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} });
34 | const createApi = async (data: any) =>
35 | post({
36 | url: URL.create,
37 | params: {},
38 | data: data,
39 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
40 | });
41 | const updateApi = async (params: any, data: any) =>
42 | post({
43 | url: URL.update,
44 | params: params,
45 | data: data,
46 | headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
47 | });
48 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} });
49 |
50 | export { listApi, createApi, updateApi, deleteApi };
51 |
52 | ```
53 |
54 | 第二步:开发view页面
55 |
56 | 在view文件夹下,新增页面vue文件,然后写入页面代码,比如user.vue
57 |
58 | 第三步:设置路由
59 |
60 | 在router的root.js文件里面配置路由地址。如下
61 |
62 | ```
63 | { path: 'overview', name: 'overview', component: () => import('/@/views/overview.vue') },
64 | { path: 'repair', name: 'repair', component: () => import('/@/views/repair.vue') },
65 | { path: 'asset', name: 'asset', component: () => import('/@/views/asset.vue') },
66 | { path: 'park', name: 'park', component: () => import('/@/views/park.vue') },
67 | { path: 'thing', name: 'thing', component: () => import('/@/views/thing.vue') },
68 | { path: 'comment', name: 'comment', component: () => import('/@/views/comment.vue') },
69 | { path: 'user', name: 'user', component: () => import('/@/views/user.vue') },
70 | ```
71 |
72 | 即可
73 |
74 | ### 配置解释
75 |
76 | 1. env.development 开发环境配置
77 | 2. eslintrc.js 代码规范化提示
78 | 3. vite.config.js vite 开发服务器配置
79 |
80 | ### 常见问题
81 |
82 | #### 变量
83 | https://blog.csdn.net/qq_41636947/article/details/117907448
84 |
85 | #### antd的css引入方式
86 | 在index.html里面引入的cdn
87 |
88 | #### cdn
89 | https://cdn.jsdelivr.net/npm/ant-design-vue@3.2.20/dist/
90 | https://cdn.staticfile.org/ant-design-vue/3.2.20/antd.min.css
91 |
92 | ### public文件夹内容在build后会自动打到dist中
93 |
--------------------------------------------------------------------------------
/web/stylelint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | plugins: ['stylelint-order'],
4 | extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
5 | customSyntax: 'postcss-html',
6 | rules: {
7 | 'function-no-unknown': null,
8 | 'selector-class-pattern': null,
9 | 'selector-pseudo-class-no-unknown': [
10 | true,
11 | {
12 | ignorePseudoClasses: ['global'],
13 | },
14 | ],
15 | 'selector-pseudo-element-no-unknown': [
16 | true,
17 | {
18 | ignorePseudoElements: ['v-deep'],
19 | },
20 | ],
21 | 'at-rule-no-unknown': [
22 | true,
23 | {
24 | ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen', 'function', 'if', 'each', 'include', 'mixin'],
25 | },
26 | ],
27 | 'no-empty-source': null,
28 | 'string-quotes': null,
29 | 'named-grid-areas-no-invalid': null,
30 | 'unicode-bom': 'never',
31 | 'no-descending-specificity': null,
32 | 'font-family-no-missing-generic-family-keyword': null,
33 | 'declaration-colon-space-after': 'always-single-line',
34 | 'declaration-colon-space-before': 'never',
35 | // 'declaration-block-trailing-semicolon': 'always',
36 | 'rule-empty-line-before': [
37 | 'always',
38 | {
39 | ignore: ['after-comment', 'first-nested'],
40 | },
41 | ],
42 | 'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
43 | 'order/order': [
44 | [
45 | 'dollar-variables',
46 | 'custom-properties',
47 | 'at-rules',
48 | 'declarations',
49 | {
50 | type: 'at-rule',
51 | name: 'supports',
52 | },
53 | {
54 | type: 'at-rule',
55 | name: 'media',
56 | },
57 | 'rules',
58 | ],
59 | { severity: 'warning' },
60 | ],
61 | },
62 | ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
63 | overrides: [
64 | {
65 | files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
66 | extends: ['stylelint-config-recommended'],
67 | rules: {
68 | 'keyframes-name-pattern': null,
69 | 'selector-pseudo-class-no-unknown': [
70 | true,
71 | {
72 | ignorePseudoClasses: ['deep', 'global'],
73 | },
74 | ],
75 | 'selector-pseudo-element-no-unknown': [
76 | true,
77 | {
78 | ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'],
79 | },
80 | ],
81 | },
82 | },
83 | {
84 | files: ['*.less', '**/*.less'],
85 | customSyntax: 'postcss-less',
86 | extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'],
87 | },
88 | ],
89 | };
90 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/classification.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | from django.db import connection
3 | from django.db.models import Q
4 | from rest_framework.decorators import api_view, authentication_classes
5 |
6 | from myapp.auth.authentication import AdminTokenAuthtication
7 | from myapp.handler import APIResponse
8 | from myapp.models import Classification
9 | from myapp.permission.permission import isDemoAdminUser
10 | from myapp.serializers import ClassificationSerializer
11 | from myapp.utils import dict_fetchall
12 |
13 |
14 | @api_view(['GET'])
15 | def list_api(request):
16 | if request.method == 'GET':
17 | classifications = Classification.objects.all().order_by('-create_time')
18 | serializer = ClassificationSerializer(classifications, many=True)
19 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
20 |
21 |
22 | @api_view(['POST'])
23 | @authentication_classes([AdminTokenAuthtication])
24 | def create(request):
25 | if isDemoAdminUser(request):
26 | return APIResponse(code=1, msg='演示帐号无法操作')
27 |
28 | classification = Classification.objects.filter(title=request.data['title'])
29 | if len(classification) > 0:
30 | return APIResponse(code=1, msg='该名称已存在')
31 |
32 | serializer = ClassificationSerializer(data=request.data)
33 | if serializer.is_valid():
34 | serializer.save()
35 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
36 |
37 | return APIResponse(code=1, msg='创建失败')
38 |
39 |
40 | @api_view(['POST'])
41 | @authentication_classes([AdminTokenAuthtication])
42 | def update(request):
43 | if isDemoAdminUser(request):
44 | return APIResponse(code=1, msg='演示帐号无法操作')
45 |
46 | try:
47 | pk = request.GET.get('id', -1)
48 | print(pk)
49 | classification = Classification.objects.get(pk=pk)
50 | except Classification.DoesNotExist:
51 | return APIResponse(code=1, msg='对象不存在')
52 |
53 | serializer = ClassificationSerializer(classification, data=request.data)
54 | if serializer.is_valid():
55 | serializer.save()
56 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
57 |
58 | return APIResponse(code=1, msg='更新失败')
59 |
60 |
61 | @api_view(['POST'])
62 | @authentication_classes([AdminTokenAuthtication])
63 | def delete(request):
64 | if isDemoAdminUser(request):
65 | return APIResponse(code=1, msg='演示帐号无法操作')
66 |
67 | try:
68 | ids = request.GET.get('ids')
69 | ids_arr = ids.split(',')
70 | # 删除自身和自身的子孩子
71 | Classification.objects.filter(Q(id__in=ids_arr)).delete()
72 | except Classification.DoesNotExist:
73 | return APIResponse(code=1, msg='对象不存在')
74 | return APIResponse(code=0, msg='删除成功')
75 |
--------------------------------------------------------------------------------
/web/src/views/sys-info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | {{ data.sysName }}
11 |
12 |
13 | {{ data.versionName }}
14 |
15 |
16 | {{ data.osName }}
17 |
18 |
19 | {{ data.pf }}
20 |
21 |
22 | {{ data.cpuCount }}
23 |
24 |
25 | {{ data.processor }}
26 |
27 |
28 | {{ data.cpuLoad }}%
29 |
30 |
31 | {{ data.memory }}G
32 |
33 |
34 | {{ data.usedMemory }}G
35 |
36 |
37 | {{ data.percentMemory }}%
38 |
39 |
40 | {{ data.sysLan }}
41 |
42 |
43 | 5.7.37
44 |
45 |
46 | 1.20.1
47 |
48 |
49 | 3.2
50 |
51 |
52 | {{ data.sysZone }}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
83 |
84 |
102 |
--------------------------------------------------------------------------------
/web/types/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/core/pull/3399
4 | import '@vue/runtime-core'
5 |
6 | export {}
7 |
8 | declare module '@vue/runtime-core' {
9 | export interface GlobalComponents {
10 | AButton: typeof import('ant-design-vue/es')['Button']
11 | ACard: typeof import('ant-design-vue/es')['Card']
12 | ACol: typeof import('ant-design-vue/es')['Col']
13 | AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
14 | ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
15 | ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
16 | ADivider: typeof import('ant-design-vue/es')['Divider']
17 | ADrawer: typeof import('ant-design-vue/es')['Drawer']
18 | ADropdown: typeof import('ant-design-vue/es')['Dropdown']
19 | AForm: typeof import('ant-design-vue/es')['Form']
20 | AFormItem: typeof import('ant-design-vue/es')['FormItem']
21 | AInput: typeof import('ant-design-vue/es')['Input']
22 | AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
23 | AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
24 | AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
25 | ALayout: typeof import('ant-design-vue/es')['Layout']
26 | ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
27 | ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
28 | ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
29 | AMenu: typeof import('ant-design-vue/es')['Menu']
30 | AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
31 | AModal: typeof import('ant-design-vue/es')['Modal']
32 | APagination: typeof import('ant-design-vue/es')['Pagination']
33 | APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
34 | ARow: typeof import('ant-design-vue/es')['Row']
35 | ASelect: typeof import('ant-design-vue/es')['Select']
36 | ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
37 | ASpace: typeof import('ant-design-vue/es')['Space']
38 | ASpin: typeof import('ant-design-vue/es')['Spin']
39 | ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
40 | ASwitch: typeof import('ant-design-vue/es')['Switch']
41 | ATable: typeof import('ant-design-vue/es')['Table']
42 | ATabPane: typeof import('ant-design-vue/es')['TabPane']
43 | ATabs: typeof import('ant-design-vue/es')['Tabs']
44 | ATag: typeof import('ant-design-vue/es')['Tag']
45 | ATextarea: typeof import('ant-design-vue/es')['Textarea']
46 | ATree: typeof import('ant-design-vue/es')['Tree']
47 | ATreeSelect: typeof import('ant-design-vue/es')['TreeSelect']
48 | ATreeSelectNode: typeof import('ant-design-vue/es')['TreeSelectNode']
49 | AUpload: typeof import('ant-design-vue/es')['Upload']
50 | AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
51 | RouterLink: typeof import('vue-router')['RouterLink']
52 | RouterView: typeof import('vue-router')['RouterView']
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/server/myapp/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from myapp import views
4 |
5 | app_name = 'myapp'
6 | urlpatterns = [
7 | # api
8 | path('admin/overview/count', views.admin.overview.count),
9 | path('admin/overview/sysInfo', views.admin.overview.sysInfo),
10 | path('admin/family/list', views.admin.family.list_api),
11 | path('admin/family/create', views.admin.family.create),
12 | path('admin/family/update', views.admin.family.update),
13 | path('admin/family/delete', views.admin.family.delete),
14 | path('admin/medical/list', views.admin.medical.list_api),
15 | path('admin/medical/create', views.admin.medical.create),
16 | path('admin/medical/update', views.admin.medical.update),
17 | path('admin/medical/delete', views.admin.medical.delete),
18 | path('admin/worker/list', views.admin.worker.list_api),
19 | path('admin/worker/create', views.admin.worker.create),
20 | path('admin/worker/update', views.admin.worker.update),
21 | path('admin/worker/delete', views.admin.worker.delete),
22 | path('admin/thing/list', views.admin.thing.list_api),
23 | path('admin/thing/detail', views.admin.thing.detail),
24 | path('admin/thing/create', views.admin.thing.create),
25 | path('admin/thing/update', views.admin.thing.update),
26 | path('admin/thing/delete', views.admin.thing.delete),
27 | path('admin/classification/list', views.admin.classification.list_api),
28 | path('admin/classification/create', views.admin.classification.create),
29 | path('admin/classification/update', views.admin.classification.update),
30 | path('admin/classification/delete', views.admin.classification.delete),
31 | path('admin/tag/list', views.admin.tag.list_api),
32 | path('admin/tag/create', views.admin.tag.create),
33 | path('admin/tag/update', views.admin.tag.update),
34 | path('admin/tag/delete', views.admin.tag.delete),
35 | path('admin/banner/list', views.admin.banner.list_api),
36 | path('admin/banner/create', views.admin.banner.create),
37 | path('admin/banner/update', views.admin.banner.update),
38 | path('admin/banner/delete', views.admin.banner.delete),
39 | path('admin/notice/list', views.admin.notice.list_api),
40 | path('admin/notice/create', views.admin.notice.create),
41 | path('admin/notice/update', views.admin.notice.update),
42 | path('admin/notice/delete', views.admin.notice.delete),
43 | path('admin/loginLog/list', views.admin.loginLog.list_api),
44 | path('admin/loginLog/create', views.admin.loginLog.create),
45 | path('admin/loginLog/update', views.admin.loginLog.update),
46 | path('admin/loginLog/delete', views.admin.loginLog.delete),
47 | path('admin/opLog/list', views.admin.opLog.list_api),
48 | path('admin/errorLog/list', views.admin.errorLog.list_api),
49 | path('admin/user/list', views.admin.user.list_api),
50 | path('admin/user/create', views.admin.user.create),
51 | path('admin/user/update', views.admin.user.update),
52 | path('admin/user/updatePwd', views.admin.user.updatePwd),
53 | path('admin/user/delete', views.admin.user.delete),
54 | path('admin/user/info', views.admin.user.info),
55 | path('admin/adminLogin', views.admin.user.admin_login),
56 |
57 |
58 | ]
59 |
--------------------------------------------------------------------------------
/web/src/views/login-log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
23 |
24 | 编辑
25 |
26 |
27 | 删除
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
112 |
113 |
131 |
--------------------------------------------------------------------------------
/web/src/views/error-log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
23 |
24 | 编辑
25 |
26 |
27 | 删除
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
123 |
124 |
142 |
--------------------------------------------------------------------------------
/web/src/assets/images/banner2.svg:
--------------------------------------------------------------------------------
1 |
49 |
--------------------------------------------------------------------------------
/web/src/views/op-log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
23 |
24 | 编辑
25 |
26 |
27 | 删除
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
125 |
126 |
144 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/overview.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | import datetime
3 | import locale
4 | import platform
5 | import random
6 | import time
7 | from multiprocessing import cpu_count
8 |
9 | import psutil
10 | from django.db import connection
11 | from rest_framework.decorators import api_view, authentication_classes
12 |
13 | from myapp import utils
14 | from myapp.handler import APIResponse
15 |
16 | from myapp.models import Thing
17 | from myapp.utils import dict_fetchall
18 | from myapp.auth.authentication import AdminTokenAuthtication
19 |
20 |
21 | @api_view(['GET'])
22 | @authentication_classes([AdminTokenAuthtication])
23 | def count(request):
24 | if request.method == 'GET':
25 | now = datetime.datetime.now()
26 |
27 | # 统计排名(sql语句)
28 | sql_str = "select id, title, pv as count from b_thing order by pv desc limit 10 "
29 | with connection.cursor() as cursor:
30 | cursor.execute(sql_str)
31 | order_rank_data = dict_fetchall(cursor)
32 |
33 | # 统计分类比例(sql语句)
34 | sql_str = "select B.title, count(B.title) as count from b_thing A join B_classification B on " \
35 | "A.classification_id = B.id group by B.title order by count desc limit 5; "
36 | with connection.cursor() as cursor:
37 | cursor.execute(sql_str)
38 | classification_rank_data = dict_fetchall(cursor)
39 |
40 | # 统计最近一周访问量(sql语句)
41 | visit_data = []
42 | week_days = utils.getWeekDays()
43 | for day in week_days:
44 | sql_str = "select re_ip, count(re_ip) as count from b_op_log where re_time like '" + day + "%' group by re_ip"
45 | with connection.cursor() as cursor:
46 | cursor.execute(sql_str)
47 | ip_data = dict_fetchall(cursor)
48 | uv = len(ip_data)
49 | pv = 0
50 | for item in ip_data:
51 | pv = pv + item['count']
52 | visit_data.append({
53 | "day": day,
54 | "uv": uv + random.randint(1, 20),
55 | "pv": pv + random.randint(20, 100)
56 | })
57 |
58 | data = {
59 | 'order_rank_data': order_rank_data,
60 | 'classification_rank_data': classification_rank_data,
61 | 'visit_data': visit_data
62 | }
63 | return APIResponse(code=0, msg='查询成功', data=data)
64 |
65 |
66 | @api_view(['GET'])
67 | @authentication_classes([AdminTokenAuthtication])
68 | def sysInfo(request):
69 | if request.method == 'GET':
70 | pyVersion = platform.python_version()
71 | osBuild = platform.architecture()
72 | node = platform.node()
73 | pf = platform.platform()
74 | processor = platform.processor()
75 | pyComp = platform.python_compiler()
76 | osName = platform.system()
77 | memory = psutil.virtual_memory()
78 |
79 | data = {
80 | 'sysName': '后台管理平台',
81 | 'versionName': '1.1.0',
82 | 'osName': osName,
83 | 'pyVersion': pyVersion,
84 | 'osBuild': osBuild,
85 | 'node': node,
86 | 'pf': pf,
87 | 'processor': processor,
88 | 'cpuCount': cpu_count(),
89 | 'pyComp': pyComp,
90 | 'cpuLoad': round((psutil.cpu_percent(1)), 2),
91 | 'memory': round((float(memory.total) / 1024 / 1024 / 1024), 2),
92 | 'usedMemory': round((float(memory.used) / 1024 / 1024 / 1024), 2),
93 | 'percentMemory': round((float(memory.used) / float(memory.total) * 100), 2),
94 | 'sysLan': locale.getdefaultlocale(),
95 | 'sysZone': time.strftime('%Z', time.localtime())
96 | }
97 |
98 | return APIResponse(code=0, msg='查询成功', data=data)
99 |
--------------------------------------------------------------------------------
/web/src/assets/images/setting-push-icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/web/types/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by 'unplugin-auto-import'
2 | export {}
3 | declare global {
4 | const EffectScope: typeof import('vue')['EffectScope']
5 | const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
6 | const computed: typeof import('vue')['computed']
7 | const createApp: typeof import('vue')['createApp']
8 | const createPinia: typeof import('pinia')['createPinia']
9 | const customRef: typeof import('vue')['customRef']
10 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
11 | const defineComponent: typeof import('vue')['defineComponent']
12 | const defineStore: typeof import('pinia')['defineStore']
13 | const effectScope: typeof import('vue')['effectScope']
14 | const getActivePinia: typeof import('pinia')['getActivePinia']
15 | const getCurrentInstance: typeof import('vue')['getCurrentInstance']
16 | const getCurrentScope: typeof import('vue')['getCurrentScope']
17 | const h: typeof import('vue')['h']
18 | const inject: typeof import('vue')['inject']
19 | const isProxy: typeof import('vue')['isProxy']
20 | const isReactive: typeof import('vue')['isReactive']
21 | const isReadonly: typeof import('vue')['isReadonly']
22 | const isRef: typeof import('vue')['isRef']
23 | const mapActions: typeof import('pinia')['mapActions']
24 | const mapGetters: typeof import('pinia')['mapGetters']
25 | const mapState: typeof import('pinia')['mapState']
26 | const mapStores: typeof import('pinia')['mapStores']
27 | const mapWritableState: typeof import('pinia')['mapWritableState']
28 | const markRaw: typeof import('vue')['markRaw']
29 | const nextTick: typeof import('vue')['nextTick']
30 | const onActivated: typeof import('vue')['onActivated']
31 | const onBeforeMount: typeof import('vue')['onBeforeMount']
32 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
33 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
34 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
35 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
36 | const onDeactivated: typeof import('vue')['onDeactivated']
37 | const onErrorCaptured: typeof import('vue')['onErrorCaptured']
38 | const onMounted: typeof import('vue')['onMounted']
39 | const onRenderTracked: typeof import('vue')['onRenderTracked']
40 | const onRenderTriggered: typeof import('vue')['onRenderTriggered']
41 | const onScopeDispose: typeof import('vue')['onScopeDispose']
42 | const onServerPrefetch: typeof import('vue')['onServerPrefetch']
43 | const onUnmounted: typeof import('vue')['onUnmounted']
44 | const onUpdated: typeof import('vue')['onUpdated']
45 | const provide: typeof import('vue')['provide']
46 | const reactive: typeof import('vue')['reactive']
47 | const readonly: typeof import('vue')['readonly']
48 | const ref: typeof import('vue')['ref']
49 | const resolveComponent: typeof import('vue')['resolveComponent']
50 | const resolveDirective: typeof import('vue')['resolveDirective']
51 | const setActivePinia: typeof import('pinia')['setActivePinia']
52 | const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
53 | const shallowReactive: typeof import('vue')['shallowReactive']
54 | const shallowReadonly: typeof import('vue')['shallowReadonly']
55 | const shallowRef: typeof import('vue')['shallowRef']
56 | const storeToRefs: typeof import('pinia')['storeToRefs']
57 | const toRaw: typeof import('vue')['toRaw']
58 | const toRef: typeof import('vue')['toRef']
59 | const toRefs: typeof import('vue')['toRefs']
60 | const triggerRef: typeof import('vue')['triggerRef']
61 | const unref: typeof import('vue')['unref']
62 | const useAttrs: typeof import('vue')['useAttrs']
63 | const useCssModule: typeof import('vue')['useCssModule']
64 | const useCssVars: typeof import('vue')['useCssVars']
65 | const useDialog: typeof import('naive-ui')['useDialog']
66 | const useLink: typeof import('vue-router')['useLink']
67 | const useLoadingBar: typeof import('naive-ui')['useLoadingBar']
68 | const useMessage: typeof import('naive-ui')['useMessage']
69 | const useNotification: typeof import('naive-ui')['useNotification']
70 | const useRoute: typeof import('vue-router')['useRoute']
71 | const useRouter: typeof import('vue-router')['useRouter']
72 | const useSlots: typeof import('vue')['useSlots']
73 | const watch: typeof import('vue')['watch']
74 | const watchEffect: typeof import('vue')['watchEffect']
75 | const watchPostEffect: typeof import('vue')['watchPostEffect']
76 | const watchSyncEffect: typeof import('vue')['watchSyncEffect']
77 | }
78 |
--------------------------------------------------------------------------------
/server/myapp/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 |
3 | from myapp.models import Thing, Classification, Tag, User, LoginLog, OpLog, Banner, \
4 | Ad, Notice, ErrorLog, Address, Medical, Family, Worker
5 |
6 |
7 | class ThingSerializer(serializers.ModelSerializer):
8 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
9 | # 额外字段
10 | classification_title = serializers.ReadOnlyField(source='classification.title')
11 |
12 | class Meta:
13 | model = Thing
14 | fields = '__all__'
15 |
16 |
17 | class DetailThingSerializer(serializers.ModelSerializer):
18 | # 额外字段
19 | classification_title = serializers.ReadOnlyField(source='classification.title')
20 |
21 | class Meta:
22 | model = Thing
23 | # 排除多对多字段
24 | exclude = ('wish', 'collect',)
25 |
26 |
27 | class UpdateThingSerializer(serializers.ModelSerializer):
28 | # 额外字段
29 | classification_title = serializers.ReadOnlyField(source='classification.title')
30 |
31 | class Meta:
32 | model = Thing
33 | # 排除多对多字段
34 | exclude = ()
35 |
36 |
37 | class ListThingSerializer(serializers.ModelSerializer):
38 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
39 | # 额外字段
40 | classification_title = serializers.ReadOnlyField(source='classification.title')
41 |
42 | class Meta:
43 | model = Thing
44 | # 排除字段
45 | exclude = ('wish', 'collect', 'description',)
46 |
47 |
48 | class ClassificationSerializer(serializers.ModelSerializer):
49 | class Meta:
50 | model = Classification
51 | fields = '__all__'
52 |
53 |
54 | class TagSerializer(serializers.ModelSerializer):
55 | class Meta:
56 | model = Tag
57 | fields = '__all__'
58 |
59 |
60 | class UserSerializer(serializers.ModelSerializer):
61 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
62 |
63 | class Meta:
64 | model = User
65 | fields = '__all__'
66 | # exclude = ('password',)
67 |
68 |
69 | class LoginLogSerializer(serializers.ModelSerializer):
70 | log_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
71 |
72 | class Meta:
73 | model = LoginLog
74 | fields = '__all__'
75 |
76 |
77 | class OpLogSerializer(serializers.ModelSerializer):
78 | re_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
79 |
80 | class Meta:
81 | model = OpLog
82 | fields = '__all__'
83 |
84 |
85 | class ErrorLogSerializer(serializers.ModelSerializer):
86 | log_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
87 |
88 | class Meta:
89 | model = ErrorLog
90 | fields = '__all__'
91 |
92 |
93 | class BannerSerializer(serializers.ModelSerializer):
94 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
95 | # extra
96 | title = serializers.ReadOnlyField(source='thing.title')
97 |
98 | class Meta:
99 | model = Banner
100 | fields = '__all__'
101 |
102 |
103 | class AdSerializer(serializers.ModelSerializer):
104 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
105 |
106 | class Meta:
107 | model = Ad
108 | fields = '__all__'
109 |
110 |
111 | class NoticeSerializer(serializers.ModelSerializer):
112 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
113 |
114 | class Meta:
115 | model = Notice
116 | fields = '__all__'
117 |
118 |
119 | class MedicalSerializer(serializers.ModelSerializer):
120 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
121 |
122 | class Meta:
123 | model = Medical
124 | fields = '__all__'
125 |
126 |
127 | class FamilySerializer(serializers.ModelSerializer):
128 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
129 |
130 | class Meta:
131 | model = Family
132 | fields = '__all__'
133 |
134 |
135 | class WorkerSerializer(serializers.ModelSerializer):
136 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
137 |
138 | class Meta:
139 | model = Worker
140 | fields = '__all__'
141 |
142 |
143 | class AddressSerializer(serializers.ModelSerializer):
144 | create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
145 |
146 | class Meta:
147 | model = Address
148 | fields = '__all__'
149 |
--------------------------------------------------------------------------------
/server/server/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for server project.
3 |
4 | Generated by 'django-admin startproject' using Django 4.1.4.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/4.1/ref/settings/
11 | """
12 | import os
13 | from pathlib import Path
14 |
15 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
16 | # BASE_DIR = Path(__file__).resolve().parent.parent
17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'django-insecure-sz@madp0ifx!b)^lg_g!f+5s*w7w_=sjgq-k+erzb%x42$^r!d'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = ['*']
29 |
30 | # Application definition
31 |
32 | INSTALLED_APPS = [
33 | 'django.contrib.admin',
34 | 'django.contrib.auth',
35 | 'django.contrib.contenttypes',
36 | 'django.contrib.sessions',
37 | 'django.contrib.messages',
38 | 'django.contrib.staticfiles',
39 | 'rest_framework',
40 | 'corsheaders', # 跨域
41 | 'myapp'
42 | ]
43 |
44 | MIDDLEWARE = [
45 | 'django.middleware.security.SecurityMiddleware',
46 | 'django.contrib.sessions.middleware.SessionMiddleware',
47 | 'corsheaders.middleware.CorsMiddleware', # 跨域配置
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.messages.middleware.MessageMiddleware',
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 | 'myapp.middlewares.LogMiddleware.OpLogs'
54 | ]
55 |
56 | CORS_ORIGIN_ALLOW_ALL = True # 允许跨域
57 |
58 | ROOT_URLCONF = 'server.urls'
59 |
60 | TEMPLATES = [
61 | {
62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
63 | 'DIRS': [],
64 | 'APP_DIRS': True,
65 | 'OPTIONS': {
66 | 'context_processors': [
67 | 'django.template.context_processors.debug',
68 | 'django.template.context_processors.request',
69 | 'django.contrib.auth.context_processors.auth',
70 | 'django.contrib.messages.context_processors.messages',
71 | ],
72 | },
73 | },
74 | ]
75 |
76 | WSGI_APPLICATION = 'server.wsgi.application'
77 |
78 | # Database
79 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases
80 |
81 | DATABASES = {
82 | 'default': {
83 | 'ENGINE': 'django.db.backends.mysql',
84 | 'NAME': 'python_yanglao',
85 | 'USER': 'root',
86 | 'PASSWORD': '4643830',
87 | 'HOST': '127.0.0.1',
88 | 'PORT': '3306',
89 | 'OPTIONS': {
90 | "init_command": "SET foreign_key_checks = 0;",
91 | }
92 | }
93 | }
94 |
95 | # Password validation
96 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
97 |
98 | AUTH_PASSWORD_VALIDATORS = [
99 | {
100 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
101 | },
102 | {
103 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
104 | },
105 | {
106 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
107 | },
108 | {
109 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
110 | },
111 | ]
112 |
113 | # Internationalization
114 | # https://docs.djangoproject.com/en/4.1/topics/i18n/
115 |
116 |
117 | LANGUAGE_CODE = 'zh-hans'
118 |
119 | # 时区
120 | TIME_ZONE = 'Asia/Shanghai'
121 |
122 | USE_I18N = True
123 |
124 | USE_L10N = True
125 |
126 | USE_TZ = False
127 |
128 | # 日期时间格式
129 | DATE_FORMAT = 'Y-m-d'
130 | DATETIME_FORMAT = 'Y-m-d H:i:s'
131 |
132 | # 上传文件路径
133 | # 并在urls.py配置+static
134 | MEDIA_ROOT = os.path.join(BASE_DIR, 'upload/')
135 | MEDIA_URL = '/upload/'
136 |
137 | # Static files (CSS, JavaScript, Images)
138 | # https://docs.djangoproject.com/en/4.1/howto/static-files/
139 |
140 | STATIC_URL = 'static/'
141 |
142 | # Default primary key field type
143 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
144 |
145 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
146 |
147 | # 跨域配置
148 | CORS_ALLOW_CREDENTIALS = True
149 | CORS_ALLOW_ALL_ORIGINS = True
150 | CORS_ALLOW_HEADERS = '*'
151 |
--------------------------------------------------------------------------------
/web/src/views/admin-login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
管理员登录
11 |
18 |
19 |
24 |
25 |
26 |
27 |
33 |
34 |
35 |
36 |
44 | 登录
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
62 |
116 |
117 |
203 |
--------------------------------------------------------------------------------
/server/myapp/views/admin/user.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 | import datetime
3 |
4 | from rest_framework.decorators import api_view, authentication_classes
5 |
6 | from myapp import utils
7 | from myapp.auth.authentication import AdminTokenAuthtication
8 | from myapp.handler import APIResponse
9 | from myapp.models import User
10 | from myapp.permission.permission import isDemoAdminUser
11 | from myapp.serializers import UserSerializer, LoginLogSerializer
12 | from myapp.utils import md5value
13 |
14 |
15 | def make_login_log(request):
16 | try:
17 | username = request.data['username']
18 | data = {
19 | "username": username,
20 | "ip": utils.get_ip(request),
21 | "ua": utils.get_ua(request)
22 | }
23 | serializer = LoginLogSerializer(data=data)
24 | if serializer.is_valid():
25 | serializer.save()
26 | else:
27 | print(serializer.errors)
28 | except Exception as e:
29 | print(e)
30 |
31 |
32 | @api_view(['POST'])
33 | def admin_login(request):
34 | username = request.data['username']
35 | password = utils.md5value(request.data['password'])
36 |
37 | users = User.objects.filter(username=username, password=password, role__in=['1', '3'])
38 | if len(users) > 0:
39 | user = users[0]
40 | data = {
41 | 'username': username,
42 | 'password': password,
43 | 'admin_token': md5value(username) # 生成令牌
44 | }
45 | serializer = UserSerializer(user, data=data)
46 | if serializer.is_valid():
47 | serializer.save()
48 | make_login_log(request)
49 | return APIResponse(code=0, msg='登录成功', data=serializer.data)
50 | else:
51 | print(serializer.errors)
52 |
53 | return APIResponse(code=1, msg='用户名或密码错误')
54 |
55 |
56 | @api_view(['GET'])
57 | def info(request):
58 | if request.method == 'GET':
59 | pk = request.GET.get('id', -1)
60 | user = User.objects.get(pk=pk)
61 | serializer = UserSerializer(user)
62 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
63 |
64 |
65 | @api_view(['GET'])
66 | def list_api(request):
67 | if request.method == 'GET':
68 | keyword = request.GET.get("keyword", '')
69 | users = User.objects.filter(username__contains=keyword).order_by('-create_time')
70 | serializer = UserSerializer(users, many=True)
71 | return APIResponse(code=0, msg='查询成功', data=serializer.data)
72 |
73 |
74 | @api_view(['POST'])
75 | @authentication_classes([AdminTokenAuthtication])
76 | def create(request):
77 | if isDemoAdminUser(request):
78 | return APIResponse(code=1, msg='演示帐号无法操作')
79 |
80 | print(request.data)
81 | if not request.data.get('username', None) or not request.data.get('password', None):
82 | return APIResponse(code=1, msg='用户名或密码不能为空')
83 | users = User.objects.filter(username=request.data['username'])
84 | if len(users) > 0:
85 | return APIResponse(code=1, msg='该用户名已存在')
86 |
87 | data = request.data.copy()
88 | data.update({'password': utils.md5value(request.data['password'])})
89 | serializer = UserSerializer(data=data)
90 | if serializer.is_valid():
91 | serializer.save()
92 | return APIResponse(code=0, msg='创建成功', data=serializer.data)
93 | else:
94 | print(serializer.errors)
95 |
96 | return APIResponse(code=1, msg='创建失败')
97 |
98 |
99 | @api_view(['POST'])
100 | @authentication_classes([AdminTokenAuthtication])
101 | def update(request):
102 | if isDemoAdminUser(request):
103 | return APIResponse(code=1, msg='演示帐号无法操作')
104 |
105 | try:
106 | pk = request.GET.get('id', -1)
107 | user = User.objects.get(pk=pk)
108 | except User.DoesNotExist:
109 | return APIResponse(code=1, msg='对象不存在')
110 |
111 | data = request.data.copy()
112 | if 'username' in data.keys():
113 | del data['username']
114 | if 'password' in data.keys():
115 | del data['password']
116 | serializer = UserSerializer(user, data=data)
117 | print(serializer.is_valid())
118 | if serializer.is_valid():
119 | serializer.save()
120 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
121 | else:
122 | print(serializer.errors)
123 | return APIResponse(code=1, msg='更新失败')
124 |
125 |
126 | @api_view(['POST'])
127 | @authentication_classes([AdminTokenAuthtication])
128 | def updatePwd(request):
129 | if isDemoAdminUser(request):
130 | return APIResponse(code=1, msg='演示帐号无法操作')
131 |
132 | try:
133 | pk = request.GET.get('id', -1)
134 | user = User.objects.get(pk=pk)
135 | except User.DoesNotExist:
136 | return APIResponse(code=1, msg='对象不存在')
137 |
138 | password = request.data.get('password', None)
139 | newPassword1 = request.data.get('newPassword1', None)
140 | newPassword2 = request.data.get('newPassword2', None)
141 |
142 | if not password or not newPassword1 or not newPassword2:
143 | return APIResponse(code=1, msg='不能为空')
144 |
145 | if user.password != utils.md5value(password):
146 | return APIResponse(code=1, msg='原密码不正确')
147 |
148 | if newPassword1 != newPassword2:
149 | return APIResponse(code=1, msg='两次密码不一致')
150 |
151 | data = request.data.copy()
152 | data.update({'password': utils.md5value(newPassword1)})
153 | serializer = UserSerializer(user, data=data)
154 | if serializer.is_valid():
155 | serializer.save()
156 | return APIResponse(code=0, msg='更新成功', data=serializer.data)
157 | else:
158 | print(serializer.errors)
159 |
160 | return APIResponse(code=1, msg='更新失败')
161 |
162 |
163 | @api_view(['POST'])
164 | @authentication_classes([AdminTokenAuthtication])
165 | def delete(request):
166 | if isDemoAdminUser(request):
167 | return APIResponse(code=1, msg='演示帐号无法操作')
168 |
169 | try:
170 | ids = request.GET.get('ids')
171 | ids_arr = ids.split(',')
172 | User.objects.filter(id__in=ids_arr).delete()
173 | except User.DoesNotExist:
174 | return APIResponse(code=1, msg='对象不存在')
175 |
176 | return APIResponse(code=0, msg='删除成功')
177 |
--------------------------------------------------------------------------------
/web/src/views/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
21 |
22 |
23 | 老人管理
24 |
25 |
26 |
27 | 房间管理
28 |
29 |
30 |
31 | 护工管理
32 |
33 |
34 |
35 | 亲属管理
36 |
37 |
38 |
39 | 病史管理
40 |
41 |
42 |
43 | 活动管理
44 |
45 |
46 |
47 | 用户管理
48 |
49 |
63 |
64 |
65 |
66 |
67 | 日志管理
68 |
69 |
70 | 登录日志
71 |
72 |
73 |
74 | 操作日志
75 |
76 |
77 |
78 | 错误日志
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 系统信息
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
152 |
218 |
--------------------------------------------------------------------------------
/web/src/views/classification.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 新增
8 | 批量删除
9 |
10 |
11 |
28 |
29 |
30 |
31 | 编辑
32 |
33 |
34 | 删除
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
244 |
245 |
263 |
--------------------------------------------------------------------------------
/web/src/views/tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 新增
8 | 批量删除
9 |
10 |
11 |
28 |
29 |
30 |
31 | 编辑
32 |
33 |
34 | 删除
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
258 |
259 |
277 |
--------------------------------------------------------------------------------