companyDataMap.vue 39 KB

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