溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Django之入門 CMDB系統(tǒng) (五) 前后端分離之前端

發(fā)布時(shí)間:2020-07-04 13:36:58 來源:網(wǎng)絡(luò) 閱讀:753 作者:295631788 欄目:系統(tǒng)運(yùn)維

Django之入門 CMDB系統(tǒng) (五) 前后端分離之前端


前言

作者: 何全,github地址: https://github.com/hequan2017 QQ交流群: 620176501

通過此教程完成從零入門,能夠獨(dú)立編寫一個(gè)簡單的CMDB系統(tǒng)。

目前主流的方法開發(fā)方式,分為2種:mvc 和 mvvc方式。本教程為 mvvc(前后端分離)的入門教程。

教程項(xiàng)目地址: https://github.com/hequan2017/panda/

教程文檔地址: https://github.com/hequan2017/pandaAdmin

說明

  • 架構(gòu)框架選擇
    • vue (入門簡單,自學(xué)可以跟著官網(wǎng)例子入門)
    • d2Admin (基于element,樣式外觀好看,例子較多)
  • 開發(fā)工具
    • vs code

相較于react, vue入門更簡單,基于模板語法,適合從mvc方式 循序漸進(jìn)到 mvvc方式。

模板選擇為 基于element的模板. iview現(xiàn)在已經(jīng)轉(zhuǎn)為收費(fèi)方式,不太適合個(gè)人開發(fā)者練習(xí)使用。antd vue版本 沒有合適的 admin模板,所以放棄了.

vue-element-admin 和 d2admin 差不多,資料文檔都非常全。看個(gè)人選擇。

demo

Django之入門 CMDB系統(tǒng) (五) 前后端分離之前端

框架文檔

  • vue

    • https://cn.vuejs.org/v2/guide/
    • axios ajax 發(fā)送請(qǐng)求 獲取信息
    • store 獲取到的信息保存
    • router 路由跳轉(zhuǎn)
  • https://fairyever.com/d2-admin/doc/zh/ #具體模板詳細(xì) 可以 看這個(gè)文檔,非常詳細(xì)

  • 下載基礎(chǔ)模板

git clone https://github.com/d2-projects/d2-admin-start-kit

