成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

關(guān)于前后端分離權(quán)限控制,元素細(xì)粒度(iview-admin實(shí)現(xiàn))

YorkChen / 3646人閱讀

摘要:按鈕方面按鈕通過自定義指令綁定其特定的操作接口信息如產(chǎn)品上傳按鈕,需要擁有產(chǎn)品上傳的信息,才可以繼續(xù)執(zhí)行按鈕的業(yè)務(wù)邏輯。

開篇啰嗦幾句

在傳統(tǒng)單體項目中,通常會有一些框架用來管理熟知的權(quán)限。如耳濡目染的 Shiro 或者 Spring Security 。然而,到了現(xiàn)在這個時代,新開始的項目會更多的才用后端微服務(wù) + 前端 mvvm 的架構(gòu)開始書寫項目。權(quán)限控制方面將變得有些許晦澀。當(dāng)然,得益于 Java 后端的 Spring 項目的可插拔式接口以及前端大部分 SPA 的架構(gòu),權(quán)限控制也并沒有變得那么的難以實(shí)現(xiàn)。相反,在我看來只要找到一個合適的插拔插件的入口,權(quán)限也可以做的很簡單,跟業(yè)務(wù)只要少許的耦合即可實(shí)現(xiàn)我們熟悉的權(quán)限模塊。

示例代碼 iview-admin-permission

實(shí)現(xiàn)思路 路由方面

后端提供當(dāng)前登錄用戶可訪問的所有可訪問資源接口信息,前端項目根據(jù)當(dāng)前用戶所擁有的接口信息,判斷是否擁有菜單權(quán)限,如果擁有主要訪問權(quán)限則加入路由,讓用戶可以訪問其當(dāng)前的頁面。

由于 iview-admin 中,路由信息與菜單是同一份資料,所以在加入路由的時候,即可實(shí)現(xiàn)菜單的隱藏與現(xiàn)實(shí)。

按鈕方面

按鈕通過 vue 自定義指令綁定其特定的操作接口信息(如:產(chǎn)品上傳按鈕,需要擁有產(chǎn)品上傳的信息,才可以繼續(xù)執(zhí)行按鈕的業(yè)務(wù)邏輯)。在按鈕或者 a 標(biāo)簽被渲染的時候,判斷是否擁有權(quán)限,如若沒有,可以隱藏,或者劫持原來綁定的點(diǎn)擊事件,使其變成無權(quán)限的彈窗提示。

該指令支持 render 生成的按鈕或者 a 標(biāo)簽。

數(shù)據(jù)表格方面

數(shù)據(jù)的隱藏與否應(yīng)該不會放在前端做吧...不然就是沙雕網(wǎng)友了。

這方面可以提供思路,在微服務(wù)架構(gòu)中,如若前端項目擁有 NodeJS 做數(shù)據(jù)轉(zhuǎn)換層,那是極好的。如果沒有,則需要在微服務(wù)路由層做一些細(xì)微的改變。即用戶無權(quán)限的數(shù)據(jù)列(屬性)給他模糊掉,變成 * 或者 輸出。這方面與前端無關(guān),那么并不會在這里出現(xiàn)。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

后端不再關(guān)注頁面上的事情,基于權(quán)限代碼讓前端自己隨心所欲編排

靈活性較高,可以控制頁面上所有的元素,可以隱藏彈窗等等喜歡的功能

缺點(diǎn):

編排路由的時候會顯得繁瑣,權(quán)限代碼比較零散,開發(fā)完業(yè)務(wù)頁面可能會忘記加上

代碼實(shí)現(xiàn)

公司實(shí)現(xiàn)并不能成為開源,所以我重新下載一份 iview-admin ,然后加入關(guān)鍵代碼,來實(shí)現(xiàn)上面所需要的功能。

常規(guī)操作:

npm install
npm run dev

正常打開窗口即可進(jìn)行修改。

接下來就小步前進(jìn)加入權(quán)限的功能。

新增權(quán)限模塊的store

