Forráskód Böngészése

新增菜单管理以及消息通知

zhangyaojie 1 éve
szülő
commit
8328f8d6a5

+ 1 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "fs-admin",
   "version": "1.2.0",
   "scripts": {
-    "dev": "vite",
+    "dev": "vite --host",
     "build": "run-p type-check build-only",
     "preview": "vite preview --port 4173",
     "build-only": "vite build",

+ 2 - 0
src/components.d.ts

@@ -25,11 +25,13 @@ declare module '@vue/runtime-core' {
     ImageUpload: typeof import('./components/ImageUpload.vue')['default']
     OrgLayout: typeof import('./components/org/OrgLayout.vue')['default']
     OrgList: typeof import('./components/org/OrgList.vue')['default']
+    PaneModel: typeof import('./components/splitpanes/PaneModel.vue')['default']
     ProCardList: typeof import('./components/ProCardList.vue')['default']
     ProForm: typeof import('./components/form/ProForm.vue')['default']
     ProTable: typeof import('./components/ProTable.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
+    SelIcon: typeof import('./components/SelIcon.vue')['default']
     SvgIcon: typeof import('./components/SvgIcon.vue')['default']
   }
 }

+ 50 - 0
src/components/SelIcon.vue

@@ -0,0 +1,50 @@
+<script setup lang="ts">
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+// 父组件传参props
+const props = defineProps<{
+  dialogVisible: boolean
+}>()
+console.log(121)
+const dialogVisible = ref(props.dialogVisible)
+const iconArr = ref<string[]>([])
+const emits = defineEmits(['closeIcon'])
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  iconArr.value.push(key)
+}
+const setIcon = (item: string) => {
+  emits('closeIcon', item)
+}
+</script>
+
+<template>
+  <el-dialog v-model="dialogVisible" title="图标选择器" width="50%" destroy-on-close>
+    <el-scrollbar height="400px">
+      <div class="iconContent">
+        <div v-for="(item, index) in iconArr" :key="index" class="iconWrap" @click="setIcon(item)">
+          <el-icon :size="60">
+            <component :is="item" />
+          </el-icon>
+          <span>{{ item }}</span>
+        </div>
+      </div>
+    </el-scrollbar>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.iconContent {
+  display: flex;
+  flex-wrap: wrap;
+  .iconWrap {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 25%;
+    padding: 15px 0;
+    cursor: pointer;
+  }
+  .iconWrap:hover {
+    color: var(--el-color-primary);
+  }
+}
+</style>

+ 35 - 0
src/components/splitpanes/PaneModel.vue

@@ -0,0 +1,35 @@
+<script lang="ts" setup>
+import { Splitpanes, Pane } from 'splitpanes'
+import 'splitpanes/dist/splitpanes.css'
+
+type Props = {
+  size?: number
+  lMinSize?: number
+  lMaxSize?: number
+  rMinSize?: number
+  rMaxSize?: number
+}
+
+interface configType {
+  config?: Props
+}
+
+const props = withDefaults(defineProps<configType>(), {
+  config: () => ({
+    size: 20
+  })
+})
+</script>
+
+<template>
+  <Splitpanes class="default-theme">
+    <Pane :size="config.size" :min-size="config.lMinSize" :max-size="config.lMaxSize">
+      <slot name="left" />
+    </Pane>
+    <Pane :min-size="config.lMinSize" :max-size="config.lMaxSize">
+      <slot name="right" />
+    </Pane>
+  </Splitpanes>
+</template>
+
+<style lang="scss" scoped></style>

+ 11 - 1
src/router/asyncRouter.ts

@@ -223,7 +223,17 @@ const asyncRouter: RouteRecordRaw[] = [
         name: 'logList',
         component: () => import('@/views/monitor/LogList.vue'),
         meta: {
-          title: '日志查询'
+          title: '日志查询',
+          icon: 'List'
+        }
+      },
+      {
+        path: '/monitor/messageNotif',
+        name: 'logList',
+        component: () => import('@/views/monitor/MessageNotif.vue'),
+        meta: {
+          title: '消息通知',
+          icon: 'ChatRound'
         }
       }
     ]

+ 88 - 0
src/views/monitor/MessageNotif.vue

