浏览代码

4-11需求开发

刘洋 2 年之前
父节点
当前提交
95d3927d24

+ 6 - 0
src/constants/app.ts

@@ -1,2 +1,8 @@
 /** 设计稿宽度 */
 export const DESIGN_WIDTH = 1680
+
+export const ROLE_BUTTON_CODES = [
+  'analysis-personnel_statistics_export',
+  'analysis-marking_progress_export',
+  'analysis-marking_progress_province_export',
+]

+ 2 - 0
src/directives/index.ts

@@ -2,10 +2,12 @@ import { dialogResize } from './dialogResize'
 import { dialogResizeImg } from './dialogResizeImg'
 import { dialogResizeStandard } from './dialogResizeStandard'
 import { customDialogResizeImg } from './customDialogResizeImg'
+import { permBtn } from './permBtn'
 
 export function setupDirectives(app: any) {
   app.directive('dialogResize', dialogResize)
   app.directive('dialogResizeImg', dialogResizeImg)
   app.directive('customDialogResizeImg', customDialogResizeImg)
   app.directive('dialogResizeStandard', dialogResizeStandard)
+  app.directive('permBtn', permBtn)
 }

+ 11 - 0
src/directives/permBtn.ts

@@ -0,0 +1,11 @@
+import useMainLayoutStore from '@/store/layout'
+export const permBtn = {
+  mounted(el: any, binding: any) {
+    const mainLayoutStore = useMainLayoutStore()
+    const permBtnCodes = mainLayoutStore.permBtnCodes
+    const { value } = binding
+    if (permBtnCodes.indexOf(value) == -1) {
+      el.style.display = 'none'
+    }
+  },
+}

+ 6 - 0
src/hooks/useOptions.ts

@@ -198,6 +198,12 @@ const useOptions = (
         if (userInfo.value?.subjectCode && !dataModel.subject) {
           changeModelValue('subject')(userInfo.value?.subjectCode)
         }
+        //如果是专家进来,则用户信息里的subjectCode是没值的,这种情况,给专家默认选择科目列表里的第一个科目
+        if (userInfo.value?.role === 'EXPERT' && !userInfo.value?.subjectCode) {
+          if (subjectList.value.length) {
+            changeModelValue('subject')(subjectList.value[0].value)
+          }
+        }
       },
       { deep: true }
     )

+ 127 - 11
src/modules/admin-role/setting/index.vue

@@ -7,7 +7,16 @@
         <el-button class="m-l-base" type="primary" :loading="loading" @click="onSubmit">保存</el-button>
       </div>
       <div class="flex-1 p-base m-t-large fill-lighter full-scroll-y-auto privilege-tree">
-        <el-tree ref="treeRef" show-checkbox node-key="index" :data="menuTree || []" @check-change="onCheckChange">
+        <!-- <el-tree ref="treeRef" show-checkbox node-key="index" :data="menuTree || []" @check-change="onCheckChange"> -->
+        <el-tree
+          ref="treeRef"
+          show-checkbox
+          node-key="id"
+          :data="myTreeData || []"
+          :props="treeProps"
+          :render-content="renderContent"
+          @check-change="onCheckChange"
+        >
         </el-tree>
       </div>
     </div>
@@ -23,12 +32,15 @@ import useFetch from '@/hooks/useFetch'
 import useMainLayoutStore from '@/store/layout'
 import BaseSelect from '@/components/element/BaseSelect.vue'
 import { cloneDeep } from 'lodash'
-
+import { ROLE_BUTTON_CODES } from '@/constants/app'
 import type { ExtractApiResponse } from '@/api/api'
 
 type MenuItem = MainLayoutStore.MenuItem
 
 type MenuItemWithId = MenuItem & { id: number }
+function createNum(m: number, n: number) {
+  return Math.floor(Math.random() * (m - n)) + n
+}
 const ROLE_OPTION = ref<any[]>([])
 useFetch('getRoleList')
   .fetch()
@@ -85,20 +97,83 @@ watch(
   },
   { immediate: true }
 )
