tousujianyi.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. const app = getApp();
  2. Page({
  3. data: {
  4. contact: '',
  5. phone: '',
  6. description: '',
  7. imageList: [],
  8. showNotification: true,
  9. countDown: 3,
  10. isFormValid: false,
  11. category: '投诉',
  12. categoryValue: '1',
  13. isPreviewMode: false,
  14. title: '',
  15. content: '',
  16. replyTime: '',
  17. replyContent: '',
  18. formSubmitted: false,
  19. isSubmitting: false,
  20. lastSubmitTime: 0,
  21. categoryMap: [
  22. {name: '投诉', value: '1'},
  23. {name: '建议', value: '2'}
  24. ],
  25. },
  26. onLoad: function (options) {
  27. this.startCountDown();
  28. // 检查是否是预览模式
  29. if (options.mode === 'preview') {
  30. const id = options.id;
  31. // 获取数据
  32. this.getDataById(id);
  33. // 设置为预览模式
  34. this.setData({
  35. isPreviewMode: true
  36. });
  37. } else {
  38. // 非预览模式才检查表单有效性
  39. this.checkFormValidity();
  40. }
  41. },
  42. onShow: function () {
  43. // 检查是否是从成功页面返回
  44. if (this.data.formSubmitted) {
  45. // 重置表单数据
  46. this.resetForm();
  47. // 重置提交状态标记
  48. this.setData({
  49. formSubmitted: false
  50. });
  51. }
  52. },
  53. startCountDown: function () {
  54. let that = this;
  55. let timer = setInterval(function () {
  56. if (that.data.countDown > 0) {
  57. that.setData({
  58. countDown: that.data.countDown - 1
  59. });
  60. } else {
  61. clearInterval(timer);
  62. }
  63. }, 1000);
  64. },
  65. goBack: function () {
  66. wx.navigateBack();
  67. },
  68. radioChange: function (e) {
  69. const value = e.detail.value;
  70. const selectedCategory = this.data.categoryMap.find(item => item.value === value || item.name === value);
  71. this.setData({
  72. category: selectedCategory.name,
  73. categoryValue: selectedCategory.value
  74. });
  75. this.checkFormValidity();
  76. },
  77. inputContact: function (e) {
  78. this.setData({
  79. contact: e.detail.value
  80. });
  81. this.checkFormValidity();
  82. },
  83. inputPhone: function (e) {
  84. const value = e.detail.value;
  85. const phoneNumber = value.replace(/\D/g, '');
  86. this.setData({
  87. phone: phoneNumber
  88. });
  89. this.checkFormValidity();
  90. },
  91. validatePhone: function (phone) {
  92. const phoneReg = /^1[3-9]\d{9}$/;
  93. return phoneReg.test(phone);
  94. },
  95. inputDescription: function (e) {
  96. this.setData({
  97. description: e.detail.value
  98. });
  99. this.checkFormValidity();
  100. },
  101. chooseImage: function () {
  102. let that = this;
  103. if (that.data.imageList.length >= 10) {
  104. wx.showToast({
  105. title: '最多只能上传10张图片',
  106. icon: 'none'
  107. });
  108. return;
  109. }
  110. wx.chooseMedia({
  111. count: 10 - that.data.imageList.length,
  112. mediaType: ['image'],
  113. sourceType: ['album', 'camera'],
  114. sizeType: ['compressed'],
  115. success: function (res) {
  116. let tempFiles = res.tempFiles;
  117. let validFiles = [];
  118. for (let i = 0; i < tempFiles.length; i++) {
  119. const file = tempFiles[i];
  120. if (file.size <= 10 * 1024 * 1024) {
  121. validFiles.push(file);
  122. } else {
  123. wx.showToast({
  124. title: '图片大小不能超过10M',
  125. icon: 'none'
  126. });
  127. }
  128. }
  129. if (validFiles.length > 0) {
  130. wx.showLoading({
  131. title: '图片压缩中...',
  132. mask: true
  133. });
  134. let processedFiles = new Array(validFiles.length);
  135. let processCount = 0;
  136. // 处理每个图片
  137. validFiles.forEach((file, index) => {
  138. that.compressImage(file.tempFilePath).then(compressedPath => {
  139. processCount++;
  140. // 获取压缩后图片的信息
  141. wx.getFileInfo({
  142. filePath: compressedPath,
  143. success: (fileInfo) => {
  144. console.log('原始大小:', file.size / 1024, 'KB');
  145. console.log('压缩后大小:', fileInfo.size / 1024, 'KB');
  146. // 在对应位置添加压缩后的图片路径
  147. processedFiles[index] = {
  148. tempFilePath: compressedPath,
  149. size: fileInfo.size
  150. };
  151. // 检查是否所有图片都已处理完成
  152. if (processCount === validFiles.length) {
  153. // 过滤掉可能的空值,并按顺序合并
  154. let newImageList = that.data.imageList.concat(processedFiles.filter(item => item));
  155. that.setData({
  156. imageList: newImageList
  157. });
  158. wx.hideLoading();
  159. }
  160. },
  161. fail: (err) => {
  162. console.error('获取文件信息失败:', err);
  163. processCount++;
  164. // 如果失败,也要检查是否处理完所有图片
  165. if (processCount === validFiles.length) {
  166. wx.hideLoading();
  167. }
  168. }
  169. });
  170. }).catch(err => {
  171. console.error('图片压缩失败:', err);
  172. processCount++;
  173. if (processCount === validFiles.length) {
  174. wx.hideLoading();
  175. }
  176. });
  177. });
  178. }
  179. }
  180. });
  181. },
  182. // 使用canvas进行图片压缩
  183. compressImage: function(imagePath) {
  184. return new Promise((resolve, reject) => {
  185. // 获取图片信息
  186. wx.getImageInfo({
  187. src: imagePath,
  188. success: (res) => {
  189. // 先获取原图大小,单位KB
  190. wx.getFileInfo({
  191. filePath: imagePath,
  192. success: (fileInfo) => {
  193. const originalSize = fileInfo.size / 1024; // 原始大小,单位KB
  194. // 根据原图大小确定压缩质量
  195. let targetQuality = 0.8; // 默认压缩质量
  196. if (originalSize <= 500) {
  197. // 如果原图已经小于500KB,保持较高质量
  198. targetQuality = 0.9;
  199. } else if (originalSize <= 1024) {
  200. // 1MB以下,适当压缩
  201. targetQuality = 0.7;
  202. } else if (originalSize <= 2048) {
  203. // 2MB以下,中等压缩
  204. targetQuality = 0.5;
  205. } else if (originalSize <= 5120) {
  206. // 5MB以下,较大压缩
  207. targetQuality = 0.3;
  208. } else {
  209. // 超过5MB,大幅压缩
  210. targetQuality = 0.2;
  211. }
  212. // 创建canvas上下文
  213. wx.createSelectorQuery()
  214. .select('#compressCanvas')
  215. .fields({ node: true, size: true })
  216. .exec((canvasRes) => {
  217. if (!canvasRes || !canvasRes[0] || !canvasRes[0].node) {
  218. // 如果找不到canvas节点,则返回原图
  219. resolve(imagePath);
  220. return;
  221. }
  222. const canvas = canvasRes[0].node;
  223. const ctx = canvas.getContext('2d');
  224. const image = canvas.createImage();
  225. image.onload = () => {
  226. // 计算要缩放的尺寸,保持宽高比
  227. let ratio = 1;
  228. // 根据原图大小调整尺寸
  229. if (originalSize > 5120) { // 5MB以上
  230. ratio = Math.min(1, 1200 / res.width, 1200 / res.height);
  231. } else if (originalSize > 2048) { // 2MB以上
  232. ratio = Math.min(1, 1500 / res.width, 1500 / res.height);
  233. } else if (originalSize > 1024) { // 1MB以上
  234. ratio = Math.min(1, 1800 / res.width, 1800 / res.height);
  235. } else {
  236. ratio = Math.min(1, 2000 / res.width, 2000 / res.height);
  237. }
  238. const targetWidth = Math.round(res.width * ratio);
  239. const targetHeight = Math.round(res.height * ratio);
  240. // 设置canvas尺寸
  241. canvas.width = targetWidth;
  242. canvas.height = targetHeight;
  243. // 清除画布
  244. ctx.clearRect(0, 0, targetWidth, targetHeight);
  245. // 绘制图片
  246. ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
  247. // 压缩参数
  248. const compressOptions = {
  249. canvas: canvas,
  250. width: targetWidth,
  251. height: targetHeight,
  252. destWidth: targetWidth,
  253. destHeight: targetHeight,
  254. fileType: 'jpg',
  255. quality: targetQuality
  256. };
  257. // 第一次尝试压缩
  258. const tryCompress = (quality, attempt = 1) => {
  259. compressOptions.quality = quality;
  260. wx.canvasToTempFilePath({
  261. ...compressOptions,
  262. success: (result) => {
  263. // 检查压缩后的大小
  264. wx.getFileInfo({
  265. filePath: result.tempFilePath,
  266. success: (compressedInfo) => {
  267. const compressedSize = compressedInfo.size / 1024; // 压缩后大小,单位KB
  268. console.log(`压缩后大小 (质量:${quality})`, compressedSize, 'KB');
  269. // 如果压缩后大小仍大于500KB且尝试次数未达上限,继续压缩
  270. if (compressedSize > 500 && attempt < 3) {
  271. // 递减质量
  272. const newQuality = Math.max(0.1, quality - 0.2);
  273. console.log(`尝试重新压缩,质量降低至${newQuality}`);
  274. tryCompress(newQuality, attempt + 1);
  275. }
  276. // 如果压缩后小于200KB且质量非最高,尝试提高质量
  277. else if (compressedSize < 200 && quality < 0.9 && attempt < 3) {
  278. // 递增质量
  279. const newQuality = Math.min(0.9, quality + 0.2);
  280. console.log(`尝试重新压缩,质量提高至${newQuality}`);
  281. tryCompress(newQuality, attempt + 1);
  282. }
  283. else {
  284. // 返回压缩后的图片
  285. resolve(result.tempFilePath);
  286. }
  287. },
  288. fail: (error) => {
  289. console.error('获取压缩后图片信息失败:', error);
  290. resolve(result.tempFilePath);
  291. }
  292. });
  293. },
  294. fail: (error) => {
  295. console.error('Canvas转图片失败:', error);
  296. // 如果转换失败则返回原图
  297. resolve(imagePath);
  298. }
  299. });
  300. };
  301. // 开始尝试压缩
  302. tryCompress(targetQuality);
  303. };
  304. image.onerror = () => {
  305. console.error('图片加载失败');
  306. resolve(imagePath);
  307. };
  308. image.src = imagePath;
  309. });
  310. },
  311. fail: (error) => {
  312. console.error('获取原始图片信息失败:', error);
  313. resolve(imagePath);
  314. }
  315. });
  316. },
  317. fail: (error) => {
  318. console.error('获取图片信息失败:', error);
  319. resolve(imagePath);
  320. }
  321. });
  322. });
  323. },
  324. previewImage: function (e) {
  325. let index = e.currentTarget.dataset.index;
  326. wx.previewImage({
  327. current: this.data.imageList[index],
  328. urls: this.data.imageList
  329. });
  330. },
  331. deleteImage: function (e) {
  332. let index = e.currentTarget.dataset.index;
  333. let imageList = this.data.imageList;
  334. imageList.splice(index, 1);
  335. this.setData({
  336. imageList: imageList
  337. });
  338. },
  339. checkFormValidity: function () {
  340. const {
  341. contact,
  342. phone,
  343. description,
  344. category
  345. } = this.data;
  346. // 只检查必填项:联系人、联系电话和内容说明
  347. const isValid = contact.trim() !== '' && this.validatePhone(phone) && description.trim() !== '';
  348. this.setData({
  349. isFormValid: isValid
  350. });
  351. return isValid;
  352. },
  353. onInputChange: function (e) {
  354. const {
  355. field
  356. } = e.currentTarget.dataset;
  357. const {
  358. value
  359. } = e.detail;
  360. this.setData({
  361. [field]: value
  362. });
  363. this.checkFormValidity();
  364. },
  365. bindPickerChange: function (e) {
  366. this.checkFormValidity();
  367. },
  368. // 添加submitForm方法
  369. submitForm: function () {
  370. // 如果正在提交中,直接返回
  371. if (this.data.isSubmitting) {
  372. return;
  373. }
  374. if (!this.checkFormValidity()) {
  375. let errorMsg = '';
  376. if (!this.data.contact.trim()) {
  377. errorMsg = '请填写联系人';
  378. } else if (!this.validatePhone(this.data.phone)) {
  379. errorMsg = '请输入正确的联系电话';
  380. } else if (!this.data.description.trim()) {
  381. errorMsg = '请填写内容说明';
  382. }
  383. wx.showToast({
  384. title: errorMsg,
  385. icon: 'none'
  386. });
  387. return;
  388. }
  389. const now = Date.now();
  390. const lastSubmitTime = this.data.lastSubmitTime;
  391. // 如果距离上次提交时间小于 2 秒,直接返回
  392. if (now - lastSubmitTime < 2000) {
  393. // wx.showToast({
  394. // title: '请勿重复提交',
  395. // icon: 'none',
  396. // });
  397. return;
  398. }
  399. // 更新上次提交时间
  400. this.setData({
  401. lastSubmitTime: now,
  402. });
  403. const fileManager = wx.getFileSystemManager();
  404. this.data.imageList.map(imgInfo => {
  405. const base64 = fileManager.readFileSync(imgInfo.tempFilePath, 'base64');
  406. imgInfo.base64 = base64;
  407. return imgInfo;
  408. })
  409. const submitData = {
  410. category: this.data.categoryValue,
  411. contact: this.data.contact,
  412. phone: this.data.phone,
  413. description: this.data.description,
  414. images: this.data.imageList,
  415. userName: app.globalData.currentAccountInfo.username,
  416. userNum: app.globalData.currentAccountInfo.usernumber
  417. };
  418. debugger
  419. // 设置正在提交中 防止重复点击提交按钮
  420. this.setData({
  421. isSubmitting: true,
  422. });
  423. console.log('提交的数据:', submitData);
  424. wx.showLoading({
  425. title: '提交中...',
  426. });
  427. wx.request({
  428. url: app.globalData.interfaceUrls.feedback,
  429. method: 'POST',
  430. header: {
  431. 'content-type': 'application/json', // 默认值
  432. 'token': app.globalData.userWxInfo.token,
  433. 'source': "wc",
  434. '!SAAS_LOGIN_TOKEN_!': app.globalData.currentAccountInfo.dsKey
  435. },
  436. data: submitData,
  437. success(res) {
  438. wx.hideLoading();
  439. if (res.data.code == '200') {
  440. wx.navigateTo({
  441. url: '/pages/tousujianyiSuccess/tousujianyiSuccess',
  442. });
  443. }
  444. },
  445. fail(error) {
  446. wx.hideLoading()
  447. utils.simleInfo('登记失败,请稍后再试')
  448. },
  449. complete: () => {
  450. this.setData({
  451. isSubmitting: false, // 提交完成,重置标志位,可继续提交
  452. formSubmitted: true // 在提交成功后设置标记,返回将重置表单
  453. });
  454. },
  455. })
  456. // 在提交成功后设置标记,返回将重置表单
  457. // this.setData({
  458. // formSubmitted: true
  459. // });
  460. },
  461. submitRepair: function () {
  462. this.submitForm();
  463. },
  464. // 根据ID获取数据
  465. getDataById: function (id) {
  466. // 这里应该是从服务器获取数据
  467. // 但为了演示,我们从本地数据中获取
  468. const pages = getCurrentPages();
  469. const prevPage = pages[pages.length - 2]; // 获取上一个页面
  470. if (prevPage && prevPage.data.noticeList) {
  471. const item = prevPage.data.noticeList.find(item => item.id == id);
  472. if (item) {
  473. const formatTime = (timeString) => {
  474. if (!timeString) return ''; // 如果时间为空,返回空字符串
  475. const date = new Date(timeString);
  476. const year = date.getFullYear();
  477. const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份补零
  478. const day = String(date.getDate()).padStart(2, '0'); // 日期补零
  479. return `${year}-${month}-${day}`;
  480. };
  481. let categoryName = '投诉';
  482. const categoryItem = this.data.categoryMap.find(cat => cat.value === item.category);
  483. if (categoryItem) {
  484. categoryName = categoryItem.name;
  485. }
  486. this.setData({
  487. category: categoryName,
  488. categoryValue: item.category || '1',
  489. contact: item.feedbackperson || '',
  490. description: item.replynote || '',
  491. phone: item.contactnumber || '',
  492. imageList: item.attachments || [],
  493. replyTime: formatTime(item.replytime) || '',
  494. replyContent: item.replycontent || ''
  495. });
  496. }
  497. }
  498. },
  499. resetForm: function () {
  500. this.setData({
  501. contact: '',
  502. phone: '',
  503. description: '',
  504. imageList: [],
  505. category: '投诉',
  506. categoryValue: '1'
  507. });
  508. }
  509. });