opSignature.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. <template>
  2. <el-dialog v-model="Visible" title="新增签章" width="1200" align-center>
  3. <template #footer>
  4. <div class="dialog-footer">
  5. <el-button @click="Visible = false">取消</el-button>
  6. <el-button
  7. v-if="activeName != null && sealFrom.createType != 2"
  8. type="primary"
  9. @click="submit()"
  10. v-loading="opSignatureLoading"
  11. >
  12. 确认{{ addStatus ? "创建" : "修改" }}
  13. </el-button>
  14. </div>
  15. </template>
  16. <el-form
  17. v-if="sealFrom.createType != 2"
  18. :model="form"
  19. ref="signatureFormRef"
  20. >
  21. <el-form-item
  22. label="印章名称:"
  23. prop="signatureName"
  24. :rules="[{ required: true, message: '请输入印章名称' }]"
  25. style="width: 400px"
  26. >
  27. <el-input v-model="form.signatureName" />
  28. </el-form-item>
  29. <el-form-item
  30. label="签章归属:"
  31. prop="signatureAttribution"
  32. :rules="[{ required: false, message: '请输入印章名称' }]"
  33. style="width: 400px"
  34. >
  35. <el-select v-model="form.signatureAttribution">
  36. <el-option
  37. v-for="item in dicts.signature_attribution"
  38. :key="item"
  39. :label="item.dictLabel"
  40. :value="item.dictValue"
  41. />
  42. </el-select>
  43. </el-form-item>
  44. <el-form-item
  45. v-if="activeName == '2' && myselfUpload == true"
  46. label="工序名称:"
  47. :rules="[{ required: true, message: '请输入' }]"
  48. style="width: 400px"
  49. >
  50. <el-input v-model="form.opName" />
  51. </el-form-item>
  52. <el-form-item
  53. v-if="activeName == '3' && myselfUpload == true"
  54. label="检验编号:"
  55. :rules="[{ required: true, message: '请输入' }]"
  56. style="width: 400px"
  57. >
  58. <el-input v-model="form.jyName" />
  59. </el-form-item>
  60. <el-form-item
  61. v-if="
  62. sealFrom.createType == 1 && activeName == '4' && myselfUpload == true
  63. "
  64. label="环绕字段:"
  65. prop="topText"
  66. :rules="[{ required: true, message: '请输入' }]"
  67. style="width: 400px"
  68. >
  69. <el-input v-model="form.topText" />
  70. </el-form-item>
  71. <el-form-item
  72. v-if="
  73. sealFrom.createType == 1 && activeName == '4' && myselfUpload == true
  74. "
  75. label="横排字段:"
  76. :rules="[{ required: false, message: '请输入' }]"
  77. style="width: 400px"
  78. >
  79. <el-input v-model="form.middleText" />
  80. </el-form-item>
  81. <el-form-item label="启用状态:">
  82. <el-switch
  83. v-model="form.signatureState"
  84. active-text="启用"
  85. active-value="1"
  86. inactive-text="禁用"
  87. inactive-value="0"
  88. style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
  89. />
  90. </el-form-item>
  91. </el-form>
  92. <div style="display: flex">
  93. <div v-if="editStatus == true" style="margin-right: 20px">
  94. <div>原签章内容</div>
  95. <img
  96. style="width: 150px; height: 150px"
  97. :src="imageUrl"
  98. v-if="oldUrl"
  99. />
  100. <span v-else>无内容(请上传后查看效果)</span>
  101. </div>
  102. <div
  103. v-if="
  104. myselfUpload != true && (addStatus ? true : editFirst ? false : true)
  105. "
  106. >
  107. <div>现签章内容</div>
  108. <img
  109. style="width: 150px; height: 150px"
  110. :src="newImageUrl"
  111. v-if="form.signatureFiles"
  112. />
  113. <span v-else>无内容(请上传后查看效果)</span>
  114. </div>
  115. </div>
  116. <!-- 签章区域 -->
  117. <div v-if="activeName != null">
  118. <el-tabs v-model="activeName" class="demo-tabs" type="card">
  119. <el-tab-pane
  120. :label="item.dictLabel"
  121. :name="item.dictValue"
  122. v-for="(item, index) in dicts.signature_type"
  123. :key="item"
  124. />
  125. </el-tabs>
  126. <el-switch
  127. v-model="myselfUpload"
  128. active-text="借用模版"
  129. inactive-text="自定义上传"
  130. @click="changeMyselfUpload"
  131. />
  132. <div v-if="myselfUpload">
  133. <!-- 人员签章 -->
  134. <div
  135. v-show="activeName == '1'"
  136. style="display: flex; flex-direction: column; align-items: center"
  137. >
  138. <div class="body">
  139. <div>
  140. <div>
  141. <div
  142. class="signature-box"
  143. :class="!startSignature ? 'signature-bg' : ''"
  144. >
  145. <VueSignaturePad
  146. ref="signaturePad"
  147. :key="startSignature"
  148. :images="[
  149. { src: 'A.png', x: 0, y: 0 },
  150. { src: 'B.png', x: 0, y: 10 },
  151. { src: 'C.png', x: 0, y: 20 },
  152. ]"
  153. :options="options"
  154. />
  155. </div>
  156. </div>
  157. <div class="operaBox">
  158. <el-button type="primary" @click="undoSignature"
  159. >撤销</el-button
  160. >
  161. <el-button @click="clearSignature">重写</el-button>
  162. <el-button type="success" @click="showSignature"
  163. >预览</el-button
  164. >
  165. </div>
  166. </div>
  167. <div>
  168. <div class="showBox">
  169. <img
  170. ref="imgRef"
  171. :src="'data:image/png;base64,' + handwritingEl"
  172. v-if="handwritingEl"
  173. />
  174. <span v-else
  175. >请先设置手写签名
  176. <br />
  177. 建议签名铺满书写框
  178. </span>
  179. </div>
  180. <div class="operaBox" v-if="handwritingEl">
  181. <el-button type="primary" @click="updownPng">下载</el-button>
  182. </div>
  183. </div>
  184. </div>
  185. </div>
  186. <!-- 工序签章 -->
  187. <div
  188. v-if="activeName == '2'"
  189. style="display: flex; flex-direction: column; align-items: center"
  190. >
  191. <div class="showGJBox">
  192. <div v-if="form.opName" ref="GJRef" class="GJBox">
  193. {{ form.opName }}
  194. </div>
  195. <span v-else>请先设置工序名称</span>
  196. </div>
  197. <div class="operaBox" v-if="form.name">
  198. <el-button type="primary" @click="updownGJPng">下载</el-button>
  199. </div>
  200. </div>
  201. <!-- 检验签章 -->
  202. <div
  203. v-if="activeName == '3'"
  204. style="display: flex; flex-direction: column; align-items: center"
  205. >
  206. <div class="showGJBox">
  207. <div v-if="form.jyName" ref="JYRef" class="JYBox">
  208. 检<span style="margin-left: 20px">{{ form.jyName }}</span>
  209. </div>
  210. <span v-else>请先设置检验编号</span>
  211. </div>
  212. <div class="operaBox" v-if="form.name">
  213. <el-button type="primary" @click="updownJYPng">下载</el-button>
  214. </div>
  215. </div>
  216. <!-- 公章签章 -->
  217. <div
  218. v-if="activeName == '4'"
  219. style="display: flex; flex-direction: column; align-items: center"
  220. >
  221. <el-radio-group
  222. v-model="sealFrom.createType"
  223. size="large"
  224. @change="createTypeChange"
  225. style="margin-bottom: 20px"
  226. >
  227. <el-radio-button :value="1">模板创建制作</el-radio-button>
  228. <el-radio-button :value="2">印模生成工具</el-radio-button>
  229. </el-radio-group>
  230. <div
  231. style="display: flex; flex-direction: column; align-items: center"
  232. >
  233. <div class="seal-make-layout">
  234. <div class="template-make" v-if="sealFrom.createType == 1">
  235. <el-button
  236. type="success"
  237. v-loading="uploading"
  238. @click="showGZSignature"
  239. >预览</el-button
  240. >
  241. </div>
  242. <div class="seal-upload" v-if="sealFrom.createType == 2">
  243. <div v-if="fileList.length == 0" class="upload-before">
  244. <el-upload
  245. v-model:file-list="fileList"
  246. :show-file-list="false"
  247. :limit="1"
  248. @change="handleChange"
  249. :auto-upload="false"
  250. >
  251. <div
  252. class="local-upload pointer"
  253. v-if="fileList.length < 1"
  254. >
  255. <div style="width: 100%">点击上传</div>
  256. </div>
  257. </el-upload>
  258. <div class="prompt">
  259. <p style="font-weight: 600">注意:</p>
  260. <p>
  261. 此模块仅作为工具实现目标效果(旋转、虚化后可导出),如需创建请在模版创建制作。
  262. </p>
  263. </div>
  264. </div>
  265. <div class="upload-img" v-else>
  266. <img
  267. :src="uploadSeal"
  268. ref="sealRef"
  269. @load="imgeLoad"
  270. style="height: 100%; display: none"
  271. />
  272. </div>
  273. <div class="SealActions" v-if="fileList.length > 0">
  274. <el-row style="margin-top: 10px">
  275. <el-col :span="4" class="center">
  276. <el-icon size="20"
  277. ><RefreshLeft @click="sealActions(-1)"
  278. /></el-icon>
  279. </el-col>
  280. <el-col :span="4" class="center">
  281. <el-icon size="20"
  282. ><RefreshRight @click="sealActions(1)"
  283. /></el-icon>
  284. </el-col>
  285. <el-col :span="4" class="center">
  286. <el-icon size="20"
  287. ><Minus @click="sealActions(-2)"
  288. /></el-icon>
  289. </el-col>
  290. <el-col :span="4" class="center">
  291. <el-icon size="20"
  292. ><Plus @click="sealActions(2)"
  293. /></el-icon>
  294. </el-col>
  295. <el-col :span="24">
  296. <el-upload
  297. v-model:file-list="fileList"
  298. :show-file-list="false"
  299. @change="handleChange2"
  300. >
  301. <el-button type="link">重新上传</el-button>
  302. </el-upload>
  303. </el-col>
  304. </el-row>
  305. <el-row style="display: flex; align-items: center">
  306. <el-col :span="8">
  307. <h4 style="line-height: 32px">旋转角度:</h4>
  308. </el-col>
  309. <el-col :span="16">
  310. <el-slider
  311. @input="sealRotateChange"
  312. v-model="sealOptions.sealRotate"
  313. :min="-180"
  314. :max="180"
  315. />
  316. </el-col>
  317. </el-row>
  318. <el-row style="display: flex; align-items: center">
  319. <el-col :span="8">
  320. <h4 style="line-height: 32px">背景透明度:</h4>
  321. </el-col>
  322. <el-col :span="16">
  323. <el-slider
  324. @input="buildCropperImage"
  325. v-model="sealOptions.sealBackground"
  326. :min="0"
  327. :max="100"
  328. />
  329. </el-col>
  330. </el-row>
  331. </div>
  332. </div>
  333. <div class="seal-preview">
  334. <div class="title">签章预览</div>
  335. <div class="seal-preview-img">
  336. <img
  337. ref="GZRef"
  338. :src="'data:image/png;base64,' + sealFrom.sealPreview"
  339. style="width: 100%; height: 100%"
  340. alt="暂未生成签章"
  341. v-if="sealFrom.sealPreview"
  342. />
  343. <span style="color: #aaa" v-else>暂未生成签章</span>
  344. </div>
  345. <div class="operaBox" v-if="sealFrom.sealPreview">
  346. <el-button type="primary" @click="updownGZPng"
  347. >下载</el-button
  348. >
  349. </div>
  350. </div>
  351. </div>
  352. </div>
  353. </div>
  354. </div>
  355. <div v-else>
  356. <!-- //自定义上传 -->
  357. <FilesUpload
  358. v-model:src="fileUrl"
  359. v-model:pdf-list="pdfUrlList"
  360. v-model:file-name-list="fileNameList"
  361. :generate-pdf="true"
  362. @finished="setFileUrl"
  363. />
  364. </div>
  365. </div>
  366. <div v-else style="color: red">功能未开放,请联系管理员!</div>
  367. </el-dialog>
  368. </template>
  369. <script setup>
  370. import { useDictionaryStore } from "@/store";
  371. import { VueSignaturePad } from "vue-signature-pad";
  372. import { useDebounceFn } from "@vueuse/core";
  373. import html2canvas from "html2canvas";
  374. import { uploadFileApi } from "@/api/file";
  375. import Cropper from "cropperjs";
  376. import "cropperjs/dist/cropper.css";
  377. import {
  378. addSignature,
  379. getGenerateSeal,
  380. getClipSeal,
  381. updateSignature,
  382. } from "@/api/signature";
  383. const { dicts } = useDictionaryStore();
  384. const Visible = ref(true);
  385. const addStatus = ref(false);
  386. const props = defineProps({
  387. editStatus: Boolean,
  388. formData: Object,
  389. rowObj: Object,
  390. });
  391. const form = ref({
  392. signatureName: "",
  393. signatureFiles: "",
  394. signatureAttribution: "",
  395. });
  396. const changeMyselfUpload = () => {
  397. form.value.signatureFiles = null;
  398. };
  399. function base64ToBlob(base64, type = "image/png") {
  400. const base64Data = base64.split(",")[1];
  401. const byteCharacters = atob(base64Data);
  402. const byteNumbers = new Array(byteCharacters.length);
  403. for (let i = 0; i < byteCharacters.length; i++) {
  404. byteNumbers[i] = byteCharacters.charCodeAt(i);
  405. }
  406. const byteArray = new Uint8Array(byteNumbers);
  407. return new Blob([byteArray], { type: type });
  408. }
  409. const opSignatureLoading = ref(false);
  410. const uploading = ref(false);
  411. const submit = async () => {
  412. opSignatureLoading.value = true;
  413. try {
  414. await signatureFormRef.value.validate(async (valid, fields) => {
  415. if (valid) {
  416. //入口逻辑判断是否为自定义上传
  417. if (myselfUpload.value == false) {
  418. //此处为自定义上传
  419. if (form.value.signatureFiles) {
  420. if (addStatus.value == true) {
  421. add(form.value.signatureFiles);
  422. } else {
  423. edit(form.value.signatureFiles);
  424. }
  425. } else {
  426. ElMessage.warning("请先上传印章!");
  427. }
  428. } else {
  429. let blob = null;
  430. //此处为借用模版创建先生成文件,进行自动进行上传操作,后在提交文件地址完成创建
  431. //根据类型选择图片数据进行处理上传
  432. switch (activeName.value) {
  433. case "1":
  434. const { isEmpty, data } = signaturePad.value.saveSignature();
  435. if (isEmpty) {
  436. message.warning("签名为空,请书写后再进行添加");
  437. return;
  438. }
  439. const resultImg = data.split(",");
  440. blob = base64ToBlob("data:image/png;base64," + resultImg[1]);
  441. break;
  442. case "2":
  443. const chartContainer1 = GJRef.value;
  444. const canvas1 = await html2canvas(chartContainer1);
  445. const img1 = canvas1.toDataURL("image/png");
  446. blob = base64ToBlob(img1);
  447. break;
  448. case "3":
  449. const chartContainer2 = JYRef.value;
  450. const canvas2 = await html2canvas(chartContainer2);
  451. const img2 = canvas2.toDataURL("image/png");
  452. blob = base64ToBlob(img2);
  453. break;
  454. case "4":
  455. const response = await getGenerateSeal({
  456. middleText: form.value.middleText,
  457. topText: form.value.topText,
  458. });
  459. blob = base64ToBlob(
  460. "data:image/png;base64," + response.data.result.entSeal
  461. );
  462. break;
  463. }
  464. const file = new File([blob], "签章文件.png", {
  465. type: "image/png",
  466. });
  467. //上传到文件服务器
  468. uploadFileApi(file, true)
  469. .then((res) => {
  470. if (addStatus.value == true) {
  471. add(res.data.fileUrl);
  472. } else {
  473. edit(res.data.fileUrl);
  474. }
  475. })
  476. .catch((err) => {
  477. ElMessage.error(err.message);
  478. });
  479. }
  480. } else {
  481. ElMessage.error("请检查表单选项");
  482. }
  483. });
  484. } catch (e) {
  485. ElMessage.error(e);
  486. } finally {
  487. opSignatureLoading.value = false;
  488. }
  489. };
  490. const add = async (url) => {
  491. form.value.signatureFiles = url;
  492. const { code } = await addSignature({
  493. ...form.value,
  494. signatureType: activeName.value,
  495. });
  496. if (code == "200") {
  497. ElMessage.success("添加成功");
  498. emit("close");
  499. }
  500. };
  501. const edit = async (url) => {
  502. form.value.signatureFiles = url;
  503. const { code } = await updateSignature({
  504. ...form.value,
  505. signatureType: activeName.value,
  506. });
  507. if (code == "200") {
  508. ElMessage.success("添加成功");
  509. emit("close");
  510. }
  511. };
  512. const update = async () => {};
  513. const oldUrl = ref("");
  514. const imageUrl = computed(() => {
  515. let url = import.meta.env.VITE_APP_UPLOAD_URL + oldUrl.value;
  516. return url;
  517. });
  518. const newImageUrl = computed(() => {
  519. let url = import.meta.env.VITE_APP_UPLOAD_URL + form.value.signatureFiles;
  520. return url;
  521. });
  522. const activeName = ref(null);
  523. const emit = defineEmits(["close"]);
  524. const editFirst = ref(true);
  525. const setActiveName = () => {
  526. if (dicts.signature_type) {
  527. activeName.value = dicts.signature_type[0].dictValue;
  528. } else {
  529. activeName.value = null;
  530. }
  531. };
  532. const myselfUploadUseHook = () => {
  533. //是否自己上传
  534. const myselfUpload = ref(false);
  535. const fileUrl = ref();
  536. const pdfUrlList = ref([]);
  537. const fileNameList = ref([]);
  538. const setFileUrl = () => {
  539. form.value.signatureFiles = fileUrl.value;
  540. if (addStatus.value == false) {
  541. editFirst.value = false;
  542. }
  543. };
  544. return { myselfUpload, fileUrl, pdfUrlList, fileNameList, setFileUrl };
  545. };
  546. const { myselfUpload, fileUrl, pdfUrlList, fileNameList, setFileUrl } =
  547. myselfUploadUseHook();
  548. const handwritingUseHook = () => {
  549. const signaturePad = ref();
  550. const signatureFormRef = ref();
  551. const startSignature = ref(false);
  552. //图片二进制编码
  553. const handwritingEl = ref(null);
  554. const imgRef = ref(null);
  555. const options = ref({
  556. penColor: "#000",
  557. dotSize: (1 + 4) / 2,
  558. minWidth: 1,
  559. maxWidth: 4,
  560. throttle: 16,
  561. minDistance: 5,
  562. backgroundColor: "rgba(0,0,0,0)",
  563. velocityFilterWeight: 0.5,
  564. onBegin: () => {
  565. startSignature.value = true;
  566. },
  567. onEnd: () => {},
  568. });
  569. function undoSignature() {
  570. signaturePad.value.undoSignature();
  571. }
  572. function clearSignature() {
  573. signaturePad.value.clearSignature();
  574. startSignature.value = false;
  575. }
  576. //下载png
  577. const updownPng = async () => {
  578. const chartContainer = imgRef.value;
  579. const canvas = await html2canvas(chartContainer);
  580. const img = canvas.toDataURL("image/png");
  581. const a = document.createElement("a");
  582. a.href = img;
  583. a.download = "手写签名.png";
  584. a.click();
  585. };
  586. //展示图片
  587. function showSignature() {
  588. //获取签名图片
  589. const { isEmpty, data } = signaturePad.value.saveSignature();
  590. if (isEmpty) {
  591. ElMessage.warning("签名为空,请书写后再进行添加");
  592. return;
  593. }
  594. const resultImg = data.split(",");
  595. //将签名图片返给main页面
  596. handwritingEl.value = resultImg[1];
  597. }
  598. return {
  599. signaturePad,
  600. signatureFormRef,
  601. startSignature,
  602. handwritingEl,
  603. imgRef,
  604. options,
  605. undoSignature,
  606. clearSignature,
  607. updownPng,
  608. showSignature,
  609. };
  610. };
  611. const {
  612. signaturePad,
  613. signatureFormRef,
  614. startSignature,
  615. handwritingEl,
  616. imgRef,
  617. options,
  618. undoSignature,
  619. clearSignature,
  620. updownPng,
  621. showSignature,
  622. } = handwritingUseHook();
  623. //工序签章
  624. const GJUseHook = () => {
  625. const GJRef = ref(null);
  626. const updownGJPng = async () => {
  627. const chartContainer = GJRef.value;
  628. const canvas = await html2canvas(chartContainer);
  629. const img = canvas.toDataURL("image/png");
  630. const a = document.createElement("a");
  631. a.href = img;
  632. a.download = "手写签名.png";
  633. a.click();
  634. };
  635. return { GJRef, updownGJPng };
  636. };
  637. const { GJRef, updownGJPng } = GJUseHook();
  638. //检验签章
  639. const JYUseHook = () => {
  640. const JYRef = ref(null);
  641. const updownJYPng = async () => {
  642. const chartContainer = JYRef.value;
  643. const canvas = await html2canvas(chartContainer);
  644. const img = canvas.toDataURL("image/png");
  645. const a = document.createElement("a");
  646. a.href = img;
  647. a.download = "手写签名.png";
  648. a.click();
  649. };
  650. return { JYRef, updownJYPng };
  651. };
  652. const { JYRef, updownJYPng } = JYUseHook();
  653. //公章签章
  654. const GZUseHook = () => {
  655. const GZRef = ref(null);
  656. const fileList = ref([]);
  657. const sealCropper = ref();
  658. const uploadSeal = ref(null);
  659. const sealRef = ref(null);
  660. const firstUpload = ref(true);
  661. const sealOptions = ref({
  662. sealRotate: 0,
  663. sealBackground: 50,
  664. imageWidth: 400,
  665. imageHeight: 400,
  666. });
  667. function getBase64(file) {
  668. return new Promise((resolve, reject) => {
  669. const reader = new FileReader();
  670. reader.readAsDataURL(file);
  671. reader.onload = () => resolve(reader.result);
  672. reader.onerror = (error) => reject(error);
  673. });
  674. }
  675. const sealFrom = ref({
  676. createType: 1,
  677. sealStyle: 1,
  678. entpName: "",
  679. middleText: "测试印章",
  680. sealPreview: false,
  681. });
  682. function createTypeChange(value) {
  683. sealFrom.value.sealPreview = false;
  684. }
  685. function handleChange(info) {
  686. getBase64(info.raw).then((res) => {
  687. uploadSeal.value = res;
  688. });
  689. }
  690. const showGZSignature = async () => {
  691. uploading.value = true;
  692. try {
  693. const response = await getGenerateSeal({
  694. middleText: form.value.middleText,
  695. topText: form.value.topText,
  696. });
  697. sealFrom.value.sealPreview = response.data.result.entSeal;
  698. } finally {
  699. uploading.value = false;
  700. }
  701. };
  702. const updownGZPng = async () => {
  703. const chartContainer = GZRef.value;
  704. const canvas = await html2canvas(chartContainer);
  705. const img = canvas.toDataURL("image/png");
  706. const a = document.createElement("a");
  707. a.href = img;
  708. a.download = "签章.png";
  709. a.click();
  710. };
  711. //重新上传印模图片
  712. async function handleChange2(info) {
  713. firstUpload.value = false;
  714. await handleChange(info);
  715. }
  716. //印章图片旋转
  717. const sealActions = useDebounceFn((type) => {
  718. switch (type) {
  719. case -1:
  720. sealCropper.value.rotate(-90);
  721. break;
  722. case 1:
  723. sealCropper.value.rotate(90);
  724. break;
  725. case -2:
  726. sealCropper.value.zoom(-0.1);
  727. break;
  728. case 2:
  729. sealCropper.value.zoom(0.1);
  730. break;
  731. }
  732. setTimeout(function () {
  733. buildCropperImage();
  734. }, 100);
  735. }, 500);
  736. function sealRotateChange(to) {
  737. sealCropper.value.rotateTo(to);
  738. setTimeout(function () {
  739. buildCropperImage();
  740. }, 100);
  741. }
  742. //将上传的图片加载到 图片处理工具中
  743. function sealPreviewCropper() {
  744. sealCropper.value = new Cropper(sealRef.value, {
  745. viewMode: 1,
  746. dragMode: "move",
  747. preview: ".before",
  748. initialAspectRatio: 1,
  749. aspectRatio: 1,
  750. background: true,
  751. autoCrop: true,
  752. autoCropArea: 0.7,
  753. zoomOnWheel: true,
  754. zoomOnTouch: true,
  755. cropBoxResizable: false,
  756. cropBoxMovable: false,
  757. wheelZoomRatio: 0.05,
  758. cropend: sealCropend,
  759. });
  760. }
  761. //图片处理完成后调用后端服务进行处理
  762. function sealCropend(end) {
  763. buildCropperImage();
  764. }
  765. //印模图片加载完成事件
  766. function imgeLoad(img) {
  767. if (!firstUpload.value) {
  768. sealCropper.value.destroy();
  769. }
  770. sealPreviewCropper();
  771. }
  772. const buildCropperImage = useDebounceFn(async () => {
  773. if (!sealCropper.value) {
  774. return;
  775. } else {
  776. }
  777. // sealCropper.value.rotateTo(sealOptions.value.sealRotate);
  778. const seal = sealCropper.value
  779. .getCroppedCanvas({
  780. width: sealOptions.value.imageWidth,
  781. height: sealOptions.value.imageHeight,
  782. imageSmoothingQuality: "high",
  783. })
  784. .toDataURL("image/jpeg");
  785. const data = {
  786. image: seal.split(",")[1],
  787. colorRange: sealOptions.value.sealBackground + 120,
  788. };
  789. const response = await getClipSeal(data);
  790. sealFrom.value.sealPreview = response.data.result.entSeal;
  791. });
  792. return {
  793. GZRef,
  794. fileList,
  795. sealFrom,
  796. sealOptions,
  797. sealRef,
  798. uploadSeal,
  799. handleChange,
  800. createTypeChange,
  801. updownGZPng,
  802. showGZSignature,
  803. imgeLoad,
  804. handleChange2,
  805. sealActions,
  806. sealRotateChange,
  807. buildCropperImage,
  808. };
  809. };
  810. const {
  811. GZRef,
  812. fileList,
  813. sealFrom,
  814. sealOptions,
  815. sealRef,
  816. uploadSeal,
  817. handleChange,
  818. createTypeChange,
  819. updownGZPng,
  820. showGZSignature,
  821. imgeLoad,
  822. handleChange2,
  823. sealActions,
  824. sealRotateChange,
  825. buildCropperImage,
  826. } = GZUseHook();
  827. onMounted(() => {
  828. if (props.editStatus == true) {
  829. addStatus.value = false;
  830. form.value.signatureName = props.rowObj.signatureName;
  831. form.value.signatureState = props.rowObj.signatureState;
  832. form.value.signatureType = props.rowObj.signatureType;
  833. form.value.signatureAttribution = props.rowObj.signatureAttribution;
  834. form.value.signatureFiles = props.rowObj.signatureFiles;
  835. form.value.id = props.rowObj.id;
  836. oldUrl.value = props.rowObj.signatureFiles;
  837. } else {
  838. addStatus.value = true;
  839. }
  840. setActiveName();
  841. });
  842. watch(
  843. () => Visible.value,
  844. (val) => {
  845. if (val == false) {
  846. emit("close");
  847. }
  848. }
  849. );
  850. </script>
  851. <style lang="scss" scoped>
  852. .local-upload {
  853. width: 160px;
  854. height: 160px;
  855. padding: 10px;
  856. border: 1px dashed rgba(0, 0, 0, 0.3);
  857. display: flex;
  858. align-items: center;
  859. justify-items: center;
  860. text-align: center;
  861. }
  862. .upload-img {
  863. width: 200px;
  864. height: 200px;
  865. }
  866. .upload-before {
  867. display: flex;
  868. }
  869. .upload-before .prompt {
  870. flex: 1;
  871. height: 100%;
  872. font-size: 12px;
  873. padding: 0 10px 0 20px;
  874. }
  875. .signature-box {
  876. width: 360px;
  877. height: 240px;
  878. border: 1px solid #dedede;
  879. margin: 0 auto;
  880. }
  881. .signature-bg {
  882. background: url("@/assets/images/signature-bg.png") no-repeat;
  883. background-size: 90% 90%;
  884. background-position: 50% 50%;
  885. }
  886. .operaBox {
  887. text-align: center;
  888. margin-top: 20px;
  889. }
  890. .showGJBox {
  891. border: 1px solid #dedede;
  892. width: 240px;
  893. height: 60px;
  894. margin: 0 auto;
  895. justify-content: center;
  896. align-items: center;
  897. display: flex;
  898. .GJBox {
  899. font-size: 20px;
  900. color: red;
  901. display: inline;
  902. border: 2px solid red;
  903. }
  904. .JYBox {
  905. font-size: 20px;
  906. color: red;
  907. display: inline;
  908. border: 2px solid red;
  909. }
  910. }
  911. .body {
  912. display: flex;
  913. justify-content: space-evenly;
  914. .showBox {
  915. width: 360px;
  916. height: 240px;
  917. border: 1px solid #dedede;
  918. margin: 0 auto;
  919. display: flex;
  920. justify-content: center;
  921. align-items: center;
  922. }
  923. }
  924. .seal-make-layout {
  925. display: flex;
  926. .template-make {
  927. }
  928. .seal-preview {
  929. padding-left: 40px;
  930. .title {
  931. font-size: 14px;
  932. font-weight: 600;
  933. height: 30px;
  934. }
  935. }
  936. .seal-upload {
  937. padding: 20px 0;
  938. width: 442px;
  939. }
  940. .seal-preview-img {
  941. width: 200px;
  942. height: 200px;
  943. padding: 10px;
  944. border: 1px solid #ededed;
  945. }
  946. }
  947. </style>