Kaynağa Gözat

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

Xiao_123 1 yıl önce
ebeveyn
işleme
583f1289ee

+ 34 - 0
components.d.ts

@@ -0,0 +1,34 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {}
+
+declare module 'vue' {
+  export interface GlobalComponents {
+    AreaTree: typeof import('./src/components/areaTree/index.vue')['default']
+    Autocomplete: typeof import('./src/components/FormUI/autocomplete/index.vue')['default']
+    copy: typeof import('./src/components/CtForm/index copy.vue')['default']
+    CtBtn: typeof import('./src/components/CtVuetify/CtBtn/index.vue')['default']
+    CtDialog: typeof import('./src/components/CtDialog/index.vue')['default']
+    CtForm: typeof import('./src/components/CtForm/index.vue')['default']
+    CtIcon: typeof import('./src/components/CtVuetify/CtIcon/index.vue')['default']
+    CtMenu: typeof import('./src/components/CtVuetify/CtMenu/index.vue')['default']
+    CtPagination: typeof import('./src/components/CtPagination/index.vue')['default']
+    CtSearch: typeof import('./src/components/CtSearch/index.vue')['default']
+    CtTextField: typeof import('./src/components/CtVuetify/CtTextField/index.vue')['default']
+    HeadSearch: typeof import('./src/components/headSearch/index.vue')['default']
+    HotPromoted: typeof import('./src/components/Enterprise/hotPromoted.vue')['default']
+    'Index copy': typeof import('./src/components/CtForm/index copy.vue')['default']
+    Info: typeof import('./src/components/Enterprise/info.vue')['default']
+    Item: typeof import('./src/components/Position/item.vue')['default']
+    JobTypeCard: typeof import('./src/components/jobTypeCard/index.vue')['default']
+    Recursive: typeof import('./src/components/areaTree/recursive.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    SimilarPositions: typeof import('./src/components/Position/similarPositions.vue')['default']
+    TextInput: typeof import('./src/components/FormUI/TextInput/index.vue')['default']
+    VerificationCode: typeof import('./src/components/VerificationCode/index.vue')['default']
+  }
+}

+ 7 - 0
src/api/common/index.js

@@ -81,6 +81,13 @@ export const getPositionTreeData = async (params) => {
   })
 }
 
