companyDataMap.vue 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. <template>
  2. <div class="card-search">
  3. <div class="map-box">
  4. <!-- 地图 -->
  5. <div
  6. id="mapDiv"
  7. ></div>
  8. <!-- 企业查询 -->
  9. <div class="company-box">
  10. <!-- 企业名称 -->
  11. <div class="search-input-box">
  12. <a-select
  13. v-model:value="searchType"
  14. @change="searchTypeChange"
  15. >
  16. <a-select-option value="company">找企业</a-select-option>
  17. <a-select-option value="post">找岗位</a-select-option>
  18. </a-select>
  19. <a-input-search
  20. v-model:value="companySearchParam.companyName"
  21. :placeholder="searchType == 'company' ? '请输入企业名称' : '请输入岗位名称'"
  22. enter-button
  23. :loading="searchLoading"
  24. style="width: 75%"
  25. @search="onSearch"
  26. />
  27. </div>
  28. <!-- 查询多选框与数据列表 -->
  29. <div class="select-data-box">
  30. <!-- 多选框 -->
  31. <div class="select-box ">
  32. <span class="cursor-pointer" style="width: 46px;padding: 0 7px" @click="searchAll">全部</span>
  33. <a-select
  34. v-model:value="companySearchParam.maxDistance"
  35. placeholder="距离范围" style="width: 35%"
  36. :allow-clear="true" size="small"
  37. >
  38. <a-select-option :value="item.value" v-for="(item,index) in rangeList" :key="index">{{
  39. item.label
  40. }}
  41. </a-select-option>
  42. </a-select>
  43. <a-select
  44. v-model:value="companySearchParam.companyModel"
  45. :options="companyModelList"
  46. :field-names="{ label: 'name', value: 'value' }"
  47. placeholder="企业规模" style="width: 49%"
  48. :allow-clear="true" size="small"
  49. >
  50. </a-select>
  51. <a-select
  52. v-model:value="companySearchParam.regionCode"
  53. :allow-clear="true"
  54. :options="regionList"
  55. :field-names="{ label: 'name', value: 'code' }"
  56. placeholder="所属县区" size="small" style="width: 152px"
  57. >
  58. </a-select>
  59. <a-select
  60. v-model:value="companySearchParam.siteID"
  61. :allow-clear="true"
  62. :options="siteList"
  63. :field-names="{ label: 'siteName', value: 'siteID' }"
  64. placeholder="所属驿站" size="small" style="width: 149px"
  65. >
  66. </a-select>
  67. </div>
  68. <!-- 企业数据列表 -->
  69. <div class="list-box">
  70. <!-- 查询类型为企业,主要展示企业数据 -->
  71. <div class="company-data-box"
  72. :class="{'check-company':nowCheckCompany.companyID == company.companyID || nowMouseenterCompany.companyID == company.companyID}"
  73. v-if="companyList.length > 0 && searchType == 'company'" v-for="(company,index) in companyList"
  74. :key="index"
  75. @click="checkCompanyChange(company)"
  76. @mouseenter="companyMouseenter(company)"
  77. @mouseleave="companyMouseenter({companyID:-1})"
  78. >
  79. <p class="company-name">
  80. {{ company.companyName }}
  81. </p>
  82. <p class="label-text">
  83. 地点:{{ company.companyAddress }}
  84. </p>
  85. <!-- 岗位 -->
  86. <div class="company-post-box label-text">
  87. <span style="width: 65px; flex: 0 0 auto">招聘岗位:</span>
  88. <a-tag v-if="company.postList && company.postList.length > 0"
  89. v-for="(label, index) in company.postList.slice(0, 3)"
  90. :key="index">
  91. {{ label.professionName }}
  92. </a-tag>
  93. <span v-if="company.postList && company.postList.length > 3" class="all-post-btn"
  94. style="width: 25px;flex: 0 0 auto">
  95. 更多
  96. </span>
  97. <span v-if="company.postList.length == 0" style="width: 25px;flex: 0 0 auto">暂无</span>
  98. </div>
  99. <!-- 标签 -->
  100. <div class="company-label-box" v-if="company.companyLabelList && company.companyLabelList.length > 0"
  101. :ref="el => companyLabelBoxRef[index] = el" :class="{'label-box-max-height': company.labelExpanded}">
  102. <a-tag v-for="(label, labelIndex) in company.companyLabelList" :key="labelIndex">
  103. {{ label.labelName }}
  104. </a-tag>
  105. <div v-if="showLaunchBtnBox(companyLabelBoxRef,index,50)">
  106. <span class="launch-btn" v-if="company.labelExpanded"
  107. @click.stop="company.labelExpanded = false">展开</span>
  108. <span class="launch-btn" v-else @click.stop="company.labelExpanded = true">收起</span>
  109. </div>
  110. </div>
  111. </div>
  112. <!-- 查询类型为岗位时,主要展示岗位数据 -->
  113. <div v-if="companyList.length>0 && searchType == 'post'"
  114. v-for="(postCompany, postCompanyIndex) in companyList" :key="postCompanyIndex"
  115. class="company-data-box"
  116. :class="{'check-company':nowCheckCompany.postID == postCompany.postID || nowMouseenterCompany.postID == postCompany.postID}"
  117. @click="checkCompanyChange(postCompany)"
  118. @mouseenter="companyMouseenter(postCompany)"
  119. @mouseleave="companyMouseenter({postID:-1})"
  120. >
  121. <div class="post-title">
  122. <span>{{ postCompany.professionName }}({{ postCompany.recruitCount }}人)</span>
  123. <span class="post-salary">{{ showSalary(postCompany.minSalary, postCompany.maxSalary) }}</span>
  124. </div>
  125. <p class="label-text">
  126. 招聘企业:{{ postCompany.companyName }}
  127. </p>
  128. <p class="label-text">
  129. 工作地点:{{ postCompany.companyAddress }}
  130. </p>
  131. <!-- 标签 -->
  132. <div class="company-label-box"
  133. v-if="postCompany.postLabelList && postCompany.postLabelList.length > 0"
  134. :ref="el => companyPostLabelBoxRef[postCompanyIndex] = el"
  135. :class="{'label-box-max-height': postCompany.labelExpanded}">
  136. <a-tag v-for="(label, labelIndex) in postCompany.postLabelList" :key="labelIndex">
  137. {{ label.labelName }}
  138. </a-tag>
  139. <div v-if="showLaunchBtnBox(companyPostLabelBoxRef,postCompanyIndex,50)">
  140. <span class="launch-btn" v-if="postCompany.labelExpanded"
  141. @click.stop="postCompany.labelExpanded = false">展开</span>
  142. <span class="launch-btn" v-else @click.stop="postCompany.labelExpanded = true">收起</span>
  143. </div>
  144. </div>
  145. </div>
  146. <!-- 空数据状态 -->
  147. <div v-if="companyList.length == 0" class="empty-box">
  148. <a-empty/>
  149. </div>
  150. </div>
  151. </div>
  152. <!-- 分页区域 -->
  153. <div class="pagination-box">
  154. <a-pagination v-model:current="companySearchParam.pageIndex" :total="companyTotal"
  155. v-model:pageSize="companySearchParam.pageSize"
  156. show-less-items @change="companyPaginationChange" simple :show-size-changer="false"/>
  157. </div>
  158. </div>
  159. <!-- 岗位信息 -->
  160. <div class="post-box" v-if="nowCheckCompany.companyID">
  161. <div v-if="searchType == 'company'"
  162. style="width: 100%; height: 100%; background-color: #F8F8F8; border-radius: 10px; padding: 10px;">
  163. <!-- 企业信息 -->
  164. <div class="post-company-box margin-bottom-10">
  165. <p class="company-name">
  166. {{ nowCheckCompany.companyName }}
  167. </p>
  168. <p class="label-text">
  169. 工作地点:{{ nowCheckCompany.companyAddress }}
  170. </p>
  171. <p class="label-text">
  172. 企业规模:{{ nowCheckCompany.companyModelType }}
  173. </p>
  174. <p class="label-text">
  175. 企业状态:{{ nowCheckCompany.recordStatusName }}
  176. </p>
  177. <!-- 标签 -->
  178. <div class="company-label-box"
  179. v-if="nowCheckCompany.companyLabelList && nowCheckCompany.companyLabelList.length > 0"
  180. :ref="el => postBoxCompanyLabelBoxRef = el"
  181. :class="{'label-box-max-height': postBoxCompanyLabelExpanded}">
  182. <a-tag v-for="(label, index) in nowCheckCompany.companyLabelList" :key="index">
  183. {{ label.labelName }}
  184. </a-tag>
  185. <div v-if="showLaunchBtnBox(postBoxCompanyLabelBoxRef, null, 50)">
  186. <span class="launch-btn" v-if="postBoxCompanyLabelExpanded"
  187. @click.stop="postBoxCompanyLabelExpanded = false">展开</span>
  188. <span class="launch-btn" v-else @click.stop="postBoxCompanyLabelExpanded = true">收起</span>
  189. </div>
  190. </div>
  191. </div>
  192. <!-- 岗位列表 -->
  193. <div class="list-box">
  194. <div class="list-post-box margin-bottom-10" v-if="postList.length > 0"
  195. v-for="(post, postIndex) in postList"
  196. :key="postIndex">
  197. <div class="post-title">
  198. <span>{{ post.professionName }}</span>
  199. <span class="post-salary">{{ showSalary(post.minSalary, post.maxSalary) }}</span>
  200. </div>
  201. <p class="label-text">
  202. 招聘人数:{{ post.recruitCount }}
  203. </p>
  204. <p class="label-text">
  205. 招聘日期:
  206. {{ dayjs(post.startTime).format("YYYY-MM-DD") }}
  207. {{ dayjs(post.endTime).format("YYYY-MM-DD") }}
  208. </p>
  209. <!-- 岗位要求 -->
  210. <div class="post-desc-box">
  211. <div class="label-text post-desc" :ref="el => postDescBoxRef[postIndex] = el"
  212. :class="{'post-desc-max-height': post.descExpanded}">
  213. 岗位要求:{{ post.postDesc }}
  214. </div>
  215. <div v-if="showLaunchBtnBox(postDescBoxRef,postIndex,36)">
  216. <span class="launch-btn" v-if="post.descExpanded"
  217. @click.stop="post.descExpanded = false">展开</span>
  218. <span class="launch-btn " v-else @click.stop="post.descExpanded = true">收起</span>
  219. </div>
  220. </div>
  221. <!-- 标签 -->
  222. <div class="company-label-box" v-if="post.labelList && post.labelList.length > 0"
  223. :ref="el => postLabelBoxRef[postIndex] = el" :class="{'label-box-max-height': post.labelExpanded}">
  224. <a-tag v-for="(label, labelIndex) in post.labelList" :key="labelIndex">
  225. {{ label.labelName }}
  226. </a-tag>
  227. <div v-if="showLaunchBtnBox(postLabelBoxRef,postIndex,50)">
  228. <span class="launch-btn" v-if="post.labelExpanded"
  229. @click.stop="post.labelExpanded = false">展开</span>
  230. <span class="launch-btn" v-else @click.stop="post.labelExpanded = true">收起</span>
  231. </div>
  232. </div>
  233. </div>
  234. <div v-else class="empty-box">
  235. <a-empty/>
  236. </div>
  237. </div>
  238. <!-- 分页控件 -->
  239. <div class="pagination-box">
  240. <a-pagination v-model:current="postSearchParams.pageIndex" :total="postTotal"
  241. v-model:pageSize="postSearchParams.pageSize"
  242. show-less-items @change="postPaginationChange" simple :show-size-changer="false"/>
  243. </div>
  244. </div>
  245. <div v-if="searchType == 'post'"
  246. style="width: 100%; height: 100%; background-color: #FFFFFF;border-radius: 10px;">
  247. <div class="post-company-box" style="max-height: 100%">
  248. <div class="post-title">
  249. <span class="font-weight-700">{{ nowCheckCompany.professionName }}</span>
  250. <span class="post-salary">{{ showSalary(nowCheckCompany.minSalary, nowCheckCompany.maxSalary) }}</span>
  251. </div>
  252. <!-- 标签 -->
  253. <div class="company-label-box"
  254. v-if="nowCheckCompany.postLabelList && nowCheckCompany.postLabelList.length > 0"
  255. >
  256. <a-tag v-for="(label, index) in nowCheckCompany.postLabelList" :key="index">
  257. {{ label.labelName }}
  258. </a-tag>
  259. </div>
  260. <!-- 联系人 -->
  261. <div class="company-contact-info">
  262. <p class="font-size-14 font-weight-700">{{ nowCheckCompany.companyName }}</p>
  263. <div class="avt-info margin-top-10" v-if="nowCheckCompany.contactName || nowCheckCompany.contactMobile">
  264. <img :src="avtO1" alt="">
  265. <div>
  266. <p class="contact-name">
  267. {{ nowCheckCompany.contactName }}
  268. </p>
  269. <p class="contact-mobile">
  270. {{ nowCheckCompany.contactMobile }}
  271. </p>
  272. </div>
  273. </div>
  274. </div>
  275. <div class="post-desc">
  276. <p class="font-size-16 font-weight-600 margin-bottom-10">职位描述:</p>
  277. <p class="font-size-12 font-weight-400 line-height-20">
  278. {{ nowCheckCompany.postDesc ? nowCheckCompany.postDesc : '暂无描述' }}</p>
  279. </div>
  280. <div class="post-company-address">
  281. <p class="font-size-16 font-weight-600 margin-bottom-10">工作地址:</p>
  282. <p class="font-size-12 font-weight-400 line-height-20">{{ nowCheckCompany.companyAddress }}</p>
  283. </div>
  284. </div>
  285. </div>
  286. </div>
  287. </div>
  288. </div>
  289. </template>
  290. <script setup lang="ts">
  291. import {onMounted, reactive, ref} from "vue";
  292. import huiZhouGeoJSON from "./geo"
  293. import {getPosition, setBoundary} from "@/utils/position";
  294. import redThIcon from "@/assets/images/redTh.png"
  295. import blueThIcon from "@/assets/images/blueTh.png"
  296. import {message, type SelectProps} from "ant-design-vue";
  297. import {getSysDictionaryList} from "@/api/system/dictionary";
  298. import {getSiteList} from "@/api/baseSettings/siteInfo";
  299. import {getRegionCodeList} from "@/api/system/area/index";
  300. import {getDataMapList, getDataMapListByPostName} from "@/api/companyService/company";
  301. import {getCompanyMapPostList} from "@/api/companyService/post";
  302. import dayjs from "dayjs";
  303. import avtO1 from "@/assets/images/avt01.png"
  304. const T = (window as any).T;
  305. const zoom = 9;
  306. let map = null;
  307. // 地图标记点
  308. let markerList = ref<Array<any>>([]);
  309. let labelList = ref<Array<any>>([]);
  310. // 查询类型
  311. const searchType = ref("company")
  312. // 企业查询条件
  313. const companySearchParam = reactive({
  314. pageIndex: 1,
  315. pageSize: 50,
  316. companyName: "",
  317. maxDistance: null,
  318. companyModel: undefined,
  319. siteID: undefined,
  320. regionCode: undefined,
  321. longitude: 114.416110,
  322. latitude: 23.111582
  323. })
  324. // 企业数据
  325. const companyList = ref<Array<any>>([])
  326. // 企业分页条数
  327. const companyTotal = ref(0);
  328. // 当前选中的企业
  329. const nowCheckCompany = ref<any>({})
  330. // 鼠标当前移入的企业
  331. const nowMouseenterCompany = ref<any>({})
  332. // 岗位查询条件
  333. const postSearchParams = reactive({
  334. pageIndex: 1,
  335. pageSize: 20,
  336. })
  337. // 当前选中的企业岗位列表
  338. const postList = ref<Array<any>>([]);
  339. // 岗位分页总条数
  340. const postTotal = ref(0);
  341. // 范围列表
  342. const rangeList = [
  343. {value: null, label: "--范围--"},
  344. {value: 5, label: "5KM"},
  345. {value: 3, label: "3KM"},
  346. {value: 1, label: "1KM"}
  347. ]
  348. // 企业规模
  349. const companyModelList = ref<SelectProps['options']>();
  350. // 所属驿站
  351. const siteList = ref<any>([]);
  352. // 所属县区
  353. const regionList = ref<SelectProps['options']>();
  354. // 查询按钮加载
  355. const searchLoading = ref(false);
  356. // 企业信息标签box ref
  357. const companyLabelBoxRef = ref<Array<any>>([]);
  358. // 企业信息-岗位标签box ref
  359. const companyPostLabelBoxRef = ref<Array<any>>([])
  360. // 岗位面板企业信息的标签盒子ref
  361. const postBoxCompanyLabelBoxRef = ref<any>(null);
  362. // 岗位面板企业信息的标签展开与收起开关
  363. const postBoxCompanyLabelExpanded = ref(true)
  364. // 岗位信息的标签box ref
  365. const postLabelBoxRef = ref<Array<any>>([]);
  366. // 岗位要求
  367. const postDescBoxRef = ref<Array<any>>([]);
  368. // 查询企业规模
  369. const getCompanyModelList = async function () {
  370. companyModelList.value = await getSysDictionaryList("CompanyModel");
  371. }
  372. // 查询驿站
  373. const getAllSites = () => {
  374. getSiteList({pageIndex: 1, pageSize: 9999}).then((result: any) => {
  375. siteList.value = result.list;
  376. })
  377. }
  378. // 查询县区
  379. const getRegionList = async function () {
  380. regionList.value = await getRegionCodeList();
  381. }
  382. // 查询事件
  383. function onSearch() {
  384. searchLoading.value = true;
  385. // 按企业名称查询
  386. if (searchType.value == 'company') {
  387. getDataMapList(companySearchParam).then((result: any) => {
  388. findFuncThen(result);
  389. }).finally(() => {
  390. searchLoading.value = false;
  391. })
  392. }
  393. // 按岗位名称反向查询
  394. if (searchType.value == "post") {
  395. getDataMapListByPostName(companySearchParam).then((result: any) => {
  396. findFuncThen(result);
  397. }).finally(() => {
  398. searchLoading.value = false;
  399. })
  400. }
  401. }
  402. // 查询全部
  403. function searchAll() {
  404. companySearchParam.companyName = "";
  405. companySearchParam.companyModel = undefined;
  406. companySearchParam.siteID = undefined;
  407. companySearchParam.regionCode = undefined;
  408. companySearchParam.maxDistance = null;
  409. companySearchParam.pageIndex = 1;
  410. companySearchParam.pageSize = 50;
  411. onSearch();
  412. }
  413. // 查询处理
  414. function findFuncThen(result: any) {
  415. companyList.value = result.list;
  416. // 处理标签和岗位JSON数据
  417. companyList.value.forEach((item: any) => {
  418. item.companyLabelList = JSON.parse(item.companyLabelList || '[]')
  419. item.postLabelList = JSON.parse(item.postLabelList || '[]')
  420. item.postList = JSON.parse(item.postList || '[]')
  421. item.labelExpanded = true;
  422. });
  423. companyTotal.value = result.total;
  424. // 重置选中
  425. nowCheckCompany.value.companyID = "";
  426. setCompanyMarker();
  427. }
  428. // 地图初始化
  429. const initMap = () => {
  430. // 初始化容器
  431. map = new T.Map('mapDiv');
  432. if (map != null) {
  433. // 设置地图显示中心点为惠州市人民政府
  434. (map as any).centerAndZoom(new T.LngLat(114.416110, 23.111582), zoom);
  435. // 地图缩放监听事件
  436. (map as any).addEventListener("zoomend", function () {
  437. let zoomLevel = (map as any).getZoom();
  438. // 计算新的icon大小
  439. let newSize = Math.min(Math.max(zoomLevel * 3, 15), 45);
  440. // 计算新的icon锚点位置
  441. let iconAnchor = new T.Point(15 * (newSize / 45), 30 * (newSize / 45));
  442. markerList.value.forEach((item: any) => {
  443. (map as any).removeOverLay(item);
  444. item.getIcon().setIconSize(new T.Point(newSize, newSize));
  445. item.getIcon().setIconAnchor(iconAnchor);
  446. (map as any).addOverLay(item);
  447. })
  448. // 更新 label 的偏移量
  449. // 计算新的偏移量,保持 label 居中且不超过初始值
  450. let offsetX = -20 + ((newSize - 45) / 3);
  451. let offsetY = 28 + ((newSize - 45) / 3);
  452. // 确保偏移量不超过初始值
  453. offsetX = Math.max(offsetX, -20);
  454. offsetY = Math.min(offsetY, 28);
  455. let newOffset = new T.Point(offsetX, offsetY);
  456. labelList.value.forEach((item: any) => {
  457. (map as any).removeOverLay(item);
  458. item.setOffset(newOffset);
  459. item.setFontSize(Math.min(12, Math.max(8, 12 - (45 - newSize) / 3)));
  460. (map as any).addOverLay(item);
  461. })
  462. });
  463. }
  464. // 设置城市边界
  465. setBoundary(map, T, huiZhouGeoJSON);
  466. // 设置登录用户的定位
  467. setLoginLocation();
  468. };
  469. // 获取当前登录用户定位
  470. function setLoginLocation() {
  471. getPosition().then((data: any) => {
  472. if (data.longitude && data.latitude) {
  473. companySearchParam.longitude = data.longitude;
  474. companySearchParam.latitude = data.latitude;
  475. } else {
  476. // 如果没有获取到经纬度,设置默认为惠州市人民政府的位置
  477. companySearchParam.longitude = 114.420244;
  478. companySearchParam.latitude = 23.116236;
  479. message.info("获取定位失败,已使用默认定位");
  480. }
  481. }).finally(() => {
  482. // 设置中心点
  483. (map as any).centerAndZoom(new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), 10);
  484. const icon = new T.Icon({
  485. iconUrl: redThIcon,
  486. iconSize: new T.Point(30, 30),
  487. iconAnchor: new T.Point(15, 30)
  488. })
  489. const point = new T.LngLat(companySearchParam.longitude, companySearchParam.latitude)
  490. // 创建标注
  491. const marker = new T.Marker(point, {
  492. icon: icon
  493. });
  494. //向地图上标记
  495. (map as any).addOverLay(marker);
  496. onSearch();
  497. })
  498. }
  499. // 设置企业地图标点
  500. function setCompanyMarker() {
  501. delMapInfo();
  502. if (companyList.value.length > 0) {
  503. // 设置图标
  504. const icon = new T.Icon({
  505. iconUrl: blueThIcon,
  506. iconSize: new T.Point(30, 30),
  507. iconAnchor: new T.Point(15, 30)
  508. })
  509. let uniqueCompanyList = companyList.value;
  510. if (searchType.value == 'post') {
  511. // 岗位查询时,会出现多个岗位相同公司,按公司的ID去重
  512. const processedCompanyIDs = new Set();
  513. const companyIDCounts = {}; // 用于统计每个companyID的数量
  514. // 过滤出唯一的公司列表,并统计每个companyID的数量
  515. uniqueCompanyList = companyList.value.filter((item: any) => {
  516. if (!processedCompanyIDs.has(item.companyID)) {
  517. processedCompanyIDs.add(item.companyID);
  518. companyIDCounts[item.companyID] = 1; // 初始化计数
  519. item.postNumber = 1; // 初始化postNumber
  520. return true;
  521. } else {
  522. companyIDCounts[item.companyID] += 1; // 增加计数
  523. return false;
  524. }
  525. });
  526. // 更新uniqueCompanyList中元素的postNumber
  527. uniqueCompanyList.forEach(item => {
  528. item.postNumber = companyIDCounts[item.companyID];
  529. });
  530. }
  531. // 解析企业数据,在地图中标记
  532. uniqueCompanyList.forEach((item: any) => {
  533. if (item.longitude && item.latitude) {
  534. const point = new T.LngLat(item.longitude, item.latitude)
  535. const marker = new T.Marker(point, {
  536. icon: icon
  537. }); // 创建标注
  538. // 给每个地图标点添加点击事件,打印item
  539. marker.addEventListener('click', () => {
  540. checkCompanyChange(item);
  541. });
  542. marker.setZIndexOffset(200);
  543. (map as any).addOverLay(marker);// 将标注添加到地图中
  544. markerList.value.push(marker);
  545. }
  546. })
  547. // 如果是岗位,打上数量文本
  548. if (searchType.value == 'post') {
  549. uniqueCompanyList.forEach((item: any) => {
  550. if (item.longitude && item.latitude) {
  551. // 创建自定义的标签
  552. let label = new T.Label({
  553. text: item.postNumber + "", //文本标注的内容
  554. position: new T.LngLat(item.longitude, item.latitude), //文本标注的地理位置
  555. offset: new T.Point(-30, 10), //文本标注的位置偏移值
  556. });
  557. label.setBackgroundColor("transparent");
  558. label.setBorderColor("transparent");
  559. label.setBorderLine(0);
  560. label.setFontSize(12);
  561. label.setFontColor("white");
  562. (map as any).addOverLay(label); // 将标注添加到地图中
  563. labelList.value.push(label);
  564. }
  565. })
  566. }
  567. }
  568. }
  569. // 企业分页器页码变更事件
  570. function companyPaginationChange() {
  571. onSearch()
  572. }
  573. // 企业信息点击事件
  574. async function checkCompanyChange(company: any) {
  575. nowCheckCompany.value = JSON.parse(JSON.stringify(company));
  576. // 设置中心点到企业位置
  577. if (company.longitude && company.latitude) {
  578. // 设置中心点
  579. (map as any).centerAndZoom(new T.LngLat(company.longitude, company.latitude), 14);
  580. }
  581. await findPostList();
  582. }
  583. // 企业信息鼠标移入移出事件
  584. function companyMouseenter(company: any) {
  585. nowMouseenterCompany.value = JSON.parse(JSON.stringify(company));
  586. }
  587. // 查询岗位
  588. async function findPostList() {
  589. await getCompanyMapPostList({...postSearchParams, companyID: nowCheckCompany.value.companyID}).then((result: any) => {
  590. postList.value = result.list;
  591. // 处理标签JSON数据
  592. postList.value.forEach((item: any) => {
  593. item.labelList = JSON.parse(item.labelList || '[]');
  594. item.labelExpanded = true;
  595. item.descExpanded = true;
  596. })
  597. postTotal.value = result.total;
  598. })
  599. }
  600. // 企业分页器页码变更事件
  601. function postPaginationChange() {
  602. findPostList();
  603. }
  604. // 岗位薪酬优化显示
  605. const showSalary = (minSalary: any, maxSalary: any) => {
  606. if (minSalary != null) {
  607. if (maxSalary != null) {
  608. return (minSalary / 1000).toString() + "-" + (maxSalary / 1000).toString() + 'K';
  609. } else {
  610. return "≥" + (minSalary / 1000).toString() + "K";
  611. }
  612. } else {
  613. if (maxSalary != null) {
  614. return "≤" + (maxSalary / 1000).toString() + "K";
  615. } else {
  616. return "";
  617. }
  618. }
  619. }
  620. // 企业标签,岗位标签等的展开与收起按钮是否显示判断方法
  621. function showLaunchBtnBox(refValue: any, index: any, scrollHeight: any) {
  622. if (index != null) {
  623. if (refValue[index] && refValue[index].scrollHeight) {
  624. return refValue[index]?.scrollHeight > scrollHeight
  625. }
  626. return false;
  627. } else {
  628. if (refValue && refValue.scrollHeight) {
  629. return refValue?.scrollHeight > scrollHeight
  630. }
  631. return false;
  632. }
  633. }
  634. // 查询类型切换事件
  635. function searchTypeChange() {
  636. // 清空数据
  637. onSearch();
  638. delMapInfo();
  639. }
  640. // 清空地图标点
  641. function delMapInfo() {
  642. // 删除已有标点
  643. if (markerList.value.length > 0) {
  644. for (let i = 0; i < markerList.value.length; i++) {
  645. (map as any).removeOverLay(markerList.value[i]);
  646. }
  647. markerList.value = [];
  648. }
  649. if (labelList.value.length > 0) {
  650. for (let i = 0; i < labelList.value.length; i++) {
  651. (map as any).removeOverLay(labelList.value[i]);
  652. }
  653. labelList.value = [];
  654. }
  655. }
  656. onMounted(() => {
  657. initMap();
  658. getCompanyModelList();
  659. getAllSites();
  660. getRegionList();
  661. })
  662. </script>
  663. <script lang="ts">
  664. // 设置页面名称进行组件缓存
  665. export default {
  666. name: "CompanyDataMap"
  667. }
  668. </script>
  669. <style lang="less">
  670. .map-box {
  671. width: 100%;
  672. height: calc(100vh - 235px);
  673. position: relative;
  674. border: 1px rgba(173, 173, 173, 0.8) solid;
  675. box-sizing: border-box;
  676. #mapDiv {
  677. position: absolute;
  678. width: 100%;
  679. height: 100%;
  680. z-index: 100;
  681. }
  682. .company-box, .post-box {
  683. position: absolute;
  684. top: 15px;
  685. bottom: 15px;
  686. z-index: 110;
  687. }
  688. .company-name, .post-title {
  689. font-size: 14px;
  690. font-weight: 600;
  691. margin-bottom: 8px;
  692. .post-salary {
  693. font-size: 12px;
  694. font-weight: normal;
  695. color: #DE4F3F;
  696. }
  697. }
  698. .label-text {
  699. font-size: 12px;
  700. color: #737373;
  701. margin-bottom: 3px;
  702. }
  703. .company-label-box {
  704. display: flex;
  705. align-items: center;
  706. flex-wrap: wrap;
  707. margin-top: 5px;
  708. overflow: hidden;
  709. position: relative;
  710. .ant-tag {
  711. margin-bottom: 3px;
  712. }
  713. }
  714. .label-box-max-height {
  715. max-height: 23px;
  716. }
  717. .ant-tag {
  718. text-align: center;
  719. overflow: hidden;
  720. text-overflow: ellipsis;
  721. white-space: nowrap;
  722. border: none;
  723. background-color: #F3F6F8;
  724. color: #899099
  725. }
  726. .search-box,
  727. .list-box,
  728. .pagination-box {
  729. width: 100%;
  730. }
  731. .list-box {
  732. margin-bottom: 10px;
  733. overflow-y: auto;
  734. }
  735. .pagination-box {
  736. display: flex;
  737. justify-content: center;
  738. }
  739. .launch-btn {
  740. font-size: 12px;
  741. color: #40a9ff;
  742. position: absolute;
  743. bottom: 4px;
  744. right: 0;
  745. cursor: pointer;
  746. }
  747. .empty-box {
  748. width: 100%;
  749. height: 450px;
  750. display: flex;
  751. justify-content: center;
  752. align-items: center;
  753. }
  754. .post-title {
  755. display: flex;
  756. justify-content: space-between;
  757. flex-wrap: wrap;
  758. }
  759. .search-input-box {
  760. margin-bottom: 10px;
  761. width: 100%;
  762. }
  763. .company-box {
  764. left: 15px;
  765. right: 300px;
  766. width: 330px;
  767. .select-data-box {
  768. width: 100%;
  769. height: calc(100% - 85px);
  770. background-color: #F8F8F8;
  771. margin-bottom: 10px;
  772. padding: 10px 9px;
  773. border-radius: 10px;
  774. .select-box {
  775. display: flex;
  776. flex-wrap: wrap;
  777. width: 100%;
  778. padding: 8px 5px;
  779. background-color: white;
  780. border-radius: 10px;
  781. }
  782. .list-box {
  783. max-height: calc(100% - 80px);
  784. margin-top: 10px;
  785. .company-data-box {
  786. background-color: white;
  787. border-radius: 10px;
  788. cursor: pointer;
  789. padding: 10px;
  790. margin-bottom: 10px;
  791. box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.3);
  792. box-sizing: border-box;
  793. border: 1px solid white;
  794. .company-post-box {
  795. width: 100%;
  796. display: flex;
  797. flex-wrap: nowrap;
  798. align-items: center;
  799. .ant-tag {
  800. flex: 1 1 auto;
  801. min-width: 0;
  802. max-width: fit-content;
  803. }
  804. .all-post-btn {
  805. cursor: pointer;
  806. }
  807. }
  808. }
  809. .check-company {
  810. border: 1px solid #007EFF;
  811. }
  812. }
  813. }
  814. .pagination-box {
  815. background-color: #F8F8F8;
  816. padding: 8px 0;
  817. border-radius: 12px;
  818. }
  819. }
  820. .post-box {
  821. width: 330px;
  822. left: calc(100% - 345px);
  823. right: 0;
  824. border-radius: 10px;
  825. .company-info-post-list {
  826. width: 100%;
  827. height: 100%;
  828. background-color: #F8F8F8;
  829. border-radius: 10px;
  830. padding: 10px;
  831. }
  832. .title {
  833. text-align: center;
  834. margin-top: 8px;
  835. }
  836. .post-company-box {
  837. background-color: white;
  838. padding: 18px;
  839. width: 100%;
  840. max-height: 156px;
  841. overflow-y: auto;
  842. border-radius: 10px;
  843. .post-title {
  844. font-size: 18px;
  845. justify-content: normal;
  846. align-items: center;
  847. margin-bottom: 10px;
  848. .post-salary {
  849. font-size: 18px;
  850. margin-left: 20px;
  851. }
  852. }
  853. .company-contact-info {
  854. margin: 20px 0;
  855. border-top: 1px solid rgba(0, 0, 0, 0.1);
  856. border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  857. padding: 18px 0;
  858. .avt-info {
  859. display: flex;
  860. align-items: center;
  861. img {
  862. width: 36px;
  863. height: 36px;
  864. margin-right: 10px;
  865. }
  866. .contact-name {
  867. font-size: 14px;
  868. color: #292934;
  869. }
  870. .contact-mobile {
  871. font-size: 12px;
  872. color: #6A6F75;
  873. }
  874. }
  875. }
  876. .post-desc {
  877. white-space: pre-wrap;
  878. margin-bottom: 14px;
  879. }
  880. }
  881. .list-box {
  882. max-height: calc(100% - 200px);
  883. .list-post-box {
  884. padding: 8px;
  885. background-color: white;
  886. border-radius: 10px;
  887. box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.3);
  888. .post-desc-box {
  889. position: relative;
  890. .launch-btn {
  891. bottom: -1px;
  892. background-color: white;
  893. }
  894. .post-desc {
  895. overflow: hidden;
  896. width: 91%;
  897. }
  898. .post-desc-max-height {
  899. max-height: 35px;
  900. }
  901. }
  902. }
  903. }
  904. }
  905. .ant-btn, .ant-input {
  906. border: none !important;
  907. }
  908. input {
  909. height: 32px;
  910. }
  911. .ant-select:not(.ant-select-customize-input) .ant-select-selector,
  912. .ant-select:not(.ant-select-customize-input) .ant-select-selector,
  913. .ant-input-affix-wrapper, .ant-select-selector {
  914. border: none !important;
  915. box-shadow: none !important;
  916. }
  917. .ant-select:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input)
  918. .ant-select-selector {
  919. border-color: transparent;
  920. box-shadow: none;
  921. }
  922. }
  923. </style>