-
-watch(privilege, () => {
-  let treeData = getTreeList(privilege.value)
-  let pIds: any[] = []
+const myTreeData = ref<any[]>([])
+const treeProps = ref({
+  label: 'name',
+})
+const renderContent = (h: any, { node, data, store }: any) => {
+  if (data.originId) {
+    return h(
+      'span',
+      {
+        class: 'custom-tree-node',
+      },
+      h('span', { class: 'is-page' }, '页面')
+    )
+  }
+  if (ROLE_BUTTON_CODES.includes(data.code)) {
+    return h(
+      'span',
+      {
+        class: 'custom-tree-node',
+      },
+      h('span', null, data.name),
+      h('span', { class: 'is-btn' }, '按钮')
+    )
+  }
+  return data.name
+}
+const getPIds = (treeData: any, pIds: any = []) => {
   for (let i = 0; i < treeData.length; i++) {
-    let item = treeData[i]
+    let item: any = treeData[i]
     if (item.children && item.children.length) {
       pIds.push(item.id)
+      getPIds(item.children, pIds)
     }
   }
+  return pIds
+}
+const newFlatArr = ref<any[]>([])
+watch(privilege, () => {
+  let data = cloneDeep(privilege.value || [])
+  data.forEach((item: any) => {
+    if (ROLE_BUTTON_CODES.includes(item.code)) {
+      item.type = 'button'
+    }
+  })
+  let hasPermBtnPageNodes: any[] = []
+  for (let i = 0; i < data.length; i++) {
+    let node = data[i]
+    if (ROLE_BUTTON_CODES.includes(node.code)) {
+      let pNode: any = data.find((v: any) => v.id == node.parentId) || {}
+      if (!hasPermBtnPageNodes.find((v: any) => v.code == node.code + '_in_page')) {
+        let id = createNum(-100000, -10000)
+        hasPermBtnPageNodes.push(
+          Object.assign({}, node, {
+            code: node.code + '_in_page',
+            nodeCode: node.nodeCode + '_in_page',
+            name: '页面',
+            hasPrivilege: !!pNode.hasPrivilege,
+            id,
+            nodeId: id + '',
+            originId: node.id,
+            type: 'page',
+          })
+        )
+      }
+    }
+  }
+
+  let concatData = [...hasPermBtnPageNodes, ...data]
+  newFlatArr.value = cloneDeep(concatData)
+  let treeData = getTreeList(concatData)
+  myTreeData.value = treeData
+
+  let pIds = getPIds(treeData)
 
   nextTick(() => {
     treeRef?.value?.setCheckedKeys(
-      privilege?.value?.filter((d) => d.hasPrivilege && pIds.indexOf(d.id) == -1)?.map((d) => d.code)
+      newFlatArr.value?.filter((d) => d.hasPrivilege && pIds.indexOf(d.id) == -1)?.map((d) => d.id)
     )
   })
 })
@@ -122,9 +197,33 @@ const menuTree = computed(() => {
     return menus.concat(filterPrivilege(menu, privilege.value || []) || [])
   }, [] as MenuItem[])
 })
