Browse Source

feat: ✨ 完成角色权限管理,修复bug

@sun-chaoqun 2 năm trước cách đây
mục cha
commit
90fbd0a34f

+ 1 - 2
components.d.ts

@@ -19,8 +19,6 @@ declare module '@vue/runtime-core' {
     ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
-    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
@@ -57,6 +55,7 @@ declare module '@vue/runtime-core' {
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    ElTree: typeof import('element-plus/es')['ElTree']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     Loading: typeof import('./src/components/Loading/index.vue')['default']
     Pagination: typeof import('./src/components/TableBase/components/Pagination.vue')['default']

+ 8 - 7
src/components/dialog/Dialog.vue

@@ -4,6 +4,8 @@ const dialogVisible = ref(false)
 interface DialogProps {
   width?: string
   alignCenter?: boolean
+  handleClose?: () => void
+  handleOpen?: () => void
 }
 withDefaults(defineProps<DialogProps>(), {
   width: '50%',
@@ -12,12 +14,10 @@ withDefaults(defineProps<DialogProps>(), {
 
 defineEmits<{ (e: 'on-close', value: boolean): void }>()
 
-const DialogOpen = () => {
-  dialogVisible.value = true
-}
-const DialogClose = () => {
-  dialogVisible.value = false
-}
+const DialogOpen = () => (dialogVisible.value = true)
+
+const DialogClose = () => (dialogVisible.value = false)
+
 // 导出方法
 defineExpose({
   DialogOpen,
@@ -29,7 +29,8 @@ defineExpose({
   <el-dialog
     v-model="dialogVisible"
     @close="DialogClose"
-    @closed="DialogClose"
+    @closed="handleClose"
+    @opened="handleOpen"
     :align-center="alignCenter"
     :width="width"
     class="bottomD"

+ 6 - 1
src/layouts/Header/Breadcrumb.vue

@@ -3,16 +3,21 @@ import { computed } from 'vue'
 import { icons } from '@/utils/common'
 import { ArrowRight } from '@element-plus/icons-vue'
 import { useRoute, useRouter } from 'vue-router'
+// import { GlobalStore } from '@/stores/index'
 
 const route = useRoute()
 const router = useRouter()
+// const globalStore = GlobalStore()
 
 const breadcrumbList: any = computed(() => {
   return route.matched.reduce((all: any, next: any) => {
     return all.some((item: any) => item['path'] == next['path']) ? all : [...all, next]
   }, [])
+  // const { path } = route
+  // return globalStore.GET_AllBreadcrumbList[path]
 })
-// console.log(breadcrumbList)
+// console.log(globalStore.GET_AllBreadcrumbList, route)
+
 const isUnicode = (val: string | undefined) => {
   if (!val) return false
   return !/ue/g.test(val)

+ 56 - 65
src/layouts/Header/index.vue

@@ -37,61 +37,47 @@ setInterval(() => {
 
 <template>
   <div class="header">
-    <el-row>
-      <el-col :xl="16" :lg="14" :sm="13" class="d-flex">
-        <el-button
-          class="isCollapse"
-          type="primary"
-          :icon="globalStore.GET_isCollapse ? Expand : Fold"
-          link
-          @click="globalStore.SET_isCollapse"
-        ></el-button>
-        <Breadcrumb></Breadcrumb>
-      </el-col>
-      <el-col :xl="2" :lg="2" :sm="3" class="notice">
-        <el-link type="primary" @click="goTask"
-          ><el-icon> <List /> </el-icon>任务管理</el-link
-        >
-      </el-col>
-      <el-col :xl="2" :sm="2" class="notice">
-        <!-- <el-popover placement="bottom-end" :width="400" trigger="click">
-          <template #reference>
-            <el-button type="primary" circle>
-              <i class="iconfont el-icon">&#xe63e;</i>
-            </el-button>
-          </template>
-          <Notice></Notice>
-        </el-popover> -->
-        <el-dropdown>
-          <el-button type="primary" circle>
-            <i class="iconfont el-icon">&#xe63e;</i>
-          </el-button>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item>action</el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-      </el-col>
-      <el-col :xl="3" :lg="4" :sm="4" class="notice">
-        <el-dropdown>
-          <div class="avatar">
-            <el-avatar> <img src="@/assets/images/avatar.jpg" /> </el-avatar>
-            <span>{{ globalStore.GET_User_Info.T_name }}</span>
-            <el-icon class="el-icon">
-              <arrow-down />
-            </el-icon>
-          </div>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item :icon="UserFilled" @click="router.push('/user')">个人中心</el-dropdown-item>
-              <el-dropdown-item :icon="SwitchButton" @click="logOut">退出登录</el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-      </el-col>
-      <el-col :xl="1" :sm="2" class="notice">{{ time }}</el-col>
-    </el-row>
+    <div class="header-left">
+      <el-button
+        class="isCollapse"
+        type="primary"
+        :icon="globalStore.GET_isCollapse ? Expand : Fold"
+        link
+        @click="globalStore.SET_isCollapse"
+      ></el-button>
+      <Breadcrumb></Breadcrumb>
+    </div>
+    <div class="header-right">
+      <el-link type="primary" class="mr-2" @click="goTask"
+        ><el-icon> <List /> </el-icon>任务管理</el-link
+      >
+      <el-dropdown class="mr-2">
+        <el-button type="primary" circle>
+          <i class="iconfont el-icon">&#xe63e;</i>
+        </el-button>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item>action</el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      <el-dropdown class="mr-2">
+        <div class="avatar">
+          <el-avatar> <img src="@/assets/images/avatar.jpg" /> </el-avatar>
+          <span>{{ globalStore.GET_User_Info.T_name }}</span>
+          <el-icon class="el-icon">
+            <arrow-down />
+          </el-icon>
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item :icon="UserFilled" @click="router.push('/user')">个人中心</el-dropdown-item>
+            <el-dropdown-item :icon="SwitchButton" divided @click="logOut">退出登录</el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      <span>{{ time }}</span>
+    </div>
   </div>
 </template>
 
@@ -99,10 +85,19 @@ setInterval(() => {
 .header {
   width: 100%;
   color: #fff;
+  display: flex;
+  flex-wrap: wrap;
   // background-color: rgba(18, 21, 39, 0.86);
-  .el-menu-demo {
-    color: #fff;
-    background-color: rgba(18, 21, 39, 0.86);
+  .header-left {
+    flex: 1;
+    display: flex;
+  }
+  .header-right {
+    display: flex;
+    align-items: center;
+    .mr-2 {
+      margin-right: 8px;
+    }
   }
   .notice {
     cursor: pointer;
@@ -110,13 +105,9 @@ setInterval(() => {
     align-items: center;
     justify-content: center;
   }
-  .d-flex {
-    display: flex;
-    align-items: center;
-    .isCollapse {
-      margin-right: 1rem;
-      font-size: 1.35rem;
-    }
+  .isCollapse {
+    margin-right: 1rem;
+    font-size: 1.35rem;
   }
   .avatar {
     cursor: pointer;

+ 9 - 1
src/router/modules/staticRouter.ts

@@ -9,12 +9,20 @@ export const staticRouter: RouteRecordRaw[] = [
     path: '/index',
     name: 'Index',
     component: () => import('@/views/Index.vue'),
-    // redirect: '/home',
+    redirect: '/home',
     meta: {
       title: '起始页'
     },
     children: [
       {
+        path: '/home',
+        name: 'Home',
+        component: () => import('@/views/home/Home.vue'),
+        meta: {
+          title: '首页'
+        }
+      },
+      {
         path: '/saleMange',
         name: 'SaleMange',
         component: () => import('@/views/storehouse/sales/index.vue'),

+ 2 - 1
src/stores/index.ts

@@ -32,7 +32,8 @@ export const GlobalStore = defineStore({
     GET_depotList: state => state.depotList,
     GET_User_Info: state => state.userInfo,
     GET_Menu_List: state => state.MenuList,
-    GET_Flat_Menu: state => state.flatMenu
+    GET_Flat_Menu: state => state.flatMenu,
+    GET_AllBreadcrumbList: state => state.allBreadcrumbList
   },
   actions: {
     SET_isloading(payload: boolean) {

+ 3 - 0
src/styles/element.scss

@@ -34,6 +34,9 @@ img {
 .el-table .cell {
   white-space: nowrap !important;
 }
+.el-table td.el-table__cell div {
+  color: #909399;
+}
 
 .el-popper.is-customized {
   /* Set padding to ensure the height is 32px */

+ 17 - 0
src/styles/index.scss

@@ -13,3 +13,20 @@ $--color-success: #107c10;
 $--color-warning: #d9822b;
 $--color-danger: #a80000;
 $--el-color-primary: #eebe77;
+
+$--app-bg-light: #151c32;
+$--app-logo: #3d7eff;
+$--nav-link: #5e6a81;
+$--nav-link-active: #fff;
+$--list-item-hover: #0c1635;
+$--main-color: #fff;
+$--secondary-color: #5e6a81;
+$--color-light: rgba(52, 129, 210, 0.2);
+$--warning-bg: #ffe5e5;
+$--warning-icon: #ff8181;
+$--applicant-bg: #e3fff1;
+$--applicant-icon: #61e1a1;
+$--close-bg: #fff8e5;
+$--close-icon: #fdbc64;
+$--draft-bg: #fed8b3;
+$--draft-icon: #e9780e;

+ 14 - 16
src/views/Index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, onMounted, computed } from 'vue'
+import { onMounted, computed } from 'vue'
 import SubMenu from '@/layouts/Menu/SubMenu.vue'
 import Main from '@/layouts/Main/index.vue'
 import Header from '@/layouts/Header/index.vue'
@@ -16,28 +16,26 @@ onMounted(() => {
 })
 
 const route = useRoute()
-const defaultActive = ref<string>(route.path)
+const defaultActive = computed(() => route.path)
 </script>
 
 <template>
   <el-container class="home-container">
     <el-aside>
       <div class="menu" :style="{ width: globalStore.GET_isCollapse ? '65px' : '210px' }">
+        <Logo />
         <el-scrollbar>
-          <div>
-            <Logo />
-            <el-menu
-              :default-active="defaultActive"
-              :collapse="globalStore.GET_isCollapse"
-              :collapse-transition="false"
-              :unique-opened="true"
-              active-text-color="#ffd04b"
-              text-color="#fff"
-              background-color="rgba(18, 21, 39, 0.86)"
-            >
-              <SubMenu :menu-list="routerList"></SubMenu>
-            </el-menu>
-          </div>
+          <el-menu
+            :default-active="defaultActive"
+            :collapse="globalStore.GET_isCollapse"
+            :collapse-transition="false"
+            :unique-opened="true"
+            active-text-color="#ffd04b"
+            text-color="#fff"
+            background-color="rgba(18, 21, 39, 0.86)"
+          >
+            <SubMenu :menu-list="routerList"></SubMenu>
+          </el-menu>
         </el-scrollbar>
       </div>
     </el-aside>

+ 159 - 70
src/views/account/roles/Roles.vue

@@ -12,6 +12,7 @@ import { GlobalStore } from '@/stores/index'
 import { ref, reactive, nextTick } from 'vue'
 import Drawer from '@/components/Drawer/index.vue'
 import Dialog from '@/components/dialog/Dialog.vue'
+import { ElTree } from 'element-plus'
 import TableBase from '@/components/TableBase/index.vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import type { ColumnProps } from '@/components/TableBase/interface/index'
@@ -25,6 +26,7 @@ interface InSys {
   checkList: string[]
 }
 
+const menuMap = new Map()
 const isNew = ref(true)
 let currentVal: any = {}
 const SysList = ref<InSys[]>([])
@@ -45,7 +47,7 @@ const DialogOpen = async (row: any) => {
   dialog.value?.DialogOpen()
 }
 
-const checkList = ref<string[]>([])
+// const checkList = ref<string[]>([])
 const columns: ColumnProps[] = [
   { type: 'index', label: '#', width: 80 },
   { prop: 'T_name', label: '姓名' },
@@ -56,46 +58,62 @@ const columns: ColumnProps[] = [
 const getSysList = async () => {
   const { Data } = await User_Sys_List({ User_tokey: globalStore.GET_User_tokey })
   SysList.value = Data as InSys[]
-  for await (let item of SysList.value) {
-    getMenuList(item.T_sys)
+  // for await (let item of SysList.value) {
+  //   getMenuList(item.T_sys)
+  // }
+  const promises = []
+  for (let item of SysList.value) {
+    promises.push(getMenuList(item.T_sys))
   }
-}
 
+  Promise.all(promises).then(res => {
+    const menuListTemporary: any[] = []
+    res.forEach((item: any) => {
+      const { Children, T_permission } = item.Data.Menu[0]
+      item.Data.Menu[0].children = Children
+      if (['/account', 'mangage', '/salary', '/stock'].includes(T_permission)) {
+        item.Data.Menu[0].father = true
+        menuMap.set(T_permission, { code: item.code, children: [] })
+      }
+      if (item.Data.Menu_checked) {
+        getPermissionArr(item.Data)
+      }
+      menuListTemporary.push(item.Data.Menu[0])
+    })
+    menuList.value = menuListTemporary
+    setCheckedTreeKeys(permissionArr.value)
+  })
+}
 const getMenuList = async (code: string) => {
   const res: any = await User_Power_Get({ User_tokey: globalStore.GET_User_tokey, T_code: code, T_id: currentVal.T_id })
-  const sys = SysList.value.find(item => item.T_sys === code) as InSys
-  sys.children = res.Data.Menu[0].Children
-  if (res.Data.Menu_checked) {
-    sys.checkList = sys.children.map((m: any) => {
-      if (res.Data.Menu_checked.includes(m.Id)) return m.T_name
-    })
-  } else {
-    sys.checkList = []
+  return {
+    code,
+    ...res
   }
 }
 
-const submitUserRole = async (item: InSys) => {
-  let Sys: any = SysList.value.find(sys => item.T_sys === sys.T_sys)
-  let childs = Sys?.children.filter((child: InSys) => Sys.checkList.includes(child.T_name))
-  let T_menu = ''
-  childs.forEach((f: any) => {
-    let str = 'M' + f.Id + '|'
-    T_menu += str
-  })
-  const params = {
-    User_tokey: globalStore.GET_User_tokey,
-    T_id: currentVal.T_id,
-    T_code: item.T_sys,
-    T_menu
-  }
+const permissionArr = ref<string[]>([])
+const getPermissionArr = (menu: any) => {
+  const { Menu, Menu_checked } = menu
+  const { Children, T_permission } = Menu[0]
+  getCurrentFlatMenu(Children, Menu_checked, T_permission)
+}
 
-  const res = await User_Power_Edit_Menu(params)
-  if (res.Code === 200) {
-    ElMessage.success('修改成功!')
-    dialog.value?.DialogClose()
-    checkList.value = []
-  }
+const getCurrentFlatMenu = (children: any[], arr: number[], permission: strng) => {
+  const fatherMenu = menuMap.get(permission)
+
+  children.forEach((item: any) => {
+    const index = arr.findIndex((num: any) => num === item.Id)
+    if (item.Children) {
+      getCurrentFlatMenu(item.Children, arr, permission)
+    }
+    if (index !== -1 && !item.Children) {
+      permissionArr.value.push(item.T_permission)
+      fatherMenu.children.push(item)
+    }
+  })
 }
+const setCheckedTreeKeys = (arr: string[]) => treeRef.value?.setCheckedKeys(arr, true)
 
 // 添加角色
 type Fn = () => void
@@ -182,6 +200,79 @@ const searchHandle = () => {
   initParam.T_name = search.value
   TableRef.value?.searchTable()
 }
+
+// 重写角色权限
+const treeRef = ref<InstanceType<typeof ElTree>>()
+const menuList = ref<any[]>([])
+
+const append = async (data: any) => {
+  const currentMenu = menuMap.get(data.T_permission)
+  const { code, children } = currentMenu
+
+  let T_menu = ''
+  children.forEach((item: any) => {
+    let str = 'M' + item.Id + '|'
+    T_menu += str
+  })
+  const params = {
+    User_tokey: globalStore.GET_User_tokey,
+    T_id: currentVal.T_id,
+    T_code: code,
+    T_menu
+  }
+  const res = await User_Power_Edit_Menu(params)
+  if (res.Code === 200) {
+    ElMessage.success('修改成功!')
+    dialog.value?.DialogClose()
+  }
+}
+let fatherData: any = {}
+const checkChange = (data: any, check: boolean) => {
+  const father = menuMap.get(data.T_permission)
+  if (father) {
+    fatherData = data
+    fatherCheckHandle(data.Children, father, check)
+    return
+  }
+  const prevFather = menuMap.get(fatherData.T_permission)
+  const { children } = prevFather
+  if (check) {
+    prevFather.children.push(data)
+  }
+  if (!father && !check) {
+    const index = children.findIndex((item: any) => item.T_permission === data.T_permission)
+    if (index !== -1) {
+      children.splice(index, 1)
+    }
+  }
+}
+/**
+ * 处理点击父级 tree 时的处理
+ */
+const fatherCheckHandle = (children: any[], father: any, check: boolean) => {
+  if (check) {
+    children.forEach((item: any) => {
+      if (item.Children) {
+        father.children.push(item)
+        fatherCheckHandle(item.Children, father, check)
+      } else {
+        father.children.push(item)
+      }
+    })
+  } else {
+    father.children = []
+  }
+}
+/**
+ * dialog 关闭回调
+ */
+const dialogCloseCallback = () => {
+  nextTick(() => {
+    menuMap.clear()
+    permissionArr.value = []
+    fatherData = {}
+  })
+}
 </script>
 
 <template>
@@ -205,38 +296,36 @@ const searchHandle = () => {
         <el-button link type="danger" size="small" :icon="Delete" @click="UserDelete(row)">删除</el-button>
       </template>
     </TableBase>
-    <Dialog ref="dialog" width="80%">
+    <Dialog ref="dialog" width="50%" :handleClose="dialogCloseCallback">
       <template #header>
         <h3>编辑权限</h3>
       </template>
-      <el-row :gutter="24">
-        <el-col :span="3"><div class="grid-content b-radius-4">功能模块</div></el-col>
-        <el-col :span="18"><div class="grid-content b-radius-4">权限</div></el-col>
-        <el-col :span="3"><div class="grid-content b-radius-4">操作</div></el-col>
-      </el-row>
-      <el-row :gutter="24" v-for="item in SysList" :key="item.T_sys">
-        <el-col :span="3"
-          ><div class="grid-content b-radius-4">
-            <el-button type="success" link>{{ item.T_name }}</el-button>
-          </div></el-col
-        >
-        <el-col :span="18"
-          ><div class="grid-content grid-active b-radius-4">
-            <transition appear name="fade-transform" mode="out-in">
-              <el-checkbox-group v-model="item.checkList" v-if="item.children">
-                <el-checkbox :label="child.T_name" v-for="child in item.children" :key="'rr' + child.Id">{{
-                  child.T_name
-                }}</el-checkbox>
-              </el-checkbox-group>
-            </transition>
-          </div>
-        </el-col>
-        <el-col :span="3"
-          ><div class="grid-content b-radius-4">
-            <el-button type="primary" @click="submitUserRole(item)">提交</el-button>
-          </div></el-col
+      <transition appear name="fade" mode="out-in">
+        <el-tree
+          ref="treeRef"
+          :data="menuList"
+          show-checkbox
+          node-key="T_permission"
+          :expand-on-click-node="false"
+          :props="{ label: 'T_name', children: 'Children' }"
+          @check-change="checkChange"
         >
-      </el-row>
+          <template #default="{ data }">
+            <div
+              class="custom-tree-node"
+              :style="{ 'border-bottom': data.Children && data.father ? ' 1px solid #ddd' : '' }"
+            >
+              <span>{{ data.T_name }}</span>
+              <span v-if="data.Children && data.father">
+                <el-button type="primary" size="small" @click="append(data)">提交</el-button>
+              </span>
+            </div>
+          </template>
+          <template #empty>
+            <el-empty></el-empty>
+          </template>
+        </el-tree>
+      </transition>
     </Dialog>
     <Drawer ref="drawerRef" :handleClose="callbackDrawer">
       <template #header="{ params }">
@@ -261,19 +350,19 @@ const searchHandle = () => {
 .roles {
   @include f-direction;
 }
-/* fade-transform */
-.fade-transform-leave-active,
-.fade-transform-enter-active {
-  transition: all 0.5s;
+/* fade */
+.fade-leave-active,
+.fade-enter-active {
+  transition: all 1s;
 }
-.fade-transform-enter-from {
+.fade-enter-from {
   opacity: 0;
-  transition: all 0.5s;
-  transform: translateY(-30px);
+  transition: all 1s;
+  transform: translateY(-60px);
 }
-.fade-transform-leave-to {
+.fade-leave-to {
   opacity: 0;
-  transition: all 0.5s;
-  transform: translateY(30px);
+  transition: all 1s;
+  transform: translateY(60px);
 }
 </style>

+ 6 - 0
src/views/account/roles/index.scss

@@ -42,3 +42,9 @@ h3 span {
 .b-radius-4 {
   border-radius: 8px;
 }
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  justify-content: space-between;
+}

+ 8 - 6
src/views/home/Home.vue

@@ -29,19 +29,21 @@
   justify-content: center;
   align-items: center;
   h1 {
+    z-index: 10;
     font-size: 4rem;
     position: absolute;
+    text-shadow: 0px 0px 2px #000;
   }
   .wrap {
     width: 100%;
-    height: 100%;
+    height: 20%;
     padding: 40px 0;
     // position: fixed;
     position: relative;
     // top: 50%;
     opacity: 0.8;
-    // background: linear-gradient(to bottom right, #50a3a2, #53e3a6);
-    // background: -webkit-linear-gradient(to bottom right, #50a3a2, #53e3a6);
+    background: linear-gradient(to bottom right, #50a3a2, #53e3a6);
+    background: -webkit-linear-gradient(to bottom right, #50a3a2, #53e3a6);
   }
   .wrap ul {
     position: absolute;
@@ -59,9 +61,9 @@
     width: 15px;
     height: 15px;
     z-index: -8;
-    // background-color: rgba(255, 255, 255, 0.15);
-    background: linear-gradient(to bottom right, #50a3a2, #53e3a6);
-    background: -webkit-linear-gradient(to bottom right, #50a3a2, #53e3a6);
+    background-color: rgba(255, 255, 255, 0.15);
+    // background: linear-gradient(to bottom right, #50a3a2, #53e3a6);
+    // background: -webkit-linear-gradient(to bottom right, #50a3a2, #53e3a6);
     animotion: square 25s infinite;
     -webkit-animation: square 25s infinite;
   }

+ 1 - 7
src/views/salary/salary/Salary.vue

@@ -75,11 +75,6 @@ const userInfo = reactive({
   T_dept: '',
   T_post: ''
 })
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  const table_header = document.querySelector('.table-header') as HTMLDivElement
-  return height - table_header.clientHeight - 24 - 60 - 12
-}
 
 const tableRowClassName = (data: any): any => {
   //判断是否相等,相同时改变背景颜色
@@ -108,7 +103,6 @@ const tableRowClassName = (data: any): any => {
         :requestApi="User_List"
         :initParam="initParam"
         layout="prev, pager, next"
-        :onResize="onResize"
         :rowClick="getSalaryParams"
         :tableRowClassName="tableRowClassName"
       >
@@ -135,7 +129,7 @@ const tableRowClassName = (data: any): any => {
       </TableBase>
     </div>
     <el-row class="h-100 f-1 margin-left-3">
-      <el-col :span="24" class="h-100" style="overflow: hidden">
+      <el-col :span="24" class="h-100" style="overflow: hidden; display: flex; flex-direction: column">
         <el-card class="m-b-3">
           <h3 class="title m-b-5">员工基本信息</h3>
           <div class="info-content">

+ 2 - 22
src/views/salary/salary/SalaryFrom.vue

@@ -2,7 +2,7 @@
 import { reuls_validator } from './relus'
 import { ElNotification, ElMessage } from 'element-plus'
 import { Salary_Post } from '@/api/salary/index'
-import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ref, reactive } from 'vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { StarFilled } from '@element-plus/icons-vue'
 
@@ -61,30 +61,10 @@ const DataEcho = (data: any) => {
 defineExpose({
   DataEcho
 })
-
-let cardHeight = ref(0)
-const resize = () => {
-  const height = document.documentElement.clientHeight
-  cardHeight.value = height - 4 * 12 - 140 - 72 - 60
-}
-interface PropsType {
-  T_uuid: string
-  year: string
-  month: string
-}
-const props = defineProps<PropsType>()
-
-onMounted(() => {
-  resize()
-  window.onresize = resize
-})
-onUnmounted(() => {
-  window.onresize = null
-})
 </script>
 
 <template>
-  <el-card class="form-card" :style="{ height: cardHeight + 'px' }">
+  <el-card class="form-card" style="flex: 1">
     <el-row :gutter="24">
       <el-col :span="12" style="margin-left: 4rem">
         <el-tag type="warning" effect="dark"

+ 1 - 1
src/views/storehouse/InventoryStatistics.vue

@@ -45,7 +45,7 @@ const columns: ColumnProps[] = [
   { prop: 'T_product_model', label: '产品型号', ellipsis: true },
   { prop: 'T_product_spec', label: '产品规格' },
   { prop: 'T_total', label: '库存数量' },
-  { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
+  { prop: 'operation', label: '操作', width: 80, fixed: 'right' }
 ]
 
 const detailColumns: ColumnProps[] = [

+ 21 - 15
src/views/storehouse/inventory/InStorageProduct.vue

@@ -16,8 +16,9 @@ type Fn = () => void
 let total = 0
 const autoSelect = ref('')
 const selectTable = ref()
-let timeout: NodeJS.Timeout
+const loading = ref(false)
 const globalStore = GlobalStore()
+const NameOptions = ref<any[]>([])
 const classOptions = ref<any[]>([])
 const modelOptions = ref<any[]>([])
 const tableProductData = ref<any[]>([])
@@ -32,14 +33,15 @@ const initParam = reactive({
   page_z: 20
 })
 const callbackProductDrawer = (done: Fn) => done()
-const querySearchAsync = async (queryString: string, cb: (arg: any) => void) => {
-  clearTimeout(timeout)
-  globalStore.SET_isloading(true)
-  timeout = setTimeout(async () => {
+const querySearchAsync = async (queryString: string) => {
+  if (queryString) {
+    loading.value = true
+    globalStore.SET_isloading(true)
     const results = await getNameAsync(queryString)
-    cb(results)
+    NameOptions.value = results
     globalStore.SET_isloading(false)
-  }, 2000)
+    loading.value = false
+  }
 }
 
 const getProductModelList = async () => {
@@ -54,7 +56,7 @@ const getProductModelList = async () => {
   globalStore.SET_isloading(false)
 }
 const handleSelect = (item: any) => {
-  initParam.T_name = item.value
+  initParam.T_name = item
   getProductModelList()
 }
 // 搜索模型
@@ -154,15 +156,19 @@ defineExpose({
             </el-col>
             <el-col :xl="7" :lg="8" :md="10" class="d-flex">
               <span class="inline-flex items-center">产品名称:</span>
-              <el-autocomplete
+              <el-select
                 v-model="autoSelect"
-                clearable
-                :fetch-suggestions="querySearchAsync"
+                filterable
+                remote
+                reserve-keyword
                 placeholder="按产品名称搜索"
-                :debounce="2000"
-                :trigger-on-focus="false"
-                @select="handleSelect"
-              />
+                remote-show-suffix
+                :remote-method="querySearchAsync"
+                :loading="loading"
+                @change="handleSelect"
+              >
+                <el-option v-for="item in NameOptions" :key="item.value" :label="item.value" :value="item.value" />
+              </el-select>
             </el-col>
             <el-col :xl="7" :lg="8" :md="12" class="d-flex">
               <span class="inline-flex items-center">产品型号:</span>

+ 0 - 9
src/views/storehouse/outStock/ContractNumber.vue

@@ -37,14 +37,6 @@ const getApproverInfo = (row: any) => {
 
 const emit = defineEmits<{ (event: 'onContactInfo', value: any): void }>()
 
-/**
- * 配置高度
- */
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  return height - 45 - 74 - 40 - 12
-}
-
 const openDrawer = () => drawer.value?.openDrawer()
 defineExpose({
   openDrawer
@@ -62,7 +54,6 @@ defineExpose({
       :initParam="InitParam"
       :requestApi="Storehouse_Contract_Out_List"
       layout="total, prev, pager, next"
-      :onResize="onResize"
       :rowClick="getApproverInfo"
     >
       <template #table-header>

+ 7 - 3
src/views/storehouse/outStock/OutStockDetail.vue

@@ -19,7 +19,7 @@ interface InfoType {
   T_signer_unit?: string
   T_submit_name: string
   T_receive_name: string
-  T_delivery_type?: string
+  T_delivery_type: string
   T_courier_number?: number
 }
 
@@ -75,7 +75,11 @@ const previewSn = (devicelist: string[]) => {
   })
 }
 
-const delivery_type = {
+interface DeliveryType {
+  [propName: string]: string
+}
+
+const delivery_type: DeliveryType = {
   1: '自送',
   2: '自提',
   3: '快递'
@@ -172,7 +176,7 @@ onMounted(() => {
           <el-row>
             <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"> <span>送货方式:</span></el-col>
             <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5"
-              ><span>{{ info.T_delivery_type ? delivery_type[info.T_delivery_type as string] : '无' }}</span></el-col
+              ><span>{{ info.T_delivery_type ? delivery_type[info.T_delivery_type] : '无' }}</span></el-col
             >
           </el-row>
           <el-row>

+ 279 - 0
src/views/storehouse/outStock/OutStockProduct.vue

@@ -0,0 +1,279 @@
+<script setup lang="ts">
+import {
+  Storehouse_Stock_List,
+  Storehouse_ProductClass_List,
+  Storehouse_Product_Model_List,
+  Storehouse_Product_Name_List
+} from '@/api/storehouse/index'
+import { ElMessage } from 'element-plus'
+import { GlobalStore } from '@/stores/index'
+import Drawer from '@/components/Drawer/index.vue'
+import { ref, reactive, onMounted, nextTick } from 'vue'
+import { default as vElTableInfiniteScroll } from 'el-table-infinite-scroll'
+
+type Fn = () => void
+
+let total = 0
+const loading = ref(false)
+const autoSelect = ref('')
+const selectTable = ref()
+const globalStore = GlobalStore()
+const classOptions = ref<any[]>([])
+const modelOptions = ref<any[]>([])
+const NameOptions = ref<any[]>([])
+const tableProductData = ref<any[]>([])
+const drawerProductRef = ref<InstanceType<typeof Drawer> | null>(null)
+
+const initParam = reactive({
+  User_tokey: globalStore.GET_User_tokey,
+  T_product_name: '',
+  T_product_model: '',
+  T_product_class: '',
+  page: 0,
+  page_z: 20
+})
+const callbackProductDrawer = (done: Fn) => done()
+const querySearchAsync = async (queryString: string) => {
+  if (queryString) {
+    loading.value = true
+    globalStore.SET_isloading(true)
+    const results = await getNameAsync(queryString)
+    NameOptions.value = results
+    globalStore.SET_isloading(false)
+    loading.value = false
+  }
+}
+
+const getProductModelList = async () => {
+  globalStore.SET_isloading(true)
+  const res: any = await Storehouse_Product_Model_List({ T_name: autoSelect.value })
+  modelOptions.value = res.Data.map((item: any, index: number) => {
+    return {
+      value: item,
+      index: index
+    }
+  })
+  globalStore.SET_isloading(false)
+}
+const handleSelect = (item: any) => {
+  initParam.T_product_name = item
+  getProductModelList()
+}
+// 搜索模型
+const searchModelHandle = () => {
+  total = 0
+  initParam.page = 1
+  tableProductData.value = []
+  getProductList()
+}
+
+// 保存选中的数据id,row-key就是要指定一个key标识这一行的数据
+const getRowKey = (row: any) => {
+  return row.Id
+}
+
+// 加载第二个抽屉数据
+const load = () => {
+  if (initParam.page && total === tableProductData.value.length) {
+    ElMessage.warning('没有更多数据了!!')
+    return
+  }
+  initParam.page++
+  getProductList()
+}
+// 勾选产品
+const ProductselectionChange = (selection: any[], row: any) => emit('ontableData', row)
+const ProductSelectionAllChange = (selection: any[]) => emit('ontableDataAll', selection)
+
+const getNameAsync = async (str: string): Promise<any> => {
+  const res: any = await Storehouse_Product_Name_List({ T_name: str, T_class: initParam.T_product_class })
+  if (!res.Data) return
+  return res.Data.map((item: any, index: number) => {
+    return {
+      value: item,
+      index: index
+    }
+  })
+}
+
+const getProductList = async () => {
+  const res: any = await Storehouse_Stock_List({ ...initParam, T_name: autoSelect.value })
+  tableProductData.value.push(...res.Data.Data)
+  total = res.Data.Num
+}
+
+// 获取产品分类
+const getProductClassList = async () => {
+  const res: any = await Storehouse_ProductClass_List({ page: 1, page_z: 999 })
+  classOptions.value = res.Data.Data
+}
+
+onMounted(() => {
+  !classOptions.value.length && getProductClassList()
+})
+
+const productColumns = [
+  { type: 'selection', width: 80 },
+  { prop: 'T_product_img', label: '产品图片', name: 'T_product_img' },
+  { prop: 'T_product_name', label: '产品名称' },
+  { prop: 'T_product_class_name', label: '产品分类' },
+  { prop: 'T_product_model', label: '产品型号', ellipsis: true },
+  { prop: 'T_product_spec', label: '产品规格' },
+  { prop: 'T_product_relation_sn', label: '关联SN', name: 'T_product_relation_sn' }
+]
+
+const openDrawer = () => drawerProductRef.value?.openDrawer()
+const clearSelection = () => selectTable.value?.clearSelection()
+const selectTableChange = (row: any) => {
+  nextTick(() => {
+    selectTable.value?.toggleRowSelection(row, false)
+  })
+}
+const emit = defineEmits<{ (event: 'ontableData', value: any): void; (event: 'ontableDataAll', value: any[]): void }>()
+
+defineExpose({
+  openDrawer,
+  clearSelection,
+  selectTableChange
+})
+</script>
+
+<template>
+  <Drawer ref="drawerProductRef" :handleClose="callbackProductDrawer" size="70%">
+    <template #header="{ params }">
+      <h4 :id="params.titleId" :class="params.titleClass">选择产品</h4>
+    </template>
+    <el-card class="box-card" shadow="never">
+      <template #header>
+        <div class="input-suffix">
+          <el-row :gutter="20" style="margin-bottom: 0">
+            <el-col :xl="5" :lg="8" :md="10" class="d-flex">
+              <span class="inline-flex items-center">产品分类:</span>
+              <el-select v-model="initParam.T_product_class" clearable placeholder="请选择分类~">
+                <el-option v-for="item in classOptions" :key="item.Id" :label="item.T_name" :value="item.Id" />
+              </el-select>
+            </el-col>
+            <el-col :xl="7" :lg="8" :md="10" class="d-flex">
+              <span class="inline-flex items-center">产品名称:</span>
+              <el-select
+                v-model="autoSelect"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="按产品名称搜索"
+                remote-show-suffix
+                :remote-method="querySearchAsync"
+                :loading="loading"
+                @change="handleSelect"
+              >
+                <el-option v-for="item in NameOptions" :key="item.value" :label="item.value" :value="item.value" />
+              </el-select>
+            </el-col>
+            <el-col :xl="7" :lg="8" :md="12" class="d-flex">
+              <span class="inline-flex items-center">产品型号:</span>
+              <el-select v-model="initParam.T_product_model" clearable placeholder="请选择型号~">
+                <el-option v-for="item in modelOptions" :key="item.index" :label="item.value" :value="item.value" />
+              </el-select>
+              <el-button type="primary" @click="searchModelHandle">搜索</el-button>
+            </el-col>
+          </el-row>
+        </div>
+      </template>
+      <el-table
+        ref="selectTable"
+        :row-key="getRowKey"
+        :data="tableProductData"
+        style="width: 100%; height: 99%"
+        :header-cell-style="{
+          background: '#dedfe0',
+          height: '50px'
+        }"
+        v-el-table-infinite-scroll="load"
+        @select="ProductselectionChange"
+        @select-all="ProductSelectionAllChange"
+      >
+        <template v-for="item in productColumns" :key="item">
+          <el-table-column v-if="item.type === 'index' || item.type === 'selection'" align="center" v-bind="item" />
+          <el-table-column v-if="!item.ellipsis && item.prop" v-bind="item" align="center">
+            <template #default="{ row }">
+              <span v-if="item.prop === 'T_product_relation_sn'">
+                <el-tag v-if="row.T_product_relation_sn === 1" effect="dark">是</el-tag>
+                <el-tag v-else type="success" effect="dark">否</el-tag>
+              </span>
+              <el-image
+                v-if="item.prop === 'T_product_img'"
+                fit="cover"
+                style="width: 50px; height: 50px"
+                :src="row.T_product_img"
+                :preview-src-list="[row.T_product_img]"
+                :preview-teleported="true"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <el-icon><Picture /></el-icon>
+                  </div>
+                </template>
+              </el-image>
+            </template>
+          </el-table-column>
+          <el-table-column v-if="item.ellipsis && item.prop === 'T_model'" align="center" v-bind="item">
+            <template #default="{ row }">
+              <el-tooltip effect="dark" :content="row.T_model" placement="bottom">
+                {{ row.T_model }}
+              </el-tooltip>
+            </template>
+          </el-table-column>
+          <el-table-column v-if="item.ellipsis && item.prop === 'T_remark'" align="center" v-bind="item">
+            <template #default="{ row }">
+              <el-tooltip effect="customized" placement="left">
+                <template #content>
+                  <div class="tooltip-content">{{ row.T_remark }}</div>
+                </template>
+                {{ row.T_remark }}
+              </el-tooltip>
+            </template>
+          </el-table-column>
+        </template>
+      </el-table>
+    </el-card>
+  </Drawer>
+</template>
+
+<style scoped lang="scss">
+.tooltip-content {
+  max-width: 500px;
+  overflow-y: auto;
+}
+.box-card {
+  height: 100%;
+  :deep(.el-card__body) {
+    height: calc(100% - 70px);
+  }
+  .sn-header {
+    display: flex;
+    justify-content: end;
+  }
+  .input-suffix {
+    width: 100%;
+    .inline-flex {
+      white-space: nowrap;
+    }
+    .d-flex {
+      display: flex;
+    }
+  }
+}
+.image-slot .el-icon {
+  font-size: 30px;
+}
+.image-slot {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  background: var(--el-fill-color-light);
+  color: var(--el-text-color-secondary);
+  font-size: 30px;
+}
+</style>

+ 38 - 20
src/views/storehouse/outStock/ReceiveOutStock.vue

@@ -4,11 +4,11 @@ import { ref, reactive, nextTick } from 'vue'
 import { useRouter } from 'vue-router'
 import { GlobalStore } from '@/stores/index'
 import type { FormInstance, FormRules } from 'element-plus'
-import { Delete, CirclePlus } from '@element-plus/icons-vue'
+import { Delete, CirclePlus, Picture } from '@element-plus/icons-vue'
 import { Storehouse_StockOut_Add } from '@/api/storehouse/index'
 import ReceiveUser from './receiveUser.vue'
 import InStorageSn from '../inventory/InStorageSn.vue'
-import InStorageProduct from '../inventory/InStorageProduct.vue'
+import OutStockProduct from './OutStockProduct.vue'
 
 interface FormType {
   T_type: number
@@ -45,7 +45,7 @@ const form = reactive<FormType>({
 
 const validate_T_product = (rule: any, value: any, callback: any) => {
   if (value.includes(undefined) || value === '') {
-    callback(new Error('请填写产品数量'))
+    callback(new Error('请填写产品数量,数量不能大于库存数量!'))
   } else if (value.includes(null)) {
     callback(new Error('请添加产品SN'))
   } else {
@@ -62,12 +62,13 @@ const rules = reactive<FormRules>({
 
 const columns = [
   { type: 'index', label: '序号', width: 80, align: 'center ' },
-  { label: '产品图片', prop: 'T_img', align: 'center ', name: 'T_img' },
-  { label: '产品名称', prop: 'T_name', align: 'center ' },
-  { label: '产品分类', prop: 'T_class_name', align: 'center ' },
-  { label: '产品型号', prop: 'T_model', align: 'center ', ellipsis: true },
-  { label: '产品规格', prop: 'T_spec', align: 'center ' },
-  { label: '是否关联SN', prop: 'T_relation_sn', align: 'center ', width: 120, name: 'T_relation_sn' },
+  { label: '产品图片', prop: 'T_product_img', align: 'center ', name: 'T_product_img' },
+  { label: '产品名称', prop: 'T_product_name', align: 'center ' },
+  { label: '产品分类', prop: 'T_product_class_name', align: 'center ' },
+  { label: '产品型号', prop: 'T_product_model', align: 'center ', ellipsis: true },
+  { label: '产品规格', prop: 'T_product_spec', align: 'center ' },
+  { label: '是否关联SN', prop: 'T_product_relation_sn', align: 'center ', width: 120, name: 'T_relation_sn' },
+  { label: '库存数量', prop: 'T_total', align: 'center ' },
   { label: '*数量', prop: 'count', align: 'center ', name: 'count' },
   { label: '*关联设备', prop: 'sn', align: 'center ', name: 'sn' },
   { prop: 'operation', label: '操作', width: 80, fixed: 'right' }
@@ -75,7 +76,8 @@ const columns = [
 
 const countBlurHandle = () => {
   form.T_product = tableData.value.map(item => {
-    if (!item.count && item.T_relation_sn !== 1) return undefined
+    if (!item.count && item.T_product_relation_sn !== 1) return undefined
+    if (item.count > item.T_total) return undefined
     return `${item.T_product_id},${item.count}|`
   })
 }
@@ -87,7 +89,7 @@ const getDeviceSnToProduct = () => {
 
   return tableData.value.map((item: any) => {
     let product: any = ''
-    if (item.T_relation_sn === 1) {
+    if (item.T_product_relation_sn === 1) {
       DeviceSnData?.forEach((value: any, key: number) => {
         if (item.T_product_id === key) {
           let str = ''
@@ -108,8 +110,12 @@ const getDeviceSnToProduct = () => {
  */
 const determineSNorCount = (DeviceSnData: any) => {
   for (const item of tableData.value) {
-    if (!item.count && item.T_relation_sn !== 1) return 'count'
-    if (item.T_relation_sn === 1 && !DeviceSnData.get(item.Id) && !DeviceSnData.size) return 'sn'
+    if ((!item.count && item.T_product_relation_sn !== 1) || item.count > item.T_total) return 'count'
+    if (
+      (item.T_product_relation_sn === 1 && !DeviceSnData.get(item.Id) && !DeviceSnData.size) ||
+      item.count > item.T_total
+    )
+      return 'sn'
   }
   return false
 }
@@ -119,7 +125,6 @@ const AddInStorage = (formEl: FormInstance | undefined) => {
   form.T_product = getDeviceSnToProduct()
   formEl.validate(async valid => {
     if (valid) {
-      console.log(form)
       const res: any = await Storehouse_StockOut_Add({
         User_tokey: globalStore.GET_User_tokey,
         ...form,
@@ -268,7 +273,7 @@ const options = globalStore.GET_depotList
               </template>
               <template #default="{ row }" v-if="item.prop === item.name">
                 <el-input
-                  v-if="item.prop === 'count' && row.T_relation_sn !== 1"
+                  v-if="item.prop === 'count' && row.T_product_relation_sn !== 1"
                   v-model.number="row.count"
                   type="text"
                   autocomplete="off"
@@ -276,7 +281,7 @@ const options = globalStore.GET_depotList
                 />
                 <div v-if="item.prop === 'sn'">
                   <el-button
-                    v-if="row.T_relation_sn === 1"
+                    v-if="row.T_product_relation_sn === 1"
                     link
                     type="primary"
                     size="small"
@@ -286,11 +291,24 @@ const options = globalStore.GET_depotList
                   >
                   <span v-else>-</span>
                 </div>
-                <span v-if="item.prop === 'T_relation_sn'">
-                  <el-tag v-if="row.T_relation_sn === 1" effect="dark">是</el-tag>
+                <span v-if="item.prop === 'T_product_relation_sn'">
+                  <el-tag v-if="row.T_product_relation_sn === 1" effect="dark">是</el-tag>
                   <el-tag v-else type="success" effect="dark">否</el-tag>
                 </span>
-                <el-image v-if="item.prop === 'T_img'" style="height: 50px" :src="row.T_img" fit="cover" />
+                <el-image
+                  v-if="item.prop === 'T_product_img'"
+                  fit="cover"
+                  style="width: 50px; height: 50px"
+                  :src="row.T_product_img"
+                  :preview-src-list="[row.T_product_img]"
+                  :preview-teleported="true"
+                >
+                  <template #error>
+                    <div class="image-slot">
+                      <el-icon><Picture /></el-icon>
+                    </div>
+                  </template>
+                </el-image>
               </template>
             </el-table-column>
             <el-table-column v-if="item.ellipsis && item.prop === 'T_model'" v-bind="item">
@@ -330,7 +348,7 @@ const options = globalStore.GET_depotList
     </el-form>
     <InStorageSn ref="drawerSnRef" @onCount="autoGetCount" />
     <ReceiveUser ref="receiveUserdialog" @onUserInfo="getReceiveInfo" />
-    <InStorageProduct
+    <OutStockProduct
       ref="drawerProductRef"
       @ontableData="ProductselectionChange"
       @ontableDataAll="ProductSelectionAllChange"

+ 14 - 0
src/views/storehouse/outStock/index.scss

@@ -47,3 +47,17 @@
     }
   }
 }
+
+.image-slot .el-icon {
+  font-size: 30px;
+}
+.image-slot {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  background: var(--el-fill-color-light);
+  color: var(--el-text-color-secondary);
+  font-size: 30px;
+}

+ 1 - 10
src/views/storehouse/outStock/receiveUser.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { nextTick, ref } from 'vue'
+import { ref } from 'vue'
 import { User_List } from '@/api/user/index'
 // import Dialog from '@/components/dialog/Dialog.vue'
 import Drawer from '@/components/Drawer/index.vue'
@@ -35,14 +35,6 @@ const callbackDrawer = (done: () => void) => {
 
 const emit = defineEmits<{ (event: 'onUserInfo', value: any): void }>()
 
-/**
- * 配置高度
- */
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  return height - 45 - 74 - 40 - 12
-}
-
 const openDrawer = () => drawerRef.value?.openDrawer()
 defineExpose({
   openDrawer
@@ -61,7 +53,6 @@ defineExpose({
         :initParam="InitParam"
         :requestApi="User_List"
         layout="total, prev, pager, next"
-        :onResize="onResize"
       >
         <template #table-header>
           <el-row :gutter="20">

+ 31 - 9
src/views/storehouse/sales/ContractForm.vue

@@ -185,6 +185,7 @@ const initParam = reactive({
   page: 0,
   page_z: 20
 })
+const NameOptions = ref<any[]>([])
 const classOptions = ref<any[]>([])
 const modelOptions = ref<any[]>([])
 // 获取产品分类
@@ -281,14 +282,17 @@ const productColumns = [
 ]
 
 // 自动搜索
+const loading = ref(false)
 const autoSelect = ref('')
-let timeout: NodeJS.Timeout
-const querySearchAsync = async (queryString: string, cb: (arg: any) => void) => {
-  clearTimeout(timeout)
-  timeout = setTimeout(async () => {
+const querySearchAsync = async (queryString: string) => {
+  if (queryString) {
+    loading.value = true
+    globalStore.SET_isloading(true)
     const results = await getNameAsync(queryString)
-    cb(results)
-  }, 2000)
+    NameOptions.value = results
+    globalStore.SET_isloading(false)
+    loading.value = false
+  }
 }
 
 const getNameAsync = async (str: string): Promise<any> => {
@@ -303,7 +307,7 @@ const getNameAsync = async (str: string): Promise<any> => {
 }
 
 const handleSelect = (item: any) => {
-  // initParam.T_name = item.value
+  initParam.T_name = item
   getProductModelList()
 }
 
@@ -448,7 +452,7 @@ defineExpose({
                   </el-col>
                   <el-col :xl="7" :lg="8" :md="10" class="d-flex">
                     <span class="inline-flex items-center">产品名称:</span>
-                    <el-autocomplete
+                    <!-- <el-autocomplete
                       v-model="autoSelect"
                       clearable
                       :fetch-suggestions="querySearchAsync"
@@ -456,7 +460,25 @@ defineExpose({
                       :debounce="2000"
                       :trigger-on-focus="false"
                       @select="handleSelect"
-                    />
+                    /> -->
+                    <el-select
+                      v-model="autoSelect"
+                      filterable
+                      remote
+                      reserve-keyword
+                      placeholder="按产品名称搜索"
+                      remote-show-suffix
+                      :remote-method="querySearchAsync"
+                      :loading="loading"
+                      @change="handleSelect"
+                    >
+                      <el-option
+                        v-for="item in NameOptions"
+                        :key="item.value"
+                        :label="item.value"
+                        :value="item.value"
+                      />
+                    </el-select>
                   </el-col>
                   <el-col :xl="7" :lg="8" :md="12" class="d-flex">
                     <span class="inline-flex items-center">产品型号:</span>

+ 3 - 15
src/views/workAttendance/Leave.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { ElMessage } from 'element-plus'
 import { GlobalStore } from '@/stores/index'
-import { ref, onMounted, onUnmounted } from 'vue'
+import { ref } from 'vue'
 import { getFormatDuration } from '@/utils/common'
 import { UserFilled } from '@element-plus/icons-vue'
 import TableBase from '@/components/TableBase/index.vue'
@@ -61,18 +61,6 @@ const LeaveUser = async (T_State: number) => {
   }
 }
 
-let cardHeight = ref(0)
-const resize = () => {
-  const height = document.documentElement.clientHeight
-  cardHeight.value = height - 3 * 12 - 140 - 60
-}
-onMounted(() => {
-  resize()
-  window.onresize = resize
-})
-onUnmounted(() => {
-  window.onresize = null
-})
 const tableRowClassName = (data: any): any => {
   //判断是否相等,相同时改变背景颜色
   let user: any = undefined
@@ -113,7 +101,7 @@ const tableRowClassName = (data: any): any => {
       enter-active-class="animate__animated animate__bounceInDown"
     >
       <el-row class="h-100 f-1 margin-left-3" v-if="userInfo.Id">
-        <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-col :span="24" class="h-100" style="overflow: hidden; display: flex; flex-direction: column">
           <el-card class="m-b-3 b-show-0">
             <h3 class="title-user m-b-5">员工基本信息</h3>
             <div class="info-content">
@@ -123,7 +111,7 @@ const tableRowClassName = (data: any): any => {
               </div>
             </div>
           </el-card>
-          <el-card class="m-b-3 b-show-0" :style="{ height: cardHeight + 'px' }">
+          <el-card class="b-show-0" style="flex: 1">
             <el-descriptions title="请假申请" :column="1" size="large" border>
               <el-descriptions-item label="请假类型:"
                 ><el-text class="mx-1" type="primary">{{

+ 0 - 6
src/views/workAttendance/MyOvertime.vue

@@ -192,11 +192,6 @@ const duration = computed(() => {
   let num: number = Number(count.toFixed(2))
   return Math.round(num * 10) / 10
 })
-
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  return height / 2
-}
 </script>
 
 <template>
@@ -348,7 +343,6 @@ const onResize = () => {
         :initParam="approverInitParam"
         :requestApi="User_List"
         layout="total, prev, pager, next"
-        :onResize="onResize"
       >
         <template #table-header>
           <el-row :gutter="20">

+ 3 - 16
src/views/workAttendance/Overtime.vue

@@ -4,7 +4,7 @@ import { GlobalStore } from '@/stores/index'
 import { getFormatDuration } from '@/utils/common'
 import { UserFilled } from '@element-plus/icons-vue'
 import TableBase from '@/components/TableBase/index.vue'
-import { ref, nextTick, onMounted, onUnmounted } from 'vue'
+import { ref, nextTick } from 'vue'
 import { ColumnProps } from '@/components/TableBase/interface/index'
 import { Overtime_List, Overtime_Approval } from '@/api/workAttendance/index'
 
@@ -66,19 +66,6 @@ const LeaveUser = async (T_State: number) => {
   }
 }
 
-let cardHeight = ref(0)
-const resize = () => {
-  const height = document.documentElement.clientHeight
-  cardHeight.value = height - 3 * 12 - 140 - 60 - 6
-}
-onMounted(() => {
-  resize()
-  window.onresize = resize
-})
-onUnmounted(() => {
-  window.onresize = null
-})
-
 const tableRowClassName = (data: any): any => {
   //判断是否相等,相同时改变背景颜色
   let user: any = undefined
@@ -119,7 +106,7 @@ const tableRowClassName = (data: any): any => {
       enter-active-class="animate__animated animate__bounceInDown"
     >
       <el-row class="h-100 f-1 margin-left-3" v-if="userInfo.Id">
-        <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-col :span="24" class="h-100" style="overflow: hidden; display: flex; flex-direction: column">
           <el-card class="m-b-3 b-show-0">
             <h3 class="title-user m-b-5">员工基本信息</h3>
             <div class="info-content">
@@ -129,7 +116,7 @@ const tableRowClassName = (data: any): any => {
               </div>
             </div>
           </el-card>
-          <el-card class="m-b-3 b-show-0" :style="{ height: cardHeight + 'px' }">
+          <el-card class="m-b-3 b-show-0" style="flxe: 1">
             <el-row>
               <el-col :span="12"
                 ><div>

+ 3 - 45
src/views/workAttendance/RecordsFinance.vue

@@ -7,11 +7,9 @@ import TableBase from '@/components/TableBase/index.vue'
 import type { FormInstance, FormRules } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { ColumnProps } from '@/components/TableBase/interface/index'
-// import Pagination from '@/components/TableBase/components/Pagination.vue'
-import { ref, reactive, onMounted, computed, onUnmounted, nextTick } from 'vue'
+import { ref, reactive, computed, nextTick } from 'vue'
 import { Leave_Finance_List, Leave_Deduct, LeaveType_List } from '@/api/workAttendance/index'
 
-// const TableData = ref()
 const LeaveType = ref<any[]>([])
 const globalStore = GlobalStore()
 const ruleFormRef = ref<FormInstance>()
@@ -83,45 +81,6 @@ const userInfo = reactive({
   T_dept: '',
   T_post: ''
 })
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  return height - 140 - 74 - 60 - 4 * 12
-}
-
-// const getTableData = async () => {
-//   const res: any = await User_List({ ...initParam })
-//   TableData.value = res.Data.Data
-//   pageable.total = res.Data.Num
-// }
-// onMounted(async () => {
-//   await getTableData()
-// })
-// const pageable = reactive({
-//   pageNum: 1,
-//   pageSize: 10,
-//   total: 0,
-//   small: false,
-//   disabled: false
-// })
-// const handleSizeChange = (val: number) => {
-//   pageable.pageSize = val
-//   LeaveTableRef.value?.getTableList()
-// }
-
-// const handleCurrentChange = (val: number) => {
-//   pageable.pageNum = val
-//   LeaveTableRef.value?.getTableList()
-// }
-let clientHeight = ref(0)
-const onContentResize = () => {
-  const height = document.documentElement.clientHeight
-  clientHeight.value = height - 140 - 60 - 3 * 12
-}
-onMounted(() => {
-  onContentResize()
-  window.onresize = onContentResize
-})
-onUnmounted(() => (window.onresize = null))
 
 const tableRowClassName = (data: any): any => {
   //判断是否相等,相同时改变背景颜色
@@ -256,7 +215,7 @@ const searchHandle = () => TableRef.value?.searchTable()
       enter-active-class="animate__animated animate__bounceInDown"
     >
       <el-row class="h-100 f-1 margin-left-3" v-if="LeaveinitParam.T_uuid">
-        <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-col :span="24" class="h-100" style="overflow: hidden; display: flex; flex-direction: column">
           <el-card class="m-b-3">
             <h3 class="title m-b-5">员工基本信息</h3>
             <div class="info-content">
@@ -297,7 +256,7 @@ const searchHandle = () => TableRef.value?.searchTable()
               </el-col>
             </el-row>
           </el-card>
-          <div :style="{ height: clientHeight + 'px' }">
+          <div style="flex: 1">
             <TableBase
               v-if="LeaveinitParam.T_uuid"
               ref="LeaveTableRef"
@@ -305,7 +264,6 @@ const searchHandle = () => TableRef.value?.searchTable()
               :requestApi="Leave_Finance_List"
               :initParam="LeaveinitParam"
               layout="total,prev, pager, next"
-              :onResize="onResize"
               :displayHeader="true"
               :dataCallback="dataCallback"
             >

+ 0 - 55
src/views/workAttendance/records/Records.vue

@@ -10,7 +10,6 @@ import { ColumnProps } from '@/components/TableBase/interface/index'
 import { Leave_User_list, Overtime_User_list, Overtime_Stat } from '@/api/workAttendance/index'
 
 let clientHeight = ref(0)
-// const TableData = ref([])
 const globalStore = GlobalStore()
 const TableRef = ref<InstanceType<typeof TableBase> | null>(null)
 const leaveRef = ref<InstanceType<typeof TableBase> | null>(null)
@@ -105,34 +104,6 @@ const getSalaryParams = (row: any) => {
   overtimeRef.value && overtimeRef.value.searchTable()
   remainingTimeRef.value && remainingTimeRef.value.searchTable()
 }
-// 获取数据
-// const getTableData = async () => {
-//   const res: any = await User_List({ ...initParam })
-//   TableData.value = res.Data.Data
-//   pageable.total = res.Data.Num
-// }
-
-// onMounted(async () => {
-//   await getTableData()
-//   // let userInfoFirst: any = TableData.value[0]
-//   // userInfoAssign(userInfoFirst)
-// })
-// 分页
-// const pageable = reactive({
-//   pageNum: 1,
-//   pageSize: 10,
-//   total: 0,
-//   small: false,
-//   disabled: false
-// })
-// const handleSizeChange = (val: number) => {
-//   pageable.pageSize = val
-//   TableRef.value && TableRef.value.getTableList()
-// }
-// const handleCurrentChange = (val: number) => {
-//   pageable.pageNum = val
-//   TableRef.value && TableRef.value.getTableList()
-// }
 // 查看加班记录
 const viewOvertime = (row: any) => {
   url = row.T_prove_img
@@ -183,32 +154,6 @@ const searchHandle = () => TableRef.value?.searchTable()
 
 <template>
   <div class="records">
-    <!-- <div class="userTable">
-      <el-table
-        ref="TableRef"
-        class="h-100"
-        style="width: 100%; height: 100%; flex: 1"
-        :data="TableData"
-        @row-click="getSalaryParams"
-        :row-style="tableRowClassName"
-      >
-        <el-table-column
-          align="center"
-          v-for="item in columns"
-          :label="item.label"
-          :prop="item.prop"
-          :key="item.prop"
-        />
-      </el-table>
-      <Pagination
-        small
-        :pager-count="3"
-        layout="total, prev, pager, next"
-        :pageable="pageable"
-        :handleSizeChange="handleSizeChange"
-        :handleCurrentChange="handleCurrentChange"
-      />
-    </div> -->
     <div style="width: 290px" class="records-table">
       <TableBase
         ref="TableRef"