DesignPic.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. import React, { useState, useEffect } from 'react'
  2. import { Button, Table, Input, Message, Loading, Radio } from '@alifd/next';
  3. import { CustomMessage } from '@/components/CustomMessage'
  4. import { urlToFile } from './utils';
  5. const RadioGroup = Radio.Group
  6. export default function DesignPic() {
  7. const [uploadUrl, setUploadUrl] = useState<string>("");
  8. // 添加状态管理表格数据
  9. const [tableData, setTableData] = useState<any[]>([]);
  10. const [loading, setLoading] = useState(false);
  11. const [isPage, setIsPage] = useState(true);
  12. const [btnLoading, setBtnLoading] = useState(false);
  13. const [isPreImg, setIsPreImg] = useState(-1)
  14. useEffect(() => {
  15. is_temu_design()
  16. }, [])
  17. const is_temu_design = async () => {
  18. try {
  19. // 获取当前活动标签页
  20. const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
  21. // 获取页面域名
  22. if (tab.url) {
  23. const url = tab.url
  24. console.log('当前页面域名:', url);
  25. if (url.includes('main/customize-goods')) {
  26. // temu定制页面
  27. setIsPage(false)
  28. }
  29. }
  30. } catch (error) {
  31. console.error('获取当前页面域名时出错:', error);
  32. }
  33. }
  34. const order_render = (value: any, index: any, record: any) => {
  35. return (
  36. <div>{index + 1}</div>
  37. )
  38. }
  39. const design_render = (value: any, index: any, record: any) => {
  40. return <>
  41. {
  42. record.pre_info && record.pre_info.pre_pic ? <>
  43. <div className='flex items-center justify-center'><img className='size-20' src={record.pre_info?.pre_pic} /></div></> : <>
  44. <span>暂无图片</span>
  45. </>
  46. }
  47. </>
  48. }
  49. const pic_render = (value: any, index: any, record: any) => {
  50. return (
  51. <>
  52. {
  53. record.design_pic ? <>
  54. <div className='flex items-center justify-center'><img className='size-20' src={record.design_pic} /></div></> : <>
  55. <span>暂无图片</span>
  56. </>
  57. }
  58. </>
  59. );
  60. }
  61. const text_render = (value: any, index: any, record: any) => {
  62. return (
  63. <p>{record.text}</p>
  64. );
  65. }
  66. // 修改图片名称渲染函数
  67. const name_render = (value: any, index: number, record: any) => {
  68. return <>
  69. <div>{record.order_no}</div>
  70. </>
  71. };
  72. // 修改图片标签渲染函数
  73. const label_render = (value: any, index: number, record: any) => {
  74. return <>
  75. <div>{record.region}</div>
  76. </>
  77. };
  78. // 完善删除功能
  79. const handleDelete = (index: number, record: any) => {
  80. // 方案1: 直接删除
  81. const newData = [...tableData];
  82. newData.splice(index, 1);
  83. setTableData(newData);
  84. // 方案2: 带确认的删除 (可选)
  85. // if (window.confirm(`确定要删除 "${record.title.name}" 吗?`)) {
  86. // const newData = [...tableData];
  87. // newData.splice(index, 1);
  88. // setTableData(newData);
  89. // Message.success('删除成功');
  90. // }
  91. };
  92. // 删除操作渲染函数
  93. const option_render = (value: any, index: number, record: any) => {
  94. return (
  95. record.is_upload ? <p className='text-green-600'>上传成功</p> :
  96. <a
  97. className='text-red-600 cursor-pointer'
  98. onClick={() => handleDelete(index, record)}
  99. >
  100. 删除
  101. </a>
  102. );
  103. };
  104. // 添加行索引以确保正确删除
  105. const dataSourceWithIndex = tableData.map((item, index) => ({
  106. ...item,
  107. index
  108. }));
  109. const getPicData = async () => {
  110. const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
  111. // 获取页面域名
  112. if (tab.url) {
  113. const url = tab.url
  114. if (!url.includes('main/customize-goods')) {
  115. // temu定制页面
  116. CustomMessage.error('当前页面不是定制页面')
  117. return
  118. }
  119. }
  120. try {
  121. setLoading(true)
  122. // 获取上传路径
  123. fetch('http://merchant.landwu.com/api/photo/getUploadUrl', {
  124. method: 'POST',
  125. headers: {
  126. 'Content-Type': 'application/json',
  127. },
  128. body: JSON.stringify({
  129. api_token: localStorage.getItem('access_token'),
  130. }),
  131. }).then(resjson => {
  132. return resjson.json();
  133. }).then(res => {
  134. if (res.data) {
  135. const { data = {} } = res;
  136. const { url = null } = data;
  137. setUploadUrl(url)
  138. }
  139. }).catch(e => {
  140. setLoading(false)
  141. })
  142. setTableData([])
  143. setBtnLoading(true)
  144. // 获取当前活动标签页
  145. const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
  146. if (tab.id) {
  147. // 发送消息到content script请求获取tbody数据
  148. const response = await browser.tabs.sendMessage(tab.id, { action: "getTbodyData" });
  149. // 在控制台输出结果
  150. if (response.tables && response.tables.length > 0) {
  151. // console.log("找到的表格数据(包含背景图片):");
  152. // 显式声明类型
  153. let tables_arr: {
  154. good_info: any;
  155. sku_info: any;
  156. order_no: any;
  157. design_sku: any;
  158. pre_design_info: any;
  159. design_info: { region: any; text: any; picture: any }[];
  160. }[] = [];
  161. let table_boj: {
  162. good_info: any;
  163. sku_info: any;
  164. order_no: any;
  165. design_sku: any;
  166. pre_design_info: any;
  167. design_info: { region: any; text: any; picture: any }[];
  168. } = {
  169. good_info: null,
  170. sku_info: null,
  171. order_no: null,
  172. design_sku: null,
  173. pre_design_info: null,
  174. design_info: []
  175. };
  176. response.tables.forEach((table_node: any, index: number) => {
  177. console.log(`表格 ${index + 1}:`, table_node);
  178. // 处理成接口参数
  179. // 处理成数组对象
  180. if (table_node.length > 4) {
  181. if (table_boj.design_info.length) {
  182. // 一个商品的定制信息
  183. tables_arr.push(table_boj)
  184. // 重置
  185. table_boj = {
  186. good_info: null,
  187. sku_info: null,
  188. order_no: null,
  189. design_sku: null,
  190. pre_design_info: null,
  191. design_info: []
  192. }
  193. }
  194. table_boj = {
  195. good_info: table_node[1],
  196. sku_info: table_node[2],
  197. order_no: table_node[3],
  198. design_sku: table_node[4],
  199. pre_design_info: table_node[13],
  200. design_info: [{
  201. region: table_node[9],
  202. text: table_node[10],
  203. picture: table_node[11],//11
  204. }]
  205. }
  206. } else {
  207. table_boj.design_info.push({
  208. region: table_node[0],
  209. text: table_node[1],
  210. picture: table_node[2],//2
  211. })
  212. }
  213. // 循环结束
  214. if (index == response.tables.length - 1) {
  215. if (table_boj.design_info.length) {
  216. // 一个商品的定制信息
  217. tables_arr.push(table_boj)
  218. }
  219. }
  220. // good_info sku_info design_info: region text picture
  221. });
  222. console.log(tables_arr, "tables_arrddd")
  223. // 上传图片和文字的参数处理
  224. let pic_text_arr: any[] = [], img_values: any = {}
  225. tables_arr.forEach(item => {
  226. let order_no = "", data_params: any[] = [], pre_info = {};
  227. if (item.order_no && item.order_no != "-") {
  228. order_no = item.order_no.substring(0, 15); // 取前15位
  229. }
  230. if (order_no) {
  231. let pre_pic = ""
  232. if (item.pre_design_info && item.pre_design_info != '-') {
  233. let pre_pic1 = (item.pre_design_info.backgroundImages as { url: string }[] | undefined)?.[0]?.url ?? '';
  234. pre_pic = pre_pic1.split('&imageMogr2/thumbnail')[0]
  235. if (!img_values[pre_pic]) {
  236. img_values[pre_pic] = {
  237. src: pre_pic,
  238. width: '',
  239. height: '',
  240. order_no,
  241. file: ''
  242. }
  243. }
  244. if (pre_pic) {
  245. pre_info = {
  246. pre_pic,
  247. width: 400,
  248. height: 400
  249. }
  250. }
  251. } else {
  252. pre_pic = ""
  253. }
  254. }
  255. // 定制信息处理
  256. if (item.design_info.length) {
  257. item.design_info.forEach(items => {
  258. const param: any = {}
  259. if (order_no || item.design_sku) {
  260. let bg_pic = ""
  261. if (items.picture && items.picture != '-') {
  262. let bg_pic1 = (items.picture.backgroundImages as { url: string }[] | undefined)?.[0]?.url ?? '';
  263. bg_pic = bg_pic1.split('&imageMogr2/thumbnail')[0]
  264. if (!img_values[bg_pic]) {
  265. img_values[bg_pic] = {
  266. src: bg_pic,
  267. width: '',
  268. height: ''
  269. }
  270. }
  271. if (bg_pic) {
  272. param['width'] = 400;
  273. param['height'] = 400;
  274. }
  275. } else {
  276. bg_pic = ""
  277. }
  278. let des_text = ""
  279. if (items.text && items.text != '-') {
  280. const des_text1 = items.text.split(";}")[1]
  281. des_text = des_text1.split("查看")[0]
  282. } else {
  283. des_text = ""
  284. }
  285. param['region'] = items.region
  286. param['text'] = des_text
  287. param['design_pic'] = bg_pic
  288. data_params.push(param)
  289. }
  290. })
  291. }
  292. if (order_no || item.design_sku) {
  293. pic_text_arr.push({
  294. order_no,
  295. design_sku: item.design_sku,
  296. pre_info,
  297. data_params
  298. })
  299. }
  300. })
  301. // console.log(img_values,'-img_valuesimg_values')
  302. let img_values1: any = {}, cIndex = 0
  303. Object.values(img_values).forEach((item, n) => {
  304. if (!img_values1[n]) {
  305. img_values1[n] = item
  306. }
  307. })
  308. const queueImg = () => {
  309. if (img_values1[cIndex]) {
  310. cIndex++
  311. queueImg()
  312. return
  313. {
  314. let img = new Image()
  315. img.onload = function () {
  316. img_values1[cIndex].width = img.width
  317. img_values1[cIndex].height = img.height
  318. img_values[img_values1[cIndex]?.src]['width'] = img.width
  319. img_values[img_values1[cIndex]?.src]['height'] = img.height
  320. cIndex++
  321. queueImg()
  322. }
  323. img.onerror = function () {
  324. cIndex++
  325. queueImg()
  326. }
  327. img.src = img_values1[cIndex]?.src
  328. }
  329. } else {
  330. getImgWh()
  331. }
  332. }
  333. queueImg()
  334. function getImgWh() {
  335. setBtnLoading(false)
  336. setLoading(false)
  337. // 筛选备货单号存在的值
  338. let pic_text_arr1 = pic_text_arr.filter(item => item.order_no && item.order_no != "-")
  339. // 为存在图片的对象添加宽高
  340. // pic_text_arr1.map(item => {
  341. // item.data_params.map((items: any) => {
  342. // if (items.design_pic) {
  343. // if (img_values[items.design_pic]) {
  344. // items['width'] = img_values[items.design_pic].width
  345. // items['height'] = img_values[items.design_pic].height
  346. // items['file'] = img_values[items.design_pic].file
  347. // }
  348. // }
  349. // })
  350. // if (item.pre_info && item.pre_info.pre_pic) {
  351. // item.pre_info.width = img_values[item.pre_info.pre_pic].width
  352. // item.pre_info.height = img_values[item.pre_info.pre_pic].height
  353. // }
  354. // })
  355. // 数据显示
  356. let table_data: any[] = []
  357. pic_text_arr1.forEach(item => {
  358. if (item.data_params.length) {
  359. item.data_params.forEach((items: any) => {
  360. table_data.push({
  361. order_no: item.order_no,
  362. pre_info: item.pre_info,
  363. design_sku: item.design_sku,
  364. design_pic: items.design_pic,
  365. file: items.file,
  366. region: items.region,
  367. text: items.text,
  368. width: items.width || "",
  369. height: items.height || "",
  370. })
  371. })
  372. }
  373. })
  374. console.log(pic_text_arr1, "pic_text_arr1aaa", img_values1)
  375. setTableData(table_data)
  376. }
  377. } else {
  378. console.log("页面上未找到表格数据");
  379. CustomMessage.error("页面上未找到表格数据");
  380. }
  381. }
  382. } catch (error) {
  383. setBtnLoading(false)
  384. setLoading(false)
  385. console.error("获取表格数据失败:", error);
  386. CustomMessage.error("获取表格数据失败");
  387. }
  388. };
  389. interface imgValuesProps {
  390. [key: string]: {
  391. design_pic: string,
  392. design_sku: string
  393. url: string
  394. width: string | number
  395. height: string | number
  396. },
  397. }
  398. const uploadPic = () => {
  399. try {
  400. const token = localStorage.getItem("access_token");
  401. console.log(tableData, "tableData")
  402. let table_obj: { [key: string]: any } = {},
  403. img_values: imgValuesProps = {}, fetch_url = "";
  404. tableData.forEach(item => {
  405. if (item.design_pic && !img_values[item.design_pic]) {
  406. img_values[item.design_pic] = {
  407. design_pic: item.design_pic,
  408. design_sku: item.design_sku,
  409. url: '',
  410. width: '',
  411. height: ''
  412. }
  413. }
  414. if (!table_obj[item.design_sku]) {
  415. table_obj[item.design_sku] = ""
  416. }
  417. if (table_obj[item.design_sku]) {
  418. const data_params = table_obj[item.design_sku].data_params
  419. data_params.push({
  420. design_pic: item.design_pic,
  421. file: item.file || "",
  422. region: item.region,
  423. text: item.text,
  424. width: item.width,
  425. height: item.height,
  426. })
  427. table_obj[item.design_sku] = {
  428. ...table_obj[item.design_sku],
  429. data_params,
  430. }
  431. } else {
  432. table_obj[item.design_sku] = {
  433. order_no: item.order_no,
  434. design_sku: item.design_sku,
  435. data_params: [{
  436. design_pic: item.design_pic,
  437. file: item.file || '',
  438. region: item.region,
  439. text: item.text,
  440. width: item.width,
  441. height: item.height,
  442. }]
  443. }
  444. }
  445. })
  446. if (uploadUrl.includes('http')) {
  447. fetch_url = `${uploadUrl}photo/temuUploadStepTwo`
  448. } else {
  449. fetch_url = `https:${uploadUrl}photo/temuUploadStepTwo`
  450. }
  451. if (!uploadUrl) {
  452. CustomMessage.warning("上传地址丢失,请再次点击获取定制数据!")
  453. return
  454. }
  455. if (Object.values(img_values) && Object.values(img_values).length) {
  456. let queImgs: any = {}, queImgsCindex = 0, imgall_values: any = {};
  457. Object.values(img_values).forEach((v, cindex) => {
  458. if (!queImgs[cindex]) {
  459. queImgs[cindex] = {
  460. design_pic: v.design_pic,
  461. design_sku: v.design_sku,
  462. }
  463. }
  464. })
  465. const imgUpdate = () => {
  466. if (queImgs[queImgsCindex]) {
  467. setLoading(true)
  468. urlToFile(queImgs[queImgsCindex]?.design_pic, `${new Date().getTime()}.png`).then(file => {
  469. if (!file) {
  470. queImgsCindex++
  471. imgUpdate()
  472. return
  473. }
  474. // photo/temuUploadStepTwo
  475. let form_data = new FormData()
  476. form_data.append('api_token', token as string)
  477. form_data.append('file', file)
  478. fetch(`https:${uploadUrl}photo/temuUploadStepOne`, {
  479. method: 'POST',
  480. headers: {
  481. // 'Content-Type': 'multipart/form-data',
  482. 'access-control-allow-credentials': 'true',
  483. 'X-CSRF-TOKEN': 'Bearer ' + token,
  484. 'Authorization': 'Bearer ' + token,
  485. },
  486. body: form_data,
  487. }).then(resjson => {
  488. return resjson.json();
  489. }).then(res => {
  490. const { data } = res;
  491. if (!imgall_values[queImgs[queImgsCindex].design_sku]) {
  492. imgall_values[queImgs[queImgsCindex].design_sku] = {
  493. }
  494. }
  495. if (!imgall_values[queImgs[queImgsCindex].design_sku][queImgs[queImgsCindex].design_pic]) {
  496. imgall_values[queImgs[queImgsCindex].design_sku][queImgs[queImgsCindex].design_pic] = {
  497. ...data
  498. }
  499. }
  500. if (imgall_values[queImgs[queImgsCindex].design_sku][queImgs[queImgsCindex].design_pic]) {
  501. imgall_values[queImgs[queImgsCindex].design_sku][queImgs[queImgsCindex].design_pic] = {
  502. ...data
  503. }
  504. }
  505. queImgsCindex++
  506. imgUpdate()
  507. }).catch(error => {
  508. queImgsCindex++
  509. imgUpdate()
  510. })
  511. })
  512. } else {
  513. for (let key in table_obj) {
  514. if (imgall_values[key]) {
  515. if (table_obj[key].data_params && Array.isArray(table_obj[key].data_params) && table_obj[key].data_params.length) {
  516. const design_pic = table_obj[key].data_params.find((item: any) => item.design_pic)
  517. if (design_pic) {
  518. table_obj[key].data_params.map((item: any) => {
  519. if (item.design_pic && imgall_values[key][item.design_pic]) {
  520. const { width, height, url, type } = imgall_values[key][item.design_pic]
  521. item['width'] = width
  522. item['height'] = height
  523. item['file'] = url
  524. item['type'] = type
  525. console.log(item)
  526. }
  527. return item
  528. })
  529. }
  530. }
  531. }
  532. }
  533. console.log(imgall_values, '-imgall_values', table_obj)
  534. imgFetchFun(table_obj)
  535. }
  536. }
  537. imgUpdate()
  538. return
  539. }
  540. function imgFetchFun(table_obj: any) {
  541. const table_params = Object.values(table_obj)
  542. let arr_index = 0
  543. const FetchFun = (params: any) => {
  544. params['api_token'] = token
  545. fetch(fetch_url, {
  546. method: 'POST',
  547. headers: {
  548. 'Content-Type': 'application/json',
  549. },
  550. body: JSON.stringify(params),
  551. }).then(resjson => {
  552. return resjson.json();
  553. }).then(res => {
  554. if (res.data.error && res.data.error.length) {
  555. // 未上传成功的 图片
  556. console.log(res.data.error, "arr_index", arr_index)
  557. res.data.error.forEach((item: any) => {
  558. CustomMessage.error(item.design_pic)
  559. })
  560. }
  561. arr_index++
  562. if (arr_index < table_params.length) {
  563. // tableData循环出备货单相同的数据,修改表格操作数据 is_upload
  564. let tableData1 = tableData.map(item => {
  565. if (item.order_no == params.order_no) {
  566. console.log(item.order_no, "item.order_no", params)
  567. item.is_upload = true
  568. }
  569. return item
  570. })
  571. // console.log(tableData1,"tableData111")
  572. setTableData(tableData1)
  573. FetchFun(table_params[arr_index])
  574. } else {
  575. setLoading(false)
  576. setTableData([])
  577. CustomMessage.success("图片上传结束")
  578. }
  579. }).catch(err => {
  580. console.log(err, "err")
  581. arr_index++
  582. if (arr_index < table_params.length) {
  583. FetchFun(table_params[arr_index])
  584. } else {
  585. setLoading(false)
  586. setTableData([])
  587. CustomMessage.success("图片上传结束")
  588. }
  589. })
  590. }
  591. setLoading(true)
  592. FetchFun(table_params[arr_index])
  593. }
  594. imgFetchFun(table_obj)
  595. } catch (error) {
  596. console.error(error, "报错")
  597. }
  598. }
  599. const evChangeRadio = (e: any) => {
  600. let ev = e && e.target ? e.target.value : e
  601. setIsPreImg(ev)
  602. }
  603. return (
  604. <Loading visible={loading} fullScreen>
  605. <div className='w-3xl h-fit'>
  606. <Button
  607. type="primary"
  608. disabled={isPage || btnLoading}
  609. style={{ marginBottom: '10px', marginRight: '10px' }}
  610. onClick={() => {
  611. getPicData()
  612. }}>获取定制数据</Button>
  613. <Button
  614. disabled={(!tableData.length) || isPage || btnLoading}
  615. type="primary"
  616. style={{ marginBottom: '10px', marginRight: '10px' }}
  617. onClick={() => {
  618. uploadPic()
  619. }}>上传定制信息</Button>
  620. <span className='text-red-500'>ps:需在Temu定制商品内容页面获取数据,且上传图片过程中请勿关闭插件</span>
  621. <div className='flex items-center' style={{ marginBottom: '10px' }}>
  622. <p style={{ fontSize: 14, marginBottom: 0, marginRight: 5 }}>是否获取合成预览图: </p>
  623. <RadioGroup value={isPreImg} onChange={(e) => {
  624. evChangeRadio(e)
  625. }} aria-labelledby="groupId">
  626. <Radio value={1}>
  627. </Radio>
  628. <Radio value={-1}>
  629. </Radio>
  630. </RadioGroup>
  631. </div>
  632. <Table dataSource={dataSourceWithIndex}>
  633. <Table.Column title="序号" cell={order_render} align="center" />
  634. {
  635. isPreImg == 1 && <Table.Column title="定制区域图" cell={design_render} align="center" />
  636. }
  637. <Table.Column title="图片" cell={pic_render} align="center" />
  638. <Table.Column title="备货单" cell={name_render} align="center" />
  639. <Table.Column title="定制区域" cell={label_render} align="center" />
  640. <Table.Column title="文字" cell={text_render} align="center" />
  641. <Table.Column title="操作" cell={option_render} align="center" />
  642. </Table>
  643. <p className='text-red-500'>ps:若获取不到数据,可能是插件未连接成功,请刷新页面</p>
  644. </div>
  645. </Loading>
  646. )
  647. }