+const onCheckChange = (a: any, b: any) => {
+  let resultKeys: any = treeRef?.value?.getCheckedKeys(true)
+  if (a.type == 'page') {
+    let underPageBtnKeys: any[] = []
+    if (!b) {
+      for (let i = 0; i < newFlatArr.value.length; i++) {
+        let item = newFlatArr.value[i]
+        if (item.parentId == a.parentId && item.type === 'button') {
+          underPageBtnKeys.push(item.id)
+        }
+      }
+      resultKeys = resultKeys?.filter((k: any) => !underPageBtnKeys.includes(k))
+      treeRef?.value?.setCheckedKeys(resultKeys)
+    }
+  }
+  if (a.type == 'button') {
+    if (!!b) {
+      let page = newFlatArr.value.find((item: any) => item.type === 'page' && item.parentId == a.parentId)
 
-const onCheckChange = () => {
-  checkedMenus.value = (treeRef?.value?.getCheckedNodes() as MenuItemWithId[]) || []
+      if (!resultKeys?.includes(page.id)) {
+        treeRef?.value?.setCheckedKeys([...resultKeys, page.id])
+      }
+    }
+  }
+  setTimeout(() => {
+    checkedMenus.value = (treeRef?.value?.getCheckedNodes() as MenuItemWithId[]) || []
+  }, 1)
 }
 
 /** 保存 */
@@ -132,7 +231,11 @@ const onSubmit = async () => {
   try {
     const checkedNodes = treeRef?.value?.getCheckedNodes() || []
     const checkedHalfNodes = treeRef?.value?.getHalfCheckedNodes() || []
-    const privilegeIds = checkedNodes.concat(checkedHalfNodes).map((d) => d.id)
+    const privilegeIds = checkedNodes
+      .concat(checkedHalfNodes)
+      .map((d) => d.id)
+      .filter((id) => id > -1)
+
     await setPrivilege({ role: role.value, privilegeIds })
     ElMessage.success('保存成功')
   } catch (error) {
@@ -145,5 +248,18 @@ const onSubmit = async () => {
 .privilege-tree {
   width: 660px;
   min-height: 320px;
+  :deep(.custom-tree-node) {
+    .is-page,
+    .is-btn {
+      padding: 0 5px;
+      font-size: 8px;
+      border-radius: 4px;
+      background: #67c23a;
+      color: #fff;
+    }
+    .is-btn {
+      margin-left: 8px;
+    }
+  }
 }
 </style>

+ 3 - 0
src/modules/admin-user/edit-user/index.vue

@@ -91,6 +91,9 @@ watch(dataModel, () => {
     model.mainNumber = dataModel.question
     model.markingGroupNumber = dataModel.group
   }
+  if (model.role === 'SECTION_LEADER') {
+    model.subjectCode = dataModel.subject || '全部'
+  }
 })
 watch(
   () => model.role,

+ 15 - 3
src/modules/analysis/marking-progress/components/GroupProgress.vue

@@ -3,10 +3,22 @@
     <base-form size="small" :items="items" :model="model">
       <template #form-item-operation>
         <el-button class="m-l-base" type="primary" @click="onSearch">查询</el-button>
-        <!-- <el-button :loading="exportLoading" type="primary" custom-1 @click="onExport">导出</el-button> -->
-        <!-- <el-button :loading="provinceExportLoading" custom-1 type="primary" @click="provinceExport"
+        <el-button
+          v-permBtn="'analysis-marking_progress_export2'"
+          :loading="exportLoading"
+          type="primary"
+          custom-1
+          @click="onExport"
+          >导出</el-button
+        >
+        <el-button
+          v-permBtn="'analysis-marking_progress_province_export'"
+          :loading="provinceExportLoading"
+          custom-1
+          type="primary"
+          @click="provinceExport"
           >省份进度导出</el-button
-        > -->
+        >
       </template>
     </base-form>
     <base-table border stripe size="small" :columns="columns" :data="result"></base-table>

+ 6 - 2
src/modules/analysis/marking-progress/components/TotalProgress.vue

@@ -76,7 +76,10 @@ provideInitOption({ renderer: 'svg' })
 const canLoadTopChart = ref(false)
 // const { subjectList, dataModel, changeModelValue } = useOptions(['subject'])
 
-const { subjectList, mainQuestionList, changeModelValue, dataModel, onOptionInit } = useOptions(['subject', 'question'])
+const { subjectList, mainQuestionList, changeModelValue, dataModel, onOptionInit, isExpert, isLeader } = useOptions([
+  'subject',
+  'question',
+])
 
 const model = reactive<any>({
   subjectCode: dataModel.subject || '',
@@ -98,7 +101,7 @@ const items = computed<EpFormItem[]>(() => [
     label: '科目',
     slotType: 'select',
     prop: 'subjectCode',
-    slot: { options: subjectList.value, onChange: changeModelValue('subject'), disabled: true },
+    slot: { options: subjectList.value, onChange: changeModelValue('subject'), disabled: !isExpert.value },
     rowKey: 'row-1',
     colProp: { span: 5 },
   },
@@ -111,6 +114,7 @@ const items = computed<EpFormItem[]>(() => [
       placeholder: '选择大题',
       options: mainQuestionList.value,
       onChange: changeModelValue('question'),
+      disabled: !isExpert.value && !isLeader.value,
     },
     rowKey: 'row-1',
     colProp: { span: 5 },

+ 8 - 1
src/modules/analysis/personnel-statistics/index.vue

@@ -4,7 +4,14 @@
       <base-form size="small" :label-width="'88px'" :disabled="loading" :model="model" :items="items">
         <template #form-item-button-group>
           <el-button :loading="loading" type="primary" @click="onSearch">查询</el-button>
-          <el-button :loading="exporting" type="primary" custom-1 @click="onExport">导出</el-button>
+          <el-button
+            v-permBtn="'analysis-personnel_statistics_export'"
+            :loading="exporting"
+            type="primary"
+            custom-1
+            @click="onExport"
+            >导出</el-button
+          >
         </template>
         <template #form-item-columns-setting>
           <div class="setting-box">

+ 26 - 20
src/modules/bootstrap/login/index.vue

@@ -121,27 +121,33 @@ function loginSuccess(loginInfo: ExtractApiResponse<'userLogin'>) {
 
   mainStore.getUserMarkConfig()
 
-  mainLayoutStore.getRenderMenuList()
-
-  /**
-   * 超级管理员每次登录完成之后需要选择考试批次
-   * 其它角色如果是首次登录,需要去设置名称.
-   * 否则大小组长进入监控首页, 评卷员进入评卷首页
-   */
-  if (loginInfo.role !== 'ADMIN') {
-    if (!loginInfo.needRealName) {
-      mainStore.getMyUserInfo()
-    }
-    replace({
-      name: loginInfo.needRealName
-        ? 'InitUserName'
+  mainLayoutStore.getRenderMenuList().then((renderMenus: any) => {
+    /**
+     * 超级管理员每次登录完成之后需要选择考试批次
+     * 其它角色如果是首次登录,需要去设置名称.
+     * 否则大小组长进入监控首页, 评卷员进入评卷首页
+     */
+    if (loginInfo.role !== 'ADMIN') {
+      if (!loginInfo.needRealName) {
+        mainStore.getMyUserInfo()
+      }
+      let next = loginInfo.needRealName
+        ? { name: 'InitUserName' }
         : loginInfo.role === 'MARKER'
-        ? 'MarkingMark'
-        : 'AnalysisMonitoring',
-    })
-  } else {
-    replace({ name: 'CheckExam' })
-  }
+        ? { name: 'MarkingMark' }
+        : { path: renderMenus[0].path || '/' }
+      replace(next)
+      // replace({
+      //   name: loginInfo.needRealName
+      //     ? 'InitUserName'
+      //     : loginInfo.role === 'MARKER'
+      //     ? 'MarkingMark'
+      //     : 'AnalysisMonitoring',
+      // })
+    } else {
+      replace({ name: 'CheckExam' })
+    }
+  })
 }
 
 function initLogin() {

+ 8 - 1
src/store/layout.ts

@@ -86,13 +86,14 @@ const useMainLayoutStore = defineStore<
   'main-layout',
   MainLayoutStore.State,
   any,
-  { toggleCollapse: () => void; getRenderMenuList: () => Promise<void> }
+  { toggleCollapse: () => void; getRenderMenuList: () => Promise<any> }
 >('main-layout', {
   state() {
     return {
       collapse: false,
       menuList: getMenuRotes(),
       renderMenus: [],
+      permBtnCodes: [],
     }
   },
   actions: {
@@ -102,9 +103,15 @@ const useMainLayoutStore = defineStore<
     async getRenderMenuList() {
       try {
         const privilege = await useFetch('getUserPrivilege').fetch()
+        this.permBtnCodes = privilege
+          .filter((node) => {
+            return node.privilegeType === 'BUTTON'
+          })
+          .map((item) => item.code)
         this.renderMenus = this.menuList.reduce((menus, menu) => {
           return menus.concat(filterPrivilege(menu, privilege) || [])
         }, [] as MainLayoutStore.MenuItem[])
+        return this.renderMenus
       } catch (error) {
         console.error(error)
       }

+ 1 - 0
types/store.d.ts

@@ -14,5 +14,6 @@ declare namespace MainLayoutStore {
     collapse: boolean
     menuList: MenuItem[]
     renderMenus: MenuItem[]
+    permBtnCodes: any[]
   }
 }