權(quán)限提供了一個用戶所擁有的所有權(quán)限列表以及一個 getters 后面會用到。

export default {
  state: {
    // { operatorCode: "ProductEndpoint#upload", name: "產(chǎn)品上傳" }
    operatorList: []
  },
  getters: {
    hasPermission: (state) => (queryOpcode) => {
      if (!state.operatorList || !state.operatorList.length) {
        return false
      }
      return state.operatorList.map(operatInfo => operatInfo.operatorCode).indexOf(queryOpcode) > -1
    }
  },
  mutations: {
    setPermissionList (state, opList) {
      state.operatorList = opList
    }
  }
}

在這里我定義簡單的后端返回值:一個 operatorCode 代表某個操作的代號,一個 name 用于分配權(quán)限的時候給用戶查看。

operatorCode 有很多中生成方式,我這里采用 SpringMVCController 名 + # + 方法名 的形式, Controller 名用于分割不同資源的資源空間。使用 Spring 的生命周期函數(shù),在項目啟動的時候掃描,寫入數(shù)據(jù)庫,相當(dāng)于所有的資源。后面的權(quán)限模型,以前該怎樣就還是怎樣,比如用戶 —> 角色 —> 資源的模型。然后根據(jù)登陸的用戶 ID 查詢,返回給前端。至于中文名字,我們項目搭配 Swagger 使用,如果沒有使用 Swagger 則需要使用自定義開發(fā)的注解指定。

注冊到 store 中:

import Vue from "vue"
import Vuex from "vuex"

import user from "./module/user"
import app from "./module/app"
import permission from "./module/permission"

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    //
  },
  mutations: {
    //
  },
  actions: {
    //
  },
  modules: {
    user,
    app,
    permission
  }
})
用戶登陸時初始化擁有的資源操作

靜態(tài)操作數(shù)據(jù):

