log_publish.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. import { Textarea, View, Image, Input, ScrollView } from "@tarojs/components";
  2. import './log_publish.scss'
  3. import { rpxToPx } from "@/utils/tools";
  4. import { useEffect, useState } from "react";
  5. import { useSelector } from "react-redux";
  6. import Taro from "@tarojs/taro";
  7. import { IconArrow, IconClose } from "@/components/basic/Icons";
  8. import NewButton, { NewButtonType } from "@/_health/base/new_button";
  9. import showAlert from "@/components/basic/Alert";
  10. import { IconCamera, IconClock } from "@/_record/components/record_icon";
  11. import { useTranslation } from "react-i18next";
  12. import { MainColorType } from "@/context/themes/color";
  13. import dayjs from "dayjs";
  14. import { addEvents } from "@/services/health";
  15. import { TimeFormatter } from "@/utils/time_format";
  16. import { BASE_IMG_URL, baseUrl } from "@/services/http/api";
  17. import LogTags from "./log_tags";
  18. import PickerCard from "@/_record/components/picker_card";
  19. import showActionSheet from "@/components/basic/ActionSheet";
  20. let useRoute;
  21. let useNavigation;
  22. let LinearGradient;
  23. if (process.env.TARO_ENV == 'rn') {
  24. useRoute = require("@react-navigation/native").useRoute
  25. useNavigation = require("@react-navigation/native").useNavigation
  26. LinearGradient = require('react-native-linear-gradient').default
  27. }
  28. export default function LogPublish(props: { onClose: any, scenario: string, tag?: any, tags: any, addTag: boolean }) {
  29. const scale = '?x-oss-process=image/format,jpg/resize,w_400'
  30. const systemInfo: any = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync();
  31. const navigationBarHeight = systemInfo.statusBarHeight + 44;
  32. const screenHeight = systemInfo.screenHeight
  33. const [showChoose, setShowChoose] = useState(false)
  34. const record = useSelector((state: any) => state.record);
  35. const [pics, setPics] = useState<any>([])
  36. const [desc, setDesc] = useState('')
  37. const [focus, setFocus] = useState(false)
  38. const [time, setTime] = useState(dayjs().format('HH:mm'))
  39. const [selDate, setSelDate] = useState(dayjs().format('YYYY-MM-DD'))
  40. const [enterTimestmap] = useState(new Date().getTime())
  41. const [scenario, setScenarios] = useState(props.scenario)
  42. const [posting, setPosting] = useState(false)
  43. const [title, setTitle] = useState(props.tag ? props.tag.title : '')
  44. const [step, setStep] = useState(props.addTag ? 1 : 2)
  45. const [inputFocus, setInputFocus] = useState(false)
  46. const [showTimePicker, setShowTimePicker] = useState(false)
  47. const [addEnter, setAddEnter] = useState(props.addTag ?? false)
  48. const { t } = useTranslation()
  49. useEffect(() => {
  50. if (step == 1) {
  51. setTimeout(() => {
  52. setInputFocus(true)
  53. }, 300)
  54. }
  55. else {
  56. setAddEnter(false)
  57. }
  58. }, [step])
  59. function addImage(isCamera) {
  60. var source: any = isCamera ? ['camera'] : ['album']
  61. Taro.chooseImage({
  62. count: 9 - pics.length,
  63. sizeType: ['compressed'],
  64. // mediaType: ['image'],
  65. sourceType: source,
  66. success: async function (res) {
  67. const results = await Promise.all(res.tempFiles.map(getImageInfo));
  68. chooseSuccess(results, true)
  69. // checkAuthorized()
  70. },
  71. fail: function (res) {
  72. }
  73. })
  74. }
  75. async function chooseSuccess(res, isAlbum) {
  76. // const filePaths = res.map(file => file.path
  77. // // process.env.TARO_ENV === 'rn' || isFilePath ? file.path : file.tempFilePath
  78. // )
  79. Taro.showLoading({
  80. title: t('health.uploading')
  81. })
  82. try {
  83. const uploadedUrls = await Promise.all(res.map(path => uploadFile2(path, isAlbum ? 'album' : 'camera')))
  84. setPics([...pics, ...uploadedUrls])
  85. Taro.hideLoading()
  86. } catch (error) {
  87. console.error('Error uploading files:', error)
  88. Taro.hideLoading()
  89. }
  90. }
  91. function uploadFile2(obj: any, source: string): Promise<string> {
  92. return new Promise((resolve, reject) => {
  93. var path = obj.path
  94. const dot = path.lastIndexOf('.')
  95. const fileExt = dot > 0 ? path.substring(dot) : ''
  96. Taro.request({
  97. method: 'GET',
  98. timeout: 30000,
  99. url: `${baseUrl}/api/thirdparty/aliyun/oss-form-upload`,
  100. header: {
  101. 'Authorization': 'bearer ' + global.token
  102. },
  103. data: {
  104. type: 'FOOD_JOURNAL',
  105. file_ext: fileExt
  106. },
  107. success: (rsp) => {
  108. if (rsp.statusCode !== 200) {
  109. reject(new Error(t('health.networkError')))
  110. return
  111. }
  112. Taro.uploadFile({
  113. timeout: 30000,
  114. url: rsp.data.upload_url,
  115. filePath: path,
  116. name: 'file',
  117. formData: rsp.data.fields,
  118. success: () => {
  119. var temp = JSON.parse(JSON.stringify(obj))
  120. temp.url = rsp.data.view_url
  121. // resolve(rsp.data.view_url)
  122. resolve(temp)
  123. },
  124. fail: (error) => {
  125. reject(error)
  126. }
  127. })
  128. },
  129. fail: reject
  130. })
  131. })
  132. }
  133. const getImageInfo = (src) => {
  134. const { tempFilePath, path } = src
  135. return new Promise((resolve) => {
  136. Taro.getImageInfo({
  137. src: tempFilePath ? tempFilePath : path,
  138. success: (result) => resolve({
  139. height: result.height,
  140. width: result.width,
  141. orientation: result.orientation,
  142. path: result.path,
  143. type: result.type
  144. }),
  145. fail: (error) => resolve({
  146. height: 1024,
  147. width: 1024,
  148. orientation: 'up',
  149. path: tempFilePath,
  150. type: 'unknown'
  151. }),
  152. });
  153. });
  154. };
  155. function tapPic() {
  156. var list = process.env.TARO_ENV == 'weapp' ? [t('health.add_photos'), t('health.camera2'), t('health.import_chat')] :
  157. [t('health.add_photos'), t('health.camera2')]
  158. showActionSheet({
  159. title: '',
  160. // showActionSheetWithOptions: showActionSheetWithOptions,
  161. itemList: list,
  162. success: function (res) {
  163. switch (res) {
  164. case 0:
  165. addImage(false)
  166. break;
  167. case 1:
  168. addImage(true)
  169. break;
  170. case 2:
  171. Taro.chooseMessageFile({
  172. count: 9 - pics.length,
  173. type: 'image',
  174. success: async function (res) {
  175. const results = await Promise.all(res.tempFiles.map(getImageInfo));
  176. chooseSuccess(results, true)
  177. },
  178. fail(res) {
  179. },
  180. })
  181. break;
  182. case 3:
  183. // setImgUrl('')
  184. break;
  185. }
  186. }
  187. })
  188. }
  189. function save() {
  190. var date = TimeFormatter.stringToDate(selDate, time)
  191. date.setMilliseconds(new Date(enterTimestmap).getMilliseconds())
  192. var params: any = {
  193. scenario: scenario, //ACTIVITY
  194. type: 'POINT',
  195. sub_type: scenario,
  196. title: title,
  197. time: {
  198. start_timestamp: date.getTime()
  199. }
  200. }
  201. // if (router.params.join_id) {
  202. // params.join_key = router.params.join_id
  203. // }
  204. var moment: any = {
  205. description: desc,
  206. }
  207. if (pics.length > 0) {
  208. var picList: any = []
  209. pics.map((item) => {
  210. picList.push({
  211. url: item.url,
  212. type: item.url.indexOf('mp4') != -1 ? 'video' : 'image',
  213. source: 'album',
  214. width: item.width,
  215. height: item.height,
  216. format: item.format
  217. })
  218. })
  219. moment.media = picList
  220. }
  221. params.moment = moment
  222. // if (event_id && event_id != 'undefined') {
  223. // params.event_id = event_id
  224. // }
  225. // if (is_temp) {
  226. // params.event = window == 'EAT' ? 'EAT_CUSTOM' : 'ACTIVE_CUSTOM'
  227. // }
  228. // params.op_page = window == 'EAT' ? 'HOME_EAT' : 'HOME_ACTIVE'
  229. params.extra = {
  230. set_time: global.set_time ? global.set_time : new Date().getTime(),
  231. confirm_time: new Date().getTime()
  232. }
  233. if (posting) return
  234. setPosting(true)
  235. // Taro.showLoading({
  236. // title: t('health.uploading')
  237. // })
  238. addEvents(params).then(res => {
  239. // setShowResult(true)
  240. // setResult(res)
  241. setPosting(false)
  242. // Taro.hideLoading()
  243. Taro.reLaunch({
  244. url: '/pages/moment/moment'
  245. })
  246. props.onClose()
  247. }).catch(e => {
  248. setPosting(false)
  249. // Taro.hideLoading()
  250. })
  251. }
  252. function getDate() {
  253. var sel = dayjs(selDate)
  254. var now = dayjs().format('YYYY-MM-DD')
  255. const yesterday = dayjs().subtract(1, 'day');
  256. if (sel.format('YYYY-MM-DD') == now) {
  257. return ''
  258. }
  259. if (yesterday.format('YYYY-MM-DD') == sel.format('YYYY-MM-DD')) {
  260. return global.language == 'en' ? 'Yesterday ' : '昨天 '
  261. }
  262. else {
  263. return global.language == 'en' ? sel.format('MMM D ') : sel.format('MMMD日 ')
  264. }
  265. }
  266. function getBackground() {
  267. var time = record.time
  268. if (!time) return '#fff'
  269. const { background_colors } = time
  270. if (!background_colors) {
  271. return '#fff'
  272. }
  273. else if (background_colors.length == 1) {
  274. return background_colors[0]
  275. }
  276. return `linear-gradient(to bottom, ${background_colors[0]}, ${background_colors[1]})`
  277. }
  278. function bgView() {
  279. if (process.env.TARO_ENV == 'weapp') {
  280. return <View className="main_bg" style={{ background: getBackground() }} />
  281. }
  282. var time = record.time
  283. if (!time) return <View />
  284. const { background_colors } = time
  285. if (!background_colors) {
  286. return <View />
  287. }
  288. else if (background_colors.length == 1) {
  289. return <View />
  290. }
  291. return <LinearGradient style={{
  292. position: 'absolute',
  293. left: 0,
  294. top: 0,
  295. width: rpxToPx(750),
  296. height: screenHeight,
  297. zIndex: 0,
  298. pointerEvents: 'none'
  299. }}
  300. colors={[background_colors[0], background_colors[1]]}
  301. start={{ x: 0, y: 0 }}
  302. end={{ x: 0, y: 1 }} pointerEvents="none" />
  303. }
  304. function publishCard() {
  305. return <View className="cardShowAni" style={{ paddingTop: rpxToPx(26) }}>
  306. <View className="content_card">
  307. <View style={{ display: 'flex', flexDirection: 'row' }} >
  308. <NewButton type={NewButtonType.custom}
  309. onClick={() => {
  310. // setStep(0)
  311. // setFocus(false)
  312. setStep(0)
  313. }}>
  314. <View className="sel_tag">
  315. {/* <View className="h34 bold">{selPostCount}</View>
  316. <View className="h34" style={{ marginLeft: rpxToPx(6) }}>次</View>
  317. <View style={{
  318. width: rpxToPx(2),
  319. height: rpxToPx(32),
  320. backgroundColor: '#000',
  321. opacity: 0.2,
  322. marginLeft: rpxToPx(12),
  323. marginRight: rpxToPx(12)
  324. }} /> */}
  325. <View className="h34 bold">{title}</View>
  326. <View style={{ width: rpxToPx(6) }} />
  327. <IconArrow width={rpxToPx(34)} color='#000' />
  328. </View>
  329. </NewButton>
  330. </View>
  331. {/* <Input placeholder="hhhhhhh" style={{textAlign:'center'}}/> */}
  332. <Textarea placeholder={t('health.add_text')} className="textarea2 h44"
  333. placeholder-style="color:rgba(0,0,0,0.2)"
  334. value={desc}
  335. focus={focus}
  336. onBlur={() => {
  337. setFocus(false)
  338. }}
  339. onInput={e => {
  340. setDesc(e.detail.value)
  341. }} />
  342. <View className="form2">
  343. {
  344. pics.map((item, index) => {
  345. return <View className="cover1" key={index}>
  346. <Image src={item.url + scale} mode="aspectFill" className="cover2" key={index} onClick={() => {
  347. Taro.previewImage({
  348. current: pics[index].url,
  349. urls: pics.map(file => file.url)
  350. })
  351. }} />
  352. <View className="cover_del" onClick={() => {
  353. showAlert({
  354. title: t('health.del_title'),
  355. content: '',
  356. cancelText: t('health.del_cancel'),
  357. confirmText: t('health.del_confirm'),
  358. showCancel: true,
  359. confirm: () => {
  360. var array = JSON.parse(JSON.stringify(pics))
  361. array.splice(index, 1)
  362. setPics(array)
  363. }
  364. })
  365. }}>
  366. <View className="cover_del_btn">
  367. <IconClose width={10} height={10} color="#fff" />
  368. </View>
  369. </View>
  370. </View>
  371. })
  372. }
  373. {
  374. pics.length < 9 && <NewButton
  375. type={NewButtonType.custom}
  376. onClick={tapPic}>
  377. <View className="cover1" style={{}}><IconCamera color="#000" width={rpxToPx(48)} /></View>
  378. </NewButton>
  379. }
  380. </View>
  381. </View>
  382. <View className="time_view" onClick={() => {
  383. setShowTimePicker(true)
  384. }}>
  385. <IconClock width={rpxToPx(36)} color={MainColorType.black_25} />
  386. <View className="h30" style={{ opacity: 0.25, marginLeft: rpxToPx(12) }}>{t('health.time')}</View>
  387. <View style={{ flex: 1 }} />
  388. <View className="h30" style={{ opacity: 0.25 }}>{getDate() + time}</View>
  389. <IconArrow width={rpxToPx(34)} color={MainColorType.black_25} />
  390. <View className="border_footer_line" style={{ left: rpxToPx(48) }} />
  391. </View>
  392. {/* {
  393. router.params.join_id && <FollowInfo user={long.follow} />
  394. } */}
  395. </View>
  396. }
  397. function publishBtn() {
  398. return <View className="main_footer" style={{ backgroundColor: 'transparent' }}>
  399. <NewButton
  400. type={NewButtonType.fill}
  401. color={MainColorType.orange}
  402. width={rpxToPx(646)}
  403. height={rpxToPx(108)}
  404. title={t('health.share_to_moment')}
  405. onClick={save}
  406. />
  407. </View>
  408. }
  409. function tagsContent() {
  410. return <ScrollView scrollY style={{height:screenHeight-navigationBarHeight}}>
  411. <View style={{ display: 'flex', alignItems: 'center', flexDirection: 'column', marginTop: rpxToPx(26) }}>
  412. <Image src={BASE_IMG_URL + 'tag.svg'} style={{ width: rpxToPx(96), height: rpxToPx(96), marginBottom: rpxToPx(24) }} />
  413. <View className="h50 bold" style={{ textAlign: 'center', width: rpxToPx(600), }}>{t('health.add_a_tag')}</View>
  414. <LogTags tags={props.tags} scenario={props.scenario} showPublish={(scenario, item, addTag) => {
  415. // props.showPublish(scenario, item, tags)
  416. if (addTag) {
  417. setStep(1)
  418. return
  419. }
  420. setTitle(item.title)
  421. setStep(2)
  422. }} />
  423. </View>
  424. </ScrollView>
  425. }
  426. function addTagContent() {
  427. return <View className="cardShowAni" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: rpxToPx(300) }}>
  428. <View className="input_form">
  429. <Input className="input_content h36" placeholder={t('health.custom_name')} value={title}
  430. placeholderClass="input_content_placeholder"
  431. focus={inputFocus}
  432. autoFocus={inputFocus}
  433. maxlength={30}
  434. onBlur={() => {
  435. setInputFocus(false)
  436. }}
  437. onInput={(e: any) => {
  438. setTitle(e.target.value)
  439. }} />
  440. <View className="form_btns">
  441. <View className="form_cancel">
  442. <NewButton btnStyle={{ flex: 1 }} type={NewButtonType.img}
  443. onClick={() => {
  444. if (addEnter) {
  445. props.onClose()
  446. return
  447. }
  448. setStep(0)
  449. }}
  450. >
  451. <View className="h30 bold">{t('health.cancel')}</View>
  452. </NewButton>
  453. </View>
  454. <View className="form_cancel">
  455. <NewButton btnStyle={{ flex: 1 }} type={NewButtonType.img}
  456. onClick={() => {
  457. if (!title || title.length == 0) return
  458. // setChooseTitle(title)
  459. // setPostCount(1)
  460. setStep(2)
  461. // addTag()
  462. }}
  463. >
  464. <View className={!title || title.length == 0 ? 'form_cancel form_confirm h30 bold' : 'form_cancel h30 bold'}
  465. >{t('health.confirm')}</View>
  466. </NewButton>
  467. </View>
  468. </View>
  469. </View>
  470. </View>
  471. }
  472. return <View className="log_publish_bg" style={{ overflow: showChoose ? 'hidden' : 'visible' }}>
  473. {
  474. bgView()
  475. }
  476. <View className="navi_bar" style={{ height: navigationBarHeight, zIndex: 1000 }}>
  477. <View style={{
  478. position: 'absolute',
  479. left: 0,
  480. right: 0,
  481. bottom: 0,
  482. height: 44,
  483. display: 'flex',
  484. alignItems: 'center',
  485. justifyContent: 'center'
  486. }}>
  487. <View style={{
  488. position: 'absolute',
  489. width: rpxToPx(92),
  490. height: rpxToPx(64),
  491. left: 22,
  492. top: 22 - rpxToPx(32)
  493. }}
  494. onClick={() => {
  495. if (addEnter) {
  496. props.onClose()
  497. return
  498. }
  499. // if (showResult) {
  500. // process.env.TARO_ENV == 'weapp' ? Taro.navigateBack() : navigation.goBack()
  501. // return
  502. // }
  503. if (title && title.length > 0 && step == 0) {
  504. setStep(2)
  505. return
  506. }
  507. if (step == 2 && (desc.length > 0 || pics.length > 0)) {
  508. showAlert({
  509. title: t('health.back_no_save'),
  510. content: '',
  511. showCancel: true,
  512. cancelText: t('health.cancel'),
  513. confirmText: t('health.exit'),
  514. confirm: () => {
  515. props.onClose()
  516. // process.env.TARO_ENV == 'weapp' ? Taro.navigateBack() : navigation.goBack()
  517. }
  518. })
  519. return
  520. }
  521. props.onClose()
  522. // process.env.TARO_ENV == 'weapp' ? Taro.navigateBack() : navigation.goBack()
  523. }}>
  524. <IconClose color={"#000"} width={rpxToPx(64)} height={rpxToPx(64)} />
  525. </View>
  526. </View>
  527. </View>
  528. <View style={{ height: navigationBarHeight }} />
  529. {
  530. step == 2 && publishCard()
  531. }
  532. {
  533. step == 2 && publishBtn()
  534. }
  535. {
  536. step == 0 && tagsContent()
  537. }
  538. {
  539. step == 1 && addTagContent()
  540. }
  541. {
  542. showTimePicker && <PickerCard onClose={() => { setShowTimePicker(false) }}
  543. value={new Date(selDate + 'T' + time + ':00').getTime()}
  544. title={global.language == 'en' ? 'Choose Time' : "选择时间"}
  545. type="datetime"
  546. onConfirm={(e) => {
  547. setSelDate(dayjs(e).format('YYYY-MM-DD'))
  548. setTime(dayjs(e).format('HH:mm'))
  549. setShowTimePicker(false)
  550. }}
  551. />
  552. }
  553. </View>
  554. }