practiceRecord.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. <template>
  2. <!-- 页面主体内容 -->
  3. <view class="common_page adffc" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
  4. <cus-header title="申领社会实践记录" bgColor="#FFFFFF"></cus-header>
  5. <!-- 证书部分 -->
  6. <view class="prove adffcac">
  7. <image class="prove-logo" mode="widthFix" :src="certificateData.logoUrl"></image>
  8. <image class="prove-title" mode="widthFix" :src="certificateData.titleUrl"></image>
  9. <image class="prove-line" mode="widthFix" :src="certificateData.lineUrl"></image>
  10. <view class="prove-no">证书编号:{{ certificateData.certificateNumber }}</view>
  11. <view class="prove-info">
  12. <view class="prove-info-pre adf">
  13. <view class="prove-info-pre-left">
  14. <view class="prove-info-pre-left-text">义工服务姓名:</view>
  15. <view class="prove-info-pre-left-tip">Volunteer service Name</view>
  16. </view>
  17. <view class="prove-info-pre-right">{{ certificateData.memberName }}</view>
  18. </view>
  19. <view class="prove-info-pre adf">
  20. <view class="prove-info-pre-left">
  21. <view class="prove-info-pre-left-text space">所属学校:</view>
  22. <view class="prove-info-pre-left-tip">Affiliated school</view>
  23. </view>
  24. <view class="prove-info-pre-right">{{ certificateData.currentSchool }}</view>
  25. </view>
  26. <view class="prove-info-pre adf">
  27. <view class="prove-info-pre-left">
  28. <view class="prove-info-pre-left-text space">证件类型:</view>
  29. <view class="prove-info-pre-left-tip">Type of ID</view>
  30. </view>
  31. <view class="prove-info-pre-right">{{ certificateData.idType }}</view>
  32. </view>
  33. <view class="prove-info-pre adf">
  34. <view class="prove-info-pre-left">
  35. <view class="prove-info-pre-left-text space">证件号码:</view>
  36. <view class="prove-info-pre-left-tip">IdCard No</view>
  37. </view>
  38. <view class="prove-info-pre-right">{{ certificateData.idCard }}</view>
  39. </view>
  40. <view class="prove-info-pre adf">
  41. <view class="prove-info-pre-left">
  42. <view class="prove-info-pre-left-text">义工服务时长:</view>
  43. <view class="prove-info-pre-left-tip">Volunteer service Time</view>
  44. </view>
  45. <view class="prove-info-pre-right">{{ certificateData.volunteerHours }}小时</view>
  46. </view>
  47. </view>
  48. <view class="prove-memo">
  49. 您累计参与了 <span>{{ certificateData.welfareCount }}</span> 场活动,捐赠了 <span>{{ certificateData.loveValue }}</span> 爱心值。向您践行志愿精神,为社会进步奉献力量致以最崇高的敬意。
  50. </view>
  51. <view class="prove-memo" style="margin-top: 30rpx;">特发此证!</view>
  52. <view class="prove-bottom">
  53. <view class="prove-bottom-pre">证明单位:{{ certificateData.issuer }}</view>
  54. <view class="prove-bottom-pre">发证日期:{{ certificateData.createDate }}</view>
  55. </view>
  56. <image class="prove-seal" mode="widthFix" :src="certificateData.sealUrl"></image>
  57. </view>
  58. <!-- 列表部分 -->
  59. <view class="list">
  60. <view class="list-box" v-for="(item,index) in activityList" :key="index">
  61. <view class="title">{{ item.activityName||'' }}</view>
  62. <view class="content adf">
  63. <view class="right">
  64. <view class="right-pre adf">
  65. <view class="tip">活动时间:</view>
  66. <view class="text">{{ item.activityStartTime||'' }}</view>
  67. </view>
  68. <view class="right-pre adf">
  69. <template v-if="item.activityLimit==2">
  70. <view class="tip">专享券贡献:</view>
  71. <view class="text">{{ item.valueLimit||0 }}张</view>
  72. </template>
  73. <template v-else>
  74. <view class="tip">爱心值贡献:</view>
  75. <view class="text">{{ item.valueLimit||0 }}</view>
  76. </template>
  77. </view>
  78. <view class="right-pre adf">
  79. <view class="tip">义工时长:</view>
  80. <view class="text">{{ item.serviceHours||0 }}小时</view>
  81. </view>
  82. <view class="right-pre adf">
  83. <view class="tip">公益合作:</view>
  84. <view class="text">{{ item.channelName||'' }}</view>
  85. </view>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. <!-- 下载按钮 -->
  91. <view class="btn" @click="handleDownload">
  92. {{ isLoading ? '生成中...' : '下载' }}
  93. </view>
  94. </view>
  95. <canvas canvas-id="pdf-canvas" :style="'width:' + canvasWidth + 'px; height:' + canvasHeight + 'px; position: fixed; left: -9999px; top: -9999px;'"></canvas>
  96. </template>
  97. <script setup>
  98. import CusHeader from '@/components/CusHeader/index.vue'
  99. import { onLoad } from '@dcloudio/uni-app'
  100. import { ref, computed, getCurrentInstance } from 'vue'
  101. const { proxy } = getCurrentInstance()
  102. // 页面数据,实际项目中这些数据应该是动态获取的
  103. const certificateData = ref({
  104. logoUrl: 'https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/965dc74b-fc45-4409-aa09-56877e75d2bc.png',
  105. titleUrl: 'https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/7c341572-58b7-4afb-801f-93bf9890fa76.png',
  106. lineUrl: 'https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/c21e680c-e83a-4f54-948c-19c49141ae99.png',
  107. backgroundUrl: 'https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/57b2f49c-beac-49e7-8840-777ec860ae59.png',
  108. sealUrl: 'https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/8ad6323d-f6a0-4057-8063-7eec97ec93f4.png',
  109. certificateNumber: '',
  110. memberName: '',
  111. currentSchool: '',
  112. idType: '居民身份证',
  113. idCard: '',
  114. volunteerHours: 0,
  115. welfareCount: 0,
  116. loveValue: 0,
  117. issuer: '善行少年服务基金会',
  118. createDate: ''
  119. });
  120. const activityList = ref([]);
  121. const isLoading = ref(false);
  122. const canvasWidth = ref(0);
  123. const canvasHeight = ref(0);
  124. // rpx转px的工具函数
  125. const rpxToPx = (rpx) => {
  126. const screenWidth = uni.getSystemInfoSync().windowWidth;
  127. return (screenWidth / 750) * rpx;
  128. };
  129. // 文本换行绘制函数
  130. const drawWrappedText = (ctx, text, x, y, maxWidth, lineHeight) => {
  131. const words = text.split('');
  132. let line = '';
  133. let currentY = y;
  134. for (let n = 0; n < words.length; n++) {
  135. const testLine = line + words[n];
  136. const metrics = ctx.measureText(testLine);
  137. const testWidth = metrics.width;
  138. if (testWidth > maxWidth && n > 0) {
  139. ctx.fillText(line, x, currentY);
  140. line = words[n];
  141. currentY += lineHeight;
  142. } else {
  143. line = testLine;
  144. }
  145. }
  146. ctx.fillText(line, x, currentY);
  147. return currentY - y + lineHeight; // 返回这段文本所占的总高度
  148. };
  149. // 获取网络图片信息,返回一个Promise
  150. const getImageInfo = (url) => {
  151. return new Promise((resolve, reject) => {
  152. uni.getImageInfo({
  153. src: url,
  154. success: (res) => resolve(res),
  155. fail: (err) => reject(err),
  156. });
  157. });
  158. };
  159. // 主下载处理函数
  160. // 主下载处理函数
  161. const handleDownload = async () => {
  162. if (isLoading.value) return;
  163. isLoading.value = true;
  164. uni.showLoading({ title: '正在生成图片...', mask: true });
  165. try {
  166. // 1. 动态计算Canvas尺寸
  167. const proveHeight = rpxToPx(945);
  168. const pagePaddingTop = rpxToPx(20); // prove-margin-top
  169. const pagePaddingBottom = rpxToPx(184); // 页面底部到按钮的距离
  170. // 估算一个列表项的高度: 上下padding(36*2) + 标题(32) + 标题margin(30) + 4行内容(4*50) + 列表项间距(20)
  171. // 这里的 4*50 是一个估算值,基于你的CSS
  172. const singleListHeight = rpxToPx(36 * 2 + 32 + 30 + (20 + 24) * 4 + 20); // 估算每行高度(margin-top + font-size)
  173. canvasWidth.value = uni.getSystemInfoSync().windowWidth;
  174. // 总高度 = 证书高度 + 列表总高度 + 页面顶部边距 + 底部空白区域
  175. canvasHeight.value = proveHeight + (singleListHeight * activityList.value.length) + pagePaddingTop + pagePaddingBottom;
  176. // 2. 预加载所有图片
  177. const imagesToLoad = [
  178. certificateData.value.backgroundUrl,
  179. certificateData.value.logoUrl,
  180. certificateData.value.titleUrl,
  181. certificateData.value.lineUrl,
  182. certificateData.value.sealUrl
  183. ];
  184. const imageInfos = await Promise.all(imagesToLoad.map(url => getImageInfo(url)));
  185. const [
  186. bgImg, logoImg, titleImg, lineImg, sealImg
  187. ] = imageInfos.map(info => info.path);
  188. // 3. 开始绘制
  189. const ctx = uni.createCanvasContext('pdf-canvas', proxy); // 传入 proxy
  190. // 绘制白色背景
  191. ctx.setFillStyle('#F5F5F5');
  192. ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
  193. let currentY = rpxToPx(20); // 从顶部20rpx的margin开始
  194. // ---- 3.1 绘制证书 (这部分逻辑基本正确,无需大改) ----
  195. const proveX = 0;
  196. const proveWidth = canvasWidth.value;
  197. // 绘制证书背景
  198. ctx.drawImage(bgImg, proveX, currentY, proveWidth, proveHeight);
  199. // 绘制Logo
  200. ctx.drawImage(logoImg, rpxToPx(72), currentY + rpxToPx(58), rpxToPx(133), rpxToPx(40));
  201. // 绘制标题
  202. ctx.drawImage(titleImg, (proveWidth - rpxToPx(363)) / 2, currentY + rpxToPx(101), rpxToPx(363), rpxToPx(40));
  203. // 绘制线条
  204. ctx.drawImage(lineImg, (proveWidth - rpxToPx(400)) / 2, currentY + rpxToPx(101 + 40 + 7), rpxToPx(400), rpxToPx(5));
  205. // 绘制证书编号
  206. ctx.setFontSize(rpxToPx(20));
  207. ctx.setFillStyle('#9F793F');
  208. ctx.setTextAlign('center');
  209. ctx.fillText(`证书编号:${certificateData.value.certificateNumber}`, proveWidth / 2, currentY + rpxToPx(190));
  210. // 绘制个人信息
  211. ctx.setTextAlign('left');
  212. const infoStartX = (proveWidth - rpxToPx(135 + 188)) / 2;
  213. const infoStartY = currentY + rpxToPx(244);
  214. const infoItems = [
  215. { label: '义工服务姓名:', value: certificateData.value?.memberName||'', tip: 'Volunteer service Name' },
  216. { label: '所属学校:', value: certificateData.value?.currentSchool||'', tip: 'Affiliated school' },
  217. { label: '证件类型:', value: certificateData.value?.idType, tip: 'Type of ID' },
  218. { label: '证件号码:', value: certificateData.value?.idCard||'', tip: 'IdCard No' },
  219. { label: '义工服务时长:', value: `${certificateData.value?.volunteerHours||0}小时`, tip: 'Volunteer service Time' }
  220. ];
  221. infoItems.forEach((item, index) => {
  222. const itemY = infoStartY + index * rpxToPx(40);
  223. ctx.setFillStyle('#252525');
  224. ctx.setFontSize(rpxToPx(17));
  225. ctx.fillText(item.label, infoStartX, itemY);
  226. ctx.setFontSize(rpxToPx(10));
  227. ctx.fillText(item.tip, infoStartX, itemY + rpxToPx(13));
  228. const valueX = infoStartX + rpxToPx(135);
  229. ctx.setFontSize(rpxToPx(17));
  230. ctx.fillText(item.value, valueX, itemY);
  231. ctx.setStrokeStyle('#DDCEAF');
  232. ctx.setLineWidth(rpxToPx(1)); // 1px的线在高清屏下更清晰
  233. ctx.beginPath();
  234. ctx.moveTo(valueX, itemY + rpxToPx(8));
  235. ctx.lineTo(valueX + rpxToPx(188), itemY + rpxToPx(8));
  236. ctx.stroke();
  237. });
  238. // 绘制memo
  239. ctx.setFontSize(rpxToPx(20));
  240. ctx.setFillStyle('#252525');
  241. // Canvas无法识别<span>,需要分段绘制或在JS中处理颜色
  242. const memoText1 = `您累计参与了 `;
  243. const memoText2 = `${certificateData.value.welfareCount}`;
  244. const memoText3 = ` 场活动,捐赠了 `;
  245. const memoText4 = `${certificateData.value.loveValue}`;
  246. const memoText5 = ` 爱心值。向您践行志愿精神,为社会进步奉献力量致以最崇高的敬意。`;
  247. const memoY = currentY + rpxToPx(500);
  248. const memoX = rpxToPx(80);
  249. ctx.fillText(memoText1, memoX, memoY);
  250. let currentX = memoX + ctx.measureText(memoText1).width;
  251. ctx.setFillStyle('#C9A771'); // 设置高亮颜色
  252. ctx.fillText(memoText2, currentX, memoY);
  253. currentX += ctx.measureText(memoText2).width;
  254. ctx.setFillStyle('#252525'); // 恢复默认颜色
  255. ctx.fillText(memoText3, currentX, memoY);
  256. currentX += ctx.measureText(memoText3).width;
  257. ctx.setFillStyle('#C9A771'); // 设置高亮颜色
  258. ctx.fillText(memoText4, currentX, memoY);
  259. currentX += ctx.measureText(memoText4).width;
  260. ctx.setFillStyle('#252525'); // 恢复默认颜色
  261. // 后续文本太长,需要换行处理,这里简化为直接绘制,实际可能需要drawWrappedText
  262. drawWrappedText(ctx, memoText5, currentX, memoY, proveWidth - rpxToPx(160) - (currentX-memoX), rpxToPx(31));
  263. ctx.fillText('特发此证!', rpxToPx(80), currentY + rpxToPx(600));
  264. // 绘制证明单位和日期
  265. ctx.setFontSize(rpxToPx(17));
  266. ctx.fillText(`证明单位:${certificateData.value.issuer}`, rpxToPx(80), currentY + rpxToPx(700));
  267. ctx.fillText(`发证日期:${certificateData.value.createDate}`, rpxToPx(80), currentY + rpxToPx(730));
  268. // 绘制印章
  269. ctx.drawImage(sealImg, proveWidth - rpxToPx(73 + 131), currentY + proveHeight - rpxToPx(69 + 131), rpxToPx(131), rpxToPx(131));
  270. currentY += proveHeight; // 更新Y坐标到证书底部
  271. // ---- 3.2 绘制活动列表 ----
  272. for (const item of activityList.value) {
  273. currentY += rpxToPx(20); // 列表项间距
  274. const boxX = rpxToPx(24);
  275. const boxWidth = canvasWidth.value - rpxToPx(48);
  276. let itemContentHeight = 0; // 动态计算每个item的高度
  277. // 预计算高度
  278. const titleHeight = rpxToPx(32);
  279. const contentPaddingTop = rpxToPx(36);
  280. const titleMarginBottom = rpxToPx(30);
  281. const contentPaddingBottom = rpxToPx(36);
  282. const rightItemLineHeight = rpxToPx(50); // 估算行高(包含margin)
  283. itemContentHeight = contentPaddingTop + titleHeight + titleMarginBottom + (rightItemLineHeight * 4) + contentPaddingBottom;
  284. // 绘制列表项背景
  285. ctx.setFillStyle('#FFFFFF');
  286. ctx.setShadow(0, rpxToPx(5), rpxToPx(10), 'rgba(0,0,0,0.05)'); // 可选:添加阴影使其更逼真
  287. ctx.fillRect(boxX, currentY, boxWidth, itemContentHeight);
  288. ctx.setShadow(0, 0, 0, 'rgba(0,0,0,0)'); // 清除阴影
  289. const contentPaddingX = rpxToPx(20);
  290. let itemInnerY = currentY + contentPaddingTop;
  291. // 绘制标题
  292. ctx.setFontSize(rpxToPx(32));
  293. ctx.setFillStyle('#252525');
  294. ctx.font = `bold ${rpxToPx(32)}px sans-serif`;
  295. ctx.fillText(item.activityName || '', boxX + contentPaddingX, itemInnerY + rpxToPx(16)); // Y微调
  296. itemInnerY += titleHeight + titleMarginBottom;
  297. // 绘制右侧文字
  298. const textStartX = boxX + contentPaddingX;
  299. ctx.setFontSize(rpxToPx(24));
  300. ctx.font = `normal ${rpxToPx(24)}px sans-serif`;
  301. const rightItems = [
  302. { tip: '活动时间:', text: item.activityStartTime || '' },
  303. { tip: `${item.activityLimit == 2 ? '专享券贡献:' : '爱心值贡献:'}`, text: `${item.valueLimit || 0}${item.activityLimit == 2 ? '张' : ''}` },
  304. { tip: '义工时长:', text: `${item.serviceHours || 0}小时` },
  305. { tip: '公益合作:', text: item.channelName || '' },
  306. ];
  307. rightItems.forEach((rightItem, idx) => {
  308. const textY = itemInnerY + (idx * rightItemLineHeight);
  309. ctx.setFillStyle('#676775');
  310. ctx.fillText(rightItem.tip, textStartX, textY);
  311. const tipWidth = ctx.measureText(rightItem.tip).width;
  312. ctx.setFillStyle('#252525');
  313. ctx.font = `bold ${rpxToPx(24)}px sans-serif`;
  314. // 绘制文本,这里不使用换行函数,因为内容一般不长
  315. ctx.fillText(rightItem.text, textStartX + tipWidth, textY);
  316. ctx.font = `normal ${rpxToPx(24)}px sans-serif`; // 重置字体
  317. });
  318. currentY += itemContentHeight;
  319. }
  320. // 4. 执行绘制并生成图片
  321. ctx.draw(false, () => {
  322. uni.canvasToTempFilePath({
  323. canvasId: 'pdf-canvas',
  324. destWidth: canvasWidth.value * 2, // 提高图片清晰度
  325. destHeight: canvasHeight.value * 2,
  326. fileType: 'png',
  327. quality: 1,
  328. success: (res) => {
  329. // 5. 保存图片到相册
  330. uni.saveImageToPhotosAlbum({
  331. filePath: res.tempFilePath,
  332. success: () => {
  333. uni.showToast({ title: '已保存到相册', icon: 'success' });
  334. },
  335. fail: (err) => {
  336. console.log(err);
  337. if (err.errMsg && err.errMsg.includes('auth deny')) {
  338. uni.showModal({
  339. title: '提示',
  340. content: '需要您授权保存相册',
  341. showCancel: false,
  342. success: () => uni.openSetting()
  343. });
  344. } else {
  345. uni.showToast({ title: '保存失败,请稍后重试', icon: 'none' });
  346. }
  347. }
  348. });
  349. },
  350. fail: (err) => {
  351. console.error('canvasToTempFilePath failed:', err);
  352. uni.showToast({ title: '图片生成失败', icon: 'none' });
  353. },
  354. complete: () => {
  355. uni.hideLoading();
  356. isLoading.value = false;
  357. }
  358. }, proxy); // 传入 proxy
  359. });
  360. } catch (error) {
  361. console.error('handleDownload error:', error);
  362. uni.hideLoading();
  363. isLoading.value = false;
  364. uni.showToast({
  365. title: '生成失败,请检查网络或资源链接', // 提示更具体
  366. icon: 'none'
  367. });
  368. }
  369. };
  370. onLoad((options)=>{
  371. proxy.$api.get(`/core/social/practice/record/${options?.id||''}`).then(({data:res})=>{
  372. if(res.code!==0) return proxy.$showToast(res.msg)
  373. certificateData.value = {...certificateData.value,...res.data};
  374. certificateData.value.idCard = certificateData.value.idCard&&certificateData.value.idCard.replace(/^(\d{6})(\d{8})(\d{3}[\dX])$/i,'$1********$3');
  375. activityList.value = res.data?.activityVos||[];
  376. })
  377. })
  378. </script>
  379. <style scoped lang="scss">
  380. .common_page {
  381. background-color: #F5F5F5;
  382. padding: 0 0 184rpx;
  383. .prove{
  384. margin: 20rpx 24rpx 0; // 改为外边距,避免影响全屏截图
  385. width: calc(100% - 40rpx);
  386. background: url('https://transcend.ringzle.com/xiaozhi-app/profile/2025/11/20/57b2f49c-beac-49e7-8840-777ec860ae59.png') no-repeat;
  387. background-size: 100% 100%;
  388. height: 945rpx;
  389. position: relative;
  390. &-logo{
  391. width: 133rpx;
  392. position: absolute;
  393. top: 58rpx;
  394. left: 72rpx;
  395. }
  396. &-title{
  397. width: 363rpx;
  398. margin-top: 101rpx;
  399. }
  400. &-line{
  401. width: 400rpx;
  402. margin-top: 7rpx;
  403. }
  404. &-seal{
  405. width: 131rpx;
  406. border-radius: 50%;
  407. position: absolute;
  408. bottom: 69rpx;
  409. right: 73rpx;
  410. }
  411. &-no{
  412. font-family: SourceHanSerifSC, SourceHanSerifSC;
  413. font-weight: bold;
  414. font-size: 20rpx;
  415. color: #9F793F;
  416. line-height: 20rpx;
  417. letter-spacing: 1px;
  418. margin-top: 9rpx;
  419. }
  420. &-info{
  421. margin-top: 27rpx;
  422. overflow: auto;
  423. &-pre{
  424. margin-top: 22rpx;
  425. &-left{
  426. width: 135rpx;
  427. &-text{
  428. font-family: PingFangSC, PingFang SC;
  429. font-weight: 400;
  430. font-size: 17rpx;
  431. color: #252525;
  432. line-height: 17rpx;
  433. letter-spacing: 1px;
  434. &.space{
  435. letter-spacing: 10rpx;
  436. }
  437. }
  438. &-tip{
  439. font-family: PingFangSC, PingFang SC;
  440. font-weight: 400;
  441. font-size: 10rpx;
  442. color: #252525;
  443. line-height: 10rpx;
  444. margin-top: 3rpx;
  445. }
  446. }
  447. &-right{
  448. width: 188rpx;
  449. font-family: PingFangSC, PingFang SC;
  450. font-weight: 400;
  451. font-size: 17rpx;
  452. color: #252525;
  453. line-height: 17rpx;
  454. padding-bottom: 8rpx;
  455. border-bottom: 2rpx solid #DDCEAF;
  456. }
  457. }
  458. }
  459. &-memo{
  460. width: 100%;
  461. padding: 0 80rpx;
  462. box-sizing: border-box;
  463. margin-top: 79rpx;
  464. font-family: PingFangSC, PingFang SC;
  465. font-weight: 400;
  466. font-size: 20rpx;
  467. color: #252525;
  468. line-height: 31rpx;
  469. label{ // canvas无法直接渲染span,颜色在js中处理
  470. color: #C9A771;
  471. margin: 0 5rpx;
  472. }
  473. }
  474. &-bottom{
  475. width: 100%;
  476. padding: 0 80rpx;
  477. box-sizing: border-box;
  478. margin-top: 89rpx;
  479. overflow: hidden;
  480. &-pre{
  481. margin-top: 16rpx;
  482. font-family: PingFangSC, PingFang SC;
  483. font-weight: 400;
  484. font-size: 17rpx;
  485. color: #252525;
  486. line-height: 20rpx;
  487. }
  488. }
  489. }
  490. .list{
  491. margin: 0 24rpx;
  492. &-box{
  493. margin-top: 20rpx;
  494. padding: 36rpx 20rpx;
  495. background: #FFFFFF;
  496. .title{
  497. font-family: PingFang-SC, PingFang-SC;
  498. font-weight: bold;
  499. font-size: 32rpx;
  500. color: #252525;
  501. line-height: 32rpx;
  502. }
  503. .content{
  504. margin-top: 30rpx;
  505. // .left{
  506. // width: 182rpx;
  507. // height: 240rpx;
  508. // image{
  509. // width: 100%;
  510. // height: 100%;
  511. // }
  512. // }
  513. .right{
  514. width: 100%;
  515. // width: calc(100% - 182rpx);
  516. // padding-left: 20rpx;
  517. // box-sizing: border-box;
  518. &-pre{
  519. margin-top: 20rpx;
  520. &:first-child{
  521. margin-top: 12rpx;
  522. }
  523. .tip{
  524. width: 150rpx;
  525. font-family: PingFangSC, PingFang SC;
  526. font-weight: 400;
  527. font-size: 24rpx;
  528. color: #676775;
  529. }
  530. .text{
  531. width: calc(100% - 150rpx);
  532. font-family: PingFang-SC, PingFang-SC;
  533. font-weight: bold;
  534. font-size: 24rpx;
  535. color: #252525;
  536. }
  537. }
  538. }
  539. }
  540. }
  541. }
  542. .btn{
  543. width: calc(100% - 210rpx);
  544. height: 90rpx;
  545. background: #B7F358;
  546. border-radius: 45rpx;
  547. font-family: PingFang-SC, PingFang-SC;
  548. font-weight: bold;
  549. font-size: 32rpx;
  550. color: #151B29;
  551. line-height: 90rpx;
  552. text-align: center;
  553. letter-spacing: 2rpx;
  554. position: fixed;
  555. left: 105rpx;
  556. bottom: 64rpx;
  557. z-index: 10;
  558. }
  559. }
  560. </style>