lrf 1 tahun lalu
induk
melakukan
c61dcd042f

File diff ditekan karena terlalu besar
+ 202 - 207
pnpm-lock.yaml


+ 8 - 1
src/lang/package/zh-cn/pages.js

@@ -28,6 +28,13 @@ export default {
     config_code: '功能编码'
   },
   role: {
-    
+    dialogTitle: '角色信息',
+    name: '角色名称',
+    namePh: '请填写角色名称',
+    code: '角色代码',
+    codePh: '请填写角色代码',
+    brief: '简介',
+    menu: '权限配置',
+    is_use: '是否启用'
   }
 }

+ 3 - 2
src/main.js

@@ -12,14 +12,15 @@ import 'virtual:svg-icons-register'
 import i18n from '@/lang/index'
 // 请求检查函数
 import { InitCheckResult } from './utils/checkResult'
-
+import { InitVariable } from './utils/variable'
 // 组件
 import globalComponents from '@/components'
 const app = createApp(App)
-globalComponents(app);
+globalComponents(app)
 setupStore(app)
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component)
 }
 app.use(i18n).use(router).mount('#app')
 InitCheckResult(app)
+InitVariable(app)

+ 40 - 0
src/store/api/system/role.js

@@ -0,0 +1,40 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/role'
+const axios = new AxiosWrapper()
+
+export const RoleStore = defineStore('role', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, '_id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del
+  }
+})

+ 11 - 0
src/utils/variable.js

@@ -0,0 +1,11 @@
+const variable = {
+  limit: 10
+}
+
+export const InitVariable = (app) => {
+  for (const key in variable) {
+    if (Object.hasOwnProperty.call(variable, key)) {
+      app.provide([key], variable[key])
+    }
+  }
+}

+ 13 - 11
src/views/system/menus/index.vue

@@ -1,15 +1,17 @@
 <template>
-  <el-row>
-    <el-col :span="24" style="text-align: right; padding: 10px">
-      <el-button type="primary" size="small" @click="toAdd()">{{ $t('common.add') }}</el-button>
-    </el-col>
-    <el-col :span="24">
-      <menu-table></menu-table>
-    </el-col>
-  </el-row>
-  <el-dialog v-model="dialog" :title="$t('pages.menus.dialogTitle')" :destroy-on-close="false" @close="toClose">
-    <menu-info></menu-info>
-  </el-dialog>
+  <div class="main animate__animated animate__backInRight">
+    <el-row>
+      <el-col :span="24" style="text-align: right; padding: 10px">
+        <el-button type="primary" size="small" @click="toAdd()">{{ $t('common.add') }}</el-button>
+      </el-col>
+      <el-col :span="24">
+        <menu-table></menu-table>
+      </el-col>
+    </el-row>
+    <el-dialog v-model="dialog" :title="$t('pages.menus.dialogTitle')" :destroy-on-close="false" @close="toClose">
+      <menu-info></menu-info>
+    </el-dialog>
+  </div>
 </template>
 
 <script setup>

+ 117 - 6
src/views/system/role/index.vue

@@ -1,19 +1,116 @@
 <template>
-  <div id="index">
+  <div class="main animate__animated animate__backInRight" v-loading="loading">
+    <el-row style="height: 5vh; padding: 5px">
+      <el-col :span="24" style="text-align: right">
+        <el-button type="primary" @click="toAdd">{{ $t('common.add') }}</el-button>
+      </el-col>
+    </el-row>
     <el-row>
-      <el-col :span="24" class="main animate__animated animate__backInRight" v-loading="loading">
-        <el-col :span="24" class="one">
-          <table></table>
-        </el-col>
+      <el-col :span="24">
+        <role-table></role-table>
       </el-col>
     </el-row>
+    <el-row justify="end" style="margin-top: 10px; height: 5vh">
+      <el-pagination
+        background
+        layout="total, prev, pager, next"
+        :page-size="limit"
+        :total="total"
+        v-model:current-page="currentPage"
+        @current-change="changePage"
+      />
+    </el-row>
+    <el-dialog v-model="dialog" :title="$t('pages.role.dialogTitle')" :destroy-on-close="true" @close="toClose">
+      <role-form></role-form>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import table from './parts/table.vue'
+import roleTable from './parts/table.vue'
+import roleForm from './parts/form.vue'
+import { RoleStore } from '@/store/api/system/role'
+import { MenusStore } from '@/store/api/system/menus'
+import { cloneDeep, get } from 'lodash-es'
+const store = RoleStore()
+const menuStore = MenusStore()
+
+const $checkRes = inject('$checkRes')
+const limit = inject('limit')
+const currentPage = ref(1)
+onMounted(() => {
+  searchMenus()
+  search({ skip: 0, limit })
+})
+const menuList = ref([])
+const searchMenus = async () => {
+  const res = await menuStore.query()
+  if ($checkRes(res)) {
+    menuList.value = res.data
+  }
+}
+
 const data = ref([])
