Przeglądaj źródła

Merge remote-tracking branch 'origin/master'

chenjun 10 miesięcy temu
rodzic
commit
2b68f1ffda

+ 8 - 1
client/src/components/PreviewImage/ImagePreview.ts

@@ -69,7 +69,7 @@ class ImagePreview {
   zoomHandler(num: number) {
     if (num === 1 && this.initZoom >= this.maxZoom) return
     if (num === -1 && this.initZoom <= this.minZoom) return
-    this.initZoom = this.initZoom + num * 0.5
+    this.initZoom = this.initZoom + num * 0.3
     this.setTranslate()
   }
   rotateHandler = (num: number) => {
@@ -93,6 +93,13 @@ class ImagePreview {
   setTranslate() {
     this.el.style.transform = `translate3d(${this.currentX}px, ${this.currentY}px, 0) scale(${this.initZoom}) rotate(${this.initRotate}deg)`
   }
+  setScaleToOriginal(type) {
+    this.el.style.width = type ? '100%' : 'auto'
+    this.currentX = this.currentY = 0
+    this.initZoom = 1
+    this.initRotate = 0
+    this.setTranslate()
+  }
 }
 
 export default ImagePreview

+ 37 - 3
client/src/components/PreviewImage/index.vue

@@ -1,7 +1,12 @@
 <template>
   <div class="preView">
     <div class="preview_box">
-      <div class="content">
+      <div class="header" v-if="closeabled">
+        <div class="icon_close">
+          <el-icon @click="closeHandler"><Close /></el-icon>
+        </div>
+      </div>
+      <div class="content" :style="{ height: closeabled ? 'calc(100% - 50px)' : '100%' }">
         <img ref="imgRef" :src="props.src" />
         <ul class="img_tool">
           <li @click="zoomHandler(1)">
@@ -16,6 +21,9 @@
           <li @click="rotateHandler(1)">
             <el-icon><RefreshRight /></el-icon>
           </li>
+          <li @click="scaleToOriginal">
+            <el-icon><ScaleToOriginal /></el-icon>
+          </li>
           <li @click="rotateHandler(0)">
             <el-icon><Refresh /></el-icon>
           </li>
@@ -38,13 +46,15 @@ const props = withDefaults(
     width: number
     height: number
     title: string
+    closeabled: boolean
   }>(),
   {
     visiable: true,
     fullScreen: false,
     width: 60,
     height: 90,
-    title: '资源预览'
+    title: '资源预览',
+    closeabled: false
   }
 )
 const emit = defineEmits<{
@@ -59,6 +69,14 @@ const zoomHandler = (num: number) => {
 const rotateHandler = (num: number) => {
   imagePreview.rotateHandler(num)
 }
+const scaleToOriginalType = ref<boolean>(false)
+const scaleToOriginal = () => {
+  scaleToOriginalType.value = !scaleToOriginalType.value
+  imagePreview.setScaleToOriginal(scaleToOriginalType.value)
+}
+const closeHandler = () => {
+  emit('close')
+}
 onMounted(() => {
   imagePreview = new ImagePreview()
   imagePreview.bindMouseEvent(imgRef.value as HTMLElement)
@@ -67,7 +85,7 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .preView {
-  z-index: 9999999;
+  z-index: 99999999;
   position: absolute;
   top: 0px;
   bottom: 0px;
@@ -83,6 +101,22 @@ onMounted(() => {
     margin: auto;
     padding: 20px;
     padding-top: 10px;
+    > .header {
+      height: 50px;
+      display: flex;
+      padding: 0px 20px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      > .icon_close {
+        font-size: 32px;
+        right: 20px;
+        color: #fff;
+        font-weight: bold;
+        cursor: pointer;
+        opacity: 0.6;
+      }
+    }
     > .content {
       height: 100%;
       padding: 0px 20px;

+ 19 - 0
client/src/views/OaSystem/marketCenter/contractSubOut/applyIndex.vue

@@ -39,6 +39,10 @@
             <img src="@/assets/imgs/OA/search.png" class="mr-8px" alt="" />
             查询</el-button
           >
+          <el-button type="primary" style="background: #3485ff" @click="exportHandle">
+            <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
+            导出</el-button
+          >
           <el-button type="danger" @click="processSubmit">
             <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
             发起签订</el-button
@@ -167,6 +171,7 @@ import { ElMessage } from 'element-plus'
 import { PageParam } from '@/interface/common'
 import { getAssetURL } from '@/utils/auth'
 import { openFlow, openProcessFlow } from '@/utils/flow'
+import download from '@/utils/download'
 import request from '@/config/axios'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import subscribe from '@/utils/Subscribe'
@@ -269,6 +274,20 @@ const handleCurrentChange = (pageNo: number) => {
   queryParams.pageNo = pageNo
   queryProjectListAjax()
 }
+const exportHandle = () => {
+  const sendData: {
+    applyType: number | null
+  } = {
+    ...queryParams
+  }
+  if (queryParams.applyType === -1) {
+    sendData['applyType'] = null
+  }
+  const urlApi = `/contract-apply/list/excel`
+  request.download({ url: urlApi, params: sendData }, '/business').then((blob) => {
+    download.excel(blob, '分/外包台账')
+  })
+}
 /**
  * 发起签订流程
  */

+ 46 - 14
client/src/views/OaSystem/marketCenter/projectCheckPass/index.vue

@@ -58,7 +58,7 @@
             </div>
           </div>
         </div>
-        <div class="tableBox">
+        <div :class="cTabIndex === -1 ? 'tableBox tableBox1' : 'tableBox tableBox2'">
           <div class="infoBox" v-if="cTabIndex !== -1">
             <ul>
               <li v-for="(item, index) in infoList" :key="index" class="mr-40px">
@@ -73,7 +73,6 @@
               stripe
               :data="tabList[cTabIndex]['data']"
               style="width: 100%; height: 100%"
-              :style="{ height: tableHeight + 'px' }"
               row-key="id"
               :header-cell-style="{
                 background: '#E5F0FB',
@@ -88,13 +87,13 @@
                 :show-overflow-tooltip="true"
                 prop="xmmc"
                 label="项目名称"
-                width="280"
+                width="320"
               />
               <el-table-column
                 :show-overflow-tooltip="true"
                 prop="xmbh"
                 label="项目编号"
-                width="280"
+                width="220"
               />
               <el-table-column label="项目经理" prop="xmjl" width="100" />
               <el-table-column label="项目状态" width="120" align="center">
@@ -120,7 +119,7 @@
               <el-table-column label="本年产值" width="140" align="center" prop="periodValue" />
               <el-table-column
                 label="剩余产值(元)"
-                width="160"
+                width="140"
                 align="center"
                 prop="surplusValue"
               />
@@ -142,7 +141,6 @@
               stripe
               :data="tabList"
               style="width: 100%; height: 100%"
-              :style="{ height: tableHeight + 'px' }"
               row-key="id"
               :header-cell-style="{
                 background: '#E5F0FB',
@@ -172,7 +170,6 @@ import { getAssetURL } from '@/utils/auth'
 defineOptions({ name: 'ProjectCheckPass' })
 
 const tableRef: any = ref(null)
-const tableHeight: any = ref(0)
 const message = useMessage()
 
 const cTabIndex = ref<number>(-1)
@@ -211,8 +208,7 @@ const infoList: any = reactive([
 const getProjectPassByList = async () => {
   const result: any = await request.get(
     {
-      url: '/projectReport/list-pass',
-      params: queryParams.value
+      url: '/projectReport/list-pass'
     },
     '/business'
   )
@@ -249,9 +245,30 @@ const switchTabHandle = (item, index) => {
   infoList[1]['num'] = item['num2']
   infoList[2]['num'] = item['num3']
   cTabIndex.value = index
+  queryParams.value = {
+    xmmc: '',
+    xmbh: '',
+    xmjl: ''
+  }
+  searchHandle()
 }
-const searchHandle = () => {
-  getProjectPassByList()
+const searchHandle = async () => {
+  const result: any = await request.get(
+    {
+      url: '/projectReport/list-pass',
+      params: queryParams.value
+    },
+    '/business'
+  )
+  if (result && result.length === 0) {
+    tabList.value[cTabIndex.value]['data'] = []
+    return
+  }
+  const nArr = result.filter((item) => {
+    return String(item['xmbm']) == tabList.value[cTabIndex.value]['xmbm']
+  })
+  console.log(nArr)
+  tabList.value[cTabIndex.value]['data'] = nArr
 }
 const currentYear = ref<string>('')
 const status = ref<number>()
@@ -291,7 +308,6 @@ const archiveProjectReportPeriodHandle = async () => {
   }
 }
 onMounted(() => {
-  tableHeight.value = tableRef.value.clientHeight
   getProjectPassByList()
 })
 </script>
@@ -308,7 +324,8 @@ onMounted(() => {
   display: flex;
   height: calc(100% - 40px);
   > .table_tab {
-    border: 1px solid #e5f0fb;
+    border: 1px solid #d9e2eb;
+    border-radius: 5px;
     > li {
       padding: 15px 30px;
       text-align: center;
@@ -324,7 +341,8 @@ onMounted(() => {
   }
   > .table_tab_container {
     margin-left: 15px;
-    border: 1px solid #e5f0fb;
+    border-radius: 5px;
+    border: 1px solid #d9e2eb;
     width: 0;
     height: 100%;
     flex: 1;
@@ -333,6 +351,20 @@ onMounted(() => {
     > .searchBox {
       margin: 15px 0px;
     }
+    > .tableBox {
+      &.tableBox1 {
+        height: calc(100% - 20px);
+        > .table {
+          height: calc(100% - 20px);
+        }
+      }
+      &.tableBox2 {
+        height: calc(100% - 120px);
+        > .table {
+          height: calc(100% - 50px);
+        }
+      }
+    }
   }
 }
 .infoBox {

+ 100 - 27
client/src/views/OaSystem/mineCenter/rightChild/index.ts

@@ -7,28 +7,75 @@ export const formConfigList = [
       {
         name: 'birthday',
         title: '出生日期',
-        type: 'time'
+        type: 'time',
+        validate: [
+          {
+            required: true,
+            message: '出生日期不能为空',
+            trigger: 'blur'
+          }
+        ]
       },
       {
-        name: 'hkxz',
-        title: '户口性质',
+        name: 'nation',
+        title: '民族',
+        type: 'select',
+        validate: [
+          {
+            required: true,
+            message: '民族不能为空',
+            trigger: 'blur'
+          }
+        ]
+      },
+      {
+        name: 'jg',
+        title: '籍贯',
+        validate: [
+          {
+            required: true,
+            message: '籍贯不能为空',
+            trigger: 'blur'
+          }
+        ]
+      },
+      {
+        name: 'zzmm',
+        title: '政治面貌',
         type: 'select',
+        validate: [
+          {
+            required: true,
+            message: '政治面貌不能为空',
+            trigger: 'blur'
+          }
+        ],
         options: [
           {
             value: 1,
-            label: '城镇'
+            label: '群众'
           },
           {
             value: 2,
-            label: '农村'
+            label: '团员'
+          },
+          {
+            value: 3,
+            label: '党员'
           }
         ]
       },
-
       {
         name: 'hyzk',
         title: '婚姻状况',
         type: 'select',
+        validate: [
+          {
+            required: true,
+            message: '婚姻状况不能为空',
+            trigger: 'blur'
+          }
+        ],
         options: [
           {
             value: 1,
@@ -45,41 +92,64 @@ export const formConfigList = [
         ]
       },
       {
-        name: 'nation',
-        title: '民族',
-        type: 'select'
+        name: 'jkzk',
+        title: '健康状况',
+        type: 'select',
+        validate: [
+          {
+            required: true,
+            message: '健康状况不能为空',
+            trigger: 'blur'
+          }
+        ],
+        options: []
       },
       {
         name: 'cjgzsj',
         title: '参加工作时间',
-        type: 'time'
+        type: 'time',
+        validate: [
+          {
+            required: true,
+            message: '参加工作时间不能为空',
+            trigger: 'blur'
+          }
+        ]
       },
       {
-        name: 'zzmm',
-        title: '政治面貌',
-        type: 'select',
-        options: [
-          {
-            value: 1,
-            label: '群众'
-          },
-          {
-            value: 2,
-            label: '团员'
-          },
+        name: 'xjzdz',
+        title: '现居住地址',
+        validate: [
           {
-            value: 3,
-            label: '党员'
+            required: true,
+            message: '现居住地址不能为空',
+            trigger: 'blur'
           }
         ]
       },
       {
-        name: 'xjzdz',
-        title: '现居住地址'
+        name: 'hkxz',
+        title: '户口性质',
+        type: 'select',
+        validate: [
+          {
+            required: true,
+            message: '户口性质不能为空',
+            trigger: 'blur'
+          }
+        ],
+        options: []
       },
       {
         name: 'hkszd',
-        title: '户口所在地'
+        title: '户口所在地',
+        validate: [
+          {
+            required: true,
+            message: '户口所在地不能为空',
+            trigger: 'blur'
+          }
+        ]
       },
       {
         name: 'xqah',
@@ -216,6 +286,8 @@ const getDictList = () => {
   const hyzk = getDictOptions('hy_type')
   // 户口性质
   const hkxz = getDictOptions('hk_type')
+  // 健康状况
+  const jkzk = getDictOptions('health')
   // 政治面貌
   const zzmm = getDictOptions('polity_type')
   // 能力等级
@@ -231,6 +303,7 @@ const getDictList = () => {
   dictOptionsObj.nation = nation
   dictOptionsObj.hyzk = hyzk
   dictOptionsObj.hkxz = hkxz
+  dictOptionsObj.jkzk = jkzk
   dictOptionsObj.zzmm = zzmm
   dictOptionsObj.nldj = nldj
   dictOptionsObj.zgxl = zgxl

+ 35 - 6
client/src/views/OaSystem/mineCenter/rightChild/wdda.vue

@@ -8,7 +8,7 @@ import WorkTable from '../../personnelManagement/ygdaPage/WorkTable.vue'
 import FamilyTable from '../../personnelManagement/ygdaPage/familyTable.vue'
 import SchoolTable from '../../personnelManagement/ygdaPage/SchoolTable.vue'
 import { cloneDeep } from 'lodash-es'
-import { FormInstance } from 'element-plus'
+import { FormInstance, FormRules } from 'element-plus'
 import moment from 'moment'
 
 /**
@@ -49,6 +49,16 @@ const tempData = ref({})
 const editDetail = () => {
   isDisabled.value = false
   tempData.value = cloneDeep(data.value)
+
+  formConfigList.forEach((item) => {
+    if (item.children) {
+      item.children.forEach((cItem) => {
+        if (cItem.validate) {
+          rules.value[cItem['name']] = cItem.validate
+        }
+      })
+    }
+  })
   // router.push({
   //   path: '/staffDetail',
   //   query: { id: data.value.userId, type: 'edit' }
@@ -58,6 +68,8 @@ const cancleEdit = () => {
   isDisabled.value = true
   data.value = cloneDeep(tempData.value)
   tempData.value = {}
+  rules.value = {}
+  formRef.value.resetFields()
 }
 // const saveDetail = () => {
 //   isDisabled.value = true
@@ -82,11 +94,21 @@ const { mutate: addUserMutate } = useMutation({
     })
   }
 })
-
+interface RuleForm {
+  birthday: string
+  nation: string
+  jg: string
+  zzmm: string
+  hyzk: string
+  jkzk: boolean
+  cjgzsj: string
+  xjzdz: string[]
+  hkxz: string
+  hkszd: string
+}
+const rules = ref<FormRules<RuleForm>>({})
 /**表单保存 */
 const submitForm = (formEl: FormInstance | undefined) => {
-  // console.log('formData.value', formData.value)
-  // return
   if (!formEl) return
   formEl.validate((valid) => {
     if (valid) {
@@ -121,7 +143,14 @@ const saveSchoolList = (newData: any[]) => {
       <!-- <el-button type="primary" @click="saveDetail" v-if="!isDisabled">保存</el-button> -->
     </div>
 
-    <el-form ref="formRef" v-if="data" :model="data" label-width="150px" :disabled="isDisabled">
+    <el-form
+      ref="formRef"
+      v-if="data"
+      :model="data"
+      label-width="150px"
+      :disabled="isDisabled"
+      :rules="rules"
+    >
       <div class="my-portrait-item" v-for="(item, index) in formConfigList" :key="index">
         <div class="title">
           <i></i>
@@ -238,7 +267,7 @@ const saveSchoolList = (newData: any[]) => {
 
       li {
         width: 50%;
-
+        padding: 2px 0px;
         span {
           display: inline-block;
           font-size: 16px;

+ 33 - 1
client/src/views/OaSystem/oaLayout/menus.vue

@@ -35,6 +35,12 @@
           <el-menu-item @click="menuClick(item, index)" :index="String(index)">
             <Icon :icon="item.icon" />
             <span>{{ item.name }}</span>
+            <span
+              class="project_corner"
+              v-if="['个人中心'].includes(item.name) && projectCorner > 0"
+            >
+              {{ projectCorner }}
+            </span>
           </el-menu-item>
           <MenusActive :menuData="menuData" v-if="mouseenterIndex == index" />
         </div>
@@ -55,6 +61,7 @@ import { useQuery } from '@tanstack/vue-query'
 import { getRecordsDetail } from '@/api/oa/staffRecords'
 import { getAttendCount } from '@/api/oa/index'
 import subscribe from '@/utils/Subscribe'
+import request from '@/config/axios'
 
 defineOptions({ name: 'Header' })
 const { t } = useI18n()
@@ -76,7 +83,18 @@ const userId = user.user.id // 当前登录的编号
 const { data } = useQuery(['fetch-staff-detail-left', userId], async () => {
   return await getRecordsDetail({ userId })
 })
-
+//个人中心角标(项目产值待审核)
+const projectCorner = ref<number>(0)
+const getProjectCheckNum = async () => {
+  const result: any = await request.get(
+    {
+      url: '/projectReport/check-num'
+    },
+    '/business'
+  )
+  projectCorner.value = result
+}
+getProjectCheckNum()
 /***
  * 根据用户ID获取待办角标
  */
@@ -258,6 +276,20 @@ onMounted(() => {
     }
   }
 
+  .project_corner {
+    background-color: #f00;
+    position: absolute;
+    right: 25px;
+    top: 10px;
+    font-size: 12px;
+    color: #fff;
+    width: 22px;
+    height: 22px;
+    line-height: 22px;
+    border-radius: 50%;
+    font-size: 14px !important;
+    text-align: center;
+  }
   .menus-btns {
     width: 100%;
     height: 40px;

+ 44 - 36
client/src/views/OaSystem/projectCenter/purchaseContract/deptContract.vue

@@ -75,9 +75,9 @@
         </el-select>
       </div>
       <div class="form form-time-range">
-        <span class="formSpan">签订时间:</span>
+        <span class="formSpan">拿出时间:</span>
         <el-date-picker
-          v-model="qdsjObj"
+          v-model="contractOnObj"
           type="daterange"
           unlink-panels
           range-separator="To"
@@ -86,9 +86,9 @@
         />
       </div>
       <div class="form form-time-range">
-        <span class="formSpan" style="width: 120px">履约到期时间:</span>
+        <span class="formSpan">拿回时间:</span>
         <el-date-picker
-          v-model="dueTimeObj"
+          v-model="contractOffObj"
           type="daterange"
           unlink-panels
           range-separator="To"
@@ -231,6 +231,10 @@ const queryParams = reactive<{
   signTimeEnd: string
   dueTimeStart: string
   dueTimeEnd: string
+  contractOnStart: string
+  contractOnEnd: string
+  contractOffStart: string
+  contractOffEnd: string
   deptId: string
 }>({
   contractNumber: '',
@@ -247,10 +251,14 @@ const queryParams = reactive<{
   signTimeEnd: '',
   dueTimeStart: '',
   dueTimeEnd: '',
+  contractOnStart: '',
+  contractOnEnd: '',
+  contractOffStart: '',
+  contractOffEnd: '',
   deptId: deptId
 })
-const qdsjObj = ref()
-const dueTimeObj = ref()
+const contractOnObj = ref()
+const contractOffObj = ref()
 
 const handleCurrentChange = (pageNo: number) => {
   queryParams.pageNo = pageNo
@@ -258,19 +266,19 @@ const handleCurrentChange = (pageNo: number) => {
   queryContractListAjax()
 }
 const exportHandle = async () => {
-  if (qdsjObj.value && qdsjObj.value.length > 0) {
-    queryParams.signTimeStart = moment(qdsjObj.value[0]).format('YYYY-MM-DD')
-    queryParams.signTimeEnd = moment(qdsjObj.value[1]).format('YYYY-MM-DD')
+  if (contractOnObj.value && contractOnObj.value.length > 0) {
+    queryParams.contractOnStart = moment(contractOnObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOnEnd = moment(contractOnObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.signTimeStart = ''
-    queryParams.signTimeEnd = ''
+    queryParams.contractOnStart = ''
+    queryParams.contractOnEnd = ''
   }
-  if (dueTimeObj.value && dueTimeObj.value.length > 0) {
-    queryParams.dueTimeStart = moment(dueTimeObj.value[0]).format('YYYY-MM-DD')
-    queryParams.dueTimeEnd = moment(dueTimeObj.value[1]).format('YYYY-MM-DD')
+  if (contractOffObj.value && contractOffObj.value.length > 0) {
+    queryParams.contractOffStart = moment(contractOffObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOffEnd = moment(contractOffObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.dueTimeStart = ''
-    queryParams.dueTimeEnd = ''
+    queryParams.contractOffStart = ''
+    queryParams.contractOffEnd = ''
   }
   exportHandleCommon(request, queryParams, '部门合同台账')
 }
@@ -285,19 +293,19 @@ const tableRef = ref<any>(null)
 const scrollLeft = ref<number>(0)
 const queryContractListAjax = async (): Promise<void> => {
   const urlApi = `/contract/page-dept`
-  if (qdsjObj.value && qdsjObj.value.length > 0) {
-    queryParams.signTimeStart = moment(qdsjObj.value[0]).format('YYYY-MM-DD')
-    queryParams.signTimeEnd = moment(qdsjObj.value[1]).format('YYYY-MM-DD')
+  if (contractOnObj.value && contractOnObj.value.length > 0) {
+    queryParams.contractOnStart = moment(contractOnObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOnEnd = moment(contractOnObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.signTimeStart = ''
-    queryParams.signTimeEnd = ''
+    queryParams.contractOnStart = ''
+    queryParams.contractOnEnd = ''
   }
-  if (dueTimeObj.value && dueTimeObj.value.length > 0) {
-    queryParams.dueTimeStart = moment(dueTimeObj.value[0]).format('YYYY-MM-DD')
-    queryParams.dueTimeEnd = moment(dueTimeObj.value[1]).format('YYYY-MM-DD')
+  if (contractOffObj.value && contractOffObj.value.length > 0) {
+    queryParams.contractOffStart = moment(contractOffObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOffEnd = moment(contractOffObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.dueTimeStart = ''
-    queryParams.dueTimeEnd = ''
+    queryParams.contractOffStart = ''
+    queryParams.contractOffEnd = ''
   }
   const sendData = {
     ...queryParams,
@@ -321,19 +329,19 @@ const contractListCalc = ref({
 })
 const queryContractListCalc = async (): Promise<void> => {
   const urlApi = `/contract/list/calc-dept`
-  if (qdsjObj.value && qdsjObj.value.length > 0) {
-    queryParams.signTimeStart = moment(qdsjObj.value[0]).format('YYYY-MM-DD')
-    queryParams.signTimeEnd = moment(qdsjObj.value[1]).format('YYYY-MM-DD')
+  if (contractOnObj.value && contractOnObj.value.length > 0) {
+    queryParams.contractOnStart = moment(contractOnObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOnEnd = moment(contractOnObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.signTimeStart = ''
-    queryParams.signTimeEnd = ''
+    queryParams.contractOnStart = ''
+    queryParams.contractOnEnd = ''
   }
-  if (dueTimeObj.value && dueTimeObj.value.length > 0) {
-    queryParams.dueTimeStart = moment(dueTimeObj.value[0]).format('YYYY-MM-DD')
-    queryParams.dueTimeEnd = moment(dueTimeObj.value[1]).format('YYYY-MM-DD')
+  if (contractOffObj.value && contractOffObj.value.length > 0) {
+    queryParams.contractOffStart = moment(contractOffObj.value[0]).format('YYYY-MM-DD')
+    queryParams.contractOffEnd = moment(contractOffObj.value[1]).format('YYYY-MM-DD')
   } else {
-    queryParams.dueTimeStart = ''
-    queryParams.dueTimeEnd = ''
+    queryParams.contractOffStart = ''
+    queryParams.contractOffEnd = ''
   }
   const sendData = {
     ...queryParams,

+ 108 - 0
client/src/views/system/user/UserForm.vue

@@ -84,6 +84,39 @@
           </el-form-item>
         </el-col>
       </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="签名">
+            <el-upload
+              class="avatar-uploader"
+              :headers="headers"
+              :action="uploadUrl"
+              :before-upload="beforeUpload"
+              :on-success="handleAvatarSuccess"
+              list-type="picture-card"
+              v-model:file-list="fileList"
+            >
+              <el-icon><Plus /></el-icon>
+              <template #file="{ file }">
+                <div>
+                  <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
+                  <span class="el-upload-list__item-actions">
+                    <span
+                      class="el-upload-list__item-preview"
+                      @click="handlePictureCardPreview(file)"
+                    >
+                      <el-icon><zoom-in /></el-icon>
+                    </span>
+                    <span class="el-upload-list__item-delete" @click="handleRemove(file)">
+                      <el-icon><Delete /></el-icon>
+                    </span>
+                  </span>
+                </div>
+              </template>
+            </el-upload>
+          </el-form-item>
+        </el-col>
+      </el-row>
       <el-row>
         <el-col :span="24">
           <el-form-item label="备注">
@@ -97,6 +130,12 @@
       <el-button @click="dialogVisible = false">取 消</el-button>
     </template>
   </Dialog>
+  <preview-image
+    @close="preVisiable = false"
+    :closeabled="true"
+    v-if="preVisiable"
+    :src="formData?.signatureUrl"
+  />
 </template>
 <script lang="ts" setup>
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -105,6 +144,10 @@ import { defaultProps, handleTree } from '@/utils/tree'
 import * as PostApi from '@/api/system/post'
 import * as DeptApi from '@/api/system/dept'
 import * as UserApi from '@/api/system/user'
+import { getAccessToken, getTenantId } from '@/utils/auth'
+import * as FileApi from '@/api/infra/file'
+import PreviewImage from '@/components/PreviewImage/index.vue'
+import { form } from '@/views/OaSystem/personnelManagement/rchbkPage/common'
 
 defineOptions({ name: 'SystemUserForm' })
 
@@ -127,6 +170,7 @@ const formData = ref({
   sex: undefined,
   postIds: [],
   remark: '',
+  signatureUrl: '',
   status: CommonStatusEnum.ENABLE,
   roleIds: []
 })
@@ -165,6 +209,13 @@ const open = async (type: string, id?: number) => {
     formLoading.value = true
     try {
       formData.value = await UserApi.getUser(id)
+      if (formData.value.signatureUrl) {
+        fileList.value = [
+          {
+            url: formData.value.signatureUrl
+          }
+        ]
+      }
     } finally {
       formLoading.value = false
     }
@@ -220,8 +271,65 @@ const resetForm = () => {
     postIds: [],
     remark: '',
     status: CommonStatusEnum.ENABLE,
+    signatureUrl: '',
     roleIds: []
   }
   formRef.value?.resetFields()
 }
+//用户签名
+const preVisiable = ref<boolean>(false)
+const fileList = ref<any>([])
+const uploadUrl = ref<string>('')
+const headers = {
+  Accept: '*',
+  Authorization: 'Bearer ' + getAccessToken(),
+  'tenant-id': getTenantId()
+}
+const beforeUpload = (file) => {
+  const arr = file.name.split('.')
+  uploadUrl.value +=
+    import.meta.env.VITE_BASE_URL +
+    import.meta.env.VITE_API_URL +
+    import.meta.env.VITE_UPLOAD_URL +
+    `?clientId=5&path=${formData.value.username}.${arr[arr.length - 1]}`
+  return true
+}
+const handleAvatarSuccess = (result, file) => {
+  if (result && result['data']) {
+    formData.value.signatureUrl = result['data']
+  }
+  fileList.value = [file]
+}
+const handlePictureCardPreview = (file) => {
+  preVisiable.value = true
+}
+const handleRemove = (file) => {
+  fileList.value = []
+  formData.value.signatureUrl = ''
+}
 </script>
+
+<style lang="scss">
+.avatar-uploader .el-upload {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  width: 108px;
+  height: 108px;
+  transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+  border-color: var(--el-color-primary);
+}
+.el-upload-list__item {
+  font-size: 28px;
+  color: #8c939d;
+  text-align: center;
+  border: 1px solid #f00;
+  width: 108px !important;
+  height: 108px !important;
+}
+</style>