index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <template>
  2. <div class="execution">
  3. <basic-container>
  4. <avue-crud
  5. ref="crud"
  6. :page="page"
  7. v-model="form"
  8. :data="tableData"
  9. :permission="permissionList"
  10. :table-loading="tableLoading"
  11. :option="tableOption"
  12. @on-load="getList"
  13. @search-change="searchChange"
  14. @refresh-change="refreshChange"
  15. @size-change="sizeChange"
  16. @current-change="currentChange"
  17. @row-del="rowDel"
  18. >
  19. <template slot="menuLeft">
  20. <el-button v-if="permissionList.addBtn" type="primary" @click="addFn" :loading="infoExportAllLoading">新增<i class="el-icon-plus el-icon--right"></i></el-button>
  21. <!-- <el-button type="primary" @click="exportTaskInfoAll()" :loading="infoExportAllLoading">批量转换经纬度<i class="el-icon-download el-icon--right"></i></el-button> -->
  22. <el-button v-if="permissionList.addBtn" class="filter-item" type="primary" size="small" icon="el-icon-upload" @click="importUserOpen">批量导入 </el-button>
  23. </template>
  24. <template slot="menu" slot-scope="scope">
  25. <el-button v-if="permissionList.editBtn" type="text" size="small" icon="el-icon-edit" @click="editFn(scope.row, scope.index)">编辑 </el-button>
  26. </template>
  27. </avue-crud>
  28. </basic-container>
  29. <!-- 新增/编辑 -->
  30. <el-dialog v-if="showAddDialog" :visible.sync="showAddDialog" :before-close="closeFn" :title="dialogType === 'add' ? '新增' : '编辑'">
  31. <el-form ref="formRef" :model="form" :rules="formRules" label-width="120px">
  32. <el-form-item label="医院名称:" prop="yymc">
  33. <el-input v-model="form.yymc" placeholder="请输入医院名称" />
  34. </el-form-item>
  35. <el-form-item label="医院城市:" prop="city">
  36. <span class="cityColor" @click="toAddress">{{ city }}</span>
  37. </el-form-item>
  38. <el-form-item label="地址:" prop="address">
  39. <div class="map" style="overflow: hidden">
  40. <el-input v-model="addressKeyword" placeholder="请输入地址来直接查找相关位置" clearable style="margin-bottom: 20px">
  41. <el-button slot="append" icon="el-icon-search" @click="getAddressKeyword"></el-button>
  42. </el-input>
  43. <div id="container" style="width: 100%; height: 300px"></div>
  44. </div>
  45. </el-form-item>
  46. <el-row>
  47. <el-col :span="12">
  48. <el-form-item label="纬度:" prop="latitude">
  49. <el-input v-model="form.latitude" placeholder="请输入纬度" />
  50. </el-form-item>
  51. </el-col>
  52. <el-col :span="12">
  53. <el-form-item label="经度:" prop="longitude">
  54. <el-input v-model="form.longitude" placeholder="请输入经度" />
  55. </el-form-item>
  56. </el-col>
  57. </el-row>
  58. <el-row>
  59. <el-col :span="12">
  60. <el-form-item label="医院规模:" prop="yygm">
  61. <el-select style="width: 100%" v-model="form.yygm" placeholder="请选择医院规模">
  62. <el-option v-for="item in yygmOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  63. </el-select>
  64. </el-form-item>
  65. </el-col>
  66. <el-col :span="12">
  67. <el-form-item label="医院性质:" prop="yyxz">
  68. <el-select style="width: 100%" v-model="form.yyxz" placeholder="请选择医院性质">
  69. <el-option v-for="item in yyxzOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  70. </el-select>
  71. </el-form-item>
  72. </el-col>
  73. </el-row>
  74. <el-row>
  75. <el-col :span="12">
  76. <el-form-item label="医院官网:" prop="website">
  77. <el-input v-model="form.website" placeholder="请输入医院官网" />
  78. </el-form-item>
  79. </el-col>
  80. <el-col :span="12">
  81. <el-form-item label="院病床总数(张):" prop="ybczs">
  82. <el-select style="width: 100%" v-model="form.ybczs" placeholder="请选择院病床总数(张)">
  83. <el-option v-for="item in ybczsOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  84. </el-select>
  85. </el-form-item>
  86. </el-col>
  87. </el-row>
  88. </el-form>
  89. <div slot="footer" class="dialog-footer">
  90. <el-button type="primary" size="small" @click="saveFn">保 存</el-button>
  91. <el-button type="default" size="small" @click="closeFn">取 消</el-button>
  92. </div>
  93. </el-dialog>
  94. <el-dialog :visible.sync="addInp" :close-on-click-modal="true" title="请选择区域">
  95. <v-distpicker type="mobile" @selected="selected" v-show="addInp"> </v-distpicker>
  96. <div slot="footer" class="dialog-footer">
  97. <el-button type="default" size="small" @click="addInp === false">取消</el-button>
  98. </div>
  99. </el-dialog>
  100. <!-- 批量导入 -->
  101. <el-dialog title="批量导入医院" :visible.sync="importUserShow" width="400px" :before-close="importUserClose">
  102. <div class="tc" v-loading="importLoading">
  103. <el-upload
  104. class="upload-demo"
  105. :action="uploadUrl"
  106. :headers="headers"
  107. :before-upload="importUserBefore"
  108. :on-preview="handleUserPreview"
  109. :on-remove="handleUserRemove"
  110. :before-remove="beforeUserRemove"
  111. :limit="1"
  112. :on-exceed="handleUserExceed"
  113. :on-success="handleUserSuccess"
  114. :on-error="handleUserError"
  115. accept=".xls,.xlsx"
  116. :file-list="userFileList"
  117. :show-file-list="false"
  118. >
  119. <el-button size="small" type="primary">批量导入(*.xlsx,*.xls)</el-button>
  120. </el-upload>
  121. <br />
  122. <el-button type="text" @click="handleDown">下载模板</el-button>
  123. </div>
  124. </el-dialog>
  125. <el-dialog title="上传失败!" width="600px" :close-on-click-modal="false" @close="importErrorDialogClose" :visible.sync="importErrorDialogShow" center>
  126. <avue-crud style="width: 100%" :data="importErrorTableData" :option="option1"> </avue-crud>
  127. </el-dialog>
  128. </div>
  129. </template>
  130. <script>
  131. import { fetchList, getObj, addObj, putObj, delObj, parseAddress } from '@/api/wmdahospital';
  132. import { tableOption } from '@/const/crud/wmdahospital';
  133. import { mapGetters } from 'vuex';
  134. import VDistpicker from 'v-distpicker';
  135. import maps from 'qqmap';
  136. import { getDictType } from '@/api/common';
  137. import { importErrorTableOption } from '@/const/crud/admin/user';
  138. import { templateDownload } from '@/api/assignPoints/currency/manList';
  139. import store from '@/store';
  140. export default {
  141. name: 'wmdahospital',
  142. data() {
  143. return {
  144. map: null,
  145. markerLayer: null, // 标记
  146. geocoder: null, // 正逆地址解析类
  147. getAddress: null,
  148. getAddCode: null,
  149. addressKeyword: '', // 地址
  150. infoExportAllLoading: false,
  151. shopInfo: {
  152. lng: null,
  153. lat: null
  154. },
  155. city: '请选择区域',
  156. addInp: false,
  157. mask: false,
  158. form: {
  159. yymc: '',
  160. address: '',
  161. latitude: '',
  162. longitude: '',
  163. yygm: '',
  164. yyxz: '',
  165. website: '',
  166. ybczs: ''
  167. },
  168. searchForm: {},
  169. tableData: [],
  170. page: {
  171. total: 0, // 总页数
  172. currentPage: 1, // 当前页数
  173. pageSize: 20 // 每页显示多少条
  174. },
  175. tableLoading: false,
  176. tableOption: tableOption,
  177. showAddDialog: false,
  178. dialogType: 'add',
  179. formRules: {
  180. yymc: [{ required: true, message: '请输入医院名称', trigger: 'blur' }],
  181. latitude: [{ required: true, message: '请输入纬度', trigger: 'blur' }],
  182. longitude: [{ required: true, message: '请输入经度', trigger: 'blur' }],
  183. yygm: [{ required: true, message: '请选择医院规模', trigger: 'blur' }],
  184. yyxz: [{ required: true, message: '请选择医院性质', trigger: 'blur' }]
  185. },
  186. yygmOptions: [],
  187. yyxzOptions: [],
  188. ybczsOptions: [],
  189. headers: {
  190. Authorization: 'Bearer ' + store.getters.access_token
  191. },
  192. importUserShow: false,
  193. importLoading: false,
  194. userFileList: [],
  195. importErrorDialogShow: false,
  196. uploadUrl: '',
  197. importErrorTableData: [],
  198. option1: importErrorTableOption
  199. };
  200. },
  201. computed: {
  202. ...mapGetters(['permissions', 'userInfo']),
  203. permissionList() {
  204. return {
  205. addBtn: this.vaildData(this.permissions.admin_wmdahospital_add, false),
  206. delBtn: this.userInfo.roles.includes(2),
  207. editBtn: this.vaildData(this.permissions.admin_wmdahospital_edit, false)
  208. };
  209. }
  210. },
  211. components: {
  212. VDistpicker
  213. },
  214. mounted() {
  215. this.loadMapScript();
  216. },
  217. destroyed() {
  218. if (this.map) {
  219. this.map.destroy();
  220. this.map = null;
  221. }
  222. },
  223. methods: {
  224. getDict() {
  225. getDictType({ type: 'hospital_level' }).then((res) => {
  226. this.yygmOptions = res.data.data;
  227. });
  228. getDictType({ type: 'hospital_xz' }).then((res) => {
  229. this.yyxzOptions = res.data.data;
  230. });
  231. getDictType({ type: 'hospital_cw' }).then((res) => {
  232. this.ybczsOptions = res.data.data;
  233. });
  234. },
  235. addFn() {
  236. this.getDict();
  237. this.addressKeyword = '';
  238. this.city = '请选择区域';
  239. this.dialogType = 'add';
  240. this.showAddDialog = true;
  241. this.$nextTick(() => {
  242. this.init();
  243. });
  244. },
  245. editFn(row, index) {
  246. this.getDict();
  247. this.addressKeyword = row.address || '';
  248. this.city = (row.province || '') + ' ' + (row.city || '') + ' ' + (row.area || '');
  249. this.form = Object.assign({}, row);
  250. this.dialogType = 'edit';
  251. this.showAddDialog = true;
  252. this.$nextTick(() => {
  253. this.init();
  254. });
  255. },
  256. saveFn() {
  257. this.form.address = this.addressKeyword;
  258. if (!this.addressKeyword) {
  259. this.$message.error('地址不能为空');
  260. return;
  261. }
  262. if (!this.city) {
  263. this.$message.error('区域不能为空');
  264. return;
  265. }
  266. if (this.dialogType === 'add') {
  267. addObj(this.form)
  268. .then((res) => {
  269. this.$message.success('添加成功');
  270. this.showAddDialog = false;
  271. this.form = {
  272. yymc: '',
  273. address: '',
  274. latitude: '',
  275. longitude: '',
  276. yygm: '',
  277. yyxz: '',
  278. website: '',
  279. ybczs: ''
  280. };
  281. this.getList(this.page);
  282. })
  283. .catch(() => {});
  284. } else {
  285. putObj(this.form)
  286. .then((res) => {
  287. this.$message.success('修改成功');
  288. this.showAddDialog = false;
  289. this.form = {
  290. yymc: '',
  291. address: '',
  292. latitude: '',
  293. longitude: '',
  294. yygm: '',
  295. yyxz: '',
  296. website: '',
  297. ybczs: ''
  298. };
  299. this.getList(this.page);
  300. })
  301. .catch(() => {});
  302. }
  303. },
  304. closeFn() {
  305. this.map.off('click', this.clickHandler);
  306. this.map = null;
  307. this.markerLayer = null; // 标记
  308. this.geocoder = null; // 正逆地址解析类
  309. this.showAddDialog = false;
  310. this.form = {
  311. yymc: '',
  312. address: '',
  313. latitude: '',
  314. longitude: '',
  315. yygm: '',
  316. yyxz: '',
  317. website: '',
  318. ybczs: ''
  319. };
  320. },
  321. // https://lbs.qq.com/webApi/javascriptGL/glGuide/glBasic
  322. loadMapScript() {
  323. var script = document.createElement('script');
  324. script.type = 'text/javascript';
  325. script.src = 'https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=C4ABZ-HWE3X-5DW4J-ZO4GH-IQBWK-TXF2X';
  326. document.body.appendChild(script);
  327. },
  328. init() {
  329. //定义地图中心点坐标
  330. var center = new TMap.LatLng(22.547931568083015, 114.1306221485138);
  331. //定义map变量,调用 TMap.Map() 构造函数创建地图
  332. this.map = new TMap.Map(document.getElementById('container'), {
  333. center: center, //设置地图中心点坐标
  334. zoom: 16, //设置地图缩放级别
  335. viewMode: '2D' // 可选 2D 3D
  336. });
  337. // 新建一个正逆地址解析类
  338. this.geocoder = new TMap.service.Geocoder();
  339. //初始化marker图层
  340. this.markerLayer = new TMap.MultiMarker({
  341. id: 'marker-layer',
  342. map: this.map
  343. });
  344. this.map.on('click', this.clickHandler);
  345. this.getAddressKeyword();
  346. },
  347. clickHandler(event) {
  348. // 获取点击后的地图坐标
  349. this.shopInfo.lng = event.latLng.getLng();
  350. this.shopInfo.lat = event.latLng.getLat();
  351. this.getAddressCode();
  352. this.removeMarker();
  353. this.markerLayer.add({
  354. position: event.latLng
  355. });
  356. },
  357. //移除marker事件
  358. removeMarker() {
  359. const markList = this.markerLayer.getGeometries();
  360. if (this.markerLayer && markList.length > 0) {
  361. this.markerLayer.remove(markList[0].id);
  362. }
  363. },
  364. getAddressKeyword() {
  365. const convert = () => {
  366. // 将给定的地址转换为坐标位置
  367. const cityText = this.city === '请选择区域' ? '' : this.city.replace(/\s*/g, '') || '';
  368. const address = cityText + this.addressKeyword;
  369. this.geocoder.getLocation({ address }).then((result) => {
  370. this.removeMarker();
  371. this.$nextTick(() => {
  372. this.markerLayer.updateGeometries([
  373. {
  374. position: result.result.location // 将得到的坐标位置用点标记标注在地图上
  375. }
  376. ]);
  377. });
  378. this.map.setCenter(result.result.location);
  379. this.form.longitude = result.result.location.lng;
  380. this.form.latitude = result.result.location.lat;
  381. });
  382. };
  383. if (this.addressKeyword) {
  384. convert();
  385. }
  386. },
  387. // 通过坐标获得地址
  388. getAddressCode() {
  389. var lat = parseFloat(this.shopInfo.lat);
  390. var lng = parseFloat(this.shopInfo.lng);
  391. var location = new TMap.LatLng(lat, lng);
  392. this.geocoder
  393. .getAddress({ location: location }) // 将给定的坐标位置转换为地址
  394. .then((result) => {
  395. this.addressKeyword = result.result.address || '';
  396. });
  397. this.form.longitude = lng;
  398. this.form.latitude = lat;
  399. },
  400. toAddress() {
  401. this.mask = true;
  402. this.addInp = true;
  403. },
  404. // 省市区三级联动
  405. selected(data) {
  406. this.mask = false;
  407. this.addInp = false;
  408. this.city = data.province.value + ' ' + data.city.value + ' ' + data.area.value;
  409. this.form.province = data.province.value;
  410. this.form.city = data.city.value;
  411. this.form.area = data.area.value;
  412. //this.city = data.area.value
  413. this.addressKeyword = data.province.value + data.city.value + data.area.value;
  414. this.getAddressKeyword();
  415. },
  416. getList(page, params) {
  417. this.tableLoading = true;
  418. fetchList(
  419. Object.assign(
  420. {
  421. current: page.currentPage,
  422. size: page.pageSize
  423. },
  424. params,
  425. this.searchForm
  426. )
  427. )
  428. .then((response) => {
  429. this.tableData = response.data.data.records;
  430. this.page.total = response.data.data.total;
  431. this.tableLoading = false;
  432. })
  433. .catch(() => {
  434. this.tableLoading = false;
  435. });
  436. },
  437. rowDel: function (row, index) {
  438. this.$confirm('是否确认删除ID为' + row.id, '提示', {
  439. confirmButtonText: '确定',
  440. cancelButtonText: '取消',
  441. type: 'warning'
  442. })
  443. .then(function () {
  444. return delObj(row.id);
  445. })
  446. .then((data) => {
  447. this.$message.success('删除成功');
  448. this.getList(this.page);
  449. });
  450. },
  451. sizeChange(pageSize) {
  452. this.page.pageSize = pageSize;
  453. },
  454. currentChange(current) {
  455. this.page.currentPage = current;
  456. },
  457. searchChange(form, done) {
  458. this.searchForm = form;
  459. this.page.currentPage = 1;
  460. this.getList(this.page, form);
  461. done();
  462. },
  463. refreshChange() {
  464. this.getList(this.page);
  465. },
  466. exportTaskInfoAll() {
  467. this.infoExportAllLoading = true;
  468. parseAddress()
  469. .then((data) => {
  470. this.$message.success('地址转换成功');
  471. this.infoExportAllLoading = false;
  472. })
  473. .catch(() => {
  474. loading();
  475. });
  476. },
  477. // 以下为批量导入功能
  478. importUserClose() {
  479. this.importUserShow = false;
  480. this.userFileList = [];
  481. },
  482. importUserBefore() {
  483. this.importLoading = true;
  484. },
  485. beforeUserRemove(file, fileList) {
  486. return this.$confirm(`确定移除 ${file.name}?`);
  487. },
  488. handleUserRemove(file, fileList) {
  489. console.log(file, fileList);
  490. },
  491. handleUserPreview(file) {
  492. console.log(file);
  493. },
  494. handleUserExceed(files, fileList) {
  495. this.$message.warning('最多上传一个文件');
  496. },
  497. handleUserSuccess(res, file) {
  498. this.importLoading = false;
  499. if (res.code === 1) {
  500. this.userFileList = [];
  501. if (res.data && res.data.code === 'FAILURE') {
  502. // 上传有错误
  503. this.importErrorTableData = [];
  504. const errorData = res.data.data;
  505. if (Object.keys(errorData).length > 0) {
  506. Object.keys(errorData).forEach((ele) => {
  507. this.importErrorTableData.push({ reason: ele, index: errorData[ele] });
  508. });
  509. }
  510. console.log(this.importErrorTableData);
  511. this.importErrorDialogShow = true;
  512. } else {
  513. // 上传失败
  514. this.$message.error(res.msg);
  515. }
  516. } else {
  517. this.$message.success('上传成功!');
  518. this.importUserClose();
  519. this.getList(this.page);
  520. }
  521. },
  522. handleUserError(err) {
  523. this.$message.error('上传失败!');
  524. this.importLoading = false;
  525. },
  526. handleDown() {
  527. this.importLoading = true;
  528. templateDownload({
  529. fileName: 'batch_create_hospital_template.xlsx'
  530. })
  531. .then((response) => {
  532. this.importLoading = false;
  533. let url = window.URL.createObjectURL(new Blob([response.data]));
  534. let link = document.createElement('a');
  535. link.style.display = 'none';
  536. link.href = url;
  537. link.setAttribute('download', '批量导入医院模板.xlsx');
  538. document.body.appendChild(link);
  539. link.click();
  540. document.body.removeChild(link);
  541. })
  542. .catch(() => {
  543. this.importLoading = false;
  544. });
  545. },
  546. importErrorDialogClose() {
  547. this.importErrorDialogShow = false;
  548. },
  549. // 导入用户
  550. importUserOpen() {
  551. this.uploadUrl = 'admin/wmdahospital/batch';
  552. this.importUserShow = true;
  553. }
  554. }
  555. };
  556. </script>
  557. <style lang="less">
  558. .distpicker-address-wrapper {
  559. color: #000000 !important;
  560. }
  561. .distpicker-address-wrapper .address-header ul li.active {
  562. color: #000000 !important;
  563. }
  564. .cityColor {
  565. color: #2d8cf0;
  566. }
  567. </style>