效果图:

前言

        在开始之前,让我们先来聊聊 Django 与 Flask 。用装修风格来类比:一个是轻奢风格(下图左),一个是叙利亚风格(下图右):

 

        Django 的使用更加惬意,大概就是租客口中的 ‘拎包入住’ 的模式,工具齐全,只需要考虑吃 肉夹馍(CBV)还是 4菜1汤(FBV),偶尔要换个冰箱(数据库)还得考虑合不合适。

        而使用 Flask 就如同找了一个叙利亚风格的出租房,看上去很简陋,欸,实际上又什么都有(指 地板、天花板 ),不过想要别有一番风味,还得自己的喜好来布置。

#Ps:文章最后附上了项目全部的代码

1、铺地板(flask 基础样式编写)

        编码工具各凭喜好,我这里使用的 Visio Studio Code  ,由于要使用到 vue框架 ,我们要在一开始就把 Flask 跟 vue 区分开来,给他们安排上单人包间。也就是说新建两个文件夹,前后 端代码分开存放。

        在后端文件夹中新建一个 app.py 来作为我们后端的主程序,在里面写入:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

         大概就是这样子:

        接着我们打开控制台,进入到 flask 文件夹中,在里面输入以下代码:

set FLASK_APP=app.py    
# 设置 FLASK 环境变量,如果名称是 app.py 或者 wsgi.py 可以忽略这一句代码

flask run
# 启动

 可以看到控制台输出了一个IP,在浏览器中打开:

 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

       

2、选择一个储物室(数据库)

        这里我选用的是 Flask + SQLAlchemy + SQLite 作为我们的数据存储体系,在 app 中导入 SQLAlchemy:

from flask_sqlalchemy import SQLAlchemy

根据我们需要的字段,创建表:

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'
# 指定数据库文件
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 允许修改跟踪数据库
db = SQLAlchemy(app)

class Books(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80), nullable=False)
    author = db.Column(db.String(120), nullable=False)
    read= db.Column(db.Boolean)
db.create_all()
# 创建表 

        重新运行 app.py 文件,可以看到数据库文件已经生成成,接下来我们定义几个函数便于我们对数据库的增删改查:

# 增
def insertData(title,author,read):
    book=Books(title=title,author=author,read=read)
    db.session.add_all([book])
    db.session.commit()

# 查
def selectDataAll():
    book_list=[]
    books = Books.query.all()
    # 类似于 select * from Books
    for s in books:
        dic = {}
        dic['id'] = s.id
        dic['title'] = s.title
        dic['author'] = s.author
        dic['read'] = s.read
        print(f'Id: {s.id} | Title: {s.title} | Author: {s.author} | Read: {s.read}')

        print('----')
        book_list.append(dic)
    return book_list

# 删
def deleteData(id):
    book = Books.query.get(id)
    # 类似于 select * from Books where id = id 
    db.session.delete(book)
    db.session.commit()
    # 提交操作到数据库
    
# 改
def updateData(id, title='', author='', read='', new_id=''):

    book = Books.query.get(id)
    if not title=='':
        book.title = title
    if not author == '':
        book.author = author
    if not read == '':
        book.read = read
    if not new_id == '':
        book.id=new_id
    db.session.commit()

        利用我们写好的 增函数 添加数据:

···

insertData('book1','zhangsan',0)
insertData('book2','lisi',1)

···

运行 app.py 后数据就被添加到数据库中。

#Ps:运行后记得在 app.py 中删除上面两句代码

 3、提交数据库数据至前端

···
from flask import Flask, jsonify, request, make_response
···

···
@app.route('/',methods=['GET','POST'])
def all_books():
    
    response_object = {'status':'success'}
    books = selectDataAll()
    response_object['books']=books
    return jsonify(response_object)
···

一切就绪,我们运行 app.py 就可以在网页中看到数据库中的数据已经被显示出来了:

4、vue框架的使用

        首先按照下方流程配置所需的应用与环境:

        1、我们的电脑中需要 node.js (中文网地址 : http://nodejs.cn/),直接下载安装就OK了( 安装教程 : https://www.runoob.com/nodejs/nodejs-install-setup.html )。

       2、安装cnpm( cmd 输入:npm install -g cnpm --registry=http://registry.npm.taobao.org)

       3、安装 vue 脚手架 ( cmd 输入:npm install -g vue-cli )

       4、安装 webpack( cmd 输入:npm install webpack -g )

       5、找到我们先前创建的vue文件夹,cd 进去,cmd 输入 : vue create 文件名 ,接着会出现一个界面:

第一个选项是 过去保存的配置

第二个是默认配置

第三个是自定义(手动选择要素)

我们选择自定义配置 ,小键盘 上下箭头移动,空格是 选中/反选 。

这里我们全选。

 然后按回车,进入下一个阶段: 

#Ps:注意:最后一条的意思是 :以后的项目是否使用以上配置 (保存配置)

安装完后,我们进入到 vue 根目录下 cmd 中输入 npm run serve 

#Ps:如果失败 请检查根目录下的 package.json 文件内容,以及尝试使用 npm run dev 指令启动

启动成功如下图:


5、跨域问题处理以及代码配置

         到了这一步就差不多接近尾声了,我们通过观察效果图可以得出我们需要的工具有 :

vue:

  • Bootstrap        # 自带样式,使用便捷    ( vue 根目录下 cmd 输入:npm install bootstrap-vue --save)
  • axios               # 用于解决跨域问题        ( vue 根目录下 cmd 输入:npm  install axios )

Flask:

  • CORS             # 用于解决跨域问题        ( cmd 输入:pip3 install flask_cors)

前端安装 axios 后,以 axios.post 这样的方式来处理跨域交互

将以上工具安装完毕后,我们开始最后的工作。


1、修改 flask 文件夹中的 app.py(配置跨域交互) :
 

···
from flask_cors import CORS # 导入CORS 包
···

···
@app.after_request
def after(resp):

    resp = make_response(resp)
    resp.headers['Access-Control-Allow-Origin'] = '*'     # 允许跨域地址
    resp.headers['Access-Control-Allow-Methods'] = '*'    # 请求 ‘*’ 就是全部
    resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type' # 头部
    resp.headers['Access-Control-Allow-Credentials'] = 'True'

    return resp

CORS(app, resources=r'/*', supports_credentials=True)

···

2、在vue文件夹下的 src / components 中创建 Alert.vue 文件 以及 Books.vue 文件

 3、配置 router / index.js(或者是 index.ts)

import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Books from "../components/Books.vue";
import Home from "../components/HelloWorld.vue";
import 'bootstrap/dist/css/bootstrap.css';
import BootstrapVue from 'bootstrap-vue'

Vue.config.productionTip=false
Vue.use(BootstrapVue);
Vue.use(VueRouter);

const routes: Array<RouteConfig> = [
  {
    path: "/home",
    name: "Home",
    component: Home,
  },
  {
    path: "/",                # 将books.vue添加到路由中去
    name: "Books",
    component: Books,
  },
  {
    path: "/about",
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
  },
];

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

export default router;

4、配置 Books.vue 以及 Alert.vue

# Books.vue

<template>
  <div class="container" align="left">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr />
        <br />
         <div>
          <b-alert variant="success" show>{{ message }}</b-alert>
          <br>
        </div>
        <br />
        <button type="button" class="btn btn-success btn-sm">Add Book</button>
        <br />
        <br />
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(book, index) in books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <div class="btn-group" role="group">
                  <button type="button" class="btn btn-warning">Update</button>
                  &nbsp;
                  <button type="button" class="btn btn-danger">Delete</button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>


<script>
import axios from "axios";
import Alert from './Alert.vue'
export default {
  data() {
    return {
      props: ['message'],
      books: [],
      BookForm: {
        delbook:"",
        id: "",
        title: "",
        author: "",
        read: []
      },
      message: '',
    };
  },
  commponents:{
    alert:Alert,
  },
  methods: {

    getBooks() {
      const path="http://127.0.0.1:5000";
      axios
        .get(path)
        .then(res => {
          this.books=res.data.books;
        })
        .catch(error => {
          console.error(error);
        });
    },
    initForm() {
      this.BookForm.delbook='null';
      this.BookForm.id="null";
      this.BookForm.title="";
      this.BookForm.author="";
      this.BookForm.read=[];
    },
  },
  created() {
    this.getBooks();
  }
};
</script>
# Alert.vue

<template><div></div></template>

        在books.vue 中,我们主要是搭建了网页的基础框架,目前只能获取数据库的数据并显示:

        接着,我们开始编写 Add Book 按钮的 代码:

<template>
  <div class="container" align="left">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr />
        <br />
         <div>
          <b-alert variant="success" show>{{ message }}</b-alert>
          <br>
        </div>
        <br />
        <button type="button" class="btn btn-success btn-sm" v-b-modal.book-add>Add Book</button>
        <br />
        <br />
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(book, index) in books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <div class="btn-group" role="group">
                  <button type="button" class="btn btn-warning">Update</button>
                  &nbsp;
                  <button type="button" class="btn btn-danger">Delete</button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
<!-- --------------------------------Add------------------------------ -->
    <!-- 注意,与上面的 Add Book 按钮绑定 -->
    <b-modal ref="addBookModal" id="book-add" title="Add a new book" hide-footer>
      <b-form @submit="add_onSubmit" @reset="add_onReset" class="w-100">
        <b-form-group id="form-title-group" label="Title:" label-for="form-title-input">
          <b-form-input
            id="form-title-input"
            type="text"
            v-model="BookForm.title"
            required
            placeholder="Enter title"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-author-group" label="Author:" label-for="form-author-input">
          <b-form-input
            id="form-author-input"
            type="text"
            v-model="BookForm.author"
            required
            placeholder="Enter author"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-read-group">
          <b-form-checkbox-group v-model="BookForm.read" id="form-checks">
            <b-form-checkbox value="true">Read?</b-form-checkbox>
          </b-form-checkbox-group>
        </b-form-group>
        <b-button-group>
          <b-button type="submit" variant="primary">Submit</b-button>
          <b-button type="reset" variant="danger">Reset</b-button>
        </b-button-group>
      </b-form>
    </b-modal>
  </div>
</template>

        以及对应的脚本代码:

<script>
import axios from "axios";
import Alert from './Alert.vue'
export default {
  data() {
    return {
      props: ['message'],
      books: [],
      BookForm: {
        delbook:"",
        id: "",
        title: "",
        author: "",
        read: []
      },
      message: '',
    };
  },
  commponents:{
    alert:Alert,
  },
  methods: {

    getBooks() {
      const path="http://127.0.0.1:5000";
      axios
        .get(path)
        .then(res => {
          this.books=res.data.books;
        })
        .catch(error => {
          console.error(error);
        });
    },
    initForm() {
      this.BookForm.delbook='null';
      this.BookForm.id="null";
      this.BookForm.title="";
      this.BookForm.author="";
      this.BookForm.read=[];
    },
        // --------------------------Add-----------------------------
    addBook(payload) {
      const path="http://127.0.0.1:5000";
      axios
        .post(path, payload)
        .then(() => {
          this.getBooks();
          this.message = 'Book added!'
        })
        .catch(error => {
          console.log(error);
          this.getBooks();
        });
    },
    add_onSubmit(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      let read=false;
      if(this.BookForm.read[0]) read=true;
      const payload={
        del_id:"null",
        id:"null",
        title: this.BookForm.title,
        author: this.BookForm.author,
        read
      };
      this.addBook(payload);
      this.initForm();
    },
    add_onReset(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      this.initForm();
    },
  },
  created() {
    this.getBooks();
  }
};
</script>

         修改后端代码(app.py)增加对 POST 请求的 辨别与操作:

···

@app.route('/',methods=['GET','POST','OPTIONS'])
def all_books():
    
    response_object = {'status':'success'}
    books = selectDataAll()
    if request.method == 'POST':

        post_data = request.get_json()
        print(post_data)
        books_id = post_data.get('id')
        del_id = post_data.get('del_id')

        title = post_data.get('title'),
        author = post_data.get('author'),
        read = post_data.get('read')

        insertData(
        title=title[0],
        author=author[0],
        read=read
        )
        response_object['message'] = 'Book added!'
    else:
        response_object['books']=books
   
    return jsonify(response_object)

···

        如法炮制,编写 update 按钮点击事件以及 del 事件

附 Books.vue 全部代码:

<template>
  <div class="container" align="left">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr />
        <br />
         <div>
          <b-alert variant="success" show>{{ message }}</b-alert>
          <br>
        </div>
        <br />
        <button type="button" class="btn btn-success btn-sm" v-b-modal.book-add>Add Book</button>
        <br />
        <br />
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(book, index) in books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <div class="btn-group" role="group">
                  <button type="button" class="btn btn-warning" v-b-modal.book-up @click="book_update(index)">Update</button>
                  &nbsp;
                  <button type="button" class="btn btn-danger" @click="book_del(index)">Delete</button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <!-- ------------------------------------------------------Add-------------------------------------------------- -->
    <b-modal ref="addBookModal" id="book-add" title="Add a new book" hide-footer>
      <b-form @submit="add_onSubmit" @reset="add_onReset" class="w-100">
        <b-form-group id="form-title-group" label="Title:" label-for="form-title-input">
          <b-form-input
            id="form-title-input"
            type="text"
            v-model="BookForm.title"
            required
            placeholder="Enter title"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-author-group" label="Author:" label-for="form-author-input">
          <b-form-input
            id="form-author-input"
            type="text"
            v-model="BookForm.author"
            required
            placeholder="Enter author"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-read-group">
          <b-form-checkbox-group v-model="BookForm.read" id="form-checks">
            <b-form-checkbox value="true">Read?</b-form-checkbox>
          </b-form-checkbox-group>
        </b-form-group>
        <b-button-group>
          <b-button type="submit" variant="primary">Submit</b-button>
          <b-button type="reset" variant="danger">Reset</b-button>
        </b-button-group>
      </b-form>
    </b-modal>
    <!-- ----------------------------------------------------Up date------------------------------------------------ -->
    <b-modal ref="upBookModal" id="book-up" title="update book" hide-footer>
      <b-form @submit="up_onSubmit" @reset="up_onReset" class="w-100">
        <b-form-group id="form-title-group" label="Title:" label-for="form-title-input">
          <b-form-input
            id="form-title-input"
            type="text"
            v-model="BookForm.title"
            required
            placeholder="Enter title"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-author-group" label="Author:" label-for="form-author-input">
          <b-form-input
            id="form-author-input"
            type="text"
            v-model="BookForm.author"
            required
            placeholder="Enter author"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-read-group">
          <b-form-checkbox-group v-model="BookForm.read" id="form-checks">
            <b-form-checkbox value="true">Read?</b-form-checkbox>
          </b-form-checkbox-group>
        </b-form-group>
        <b-button-group>
          <b-button type="submit" variant="primary">Submit</b-button>
          <b-button type="reset" variant="danger">Reset</b-button>
        </b-button-group>
      </b-form>
    </b-modal>
    <!-- ------------------------------------------------------del-------------------------------------------------- -->
  
  </div>
</template>


<script>
import axios from "axios";
import Alert from './Alert.vue'
export default {
  data() {
    return {
      props: ['message'],
      books: [],
      BookForm: {
        delbook:"",
        id: "",
        title: "",
        author: "",
        read: []
      },
      message: '',
    };
  },
  commponents:{
    alert:Alert,
  },
  methods: {

    getBooks() {
      const path="http://127.0.0.1:5000";
      axios
        .get(path)
        .then(res => {
          this.books=res.data.books;
        })
        .catch(error => {
          console.error(error);
        });
    },
    initForm() {
      this.BookForm.delbook='null';
      this.BookForm.id="null";
      this.BookForm.title="";
      this.BookForm.author="";
      this.BookForm.read=[];
    },
        // --------------------------Add-----------------------------
    addBook(payload) {
      const path="http://127.0.0.1:5000";
      axios
        .post(path, payload)
        .then(() => {
          this.getBooks();
          this.message = 'Book added!'
        })
        .catch(error => {
          console.log(error);
          this.getBooks();
        });
    },
    add_onSubmit(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      let read=false;
      if(this.BookForm.read[0]) read=true;
      const payload={
        del_id:"null",
        id:"null",
        title: this.BookForm.title,
        author: this.BookForm.author,
        read
      };
      this.addBook(payload);
      this.initForm();
    },
    add_onReset(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      this.initForm();
    },
    // ------------------------up date-----------------------
    book_update:function(index){
      this.BookForm.id=index+1
    },
    upBook(payload) {
      const path="http://127.0.0.1:5000";
      axios
        .post(path, payload)
        .then(() => {
          this.getBooks();
          this.message = 'Book update!'
        })
        .catch(error => {
          console.log(error);
          this.getBooks();
        });
    },
    up_onSubmit(evt) {
      evt.preventDefault();
      this.$refs.upBookModal.hide();
      let read=false;
      if(this.BookForm.read[0]) read=true;
      const payload={
        id: this.BookForm.id,
        del_id: "null",
        title: this.BookForm.title,
        author: this.BookForm.author,
        read
      };
      this.upBook(payload);
      this.initForm();
    },
    up_onReset(evt) {
      evt.preventDefault();
      this.$refs.upBookModal.hide();
      this.initForm();
    },
    // --------------------------del-------------------------
    book_del:function(index){
      this.BookForm.delbook=index+1
      let read=false;
      if(this.BookForm.read[0]) read=true;
      const payload={
        id:"null",
        del_id: this.BookForm.delbook,
        title: this.BookForm.title,
        author: this.BookForm.author,
        read
      };
      this.delBook(payload);
      this.initForm();
    },
    delBook(payload) {
      const path="http://127.0.0.1:5000";
      axios
        .post(path, payload)
        .then(() => {
          this.getBooks();
          this.message = 'Book del!'
        })
        .catch(error => {
          console.log(error);
          this.getBooks();
        });
    },
  },
  created() {
    this.getBooks();
  }
};
</script>

        附后端代码:

from flask import Flask, jsonify, request, make_response
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS


app = Flask(__name__)
@app.after_request
def after(resp):

    resp = make_response(resp)
    resp.headers['Access-Control-Allow-Origin'] = '*'
    resp.headers['Access-Control-Allow-Methods'] = '*'
    resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
    resp.headers['Access-Control-Allow-Credentials'] = 'True'

    return resp


app.config['DEBUG'] = True
CORS(app, resources=r'/*', supports_credentials=True)

# ------------------database----------------------------
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
class Books(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80), nullable=False)
    author = db.Column(db.String(120), nullable=False)
    read= db.Column(db.Boolean)
db.create_all()

# 增
def insertData(title,author,read):
    book=Books(title=title,author=author,read=read)
    db.session.add_all([book])
    db.session.commit()



# 查
def selectDataAll():
    book_list=[]
    books = Books.query.all()

    for s in books:
        dic = {}
        dic['id'] = s.id
        dic['title'] = s.title
        dic['author'] = s.author
        dic['read'] = s.read
        print(f'Id: {s.id} | Title: {s.title} | Author: {s.author} | Read: {s.read}')

        print('----')
        book_list.append(dic)
    return book_list

# 删
def deleteData(id):
    book = Books.query.get(id)
    db.session.delete(book)
    db.session.commit()

# 改
def updateData(id, title='', author='', read='', new_id=''):

    book = Books.query.get(id)
    if not title=='':
        book.title = title
    if not author == '':
        book.author = author
    if not read == '':
        book.read = read
    if not new_id == '':
        book.id=new_id
    db.session.commit()




# ------------------database---end----------------------





@app.route('/',methods=['GET','POST','OPTIONS'])
def all_books():
    
    response_object = {'status':'success'}
    books = selectDataAll()
    if request.method == 'POST':

        post_data = request.get_json()
        print(post_data)
        books_id = post_data.get('id')
        del_id = post_data.get('del_id')
        if books_id == 'null' and del_id == 'null':
            title = post_data.get('title'),
            author = post_data.get('author'),
            read = post_data.get('read')


            insertData(
                title=title[0],
                author=author[0],
                read=read
            )
            response_object['message'] = 'Book added!'
        elif (not books_id == 'null') and del_id == 'null':
            put_data = post_data
            books_id = put_data.get('id')
            title = put_data.get('title')
            author = put_data.get('author')
            read = put_data.get('read')
            updateData(
                id=books_id,
                title=title,
                author=author,
                read=read
            )
            response_object['message'] = 'Book update!'
        else:
            deleteData(del_id)
            response_object['message'] = 'Book del!'
    else:
        response_object['books']=books
    books = selectDataAll()
    for i in range(len(books)):
        id=books[i]['id']
        updateData(id=id,new_id=i+1)
    print(response_object)
    return jsonify(response_object)



if __name__ == '__main__':
    app.run(port=5000,debug=True)

至此结束,码字辛苦,还请各位哥哥姐姐留个赞再走呗,先谢谢了嗷~!

Logo

前往低代码交流专区

更多推荐