@@ -0,0 +1,88 @@
+<script setup lang="ts">
+import type { BasicForm, ICRUD } from '@/types/form'
+
+const curRow = ref<any>(null)
+
+const CRUD: ICRUD = {
+  create(data: any) {
+    console.log(data)
+  },
+  update(data: any) {
+    console.log(data)
+  },
+  getList() {
+    return new Promise(resolve => {
+      resolve({
+        list: [],
+        total: 2
+      })
+    })
+  },
+  delete(data: any) {
+    console.log(data)
+  },
+  deleteBatch(data: any) {
+    console.log(data)
+  }
+}
+
+const formConfig = reactive<BasicForm>({
+  span: 12,
+  formItems: [
+    {
+      label: '消息名称',
+      value: '',
+      name: 'name',
+      type: 'input',
+      search: true
+    },
+    {
+      label: '消息内容',
+      value: '',
+      name: 'loginName',
+      type: 'input',
+      search: true
+    },
+    {
+      label: '消息类型',
+      value: '',
+      name: 'password',
+      type: 'input',
+      rules: [{ required: true, message: '请输入密码', trigger: 'blur' }]
+    },
+    {
+      label: '启用',
+      value: '1',
+      name: 'loginFlag',
+      type: 'switch',
+      props: {
+        'active-value': '1',
+        'inactive-value': '0'
+      }
+    }
+  ]
+})
+
+const handleCreate = () => {
+  console.log('1')
+}
+const handleEdit = (row: any) => {
+  console.log(row)
+}
+</script>
+
+<template>
+  <pro-table
+    :crud="CRUD"
+    :formConfig="formConfig"
+    :toolbarConfig="{ custom: true }"
+    @click-create="handleCreate"
+    @click-edit="handleEdit"
+  >
+    <vxe-column field="name" title="消息名称"></vxe-column>
+    <vxe-column field="loginName" title="消息内容"></vxe-column>
+    <vxe-column field="phone" title="消息类型"></vxe-column>
+  </pro-table>
+</template>
+
+<style lang="scss" scoped></style>

+ 347 - 60
src/views/system/menu.vue

@@ -1,58 +1,82 @@
 <script lang="ts" setup>
-import { Splitpanes, Pane } from 'splitpanes'
-import 'splitpanes/dist/splitpanes.css'
+import PaneModel from '@/components/splitpanes/PaneModel.vue'
 import { ElTree } from 'element-plus'
-import type { BasicForm } from '@/types/form'
+import type { BasicForm, ICRUD } from '@/types/form'
+import SelIcon from '@/components/SelIcon.vue'
 interface Tree {
   id: number
   label: string
   children?: Tree[]
+  icon?: string
+}
+const config = {
+  size: 20,
+  lMinSize: 20
+}
+const configSmall = {
+  size: 50
 }
-
 const filterText = ref('')
 const treeRef = ref<InstanceType<typeof ElTree>>()
-
 const defaultProps = {
   children: 'children',
   label: 'label'
 }
-
-watch(filterText, val => {
-  treeRef.value?.filter(val)
-})
-
-const filterNode = (value: string, data: any) => {
-  if (!value) return true
-  return data.label.includes(value)
-}
-
 const data: Tree[] = [
   {
     id: 1,
     label: '系统管理',
+    icon: 'Edit',
     children: [
       {
         id: 3,
-        label: '用户管理'
+        label: '用户管理',
+        icon: 'Edit'
       },
       {
         id: 4,
-        label: '角色管理'
+        label: '角色管理',
+        icon: 'Edit'
       }
     ]
   },
   {
     id: 2,
     label: '组织机构',
+    icon: 'Edit',
     children: [
       {
         id: 5,
-        label: '机构管理'
+        label: '机构管理',
+        icon: 'Edit'
       }
     ]
   }
 ]
+const dialogVisible = ref(false)
 
