Explorar o código

企业-职位编辑

Xiao_123 hai 11 meses
pai
achega
a140b7d388

+ 2 - 5
src/components/AreaSelect/index.vue

@@ -60,10 +60,7 @@ const props = defineProps({
 const selectItems = ref([])
 // 回显
 if (props.select.length) {
-  selectItems.value = props.select.map(e => {
-    if (typeof e === 'string') e = Number(e)
-    return e
-  })
+  selectItems.value = props.select
 }
 
 // 点击
@@ -82,7 +79,7 @@ const handleAreaClick = async (val, preName) => {
 let items = ref([])
 getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
   data = data?.length && data || []
-  const chinaTreeData = data.filter(e => Number(e.id) === 1)
+  const chinaTreeData = data.filter(e => e.id === '1')
   if (!chinaTreeData.length) return
   items.value = chinaTreeData[0].children
 })

+ 5 - 1
src/components/Empty/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="d-flex align-center flex-column white-bgc py-5 elevation-1" style="border-radius: 10px;">
+  <div :class="['d-flex', 'align-center', 'flex-column', 'white-bgc', 'py-5', {'elevation-1': elevation}]" style="border-radius: 10px;">
     <v-img :width="width" :height="height" cover src="../../assets/noData.png"></v-img>
     <div class="message">{{ message }}</div>
   </div>
@@ -19,6 +19,10 @@ defineProps({
   message: {
     type: String,
     default: '暂无数据'
+  },
+  elevation: {
+    type: Boolean,
+    default: true
   }
 })
 </script>

+ 6 - 1
src/components/FormUI/TextInput/index.vue

@@ -27,6 +27,7 @@
       @update:modelValue="modelValueUpDate"
       @click:append="appendClick"
       @click:append-inner="appendInnerClick"
+      @keyup.enter="handleKeyup"
     ></v-text-field>
   </div>
 </template>
@@ -35,7 +36,7 @@ import { defineEmits, ref, watch } from 'vue';
 defineOptions({ name:'FormUI-v-text-field'})
 
 const props = defineProps({item: Object, modelValue: [String, Number]})
-const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick'])
+const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter'])
 const item = props.item
 const value = ref(props.modelValue)
 
@@ -56,6 +57,10 @@ const appendInnerClick = () => {
   emit('appendInnerClick', value.value)
 }
 
+const handleKeyup = () => {
+  emit('enter', value.value)
+}
+
 const handleWheel = (event, item) => {
   if (item.type !== 'number') return
   event.preventDefault()

+ 12 - 2
src/components/FormUI/autocomplete/index.vue

@@ -27,15 +27,25 @@
 </template>
 <script setup>
 import { debounce } from 'lodash'
-import { ref, defineEmits } from 'vue';
+import { ref, defineEmits, watch } from 'vue';
 defineOptions({ name:'FormUI-v-autocomplete'})
 
 const props = defineProps({item: Object, modelValue: [String, Number]})
+
+const value = ref()
+watch(
+  () => props.modelValue,
+  (newVal) => {
+    value.value = newVal
+  },
+  { immediate: true },
+  { deep: true }
+)
+
 const emit = defineEmits(['update:modelValue', 'change', 'search'])
 const item = props.item
 const searchDebouncedTime = item.searchDebouncedTime === 0 ? 0 : 500
 
-const value = ref(props.modelValue)
 const modelValueUpDate = (val) => {
   value.value = val
   emit('update:modelValue', value.value)

+ 10 - 2
src/locales/en.js

@@ -15,6 +15,8 @@ export default {
     submittedSuccessfully: 'Submitted successful',
     switchSuccessful: 'Switch successful',
     addMsg: 'New successfully added',
+    editSuccessMsg: 'Edit successful',
+    publishSuccessMsg: 'Successfully published',
     delMsg: 'Delete successful',
     uploadPictures: 'upload pictures',
     uploadSucMsg: 'Upload successful',
@@ -27,7 +29,10 @@ export default {
     home: 'Home',
     position: 'Position',
     company: 'Company',
-    operationSuccessful: 'Operation successful'
+    operationSuccessful: 'Operation successful',
+    close: 'Close',
+    suspend: 'Suspend',
+    release: 'Release'
   },
   sys: {
     api: {
@@ -118,7 +123,10 @@ export default {
     similarPosition: 'Similar positions',
     address: 'Work address',
     positionCollection: 'Position Collection',
-    companyCollection: 'Company Collection'
+    companyCollection: 'Company Collection',
+    recruitmentStatistics: 'Recruitment statistics',
+    positionInformation: 'Basic information of the position',
+    requirementDesc: 'We will accurately recommend suitable talents to you based on the following conditions. Please fill in as much detail as possible'
   },
   enterprise: {
     companyInfo: 'Company Information',

+ 10 - 2
src/locales/zh-CN.js

@@ -16,6 +16,8 @@ export default {
     switchSuccessful: '切换成功',
     addMsg: '新增成功',
     delMsg: '删除成功',
+    editSuccessMsg: '编辑成功',
+    publishSuccessMsg: '发布成功',
     uploadPictures: '上传图片',
     uploadSucMsg: '上传成功',
     uploadErrMsg: '上传失败',
@@ -27,7 +29,10 @@ export default {
     home: '首页',
     position: '职位',
     company: '公司',
-    operationSuccessful: '操作成功'
+    operationSuccessful: '操作成功',
+    close: '关闭',
+    suspend: '暂停',
+    release: '发布'
   },
   sys: {
     api: {
@@ -118,7 +123,10 @@ export default {
     similarPosition: '相似职位',
     address: '工作地址',
     positionCollection: '职位收藏',
-    companyCollection: '公司收藏'
+    companyCollection: '公司收藏',
+    recruitmentStatistics: '招聘统计',
+    positionInformation: '职位基本信息',
+    requirementDesc: '我们将通过以下条件,为您精确推荐合适的人才,请尽量详细填写'
   },
   enterprise: {
     companyInfo: '公司信息',

+ 8 - 0
src/router/modules/enterprise.js

@@ -67,6 +67,14 @@ const enterprise = [
           title: '新增职位'
         },
         component: () => import('@/views/enterprise/positionManagement/components/add.vue')
+      },
+      {
+        path: '/enterprise/position/edit',
+        show: true,
+        meta: {
+          title: '职位编辑'
+        },
+        component: () => import('@/views/enterprise/positionManagement/components/add.vue')
       }
     ]
   },

+ 32 - 11
src/views/enterprise/positionManagement/components/add.vue

@@ -11,13 +11,13 @@
           <div>
             <h2 class="mt-n1 headline font-weight-regular">{{ val.title }}</h2>
             <div class="mb-4 desc">{{ val.desc }}</div>
-            <component class="mt-10" :is="val.path" :ref="val.ref"></component>
+            <component class="mt-10" :is="val.path" :ref="val.ref" :itemData="itemData"></component>
           </div>
         </v-timeline-item>
       </v-timeline>
       <div class="text-center mb">
-        <v-btn class="half-button mr-3" color="primary" variant="outlined" @click="handleCancel">取 消</v-btn>
-        <v-btn class="half-button" color="primary" @click="handleSave">发 布</v-btn>
+        <v-btn class="half-button mr-3" color="primary" variant="outlined" @click="handleCancel">{{ $t('common.cancel') }}</v-btn>
+        <v-btn class="half-button" color="primary" @click="handleSave">{{ $t('common.release') }}</v-btn>
       </div>
     </v-card>
   </div>
@@ -26,29 +26,34 @@
 <script setup>
 defineOptions({ name: 'enterprise-position-add'})
 import { ref } from 'vue'
-import { useRouter } from 'vue-router'
-import { saveJobAdvertised } from '@/api/position'
+import { useRouter, useRoute } from 'vue-router'
+import { dealDictArrayData } from '@/views/recruit/position/components/dict'
+import { saveJobAdvertised, getJobAdvertisedList } from '@/api/position'
 import baseInfo from './baseInfo.vue'
 import jobRequirements from './jobRequirements.vue'
 import Snackbar from '@/plugins/snackbar'
+import { useI18n } from '@/hooks/web/useI18n'
 
+const { t } = useI18n()
+const route = useRoute()
 const router = useRouter()
 const baseInfoRef = ref()
 const jobRequirementsRef = ref()
+const itemData = ref({})
 const list = [
   {
     color: '#00897B',
     icon: 'mdi-numeric-1',
-    title: '职位基本信息',
-    desc: '职位发布成功后,招聘类型、职位名称、职位类型、工作城市,将无法修改',
+    title: t('position.positionInformation'),
+    desc: '',
     path: baseInfo,
     ref: baseInfoRef
   },
   {
     color: 'indigo-lighten-2',
     icon: 'mdi-numeric-2',
-    title: '职位要求',
-    desc: '我们将通过以下条件,为您精确推荐合适的人才,请尽量详细填写',
+    title: t('position.jobRequirements'),
+    desc: t('position.requirementDesc'),
     path: jobRequirements,
     ref: jobRequirementsRef
   }
@@ -56,6 +61,7 @@ const list = [
 
 // 取消
 const handleCancel = () => {
+  itemData.value = {}
   router.push('/enterprise/position')
 }
 
@@ -65,9 +71,24 @@ const handleSave = async () => {
   const requirement = await jobRequirementsRef.value[0].getQuery()
   if (!baseInfo || !requirement) return Snackbar.warning('请将信息填写完整')
   const query = Object.assign(baseInfo, requirement)
+// 有id则为编辑
+  if (route.query && route.query.id) query.id = route.query.id
   await saveJobAdvertised(query)
-  Snackbar.success('发布成功')
-  router.push('/enterprise/position')
+  Snackbar.success(route.query.id ? t('common.editSuccessMsg') : t('common.publishSuccessMsg'))
+  handleCancel()
+}
+
+// 获取编辑的职位详情
+const getPositionDetail = async (id) => {
+  const { list } = await getJobAdvertisedList({ pageSize: 1, pageNo: 1, id })
+  if (!list.length) return
+  const arr = dealDictArrayData([], list)
+  itemData.value = arr[0]
+}
+
+// 有id为编辑
+if (route.query && route.query.id) {
+  if (route.query.id) getPositionDetail(route.query.id)
 }
 </script>
 

+ 37 - 21
src/views/enterprise/positionManagement/components/baseInfo.vue

@@ -8,18 +8,18 @@
         </div>
       </template>
       <template #positionId="{ item }">
-          <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
-            <template v-slot:activator="{  props }">
-              <textUI
-                :modelValue="item.value"
-                :item="item"
-                v-bind="props"
-                style="position: relative;"
-              ></textUI>
-            </template>
-            <jobTypeCard class="jobTypeCardBox" :select="[query.positionId].filter(Boolean)" :isSingle="true" @handleJobClick="handleJobClickItem"></jobTypeCard>
-          </v-menu>
-        </template>
+        <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
+          <template v-slot:activator="{  props }">
+            <textUI
+              :modelValue="item.value"
+              :item="item"
+              v-bind="props"
+              style="position: relative;"
+            ></textUI>
+          </template>
+          <jobTypeCard class="jobTypeCardBox" :select="[query.positionId].filter(Boolean)" :isSingle="true" @handleJobClick="handleJobClickItem"></jobTypeCard>
+        </v-menu>
+      </template>
     </CtForm>
   </div>
 </template>
@@ -27,22 +27,18 @@
 <script setup>
 defineOptions({ name: 'position-add-baseInfo'})
 import CtForm from '@/components/CtForm'
-import { reactive, ref, defineExpose } from 'vue'
+import { reactive, ref, defineExpose, watch } from 'vue'
 import textUI from '@/components/FormUI/TextInput'
 import jobTypeCard from '@/components/jobTypeCard'
 
+const props = defineProps({
+  itemData: Object
+})
+
 const formPageRef = ref()
 let query = reactive({})
 const items = ref({
   options: [
-    {
-      type: 'text',
-      key: 'enterpriseName',
-      disabled: true,
-      value: '辞图科技·计算机软件·广州辞图科技有限公司',
-      label: '公司名称 *',
-      noParam: true
-    },
     {
       type: 'text',
       key: 'name',
@@ -54,6 +50,7 @@ const items = ref({
       slotName: 'positionId',
       key: 'positionId',
       value: '',
+      labelKey: 'positionName',
       label: '职位类型 *',
       noParam: true,
       rules: [v => !!v || '请选择职位类型']
@@ -103,6 +100,25 @@ const setValue = (key, value) => {
   items.value.options.find(e => e.key === key).value = value
 }
 
+// 编辑回显
+watch(
+  () => props.itemData,
+  (val) => {
+    if (!Object.keys(val).length) return
+    items.value.options.forEach(e => {
+      if (e.labelKey) {
+        query[e.key] = val[e.key]
+        e.value = val[e.labelKey]
+        return
+      }
+      if (e.noParam) return
+      e.value = val[e.key]
+    })
+  },
+  { immediate: true },
+  { deep: true }
+)
+
 // 职位类型
 const handleJobClickItem = (list, name) => {
   if (!list.length) {

+ 12 - 4
src/views/enterprise/positionManagement/components/item.vue

@@ -37,14 +37,14 @@
         <div>刷新时间:2024.5.30(90天后到期)</div>
         <div class="d-flex">
           <div v-if="tab === 1">
-            <span class="cursor-pointer">关闭</span>
+            <span class="cursor-pointer">{{ $t('common.close') }}</span>
             <span class="lines"></span>
-            <span class="cursor-pointer">暂停</span>
+            <span class="cursor-pointer">{{ $t('common.suspend') }}</span>
           </div>
           <div class="ml-10">
-            <span class="cursor-pointer">招聘统计</span>
+            <span class="cursor-pointer">{{ $t('position.recruitmentStatistics') }}</span>
             <span class="lines"></span>
-            <span class="cursor-pointer">编辑</span>
+            <span class="cursor-pointer" @click="handleEdit(val)">{{ $t('common.edit') }}</span>
           </div>
         </div>
       </div>
@@ -54,6 +54,8 @@
 
 <script setup>
 defineOptions({ name: 'enterprise-position-item'})
+import { useRouter } from 'vue-router'
+
 defineProps({
   tab: {
     type: Number,
@@ -61,6 +63,12 @@ defineProps({
   },
   items: Array
 })
+
+const router = useRouter()
+// 职位编辑
+const handleEdit = (val) => {
+  router.push(`/enterprise/position/edit?id=${val.id}`)
+}
 </script>
 
 <style scoped lang="scss">

+ 38 - 10
src/views/enterprise/positionManagement/components/jobRequirements.vue

@@ -23,9 +23,13 @@ defineOptions({ name: 'position-add-job-requirements'})
 import CtForm from '@/components/CtForm'
 import areaType from '@/components/AreaSelect'
 import textUI from '@/components/FormUI/TextInput'
-import { reactive, ref, defineExpose } from 'vue'
+import { reactive, ref, defineExpose, watch } from 'vue'
 import { getDict } from '@/hooks/web/useDictionaries'
 
+const props = defineProps({
+  itemData: Object
+})
+
 const formPageRef = ref()
 let query = reactive({})
 const items = ref({
@@ -69,7 +73,7 @@ const items = ref({
     {
       type: 'text',
       key: 'payFrom',
-      value: '12000',
+      value: '',
       col: 4,
       label: '最低薪资 *',
       suffix: '元',
@@ -78,7 +82,7 @@ const items = ref({
     {
       type: 'text',
       key: 'payTo',
-      value: '15000',
+      value: '',
       col: 4,
       label: '最高薪资 *',
       flexStyle: 'mx-3',
@@ -102,13 +106,14 @@ const items = ref({
       key: 'areaId',
       value: null,
       noParam: true,
+      labelKey: 'areaName',
       label: '工作城市 *',
       rules: [v => !!v || '请选择工作城市']
     },
     {
       type: 'text',
       key: 'address',
-      value: '先烈中路100号大院8栋',
+      value: '',
       label: '详情地址 *',
       rules: [v => !!v || '请填写详细地址'],
     },
@@ -116,12 +121,14 @@ const items = ref({
 })
 
 // 获取字典内容
-items.value.options.forEach(async (e) => {
-  if (e.dictTypeName) {
-    const { data } = await getDict(e.dictTypeName)
-    e.items = data
-  }
-})
+const getDictData = async () => {
+  items.value.options.forEach(async (e) => {
+    if (e.dictTypeName) {
+      const { data } = await getDict(e.dictTypeName)
+      e.items = data
+    }
+  })
+}
 
 // 工作城市
 const handleArea = (list, name) => {
@@ -147,6 +154,27 @@ const getQuery = async () => {
   return query
 }
 
+// 编辑回显
+watch(
+  () => props.itemData,
+  async (val) => {
+    await getDictData()
+    if (!Object.keys(val).length) return
+    // 编辑
+    items.value.options.forEach(e => {
+      if (e.labelKey) {
+        query[e.key] = val[e.key]
+        e.value = val[e.labelKey]
+        return
+      }
+      if (e.noParam) return
+      e.value = val[e.key]
+    })
+  },
+  { immediate: true },
+  { deep: true }
+)
+
 defineExpose({
   formPageRef,
   getQuery

+ 31 - 17
src/views/enterprise/positionManagement/index.vue

@@ -1,23 +1,24 @@
 <template>
   <div>
     <v-card class="card-box pa-5">
+      <div class="d-flex justify-center mt-3">
+        <TextUI :item="textItem" @enter="handleEnter"></TextUI>
+      </div>
       <div class="text-end">
-        <v-btn class="btn mr-3" prepend-icon="mdi-filter-outline" color="primary" variant="outlined" @click="handleScreen">{{ $t('position.screen') }}</v-btn>
+        <!-- <v-btn class="btn mr-3" prepend-icon="mdi-filter-outline" color="primary" variant="outlined" @click="handleScreen">{{ $t('position.screen') }}</v-btn> -->
         <v-btn class="btn" prepend-icon="mdi-plus" color="primary" @click="handleAdd">{{ $t('position.newPositionsAdded') }}</v-btn>
       </div>
-      <div class="d-flex justify-center mt-3">
-        <TextUI :item="textItem"></TextUI>
-      </div>
+      
       <div>
         <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#fff">
-          <v-tab :value="1">{{ $t('position.recruitmentInProgress') }}</v-tab>
-          <v-tab :value="2">{{ $t('position.closed') }}</v-tab>
-          <v-tab :value="3">{{ $t('position.expiredPosition') }}</v-tab>
-          <v-tab :value="4">{{ $t('position.drafts') }}</v-tab>
+          <v-tab :value="1"> {{ $t('position.recruitmentInProgress') }}</v-tab>
+          <v-tab :value="2"> {{ $t('position.closed') }}</v-tab>
+          <v-tab :value="3"> {{ $t('position.expiredPosition') }}</v-tab>
+          <v-tab :value="4"> {{ $t('position.drafts') }}</v-tab>
         </v-tabs>
         <v-window v-model="tab" class="mt-3">
           <v-window-item :value="1">
-            <PositionItem :tab="tab" :items="items"></PositionItem>
+            <PositionItem v-if="items.length" :tab="tab" :items="items"></PositionItem>
           </v-window-item>
           <v-window-item :value="2">
             <PositionItem :tab="tab"></PositionItem>
@@ -29,10 +30,12 @@
             <PositionItem :tab="tab"></PositionItem>
           </v-window-item>
         </v-window>
+        <Empty v-if="!items.length" :message="tipsText" :elevation="false"></Empty>
         <CtPagination
+          v-if="items.length"
           :total="total"
-          :page="page.pageNo"
-          :limit="page.pageSize"
+          :page="query.pageNo"
+          :limit="query.pageSize"
           @handleChange="handleChangePage"
         ></CtPagination>
       </div>
@@ -52,10 +55,12 @@ import { dealDictArrayData } from '@/views/recruit/position/components/dict'
 const router = useRouter()
 const tab = ref(1)
 const total = ref(0)
-const page = ref({
+const tipsText = ref('暂无数据')
+const query = ref({
   pageSize: 10,
   pageNo: 1
 })
+
 const items = ref([])
 const textItem = ref({
   type: 'text',
@@ -65,22 +70,31 @@ const textItem = ref({
   appendInnerIcon: 'mdi-magnify'
 })
 
-const handleScreen = () => {}
+// const handleScreen = () => {}
 const handleAdd = () => {
   router.push('/enterprise/position/add')
 }
 
 // 获取职位列表
 const getPositionList = async () => {
-  const { list, total: number } = await getJobAdvertisedList(page.value)
-  if (!list.length) return
+  const { list, total: number } = await getJobAdvertisedList(query.value)
+  if (!list.length) {
+    if (query.value.name) tipsText.value = '暂无数据,请更换关键词后再试'
+  }
   total.value = number
-  items.value = dealDictArrayData([], list)
+  items.value = list.length ? dealDictArrayData([], list) : []
 }
 getPositionList()
 
 const handleChangePage = (index) => {
-  page.value.pageNo = index
+  query.value.pageNo = index
+  getPositionList()
+}
+
+// 职位名称检索
+const handleEnter = (e) => {
+  query.value.name = e
+  query.value.pageNo = 1
   getPositionList()
 }
 </script>