index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. import {
  2. ref,
  3. defineComponent,
  4. computed,
  5. onMounted,
  6. onUnmounted,
  7. reactive,
  8. } from 'vue';
  9. import { useRoute, useRouter } from 'vue-router';
  10. import { upload, UploadData } from '@/api/common';
  11. import cloneDeep from 'lodash/cloneDeep';
  12. import div from '@/components/div';
  13. import { useCommonStore, useIncidentStore } from '@/store';
  14. import QueryMap from '@/components/QueryMap';
  15. import { BaseMediaUrl } from '@/utils/index';
  16. import { api_getusergps } from '@/service/login';
  17. // import MediaUpload from '@/components/MediaUpload';
  18. /** @ts-ignore */
  19. import icon_map_location from '@/assets/icons/home/icon_map_location@2x.png';
  20. /** @ts-ignore */
  21. import icon_location from '@/assets/icons/incident/location.png';
  22. import './index.scss';
  23. import {
  24. DropdownItemOption,
  25. NavBar,
  26. DropdownMenu,
  27. DropdownItem,
  28. Icon,
  29. PullRefresh,
  30. Sticky,
  31. Collapse,
  32. CollapseItem,
  33. Loading,
  34. Form,
  35. Field,
  36. CellGroup,
  37. Popup,
  38. Picker,
  39. ActionBar,
  40. Button,
  41. Notify,
  42. Uploader,
  43. UploaderFileListItem,
  44. Toast,
  45. } from 'vant';
  46. import { isArray } from 'lodash';
  47. export default defineComponent({
  48. name: 'IncidentManagementReport',
  49. setup() {
  50. const store = useIncidentStore();
  51. const commonStore = useCommonStore();
  52. const detail = computed(() => store.incidentDetail?.baseInfo);
  53. const addr = ref('');
  54. const form = ref({ ...detail.value });
  55. const formRef = ref();
  56. const showCreateByPicker = ref(false);
  57. const showCreateByDeptPicker = ref(false);
  58. const showCreateTypeByPicker = ref(false);
  59. const route = useRoute();
  60. const router = useRouter();
  61. const isshowprew = ref(false);
  62. const videosrclist = reactive([]);
  63. const imagessrclist = reactive([]);
  64. const videop = ref();
  65. const videopsrc = ref("");
  66. const handleUpload = (file) => {
  67. upload(file).then((res) => {
  68. let type = /\w+([.jpg|.png|.gif|.swf|.bmp|.jpeg]){1}$/.test(
  69. res.data.fileName?.substr( res.data.fileName?.lastIndexOf('.') + 1 ) ?? '',
  70. );
  71. if (!type) {
  72. videosrclist.push(res.data);
  73. } else {
  74. imagessrclist.push(res.data);;
  75. }
  76. }).catch((error)=>{
  77. });
  78. };
  79. const handleSaveReport = async () => {
  80. const { id } = route.query;
  81. const saveFn = id ? store.putIncidentItem : store.postIncidentItem;
  82. // console.log(formRef);
  83. const result = await saveFn({
  84. ...form.value,
  85. // level: Number(form.value.level) ?? null,
  86. // type: Number(form.value.type) ?? null,
  87. pic: imagessrclist.map((i: UploadData) => i.url).join(','),
  88. video: videosrclist.map((i: UploadData) => i.url).join(','),
  89. source: '1',
  90. // status: route.params.status as unknown as number,
  91. });
  92. if (result) {
  93. router.push("/")
  94. }
  95. };
  96. const getLocation = () => {
  97. api_getusergps().then((res) => {
  98. var lat = null;
  99. var lon = null;
  100. var iserror = false;
  101. try {
  102. lat = res.result[0].lat;
  103. lon = res.result[0].lon;
  104. if (lat == null || lat == undefined) iserror = true;
  105. } catch (e) {
  106. iserror = true;
  107. }
  108. if (!iserror) {
  109. var location = `${lon},${lat}`;
  110. fetch(
  111. `https://minedata.cn/service/lbs/reverse/v1/regeo?location=${location}&key=${window.key}`,
  112. {
  113. method: 'GET',
  114. headers: new Headers({
  115. 'Content-Type': 'application/json',
  116. }),
  117. },
  118. )
  119. .then((res) => res.json())
  120. .then((data) => {
  121. // console.log(data)
  122. // console.log(data.regeocodes[0].formatted_address);
  123. var ll = data.regeocodes[0].formatted_address;
  124. if (!window.map) {
  125. Notify({
  126. type: 'danger',
  127. message: '地图插件初始化异常, 请刷新页面 (Ctrl + R)',
  128. });
  129. return;
  130. }
  131. window.map.flyTo({
  132. center: [
  133. Number(location.split(',')[0]),
  134. Number(location.split(',')[1]),
  135. ],
  136. zoom: 14,
  137. bearing: 0,
  138. pitch: 0,
  139. duration: 2000,
  140. });
  141. if (window.map && window._marker) {
  142. window._marker.remove();
  143. window._marker = null;
  144. }
  145. if (window.map) {
  146. var el = document.createElement('div');
  147. el.id = 'marker';
  148. el.style.backgroundImage = `url(${icon_map_location})`;
  149. el.style.backgroundSize = 'cover';
  150. el.style.width = '24px';
  151. el.style.height = '24px';
  152. el.style.borderRadius = '50%';
  153. const popup = new window.minemap.Popup({
  154. closeOnClick: false,
  155. closeButton: false,
  156. offset: [0, -15],
  157. }).setText(ll);
  158. window._marker = new window.minemap.Marker(el, {
  159. offset: [-12, -12],
  160. })
  161. .setLngLat([
  162. Number(location.split(',')[0]),
  163. Number(location.split(',')[1]),
  164. ])
  165. .setPopup(popup)
  166. .addTo(window.map);
  167. }
  168. window._marker.togglePopup();
  169. // var ll =
  170. // data.regeocodes[0].formatted_address.replaceAll(
  171. // '江苏省宿迁市',
  172. // "");
  173. form.value.locations = location;
  174. form.value.addr = ll;
  175. });
  176. // minemap.service
  177. // .adminByPointData({
  178. // location: `${position.coords.longitude},${position.coords.latitude}`,
  179. // })
  180. // .then(function (response) {
  181. // console.log(response.data);
  182. // })
  183. // .catch(function (error) {
  184. // console.error(error);
  185. // });
  186. // (ee) => {
  187. // console.log(ee);
  188. // },
  189. } else {
  190. Notify({
  191. type: 'danger',
  192. message: 'App不支持地理定位。',
  193. });
  194. }
  195. })
  196. };
  197. onMounted(async () => {
  198. commonStore.getGlobalDict('zhdd_incident_level');
  199. commonStore.getGlobalDict('zhdd_incident_type');
  200. commonStore.getGlobalDict('zhdd_org_upload');
  201. try {
  202. getLocation();
  203. } catch (E) { }
  204. route.query.id && (await store.getIncidentItem(route.query.id as string));
  205. addr.value = detail?.value?.addr ?? '';
  206. if (detail.value) {
  207. form.value = cloneDeep(detail.value);
  208. }
  209. });
  210. onUnmounted(() => {
  211. commonStore.uploadFiles = [];
  212. store.incidentDetail = {};
  213. store.incidentDetail = {
  214. baseInfo: {},
  215. process: [],
  216. task: [],
  217. };
  218. });
  219. return () => (
  220. <div class="incident-report-container ">
  221. <NavBar
  222. title="应急事件上报"
  223. left-arrow
  224. fixed
  225. onClick-left={() => {
  226. // router.push('/home')
  227. uni.postMessage({
  228. data: 'test',
  229. });
  230. uni.navigateBack();
  231. }}
  232. />
  233. <Popup
  234. style="width:100%;background:rgba(0,0,0,0)"
  235. v-model:show={isshowprew.value}
  236. onClose={() => {
  237. videop.value.pause();
  238. }}>
  239. <video
  240. ref={videop}
  241. src={videopsrc.value}
  242. controls
  243. autoplay="true"
  244. style="width:100%"></video>
  245. </Popup>
  246. <Form>
  247. <CellGroup>
  248. <Field
  249. v-model={form.value.name}
  250. name="事件标题"
  251. label="事件标题"
  252. placeholder="请输入"
  253. required
  254. rules={[{ required: true, message: '事件标题必填' }]}
  255. />
  256. {/* <Field
  257. v-model={form.value.createBy}
  258. name="上报人"
  259. label="上报人"
  260. placeholder="请输入"
  261. required
  262. rules={[{ required: true, message: '上报人必填' }]}
  263. />
  264. <Field
  265. v-model={form.value.createDeptText}
  266. name="上报单位"
  267. is-link
  268. readonly
  269. label="上报单位"
  270. placeholder="请选择"
  271. required
  272. rules={[{ required: true, message: '上报单位必填' }]}
  273. onClick={() => (showCreateByDeptPicker.value = true)}
  274. />
  275. <Field
  276. v-model={form.value.typeText}
  277. is-link
  278. readonly
  279. name="事件类型"
  280. label="事件类型"
  281. placeholder="请选择"
  282. required
  283. rules={[{ required: true, message: '事件类型必填' }]}
  284. onClick={() => (showCreateTypeByPicker.value = true)}
  285. />
  286. <Popup
  287. v-model:show={showCreateTypeByPicker.value}
  288. position="bottom">
  289. <Picker
  290. columns={commonStore.globalDict['zhdd_incident_type']?.map(
  291. (i) => ({ text: i.dictLabel, value: i.dictValue }),
  292. )}
  293. onConfirm={(value) => {
  294. form.value.typeText = value.text;
  295. form.value.type = value.value;
  296. console.log(value, '---');
  297. showCreateTypeByPicker.value = false;
  298. }}
  299. onCancel={() => {
  300. showCreateTypeByPicker.value = false;
  301. }}
  302. />
  303. </Popup>
  304. <Field
  305. v-model={form.value.levelText}
  306. is-link
  307. readonly
  308. name="事件等级"
  309. label="事件等级"
  310. placeholder="请选择"
  311. required
  312. rules={[{ required: true, message: '事件等级必填' }]}
  313. onClick={() => (showCreateByPicker.value = true)}
  314. />
  315. <Popup v-model:show={showCreateByPicker.value} position="bottom">
  316. <Picker
  317. columns={commonStore.globalDict['zhdd_incident_level']?.map(
  318. (i) => ({ text: i.dictLabel, value: i.dictValue }),
  319. )}
  320. onConfirm={(value) => {
  321. form.value.levelText = value.text;
  322. form.value.level = value.value;
  323. console.log(value, '---');
  324. showCreateByPicker.value = false;
  325. }}
  326. onCancel={() => {
  327. showCreateByPicker.value = false;
  328. }}
  329. />
  330. </Popup>
  331. <Popup
  332. v-model:show={showCreateByDeptPicker.value}
  333. position="bottom">
  334. <Picker
  335. columns={commonStore.globalDict['zhdd_org_upload']?.map(
  336. (i) => ({ text: i.dictLabel, value: i.dictValue }),
  337. )}
  338. onConfirm={(value) => {
  339. form.value.createDeptText = value.text;
  340. form.value.createDept = value.value;
  341. console.log(value, '---');
  342. showCreateByDeptPicker.value = false;
  343. }}
  344. onCancel={() => {
  345. showCreateByDeptPicker.value = false;
  346. }}
  347. />
  348. </Popup> */}
  349. <Field
  350. v-model={form.value.des}
  351. rows={2}
  352. autosize
  353. name="事件描述"
  354. label="事件描述"
  355. type="textarea"
  356. placeholder="请输入"
  357. />
  358. <div style={"position:relative"}>
  359. <Field
  360. v-model={form.value.addr}
  361. name="事件地点"
  362. label="事件地点"
  363. placeholder="请输入"
  364. required
  365. style={'width:85%'}
  366. onChange={(add: string) => {
  367. addr.value = add.target.value;
  368. }}
  369. rules={[{ required: true, message: '事件地点必填' }]}
  370. />
  371. <Button style={'border:none;position:absolute;right:10px;top:0'} onClick={() => { getLocation();}}>
  372. <img style={'width:20px;height:20px'} src={icon_location} />
  373. </Button>
  374. </div>
  375. <QueryMap
  376. address={addr.value}
  377. v-model:locations={form.value.locations}
  378. v-model:longitude={(form.value.locations ?? '').split(',')[0]}
  379. v-model:latitude={(form.value.locations ?? '').split(',')[1]}
  380. onChoose={(name) => {
  381. form.value.addr = name;
  382. }}
  383. onRestPositon={() => {
  384. addr.value = store.incidentDetail.baseInfo?.addr ?? '';
  385. form.value.addr = store.incidentDetail.baseInfo?.addr;
  386. form.value.locations = store.incidentDetail.baseInfo?.locations;
  387. }}
  388. />
  389. <Field
  390. name="上传图片"
  391. label="上传图片"
  392. placeholder="请输入"
  393. v-slots={{
  394. input: () => (
  395. <Uploader
  396. accept="image/*"
  397. v-model={form.value.pic}
  398. max-size={20 * 1024 * 1024}
  399. onOversize={() => {
  400. Toast('最大支持20M的图片');
  401. }}
  402. onDelete={(filep, detail) => {
  403. imagessrclist.splice(detail.index, 1);
  404. console.log(imagessrclist);
  405. }}
  406. afterRead={(
  407. file: UploaderFileListItem | UploaderFileListItem[],
  408. ) => {
  409. handleUpload((isArray(file) ? file[0] : file).file);
  410. return false;
  411. }}
  412. />
  413. ),
  414. }}
  415. />
  416. <Field
  417. name="上传视频"
  418. label="上传视频"
  419. placeholder="请输入"
  420. v-slots={{
  421. input: () => (
  422. <Uploader
  423. accept="video/*"
  424. v-model={form.value.video}
  425. max-size={100 * 1024 * 1024}
  426. onOversize={() => {
  427. Toast('最大支持100M的视屏');
  428. }}
  429. afterRead={(
  430. file: UploaderFileListItem | UploaderFileListItem[],
  431. ) => {
  432. handleUpload((isArray(file) ? file[0] : file).file);
  433. return false;
  434. }}
  435. onDelete={(filep, detail) => {
  436. videosrclist.splice(detail.index, 1);
  437. console.log(videosrclist);
  438. }}
  439. onClick-preview={(index) => {
  440. isshowprew.value = true;
  441. try {
  442. videopsrc.value = URL.createObjectURL(index.file);
  443. videop.value.play();
  444. videop.value.currentTime = 0;
  445. } catch (error) {}
  446. }}
  447. // v-slots={{
  448. // 'preview-cover': () => (
  449. // <video
  450. // style="width:100%;height:100%"
  451. // controls
  452. // src="https://vod-progressive.akamaized.net/exp=1643032410~acl=%2Fvimeo-prod-skyfire-std-us%2F01%2F2673%2F7%2F188365455%2F623960082.mp4~hmac=e4a0fb352e805ff4fe74317441bf6f6deb059508cc7bb2f7aa92884023b13818/vimeo-prod-skyfire-std-us/01/2673/7/188365455/623960082.mp4?filename=Emoji+Saver+-+Drama.mp4"></video>
  453. // ),
  454. // }}
  455. />
  456. ),
  457. }}
  458. />
  459. </CellGroup>
  460. <div style={{ height: '80px' }}></div>
  461. <ActionBar>
  462. <div style={{ padding: '10px 20px' }}>
  463. <Button block type="primary" onClick={handleSaveReport}>
  464. 提交
  465. </Button>
  466. </div>
  467. </ActionBar>
  468. </Form>
  469. </div>
  470. );
  471. },
  472. });