├─.DS_Store 
├─.browserslistrc ------------------- // 目標(biāo)瀏覽器配置
├─.env ------------------------------ // 基礎(chǔ)環(huán)境變量配置
├─.env.development ------------------ // 開發(fā)模式下的環(huán)境變量配置
├─.env.netlify ---------------------- // Netlify 構(gòu)建時(shí)的環(huán)境變量
├─.env.nomock ----------------------- // 無 mock 數(shù)據(jù)構(gòu)建模式下的環(huán)境變量
├─.env.travis ----------------------- // Travis 構(gòu)建時(shí)的環(huán)境變量
├─.eslintignore --------------------- // ESLint 的忽略目錄配置
├─.eslintrc.js ---------------------- // ESLint 的配置文件
├─.github --------------------------- // Github 配置
│?└─ISSUE_TEMPLATE ------------------ // GitHub issue 模板
├─.gitignore ------------------------ // git 的忽略配置
├─.postc***c.js --------------------- // postcss 插件設(shè)置
├─.travis.yml ----------------------- // Travis 配置文件
├─LICENSE --------------------------- // 開源協(xié)議
├─README.md ------------------------- // 介紹
├─README.zh.md ---------------------- // 中文介紹 在碼云倉庫下自動(dòng)加載這個(gè)文件
├─babel.config.js ------------------- // babel 設(shè)置
├─cdnrefresh-dirs.txt --------------- // 自動(dòng)構(gòu)建后在七牛 CDN 上刷新的目錄
├─d2-admin.babel -------------------- // 多國語設(shè)置軟件 BabelEdit 的項(xiàng)目文件
├─doc ------------------------------- // 文檔素材
│?└─image 
├─jest.config.js -------------------- // 單元測試配置
├─jsconfig.json --------------------- // 指定根文件和 JavaScript 語言服務(wù)提供的功能選項(xiàng)
├─package-lock.json ----------------- // 鎖定依賴版本
├─package.json ---------------------- // 項(xiàng)目信息和依賴
├─public ---------------------------- // 靜態(tài)資源文件夾,不經(jīng)過 webpack 處理
│?├─html ---------------------------- // 用于演示加載靜態(tài)頁面的資源
│?├─icon.ico ------------------------ // 網(wǎng)站圖標(biāo)
│?├─image 
│?│?├─baidu-pan-logo.png 
│?│?├─loading ----------------------- // index.html 使用的加載圖標(biāo)
│?│?└─theme ------------------------- // 主題圖片資源
│?│?  ├─d2 
│?│?  ├─line 
│?│?  ├─star 
│?│?  ├─tomorrow-night-blue 
│?│?  └─violet 
│?├─index.html ---------------------- // 網(wǎng)站的基礎(chǔ)頁面模板
│?├─lib ----------------------------- // 靜態(tài)依賴
│?│?└─UEditor ----------------------- // 編輯器
│?└─markdown ------------------------ // 用于展示 Markdown 遠(yuǎn)程加載資源的文件
├─qiniu-config ---------------------- // 用于構(gòu)建展示網(wǎng)站的七牛設(shè)置
├─qshell ---------------------------- // 七牛 SDK
├─src ------------------------------- // 主要的代碼目錄
│?├─App.vue ------------------------- // 項(xiàng)目根組件
│?├─api ----------------------------- // 請(qǐng)求接口
│?├─assets -------------------------- // 資源文件夾
│?│?├─style ------------------------- // 樣式資源
│?│?│?├─animate --------------------- // 頁面過渡動(dòng)畫
│?│?│?├─fixed ----------------------- // 覆蓋一些組件庫的默認(rèn)樣式
│?│?│?├─public-class.scss ----------- // 導(dǎo)出可以直接使用的 class
│?│?│?├─public.scss ----------------- // 導(dǎo)出所有公用的 scss 資源
│?│?│?├─theme ----------------------- // 主題樣式相關(guān)
│?│?│?│?├─d2 
│?│?│?│?├─line 
│?│?│?│?├─register.scss ------------- // 注冊(cè)所有主題樣式
│?│?│?│?├─star 
│?│?│?│?├─theme-base.scss ----------- // 每個(gè)主題共用的樣式
│?│?│?│?├─theme.scss ---------------- // 每個(gè)主題特有的設(shè)置
│?│?│?│?├─tomorrow-night-blue 
│?│?│?│?└─violet 
│?│?│?└─unit ------------------------ // scss 的基礎(chǔ)變量
│?│?└─svg-icons --------------------- // svg 圖標(biāo)
│?│?  ├─icons ----------------------- // 用來存放圖標(biāo)的文件夾
│?│?  └─index.js -------------------- // 自動(dòng)導(dǎo)入所有符合條件的圖標(biāo)
│?├─components ---------------------- // 組件
│?│?├─d2-container 
│?│?├─d2-container-frame 
│?│?├─d2-count-up 
│?│?├─d2-highlight 
│?│?├─d2-icon 
│?│?├─d2-icon-select 
│?│?├─d2-icon-svg 
│?│?├─d2-icon-svg-select 
│?│?├─d2-link-btn 
│?│?├─d2-markdown 
│?│?├─d2-mde 
│?│?├─d2-module-index-banner 
│?│?├─d2-module-index-menu 
│?│?├─d2-quill 
│?│?├─d2-ueditor 
│?│?├─highlight-styles -------------- // 代碼高亮樣式
│?│?└─index.js ---------------------- // 組件注冊(cè)
│?├─i18n.js ------------------------- // 國際化配置
│?├─layout -------------------------- // 布局
│?│?└─header-aside ------------------ // 具有頂欄加側(cè)邊欄的布局
│?├─libs ---------------------------- // 一些通用的方法
│?│?├─util.cookies.js 
│?│?├─util.db.js 
│?│?├─util.import.development.js ---- // 開發(fā)環(huán)境下使用的頁面導(dǎo)入方法
│?│?├─util.import.production.js ----- // 開發(fā)環(huán)境下使用的頁面導(dǎo)入方法
│?│?├─util.js 
│?│?└─util.log.js 
│?├─locales ------------------------- // 國際化語言配置
│?│?├─en.json 
│?│?├─ja.json 
│?│?├─zh-chs.json 
│?│?└─zh-cht.json 
│?├─main.js ------------------------- // 程序主入口
│?├─menu ---------------------------- // 靜態(tài)的菜單數(shù)據(jù)
│?│?├─index.js 
│?│?└─modules 
│?│?  ├─demo-business.js 
│?│?  ├─demo-charts.js 
│?│?  ├─demo-components.js 
│?│?  ├─demo-d2-crud.js 
│?│?  ├─demo-element.js 
│?│?  ├─demo-frame.js 
│?│?  ├─demo-playground.js 
│?│?  └─demo-plugins.js 
│?├─mock 
│?│?├─api --------------------------- // 需要注冊(cè)的接口
│?│?├─d2-mock ----------------------- // 簡化接口注冊(cè)的工具
│?│?└─index.js ---------------------- // mock 數(shù)據(jù)自動(dòng)注冊(cè)
│?├─plugin -------------------------- // 插件
│?│?├─axios ------------------------- // 網(wǎng)絡(luò)請(qǐng)求
│?│?├─d2admin ----------------------- // 統(tǒng)一注冊(cè)系統(tǒng)必須的資源
│?│?├─error ------------------------- // 錯(cuò)誤攔截
│?│?├─log --------------------------- // 日志
│?│?└─open -------------------------- // 新窗口打開
│?├─router -------------------------- // 路由
│?│?├─index.js ---------------------- // 注冊(cè)路由以及設(shè)置攔截規(guī)則
│?│?├─modules ----------------------- // 預(yù)先設(shè)置好的靜態(tài)路由
│?│?│?├─business.js 
│?│?│?├─charts.js 
│?│?│?├─components.js 
│?│?│?├─d2-crud.js 
│?│?│?├─element.js 
│?│?│?├─frame.js 
│?│?│?├─playground.js 
│?│?│?└─plugins.js 
│?│?└─routes.js --------------------- // 導(dǎo)入所有路由
│?├─setting.js ---------------------- // 全局設(shè)置
│?├─store --------------------------- // vuex
│?│?├─index.js ---------------------- // vuex 注冊(cè)主入口
│?│?└─modules ----------------------- // 模塊目錄
│?│?  └─d2admin --------------------- // 系統(tǒng)自帶模塊,業(yè)務(wù)模塊建議在同級(jí)新建
│?│?    ├─index.js ------------------ // 模塊主入口
│?│?    └─modules ------------------- // 子模塊
│?│?      ├─account.js -------------- // 用戶身份
│?│?      ├─color.js ---------------- // 主題顏色
│?│?      ├─db.js ------------------- // 本地?cái)?shù)據(jù)庫
│?│?      ├─fullscreen.js ----------- // 全屏
│?│?      ├─gray.js ----------------- // 灰度模式
│?│?      ├─log.js ------------------ // 日志
│?│?      ├─menu.js ----------------- // 菜單
│?│?      ├─page.js ----------------- // 多頁面控制
│?│?      ├─releases.js ------------- // 版本
│?│?      ├─search.js --------------- // 全局搜索
│?│?      ├─size.js ----------------- // 全局尺寸
│?│?      ├─theme.js ---------------- // 主題
│?│?      ├─transition.js ----------- // 過渡效果
│?│?      ├─ua.js ------------------- // 瀏覽器信息
│?│?      └─user.js ----------------- // 用戶信息
│?└─views --------------------------- // 頁面視圖
│?  ├─demo -------------------------- // 演示頁面
│?  │?├─business -------------------- // 業(yè)務(wù)頁面演示
│?  │?│?├─index 
│?  │?│?├─issues 
│?  │?│?└─table 
│?  │?├─charts ---------------------- // 圖表
│?  │?│?├─index 
│?  │?│?└─list 
│?  │?│?  ├─_data 
│?  │?│?  ├─_mixin 
│?  │?│?  ├─bar 
│?  │?│?  ├─candle 
│?  │?│?  ├─funnel 
│?  │?│?  ├─gauge 
│?  │?│?  ├─heatmap 
│?  │?│?  ├─histogram 
│?  │?│?  ├─line 
│?  │?│?  ├─map 
│?  │?│?  ├─pie 
│?  │?│?  ├─radar 
│?  │?│?  ├─ring 
│?  │?│?  ├─sankey 
│?  │?│?  ├─scatter 
│?  │?│?  ├─tree 
│?  │?│?  └─waterfall 
│?  │?├─components ------------------ // 內(nèi)置組件演示
│?  │?│?├─container 
│?  │?│?├─contextmenu 
│?  │?│?├─countup 
│?  │?│?├─editor-quill 
│?  │?│?├─editor-simpleMDE 
│?  │?│?├─editor-ueditor 
│?  │?│?├─highlight 
│?  │?│?├─icon 
│?  │?│?├─index 
│?  │?│?├─json-tree 
│?  │?│?├─layout 
│?  │?│?└─markdown 
│?  │?├─d2-crud --------------------- // D2CRUD 表格封裝演示
│?  │?├─element --------------------- // Element UI 組件
│?  │?├─frame ----------------------- // 嵌套第三方頁面演示
│?  │?├─playground ------------------ // 試驗(yàn)臺(tái)
│?  │?│?├─add-routes ---------------- // 動(dòng)態(tài)添加路由
│?  │?│?├─db ------------------------ // 數(shù)據(jù)持久化
│?  │?│?├─env ----------------------- // 環(huán)境變量
│?  │?│?├─index 
│?  │?│?├─locales ------------------- // 多國語
│?  │?│?├─log ----------------------- // 日志
│?  │?│?├─page-argu ----------------- // 頁面參數(shù)
│?  │?│?├─page-cache ---------------- // 頁面緩存
│?  │?│?└─store --------------------- // 全局狀態(tài)控制
│?  │?│?  ├─fullscreen 
│?  │?│?  ├─gray 
│?  │?│?  ├─menu 
│?  │?│?  ├─page 
│?  │?│?  ├─size 
│?  │?│?  ├─theme 
│?  │?│?  ├─transition 
│?  │?│?  └─ua 
│?  │?└─plugins --------------------- // 插件演示
│?  │?  ├─better-scroll 
│?  │?  ├─clipboard-polyfill 
│?  │?  ├─day 
│?  │?  ├─export 
│?  │?  ├─import 
│?  │?  ├─index 
│?  │?  ├─js-cookie 
│?  │?  └─mock 
│?  └─system ------------------------ // 系統(tǒng)頁面
│?    ├─error 
│?    ├─function 
│?    │?├─redirect ------------------ // 重定向
│?    │?└─refresh ------------------- // 刷新
│?    ├─index 
│?    ├─log 
│?    └─login 
├─tests ----------------------------- // 單元測試
├─tools 
│?└─vue-filename-injector ----------- // 用于對(duì)每個(gè)組件注入源代碼位置的插件
└─vue.config.js --------------------- // vue-cli3 的項(xiàng)目配置文件