+watch(filterText, val => {
+  treeRef.value?.filter(val)
+})
+const filterNode = (value: string, data: any) => {
+  if (!value) return true
+  return data.label.includes(value)
+}
+const menuId = ref('')
+const handleNodeClick = (val: any) => {
+  console.log(val)
+  menuId.value = val.id
+}
+const changeType = (val: number) => {
+  console.log(val)
+  if (val == 1) {
+    const arr = [4, 5, 8, 9, 10]
+  } else if (val == 3) {
+    const arr = [6, 7, 8, 9]
+  } else if (val == 4) {
+    const arr = [5, 6, 7, 8, 9, 10]
+  }
+}
 const formConfig = reactive<BasicForm>({
   span: 24,
   props: {
@@ -80,15 +104,36 @@ const formConfig = reactive<BasicForm>({
     },
     {
       label: '类型',
+      value: 1,
+      name: 'type',
+      type: 'select',
+      options: [
+        { label: '目录', value: 1 },
+        { label: '菜单', value: 2 },
+        { label: '按钮', value: 3 },
+        { label: '路由', value: 4 }
+      ],
+      events: {
+        change: changeType
+      }
+    },
+    {
+      label: '链接地址',
       value: '',
-      name: 'part',
+      name: 'loginName',
+      type: 'input',
+      placeholder: '请填写路由路径或者超链接'
+    },
+    {
+      label: '链接类型',
+      value: '',
+      name: 'company',
       type: 'select',
       options: [
-        { label: '目录', value: '1' },
-        { label: '菜单', value: '2' },
-        { label: '按钮', value: '3' },
-        { label: '路由', value: '4' }
-      ]
+        { label: 'link', value: 1 },
+        { label: 'iframe', value: 2 }
+      ],
+      placeholder: '路由请留空,http链接选择iframe,新窗口打开选择link'
     },
     {
       label: '菜单图标',
@@ -101,6 +146,74 @@ const formConfig = reactive<BasicForm>({
           alias: 'append1'
         }
       ]
+    },
+    {
+      label: '可见',
+      value: '',
+      name: 'vis',
+      type: 'radio-group',
+      options: [
+        {
+          label: '1',
+          value: '显示'
+        },
+        {
+          label: '0',
+          value: '隐藏'
+        }
+      ]
+    },
+    {
+      label: '固定在标签栏',
+      value: '',
+      name: 'vis',
+      type: 'radio-group',
+      options: [
+        {
+          label: '1',
+          value: '是'
+        },
+        {
+          label: '0',
+          value: '否'
+        }
+      ]
+    },
+    {
+      label: '隐藏面包屑',
+      value: '',
+      name: 'vis',
+      type: 'radio-group',
+      options: [
+        {
+          label: '1',
+          value: '是'
+        },
+        {
+          label: '0',
+          value: '否'
+        }
+      ]
+    },
+    {
+      label: '授权标识',
+      value: '',
+      name: 'loginName',
+      type: 'input',
+      placeholder: '多个用逗号隔开'
+    },
+    {
+      label: '备注',
+      value: '',
+      name: 'loginName',
+      type: 'input',
+      props: {
+        autosize: {
+          minRows: 2,
+          maxRows: 6
+        },
+        type: 'textarea'
+      }
     }
   ]
 })