+const total = ref(0)
+const search = async (e = { skip: 0, limit }) => {
+  const { skip, limit } = e
+  let info = { limit: limit, skip: skip }
+  let res = await store.query(info)
+  if ($checkRes(res)) {
+    data.value = res.data
+    total.value = res.total
+  }
+}
 
+const form = ref({})
+const dialog = ref(false)
+
+const toEdit = async (data) => {
+  let res = await store.fetch(data._id)
+  if ($checkRes(res)) {
+    form.value = res.data
+    dialog.value = true
+  }
+}
+const toDelete = async (data) => {
+  ElMessageBox.confirm('您确定删除该数据?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    const res = await store.del(data._id)
+    if ($checkRes(res, true)) {
+      search({ skip: 0, limit })
+    }
+  })
+}
+const onSubmit = async () => {
+  const data = cloneDeep(form.value)
+  console.log(data)
+  let res
+  if (get(data, '_id')) res = await store.update(data)
+  else res = await store.create(data)
+  if ($checkRes(res, true)) {
+    search({ skip: 0, limit })
+    toClose()
+  }
+}
+const changeUse = async (data) => {
+  let is_use = '1'
+  if (data.is_use === '1') is_use = '0'
+  const res = await store.update({ _id: data._id, is_use })
+  if ($checkRes(res, true)) {
+    search({ skip: 0, limit })
+  }
+}
+const toAdd = () => {
+  dialog.value = true
+  form.value = { is_use: '0', menu: ['home'] }
+}
+const toClose = () => {
+  dialog.value = false
+  form.value = { menu: {} }
+}
 // 加载中
 const loading = ref(false)
 // 请求
@@ -22,7 +119,21 @@ onMounted(async () => {
   loading.value = false
 })
 
+const changePage = (page = currentPage.value) => {
+  const obj = { skip: (page - 1) * limit, limit: limit }
+  search(obj)
+}
+
 // provide
 provide('data', data)
+provide('total', total)
+provide('menuList', menuList)
+provide('form', form)
+provide('dialog', dialog)
+provide('toAdd', toAdd)
+provide('toEdit', toEdit)
+provide('onSubmit', onSubmit)
+provide('changeUse', changeUse)
+provide('toDelete', toDelete)
 </script>
 <style scoped lang="scss"></style>

+ 105 - 0
src/views/system/role/parts/form.vue