登錄部分

  • src/modules/d2admin/modules/account.js #主要為 前端登錄信息 狀態(tài) 保存和讀取
try {
      AccountLoginInfo(res.token)
        .then(resinfo => {
          const data = resinfo.data    /*  獲取后端用戶信息*/
          console.log(resinfo)
          dispatch(               
            'd2admin/user/set',
            {
              name: resinfo.name
            },
            { root: true }
          )
          resolve(data)
        })
        .catch(err => {
          reject(err)
        })
    } catch (error) {
      reject(error)
    }
  • src/api/sys.login.js # 設(shè)置獲取 token 和 info
import request from '@/plugin/axios'

export function AccountLogin (data) {
  return request({
    url: '/api/token',
    method: 'post',
    data
  })
}
export const AccountLoginInfo = token => {
  return request({
    url: '/system/api/user_info',
    data: {
      token
    },
    method: 'post'
  })
}
  • 登錄token攔截設(shè)置 src\plugin\axios\index.js
// 請(qǐng)求攔截器
service.interceptors.request.use(
  config => {
    // 在請(qǐng)求發(fā)送之前做一些處理
    const token = util.cookies.get('token')
    // 讓每個(gè)請(qǐng)求攜帶token-- ['X-Token']為自定義key 請(qǐng)根據(jù)實(shí)際情況自行修改
    // config.headers[''] = token
    if (token) {
      config.headers['Authorization'] = `token ${token}`
    }
    return config
  },
  error => {
    // 發(fā)送失敗
    console.log(error)
    return Promise.reject(error)
  }
)
  • 用戶信息調(diào)用例子