// src/mock/data/permission-data.js
export const getPermissionCodeList = (userName) => {
  if (String(userName) === "1") {
    return [{ operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#putawayProduct", name: "產(chǎn)品上架" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" }]
  } else {
    return [{ operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" },
      { operatorCode: "ProductManageEndpoint#deleteByUuid", name: "產(chǎn)品刪除" }]
  }
}

登陸操作:

// src/view/login/login.vue
...
handleSubmit ({ userName, password }) {
    this.handleLogin({ userName, password }).then(res => {
        this.getUserInfo().then(res => {
            // 用戶登陸成功時候加入操作Code
            this.$store.commit("setPermissionList", getPermissionCodeList(userName))
            this.$router.push({
                name: this.$config.homeName
            })
        })
    })
}

加入用戶列表的信息,不然不能登錄,因為作者默認(rèn)只放了兩個,當(dāng)然生產(chǎn)項目肯定不管這里啦~~

// src/mock/login.js
const USER_MAP = {
  super_admin: {
    name: "super_admin",
    user_id: "1",
    access: ["super_admin", "admin"],
    token: "super_admin",
    avator: "https://file.iviewui.com/dist/a0e88e83800f138b94d2414621bd9704.png"
  },
  admin: {
    name: "admin",
    user_id: "2",
    access: ["admin"],
    token: "admin",
    avator: "https://avatars0.githubusercontent.com/u/20942571?s=460&v=4"
  },
  zhangsan: {
    name: "zhangsan",
    user_id: "3",
    access: ["super_admin", "admin"],
    token: "zhangsan",
    avator: "https://avatars0.githubusercontent.com/u/20942571?s=460&v=4"
  },
  lisi: {
    name: "lisi",
    user_id: "4",
    access: ["super_admin", "admin"],
    token: "lisi",
    avator: "https://avatars0.githubusercontent.com/u/20942571?s=460&v=4"
  }
}
...

加入權(quán)限數(shù)據(jù):

// src/mock/data/permission-data.js
export const getPermissionCodeList = (userName) => {
  if (userName === "zhangsan") {
    return [{ operatorCode: "ProductManageEndpoint#list", name: "產(chǎn)品列表" },
      { operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#putawayProduct", name: "產(chǎn)品上架" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" }]
  } else {
    return [{ operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" },
      { operatorCode: "ProductManageEndpoint#deleteByUuid", name: "產(chǎn)品刪除" }]
  }
}
開始加載菜單

剛開始我們先設(shè)立一個口進(jìn)入,由于項目的 menuList 掛載在 storeapp.js 里面,所以我們需要在這里動刀。

這里修改造成兩個事情:1. 清掉項目原來的 router 的菜單,但是還可以訪問(因為內(nèi)存加載了);2. 我加入了需要權(quán)限管理的菜單,但是還不能訪問,因為路由還沒有。

// src/store/module/app.js
...
getters: {
    menuList: (state, getters, rootState) => {
      const allMenus = []
      const menuWithPermission = getMenuWithPermissionByRouter(routersWithPermission, rootState.permission.operatorList)
      allMenus.push(...menuWithPermission)
      return allMenus
    }
}
...

那么這里用到一個函數(shù) getMenuWithPermissionByRouter,我決定放在 util.js 即可

// src/libs/util.js
...
export const getMenuWithPermissionByRouter = (routerWithPermissionList, userOperatorList) => {
  return []
}

OK,運(yùn)行項目。很好,沒有出現(xiàn)錯誤,繼續(xù)前行。

我的需求是這樣的,當(dāng)一個路由,在 meta 里面的 requireCode 用戶擁有第一項操作權(quán)限的時候,是可以進(jìn)入的,也就是菜單需要加載出來。

export default [
  {
    path: "/product",
    name: "product",
    component: Main,
    meta: {
      hideInBread: true
    },
    children: [
      {
        path: "list",
        name: "list",
        meta: {
          title: "product-list",
          requireCode: ["ProductManageEndpoint#list", "ProductManageEndpoint#createProduct", "ProductManageEndpoint#putawayProduct", "ProductManageEndpoint#delistProduct", "ProductManageEndpoint#deleteByUuid"]
        },
        component: () => import("@/view/product/product-list.vue")
      },
      {
        path: "add",
        name: "add",
        meta: {
          title: "product-add",
          requireCode: ["ProductManageEndpoint#createProduct"]
        },
        component: () => import("@/view/product/product-form.vue")
      }
    ]
  }
]

當(dāng)這個頁面需要同時滿足兩個要求的資源操作的時候,第一項使用數(shù)組裝載。即:

requireCode: [["ProductManageEndpoint#list", "ProductManageEndpoint#createProduct"], "ProductManageEndpoint#putawayProduct", "ProductManageEndpoint#delistProduct", "ProductManageEndpoint#deleteByUuid"]

那么這個頁面需要同時滿足前面兩個的時候,才能加載出來。

那么開始編寫 getMenuWithPermissionByRouter 函數(shù)。呃,怎么說呢,先把 Aresn 的代碼拷過來改改滿足我上面的需求即可:

/**
 * 根據(jù)需要控制菜單的路由,獲取菜單列表
 * @param routerWithPermissionList 需要權(quán)限管理的路由
 * @param userOperatorList 用戶擁有的所有操作
 * @returns {Array}
 */
export const getMenuWithPermissionByRouter = (routerWithPermissionList, userOperatorList) => {
  // debugger
  let res = []
  const allOpCodeArr = userOperatorList.map(opCodeObj => opCodeObj.operatorCode)
  forEach(routerWithPermissionList, item => {
    if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
      let obj = {
        icon: (item.meta && item.meta.icon) || "",
        name: item.name,
        meta: item.meta
      }
      if ((hasChild(item) || (item.meta && item.meta.showAlways))) {
        obj.children = getMenuWithPermissionByRouter(item.children, userOperatorList)
      }
      if (hasThisMenuPermission(item, allOpCodeArr) || hasChild(item)) res.push(obj)
    }
  })
  return res
}

const hasThisMenuPermission = (routerInfo, allOpCodeArr) => {
  if (routerInfo.meta && routerInfo.meta.requireCode && routerInfo.meta.requireCode.length) {
    const requireCode = routerInfo.meta.requireCode[0]
    if (Array.isArray(requireCode)) {
      let hasPermission = true
      for (let i = 0; i < requireCode.length; i++) {
        hasPermission = hasPermission && allOpCodeArr.indexOf(requireCode[i]) > -1
      }
      return hasPermission
    } else {
      return allOpCodeArr.indexOf(requireCode) > -1
    }
  } else return false
}

OK,如圖所示,已經(jīng)把菜單加載出來了。但是現(xiàn)在出現(xiàn)一個問題,就是刷新頁面的時候,什么都沒了。因為刷新頁面的時候,內(nèi)存中的 store 被置空了。

頁面加載的時候自動加載操作
// src/view/single-page/home/home.vue
...
mounted () {
    // 需要在這里加載權(quán)限信息
    const userName = this.$store.state.user.userName
    this.$store.commit("setPermissionList", getPermissionCodeList(userName))
}
...

可喜可賀,刷新的時候,菜單已經(jīng)出來了。但是有個報錯,報 sideMenuundefined 異常。是由于剛開始刷新頁面的時候,菜單還沒有出來,而代碼寫死了讀第0個元素,菜單是空數(shù)組,所以理所當(dāng)然導(dǎo)致了異常。

// src/components/main/components/side-menu/side-menu.vue 第 20 行,去掉最后屬性的或者即可

OK,我感覺最難的菜單搞定了。接下來要搞定路由。

動態(tài)新增有權(quán)限的路由

有了前面的鋪墊,我感覺路由要好做很多了。

動態(tài)路由有一個調(diào)用的函數(shù)是:

router.addRoutes(routes: Array)

那么我們需要做的只是,獲取有權(quán)限的路由,然后加入到 Vue 中。

但是這段代碼有點(diǎn)問題就是每次進(jìn)入都會調(diào)用一次= =

// src/view/single-page/home/home.vue
mounted () {
    // 需要在這里加載權(quán)限信息
    const userName = this.$store.state.user.userName
    this.$store.commit("setPermissionList", getPermissionCodeList(userName))
    const routers = getRouterWithPermission(routersWithPermission, this.$store.state.permission.operatorList)
    const originRouteNames = this.$router.options.routes.map(r => r.name)
    // 需要解決重復(fù)加入問題
    if (routers && routers.length && originRouteNames.indexOf(routers[0].name) < 0) {
        this.$router.addRoutes(routers)
    }
}

好了,菜單有了,路由有了,點(diǎn)擊測試能否進(jìn)入。

然后被作者的路由攔截到了-,-

// src/router/index.js
const turnTo = (to, access, next) => {
  next() // 直接過。
}
測試路由和菜單

分別登陸 zhangsanlisi 從上面的測試權(quán)限數(shù)據(jù)可以看到,lisi 是進(jìn)入不了產(chǎn)品列表的。

登陸lisi

強(qiáng)行進(jìn)入:

OK,正確了,如果沒有權(quán)限,肯定就沒有頁面嘛,直接404.

登陸zhangsan

按鈕的控制

頁面的跳轉(zhuǎn)搞定了,接下來就要考慮按鈕的問題了。那么上面設(shè)置的 VuexhasPermission 在這里派上用場。關(guān)于按鈕的綁定,思路來源于 Vue 的自定義指令,只需要在主頁面注冊自定義指令,每次頁面加載的時候,加載到指定指令的組件,開始讀取這個組件的 operaCode 是否在當(dāng)前登陸的用戶中,如果存在,則繼續(xù)執(zhí)行,如果不存在,則拿到了節(jié)點(diǎn)的 Vnode 開始設(shè)置我們想要的東西,比如鎖定按鈕啊,劫持點(diǎn)擊事件彈出提示,或者隱藏都可以。

// src/main.js
/**
 * 注冊權(quán)限控制指令
 */
Vue.directive("opcode", {
  bind: function (el, opcode, vnode, oldVNode) {
    const requireOpCode = opcode.value
    // 如果用戶沒有這個操作Code的權(quán)限,那么劫持click事件,賦予彈出無權(quán)限彈窗的事件
    console.log(requireOpCode);
    if (vnode.componentInstance === undefined || vnode.componentInstance === null) {
      if (!vnode.context.$store.getters.hasPermission(requireOpCode)) {
        vnode.data.on.click.fns = function () {
          Modal.warning({
            title: "無權(quán)限",
            content: "很抱歉,您沒有這項操作的權(quán)限"
          })
        }
      }
    } else {
      if (!vnode.componentInstance.$store.getters.hasPermission(requireOpCode)) {
        vnode.componentInstance.$off("click")
        vnode.componentInstance.$on("click", function () {
          Modal.warning({
            title: "無權(quán)限",
            content: "很抱歉,您沒有這項操作的權(quán)限"
          })
        })
      }
    }
  }
})

常規(guī)按鈕使用:


Render 函數(shù)中使用:

// src/view/product/product-list.vue
prodColumns: [
        {
          title: "商品名稱",
          key: "pname",
          align: "center",
          width: 300
        },
        {
          title: "操作",
          key: "operator",
          align: "center",
          render: (h, params) => {
            return h("a", {
              attrs: {
                href: "javascript:;"
              },
              on: {
                click: () => {
                  this.handleEditProduct(params.row)
                }
              },
              // 在這里注冊自定義指令
              directives: [
                {
                  name: "opcode",
                  value: "ProductManageEndpoint#updateByUuid"
                }
              ]
            }, "編輯")
          }
        }
      ],

這種方式即使分頁,改變表格數(shù)據(jù),也可以被主頁面上的判斷監(jiān)聽到。

PS:在這里就可以看到了按鈕的編排的問題,即我們新增產(chǎn)品的頁面接口,是在填寫完 Form 表單的時候才開始請求的,但是我們需要在兩個地方做設(shè)置,一個是跳轉(zhuǎn)頁面(路由+菜單),一個是新增產(chǎn)品的按鈕,即需要把這個權(quán)限攔截提前到進(jìn)入頁面之前。這方面現(xiàn)在確實(shí)想不到更好的解決辦法了。

演示

回顧一下權(quán)限設(shè)置數(shù)據(jù):

export const getPermissionCodeList = (userName) => {
  if (userName === "zhangsan") {
    return [{ operatorCode: "ProductManageEndpoint#list", name: "產(chǎn)品列表" },
      { operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#putawayProduct", name: "產(chǎn)品上架" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" }]
  } else {
    return [{ operatorCode: "ProductManageEndpoint#createProduct", name: "產(chǎn)品上傳" },
      { operatorCode: "ProductManageEndpoint#delistProduct", name: "產(chǎn)品下架" },
      { operatorCode: "ProductManageEndpoint#deleteByUuid", name: "產(chǎn)品刪除" }]
  }
}

我們知道,用戶名為 zhangsan 是沒有刪除產(chǎn)品權(quán)限以及編輯產(chǎn)品的,但是前面的都有。而 lisi 用戶,沒有產(chǎn)品列表功能,所以列表都不可以進(jìn)去。

zhangsan 用戶界面:

lisi 用戶界面:

權(quán)限編排頁面建議

我們知道,經(jīng)典權(quán)限模型是用戶、角色、資源的編排。那么我們可以設(shè)置一系列的角色,當(dāng)然記得給自己預(yù)留一個流氓角色可以讀取所有資源和角色的,利于管理。每個角色可以綁定不同的資源,也可以綁定不同的用戶。這樣用戶一旦登陸,即可讀取所有角色中的所有資源操作(記得去重)。

我們公司的綁定界面,通過讀取用戶所擁有的所有資源,再把 router 中綁定的讀取出來(當(dāng)然過濾掉當(dāng)前綁定用戶無權(quán)限的),然后通過加載勾選,最后發(fā)送請求給服務(wù)器做修改。這樣設(shè)置可以無限極下限,只要賦予用戶授權(quán)的權(quán)限,用戶即可一直創(chuàng)建然后綁定他想綁定的。

權(quán)限模塊建議

當(dāng)前所做的都是前端需要做的工作,后端也需要配合完成接口的調(diào)用,即使前端做了設(shè)置,但是有些用戶可以繞過前端,這時候所有的權(quán)限操作就需要后端來攔截了。

作者:WeidanLi
我的博客

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102387.html

相關(guān)文章

  • sSpring Boot多模塊+ Shiro + Vue:前后分離登陸整合,權(quán)限認(rèn)證(一)

    摘要:前言本文主要使用來實(shí)現(xiàn)前后端分離的認(rèn)證登陸和權(quán)限管理,適合和我一樣剛開始接觸前后端完全分離項目的同學(xué),但是你必須自己搭建過前端項目和后端項目,本文主要是介紹他們之間的互通,如果不知道這么搭建前端項目的同學(xué)可以先找別的看一下。 前言 本文主要使用spring boot + shiro + vue來實(shí)現(xiàn)前后端分離的認(rèn)證登陸和權(quán)限管理,適合和我一樣剛開始接觸前后端完全分離項目的同學(xué),但是你必...

    macg0406 評論0 收藏0
  • [ 好文分享 ] 美團(tuán)酒店Node全棧開發(fā)實(shí)踐

    摘要:我所在的美團(tuán)酒店事業(yè)部去年月份成立,新的業(yè)務(wù)新的開發(fā)團(tuán)隊,這一切使得我們的前后端分離推進(jìn)的很徹底。日志監(jiān)控平臺日志監(jiān)控平臺是美團(tuán)內(nèi)部的一個日志收集系統(tǒng),目前美團(tuán)統(tǒng)一使用收集日志,具有接收格式日志的能力,而日志監(jiān)控平臺也是以格式日志來收集。 轉(zhuǎn)自:美團(tuán)技術(shù)團(tuán)隊 作者:美團(tuán)技術(shù)團(tuán)隊 分享理由:很好的分享,可見,基于Node的前后端分離的架構(gòu)是越顯流行和重要,前端攻城獅們,No...

    wangdai 評論0 收藏0
  • 從NNVM看2016年深度學(xué)習(xí)框架發(fā)展趨勢

    摘要:兩者取長補(bǔ)短,所以深度學(xué)習(xí)框架在年,迎來了前后端開發(fā)的黃金時代。陳天奇在今年的中,總結(jié)了計算圖優(yōu)化的三個點(diǎn)依賴性剪枝分為前向傳播剪枝,例已知,,求反向傳播剪枝例,,求,根據(jù)用戶的求解需求,可以剪掉沒有求解的圖分支。 虛擬框架殺入從發(fā)現(xiàn)問題到解決問題半年前的這時候,暑假,我在SIAT MMLAB實(shí)習(xí)??粗乱粫号躎orch,一會兒跑MXNet,一會兒跑Theano。SIAT的服務(wù)器一般是不...

    ThinkSNS 評論0 收藏0
  • 如何從零設(shè)計結(jié)構(gòu)清晰、操作友好的權(quán)限管理模塊

    摘要:同時將用戶關(guān)聯(lián)到用戶組,從而可以在不斷變動權(quán)限的情況下,配置一次對應(yīng)關(guān)系,將用戶權(quán)限限制到單個上。這樣在返回數(shù)據(jù)的時候,所有記錄均為對其具有操作權(quán)限的對象。當(dāng)然是天生的組件化設(shè)計理念。 前言 在開講之前,先列舉幾個場景:場景一Hi,今天那個銷售總監(jiān)說要設(shè)立幾個銷售經(jīng)理的職位,然后每個經(jīng)理管理自己小組的銷售員,我們把用戶的銷售數(shù)據(jù)按組分開來吧。場景二Mario,今天那個市場部的說要分立幾...

    kel 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<