@@ -0,0 +1,105 @@
+<template>
+  <el-form label-position="left" label-width="100px">
+    <el-form-item :label="$t('pages.role.name')">
+      <el-input v-model="form.name" :placeholder="$t('pages.role.namePh')"></el-input>
+    </el-form-item>
+    <el-form-item :label="$t('pages.role.code')">
+      <el-input v-model="form.code" :placeholder="$t('pages.role.codePh')"></el-input>
+    </el-form-item>
+    <el-form-item :label="$t('pages.role.is_use')">
+      <el-radio-group v-model="form.is_use">
+        <el-radio label="0">{{ $t('common.is_use_use') }}</el-radio>
+        <el-radio label="1">{{ $t('common.is_use_notUse') }}</el-radio>
+      </el-radio-group>
+    </el-form-item>
+    <el-form-item :label="$t('pages.role.brief')">
+      <el-input v-model="form.brief" type="textarea" :rows="3"></el-input>
+    </el-form-item>
+    <el-form-item :label="$t('pages.role.menu')">
+      <el-tree ref="roleTree" :data="getMenus()" node-key="code" default-expand-all show-checkbox @check-change="seletNode">
+        <template #default="{ data }">
+          <span>{{ data.name }}</span>
+        </template>
+      </el-tree>
+    </el-form-item>
+    <el-row justify="center" style="text-align: center">
+      <el-col :span="6">
+        <el-button @click="onSubmit" type="primary">{{ $t('common.save') }}</el-button>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script setup>
+const form = inject('form')
+const menuList = inject('menuList')
+const onSubmit = inject('onSubmit')
+const roleTree = ref()
+const getMenus = () => {
+  const list = toRaw(menuList.value)
+  const result = dealMenu(list)
+  return result
+}
+onMounted(() => {
+  const treeSelected = toRaw(form.value.menu)
+  for (const key of treeSelected) {
+    roleTree.value.setChecked(key, true)
+  }
+  console.log('init')
+})
+/**
+ * 处理菜单:将目录,菜单,子页面,权限统一转成 {name,code}形式,code为唯一值
+ * 目录,菜单,子页面:
+ *  route_name是唯一的,所以使用route_name作为code
+ * 权限:
+ *  code并不是唯一,但是加上route_name就是唯一的
+ * @param {Array} list 菜单列表
+ * @param {Array} route_names 将上级的route_name放入此数组中,在转为权限数据时,拼接成code
+ */
+const dealMenu = (list, route_names = []) => {
+  const result = []
+  for (const i of list) {
+    const { name, config = [], children = [], route_name } = i
+    const thisRouteNameArr = [...route_names, route_name]
+    const obj = { name, code: thisRouteNameArr.join('.') }
+    if (route_name === 'home') obj.disabled = true
+    const nextList = []
+    // 先处理该页面配置
+    if (config.length >= 0) {
+      // 如果有配置: 菜单,子页面两种情况,都统一处理
+      for (const c of config) {
+        const codeRouteNameArr = [...thisRouteNameArr]
+        codeRouteNameArr.push(c.code)
+        const cobj = { name: c.zh, code: codeRouteNameArr.join('.') }
+        nextList.push(cobj)
+      }
+    }
+    // 再处理菜单/子页面
+    if (children.length > 0) {
+      const nextRouteNames = [...thisRouteNameArr]
+      const midResult = dealMenu(children, nextRouteNames)
+      nextList.push(...midResult)
+    }
+    obj.children = nextList
+    result.push(obj)
+  }
+  return result
+}
+
+/**
+ * 选择节点
+ */
+const seletNode = () => {
+  if (!form.value.menu) {
+    form.value.menu = []
+  }
+  const selected = roleTree.value.getCheckedNodes(false, true)
+  const result = []
+  for (const s of selected) {
+    const rs = toRaw(s)
+    result.push(rs.code)
+  }
+  form.value.menu = result
+}
+</script>
+<style scoped></style>

+ 39 - 1
src/views/system/role/parts/table.vue

@@ -1,8 +1,46 @@
 <template>
-  <el-table :data="data"></el-table>
+  <el-table :data="data" row-key="_id" border height="70vh">
+    <el-table-column align="center" :label="$t('pages.role.name')" prop="name"></el-table-column>
+    <el-table-column align="center" :label="$t('pages.role.code')" prop="code"></el-table-column>
+    <el-table-column align="center" :label="$t('pages.role.is_use')" prop="is_use">
+      <template #default="{ row }">{{ getStatus(row) }} </template>
+    </el-table-column>
+    <el-table-column align="center" :label="$t('common.opera')">
+      <template #default="{ row }">
+        <el-link :underline="false" type="primary" size="mini" @click="toEdit(row)" style="margin-right: 10px">{{ $t('common.update') }}</el-link>
+        <el-link :underline="false" v-if="row.is_use === '1'" type="success" size="mini" @click="toEdit(row)" style="margin-right: 10px">
+          {{ $t('common.is_use_use') }}
+        </el-link>
+        <el-link :underline="false" v-if="row.is_use === '0'" type="warning" size="mini" @click="toEdit(row)" style="margin-right: 10px">
+          {{ $t('common.is_use_notUse') }}
+        </el-link>
+        <el-link v-if="row.is_default !== '0'" :underline="false" type="danger" size="mini" @click="toDelete(row)">
+          {{ $t('common.delete') }}
+        </el-link>
+      </template>
+    </el-table-column>
+  </el-table>
 </template>
 
 <script setup>
 const data = inject('data')
+const toEdit = inject('toEdit')
+const toDelete = inject('toDelete')
+const { t } = useI18n()
+const getStatus = (row) => {
+  let word = ''
+  switch (row.is_use) {
+    case '0':
+      word = t('common.is_use_use')
+      break
+    case '1':
+      word = t('common.is_use_notUse')
+      break
+
+    default:
+      break
+  }
+  return word
+}
 </script>
 <style scoped></style>