├── .gitignore ├── Readme.md ├── doc └── python_job.docx ├── server ├── manage.py ├── myapp │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── auth │ │ ├── __init__.py │ │ └── authentication.py │ ├── handler.py │ ├── middlewares │ │ ├── LogMiddleware.py │ │ └── __init__.py │ ├── models.py │ ├── permission │ │ ├── __init__.py │ │ └── permission.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views │ │ ├── __init__.py │ │ └── index │ │ ├── __init__.py │ │ ├── address.py │ │ ├── classification.py │ │ ├── comment.py │ │ ├── company.py │ │ ├── notice.py │ │ ├── order.py │ │ ├── post.py │ │ ├── resume.py │ │ ├── tag.py │ │ ├── thing.py │ │ └── user.py ├── readme.md ├── requirements.txt ├── server │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── upload │ ├── company │ ├── 1685770491227.png │ ├── 1685770768426.jpeg │ ├── 1685771081809.png │ └── 1685848419951.png │ ├── cover │ └── 1.jpeg │ ├── img │ ├── Wechat.jpeg │ ├── a.png │ ├── b.png │ └── weixin.png │ ├── raw │ └── 1685780971830.jpeg │ └── resume │ ├── 1685772029168.png │ ├── 1685772036385.jpeg │ ├── 1685772067199.jpeg │ ├── logo.png │ └── logo_Yvy8kmb.png └── web ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .stylelintignore ├── README.md ├── build ├── constant.ts └── vite │ └── plugins │ ├── autoImport.ts │ ├── component.ts │ ├── compress.ts │ ├── imagemin.ts │ ├── index.ts │ ├── progress.ts │ ├── restart.ts │ ├── unocss.ts │ └── visualizer.ts ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public ├── favicon.ico └── images │ ├── admin-login-bg.jpg │ ├── bg2.jpg │ └── demo.jpg ├── src ├── App.vue ├── api │ ├── admin │ │ ├── ad.ts │ │ ├── classification.ts │ │ ├── comment.ts │ │ ├── company.ts │ │ ├── log.ts │ │ ├── notice.ts │ │ ├── order.ts │ │ ├── overview.ts │ │ ├── resume.ts │ │ ├── tag.ts │ │ ├── thing.ts │ │ └── user.ts │ └── index │ │ ├── address.ts │ │ ├── classification.ts │ │ ├── comment.ts │ │ ├── company.ts │ │ ├── notice.ts │ │ ├── order.ts │ │ ├── post.ts │ │ ├── resume.ts │ │ ├── tag.ts │ │ ├── thing.ts │ │ ├── thingCollect.ts │ │ ├── thingWish.ts │ │ └── user.ts ├── assets │ ├── fonts │ │ ├── Blimone-ExtraBold.woff │ │ ├── Blimone-ExtraLight.woff │ │ ├── Blimone-Light.woff │ │ └── Blimone-Regular.woff │ ├── icons │ │ ├── logo.png │ │ └── svg │ │ │ ├── github.svg │ │ │ ├── logo.svg │ │ │ ├── marks.svg │ │ │ ├── test.svg │ │ │ ├── ts.svg │ │ │ └── twitter.svg │ ├── images │ │ ├── add.svg │ │ ├── address-right-icon.svg │ │ ├── ali-pay-icon.svg │ │ ├── avatar.jpg │ │ ├── banner-02.webp │ │ ├── banner2.svg │ │ ├── cart-icon.svg │ │ ├── clear-search.svg │ │ ├── code-icon.svg │ │ ├── delete-icon.svg │ │ ├── ebook-download-icon.svg │ │ ├── ic-company.png │ │ ├── k-logo.png │ │ ├── login-banner.png │ │ ├── login.png │ │ ├── logo.png │ │ ├── mail-icon.svg │ │ ├── message-icon.svg │ │ ├── order-address-icon.svg │ │ ├── order-icon.svg │ │ ├── order-point-icon.svg │ │ ├── order-thing-icon.svg │ │ ├── pwd-hidden.svg │ │ ├── pwd-icon.svg │ │ ├── qunerweima.jpg │ │ ├── read-online-icon.svg │ │ ├── recommend-hover.svg │ │ ├── register-name.svg │ │ ├── search-icon.svg │ │ ├── searchIcon.svg │ │ ├── setting-card-icon.svg │ │ ├── setting-icon.svg │ │ ├── setting-msg-icon.svg │ │ ├── setting-push-icon.svg │ │ ├── setting-safe-icon.svg │ │ ├── share-icon.svg │ │ ├── tel-icon.svg │ │ ├── want-read-hover.svg │ │ ├── wb-share.svg │ │ └── wx-pay-icon.svg │ └── styles │ │ └── base.less ├── core │ └── bootstrap.js ├── main.ts ├── router │ ├── index.ts │ └── root.ts ├── store │ ├── constants.ts │ ├── index.ts │ └── modules │ │ ├── app │ │ ├── index.ts │ │ └── types.ts │ │ └── user │ │ ├── index.ts │ │ └── types.ts ├── styles │ ├── index.less │ └── reset.less ├── utils │ ├── auth.ts │ ├── http │ │ └── axios │ │ │ ├── index.ts │ │ │ ├── status.ts │ │ │ └── type.ts │ ├── index.ts │ └── result.ts └── views │ └── index │ ├── components │ ├── content.vue │ ├── footer.vue │ ├── header.vue │ └── search-content-view.vue │ ├── confirm.vue │ ├── detail.vue │ ├── index.vue │ ├── login.vue │ ├── pay.vue │ ├── portal.vue │ ├── register.vue │ ├── search.vue │ ├── user │ ├── address-view.vue │ ├── collect-thing-view.vue │ ├── comment-view.vue │ ├── company-edit-view.vue │ ├── company-post-view.vue │ ├── fans-view.vue │ ├── follow-view.vue │ ├── message-view.vue │ ├── mine-infos-view.vue │ ├── modal │ │ └── edit-address.vue │ ├── my-post-view.vue │ ├── my-thing-view.vue │ ├── order-view.vue │ ├── push-view.vue │ ├── resume-edit-view.vue │ ├── score-view.vue │ ├── security-view.vue │ ├── userinfo-edit-view.vue │ └── wish-thing-view.vue │ └── usercenter.vue ├── stylelint.config.js ├── tsconfig.json ├── types ├── auto-imports.d.ts ├── components.d.ts └── env.d.ts ├── vite.config.ts └── yarn.lock /.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 -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | > 学习过程中,遇到问题可以咨询作者 2 | 3 | 4 | ### 演示地址 5 | 6 | 前台地址: http://job.gitapp.cn 7 | 8 | 后台地址:http://job.gitapp.cn/admin 9 | 10 | 后台管理帐号: 11 | 12 | 用户名:admin123 13 | 密码:admin123 14 | 15 | 16 | ### 功能介绍 17 | 18 | 平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。 19 | 20 | 整个平台包括前台和后台两个部分。 21 | 22 | - 前台功能包括:首页、岗位详情页、简历中心、用户设置模块。 23 | - 后台功能包括:总览、岗位管理、公司管理、分类管理、标签管理、评论管理、用户管理、运营管理、日志管理、系统信息模块。 24 | 25 | 26 | ### 代码结构 27 | 28 | - server目录是后端代码 29 | - web目录是前端代码 30 | 31 | ### 部署运行 32 | 33 | #### 后端运行步骤 34 | 35 | (1) 安装python 3.8 36 | 37 | (2) 安装依赖。进入server目录下,执行 pip install -r requirements.txt 38 | 39 | (3) 安装mysql 5.7数据库,并创建数据库,创建SQL如下: 40 | ``` 41 | CREATE DATABASE IF NOT EXISTS xxx DEFAULT CHARSET utf8 COLLATE utf8_general_ci 42 | ``` 43 | (4) 恢复sql数据。在mysql下依次执行如下命令: 44 | 45 | ``` 46 | mysql> use xxx; 47 | mysql> source D:/xxx/xxx/xxx.sql; 48 | ``` 49 | 50 | (5) 启动django服务。在server目录下执行: 51 | ``` 52 | python manage.py runserver 53 | ``` 54 | 55 | #### 前端运行步骤 56 | 57 | (1) 安装node 16.14 58 | 59 | (2) 进入web目录下,安装依赖,执行: 60 | ``` 61 | npm install 62 | ``` 63 | (3) 运行项目 64 | ``` 65 | npm run dev 66 | ``` 67 | 68 | 69 | ### 界面预览 70 | 71 | 首页 72 | 73 | ![](https://github.com/geeeeeeeek/python_job/blob/master/server/upload/img/a.png) 74 | 75 | 76 | 后台页面 77 | 78 | ![](https://github.com/geeeeeeeek/python_job/blob/master/server/upload/img/b.png) 79 | 80 | 81 | 82 | 83 | ### 参考论文 84 | 85 | [点击查看](doc/python_job.docx) 86 | 87 | 88 | 89 | ### 付费咨询 90 | 91 | 微信:lengqin1024 92 | 93 | 94 | -------------------------------------------------------------------------------- /doc/python_job.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/doc/python_job.docx -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /server/myapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/myapp/__init__.py -------------------------------------------------------------------------------- /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, Comment 5 | 6 | admin.site.register(Classification) 7 | admin.site.register(Tag) 8 | admin.site.register(Thing) 9 | admin.site.register(User) 10 | admin.site.register(Comment) 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /server/myapp/auth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/myapp/auth/__init__.py -------------------------------------------------------------------------------- /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 | users = User.objects.filter(admin_token=adminToken) 13 | """ 14 | 判定条件: 15 | 1. 传了adminToken 16 | 2. 查到了该帐号 17 | 3. 该帐号是管理员或演示帐号 18 | """ 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 | class TokenAuthtication(BaseAuthentication): 28 | def authenticate(self, request): 29 | token = request.META.get("HTTP_TOKEN", "") 30 | if token is not None: 31 | print("检查token==>" + token) 32 | users = User.objects.filter(token=token) 33 | # print(users) 34 | """ 35 | 判定条件: 36 | 1. 传了token 37 | 2. 查到了该帐号 38 | 3. 该帐号是普通用户 39 | """ 40 | if not token or len(users) == 0 or (users[0].role in ['1', '3']): 41 | raise exceptions.AuthenticationFailed("AUTH_FAIL_FRONT") 42 | else: 43 | print('token验证通过') 44 | else: 45 | print("检查token==>token 为空") 46 | raise exceptions.AuthenticationFailed("AUTH_FAIL_FRONT") 47 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /server/myapp/middlewares/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/myapp/middlewares/__init__.py -------------------------------------------------------------------------------- /server/myapp/permission/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/myapp/permission/__init__.py -------------------------------------------------------------------------------- /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/myapp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /server/myapp/views/__init__.py: -------------------------------------------------------------------------------- 1 | from myapp.views.admin import * 2 | from myapp.views.index import * 3 | -------------------------------------------------------------------------------- /server/myapp/views/index/__init__.py: -------------------------------------------------------------------------------- 1 | from myapp.views.index.classification import * 2 | from myapp.views.index.tag import * 3 | from myapp.views.index.user import * 4 | from myapp.views.index.thing import * 5 | from myapp.views.index.resume import * 6 | from myapp.views.index.post import * 7 | from myapp.views.index.company import * 8 | from myapp.views.index.comment import * 9 | from myapp.views.index.order import * 10 | from myapp.views.index.notice import * 11 | from myapp.views.index.address import * 12 | -------------------------------------------------------------------------------- /server/myapp/views/index/address.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 TokenAuthtication 6 | from myapp.handler import APIResponse 7 | from myapp.models import Address 8 | from myapp.serializers import AddressSerializer 9 | 10 | 11 | @api_view(['GET']) 12 | def list_api(request): 13 | if request.method == 'GET': 14 | userId = request.GET.get('userId', -1) 15 | 16 | if userId != -1: 17 | addresses = Address.objects.filter(user=userId).order_by('-create_time') 18 | serializer = AddressSerializer(addresses, many=True) 19 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 20 | else: 21 | return APIResponse(code=1, msg='userId不能为空') 22 | 23 | 24 | @api_view(['POST']) 25 | @authentication_classes([TokenAuthtication]) 26 | def create(request): 27 | 28 | address_content = request.POST.get('desc', None) 29 | user = request.POST.get('user', None) 30 | default = request.POST.get('default', False) 31 | 32 | if address_content is None or user is None: 33 | return APIResponse(code=1, msg='不能为空') 34 | 35 | if default: 36 | # 其他置为false 37 | Address.objects.filter(user=user).update(default=False) 38 | 39 | serializer = AddressSerializer(data=request.data) 40 | if serializer.is_valid(): 41 | serializer.save() 42 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 43 | else: 44 | utils.log_error(request, '参数错误') 45 | 46 | return APIResponse(code=1, msg='创建失败') 47 | 48 | 49 | @api_view(['POST']) 50 | @authentication_classes([TokenAuthtication]) 51 | def update(request): 52 | 53 | try: 54 | pk = request.GET.get('id', -1) 55 | addresses = Address.objects.get(pk=pk) 56 | except Address.DoesNotExist: 57 | return APIResponse(code=1, msg='对象不存在') 58 | 59 | user = request.data['user'] 60 | default = request.data['default'] 61 | 62 | if default: 63 | # 其他置为false 64 | Address.objects.filter(user=user).update(default=False) 65 | 66 | serializer = AddressSerializer(addresses, data=request.data) 67 | if serializer.is_valid(): 68 | serializer.save() 69 | return APIResponse(code=0, msg='更新成功', data=serializer.data) 70 | else: 71 | utils.log_error(request, '参数错误') 72 | 73 | return APIResponse(code=1, msg='更新失败') 74 | 75 | 76 | @api_view(['POST']) 77 | @authentication_classes([TokenAuthtication]) 78 | def delete(request): 79 | 80 | try: 81 | ids = request.GET.get('ids') 82 | ids_arr = ids.split(',') 83 | Address.objects.filter(id__in=ids_arr).delete() 84 | except Address.DoesNotExist: 85 | return APIResponse(code=1, msg='对象不存在') 86 | 87 | return APIResponse(code=0, msg='删除成功') 88 | -------------------------------------------------------------------------------- /server/myapp/views/index/classification.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | from django.db import connection 3 | from rest_framework.decorators import api_view 4 | 5 | from myapp.handler import APIResponse 6 | from myapp.models import Classification 7 | from myapp.serializers import ClassificationSerializer 8 | from myapp.utils import dict_fetchall 9 | 10 | 11 | @api_view(['GET']) 12 | def list_api(request): 13 | if request.method == 'GET': 14 | classifications = Classification.objects.all().order_by('-create_time') 15 | serializer = ClassificationSerializer(classifications, many=True) 16 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /server/myapp/views/index/comment.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 Comment 7 | from myapp.permission.permission import isDemoAdminUser 8 | from myapp.serializers import CommentSerializer 9 | 10 | 11 | @api_view(['GET']) 12 | def list_api(request): 13 | if request.method == 'GET': 14 | thingId = request.GET.get("thingId", None) 15 | order = request.GET.get("order", 'recent') 16 | 17 | if thingId: 18 | if order == 'recent': 19 | orderBy = '-comment_time' 20 | else: 21 | orderBy = '-like_count' 22 | 23 | comments = Comment.objects.select_related("thing").filter(thing=thingId).order_by(orderBy) 24 | # print(comments) 25 | serializer = CommentSerializer(comments, many=True) 26 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 27 | else: 28 | return APIResponse(code=1, msg='thingId不能为空') 29 | 30 | @api_view(['GET']) 31 | def list_my_comment(request): 32 | if request.method == 'GET': 33 | userId = request.GET.get("userId", None) 34 | order = request.GET.get("order", 'recent') 35 | 36 | if userId: 37 | if order == 'recent': 38 | orderBy = '-comment_time' 39 | else: 40 | orderBy = '-like_count' 41 | 42 | comments = Comment.objects.select_related("thing").filter(user=userId).order_by(orderBy) 43 | # print(comments) 44 | serializer = CommentSerializer(comments, many=True) 45 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 46 | else: 47 | return APIResponse(code=1, msg='userId不能为空') 48 | 49 | @api_view(['POST']) 50 | def create(request): 51 | serializer = CommentSerializer(data=request.data) 52 | if serializer.is_valid(): 53 | serializer.save() 54 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 55 | else: 56 | print(serializer.errors) 57 | 58 | return APIResponse(code=1, msg='创建失败') 59 | 60 | 61 | @api_view(['POST']) 62 | def delete(request): 63 | try: 64 | ids = request.GET.get('ids') 65 | ids_arr = ids.split(',') 66 | Comment.objects.filter(id__in=ids_arr).delete() 67 | except Comment.DoesNotExist: 68 | return APIResponse(code=1, msg='对象不存在') 69 | 70 | return APIResponse(code=0, msg='删除成功') 71 | 72 | 73 | @api_view(['POST']) 74 | def like(request): 75 | try: 76 | commentId = request.GET.get('commentId') 77 | comment = Comment.objects.get(pk=commentId) 78 | comment.like_count += 1 79 | comment.save() 80 | except Comment.DoesNotExist: 81 | return APIResponse(code=1, msg='对象不存在') 82 | 83 | return APIResponse(code=0, msg='推荐成功') 84 | -------------------------------------------------------------------------------- /server/myapp/views/index/company.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 Classification, Thing, Tag, Company 8 | from myapp.permission.permission import isDemoAdminUser 9 | from myapp.serializers import ThingSerializer, UpdateThingSerializer, CompanySerializer 10 | 11 | 12 | @api_view(['GET']) 13 | def list_user_company_api(request): 14 | if request.method == 'GET': 15 | userId = request.GET.get('userId', None) 16 | if userId: 17 | companies = Company.objects.filter(user=userId) 18 | serializer = CompanySerializer(companies, many=True) 19 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 20 | 21 | 22 | @api_view(['POST']) 23 | def create(request): 24 | 25 | companies = Company.objects.filter(user=request.data['user']) 26 | if companies and len(companies) > 0: 27 | return APIResponse(code=1, msg='已创建过了') 28 | 29 | serializer = CompanySerializer(data=request.data) 30 | if serializer.is_valid(): 31 | serializer.save() 32 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 33 | else: 34 | print(serializer.errors) 35 | utils.log_error(request, '参数错误') 36 | 37 | return APIResponse(code=1, msg='创建失败') 38 | 39 | 40 | @api_view(['POST']) 41 | def update(request): 42 | 43 | try: 44 | pk = request.GET.get('id', -1) 45 | company = Company.objects.get(pk=pk) 46 | except Company.DoesNotExist: 47 | return APIResponse(code=1, msg='对象不存在') 48 | 49 | serializer = CompanySerializer(company, data=request.data) 50 | if serializer.is_valid(): 51 | serializer.save() 52 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 53 | else: 54 | print(serializer.errors) 55 | utils.log_error(request, '参数错误') 56 | 57 | return APIResponse(code=1, msg='更新失败') 58 | 59 | 60 | @api_view(['POST']) 61 | def delete(request): 62 | 63 | try: 64 | ids = request.GET.get('ids') 65 | ids_arr = ids.split(',') 66 | Company.objects.filter(id__in=ids_arr).delete() 67 | except Company.DoesNotExist: 68 | return APIResponse(code=1, msg='对象不存在') 69 | return APIResponse(code=0, msg='删除成功') 70 | -------------------------------------------------------------------------------- /server/myapp/views/index/notice.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 Notice 6 | from myapp.serializers import NoticeSerializer 7 | 8 | 9 | @api_view(['GET']) 10 | def list_api(request): 11 | if request.method == 'GET': 12 | notices = Notice.objects.all().order_by('-create_time') 13 | serializer = NoticeSerializer(notices, many=True) 14 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 15 | 16 | -------------------------------------------------------------------------------- /server/myapp/views/index/order.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 TokenAuthtication 8 | from myapp.handler import APIResponse 9 | from myapp.models import Order, Thing 10 | from myapp.serializers import OrderSerializer 11 | 12 | 13 | @api_view(['GET']) 14 | def list_api(request): 15 | if request.method == 'GET': 16 | userId = request.GET.get('userId', -1) 17 | orderStatus = request.GET.get('orderStatus', '') 18 | 19 | orders = Order.objects.all().filter(user=userId).filter(status__contains=orderStatus).order_by('-order_time') 20 | serializer = OrderSerializer(orders, many=True) 21 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 22 | 23 | 24 | @api_view(['POST']) 25 | @authentication_classes([TokenAuthtication]) 26 | def create(request): 27 | 28 | data = request.data.copy() 29 | if data['user'] is None or data['thing'] is None or data['count'] is None: 30 | return APIResponse(code=1, msg='参数错误') 31 | 32 | thing = Thing.objects.get(pk=data['thing']) 33 | count = data['count'] 34 | if thing.repertory < int(count): 35 | return APIResponse(code=1, msg='库存不足') 36 | 37 | create_time = datetime.datetime.now() 38 | data['create_time'] = create_time 39 | data['order_number'] = str(utils.get_timestamp()) 40 | data['status'] = '1' 41 | serializer = OrderSerializer(data=data) 42 | if serializer.is_valid(): 43 | serializer.save() 44 | # 减库存(支付后) 45 | # thing.repertory = thing.repertory - int(count) 46 | # thing.save() 47 | 48 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 49 | else: 50 | print(serializer.errors) 51 | return APIResponse(code=1, msg='创建失败') 52 | 53 | 54 | @api_view(['POST']) 55 | @authentication_classes([TokenAuthtication]) 56 | def cancel_order(request): 57 | """ 58 | cancal 59 | """ 60 | try: 61 | pk = request.GET.get('id', -1) 62 | order = Order.objects.get(pk=pk) 63 | except Order.DoesNotExist: 64 | return APIResponse(code=1, msg='对象不存在') 65 | 66 | data = { 67 | 'status': 7 68 | } 69 | serializer = OrderSerializer(order, data=data) 70 | if serializer.is_valid(): 71 | serializer.save() 72 | # 加库存 73 | # thingId = request.data['thing'] 74 | # thing = Thing.objects.get(pk=thingId) 75 | # thing.repertory = thing.repertory + 1 76 | # thing.save() 77 | 78 | # 加积分 79 | # order.user.score = order.user.score + 1 80 | # order.user.save() 81 | 82 | return APIResponse(code=0, msg='取消成功', data=serializer.data) 83 | else: 84 | print(serializer.errors) 85 | return APIResponse(code=1, msg='更新失败') 86 | -------------------------------------------------------------------------------- /server/myapp/views/index/post.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 Post 8 | from myapp.permission.permission import isDemoAdminUser 9 | from myapp.serializers import PostSerializer 10 | 11 | 12 | @api_view(['GET']) 13 | def list_user_post_api(request): 14 | if request.method == 'GET': 15 | userId = request.GET.get("userId", None) 16 | if userId is None: 17 | return APIResponse(code=1, msg='userId不能为空') 18 | 19 | posts = Post.objects.filter(user=userId).order_by('-create_time') 20 | serializer = PostSerializer(posts, many=True) 21 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 22 | 23 | 24 | @api_view(['GET']) 25 | def list_company_post_api(request): 26 | if request.method == 'GET': 27 | companyId = request.GET.get("companyId", None) 28 | if companyId is None: 29 | return APIResponse(code=1, msg='companyId不能为空') 30 | 31 | posts = Post.objects.filter(company=companyId).order_by('-create_time') 32 | serializer = PostSerializer(posts, many=True) 33 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 34 | 35 | @api_view(['POST']) 36 | def create(request): 37 | 38 | serializer = PostSerializer(data=request.data) 39 | if serializer.is_valid(): 40 | serializer.save() 41 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 42 | else: 43 | print(serializer.errors) 44 | utils.log_error(request, '参数错误') 45 | 46 | return APIResponse(code=1, msg='创建失败') 47 | -------------------------------------------------------------------------------- /server/myapp/views/index/resume.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 Resume 8 | from myapp.permission.permission import isDemoAdminUser 9 | from myapp.serializers import ResumeSerializer 10 | 11 | 12 | 13 | @api_view(['GET']) 14 | def detail(request): 15 | 16 | try: 17 | user = request.GET.get('user', -1) 18 | resumes = Resume.objects.filter(user=user) 19 | print(resumes) 20 | except Resume.DoesNotExist: 21 | utils.log_error(request, '对象不存在') 22 | return APIResponse(code=1, msg='对象不存在') 23 | 24 | if request.method == 'GET': 25 | if resumes and len(resumes) > 0: 26 | serializer = ResumeSerializer(resumes[0]) 27 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 28 | else: 29 | return APIResponse(code=1, msg='不存在') 30 | 31 | 32 | 33 | @api_view(['POST']) 34 | def create(request): 35 | 36 | resumes = Resume.objects.filter(user=request.data['user']) 37 | if resumes and len(resumes) > 0: 38 | return APIResponse(code=1, msg='已创建过了') 39 | 40 | serializer = ResumeSerializer(data=request.data) 41 | if serializer.is_valid(): 42 | serializer.save() 43 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 44 | else: 45 | print(serializer.errors) 46 | utils.log_error(request, '参数错误') 47 | 48 | return APIResponse(code=1, msg='创建失败') 49 | 50 | 51 | @api_view(['POST']) 52 | def update(request): 53 | 54 | 55 | try: 56 | pk = request.GET.get('id', -1) 57 | resume = Resume.objects.get(pk=pk) 58 | except Resume.DoesNotExist: 59 | return APIResponse(code=1, msg='对象不存在') 60 | 61 | serializer = ResumeSerializer(resume, data=request.data) 62 | if serializer.is_valid(): 63 | serializer.save() 64 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 65 | else: 66 | print(serializer.errors) 67 | utils.log_error(request, '参数错误') 68 | 69 | return APIResponse(code=1, msg='更新失败') 70 | 71 | 72 | -------------------------------------------------------------------------------- /server/myapp/views/index/tag.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 Tag 6 | from myapp.serializers import TagSerializer 7 | 8 | 9 | @api_view(['GET']) 10 | def list_api(request): 11 | if request.method == 'GET': 12 | tags = Tag.objects.all().order_by('-create_time') 13 | serializer = TagSerializer(tags, many=True) 14 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 15 | 16 | -------------------------------------------------------------------------------- /server/myapp/views/index/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 TokenAuthtication 8 | from myapp.handler import APIResponse 9 | from myapp.models import User 10 | from myapp.serializers import UserSerializer, LoginLogSerializer 11 | from myapp.utils import md5value 12 | 13 | 14 | def make_login_log(request): 15 | try: 16 | username = request.data['username'] 17 | data = { 18 | "username": username, 19 | "ip": utils.get_ip(request), 20 | "ua": utils.get_ua(request) 21 | } 22 | serializer = LoginLogSerializer(data=data) 23 | if serializer.is_valid(): 24 | serializer.save() 25 | else: 26 | print(serializer.errors) 27 | except Exception as e: 28 | print(e) 29 | 30 | 31 | @api_view(['POST']) 32 | def login(request): 33 | username = request.data['username'] 34 | password = utils.md5value(request.data['password']) 35 | 36 | users = User.objects.filter(username=username, password=password) 37 | if len(users) > 0: 38 | user = users[0] 39 | 40 | if user.role in ['1', '3']: 41 | return APIResponse(code=1, msg='该帐号为后台管理员帐号') 42 | 43 | data = { 44 | 'username': username, 45 | 'password': password, 46 | 'token': md5value(username) # 生成令牌 47 | } 48 | serializer = UserSerializer(user, data=data) 49 | if serializer.is_valid(): 50 | serializer.save() 51 | make_login_log(request) 52 | return APIResponse(code=0, msg='登录成功', data=serializer.data) 53 | else: 54 | print(serializer.errors) 55 | 56 | return APIResponse(code=1, msg='用户名或密码错误') 57 | 58 | 59 | @api_view(['POST']) 60 | def register(request): 61 | print(request.data) 62 | username = request.data.get('username', None) 63 | password = request.data.get('password', None) 64 | repassword = request.data.get('repassword', None) 65 | if not username or not password or not repassword: 66 | return APIResponse(code=1, msg='用户名或密码不能为空') 67 | if password != repassword: 68 | return APIResponse(code=1, msg='密码不一致') 69 | users = User.objects.filter(username=username) 70 | if len(users) > 0: 71 | return APIResponse(code=1, msg='该用户名已存在') 72 | 73 | data = { 74 | 'username': username, 75 | 'password': password, 76 | 'role': 2, # 角色2 77 | 'status': 0, 78 | } 79 | data.update({'password': utils.md5value(request.data['password'])}) 80 | serializer = UserSerializer(data=data) 81 | if serializer.is_valid(): 82 | serializer.save() 83 | return APIResponse(code=0, msg='创建成功', data=serializer.data) 84 | else: 85 | print(serializer.errors) 86 | 87 | return APIResponse(code=1, msg='创建失败') 88 | 89 | 90 | @api_view(['GET']) 91 | def info(request): 92 | if request.method == 'GET': 93 | pk = request.GET.get('id', -1) 94 | user = User.objects.get(pk=pk) 95 | serializer = UserSerializer(user) 96 | return APIResponse(code=0, msg='查询成功', data=serializer.data) 97 | 98 | 99 | @api_view(['POST']) 100 | @authentication_classes([TokenAuthtication]) 101 | def update(request): 102 | try: 103 | pk = request.GET.get('id', -1) 104 | user = User.objects.get(pk=pk) 105 | except User.DoesNotExist: 106 | return APIResponse(code=1, msg='对象不存在') 107 | 108 | data = request.data.copy() 109 | if 'username' in data.keys(): 110 | del data['username'] 111 | if 'password' in data.keys(): 112 | del data['password'] 113 | if 'role' in data.keys(): 114 | del data['role'] 115 | serializer = UserSerializer(user, data=data) 116 | print(serializer.is_valid()) 117 | if serializer.is_valid(): 118 | serializer.save() 119 | return APIResponse(code=0, msg='更新成功', data=serializer.data) 120 | else: 121 | print(serializer.errors) 122 | 123 | return APIResponse(code=1, msg='更新失败') 124 | 125 | 126 | @api_view(['POST']) 127 | @authentication_classes([TokenAuthtication]) 128 | def updatePwd(request): 129 | 130 | try: 131 | pk = request.GET.get('id', -1) 132 | user = User.objects.get(pk=pk) 133 | except User.DoesNotExist: 134 | return APIResponse(code=1, msg='对象不存在') 135 | 136 | print(user.role) 137 | if user.role != '2': 138 | return APIResponse(code=1, msg='参数非法') 139 | 140 | password = request.data.get('password', None) 141 | newPassword1 = request.data.get('newPassword1', None) 142 | newPassword2 = request.data.get('newPassword2', None) 143 | 144 | if not password or not newPassword1 or not newPassword2: 145 | return APIResponse(code=1, msg='不能为空') 146 | 147 | if user.password != utils.md5value(password): 148 | return APIResponse(code=1, msg='原密码不正确') 149 | 150 | if newPassword1 != newPassword2: 151 | return APIResponse(code=1, msg='两次密码不一致') 152 | 153 | data = request.data.copy() 154 | data.update({'password': utils.md5value(newPassword1)}) 155 | serializer = UserSerializer(user, data=data) 156 | if serializer.is_valid(): 157 | serializer.save() 158 | return APIResponse(code=0, msg='更新成功', data=serializer.data) 159 | else: 160 | print(serializer.errors) 161 | 162 | return APIResponse(code=1, msg='更新失败') -------------------------------------------------------------------------------- /server/readme.md: -------------------------------------------------------------------------------- 1 | ### 后端部署步骤 2 | 3 | > 部署过程中,如遇问题可咨询作者:lengqin1024(微信) 4 | 5 | 1. 安装mysql数据库,启动服务 6 | 2. 打开cmd命令行,进入mysql,并新建数据库 7 | ``` 8 | mysql -u root -p 9 | CREATE DATABASE IF NOT EXISTS python_job DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 10 | ``` 11 | 3. 恢复sql数据 12 | ``` 13 | use xxx 14 | source xxxx.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 0.0.0.0:9003 26 | ``` 27 | 7. 后期维护改动 28 | 29 | 将修改的py文件覆盖服务器的py文件即可,重启django 30 | 31 | ### 删除数据库 32 | 33 | drop database if exists shop; 34 | 35 | ### 创建数据库 36 | 37 | CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 38 | 39 | 40 | ### 迁移数据库表 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 | django-cors-headers 55 | 56 | ### 多对多技术参考 57 | 58 | https://www.cnblogs.com/SunshineKimi/p/14140900.html 59 | 60 | ### 二级分类设计 61 | https://blog.csdn.net/weixin_47971206/article/details/124199978 62 | 63 | ### 常见问题 64 | 65 | 多对多的查询可通过related_name别名查询 66 | join查询 67 | ForeignKey的时候字段会自动加_id后缀 68 | 学习SerializerMethodField 69 | 跨域配置 django-cors-headers 70 | 数据库备份命令: 71 | mysqldump -u root -p --databases 数据库名称 > xxx.sql 72 | 数据库还原命令: 73 | source D:/xxx/xxx/shop.sql; 74 | 创建管理员命令: 75 | insert into b_user(username,password,role,status) values('admin111',md5('admin111'),1,'0'); 76 | 77 | 接口请求频次限制 78 | 79 | 80 | ### 登录接口 81 | 82 | 调login -> 生成token 83 | 84 | ### 注意 85 | 86 | update接口的时候,如果model里面存在多对多字段,则需要设置explode 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /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/server/__init__.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | pymysql.install_as_MySQLdb() 3 | 4 | print("===============install pymysql==============") -------------------------------------------------------------------------------- /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/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_job', 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /server/upload/company/1685770491227.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/company/1685770491227.png -------------------------------------------------------------------------------- /server/upload/company/1685770768426.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/company/1685770768426.jpeg -------------------------------------------------------------------------------- /server/upload/company/1685771081809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/company/1685771081809.png -------------------------------------------------------------------------------- /server/upload/company/1685848419951.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/company/1685848419951.png -------------------------------------------------------------------------------- /server/upload/cover/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/cover/1.jpeg -------------------------------------------------------------------------------- /server/upload/img/Wechat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/img/Wechat.jpeg -------------------------------------------------------------------------------- /server/upload/img/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/img/a.png -------------------------------------------------------------------------------- /server/upload/img/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/img/b.png -------------------------------------------------------------------------------- /server/upload/img/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/img/weixin.png -------------------------------------------------------------------------------- /server/upload/raw/1685780971830.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/raw/1685780971830.jpeg -------------------------------------------------------------------------------- /server/upload/resume/1685772029168.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/resume/1685772029168.png -------------------------------------------------------------------------------- /server/upload/resume/1685772036385.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/resume/1685772036385.jpeg -------------------------------------------------------------------------------- /server/upload/resume/1685772067199.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/resume/1685772067199.jpeg -------------------------------------------------------------------------------- /server/upload/resume/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/resume/logo.png -------------------------------------------------------------------------------- /server/upload/resume/logo_Yvy8kmb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/server/upload/resume/logo_Yvy8kmb.png -------------------------------------------------------------------------------- /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/.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 | -------------------------------------------------------------------------------- /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/.stylelintignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /public/* 3 | public/* 4 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | ### 学习文档 2 | 3 | > 部署过程中,如遇问题可咨询作者:lengqin1024(微信) 4 | 5 | #### 部署步骤 6 | 7 | 1. 修改constants.ts中的BASE_URL 8 | 2. vite build 9 | 3. 将dist部署到nginx 10 | 11 | 12 | #### 配置解释 13 | 14 | 1. env.development 开发环境配置 15 | 2. eslintrc.js 代码规范化提示 16 | 3. vite.config.js vite 开发服务器配置 17 | 18 | #### 常见问题 19 | 20 | ##### 变量 21 | https://blog.csdn.net/qq_41636947/article/details/117907448 22 | 23 | ##### antd的css引入方式 24 | 在index.html里面引入的cdn 25 | 26 | ##### cdn 27 | https://cdn.jsdelivr.net/npm/ant-design-vue@3.2.20/dist/ 28 | https://cdn.staticfile.org/ant-design-vue/3.2.20/antd.min.css 29 | 30 | #### public文件夹内容在build后会自动打到dist中 31 | -------------------------------------------------------------------------------- /web/build/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Config 3 | * @description 项目配置 4 | */ 5 | 6 | 7 | // 本地服务端口 8 | export const VITE_PORT = 3000; 9 | 10 | // 包依赖分析 11 | export const ANALYSIS = true; 12 | 13 | // 代码压缩 14 | export const COMPRESSION = true; 15 | 16 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 求职招聘网站 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /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/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/images/admin-login-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/public/images/admin-login-bg.jpg -------------------------------------------------------------------------------- /web/public/images/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/public/images/bg2.jpg -------------------------------------------------------------------------------- /web/public/images/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/public/images/demo.jpg -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /web/src/api/admin/ad.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | list = '/myapp/admin/ad/list', 5 | create = '/myapp/admin/ad/create', 6 | update = '/myapp/admin/ad/update', 7 | delete = '/myapp/admin/ad/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/api/admin/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/admin/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/api/admin/company.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/company/list', 7 | create = '/myapp/admin/company/create', 8 | update = '/myapp/admin/company/update', 9 | delete = '/myapp/admin/company/delete', 10 | } 11 | 12 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} }); 13 | const createApi = async (data: any) => 14 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 15 | const updateApi = async (params:any, data: any) => 16 | post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 17 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} }); 18 | 19 | export { listApi, createApi, updateApi, deleteApi }; 20 | -------------------------------------------------------------------------------- /web/src/api/admin/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/src/api/admin/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) => 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/api/admin/order.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | list = '/myapp/admin/order/list', 5 | create = '/myapp/admin/order/create', 6 | update = '/myapp/admin/order/update', 7 | delete = '/myapp/admin/order/delete', 8 | cancel = '/myapp/admin/order/cancel_order', 9 | cancelUserOrder = '/api/order/cancelUserOrder', 10 | userOrderList = '/api/order/userOrderList', 11 | } 12 | 13 | const listApi = async (params: any) => 14 | get({url: URL.list, params: params, data: {}, headers: {}}); 15 | const userOrderListApi = async (params: any) => 16 | get({url: URL.userOrderList, params: params, data: {}, headers: {}}); 17 | 18 | const createApi = async (data: any) => 19 | post({ 20 | url: URL.create, 21 | params: {}, 22 | data: data, 23 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'} 24 | }); 25 | const updateApi = async (params: any, data: any) => 26 | post({ 27 | url: URL.update, 28 | params: params, 29 | data: data, 30 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'} 31 | }); 32 | const deleteApi = async (params: any) => 33 | post({url: URL.delete, params: params, headers: {}}); 34 | 35 | const cancelApi = async (params: any) => 36 | post({url: URL.cancel, params: params, headers: {}}); 37 | 38 | const cancelUserOrderApi = async (params: any) => 39 | post({url: URL.cancelUserOrder, params: params, headers: {}}); 40 | 41 | export {listApi, userOrderListApi, createApi, updateApi, deleteApi, cancelApi, cancelUserOrderApi}; 42 | -------------------------------------------------------------------------------- /web/src/api/admin/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/api/admin/resume.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/resume/list', 7 | create = '/myapp/admin/resume/create', 8 | update = '/myapp/admin/resume/update', 9 | delete = '/myapp/admin/resume/delete', 10 | } 11 | 12 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} }); 13 | const createApi = async (data: any) => 14 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 15 | const updateApi = async (params:any, data: any) => 16 | post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 17 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, headers: {} }); 18 | 19 | export { listApi, createApi, updateApi, deleteApi }; 20 | -------------------------------------------------------------------------------- /web/src/api/admin/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/api/admin/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, 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/api/admin/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 | -------------------------------------------------------------------------------- /web/src/api/index/address.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | list = '/myapp/index/address/list', 5 | create = '/myapp/index/address/create', 6 | update = '/myapp/index/address/update', 7 | delete = '/myapp/index/address/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/api/index/classification.ts: -------------------------------------------------------------------------------- 1 | import { get, post } from '/@/utils/http/axios'; 2 | enum URL { 3 | list = '/myapp/index/classification/list', 4 | } 5 | 6 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} }); 7 | 8 | export { listApi}; 9 | -------------------------------------------------------------------------------- /web/src/api/index/comment.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | create = '/myapp/index/comment/create', 5 | listThingComments = '/myapp/index/comment/list', 6 | listUserComments = '/myapp/index/comment/listMyComments', 7 | like = '/myapp/index/comment/like' 8 | } 9 | 10 | const createApi = async (data: any) => post({ 11 | url: URL.create, 12 | params: {}, 13 | data: data, 14 | headers: {'Content-Type': 'multipart/form-data;charset=utf-8'} 15 | }); 16 | const listThingCommentsApi = async (params: any) => get({url: URL.listThingComments, params: params, data: {}, headers: {}}); 17 | const listUserCommentsApi = async (params: any) => get({url: URL.listUserComments, params: params, data: {}, headers: {}}); 18 | const likeApi = async (params: any) => post({url: URL.like, params: params, headers: {}}); 19 | 20 | export {createApi, listThingCommentsApi,listUserCommentsApi, likeApi}; 21 | -------------------------------------------------------------------------------- /web/src/api/index/company.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 | listUserCompany = '/myapp/index/company/list_user_company_api', 7 | create = '/myapp/index/company/create', 8 | update = '/myapp/index/company/update', 9 | } 10 | 11 | const listUserCompanyApi = async (params: any) => get({ url: URL.listUserCompany, params: params, data: {}, headers: {} }); 12 | const createApi = async (data: any) => 13 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 14 | const updateApi = async (params:any, data: any) => 15 | post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 16 | 17 | export { listUserCompanyApi, createApi, updateApi }; 18 | -------------------------------------------------------------------------------- /web/src/api/index/notice.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | list = '/myapp/index/notice/list_api', 5 | } 6 | 7 | const listApi = async (params: any) => 8 | get({url: URL.list, params: params, data: {}, headers: {}}); 9 | 10 | export {listApi}; 11 | -------------------------------------------------------------------------------- /web/src/api/index/order.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | create='/myapp/index/order/create', 5 | cancelUserOrder = '/myapp/index/order/cancel_order', 6 | userOrderList = '/myapp/index/order/list', 7 | } 8 | 9 | const createApi = async (data: any) => 10 | post({url: URL.create, data: data, headers: {}}); 11 | 12 | const userOrderListApi = async (params: any) => 13 | get({url: URL.userOrderList, params: params, data: {}, headers: {}}); 14 | 15 | const cancelUserOrderApi = async (params: any) => 16 | post({url: URL.cancelUserOrder, params: params, headers: {}}); 17 | 18 | export {createApi, userOrderListApi, cancelUserOrderApi}; 19 | -------------------------------------------------------------------------------- /web/src/api/index/post.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 | listUserPostUrl = '/myapp/index/post/list_user_post_api', 7 | listCompanyPostUrl = '/myapp/index/post/list_company_post_api', 8 | create = '/myapp/index/post/create', 9 | } 10 | 11 | const listUserPostApi = async (params: any) => get({ url: URL.listUserPostUrl, params: params, data: {}, headers: {} }); 12 | const listCompanyPostApi = async (params: any) => get({ url: URL.listCompanyPostUrl, params: params, data: {}, headers: {} }); 13 | const createApi = async (data: any) => 14 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 15 | 16 | export { listUserPostApi, listCompanyPostApi, createApi }; 17 | -------------------------------------------------------------------------------- /web/src/api/index/resume.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 | detail = '/myapp/index/resume/detail', 7 | create = '/myapp/index/resume/create', 8 | update = '/myapp/index/resume/update', 9 | } 10 | 11 | const detailApi = async (params: any) => get({ url: URL.detail, params: params, data: {}, headers: {} }); 12 | const createApi = async (data: any) => 13 | post({ url: URL.create, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 14 | const updateApi = async (params:any, data: any) => 15 | post({ url: URL.update,params: params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 16 | 17 | export { detailApi, createApi, updateApi }; 18 | -------------------------------------------------------------------------------- /web/src/api/index/tag.ts: -------------------------------------------------------------------------------- 1 | import {get, post} from '/@/utils/http/axios'; 2 | 3 | enum URL { 4 | list = '/myapp/index/tag/list', 5 | } 6 | 7 | const listApi = async (params: any) => 8 | get({url: URL.list, params: params, data: {}, headers: {}}); 9 | 10 | export {listApi}; 11 | -------------------------------------------------------------------------------- /web/src/api/index/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/index/thing/list', 7 | listUserThing = '/myapp/index/thing/list_user_thing', 8 | create = '/myapp/index/thing/create', 9 | update = '/myapp/index/thing/update', 10 | delete = '/myapp/index/thing/delete', 11 | detail = '/myapp/index/thing/detail', 12 | addWishUser = '/myapp/index/thing/addWishUser', 13 | addCollectUser = '/myapp/index/thing/addCollectUser', 14 | getCollectThingList = '/myapp/index/thing/getCollectThingList', 15 | getWishThingList = '/myapp/index/thing/getWishThingList', 16 | removeCollectUser = '/myapp/index/thing/removeCollectUser', 17 | removeWishUser = '/myapp/index/thing/removeWishUser' 18 | } 19 | 20 | const listApi = async (params: any) => get({ url: URL.list, params: params, data: {}, headers: {} }); 21 | const listUserThingApi = async (params: any) => get({ url: URL.listUserThing, params: params, data: {}, headers: {} }); 22 | const createApi = async (data:any) => post({ url: URL.create, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 23 | const updateApi = async (params: any, data:any) => post({ url: URL.update, params: params, data: data,headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 24 | const deleteApi = async (params: any) => post({ url: URL.delete, params: params, data: {}, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 25 | const detailApi = async (params: any) => get({ url: URL.detail, params: params, headers: {} }); 26 | const addWishUserApi = async (params: any) => post({ url: URL.addWishUser, params: params, headers: {} }); 27 | const addCollectUserApi = async (params: any) => post({ url: URL.addCollectUser, params: params, headers: {} }); 28 | const getCollectThingListApi = async (params: any) => get({ url: URL.getCollectThingList, params: params, headers: {} }); 29 | const getWishThingListApi = async (params: any) => get({ url: URL.getWishThingList, params: params, headers: {} }); 30 | 31 | const removeCollectUserApi = async (params: any) => post({ url: URL.removeCollectUser, params: params, headers: {} }); 32 | const removeWishUserApi = async (params: any) => post({ url: URL.removeWishUser, params: params, headers: {} }); 33 | 34 | 35 | export { listApi,listUserThingApi, createApi,updateApi,deleteApi,detailApi, addWishUserApi,addCollectUserApi, getCollectThingListApi, 36 | getWishThingListApi, removeCollectUserApi, removeWishUserApi }; 37 | -------------------------------------------------------------------------------- /web/src/api/index/thingCollect.ts: -------------------------------------------------------------------------------- 1 | 2 | import { get, post } from '/@/utils/http/axios'; 3 | import { UserState } from '/@/store/modules/user/types'; 4 | 5 | enum URL { 6 | userCollectList = '/api/thingCollect/getUserCollectList', 7 | collect = '/api/thingCollect/collect', 8 | unCollect = '/api/thingCollect/unCollect', 9 | } 10 | 11 | const collectApi = async (data: any) => post({ url: URL.collect, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 12 | const unCollectApi = async (params: any) => post({ url: URL.unCollect, params: params, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 13 | const userCollectListApi = async (params: any) => get({ url: URL.userCollectList, params: params }); 14 | 15 | export { collectApi, unCollectApi, userCollectListApi }; 16 | -------------------------------------------------------------------------------- /web/src/api/index/thingWish.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 | userWishList = '/api/thingWish/getUserWishList', 7 | wish = '/api/thingWish/wish', 8 | unWish = '/api/thingWish/unWish', 9 | } 10 | 11 | const wishApi = async (data: any) => post({ url: URL.wish, params: {}, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 12 | const unWishApi = async (params: any) => post({ url: URL.unWish, params: params, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 13 | const userWishListApi = async (params: any) => get({ url: URL.userWishList, params: params }); 14 | 15 | export { wishApi, unWishApi, userWishListApi }; 16 | -------------------------------------------------------------------------------- /web/src/api/index/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 | userLogin = '/myapp/index/user/login', 7 | userRegister = '/myapp/index/user/register', 8 | detail = '/myapp/index/user/info', 9 | updateUserPwd = '/myapp/index/user/updatePwd', 10 | updateUserInfo = '/myapp/index/user/update' 11 | } 12 | interface LoginRes { 13 | token: string; 14 | } 15 | 16 | export interface LoginData { 17 | username: string; 18 | password: string; 19 | } 20 | 21 | const detailApi = async (params: any) => get({ url: URL.detail, params: params, data: {}, headers: {} }); 22 | const userLoginApi = async (data: LoginData) => post({ url: URL.userLogin, data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 23 | const userRegisterApi = async (data: any) => post({ url: URL.userRegister, params: {}, data: data }); 24 | const updateUserPwdApi = async (params: any, data:any) => post({ url: URL.updateUserPwd, params: params, data:data }); 25 | const updateUserInfoApi = async (params: any,data: any) => post({ url: URL.updateUserInfo, params:params, data: data, headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' } }); 26 | 27 | export { detailApi, userLoginApi, userRegisterApi, updateUserPwdApi, updateUserInfoApi}; 28 | -------------------------------------------------------------------------------- /web/src/assets/fonts/Blimone-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/fonts/Blimone-ExtraBold.woff -------------------------------------------------------------------------------- /web/src/assets/fonts/Blimone-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/fonts/Blimone-ExtraLight.woff -------------------------------------------------------------------------------- /web/src/assets/fonts/Blimone-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/fonts/Blimone-Light.woff -------------------------------------------------------------------------------- /web/src/assets/fonts/Blimone-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/fonts/Blimone-Regular.woff -------------------------------------------------------------------------------- /web/src/assets/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/icons/logo.png -------------------------------------------------------------------------------- /web/src/assets/icons/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/assets/icons/svg/marks.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/svg/test.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/assets/icons/svg/ts.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /web/src/assets/icons/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/address-right-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/ali-pay-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/avatar.jpg -------------------------------------------------------------------------------- /web/src/assets/images/banner-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/banner-02.webp -------------------------------------------------------------------------------- /web/src/assets/images/banner2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 12 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /web/src/assets/images/cart-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/clear-search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/code-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/delete-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /web/src/assets/images/ebook-download-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/ic-company.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/ic-company.png -------------------------------------------------------------------------------- /web/src/assets/images/k-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/k-logo.png -------------------------------------------------------------------------------- /web/src/assets/images/login-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/login-banner.png -------------------------------------------------------------------------------- /web/src/assets/images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/login.png -------------------------------------------------------------------------------- /web/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/logo.png -------------------------------------------------------------------------------- /web/src/assets/images/mail-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/message-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /web/src/assets/images/order-address-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/order-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/order-point-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/order-thing-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/pwd-hidden.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/src/assets/images/pwd-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/qunerweima.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/images/qunerweima.jpg -------------------------------------------------------------------------------- /web/src/assets/images/read-online-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /web/src/assets/images/recommend-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /web/src/assets/images/register-name.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/search-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/searchIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/setting-card-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/src/assets/images/setting-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/setting-msg-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/setting-push-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/setting-safe-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/share-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/assets/images/tel-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/images/want-read-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/wb-share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/assets/images/wx-pay-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /web/src/assets/styles/base.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/python_job/570498312a91d5a1ea8f0988376bfc43f6bb6c0f/web/src/assets/styles/base.less -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/src/main.ts: -------------------------------------------------------------------------------- 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/router/index.ts: -------------------------------------------------------------------------------- 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 | 14 | const router = createRouter({ 15 | history: createWebHistory(), 16 | routes: root, 17 | }); 18 | 19 | router.beforeEach(async (to, from, next) => { 20 | console.log(to, from) 21 | 22 | /** 后台路由 **/ 23 | if (to.path.startsWith('/admin')) { 24 | if (localStorage.getItem(ADMIN_USER_TOKEN)) { 25 | if (to.path === adminLoginRoutePath) { 26 | next({ path: '/' }) 27 | } else { 28 | next() 29 | } 30 | } else { 31 | if (allowList.includes(to.name as string)) { 32 | // 在免登录名单,直接进入 33 | next() 34 | } else { 35 | next({ path: adminLoginRoutePath, query: { redirect: to.fullPath } }) 36 | } 37 | } 38 | // next() 39 | } 40 | 41 | /** 前台路由 **/ 42 | if (to.path.startsWith('/index')) { 43 | if (localStorage.getItem(USER_TOKEN)) { 44 | if (to.path === loginRoutePath) { 45 | next({ path: '/' }) 46 | } else { 47 | next() 48 | } 49 | } else { 50 | if (allowList.includes(to.name as string)) { 51 | // 在免登录名单,直接进入 52 | next() 53 | } else { 54 | next({ path: loginRoutePath, query: { redirect: to.fullPath } }) 55 | } 56 | } 57 | // next() 58 | } 59 | 60 | }); 61 | 62 | router.afterEach((_to) => { 63 | // 回到顶部 64 | document.getElementById("html")?.scrollTo(0, 0) 65 | }); 66 | 67 | export default router; 68 | -------------------------------------------------------------------------------- /web/src/store/constants.ts: -------------------------------------------------------------------------------- 1 | const BASE_URL = 'http://127.0.0.1:8000' 2 | // const BASE_URL = 'http://124.221.83.178:9001' 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 | 13 | export {BASE_URL, USER_TOKEN, USER_NAME, USER_ID, ADMIN_USER_ID,ADMIN_USER_NAME,ADMIN_USER_TOKEN } 14 | -------------------------------------------------------------------------------- /web/src/store/index.ts: -------------------------------------------------------------------------------- 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/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/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/src/store/modules/user/index.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import {loginApi as adminLogin} from '/@/api/admin/user'; 3 | import {userLoginApi} from '/@/api/index/user'; 4 | import { setToken, clearToken } from '/@/utils/auth'; 5 | import { UserState } from './types'; 6 | import {USER_ID, USER_NAME, USER_TOKEN, ADMIN_USER_ID,ADMIN_USER_NAME,ADMIN_USER_TOKEN} from "/@/store/constants"; 7 | 8 | export const useUserStore = defineStore('user', { 9 | state: (): UserState => ({ 10 | user_id: undefined, 11 | user_name: undefined, 12 | user_token: undefined, 13 | 14 | admin_user_id: undefined, 15 | admin_user_name: undefined, 16 | admin_user_token: undefined, 17 | }), 18 | getters: {}, 19 | actions: { 20 | // 用户登录 21 | async login(loginForm) { 22 | const result = await userLoginApi(loginForm); 23 | console.log('result==>', result) 24 | 25 | if(result.code === 0) { 26 | this.$patch((state)=>{ 27 | state.user_id = result.data.id 28 | state.user_name = result.data.username 29 | state.user_token = result.data.token 30 | console.log('state==>', state) 31 | }) 32 | 33 | localStorage.setItem(USER_TOKEN, result.data.token) 34 | localStorage.setItem(USER_NAME, result.data.username) 35 | localStorage.setItem(USER_ID, result.data.id) 36 | } 37 | 38 | return result; 39 | }, 40 | // 用户登出 41 | async logout() { 42 | // await userLogout(); 43 | this.$patch((state)=>{ 44 | localStorage.removeItem(USER_ID) 45 | localStorage.removeItem(USER_NAME) 46 | localStorage.removeItem(USER_TOKEN) 47 | 48 | state.user_id = undefined 49 | state.user_name = undefined 50 | state.user_token = undefined 51 | }) 52 | }, 53 | 54 | // 管理员登录 55 | async adminLogin(loginForm) { 56 | const result = await adminLogin(loginForm); 57 | console.log('result==>', result) 58 | 59 | if(result.code === 0) { 60 | this.$patch((state)=>{ 61 | state.admin_user_id = result.data.id 62 | state.admin_user_name = result.data.username 63 | state.admin_user_token = result.data.admin_token 64 | console.log('state==>', state) 65 | }) 66 | 67 | localStorage.setItem(ADMIN_USER_TOKEN, result.data.admin_token) 68 | localStorage.setItem(ADMIN_USER_NAME, result.data.username) 69 | localStorage.setItem(ADMIN_USER_ID, result.data.id) 70 | } 71 | 72 | return result; 73 | }, 74 | // 管理员登出 75 | async adminLogout() { 76 | // await userLogout(); 77 | this.$patch((state)=>{ 78 | localStorage.removeItem(ADMIN_USER_ID) 79 | localStorage.removeItem(ADMIN_USER_NAME) 80 | localStorage.removeItem(ADMIN_USER_TOKEN) 81 | 82 | state.admin_user_id = undefined 83 | state.admin_user_name = undefined 84 | state.admin_user_token = undefined 85 | }) 86 | }, 87 | }, 88 | }); 89 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/src/styles/index.less: -------------------------------------------------------------------------------- 1 | //自定义css 2 | a { 3 | color: #1890ff; 4 | } 5 | -------------------------------------------------------------------------------- /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/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/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: import.meta.env.BASE_URL + '', 11 | baseURL: BASE_URL + '', 12 | timeout: 15000, 13 | }); 14 | 15 | // axios实例拦截请求 16 | service.interceptors.request.use( 17 | (config: InternalAxiosRequestConfig) => { 18 | 19 | config.headers.ADMINTOKEN = localStorage.getItem(ADMIN_USER_TOKEN) 20 | config.headers.TOKEN = localStorage.getItem(USER_TOKEN) 21 | 22 | return config; 23 | }, 24 | (error: AxiosError) => { 25 | return Promise.reject(error); 26 | }, 27 | ); 28 | 29 | // axios实例拦截响应 30 | service.interceptors.response.use( 31 | (response: AxiosResponse) => { 32 | if(response.status == 200) { 33 | if(response.data.code == 0 || response.data.code == 200) { 34 | return response 35 | }else { 36 | return Promise.reject(response.data) 37 | } 38 | } else { 39 | return Promise.reject(response.data) 40 | } 41 | }, 42 | // 请求失败 43 | (error: any) => { 44 | console.log(error.response.status) 45 | if(error.response.status == 404) { 46 | // todo 47 | } else if(error.response.status == 403) { 48 | // todo 49 | } 50 | return Promise.reject(error) 51 | }, 52 | ); 53 | 54 | 55 | 56 | const request = (config: AxiosRequestConfig): Promise => { 57 | const conf = config; 58 | return new Promise((resolve, reject) => { 59 | service.request>(conf).then((res: AxiosResponse) => { 60 | const data = res.data 61 | resolve(data as T); 62 | }).catch(err => { 63 | reject(err) 64 | }); 65 | }); 66 | }; 67 | 68 | export function get(config: AxiosRequestConfig): Promise { 69 | return request({ ...config, method: 'GET' }); 70 | } 71 | 72 | export function post(config: AxiosRequestConfig): Promise { 73 | return request({ ...config, method: 'POST' }); 74 | } 75 | 76 | export default request; 77 | 78 | export type { AxiosInstance, AxiosResponse }; 79 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/src/views/index/components/footer.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | 30 | 84 | -------------------------------------------------------------------------------- /web/src/views/index/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | -------------------------------------------------------------------------------- /web/src/views/index/portal.vue: -------------------------------------------------------------------------------- 1 | 8 | 25 | 28 | -------------------------------------------------------------------------------- /web/src/views/index/register.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 88 | 89 | 211 | -------------------------------------------------------------------------------- /web/src/views/index/search.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /web/src/views/index/user/collect-thing-view.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 86 | 196 | -------------------------------------------------------------------------------- /web/src/views/index/user/comment-view.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 70 | 156 | -------------------------------------------------------------------------------- /web/src/views/index/user/fans-view.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | 104 | -------------------------------------------------------------------------------- /web/src/views/index/user/follow-view.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 28 | 105 | -------------------------------------------------------------------------------- /web/src/views/index/user/message-view.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 57 | 144 | -------------------------------------------------------------------------------- /web/src/views/index/user/modal/edit-address.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 113 | 114 | 117 | -------------------------------------------------------------------------------- /web/src/views/index/user/push-view.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 80 | 184 | -------------------------------------------------------------------------------- /web/src/views/index/user/score-view.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 35 | 68 | -------------------------------------------------------------------------------- /web/src/views/index/user/security-view.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 100 | 198 | -------------------------------------------------------------------------------- /web/src/views/index/usercenter.vue: -------------------------------------------------------------------------------- 1 | 19 | 37 | 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | --------------------------------------------------------------------------------