addCommon.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <template>
  2. <view class="main">
  3. <view class="bg-white default_title">
  4. <u--form :model="inputForm" labelWidth="130px" class="u-form default_title" labelPosition="left"
  5. ref="inputForm">
  6. <u-form-item label="年度" borderBottom prop="yearNum">
  7. <u--input readonly v-model="inputForm.yearNum" placeholder="输入年度" border="none"></u--input>
  8. </u-form-item>
  9. <u-form-item label="卡号" borderBottom prop="cardNum">
  10. <u--input readonly v-model="inputForm.cardNum" placeholder="输入卡号" border="none"></u--input>
  11. </u-form-item>
  12. </u--form>
  13. </view>
  14. <view class="bg-white margin-top main_info">
  15. <u--form :model="inputForm" :rules="rules" labelWidth="130px" class="u-form default_title"
  16. labelPosition="left" ref="inputForm">
  17. <u-form-item required label="文件名称" borderBottom prop="name">
  18. <u--input v-model="inputForm.name" placeholder="请输入文件名称" border="none"></u--input>
  19. </u-form-item>
  20. <u-form-item label="拟稿人" borderBottom prop="authorPerson">
  21. <u--input readonly v-model="$auth.getUserInfo().name" border="none"></u--input>
  22. </u-form-item>
  23. <u-form-item required label="校对人" borderBottom prop="proofreader">
  24. <office-user-select v-model="inputForm.proofreader" placeholder="请选择校对人" title="校对人"
  25. myOffice types="3,6"></office-user-select>
  26. </u-form-item>
  27. <u-form-item label="印数" borderBottom prop="printNum">
  28. <u-number-box integer v-model="inputForm.printNum"></u-number-box>
  29. </u-form-item>
  30. <u-form-item label="是否公开" borderBottom prop="open">
  31. <u-radio-group v-model="inputForm.open">
  32. <u-radio style="margin-right: 20px;" label="是" name="1"></u-radio>
  33. <u-radio label="否" name="0"></u-radio>
  34. </u-radio-group>
  35. </u-form-item>
  36. <u-form-item label="备注" borderBottom prop="remark">
  37. <u--input v-model="inputForm.remark" placeholder="请输入备注" border="none"></u--input>
  38. </u-form-item>
  39. <u-form-item label="文件附件" prop="attachment" labelPosition="top">
  40. <lsj-upload ref="lsjUpload" childId="upload1" width="250rpx" height="200rpx" :option="option"
  41. :debug="false" :instantly="false" @uploadEnd="onuploadEnd" @change="afterRead">
  42. <view class="addfile flex">
  43. <u-icon size="20" bold name="plus"></u-icon>
  44. </view>
  45. </lsj-upload>
  46. <!-- #ifdef H5 -->
  47. <u-icon class="addfile" @click="takePhoto" size="20" bold
  48. name="camera"></u-icon>
  49. <!-- #endif -->
  50. </u-form-item>
  51. <view class="text-danger" style="font-size: 12px!important;">请确保上传图片内容清晰可见,所有涉密敏感信息不得上传</view>
  52. <view class="other_info">
  53. <view class="other_pdf">
  54. <u-cell-group>
  55. <u-cell v-for="item in fileList">
  56. <view slot="title">
  57. <u--text :lines="1" :text="item.name"></u--text>
  58. </view>
  59. <u-icon slot="value" size="28" name="trash-fill" @click="deleteFile(item)"></u-icon>
  60. </u-cell>
  61. </u-cell-group>
  62. </view>
  63. </view>
  64. <u-upload :fileList="imgList" @delete="deletePic" multiple :maxCount="imgList.length"
  65. :previewFullImage="true"></u-upload>
  66. <view class="submit_btn flex">
  67. <u-button v-if="!loading" type="primary" text="提交新增" @click="formSubmit"></u-button>
  68. <u-button v-if="loading" :loading="loading" type="primary" text="加载中"></u-button>
  69. </view>
  70. </u--form>
  71. </view>
  72. <u-toast ref="uToast"></u-toast>
  73. <u-overlay :show="loading">
  74. <view class="warp">
  75. <view class="rect"><u-button plain loading loadingText="加载中"></u-button></view>
  76. </view>
  77. </u-overlay>
  78. </view>
  79. </template>
  80. <script>
  81. import {
  82. isImageFormat
  83. } from "@/common/util.js"
  84. import BASE_URL from '@/config.js'
  85. import moment from "moment"
  86. import * as $auth from "@/common/auth.js"
  87. import userService from "@/api/sys/userService"
  88. import fileService from '@/api/file/fileService.js'
  89. import yzCirculationCardService from '@/api/commonseal/yzCirculationCardService.js'
  90. export default {
  91. mounted() {
  92. userService.list({
  93. current: 1,
  94. size: 10000
  95. }).then((res) => {
  96. this.userList = res.records
  97. }).catch((e) => {
  98. throw e
  99. })
  100. yzCirculationCardService.getCardNum().then(data => {
  101. this.inputForm.authorPerson = $auth.getUserInfo().id
  102. const currentYear = new Date().getFullYear();
  103. this.inputForm.yearNum = currentYear
  104. this.cardNum = data
  105. this.inputForm.cardNum = "[" + currentYear + "]" + data + "号"
  106. })
  107. },
  108. data() {
  109. return {
  110. imgsrc: {},
  111. loading: false,
  112. isreceive: false,
  113. iswrite: false,
  114. fileLists: [],
  115. fileList: [],
  116. files: [],
  117. imgList: [],
  118. userList: [],
  119. cardNum: "",
  120. inputForm: {
  121. id: '',
  122. name: '',
  123. authorPerson: '',
  124. proofreader: '',
  125. open: '1',
  126. printNum: '1',
  127. state: '1',
  128. cardNum: '',
  129. yearNum: '',
  130. attachment: '',
  131. remark: '',
  132. },
  133. rules: {
  134. name: [{
  135. required: true,
  136. message: '请输入文件名称',
  137. trigger: ['blur', 'change']
  138. }]
  139. },
  140. // 上传接口参数
  141. option: {
  142. url: BASE_URL + '/gwfile/upload?uploadPath=commonseal',
  143. name: 'file',
  144. header: {
  145. "token": $auth.getUserToken()
  146. },
  147. },
  148. }
  149. },
  150. methods: {
  151. showToast(params) {
  152. this.loading = false
  153. this.$refs.uToast.show({
  154. ...params,
  155. complete() {
  156. params.url && uni.redirectTo({
  157. url: params.url
  158. })
  159. }
  160. })
  161. },
  162. // 删除文件
  163. deleteFile(item) {
  164. let that = this
  165. that.fileList = that.fileList.filter(i => !(i.url == item.url))
  166. that.fileLists = that.fileLists.filter(i => !(i.url == item.url))
  167. },
  168. // 删除图片
  169. deletePic(event) {
  170. let that = this
  171. this.imgList = this.imgList.filter(item => item.name != event.file.name)
  172. that.fileLists = that.fileLists.filter(i => !(i.name == event.file.name))
  173. },
  174. // 图片压缩
  175. compressH5(urlData, targetSizeKB, initialQuality = 1.0) {
  176. const maxQuality = 1.0;
  177. const minQuality = 0.0;
  178. const tolerance = 0.01; // 根据需要调整公差
  179. if (!urlData.url) {
  180. this.$set(urlData, "url", urlData.path)
  181. }
  182. let that = this
  183. return new Promise((resolve, reject) => {
  184. let binarySearch = (min, max) => {
  185. const midQuality = (min + max) / 2;
  186. uni.getImageInfo({
  187. src: urlData.url,
  188. success(res) {
  189. const reader = new FileReader();
  190. let img = new Image()
  191. img.src = res.path
  192. img.onload = function() {
  193. const canvas = document.createElement('canvas');
  194. const ctx = canvas.getContext('2d');
  195. canvas.width = img.width;
  196. canvas.height = img.height;
  197. ctx.clearRect(0, 0, canvas.width, canvas.height);
  198. ctx.drawImage(img, 0, 0, canvas.width, canvas
  199. .height);
  200. // 使用异步的 toBlob 方法
  201. canvas.toBlob(async (blob) => {
  202. const fileSizeKB = blob.size /
  203. 1024;
  204. if (Math.abs(fileSizeKB -
  205. targetSizeKB) <
  206. tolerance || max - min <
  207. tolerance) {
  208. // 当前质量足够接近目标大小,使用当前质量解析
  209. reader.readAsDataURL(blob);
  210. reader.onload = () => {
  211. let result = that
  212. .uploadFilePromise(reader
  213. .result)
  214. setTimeout(() => {
  215. resolve(result)
  216. }, 300)
  217. }
  218. } else if (fileSizeKB >
  219. targetSizeKB) {
  220. // 如果文件大小太大,降低质量,继续二分查找
  221. binarySearch(min, midQuality);
  222. } else {
  223. // 如果文件大小太小,增加质量,继续二分查找
  224. binarySearch(midQuality, max);
  225. }
  226. }, urlData.type, midQuality);
  227. };
  228. }
  229. })
  230. }
  231. // 开始二分查找
  232. binarySearch(minQuality, maxQuality);
  233. })
  234. },
  235. // 拍照
  236. takePhoto() {
  237. let that = this
  238. // 调用uniapp的chooseImage API 选择图片
  239. uni.chooseImage({
  240. count: 1, // 默认9, 设置图片的数量
  241. sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
  242. sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
  243. success: (chooseImageRes) => {
  244. uni.showLoading({
  245. title: "上传中"
  246. })
  247. const tempFilePaths = chooseImageRes.tempFiles[0];
  248. // 成功选择图片后进行压缩处理
  249. that.compressH5(tempFilePaths, 150).then(result => {
  250. const fileName = result.split(/[/\\=]/).pop();
  251. let item1 = {
  252. name: fileName,
  253. url: BASE_URL + result
  254. }
  255. let item = {
  256. name: fileName,
  257. path: result,
  258. }
  259. that.fileLists.push(item)
  260. that.imgList.push(item1)
  261. uni.hideLoading()
  262. })
  263. },
  264. fail: (error) => {
  265. uni.hideLoading()
  266. }
  267. });
  268. },
  269. // 拍照上传
  270. async uploadFilePromise(param) {
  271. return new Promise((resolve, reject) => {
  272. let a = uni.uploadFile({
  273. url: this.BASE_URL + '/gwfile/upload?uploadPath=commonseal',
  274. filePath: param,
  275. name: 'file',
  276. header: {
  277. "token": $auth.getUserToken()
  278. },
  279. success: (res) => {
  280. setTimeout(() => {
  281. resolve(res.data)
  282. }, 300)
  283. }
  284. })
  285. });
  286. },
  287. // 新增图片
  288. afterRead(files) {
  289. uni.showLoading({
  290. title: "上传中"
  291. })
  292. let lists = [...files.values()]
  293. this.files = files
  294. for (let i = 0; i < lists.length; i++) {
  295. if (lists[i].type == 'success') continue;
  296. if (isImageFormat(lists[i].name) && lists[i].size > 200 * 1024) {
  297. this.compressAPP(lists[i], 150).then(res => {
  298. this.files.get(lists[i].name).file = res
  299. this.upload(lists[i].name)
  300. })
  301. } else {
  302. this.upload(lists[i].name)
  303. }
  304. }
  305. },
  306. // app图片压缩
  307. compressAPP(urlData, targetSizeKB, initialQuality = 1.0) {
  308. const maxQuality = 1.0;
  309. const minQuality = 0.0;
  310. const tolerance = 0.01; // 根据需要调整公差
  311. let that = this
  312. return new Promise((resolve, reject) => {
  313. let binarySearch = (min, max) => {
  314. const midQuality = (min + max) / 2;
  315. uni.getImageInfo({
  316. src: urlData.path,
  317. success: function(res) {
  318. let img = new Image()
  319. img.src = res.path
  320. img.onload = function() {
  321. const canvas = document.createElement('canvas');
  322. const ctx = canvas.getContext('2d');
  323. canvas.width = img.width;
  324. canvas.height = img.height;
  325. ctx.clearRect(0, 0, canvas.width, canvas.height);
  326. ctx.drawImage(img, 0, 0, canvas.width, canvas
  327. .height);
  328. // 使用异步的 toBlob 方法
  329. canvas.toBlob(async (blob) => {
  330. const fileSizeKB = blob.size / 1024;
  331. if (Math.abs(fileSizeKB -
  332. targetSizeKB) <
  333. tolerance || max - min <
  334. tolerance) {
  335. // 当前质量足够接近目标大小,使用当前质量解析
  336. const file = new File([blob], urlData
  337. .name, {
  338. type: blob.type,
  339. lastModified: new Date()
  340. .getTime(), // 使用当前时间作为最后修改时间
  341. });
  342. setTimeout(() => {
  343. resolve(file)
  344. }, 300)
  345. } else if (fileSizeKB >
  346. targetSizeKB) {
  347. // 如果文件大小太大,降低质量,继续二分查找
  348. binarySearch(min, midQuality);
  349. } else {
  350. // 如果文件大小太小,增加质量,继续二分查找
  351. binarySearch(midQuality, max);
  352. }
  353. }, urlData.file.type, midQuality);
  354. };
  355. },
  356. fail: function(err) {
  357. resolve(false);
  358. }
  359. });
  360. }
  361. // 开始二分查找
  362. binarySearch(minQuality, maxQuality);
  363. })
  364. },
  365. // APP手动上传
  366. upload(name) {
  367. // name=指定文件名,不指定则上传所有type等于waiting和fail的文件
  368. this.$refs['lsjUpload'].upload(name);
  369. },
  370. onuploadEnd(item) {
  371. console.log(`${item.name}已上传结束,上传状态=${item.type}`);
  372. // 更新当前窗口状态变化的文件
  373. this.files.set(item.name, item);
  374. let file = {
  375. name: item.name,
  376. path: item.responseText
  377. }
  378. this.fileLists.push(file)
  379. const fileName = item.name.split(/[/\\=]/).pop();
  380. if (isImageFormat(item.name)) {
  381. let item1 = {
  382. name: fileName,
  383. url: this.BASE_URL + item.responseText
  384. }
  385. this.imgList.push(item1)
  386. } else {
  387. let a = {
  388. name: fileName,
  389. url: this.BASE_URL + item.responseText
  390. }
  391. this.fileList.push(a)
  392. }
  393. uni.hideLoading()
  394. // 微信小程序Map对象for循环不显示,所以转成普通数组,
  395. // 如果你用不惯Map对象,也可以像这样转普通数组,组件使用Map主要是避免反复文件去重操作
  396. // #ifdef MP-WEIXIN
  397. this.wxFiles = [...this.files.values()];
  398. // #endif
  399. // 强制更新视图
  400. this.$forceUpdate();
  401. // ---可删除--演示判断是否所有文件均已上传成功
  402. let isAll = [...this.files.values()].find(item => item.type !== 'success');
  403. if (!isAll) {
  404. } else {
  405. console.log(isAll.name + '待上传');
  406. }
  407. },
  408. // 下载文件
  409. download(param) {
  410. fileService.download(param).then(data)
  411. },
  412. // 表单提交
  413. formSubmit() {
  414. this.loading = true
  415. let auditForm = Object.assign({}, this.inputForm);
  416. let files = []
  417. this.fileLists.forEach(item => {
  418. files.push(item.path)
  419. })
  420. auditForm.attachment = files.join(",");
  421. auditForm.cardNum = this.cardNum
  422. this.$refs.inputForm.validate().then(valid => {
  423. if (valid) {
  424. yzCirculationCardService.save(auditForm).then((data) => {
  425. let param = {
  426. type: 'success',
  427. message: data,
  428. iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/success.png',
  429. url: "/pages/index/index"
  430. }
  431. this.showToast(param);
  432. }).catch(() => {
  433. let param = {
  434. type: 'error',
  435. message: data,
  436. iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/error.png',
  437. }
  438. this.showToast(param);
  439. })
  440. }
  441. }).catch(e => {
  442. this.loading = false
  443. })
  444. }
  445. }
  446. }
  447. </script>
  448. <style>
  449. .warp {
  450. display: flex;
  451. align-items: center;
  452. justify-content: center;
  453. height: 100%;
  454. }
  455. .default_title {
  456. padding-top: 0px;
  457. }
  458. .main_info {
  459. margin-top: 15px;
  460. }
  461. .u-form-item__body__right__message span {
  462. font-size: 12px !important;
  463. }
  464. .submit_btn {
  465. background-color: #fefefe;
  466. margin-top: 20px;
  467. box-shadow: none;
  468. margin-bottom: 20px;
  469. }
  470. .submit_btn button {
  471. height: 40px;
  472. width: 80%;
  473. border-radius: 30px;
  474. }
  475. .addfile {
  476. width: 80px;
  477. height: 80px;
  478. background-color: #eee;
  479. justify-content: center;
  480. margin: 10px;
  481. }
  482. .takephoto {
  483. padding-left: 9%;
  484. }
  485. .other_info {
  486. width: 100%;
  487. }
  488. </style>