position_list.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. import 'dart:async';
  2. import 'package:amap_location_flutter_plugin/amap_location_flutter_plugin.dart';
  3. import 'package:amap_location_flutter_plugin/amap_location_option.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:liftmanager/widgets/app_search_bar.dart';
  6. import 'package:liftmanager/res/resources.dart';
  7. import 'package:liftmanager/routers/fluro_navigator.dart';
  8. import 'package:liftmanager/internal/bbs/bbs_router.dart';
  9. import 'package:flutter_screenutil/flutter_screenutil.dart';
  10. import 'package:liftmanager/mvp/base_page_state.dart';
  11. import 'package:liftmanager/internal/bbs/presenter/position_list_presenter.dart';
  12. import 'package:liftmanager/internal/search/presenter/base_list_provider.dart';
  13. import 'package:liftmanager/internal/bbs/model/mix_model.dart';
  14. import 'package:liftmanager/widgets/state_layout.dart';
  15. import 'package:liftmanager/widgets/my_refresh_list.dart';
  16. import 'package:liftmanager/utils/time_format.dart';
  17. import 'package:city_pickers/city_pickers.dart';
  18. import 'package:provider/provider.dart';
  19. import 'package:liftmanager/internal/search/search_router.dart';
  20. import 'package:liftmanager/net/api_service.dart';
  21. import 'package:liftmanager/utils/toast.dart';
  22. import 'package:flutter_picker/flutter_picker.dart';
  23. import 'package:liftmanager/internal/wode/model/table_dicts_model.dart';
  24. import 'package:permission_handler/permission_handler.dart';
  25. class PositionList extends StatefulWidget {
  26. @override
  27. PositionListState createState() => PositionListState();
  28. }
  29. class PositionListState
  30. extends BasePageState<PositionList, PositionListPresenterSeconds> {
  31. Map<String, Object> _locationResult;
  32. StreamSubscription<Map<String, Object>> _locationListener;
  33. AmapLocationFlutterPlugin _locationPlugin = new AmapLocationFlutterPlugin();
  34. BaseListProvider<Records> provider = BaseListProvider<Records>();
  35. int _page = 1;
  36. List<dynamic> categoryList = [
  37. {"title": "地址", "label": "请选择地址"},
  38. {"title": "所属职位", "label": "所属职位"},
  39. {"title": "薪资范围", "label": "薪资范围"}
  40. ];
  41. ScrollController _scrollController = new ScrollController();
  42. @override
  43. void initState() {
  44. provider.setStateTypeNotNotify(StateType.loading);
  45. super.initState();
  46. ///移除定位监听
  47. if (null != _locationListener) {
  48. _locationListener.cancel();
  49. }
  50. ///销毁定位
  51. if (null != _locationPlugin) {
  52. _locationPlugin.destroy();
  53. }
  54. getInitLocation();
  55. _locationListener = _locationPlugin
  56. .onLocationChanged()
  57. .listen((Map<String, Object> result) {
  58. setState(() {
  59. _locationPlugin.stopLocation();
  60. _locationResult = result;
  61. print(_locationResult["city"]);
  62. print(_locationResult["province"]);
  63. // address latitude longitude
  64. provinceName = _locationResult["province"];
  65. categoryList[0]['label'] = _locationResult["city"];
  66. _onRefresh();
  67. setState(() {});
  68. _locationResult.forEach((key, value) {
  69. print(111);
  70. print('key:$key :');
  71. print('value:$value :');
  72. });
  73. });
  74. });
  75. getJobClass();
  76. }
  77. // 获取定位权限
  78. Future<bool> requestPermission() async {
  79. final permissions = await PermissionHandler()
  80. .requestPermissions([PermissionGroup.location]);
  81. if (permissions[PermissionGroup.location] == PermissionStatus.granted) {
  82. return true;
  83. } else {
  84. toasts('需要定位权限!');
  85. _onRefresh();
  86. setState(() {});
  87. return false;
  88. }
  89. }
  90. getInitLocation() async {
  91. // if (await requestPermission()) {
  92. // final location = await AmapLocation.fetchLocation();
  93. // provinceName = location.province;
  94. // cityName = location.city;
  95. // categoryList[0]['label'] = location.city;
  96. // _onRefresh();
  97. // setState(() {});
  98. // }else {
  99. // _onRefresh();
  100. // }
  101. if (await requestPermission()) {
  102. if (null != _locationPlugin) {
  103. ///开始定位之前设置定位参数
  104. _setLocationOption();
  105. _locationPlugin.startLocation();
  106. }
  107. }
  108. }
  109. void _setLocationOption() {
  110. if (null != _locationPlugin) {
  111. AMapLocationOption locationOption = new AMapLocationOption();
  112. ///是否单次定位
  113. locationOption.onceLocation = true;
  114. ///是否需要返回逆地理信息
  115. locationOption.needAddress = true;
  116. ///逆地理信息的语言类型
  117. locationOption.geoLanguage = GeoLanguage.DEFAULT;
  118. ///设置Android端连续定位的定位间隔
  119. locationOption.locationInterval = 20000;
  120. ///设置Android端的定位模式<br>
  121. ///可选值:<br>
  122. ///<li>[AMapLocationMode.Battery_Saving]</li>
  123. ///<li>[AMapLocationMode.Device_Sensors]</li>
  124. ///<li>[AMapLocationMode.Hight_Accuracy]</li>
  125. locationOption.locationMode = AMapLocationMode.Hight_Accuracy;
  126. ///设置iOS端的定位最小更新距离<br>
  127. locationOption.distanceFilter = -1;
  128. ///设置iOS端期望的定位精度
  129. /// 可选值:<br>
  130. /// <li>[DesiredAccuracy.Best] 最高精度</li>
  131. /// <li>[DesiredAccuracy.BestForNavigation] 适用于导航场景的高精度 </li>
  132. /// <li>[DesiredAccuracy.NearestTenMeters] 10米 </li>
  133. /// <li>[DesiredAccuracy.Kilometer] 1000米</li>
  134. /// <li>[DesiredAccuracy.ThreeKilometers] 3000米</li>
  135. locationOption.desiredAccuracy = DesiredAccuracy.NearestTenMeters;
  136. ///设置iOS端是否允许系统暂停定位
  137. locationOption.pausesLocationUpdatesAutomatically = false;
  138. ///将定位参数设置给定位插件
  139. _locationPlugin.setLocationOption(locationOption);
  140. }
  141. }
  142. @override
  143. void dispose() {
  144. _scrollController.dispose();
  145. ///移除定位监听
  146. if (null != _locationListener) {
  147. _locationListener.cancel();
  148. }
  149. ///销毁定位
  150. if (null != _locationPlugin) {
  151. _locationPlugin.destroy();
  152. }
  153. super.dispose();
  154. }
  155. List<TableDictsModel> jobClass = [];
  156. // 获取招聘职位分类
  157. Future getJobClass() async {
  158. NewApiService().queryConstant('recruitment_info', 'job', onSuccess: (res) {
  159. jobClass = res;
  160. setState(() {});
  161. }, onError: (code, msg) {
  162. toasts(msg);
  163. });
  164. }
  165. Result addressResult = new Result();
  166. String provinceName;
  167. String cityName;
  168. // 选择地址
  169. void _clickEventFunc() async {
  170. Result tempResult = await CityPickers.showCityPicker(
  171. theme:ThemeData.light(),
  172. context: context,
  173. showType:ShowType.pc,
  174. cancelWidget: GestureDetector(
  175. onTap: (){
  176. provinceName = null;
  177. cityName = null;
  178. categoryList[0]['label'] = "不限地区";
  179. Navigator.pop(context);
  180. _onRefresh();
  181. setState(() {});
  182. },
  183. child: Text("不限地区",
  184. style: TextStyle(
  185. color: Color(0xffff0000),
  186. ),
  187. ),
  188. ),
  189. locationCode: addressResult != null
  190. ?
  191. addressResult.areaId ??
  192. addressResult.cityId
  193. ??addressResult.provinceId
  194. : null, // 初始化地址信息
  195. );
  196. if (tempResult != null) {
  197. print(tempResult);
  198. addressResult = tempResult;
  199. if(tempResult.cityName == "市辖区"){
  200. cityName = "重庆城区";
  201. }else {
  202. cityName = tempResult.cityName;
  203. }
  204. provinceName = tempResult.provinceName;
  205. if(cityName == "县"){
  206. categoryList[0]['label'] = "重庆市-县";
  207. }else{
  208. categoryList[0]['label'] = cityName;
  209. }
  210. _onRefresh();
  211. setState(() {});
  212. }
  213. }
  214. changTypeClass(item, index) {
  215. if (index == 0) {
  216. // 选择地址
  217. _clickEventFunc();
  218. } else if (index == 1) {
  219. // 所属职位
  220. showPositionModal(context);
  221. } else if (index == 2) {
  222. // 薪资范围
  223. showSalaryModal(context);
  224. }
  225. setState(() {});
  226. }
  227. // 所属职位
  228. List jobClassData = [];
  229. String jobSelected;
  230. showPositionModal(BuildContext context) {
  231. jobClassData.clear();
  232. jobClassData.add("不限");
  233. for (var i = 0; i < jobClass.length; i++) {
  234. jobClassData.add(jobClass[i].value);
  235. }
  236. new Picker(
  237. // selecteds:[0],
  238. cancelText:"取消",
  239. confirmText:"确认",
  240. adapter: PickerDataAdapter<String>(
  241. pickerdata: jobClassData,
  242. ),
  243. changeToFirst: true,
  244. hideHeader: false,
  245. onConfirm: (Picker picker, List value) {
  246. if (value[0] == 0) {
  247. jobSelected = null;
  248. } else {
  249. jobSelected = picker.getSelectedValues()[0];
  250. print(jobSelected);
  251. }
  252. categoryList[1]['label'] = picker.getSelectedValues()[0];
  253. _onRefresh();
  254. setState(() {});
  255. },
  256. ).showModal(this.context);
  257. }
  258. List salaryRange = [
  259. {
  260. "title": "不限",
  261. "min": null,
  262. "max": null,
  263. },
  264. {
  265. "title": "3K以下",
  266. "min": 0,
  267. "max": 3000,
  268. },
  269. {
  270. "title": "3-5k",
  271. "min": 3000,
  272. "max": 5000,
  273. },
  274. {
  275. "title": "5-10k",
  276. "min": 5000,
  277. "max": 10000,
  278. },
  279. {
  280. "title": "10-20k",
  281. "min": 10000,
  282. "max": 20000,
  283. },
  284. {
  285. "title": "20-50k",
  286. "min": 20000,
  287. "max": 50000,
  288. },
  289. {
  290. "title": "50K以上",
  291. "min": 50000,
  292. "max": null,
  293. }
  294. ];
  295. // 薪资范围
  296. List moneyList;
  297. List salaryRangeData = [];
  298. int minSalary;
  299. int maxSalary;
  300. showSalaryModal(BuildContext context) {
  301. salaryRangeData.clear();
  302. for (var i = 0; i < salaryRange.length; i++) {
  303. salaryRangeData.add(salaryRange[i]["title"]);
  304. }
  305. new Picker(
  306. selectedTextStyle:TextStyle(
  307. color: Colors.black
  308. ),
  309. selecteds:moneyList,
  310. cancelText:"取消",
  311. confirmText:"确认",
  312. adapter: PickerDataAdapter<String>(
  313. pickerdata: salaryRangeData,
  314. ),
  315. changeToFirst: true,
  316. hideHeader: false,
  317. onConfirm: (Picker picker, List value) {
  318. print(12389);
  319. print(value);
  320. moneyList = value;
  321. int index = value[0];
  322. minSalary = salaryRange[index]['min'];
  323. maxSalary = salaryRange[index]['max'];
  324. categoryList[2]['label'] = salaryRange[index]["title"];
  325. _onRefresh();
  326. setState(() {});
  327. },
  328. ).showModal(this.context);
  329. }
  330. @override
  331. Widget build(BuildContext context) {
  332. double width = MediaQuery.of(context).size.width;
  333. double height = MediaQuery.of(context).size.height;
  334. return ChangeNotifierProvider<BaseListProvider<Records>>(
  335. create: (_) => provider,
  336. child: Scaffold(
  337. appBar: SearchAppBar2(
  338. onPressed: (text) {
  339. jobSelected = text;
  340. _onRefresh();
  341. },
  342. ),
  343. body: Container(
  344. child: Stack(
  345. children: <Widget>[
  346. Column(
  347. children: <Widget>[
  348. Container(
  349. width: width,
  350. height: ScreenUtil().setWidth(50),
  351. decoration: BoxDecoration(
  352. border: Border(
  353. bottom: BorderSide(width: 0.5, color: Colours.line),
  354. ),
  355. ),
  356. child: ListView(
  357. scrollDirection: Axis.horizontal,
  358. children: <Widget>[
  359. SizedBox(width: ScreenUtil().setWidth(15)),
  360. TopTitle(
  361. categoryList: categoryList,
  362. fun: changTypeClass,
  363. )
  364. ],
  365. )),
  366. Expanded(
  367. flex: 1,
  368. child: Consumer<BaseListProvider<Records>>(
  369. builder: (_, provider, __) {
  370. return MyListView(
  371. key: Key('position_list'),
  372. itemCount: provider.list.length,
  373. stateType: provider.stateType,
  374. onRefresh: _onRefresh,
  375. loadMore: _loadMore,
  376. hasMore: provider.hasMore,
  377. itemBuilder: (_, index) {
  378. return GestureDetector(
  379. child: Container(
  380. padding: EdgeInsets.only(
  381. left: ScreenUtil().setWidth(15),
  382. right: ScreenUtil().setWidth(15),
  383. top: ScreenUtil().setHeight(10),
  384. bottom: ScreenUtil().setHeight(10)),
  385. decoration: BoxDecoration(
  386. border: Border(
  387. bottom: BorderSide(
  388. width: 0.5, color: Colours.line),
  389. ),
  390. ),
  391. child: Column(
  392. // crossAxisAlignment: CrossAxisAlignment.start,
  393. children: <Widget>[
  394. Container(
  395. padding: EdgeInsets.only(
  396. bottom: ScreenUtil().setHeight(5)),
  397. child: Row(
  398. mainAxisAlignment:
  399. MainAxisAlignment.spaceBetween,
  400. children: <Widget>[
  401. Container(
  402. width: width*0.4,
  403. child: Text(
  404. provider.list[index].job ?? '',
  405. textAlign: TextAlign.left,
  406. style: TextStyle(
  407. fontSize:
  408. ScreenUtil().setSp(17),
  409. ),
  410. overflow: TextOverflow.ellipsis,
  411. ),
  412. ),
  413. Container(
  414. width:width*0.4,
  415. child:Text(
  416. // 'jiage',
  417. "¥" +
  418. provider
  419. .list[index].lowerSalary
  420. .toString() +
  421. "-" +
  422. provider
  423. .list[index].upperSalary
  424. .toString(),
  425. textAlign: TextAlign.right,
  426. style: TextStyle(
  427. fontSize:
  428. ScreenUtil().setSp(17),
  429. color: Color(0xffff0000),
  430. ),
  431. overflow: TextOverflow.ellipsis,
  432. ),
  433. )
  434. ],
  435. ),
  436. ),
  437. Row(
  438. mainAxisAlignment:
  439. MainAxisAlignment.spaceBetween,
  440. children: <Widget>[
  441. Container(
  442. width: width*0.5,
  443. child: Text(
  444. provider.list[index].company?.name ??
  445. '',
  446. textAlign: TextAlign.left,
  447. overflow: TextOverflow.ellipsis,
  448. style: TextStyle(
  449. fontSize:
  450. ScreenUtil().setSp(14),
  451. color: Color(0xff999999),
  452. ),
  453. ),
  454. ),
  455. provider.list[index]
  456. .provinceName !=
  457. null?Container(
  458. width: width*0.35,
  459. child: Text(
  460. (provider.list[index]
  461. .provinceName !=
  462. null
  463. ? provider
  464. .list[index].provinceName
  465. : '') +
  466. "-" +
  467. (provider.list[index]
  468. .cityName !=
  469. null
  470. ? provider
  471. .list[index].cityName
  472. : ''),
  473. textAlign: TextAlign.right,
  474. overflow: TextOverflow.ellipsis,
  475. style: TextStyle(
  476. fontSize:
  477. ScreenUtil().setSp(14),
  478. color: Color(0xff999999),
  479. ),
  480. ),
  481. ):Container(child:null),
  482. ],
  483. ),
  484. Row(
  485. mainAxisAlignment:
  486. MainAxisAlignment.spaceBetween,
  487. children: <Widget>[
  488. Container(
  489. width: width * 0.65,
  490. child: Text(
  491. provider.list[index].info ?? '',
  492. textAlign: TextAlign.left,
  493. style: TextStyle(
  494. fontSize:
  495. ScreenUtil().setSp(14),
  496. color: Color(0xff999999),
  497. ),
  498. overflow: TextOverflow.ellipsis,
  499. ),
  500. ),
  501. Text(
  502. DateUtils.instance.getFormartData(
  503. timeSamp: provider
  504. .list[index].createTime,
  505. format: "yyyy-MM-dd"),
  506. textAlign: TextAlign.left,
  507. style: TextStyle(
  508. fontSize: ScreenUtil().setSp(14),
  509. color: Color(0xff999999),
  510. ),
  511. ),
  512. ],
  513. ),
  514. ]),
  515. ),
  516. onTap: () {
  517. NavigatorUtils.push(context,
  518. "${BbsRouter.positionDetail}?id=${provider.list[index].id.toString()}");
  519. },
  520. );
  521. },
  522. );
  523. }))
  524. ],
  525. ),
  526. ],
  527. ),
  528. ),
  529. ),
  530. );
  531. }
  532. Future _onRefresh() async {
  533. _page = 1;
  534. await presenter.getPositionList(
  535. _page,
  536. provinceName: provinceName,
  537. cityName: cityName,
  538. minSalary: minSalary,
  539. maxSalary: maxSalary,
  540. job: jobSelected,
  541. );
  542. }
  543. Future _loadMore() async {
  544. _page++;
  545. await presenter.getPositionList(
  546. _page,
  547. provinceName: provinceName,
  548. cityName: cityName,
  549. minSalary: minSalary,
  550. maxSalary: maxSalary,
  551. job: jobSelected,
  552. );
  553. }
  554. @override
  555. PositionListPresenterSeconds createPresenter() {
  556. return PositionListPresenterSeconds();
  557. }
  558. }
  559. class TopTitle extends StatelessWidget {
  560. TopTitle({Key key, this.categoryList, this.fun}) : super(key: key);
  561. List<dynamic> categoryList;
  562. Function fun;
  563. List<Widget> listWidget(context) => categoryList.asMap().keys.map((index) {
  564. return index!=1?GestureDetector(
  565. onTap: () {
  566. fun(categoryList[index], index);
  567. },
  568. child: Container(
  569. child: Row(children: <Widget>[
  570. Row(
  571. children: <Widget>[
  572. Text(
  573. categoryList[index]['label'] ?? '',
  574. style: TextStyle(
  575. fontSize: ScreenUtil().setSp(16)),
  576. textAlign: TextAlign.start,
  577. ),
  578. Container(
  579. padding: EdgeInsets.only(top: 3),
  580. child: Icon(
  581. Icons.keyboard_arrow_down,
  582. size: 26.0,
  583. ),
  584. )
  585. ],
  586. ),
  587. SizedBox(width: ScreenUtil().setWidth(15)),
  588. ]),
  589. ),
  590. ):Container(child:null);
  591. }).toList();
  592. @override
  593. Widget build(BuildContext context) {
  594. return Container(
  595. child: Row(children: listWidget(context)),
  596. );
  597. }
  598. }