import { mapState, mapActions } from 'vuex'
export default {
  computed: {
    ...mapState('d2admin/user', [
      'info'
    ])
  },

/*  info 為 上面保存的信息,這里可以在用戶登錄后 直接讀取*/
{{info.name ? `你好 ${info.name}` : '未登錄'}}

增刪改查例子

  • 路由

  • src/api/sys.login.js
import request from '@/plugin/axios'

export const TestGetList = parameter => {
  return request({
    url: `/system/test?${parameter}`,
    method: 'get'
  })
}

export const TestCreate = data => {
  return request({
    url: '/system/test',
    data: data,
    method: 'post'
  })
}

export const TestGetInfo = id => {
  return request({
    url: `/system/test/${id}`,
    method: 'get'
  })
}

export const TestUpdate = (id, data) => {
  return request({
    url: `/system/test/${id}`,
    data: data,
    method: 'PUT'
  })
}

export const TestDelete = id => {
  return request({
    url: `/system/test/${id}`,
    method: 'delete'
  })
}
  • src/asset/index.vue
<template>
  <d2-container>
    <template slot="header">演示頁面</template>
    <el-input v-model="input"
              placeholder="請(qǐng)輸入姓名"
              
              prefix-icon="el-icon-search"
              clearable></el-input>?
    <el-button type="primary"
               icon="el-icon-search"
               @click="search">搜索</el-button>
    <br /><br />
    <d2-crud ref="d2Crud"
             :columns="columns"
             :data="data"
             add-title="資產(chǎn)管理"
             :add-template="addTemplate"
             :form-options="formOptions"
             :add-rules="addRules"
             :edit-rules="addRules"
             :edit-template="editTemplate"
             :rowHandle="rowHandle"
             @row-add="handleRowAdd"
             @row-edit="handleRowEdit"
             @row-remove="handleRowRemove"
             :pagination="pagination"
             @dialog-cancel="handleDialogCancel"
             @el-icon-more="handleInfo"
             @pagination-current-change="paginationCurrentChange">
      <el-button slot="header"
                 
                 @click="addRow">新增</el-button>
    </d2-crud>
  </d2-container>
</template>

<script>

import { TestGetList, TestCreate, TestUpdate, TestDelete, TestGetInfo } from '@api/sys.login'
export default {
  data () {
    return {
      columns: [
        {
          title: '日期',
          key: 'date'
        },
        {
          title: '姓名',
          key: 'name'
        },
        {
          title: '地址',
          key: 'address'
        }
      ],
      data: [
      ],
      input: '',
      addTemplate: {
        date: {
          title: '日期',
          value: '2016-05-05'
        },
        name: {
          title: '姓名',
          value: '王小虎'
        },
        address: {
          title: '地址',
          value: '上海市普陀區(qū)金沙江路 1520 弄'
        }
      },
      formOptions: {
        labelWidth: '80px',
        labelPosition: 'left',
        saveLoading: false
      },
      pagination: {
        currentPage: 1,
        pageSize: 10,
        total: 0
      },
      addRules: {
        date: [{ required: true, message: '請(qǐng)輸入日期', trigger: 'blur' }],
        name: [{ required: true, message: '請(qǐng)輸入姓名', trigger: 'blur' }],
        address: [{ required: true, message: '請(qǐng)輸入地址', trigger: 'blur' }]
      },
      rowHandle: {
        columnHeader: '編輯表格',
        edit: {
          icon: 'el-icon-edit',
          type: 'primary',
          text: '編輯',
          size: 'small',
          show (index, row) {
            return true
          },
          disabled (index, row) {
            return false
          }
        },
        custom: [
          {
            text: '詳情',
            type: 'info',
            size: 'small',
            fixed: 'right',
            emit: 'el-icon-more'
          }
        ],
        remove: {
          icon: 'el-icon-delete',
          size: 'small',
          fixed: 'right',
          confirm: true,
          show (index, row) {
            return true
          },
          disabled (index, row) {
            return false
          }
        }
      },
      editTemplate: {
        date: {
          title: '日期',
          value: ''
        },
        name: {
          title: '姓名',
          value: ''
        },
        address: {
          title: '地址',
          value: ''
        },
        forbidEdit: {
          title: '禁用按鈕',
          value: false,
          component: {
            show: false
          }
        },
        showEditButton: {
          title: '顯示按鈕',
          value: true,
          component: {
            show: false
          }
        }
      }
    }
  },
  created () {
    this.test_get_list()
  },
  methods: {
    handleInfo ({ index, row }) {
      TestGetInfo(row.id).then(res => {
        this.$alert(`${res.date} ${res.address}`, `${res.name}`, {
          confirmButtonText: '確定'
        }).catch(err => {
          console.log(`獲取信息錯(cuò)誤 ${err}`)
        })
      })
    },
    test_get_list (parameter) {
      TestGetList(parameter).then(res => {
        console.log(res)
        this.data = res.results
        this.pagination.total = res.count
      }).catch(err => {
        console.log(`獲取信息錯(cuò)誤 ${err}`)
      })
    },
    paginationCurrentChange (currentPage) {
      TestGetList(`page=${currentPage}`).then(res => {
        console.log(res)
        this.data = res.results
        this.pagination.total = res.count
      }).catch(err => {
        console.log(`獲取信息錯(cuò)誤 ${err}`)
      })
      this.pagination.currentPage = currentPage
      // this.fetchData()
    },
    addRow () {
      this.$refs.d2Crud.showDialog({
        mode: 'add'
      })
    },
    search () {
      this.test_get_list(`name=${this.input}`)
    },
    handleRowAdd (row, done) {
      this.formOptions.saveLoading = true
      TestCreate(row).then(res => {
      }).catch(err => {
        console.log(err)
      })
      setTimeout(() => {
        this.$message({
          message: '保存成功',
          type: 'success'
        })
        done()
        this.formOptions.saveLoading = false
        this.test_get_list()
      }, 300)
    },
    handleDialogCancel (done) {
      this.$message({
        message: '取消',
        type: 'warning'
      })
      done()
    },
    handleRowEdit ({ index, row }, done) {
      this.formOptions.saveLoading = true
      this.addTemplate.data = row.data
      this.addTemplate.name = row.name
      this.addTemplate.address = row.address
      TestUpdate(row.id, row).then(res => {
      }).catch(err => {
        console.log(err)
      })
      setTimeout(() => {
        this.$message({
          message: '編輯成功',
          type: 'success'
        })
        done()
        this.formOptions.saveLoading = false
        this.test_get_list()
      }, 300)
    },
    handleRowRemove ({ index, row }, done) {
      TestDelete(row.id).then(res => {
      }).catch(err => {
        console.log(err)
      })
      setTimeout(() => {
        console.log(index)
        console.log(row)
        this.$message({
          message: '刪除成功',
          type: 'success'
        })
        done()
        this.test_get_list()
      }, 300)
    }
  }
}
</script>
  • src/router/routers.js

const frameIn = [
  {
    path: '/',
    redirect: { name: 'index' },
    component: layoutHeaderAside,
    children: [
      // 首頁
      {
        path: 'index',
        name: 'index',
        meta: {
          auth: true
        },
        component: _import('system/index')
      },
      // 演示頁面
      {
        path: 'asset',
        name: 'asset',
        meta: {
          title: '演示頁面',
          auth: true
        },
        component: _import('asset/index')
      },
      {
        path: 'log',
        name: 'log',
        meta: {
          title: '前端日志',
          auth: true
        },
        component: _import('system/log')
      },
      // 刷新頁面 必須保留
      {
        path: 'refresh',
        name: 'refresh',
        hidden: true,
        component: _import('system/function/refresh')
      },
      // 頁面重定向 必須保留
      {
        path: 'redirect/:route*',
        name: 'redirect',
        hidden: true,
        component: _import('system/function/redirect')
      }
    ]
  }
]

部署

npm install

npm run dev

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI