优点

1、使不同应用开发环境相互独立
2、环境升级不影响其他应用,也不会影响全局的python环境
3、防止出现包管理混乱及包版本冲突

windows

安装
# 建议使用pip3安装到python3环境下
pip3 install virtualenv
pip3 install virtualenvwrapper-win
配置虚拟环境管理器工作目录
# 配置环境变量:
# 控制面板 => 系统和安全 => 系统 => 高级系统设置 => 环境变量 => 系统变量 => 点击新建 => 填入变量名与值
变量名:WORKON_HOME  变量值:自定义存放虚拟环境的绝对路径
eg: WORKON_HOME: D:\Virtualenvs

# 同步配置信息:
# 去向Python3的安装目录 => Scripts文件夹 => virtualenvwrapper.bat => 双击

MacOS、Linux

安装
# 建议使用pip3安装到python3环境下
pip3 install -i https://pypi.douban.com/simple virtualenv
pip3 install -i https://pypi.douban.com/simple virtualenvwrapper
工作文件
# 先找到virtualenvwrapper的工作文件 virtualenvwrapper.sh,该文件可以刷新自定义配置,但需要找到它
# MacOS可能存在的位置 /Library/Frameworks/Python.framework/Versions/版本号文件夹/bin
# Linux可能所在的位置 /usr/local/bin  |  ~/.local/bin  |  /usr/bin
# 建议不管virtualenvwrapper.sh在哪个目录,保证在 /usr/local/bin 目录下有一份
# 如果不在 /usr/local/bin 目录,如在 ~/.local/bin 目录,则复制一份到 /usr/local/bin 目录
    -- sudo cp -rf ~/.local/bin/virtualenvwrapper.sh /usr/local/bin
配置
# 在 ~/.bash_profile 完成配置,virtualenvwrapper的默认默认存放虚拟环境路径是 ~/.virtualenvs
# WORKON_HOME=自定义存放虚拟环境的绝对路径,需要自定义就解注
VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

# 在终端让配置生效:
    -- source ~/.bash_profile

使用

# 在终端工作的命令

# 1、创建虚拟环境到配置的WORKON_HOME路径下

创建环境  --mkvirtualenv t1
查看环境  --pip list  
# 选取默认Python环境创建虚拟环境:
    -- mkvirtualenv 虚拟环境名称
# 基于某Python环境创建虚拟环境:(用于区别当前python环境安装)
    -- mkvirtualenv -p python2.7 虚拟环境名称
    -- mkvirtualenv -p python3.6 虚拟环境名称

# 2、查看已有的虚拟环境
    -- workon

# 3、使用某个虚拟环境
    -- workon 虚拟环境名称    例 worken t1
    
# 4、进入|退出 该虚拟环境的Python环境
    -- python | exit()

# 5、为虚拟环境安装模块
    -- pip或pip3 install 模块名

# 6、退出当前虚拟环境
    -- deactivate

# 7、删除虚拟环境(删除当前虚拟环境要先退出)    递归删除,比普通删除速度快
    -- rmvirtualenv 虚拟环境名称

 

linux下用virtualenv创建虚拟环境

# 此为简便方法,缺点是要记住虚拟环境的路径,不如上面的方法能看当前所有虚拟环境
1.创建虚拟环境文件夹
/home/myproject_lu

2.进入该目录下,创建虚拟环境
virtualenv --python=python3 matrix-client-lu
# --no-site-packages 在ubantu20.04里python3.8这个参数报错,内部已经带上了该参数
# --python选择python版本

3.在创建的虚拟环境文件夹下bin文件里会有activate文件
(/home/myproject_lu/matrix-client-lu/bin)

4.激活虚拟环境,进入虚拟环境  在bin文件下
source activate

5.在虚拟环境下,查看python3,和pip3来源
which python3
which pip3

6.退出虚拟环境
deactivate

7.删除虚拟环境,只要删除对应的虚拟环境文件即可

 

pycharm使用

新建项目

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

添加环境

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

使用环境

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

 

luffy后台

后台:Django项目创建

环境

"""
为luffy项目创建一个虚拟环境
>: mkvirtualenv luffy
"""

"""
按照基础环境依赖
>: pip install django==2.0.7
>: pip install djangorestframework
>: pip install pymysql
"""

创建项目

"""
前提:在目标目录新建luffy文件夹
>: cd 建立的luffy文件夹
>: django-admin startproject luffyapi

开发:用pycharm打开项目,并选择提前备好的虚拟环境
"""

重构项目目录

"""
├── luffyapi
    ├── logs/                # 项目运行时/开发时日志目录 - 包
    ├── manage.py            # 脚本文件
    ├── luffyapi/              # 项目主应用,开发时的代码保存 - 包
         ├── apps/              # 开发者的代码保存目录,以模块[子应用]为目录保存 - 包
        ├── libs/              # 第三方类库的保存目录[第三方组件、模块] - 包
        ├── settings/          # 配置目录 - 包
            ├── dev.py       # 项目开发时的本地配置
            └── prod.py      # 项目上线时的运行配置
        ├── urls.py            # 总路由
        └── utils/             # 多个模块[子应用]的公共函数类库[自己开发的组件]
    └── scripts/               # 保存项目运营时的脚本文件 - 文件夹
"""
聊一聊为什么要集体管理django项目的应用app,如果用子文件夹集中管理了应用app,如何实现app可以直接在settings中注册
一个成型的Django项目,会有很多功能模块,每一个功能模块对应一个app管理,所以导致项目由众多app,集中管理,结构更清晰,代码更规范。
例如用apps文件夹集中管理app,要将apps文件夹在settings配置中添加到环境变量,apps下的应用都是对Django项目直接可见,所以可以直接注册

配置开发环境

"""
1.修改 wsgi.py 与 manage.py 两个文件:
# manage.py                线下启动, startapp,数据迁移功能
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev')  # 修改配置路径
# wsgi.py                线上启动
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.prod')  # 修改配置路径
# manage_prod.py        线上也需要startapp,数据迁移功能,所以拷贝一个
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.prod')  # 修改配置路径

2.将settings.py删除或改名,内容拷贝到settings/dev.py中

3.修改dev.py文件内容
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

4.修改启动配置:见插图 (如果启动不了,修改DJANGO_SETTINGS_MODULE字段(参考manage.py))

5.在任何一个__init__.py文件中测试默认配置文件是否是dev.py文件
from django.conf import settings
print(settings)
"""

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

 

luffy后台配置

环境变量

dev.py
# 环境变量操作:小luffyapiBASE_DIR与apps文件夹都要添加到环境变量
import sys
sys.path.insert(0, BASE_DIR)  
APPS_DIR = os.path.join(BASE_DIR, 'apps')
sys.path.insert(1, APPS_DIR)  # 添加后INSTALLED_APPS=('apps.app01')内直接写app01即可, 不加就要写全


#或者开头这样写(我测试的是这种)
import os,sys

# BASE_DIR现在是小luffyapi
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 代码将BASE_DIR添加到环境变量,pycharm开发就可以采用Sources Root技巧
sys.path.append(BASE_DIR)
# apps文件夹要添加到环境变量中,那apps下面的模块就可以直接被注册了
APPS_DIR = os.path.join(BASE_DIR,'apps')
sys.path.append(APPS_DIR)
在写项目直接导入utils文件夹也不''错误提示'' 直接把luffyapi右键变成环境变量即可

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

封装logger

# django自带Logging,但是默认只玩控制台记录,所以要明文配置一下
"""日志logging
    i)import logging 是Python解释器自带的(os、sys)
    ii)django项目加载settings配置,加载了LOGGING的配置字典,加载了配置中的所有logger
        logger(打印者)会有handler(输出位置),handler会有formatter(打印格式)和filter(信息过滤器)
    iii)提供logging模块的getLogger('logger名')方法来获取打印者,调用日志打印功能
    iv)打印级别有5种:debug、info、warning、error、critical,自己根据需求选择,你将要打印的信息定义为什么级别,你就采用什么级别进行打印
    v)handler可以设置收纳的级别信息,如果file handler设置了收纳级别为WARNING,那用logger完成五种几个的各一个信息打印,只有warning及以上级别会被记录到file中
"""
dev.py
# 真实项目上线后,日志文件打印级别不能过低,因为一次日志记录就是一次文件io操作
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已存在的日志器(像django自带的输出)
    'formatters': {  # 日志信息显示的格式
        'verbose': {  # 输出格式
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {  # 输出格式
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {  # 日志过滤器
        'require_debug_true': {  # django在debug情况下才输出日志
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志处理器
        'console': {  # 向终端输出日志
            # 实际开发建议使用WARNING
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有debug为true才输出
            'class': 'logging.StreamHandler',
            'formatter': 'simple'  # 输出格式
        },
        'file': {  # 向文件输出(不需要过滤)
            # 实际开发建议使用ERROR
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose',
            # 文件内容编码
            'encoding': 'utf-8'
        },
    },
    # 日志对象
    'loggers': {  # 日志器
        'django': {  # 定义一个名为django的日志器(可以添加level等级)
            'handlers': ['console', 'file'],
            'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}
utils/logging.py
import logging
logger = logging.getLogger('django')

logging模块的使用方法:

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

代码示例:

# 项目/.../settings
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'default': {
            'format': '%(asctime)s %(levelname)s %(pathname)s %(filename)s %(funcName)s %(lineno)d %(message)s',
            # 自定义输出格式
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s',
        },
    },
    'filters': {
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'articles_handler': {  # 可为每个app设定一个handler
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 输出到文件
            'encoding': "utf-8",
            'filename': os.path.join(LOGS_ROOT, 'articles.log'),  # 日志文件路径+文件名
            'formatter': 'default'
        },
        'crawler_handler': {  # 可为每个app设定一个handler
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 输出到文件
            'encoding': "utf-8",
            'filename': os.path.join(LOGS_ROOT, 'crawler.log'),  # 日志文件路径+文件名
            'formatter': 'default'
        }
    },
    'loggers': {    # 项目apps里有articles和crawler两个app
        'articles': {  # 可为每个app设定一个logger
            'handlers': ['articles_handler', 'console'],  # 选定handler进行处理
            'level': 'INFO',
            'propagate': False,
        },
        'crawler': {  # 可为每个app设定一个logger
            'handlers': ['crawler_handler', 'console'],  # 选定handler进行处理
            'level': 'INFO',
            'propagate': False,
        }

    }
}
# 项目/apps/.../save_invitation.py
import logging

logging = logging.getLogger("articles")

...
logging.info('source_column:' + source_column + " 发布者:" + ran_user[0])  # 记录 来源栏目 用户名  注意:info中不可以用逗号分割

 

封装项目异常处理

utils/exception.py
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.views import Response
from rest_framework import status
from utils.logging import logger
import logging    
logging.getLogger('django')
def exception_handler(exc, context):  # 重写drf内部异常处理方法,追加无法处理的异常情况下异常的记录
    response = drf_exception_handler(exc, context)
    if response is None:
        # 记录服务器异常
        logger.critical('%s' % exc)
        response = Response({'detail': '服务器异常,请重试...'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    return response
settings.py

注意:要放在django配置之上,否则不起作用

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
}

可以做utils文件下logging.py文件

import logging
logger = logging.getLogger('django')

 

二次封装Response模块

utils/response.py
from rest_framework.response import Response

class APIResponse(Response):
    def __init__(self, status=0, msg='ok', http_status=None, headers=None, exception=False, **kwargs):
        data = {
            'status': status,
            'msg': msg,
        }
        if kwargs:
            data.update(kwargs)
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)

路由组件配置

utils/router.py
from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter

class SimpleRouter(DRFSimpleRouter):
    routes = [
        # List route.  /资源s/
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',  # 群查
                'post': 'create',  # 单增、群增
                'put': 'multiple_update',  # 群整改
                'patch': 'multiple_partial_update',  # 群局改
                'delete': 'multiple_destroy',  # 群删
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.  /资源s/(pk)/
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',  # 单查
                'put': 'update',  # 单整改
                'patch': 'partial_update',  # 单局改
                'delete': 'destroy'  # 单删
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]

# 对外提供十大接口的router对象
router = SimpleRouter()
# eg: router.register('users', UserModelViewSet, basename='user')
"""
/users/
'get': 'list',  # 群查
'post': 'create',  # 单增、群增
'put': 'multiple_update',  # 群整改
'patch': 'multiple_partial_update',  # 群局改
'delete': 'multiple_destroy',  # 群删

/users/(pk)/
'get': 'retrieve',  # 单查
'put': 'update',  # 单整改
'patch': 'partial_update',  # 单局改
'delete': 'destroy'  # 单删
"""

 

luffy数据库

数据库配置

创建数据库

"""
1.管理员连接数据库
>: mysql -uroot -proot

2.创建数据库
>: create database luffy default charset=utf8;

3.查看用户
>: select user,host,password from mysql.user;

# 5.7往后的版本
>: select user,host,authentication_string from mysql.user;
"""

为指定数据库配置指定账户

"""
设置权限账号密码
# 授权账号命令:grant 权限(create, update) on 库.表 to '账号'@'host' identified by '密码'

1.配置任意ip都可以连入数据库的账户        luffy 用户名    Luffy123? 密码
注释:只给luffy这个库所有的权限
>: grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?';

2.由于数据库版本的问题,可能本地还连接不上,就给本地用户单独配置    我5.6.40要用此命令
>: grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?';

3.刷新一下权限
>: flush privileges;

只能操作luffy数据库的账户
账号:luffy
密码:Luffy123?
"""

#再次登录  
mysql -uluffy -pLuffy123?

配置文件配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'luffy',
        'USER': 'luffy',
        'PASSWORD': 'Luffy123?',
        'HOST': 'localhost',
        'PORT': 3306
    }
}
import pymysql
pymysql.install_as_MySQLdb()

 

Django 2.x 一些版本pymysql兼容问题

Django不采用2.0.7版本很可能出现以下问题,需要修改源代码

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

 

User模块User表

创建user模块

前提:在 luffy 虚拟环境下

1.终端从项目根目录进入apps目录
>: cd luffyapi & cd apps

2.创建app
>: python ../../manage.py startapp user

创建User表对应的model:user/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    mobile = models.CharField(max_length=11, unique=True)
    # 需要pillow包的支持
    icon = models.ImageField(upload_to='icon', default='icon/default.png')

    class Meta:
        db_table = 'luffy_user'
        verbose_name = '用户表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

注册user模块,配置User表:dev.py

INSTALLED_APPS = [
    # ...
    'user',
]

# 自定义User表
AUTH_USER_MODEL = 'user.User'

配置media

media配置:dev.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
media目录配置
"""
├── luffyapi
    └──    luffyapi/
           └──    media/      
            └──    icon 
                └── default.png
"""
主路由:luffyapi/urls.py
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.static import serve
from django.conf import settings
urlpatterns = [
    path('admin/', admin.site.urls),

    path('user/', include('user.urls')),

    re_path('^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]
子路由:user/urls.py
from django.urls import path, include
from utils.router import router

# 注册ViewSet的路由
# router.register()

urlpatterns = [
    path('', include(router.urls)),
]

数据库迁移

"""
1)去向大luffyapi所在目录的终端

2)安装pillow模块
pip install pillow

3)数据库迁移
python manage.py makemigrations
python manage.py migrate
"""

 

luffy前台

前台

vue环境

1.傻瓜式安装node: 
官网下载:https://nodejs.org/zh-cn/

2.安装cnpm: 
>: npm install -g cnpm --registry=https://registry.npm.taobao.org

3.安装vue最新脚手架: 
>: cnpm install -g @vue/cli

注:如果2、3步报错,清除缓存后重新走2、3步
>: npm cache clean --force

创建项目

"""
前提:在目标目录新建luffy文件夹
>: cd 建立的luffy文件夹
>: vue create luffycity
"""

路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

 路飞项目搭建1      虚拟环境搭建, luffy后台(配置, 数据库, user模块user表), luffy前台配置

重构项目目录

"""
├── luffycity
    ├── public/                      # 项目共有资源
        ├── favicon.ico                # 站点图标
        └── index.html                # 主页
    ├── src/                          # 项目主应用,开发时的代码保存
        ├── assets/                  # 前台静态资源总目录
            ├── css/                # 自定义css样式
                └── global.css        # 自定义全局样式
            ├── js/                    # 自定义js样式
                └── settings.js        # 自定义配置文件
            └── img/                # 前台图片资源
        ├── components/                # 小组件目录
        ├── views/                  # 页面组件目录
        ├── App.vue                    # 根组件
        ├── main.js                    # 入口脚本文件
        ├── router            
            └── index.js            # 路由脚本文件
        store                
            └── index.js            # 仓库脚本文件
    ├── vue.config.js                # 项目配置文件
    └── *.*                            # 其他配置文件
"""

文件修订:目录中非配置文件的多余文件可以移除

App.vue
<template>
    <div id="app">
        <router-view/>
    </div>
</template>
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter);

const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home
    },
    {
        path: '/home',
        redirect: '/',
    },
];

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

export default router
Home.vue
<template>
    <div class="home">
    </div>
</template>

<script>
    export default {
        name: 'home',
        components: {
        },
    }
</script>

全局配置:全局样式、配置文件

global.css
/* 声明全局样式和项目的初始化样式 */
body, h1, h2, h3, h4, h5, h6, p, table, tr, td, ul, li, a, form, input, select, option, textarea {
    margin: 0;
    padding: 0;
    font-size: 15px;
}

a {
    text-decoration: none;
    color: #333;
}

ul {
    list-style: none;
}

table {
    border-collapse: collapse; /* 合并边框 */
}
settings.js
export default {
    base_url: 'http://127.0.0.1:8000'
}
main.js
// 配置全局样式
import '@/assets/css/global.css'

// 配置全局自定义设置
import settings from '@/assets/js/settings'
Vue.prototype.$settings = settings;
// 在所有需要与后台交互的组件中:this.$settings.base_url + '再拼接具体后台路由'

 

luffy前台配置

axios前后台交互

安装:前端项目目录下的终端
>: cnpm install axios
配置:main.js
import axios from 'axios'
Vue.prototype.$axios = axios;

cookies操作

安装:前端项目目录下的终端
>: cnpm install vue-cookies
配置:main.js
import cookies from 'vue-cookies'
Vue.prototype.$cookies = cookies;

element-ui页面组件框架

安装:前端项目目录下的终端
>: cnpm install element-ui
配置:main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

bootstrap页面组件框架

安装:前端项目目录下的终端
>: cnpm install jquery
>: cnpm install bootstrap@3
配置jquery:vue.config.js 项目根目录下
const webpack = require("webpack");

module.exports = {
    configureWebpack: {
        plugins: [
            new webpack.ProvidePlugin({
                $: "jquery",
                jQuery: "jquery",
                "window.jQuery": "jquery",
                "window.$": "jquery",
                Popper: ["popper.js", "default"]
            })
        ]
    }
};
配置bootstrap:main.js
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'

 



 

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-05-05
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-16
  • 2021-09-14
猜你喜欢
  • 2022-12-23
  • 2022-03-04
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-17
相关资源
相似解决方案