index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. <template>
  2. <view class="container">
  3. <view class="example">
  4. <!-- 基础表单校验 -->
  5. <uni-forms ref="valiForm" label-align="right" :rules="rules" :modelValue="valiFormData">
  6. <uni-forms-item label="单号" :required="true" name="orderNum">
  7. <uni-easyinput v-model="valiFormData.orderNum" placeholder="请输入单号" suffixIcon="scan" :focus="focusType" @iconClick="scan" />
  8. </uni-forms-item>
  9. <uni-forms-item label="重量" required name="weight">
  10. <uni-easyinput
  11. v-model="valiFormData.weight"
  12. placeholder="请输入重量"
  13. oninput="value=value.replace(/[^\d.]/g,'').replace(/^\./g, '').replace(/\.{2,}/g, '').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').match(/^\d*(\.?\d{0,2})/g)[0] || null"
  14. >
  15. <template #right>
  16. <view class="weight-right">KG</view>
  17. </template>
  18. </uni-easyinput>
  19. </uni-forms-item>
  20. <uni-forms-item label="图片" name="images">
  21. <view class="upload-container">
  22. <view class="preview">
  23. <view v-for="(img, index) in images" :key="index" class="image-container">
  24. <image :src="img.path" class="preview-image" mode="aspectFill" @click="openPreview(img.path)" />
  25. <progress :percent="img.progress || 0" :activeColor="Number(img.progress || 0) === 100 ? '#00ff00' : '#10AEFF'" stroke-width="3" />
  26. <text v-if="img.status === 'success'">1</text>
  27. <uni-icons class="delete-icon" @click="deleteImage(index)" type="clear" size="20" color="red" />
  28. </view>
  29. <view v-if="images.length < maxImages" class="choose-image-container" @click="chooseImage">
  30. <uni-icons type="plusempty" size="40" color="#ccc" />
  31. </view>
  32. </view>
  33. <text style="padding-top: 8px">最多选择6张图片</text>
  34. <!-- 放大预览 -->
  35. <view v-if="previewImage" class="preview-modal" @click="closePreview">
  36. <image :src="previewImage" class="preview-large" mode="aspectFit" @click.stop="closePreview" />
  37. </view>
  38. </view>
  39. </uni-forms-item>
  40. </uni-forms>
  41. <view class="button-group">
  42. <button type="info" @click="reset">重置</button>
  43. <button type="primary" @click="onsubmit" :loading="loading">
  44. <uni-icons v-if="!loading" type="checkmarkempty" size="18" color="white"></uni-icons>
  45. 提交
  46. </button>
  47. </view>
  48. </view>
  49. <view class="history">
  50. <view class="item" v-for="(item, i) in historyList.slice(0, 5)" :key="i">
  51. <text class="code" :style="{ color: item.status ? 'green' : '#666' }">
  52. {{ item.orderNum }}
  53. <text v-if="item.batch_text">批次号: {{ item.batch_text }}</text>
  54. <text v-if="item.space">仓位编码: {{ item.space }}</text>
  55. {{ item.type }}
  56. </text>
  57. <uni-icons v-if="item.status" type="checkmarkempty" class="status" size="16" color="green"></uni-icons>
  58. <text class="status fail" v-else>F</text>
  59. <text style="margin-left: 10rpx; font-weight: 300">
  60. {{ '\r\n' + item.createTime }}
  61. </text>
  62. </view>
  63. </view>
  64. <uni-popup ref="message" type="message">
  65. <uni-popup-message :type="messageType" :message="messageText" :duration="2000"></uni-popup-message>
  66. </uni-popup>
  67. <uni-popup ref="alertDialog" type="dialog">
  68. <uni-popup-dialog
  69. type="info"
  70. cancelText="否"
  71. confirmText="是"
  72. title="提示"
  73. content="是否按默认申报(默认申报为:衣服,10件,单价1)"
  74. @confirm="dialogConfirm"
  75. @close="dialogClose"
  76. ></uni-popup-dialog>
  77. </uni-popup>
  78. <uni-popup ref="printerDialog" type="dialog" :is-mask-click="false">
  79. <view style="width: 90%; margin: 0 auto; min-height: 250px; background-color: #fff; border-radius: 5px">
  80. <view class="" style="font-size: 20px; border-bottom: 1px solid #e1e1e1; padding: 15px 10px">
  81. <view>
  82. <view style="margin-bottom: 20px">
  83. <text>打印尾程面单</text>
  84. </view>
  85. <view>
  86. <view v-if="printerList">
  87. <view>
  88. <uni-data-checkbox
  89. multiple
  90. v-model="sendToPeinter"
  91. :localdata="[
  92. {
  93. text: '发送到标签打印机打印',
  94. value: 1
  95. }
  96. ]"
  97. @change="sendToPeinterFun"
  98. >
  99. >
  100. </uni-data-checkbox>
  101. <uni-data-checkbox :disabled="sendToPeinter.length === 0" v-model="selectPrinter" :localdata="printers"></uni-data-checkbox>
  102. </view>
  103. </view>
  104. </view>
  105. </view>
  106. <view style="text-align: center; position: absolute; bottom: 10px; width: 90%; display: flex; margin: 0 auto; left: 0; right: 0">
  107. <button @click="close" style="width: 35%">关闭</button>
  108. <button
  109. @click="printConfirm"
  110. :hover-stay-time="500"
  111. :loading="subLoading"
  112. :disabled="sendToPeinter.length === 0 || subLoading"
  113. class="my-bt-bg"
  114. style="width: 35%"
  115. >
  116. 打印
  117. </button>
  118. </view>
  119. </view>
  120. </view>
  121. </uni-popup>
  122. </view>
  123. </template>
  124. <script setup lang="ts">
  125. import dayjs from 'dayjs';
  126. import permision from '@/common/permission.js';
  127. import { ref, reactive, nextTick, computed } from 'vue';
  128. import { onShow, onHide, onNavigationBarButtonTap } from '@dcloudio/uni-app';
  129. import { getAliyunOssSignatureUrl, weighScanURL, checkWaybillGoodsURL, printWaybillLabelURL, getWarehouseSpaceURL, getBindParamsURL, getPrinterListURL } from '@/utils/api.js';
  130. import { uuid } from '@/utils/random.ts';
  131. const alertDialog = ref();
  132. const message = ref();
  133. const valiForm = ref();
  134. const printerDialog = ref();
  135. const token = ref(null);
  136. const user = ref(null);
  137. const loading = ref(false);
  138. const images = ref([]);
  139. const messageType = ref('');
  140. const messageText = ref('');
  141. const batchOptions = ref([] as any);
  142. const spaces = ref([] as any);
  143. const previewImage = ref(null);
  144. const maxImages = ref(6); // 最大上传图片数量
  145. const printerList = ref({} as any);
  146. const historyList = ref([] as any);
  147. const printers = ref([] as any);
  148. const sendToPeinter = ref([1]);
  149. const selectPrinter = ref(0);
  150. const subLoading = ref(false);
  151. const focusType = ref(true);
  152. const result = ref();
  153. // 校验表单数据
  154. const valiFormData = reactive({
  155. images: [],
  156. orderNum: '',
  157. batch_number: '',
  158. space_code: '',
  159. weight: '',
  160. typing: true,
  161. });
  162. const rules = computed(() => {
  163. return {
  164. orderNum: {
  165. rules: [
  166. {
  167. required: true,
  168. errorMessage: '单号不能为空'
  169. }
  170. ]
  171. },
  172. weight: {
  173. rules: [
  174. {
  175. required: true,
  176. errorMessage: '重量不能为空'
  177. },
  178. {
  179. format: 'number',
  180. errorMessage: '重量只能输入数字'
  181. }
  182. ]
  183. }
  184. };
  185. });
  186. const spaceRes = computed(() => {
  187. try {
  188. const res = spaces.value.find((item: any) => item.value === valiFormData.space_code) as any;
  189. // console.log(res);
  190. return res?.text;
  191. } catch (e) {
  192. console.log(e);
  193. return '';
  194. }
  195. });
  196. onShow(() => {
  197. focusType.value = true;
  198. loading.value = false;
  199. token.value = uni.getStorageSync('token');
  200. user.value = uni.getStorageSync('user');
  201. getWarehouseSpace();
  202. getBindParams();
  203. getPrinterList();
  204. });
  205. onHide(() => {
  206. focusType.value = false;
  207. loading.value = false;
  208. });
  209. onNavigationBarButtonTap((event: any) => {
  210. if (event.index === 0) {
  211. uni.navigateTo({
  212. url: '/pages/weigh/instockLog'
  213. });
  214. }
  215. });
  216. const getWarehouseSpace = () => {
  217. uni.request({
  218. url: getWarehouseSpaceURL,
  219. method: 'POST',
  220. header: {
  221. batoken: token.value
  222. },
  223. data: {
  224. code: ''
  225. },
  226. success: (res: any) => {
  227. if (res.data.code === 1) {
  228. spaces.value = res.data.data.spaces.map((item) => {
  229. return {
  230. text: item.name,
  231. value: item.code
  232. };
  233. });
  234. }
  235. }
  236. });
  237. };
  238. const getBindParams = () => {
  239. uni.request({
  240. url: getBindParamsURL,
  241. method: 'GET',
  242. header: {
  243. batoken: token.value
  244. },
  245. success: (res: any) => {
  246. if (res.data.code === 1) {
  247. batchOptions.value = res.data.data.batch_number.map((item) => {
  248. return {
  249. text: item.name,
  250. value: item.id
  251. };
  252. });
  253. }
  254. },
  255. fail(e) {
  256. console.log('fail--', e);
  257. }
  258. });
  259. };
  260. const getPrinterList = () => {
  261. uni.request({
  262. url: getPrinterListURL,
  263. method: 'GET',
  264. header: {
  265. batoken: token.value
  266. },
  267. success: (res: any) => {
  268. if (res.data.code === 1) {
  269. printerList.value = res.data.data.printers;
  270. printers.value = Object.values(printerList.value).map((item: any) => {
  271. return {
  272. text: item.name,
  273. value: item.value
  274. };
  275. });
  276. }
  277. },
  278. fail(e) {
  279. console.log('fail--', e);
  280. }
  281. });
  282. };
  283. const reset = () => {
  284. loading.value = false;
  285. focusType.value = false;
  286. images.value = [];
  287. valiFormData.orderNum = '';
  288. valiFormData.batch_number = '';
  289. valiFormData.weight = '';
  290. valiFormData.typing = true;
  291. nextTick(() => {
  292. focusType.value = true;
  293. });
  294. };
  295. const scan = async () => {
  296. // #ifdef APP-PLUS
  297. let status = await checkPermission();
  298. if (status !== 1) {
  299. return;
  300. }
  301. // #endif
  302. uni.scanCode({
  303. success: (res: any) => {
  304. result.value = res.result;
  305. valiFormData.orderNum = res.result;
  306. // this.onsubmit();
  307. },
  308. fail: (err) => {
  309. // 需要注意的是小程序扫码不需要申请相机权限
  310. }
  311. });
  312. };
  313. // #ifdef APP-PLUS
  314. const checkPermission = async () => {
  315. let status = permision.isIOS ? await permision.requestIOS('camera') : await permision.requestAndroid('android.permission.CAMERA');
  316. if (status === null || status === 1) {
  317. status = 1;
  318. } else {
  319. uni.showModal({
  320. content: 'Camera permission required',
  321. confirmText: 'Setting',
  322. success: function (res) {
  323. if (res.confirm) {
  324. permision.gotoAppSetting();
  325. }
  326. }
  327. });
  328. }
  329. return status;
  330. };
  331. // #endif
  332. let st;
  333. const warehouseScan = async () => {
  334. st && clearTimeout(st);
  335. await checkWaybillGoods();
  336. };
  337. const checkWaybillGoods = async () => {
  338. loading.value = true;
  339. uni.request({
  340. url: checkWaybillGoodsURL,
  341. method: 'POST',
  342. header: {
  343. batoken: token.value
  344. },
  345. data: {
  346. order_no: valiFormData.orderNum
  347. },
  348. success: (res: any) => {
  349. if (res.data.code === 1 && res.data.data.is_goods_empty) {
  350. alertDialog.value.open();
  351. } else {
  352. weighScan(0);
  353. }
  354. }
  355. });
  356. };
  357. const weighScan = (is_save_goods: number) => {
  358. const images = getImages();
  359. console.log('images--', images);
  360. let allImgVerify = true;
  361. for (var i = 0; i < images.length; i++) {
  362. if (!images[i].savePath) {
  363. allImgVerify = false;
  364. }
  365. }
  366. if (!allImgVerify) {
  367. messageType.value = 'error';
  368. messageText.value = '图片还没上传完毕,请稍后...';
  369. message.value.open();
  370. loading.value = false;
  371. return;
  372. }
  373. uni.request({
  374. url: weighScanURL,
  375. method: 'POST',
  376. header: {
  377. batoken: token.value
  378. },
  379. data: {
  380. order_no: valiFormData.orderNum,
  381. weight: valiFormData.weight,
  382. is_save_goods: is_save_goods,
  383. images
  384. },
  385. success: (res: any) => {
  386. loading.value = false;
  387. if (res.data.code == 1) {
  388. messageType.value = 'success';
  389. messageText.value = res.data.msg;
  390. message.value.open();
  391. console.log('res.data.data---', res.data.data);
  392. const historyItem = {
  393. orderNum: valiFormData.orderNum,
  394. createTime: new Date(),
  395. space: spaceRes.value,
  396. type: '称重',
  397. status: true
  398. };
  399. historyList.value.unshift(historyItem);
  400. uni.setStorageSync('weighHistory', historyList.value);
  401. getHistory();
  402. if (res.data.data.express_label) {
  403. console.log('有打印面单');
  404. // selectPrinter.value = sendToPeinter.value.length > 0 ? printers.value[0].value : 0;
  405. // printerDialog.value.open();
  406. } else {
  407. st = setTimeout(() => {
  408. reset();
  409. st && clearTimeout(st);
  410. }, 1000);
  411. }
  412. } else {
  413. messageType.value = 'error';
  414. messageText.value = res.data.msg;
  415. message.value.open();
  416. const historyItem = {
  417. orderNum: valiFormData.orderNum,
  418. createTime: new Date(),
  419. space: spaceRes.value,
  420. type: '称重',
  421. status: false
  422. };
  423. historyList.value.unshift(historyItem);
  424. uni.setStorageSync('weighHistory', historyList.value);
  425. getHistory();
  426. st = setTimeout(() => {
  427. reset();
  428. st && clearTimeout(st);
  429. }, 1000);
  430. }
  431. }
  432. });
  433. };
  434. const close = () => {
  435. printerDialog.value.close();
  436. st = setTimeout(() => {
  437. reset();
  438. st && clearTimeout(st);
  439. }, 700);
  440. };
  441. const dialogConfirm = () => {
  442. weighScan(1);
  443. };
  444. const dialogClose = () => {
  445. weighScan(0);
  446. };
  447. const sendToPeinterFun = (res: any) => {
  448. if (!res.detail.value.length > 0) {
  449. selectPrinter.value = 0;
  450. } else {
  451. selectPrinter.value = printers.value[0].value;
  452. }
  453. };
  454. const printConfirm = () => {
  455. subLoading.value = true;
  456. uni.request({
  457. url: printWaybillLabelURL,
  458. method: 'POST',
  459. header: {
  460. batoken: token.value
  461. },
  462. data: {
  463. order_no: valiFormData.orderNum,
  464. printer_code: selectPrinter
  465. },
  466. success: (res) => {
  467. close();
  468. subLoading.value = false;
  469. console.log('打印成功', res);
  470. messageType.value = 'success';
  471. messageText.value = '打印成功';
  472. message.value.open();
  473. }
  474. });
  475. };
  476. const onsubmit = () => {
  477. valiForm.value
  478. .validate()
  479. .then((res) => {
  480. warehouseScan();
  481. })
  482. .catch((err) => {
  483. console.log('err', err);
  484. });
  485. };
  486. const getHistory = () => {
  487. historyList.value = uni.getStorageSync('weighHistory');
  488. };
  489. const getImages = () => {
  490. const res = images.value.map((item: any) => {
  491. return {
  492. name: item.name,
  493. savePath: item.serverUrl,
  494. fileSize: item.size,
  495. mimeType: item.type
  496. };
  497. });
  498. console.log('res22 ', res);
  499. return res;
  500. };
  501. // 获取阿里云oss签名
  502. const getAliyunOssSignature = (rawFiles) => {
  503. uni.request({
  504. url: getAliyunOssSignatureUrl,
  505. method: 'GET',
  506. header: {
  507. batoken: token.value
  508. },
  509. success: ({ data }: any) => {
  510. const signature = data.data.signature;
  511. const uploadPromises = rawFiles.map((image) => {
  512. return upLoadFile(signature, image);
  513. });
  514. Promise.all(uploadPromises)
  515. .then((results) => {
  516. console.log('所有图片加载成功:', results);
  517. uni.showToast({
  518. title: '加载成功',
  519. icon: 'success'
  520. });
  521. })
  522. .catch((error) => {
  523. console.error('加载失败:', error);
  524. uni.showToast({
  525. title: '加载失败',
  526. icon: 'none'
  527. });
  528. });
  529. },
  530. fail: (err) => {
  531. console.log(err);
  532. }
  533. });
  534. };
  535. const upLoadFile = (signature, image) => {
  536. const fileData = {
  537. policy: signature.policy,
  538. signature: signature.signature,
  539. ossaccessKeyId: signature.ossAccessKeyId,
  540. key: signature.dir + dayjs().format('YYYYMMDD') + '/' + uuid() + '_' + image.name,
  541. dir: signature.dir,
  542. host: signature.host,
  543. file: image.file
  544. };
  545. return new Promise((resolve, reject) => {
  546. let name = image.name;
  547. // #ifdef APP-PLUS
  548. name = 'file';
  549. // #endif
  550. const uploadTask = uni.uploadFile({
  551. url: signature.host, // 你的上传接口地址
  552. filePath: image.path,
  553. name: name, // 这里根据后端需要的字段来定义
  554. formData: fileData,
  555. success: (uploadFileRes) => {
  556. if (uploadFileRes.statusCode === 204 || uploadFileRes.statusCode === 200) {
  557. image!.serverUrl = fileData.key;
  558. resolve(uploadFileRes);
  559. } else {
  560. reject(uploadFileRes);
  561. }
  562. },
  563. fail: (error) => {
  564. console.log('error++', error);
  565. reject(error);
  566. },
  567. // 更新上传进度
  568. complete: () => {
  569. console.log('complete---');
  570. image.progress = 100;
  571. }
  572. });
  573. // 可选:监听上传进度变化
  574. uploadTask.onProgressUpdate((progressEvent) => {
  575. image.progress = progressEvent.progress; // 更新进度
  576. });
  577. });
  578. };
  579. const chooseImage = () => {
  580. uni.chooseImage({
  581. count: maxImages.value - images.value.length,
  582. success: (res) => {
  583. images.value = images.value.concat(
  584. res.tempFiles.map((item) => {
  585. const res = {
  586. size: item.size,
  587. path: item.path,
  588. name: item.name,
  589. type: item.type,
  590. progress: 0,
  591. file: item
  592. };
  593. // #ifdef APP-PLUS
  594. //文件名操作
  595. //获取文件后缀
  596. const suffix = item.path.substring(item.path.lastIndexOf('.') + 1);
  597. //获取文件名
  598. const fileName = item.path.substring(item.path.lastIndexOf('/') + 1);
  599. res.name = fileName;
  600. res.type = suffix;
  601. // #endif
  602. return res;
  603. })
  604. );
  605. const paddingImages = images.value.filter((image) => image.progress === 0);
  606. nextTick(() => {
  607. getAliyunOssSignature(paddingImages);
  608. });
  609. },
  610. fail: (err) => {
  611. console.error(err);
  612. }
  613. });
  614. };
  615. const deleteImage = (index) => {
  616. images.value.splice(index, 1);
  617. };
  618. const openPreview = (image) => {
  619. previewImage.value = image; // 设置放大预览的图片
  620. };
  621. const closePreview = () => {
  622. previewImage.value = null; // 关闭预览
  623. };
  624. </script>
  625. <style lang="scss">
  626. .example {
  627. padding: 15px;
  628. background-color: #fff;
  629. }
  630. .checkbox-cum {
  631. margin-bottom: 20rpx;
  632. font-size: 14rpx;
  633. }
  634. .sub-choice {
  635. margin-bottom: 20rpx;
  636. margin-left: 20rpx;
  637. font-size: 14rpx;
  638. }
  639. .button-group {
  640. margin-top: 15px;
  641. display: flex;
  642. flex-direction: row;
  643. justify-content: space-around;
  644. button {
  645. display: flex;
  646. align-items: center;
  647. justify-content: center;
  648. height: 35px;
  649. width: 50%;
  650. margin-left: 10px;
  651. font-size: 16rpx;
  652. }
  653. .uni-icons {
  654. margin-right: 10px;
  655. }
  656. }
  657. .weight-right {
  658. padding-right: 10rpx;
  659. font-size: 14rpx;
  660. }
  661. .history {
  662. display: flex;
  663. width: 100%;
  664. flex-direction: column;
  665. justify-items: start;
  666. .title {
  667. padding: 20rpx;
  668. font-size: 24rpx;
  669. font-weight: 600;
  670. }
  671. .code {
  672. font-weight: 600;
  673. }
  674. .item {
  675. padding: 20rpx;
  676. font-size: 20rpx;
  677. color: #666;
  678. .status {
  679. padding-left: 20rpx;
  680. }
  681. .fail {
  682. font-weight: 600;
  683. color: #f00;
  684. }
  685. }
  686. }
  687. </style>
  688. <style scoped>
  689. .upload-container {
  690. padding: 16px;
  691. }
  692. .preview {
  693. display: flex;
  694. flex-wrap: wrap;
  695. }
  696. .image-container {
  697. position: relative;
  698. margin-right: 10px;
  699. margin-bottom: 10px;
  700. }
  701. .preview-image {
  702. width: 130rpx;
  703. height: 130rpx;
  704. }
  705. .delete-icon {
  706. position: absolute;
  707. top: 0;
  708. right: 0;
  709. cursor: pointer;
  710. }
  711. .choose-image-container {
  712. width: 130rpx;
  713. /* 与图片大小一致 */
  714. height: 130rpx;
  715. /* 与图片大小一致 */
  716. display: flex;
  717. align-items: center;
  718. justify-content: center;
  719. background-color: #fff;
  720. /* 背景颜色为白色 */
  721. border: 1px dashed #cccccc;
  722. /* 虚线边框 */
  723. border-radius: 5px;
  724. /* 圆角 */
  725. cursor: pointer;
  726. }
  727. .preview-modal {
  728. position: fixed;
  729. top: 0;
  730. left: 0;
  731. right: 0;
  732. bottom: 0;
  733. background-color: rgba(0, 0, 0, 0.8);
  734. /* 半透明背景 */
  735. display: flex;
  736. align-items: center;
  737. justify-content: center;
  738. z-index: 999;
  739. }
  740. .preview-large {
  741. max-width: 90%;
  742. /* 最大宽度 */
  743. max-height: 90%;
  744. /* 最大高度 */
  745. }
  746. .progress-bar {
  747. position: absolute;
  748. bottom: 0;
  749. left: 0;
  750. width: 100%;
  751. }
  752. </style>