+// 获取区域树形
+export const getAreaTreeData = async () => {
+  return await request.get({
+    url: '/app-api/menduner/system/area/get/tree'
+  })
+}
+
 // 获取行业树形
 export const getIndustryTreeData = async (params) => {
   return await request.get({

+ 194 - 0
src/components/CtSearch/index.vue

@@ -0,0 +1,194 @@
+<template>
+  <div class="d-flex align-center search" v-if="search.show">
+    <ct-text-field
+      v-model="searchValue"
+      elevation="0"
+      density="compact"
+      hide-details
+      variant="text"
+      clear-icon="mdi-close-circle"
+      append-inner-icon="mdi-magnify"
+      placeholder="搜索职位/公司"
+      class="search-fullHeight"
+      type="text"
+      clearable
+      @click:append="sendMessage"
+      @click:append-inner="toggleMarker"
+      @click:clear="clearMessage"
+      @click:prepend="changeIcon"
+    >
+      <template v-slot:append>
+        <div class="full">
+          <ct-btn class="search-fullHeight search-btn" block width="100" elevation="0" @click="handleSearch">搜索</ct-btn>
+        </div>
+        
+      </template>
+      <template v-slot:prepend v-if="list.key">
+        <ct-menu :close-on-content-click="false">
+          <template v-slot:activator="{ props }">
+            <ct-btn
+              class="full"
+              color="primary"
+              variant="text"
+              v-bind="props"
+            >
+              {{ selectValue }}
+              <ct-icon right>mdi-menu-down</ct-icon>
+            </ct-btn>
+          </template>
+          <div class="list d-flex" @mouseleave="itemChildren = {}">
+            <div class="list-left py-2">
+              <div
+                v-for="(item, index) in list.data"
+                :key="'list' + index"
+                class="list-left-item d-flex justify-space-between pa-2"
+                :class="{ hover: itemChildren.value === item.value }"
+                @mousemove="handleMousemove(item)"
+              >
+                {{ item.label }}
+                <ct-icon v-if="item.children && item.children.length">mdi-menu-right</ct-icon>
+              </div>
+            </div>
+            <div class="list-right pa-2 ml-1" v-show="itemChildren.children && itemChildren.children.length">
+              <div class="text-h6">{{ itemChildren.label }}</div>
+              <div v-for="item in itemChildren.children" :key="item.value" class="d-flex">
+                <div class="list-right-title"></div>
+                <div class="list-right-body"></div>
+              </div>
+            </div>
+          </div>
+        </ct-menu>
+      </template>
+    </ct-text-field>
+  </div>
+  <slot name="search-next"></slot>
+  <div>
+    <div v-for="item in option" :key="'search' + item.label" class="option">
+      <div class="option-label">{{ item.label }}</div>
+      <div class="option-value" @click="handleClick(item)">{{ item.items.find(e => e.value === item.value).label }}</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue'
+defineOptions({ name: 'ct-search'})
+const props = defineProps({
+  search: {
+    type: Boolean,
+    default: () => {
+      return {
+        show: true,
+        placeholder: 'Search...'
+      }
+    }
+  },
+  list: {
+    type: Object,
+    default: () => {
+      return {
+        key: 'test',
+        value: null,
+        itemLabel: 'label',
+        itemValue: 'value',
+        data: []
+      }
+    }
+  },
+  option: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const itemChildren = ref({})
+
+const searchValue = ref(null)
+
+const selectValue = computed(() => {
+  const label = props.list.itemLabel ?? 'label'
+  const value = props.list.itemValue ?? 'value'
+  const item = props.list.data.find(_e => _e.value === props.list[value])
+  return item[label]
+})
+
+const handleMousemove = (item) => {
+  const value = props.list.itemValue ?? 'value'
+  if (itemChildren.value[value] === item.value) {
+    return
+  }
+  itemChildren.value = item
+}
+
+const handleSearch = () => {
+
+}
+
+
+</script>
+
+<style lang="scss" scoped>
+.full {
+  width: 100%;
+  height: 100%;
+}
+.search {
+  height: 50px;
+  width: 800px;
+  margin: 0 auto;
+  border: 2px solid var(--v-primary-base);
+  border-radius: 5px;
+  overflow: hidden;
+
+  &-fullHeight {
+    height: 50px;
+  }
+  &-btn {
+    font-size: 18px;
+    color: #fff;
+    background-color: var(--v-primary-base);
+  }
+  :deep(.v-field__input) {
+    padding: 0;
+    height: 100%;
+    padding-left: 20px;
+  }
+}
+.hover {
+  color: var(--v-primary-base);
+  background-color: #f8f8f8;
+}
+.list {
+  height: 242px;
+  &:hover {
+    .list-right {
+      display: block;
+    }
+  }
+  &-left {
+    background: #FFF;
+    height: 100%;
+    overflow-y: auto;
+  }
+  &-right {
+    background: #FFF;
+    display: none;
+    width: 525px;
+    height: 100%;
+    overflow-y: auto;
+  }
+}
+
+::-webkit-scrollbar {
+  width: 4px;
+  height: 10px;
+}
+::-webkit-scrollbar-thumb, .temporaryAdd ::-webkit-scrollbar-thumb, .details_edit ::-webkit-scrollbar-thumb {
+  // 滚动条-颜色
+  background: #c3c3c379;
+}
+::-webkit-scrollbar-track, .temporaryAdd ::-webkit-scrollbar-track, .details_edit ::-webkit-scrollbar-track {
+  // 滚动条-底色
+  background: #e5e5e58f;
+}
+</style>

+ 21 - 0
src/components/CtVuetify/CtBtn/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <v-btn v-bind="attr">
+    <template v-for="(item, key, i) in slots" :key="i" v-slot:[key]="data">
+      <slot v-if="key" :name="key" v-bind="data"></slot>
+    </template>
+  </v-btn>
+</template>
+
+<script setup>
+import { useAttrs, useSlots } from 'vue'
+defineOptions({name: 'ct-button'})
+const attr = useAttrs()
+const slots = useSlots()
+// slots.forEach(e => {
+//   console.log(e)
+// })
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 21 - 0
src/components/CtVuetify/CtDialog/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <v-dialog v-bind="attr">
+    <template v-for="(item, key, i) in slots" :key="i" v-slot:[key]="data">
+      <slot v-if="key" :name="key" v-bind="data"></slot>
+    </template>
+  </v-dialog>
+</template>
+
+<script setup>
+import { useAttrs, useSlots } from 'vue'
+defineOptions({name: 'ct-button'})
+const attr = useAttrs()
+const slots = useSlots()
+// slots.forEach(e => {
+//   console.log(e)
+// })
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 21 - 0
src/components/CtVuetify/CtIcon/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <v-icon v-bind="attr">
+    <template v-for="(item, key, i) in slots" :key="i" v-slot:[key]="data">
+      <slot v-if="key" :name="key" v-bind="data"></slot>
+    </template>
+  </v-icon>
+</template>
+
+<script setup>
+import { useAttrs, useSlots } from 'vue'
+defineOptions({name: 'ct-button'})
+const attr = useAttrs()
+const slots = useSlots()
+// slots.forEach(e => {
+//   console.log(e)
+// })
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 18 - 0
src/components/CtVuetify/CtMenu/index.vue

@@ -0,0 +1,18 @@
+<template>
+  <v-menu v-bind="attr">
+    <template v-for="(item, key, i) in slots" :key="i" v-slot:[key]="data">
+      <slot v-if="key" :name="key" v-bind="data"></slot>
+    </template>
+  </v-menu>
+</template>
+
+<script setup>
+import { useAttrs, useSlots } from 'vue'
+defineOptions({name: 'ct-button'})
+const attr = useAttrs()
+const slots = useSlots()
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 18 - 0
src/components/CtVuetify/CtTextField/index.vue

@@ -0,0 +1,18 @@
+<template>
+  <v-text-field v-bind="attr">
+    <template v-for="(item, key, i) in slots" :key="i" v-slot:[key]="data">
+      <slot v-if="key" :name="key" v-bind="data"></slot>
+    </template>
+  </v-text-field>
+</template>
+
+<script setup>
+import { useAttrs, useSlots } from 'vue'
+defineOptions({name: 'ct-input'})
+const attr = useAttrs()
+const slots = useSlots()
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 21 - 0
src/components/areaTree/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div v-if="show">
+    <recursive v-if="items?.length" :items="items"></recursive>
+  </div>
+</template>
+<script setup>
+import recursive from './recursive'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { ref } from 'vue';
+defineOptions({ name:'common-components-areaTree'})
+
+let items = ref() 
+let children = ref() 
+const show = ref(false) 
+getDict('areaTreeData', {}, 'areaTreeData').then(({ data }) => {
+  items.value = data?.length && data || []
+  children.value = items.value[0]?.children.length && items.value[0].children || []
+  show.value = true
+})
+
+</script>

+ 41 - 0
src/components/areaTree/recursive.vue

@@ -0,0 +1,41 @@
+<template>
+  <div>
+    <span class="mr-5" v-if="currentRowType > 2">不限</span>
+    <span v-for="(item, index) in props.items" :key="item.id">
+      <span v-if="index < num" class="mr-6" style="line-height: 32px;" @click="handleClick(item)">{{ item.name }}</span>
+    </span>
+    <span v-if="props.items?.length >= num" @click="handleClick(props.items, 'other')">其他</span>
+    <recursive v-if="children" :items="children"></recursive>
+  </div>
+</template>
+<script setup>
+import { ref } from 'vue'
+import recursive from './recursive'
+defineOptions({ name:'common-components-areaTree-recursive'})
+
+const num = 15
+const props = defineProps({items: Object})
+let children = ref('')
+let currentRowType = ref(0)
+if (props.items && props.items.length) {
+  const item = props.items[0] // 首个子级
+  currentRowType.value = item.type - 0 // 当前块级数据type
+  if (currentRowType.value < 2 && item.children && item.children.length) { // 展开子级(暂时默认展示到国家级)
+    children.value = item.children
+  }
+}
+
+const handleClick = (item, btnType) => {
+  console.log(btnType, 'item', item)
+  if (btnType === 'other') {
+    return
+  } else {
+    if ((item.type - 0) === 1) children.value = item.children // 国家级
+    else if ((item.type - 0) > 1) { // 省级
+      children.value = item.children
+    }
+    console.log('children', children)
+  }
+}
+
+</script>

+ 2 - 1
src/hooks/web/useDictionaries.js

@@ -1,4 +1,4 @@
-import { getDictData, getIndustryListData, getAreaListData, getPositionTreeData } from '@/api/common/index'
+import { getDictData, getIndustryListData, getAreaListData, getPositionTreeData, getAreaTreeData } from '@/api/common/index'
 // 定义对应的api
 // const DICT_CITY_API = {
 //   menduner_exp_type: getDictData
@@ -23,6 +23,7 @@ export const getDict = (type, params, apiType = 'dict') => {
       const apiFn = {
         dict: getDictData,
         positionTreeData: getPositionTreeData, // 职位tree
+        areaTreeData: getAreaTreeData, // 区域tree
         industryList: getIndustryListData,
         areaList: getAreaListData
       }

+ 21 - 0
src/views/recruit/position/components/cityFilter.vue

@@ -0,0 +1,21 @@
+<template>
+    <div>
+      <v-tabs v-model="tab" align-tabs="start" color="primary" @click="tabClick">
+        <v-tab :value="1">城市和区域</v-tab>
+      </v-tabs>
+      <v-window v-model="tab" class="mt-3">
+        <v-window-item :value="1">
+          <areaTree></areaTree>
+        </v-window-item>
+      </v-window>
+    </div>
+</template>
+<script setup>
+import areaTree from '@/components/areaTree'
+import { ref } from 'vue'
+
+defineOptions({name: 'retrieval-components-cityFilter'})
+const tab = ref(1)
+const tabClick = () => {
+}
+</script>

+ 9 - 0
src/views/recruit/position/components/conditionFilter.vue

@@ -0,0 +1,9 @@
+<template>
+  <div>
+    筛选
+  </div>
+</template>
+<script setup>
+
+defineOptions({name: 'retrieval-components-conditionFilter'})
+</script>

+ 11 - 4
src/views/recruit/position/index.vue

@@ -1,10 +1,14 @@
 <!-- 检索列表页 - 职位检索 -->
 <template>
   <div class="default-width">
-    <div>
-      检索
-    </div>
-    <div>筛选条件</div>
+    <div style="width: 100%; height: 20px;"></div>
+    <v-card>
+      <div class="stickyBox my-5">
+        <headSearch></headSearch>
+      </div>
+      <cityFilter class="mx-5 mb-5"></cityFilter>
+      <conditionFilter class="mx-5 mb-5"></conditionFilter>
+    </v-card>
     <div>
       <div>
         左侧列表
@@ -14,6 +18,9 @@
   </div>
 </template>
 <script setup>
+import cityFilter from './components/cityFilter'
+import conditionFilter from './components/conditionFilter'
+import headSearch from '@/components/headSearch'
 import { useRoute } from 'vue-router'
 defineOptions({name: 'retrieval-position-page'})
 const route = useRoute()

+ 8 - 1
vite.config.mjs

@@ -17,7 +17,14 @@ export default defineConfig({
     // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
     Vuetify(),
     Components({
-      dts: false
+      dts: true,
+      resolvers: [
+        (name) => {
+          if (name.startsWith('Base')) {
+            return { importName: name.slice(4), path: `@/components/CtVuetify/${name}.vue` }
+          }
+        },
+      ]
     }),
     // ViteFonts({
     //   google: {