@@ -113,47 +226,221 @@ const update = (data: any) => {
 const formData = reactive<any>({})
 
 const selIcon = () => {
-  console.log(121)
+  dialogVisible.value = true
+}
+const setIcon = (val: string) => {
+  console.log(val)
+  dialogVisible.value = false
+  formData.remark = val
+}
+const CRUD: ICRUD = {
+  create(data: any) {
+    console.log(data)
+  },
+  update(data: any) {
+    console.log(data)
+  },
+  getList() {
+    return new Promise(resolve => {
+      resolve({
+        list: [],
+        total: 2
+      })
+    })
+  },
+  delete(data: any) {
+    console.log(data)
+  },
+  deleteBatch(data: any) {
+    console.log(data)
+  }
+}
+const logtFormConfig = reactive<BasicForm>({
+  span: 24,
+  formItems: [
+    {
+      label: '数据规则名称',
+      value: '',
+      name: 'loginName',
+      type: 'input'
+    },
+    {
+      label: '规则mapper方法',
+      value: '',
+      name: 'loginName',
+      type: 'input'
+    }
+  ]
+})
+const curRow = ref<any>(null)
+const handleRowClick = ({ row }: { row: any }) => {
+  console.log(row)
+  curRow.value = row
+}
+const rowClassName = ({ row }: { row: any }) => {
+  if (row.id === curRow.value.id) {
+    return 'active'
+  }
 }
 </script>
 
 <template>
-  <splitpanes class="default-theme">
-    <pane min-size="20%" max-size="70">
-      <el-card class="box-card left">
-        <template #header>
-          <el-input v-model="filterText" placeholder="输入关键字进行过滤" />
-        </template>
-        <el-tree
-          ref="treeRef"
-          class="filter-tree"
-          :data="data"
-          :props="defaultProps"
-          default-expand-all
-          :filter-node-method="filterNode"
-          show-checkbox
-        />
+  <PaneModel :config="config">
+    <template #left>
+      <el-card class="left">
+        <el-container>
+          <el-header>
+            <el-input v-model="filterText" placeholder="输入关键字进行过滤" />
+          </el-header>
+          <el-main>
+            <el-tree
+              ref="treeRef"
+              class="filter-tree"
+              :data="data"
+              :props="defaultProps"
+              default-expand-all
+              :filter-node-method="filterNode"
+              show-checkbox
+              @node-click="handleNodeClick"
+            >
+              <template #default="{ node, data }">
+                <span class="custom-tree-node">
+                  <el-icon :size="18">
+                    <component :is="data.icon" />
+                  </el-icon>
+                  <span>{{ node.label }}</span>
+                </span>
+              </template>
+            </el-tree>
+          </el-main>
+          <el-footer>
+            <el-button icon="plus" type="primary" size="small"></el-button>
+            <el-button icon="delete" type="danger" size="small"></el-button>
+            <el-button icon="refresh" size="small"></el-button>
+          </el-footer>
+        </el-container>
       </el-card>
-    </pane>
-    <pane>
-      <el-card class="box-card up">
-        <template #header>
-          <span>工作台</span>
+    </template>
+    <template #right>
+      <el-empty
+        v-if="menuId == ''"
+        description="请选择左侧菜单后操作"
+        image="http://cloud.jeeplus.org/assets/empty.066d9fc7.svg"
+      />
+      <PaneModel v-else :config="configSmall" class="form-scoll">
+        <template #left>
+          <el-card class="right">
+            <template #header>
+              <span>office管理</span>
+            </template>
+            <pro-form
+              class="form-wrap"
+              :formConfig="formConfig"
+              :formData="formData"
+              ref="proFormRef"
+              :update="update"
+              :create="create"
+            >
+              <template #append> testt </template>
+              <template #append1>
+                <el-button @click="selIcon">
+                  <el-icon class="el-icon--right"><MoreFilled /></el-icon>
+                </el-button>
+              </template>
+            </pro-form>
+            <div class="text-center">
+              <el-button type="primary">保存</el-button>
+            </div>
+          </el-card>
         </template>
-        <pro-form :formConfig="formConfig" :formData="formData" ref="proFormRef" :update="update" :create="create">
-          <template #append> testt </template>
-          <template #append1>
-            <el-button @click="selIcon">
-              <el-icon class="el-icon--right"><MoreFilled /></el-icon>
-            </el-button>
-          </template>
-        </pro-form>
-      </el-card>
-    </pane>
-    <pane max-size="70">
-      <span>4</span>
-    </pane>
-  </splitpanes>
+        <template #right>
+          <pro-table
+            :crud="CRUD"
+            :toolbarConfig="{ custom: true }"
+            :formConfig="logtFormConfig"
+            :row-class-name="rowClassName"
+            @cell-click="handleRowClick"
+          >
+            <template #header>
+              <span class="card-header">数据权限规则</span>
+            </template>
+            <vxe-column field="description" title="数据规则名称"></vxe-column>
+            <vxe-column field="type" title="规则mapper方法"></vxe-column>
+          </pro-table>
+        </template>
+      </PaneModel>
+    </template>
+  </PaneModel>
+  <SelIcon v-if="dialogVisible" :dialogVisible="dialogVisible" @closeIcon="setIcon" />
 </template>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+:deep(.splitpanes__splitter) {
+  background: transparent !important;
+}
+.el-container {
+  height: 100%;
+}
+.card-header {
+  padding-bottom: 14px;
+  border-bottom: 1px solid var(--el-card-border-color);
+  box-sizing: border-box;
+  margin-top: -2px;
+  margin-bottom: 16px;
+}
+.right {
+  height: 100%;
+  :deep(.el-card__body) {
+    height: 100%;
+  }
+  .form-wrap {
+    padding-right: 20px;
+  }
+}
+.el-empty {
+  height: 100%;
+  background-color: #fff;
+}
+.form-scoll {
+  :deep(.el-card__body) {
+    overflow: auto;
+  }
+}
+:deep(.splitpanes__pane) {
+  background-color: #fff;
+}
+.left {
+  height: 100%;
+  :deep(.el-tree-node__content) {
+    height: 36px;
+  }
+  .custom-tree-node {
+    display: flex;
+    align-items: center;
+    span {
+      margin-left: 5px;
+    }
+  }
+  .el-header {
+    border-bottom: 1px solid #e6e6e6;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 53px;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+    height: 100%;
+  }
+  .el-main {
+    overflow: auto;
+    flex: 1;
+    flex-basis: 100%;
+  }
+  .el-footer {
+    border-top: 1px solid #e6e6e6;
+    display: flex;
+    align-items: center;
+  }
+}
+</style>