├── .DS_Store ├── .cloudbase └── container │ └── debug.json ├── .gitignore ├── LICENSE ├── README.md ├── cloudfunctions ├── addUser │ ├── index.js │ ├── package-lock.json │ └── package.json ├── autoKoutu │ ├── config.json │ ├── index.js │ └── package.json ├── baiduKoutu │ ├── config.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── batchUpload │ ├── config.json │ ├── index.js │ └── package.json ├── cloudbase_auth │ ├── config.json │ ├── index.js │ └── package.json ├── customerService │ ├── config.json │ ├── index.js │ └── package.json ├── getClothes │ ├── config.json │ ├── index.js │ └── package.json ├── getHairs │ ├── config.json │ ├── index.js │ └── package.json ├── getTodayBgList │ ├── config.json │ ├── index.js │ └── package.json ├── imageCompose │ ├── config.json │ ├── index.js │ └── package.json ├── imagemin │ ├── config.json │ ├── index.js │ └── package.json ├── imgSecCheck │ ├── AliCloud.js │ ├── config.json │ ├── index.js │ └── package.json ├── printStyle │ ├── config.json │ ├── index.js │ └── package.json ├── setUserInfo │ ├── config.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── shareSuccessCallback │ ├── config.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── shareUpdate │ ├── config.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── subscribeEvent │ ├── config.json │ ├── index.js │ └── package.json ├── timingDelete │ ├── config.json │ ├── index.js │ └── package.json ├── timingGetImage │ ├── config.json │ ├── index.js │ └── package.json ├── timingTriggerSignMessage │ ├── config.json │ ├── index.js │ └── package.json ├── timingUpdateBgListIndex │ ├── config.json │ ├── index.js │ └── package.json ├── triggerSubscrib │ ├── config.json │ ├── index.js │ └── package.json ├── updateImageCount │ ├── config.json │ ├── index.js │ └── package.json ├── useCount │ ├── index.js │ ├── package-lock.json │ └── package.json ├── useVipCount │ ├── index.js │ ├── package-lock.json │ └── package.json ├── vipKoutu │ ├── config.json │ ├── index.js │ ├── package-lock.json │ └── package.json └── xiangguan │ ├── config.json │ ├── imgList.js │ ├── index.js │ └── package.json ├── miniprogram ├── .DS_Store ├── app.js ├── app.json ├── app.miniapp.json ├── app.wxss ├── colorui │ ├── icon.wxss │ └── main.wxss ├── components │ ├── color-picker │ │ ├── color-picker.js │ │ ├── color-picker.json │ │ ├── color-picker.wxml │ │ ├── color-picker.wxss │ │ └── color-picker.wxss.map │ └── pageScrollMessage │ │ ├── pageScrollMessage.js │ │ ├── pageScrollMessage.json │ │ ├── pageScrollMessage.wxml │ │ └── pageScrollMessage.wxss ├── images │ ├── WechatIMG199.jpeg │ ├── index-active.png │ ├── index.png │ ├── me-active.png │ ├── me.png │ ├── shareShow.jpg │ └── take.png ├── pages │ ├── editPhoto │ │ ├── complete │ │ │ ├── complete.js │ │ │ ├── complete.json │ │ │ ├── complete.wxml │ │ │ └── complete.wxss │ │ ├── editPhotoPlus │ │ │ ├── editPhotoPlus.js │ │ │ ├── editPhotoPlus.json │ │ │ ├── editPhotoPlus.wxml │ │ │ ├── editPhotoPlus.wxss │ │ │ ├── hex-rgb.js │ │ │ ├── imgSecCheck.js │ │ │ └── touch.js │ │ ├── hair │ │ │ ├── hair.js │ │ │ ├── hair.json │ │ │ ├── hair.wxml │ │ │ └── hair.wxss │ │ ├── imageStyle │ │ │ ├── imageStyle.js │ │ │ ├── imageStyle.json │ │ │ ├── imageStyle.wxml │ │ │ └── imageStyle.wxss │ │ └── images │ │ │ ├── acl8s-bu9e0-016_s.png │ │ │ ├── ahvga-47qgd-003_s.png │ │ │ ├── author.png │ │ │ ├── click-white-left.png │ │ │ ├── click-white.png │ │ │ ├── crop.png │ │ │ ├── look-video.png │ │ │ ├── move-white.png │ │ │ ├── use.png │ │ │ └── zoom-white.png │ ├── helpMake │ │ ├── helpMake.js │ │ ├── helpMake.json │ │ ├── helpMake.wxml │ │ └── helpMake.wxss │ ├── imgCompose │ │ ├── compose.js │ │ ├── compose.json │ │ ├── compose.wxml │ │ └── compose.wxss │ ├── imgZip │ │ ├── image │ │ │ └── choosePic.png │ │ ├── imgZip.js │ │ ├── imgZip.json │ │ ├── imgZip.wxml │ │ └── imgZip.wxss │ ├── index │ │ ├── .DS_Store │ │ ├── images │ │ │ ├── 3001677327372_.pic.jpg │ │ │ ├── custom.png │ │ │ ├── home_icon0.png │ │ │ ├── home_icon1.png │ │ │ ├── home_icon2.png │ │ │ ├── home_icon3.png │ │ │ ├── home_icon4.png │ │ │ ├── home_icon5.png │ │ │ ├── home_icon6.png │ │ │ ├── home_icon7.png │ │ │ ├── hot.png │ │ │ ├── icon-more.png │ │ │ ├── icon-size1.png │ │ │ ├── icon-size2.png │ │ │ ├── search-input.png │ │ │ ├── space-img.png │ │ │ └── 微信图片_20200712103220.jpg │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── mein │ │ ├── images │ │ │ └── wave.gif │ │ ├── mein.js │ │ ├── mein.json │ │ ├── mein.wxml │ │ └── mein.wxss │ ├── preEdit │ │ ├── preEdit.js │ │ ├── preEdit.json │ │ ├── preEdit.wxml │ │ └── preEdit.wxss │ ├── searchSize │ │ ├── custom │ │ │ ├── custom.js │ │ │ ├── custom.json │ │ │ ├── custom.wxml │ │ │ └── custom.wxss │ │ ├── images │ │ │ ├── card-people.png │ │ │ ├── custom-img.png │ │ │ └── search-input.png │ │ ├── searchSize.js │ │ ├── searchSize.json │ │ ├── searchSize.wxml │ │ ├── searchSize.wxss │ │ └── searchView │ │ │ ├── searchView.js │ │ │ ├── searchView.json │ │ │ ├── searchView.wxml │ │ │ └── searchView.wxss │ ├── share │ │ ├── share.js │ │ ├── share.json │ │ ├── share.wxml │ │ └── share.wxss │ └── zipSuccess │ │ ├── zipSuccess.js │ │ ├── zipSuccess.json │ │ ├── zipSuccess.wxml │ │ └── zipSuccess.wxss └── sitemap.json ├── project.config.json ├── project.miniapp.json └── project.private.config.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/.DS_Store -------------------------------------------------------------------------------- /.cloudbase/container/debug.json: -------------------------------------------------------------------------------- 1 | {"containers":[]} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/pachong 3 | **/uploadTest 4 | **/example -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 证件照 小程序 (原生微信小程序 + 云开发) 3 | 一个用于生成证件照的小程序 4 | 5 | ## 本项目仅供学习交流,禁止商业用途 6 | 7 | **声明:本项目禁止商用!否则后果自负。** 8 | 9 | **网络上发现了一些基于此项目的二次创作,感谢你们的二次创作。请尊重原创,辛苦二次创作以及分享的时候注明原作者原项目来源。** 10 | 11 | **不要直接问怎么配置上架。** 12 | 13 | **技术指导请先赞赏** 14 | 15 | ### 功能 16 | 17 | 用于免费快速生成证件照 18 | 19 | - 可自定义照片尺寸、背景颜色 20 | - 支持换装、换发型 21 | - 支持手势拖拽缩放编辑图片 22 | 23 | 24 | - 客服消息自动回复 25 | - 定时触发器 26 | - 云函数海报生成,图片合成 27 | 28 | ------------------------- 29 | 30 | ![小程序码](https://6465-dev-4iov0-1301148496.tcb.qcloud.la/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20210411162950.jpg?sign=1cdabb92e1b2f3ffa846fc4f8007f5f8&t=1618129824) 31 | 32 | ![欢迎骚扰](https://6465-dev-4iov0-1301148496.tcb.qcloud.la/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200606104940.jpg?sign=185169727273f47f237464b4ebf90106&t=1618129640) 33 | 34 | ![赞赏支持](https://6465-dev-4iov0-1301148496.tcb.qcloud.la/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20200327222252.jpg?sign=9b042f8caa5f3a4e4506cdd75b04f789&t=1618129652) 35 | 36 | 37 | -------------------------------------------------------------------------------- /cloudfunctions/addUser/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | const db = cloud.database() 7 | 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | const wxContext = cloud.getWXContext() 11 | 12 | const openid = wxContext.OPENID 13 | 14 | const { data } = await db.collection('user').where({ openid }).get() 15 | 16 | if (data.length) return { openid } 17 | 18 | await db.collection('user').add({ 19 | data: { 20 | openid, 21 | create_time: Date.now(), 22 | accumCreatePhoto: 0, 23 | count: 1, 24 | signInDate: '' 25 | } 26 | }) 27 | 28 | return { 29 | openid: wxContext.OPENID, 30 | unionid: wxContext.UNIONID 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cloudfunctions/addUser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getBaiduToken", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "latest" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/autoKoutu/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/autoKoutu/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | // 自动抠图,待完成 7 | const access_token = process.env.access_token 8 | 9 | // 云函数入口函数 10 | exports.main = async (event, context) => { 11 | const wxContext = cloud.getWXContext() 12 | 13 | return { 14 | event, 15 | openid: wxContext.OPENID, 16 | appid: wxContext.APPID, 17 | unionid: wxContext.UNIONID, 18 | } 19 | } -------------------------------------------------------------------------------- /cloudfunctions/autoKoutu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autoKoutu", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.3.2" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/baiduKoutu/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/baiduKoutu/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | const http = require('http') 4 | const https = require('https') 5 | const dayjs = require('dayjs') 6 | const AipBodyAnalysisClient = require("baidu-aip-sdk").bodyanalysis; 7 | 8 | // 设置APPID/AK/SK 9 | const APP_ID = process.env.APP_ID; 10 | const API_KEY = process.env.API_KEY; 11 | const SECRET_KEY = process.env.SECRET_KEY; 12 | 13 | // 新建一个对象,建议只保存一个对象调用服务接口 14 | const client = new AipBodyAnalysisClient(APP_ID, API_KEY, SECRET_KEY); 15 | 16 | cloud.init() 17 | 18 | const db = cloud.database() 19 | 20 | // 云函数入口函数 21 | exports.main = async (event, context) => { 22 | // 获取图片buffer 23 | const imgBuffer = await getHttpBuffer(event.filePath) 24 | // 图片的base64 25 | const imageBase64 = encodeURI(imgBuffer.toString('base64')) 26 | // 百度抠图结果 27 | const { foreground, error_code, error_msg } = (await client.bodySeg(imageBase64, { type: 'foreground' })) 28 | if (error_code) return { msg: error_msg } 29 | if (!foreground) return { foreground, error_code, error_msg } 30 | 31 | // 将抠图结果存储到云存储 32 | const resultFileId = await uploadBase64(foreground) 33 | db.collection('tmp-file').add({ data: { time: Date.now(), fileID: resultFileId } }) 34 | const url = await getFileUrlByFileID(resultFileId) 35 | 36 | return { 37 | originFilePath: event.filePath, 38 | baiduKoutuResultFileId: resultFileId, 39 | baiduKoutuUrl: url 40 | } 41 | } 42 | 43 | // 根据http地址获取图片 buffer 44 | function getHttpBuffer (src) { 45 | const protocol = src.split('://')[0] 46 | return new Promise((resolve, reject) => { 47 | ;({ http, https }[protocol]).get(src, res => { 48 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 49 | let rawData = '' 50 | res.setEncoding('binary') 51 | res.on('data', chunk => (rawData += chunk)) 52 | res.on('end', () => resolve(Buffer.from(rawData, 'binary'))) 53 | }) 54 | }) 55 | } 56 | 57 | // base64 转为图片,存入云存储, 返回文件id 58 | async function uploadBase64 (base64) { 59 | // base64 转为buffer 60 | const buffer = Buffer.from(base64, 'base64') 61 | // 临时文件名 62 | const temFileName = `${Date.now()}-${Math.random()}.png` 63 | // 将图片上传到云存储,并拿到文件id 64 | return await cloudUploadFile(`tmp/${dayjs().format('YYYY-MM-DD')}/${temFileName}`, buffer) 65 | 66 | } 67 | 68 | // 上传图片到云存储,返回图片id 69 | async function cloudUploadFile (cloudPath, fileContent) { 70 | return (await cloud.uploadFile({ cloudPath, fileContent })).fileID 71 | } 72 | 73 | // 获取文件的临时访问url 74 | async function getFileUrlByFileID (fileID) { 75 | return (await cloud.getTempFileURL({ 76 | fileList: [fileID] 77 | })).fileList[0].tempFileURL 78 | } -------------------------------------------------------------------------------- /cloudfunctions/baiduKoutu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "baiduKoutu", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "latest", 13 | "baidu-aip-sdk": "^2.4.6", 14 | "dayjs": "^1.10.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cloudfunctions/batchUpload/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/batchUpload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batchUpload", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/cloudbase_auth/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/cloudbase_auth/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | // 云函数入口函数 7 | exports.main = async (event, context) => { 8 | const wxContext = cloud.getWXContext() 9 | 10 | console.log(event) 11 | console.log(wxContext) 12 | // 跨账号调用时,由此拿到来源方小程序/公众号 AppID 13 | console.log(wxContext.FROM_APPID) 14 | // 跨账号调用时,由此拿到来源方小程序/公众号的用户 OpenID 15 | console.log(wxContext.FROM_OPENID) 16 | // 跨账号调用、且满足 unionid 获取条件时,由此拿到同主体下的用户 UnionID 17 | console.log(wxContext.FROM_UNIONID) 18 | 19 | return { 20 | errCode: 0, 21 | errMsg: '', 22 | auth: JSON.stringify({}) 23 | } 24 | } -------------------------------------------------------------------------------- /cloudfunctions/cloudbase_auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudbase_auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/customerService/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/customerService/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | // 云函数入口函数 7 | exports.main = async (event, context) => { 8 | const wxContext = cloud.getWXContext() 9 | 10 | if (event.Content && event.Content.indexOf('人工') > -1) { 11 | return await sendImg(wxContext) 12 | } 13 | 14 | // await sendText(wxContext) 15 | 16 | await sendLink(wxContext) 17 | 18 | await sendImg(wxContext) 19 | 20 | return { 21 | event 22 | } 23 | } 24 | 25 | // 发送文字消息 26 | function sendText (wxContext) { 27 | return cloud.openapi.customerServiceMessage.send({ 28 | touser: wxContext.OPENID, 29 | msgtype: 'text', 30 | text: { content: '您好, 点击下方查看 证件照制作教程。 如有其他问题,请扫描下方二维码添加微信交流群(领次数、帮制作、人工定制等)。', }, 31 | }) 32 | } 33 | 34 | // 发送视频教程连接 35 | async function sendLink (wxContext) { 36 | const videoSrc = 'https://mp.weixin.qq.com/s/tUKESPrY3-LIiizw3Ho2tQ' 37 | await cloud.openapi.customerServiceMessage.send({ 38 | touser: wxContext.OPENID, 39 | msgtype: 'link', 40 | link: { 41 | title: '证件照、免冠照制作教程', 42 | description: '一寸、二寸证件照、免冠照,制作教程', 43 | url: videoSrc, 44 | thumbUrl: await getFileUrlByFileID('cloud://dev-4iov0.6465-dev-4iov0-1301148496/微信图片_20210109164906.jpg') 45 | } 46 | }) 47 | } 48 | 49 | // 发送图片 作者二维码 50 | async function sendImg (wxContext) { 51 | 52 | // 上传图片素材 我的二维码 53 | const { mediaId } = await cloud.openapi.customerServiceMessage.uploadTempMedia({ 54 | type: 'image', 55 | media: { 56 | contentType: 'image/jpg', 57 | value: await getCloudBuffer('cloud://dev-4iov0.6465-dev-4iov0-1301148496/WechatIMG199.jpeg') 58 | } 59 | }) 60 | 61 | // 发送图片消息 62 | await cloud.openapi.customerServiceMessage.send({ 63 | touser: wxContext.OPENID, 64 | msgtype: 'image', 65 | image: { 66 | mediaId: mediaId 67 | } 68 | }) 69 | 70 | } 71 | 72 | // 根据云存储id 获取图片buffer 73 | async function getCloudBuffer (fileID) { 74 | return (await cloud.downloadFile({ fileID: fileID })).fileContent 75 | } 76 | 77 | // 获取文件的临时访问url 78 | async function getFileUrlByFileID (fileID) { 79 | return (await cloud.getTempFileURL({ 80 | fileList: [fileID] 81 | })).fileList[0].tempFileURL 82 | } -------------------------------------------------------------------------------- /cloudfunctions/customerService/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "customerService", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.3.2" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/getClothes/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/getClothes/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 云函数入口函数,获取换装的图片地址 8 | exports.main = async (event, context) => { 9 | const wxContext = cloud.getWXContext() 10 | 11 | const clothes = {} 12 | clothes.nan = await getByTag('nan') 13 | clothes.nv = await getByTag('nv') 14 | clothes.other = await getByTag('other') 15 | 16 | return { 17 | event, 18 | clothes, 19 | openid: wxContext.OPENID, 20 | appid: wxContext.APPID, 21 | unionid: wxContext.UNIONID, 22 | } 23 | } 24 | 25 | // 查询图片地址 26 | async function getByTag (tag) { 27 | return (await db.collection('resource-images').where({ 28 | type: 'clothes', 29 | tag, 30 | }).get()).data 31 | } 32 | -------------------------------------------------------------------------------- /cloudfunctions/getClothes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getClothes", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.5.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/getHairs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/getHairs/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 云函数入口函数 8 | exports.main = async (event, context) => { 9 | const wxContext = cloud.getWXContext() 10 | 11 | const hairs = {} 12 | hairs.nan = await getByTag('nan') 13 | hairs.nv = await getByTag('nv') 14 | 15 | return { 16 | event, 17 | hairs, 18 | openid: wxContext.OPENID, 19 | appid: wxContext.APPID, 20 | unionid: wxContext.UNIONID, 21 | } 22 | } 23 | 24 | async function getByTag (tag) { 25 | return (await db.collection('resource-images').where({ 26 | type: 'hairs', 27 | tag, 28 | }).get()).data 29 | } 30 | -------------------------------------------------------------------------------- /cloudfunctions/getHairs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getHairs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.5.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/getTodayBgList/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/getTodayBgList/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 人像抠图共享小程序用 8 | 9 | // 云函数入口函数 10 | exports.main = async (event, context) => { 11 | 12 | const bgDataInfo = await db 13 | .collection('global-config') 14 | .doc('803723f46336f9d70058cdda67e1a3d8') 15 | .get() 16 | 17 | const { data } = await db.collection('bg-images') 18 | .orderBy('create_time', 'asc') 19 | .skip(bgDataInfo.data.bgListIndex) 20 | .limit(4) 21 | .get() 22 | 23 | return { 24 | bgList: data, 25 | } 26 | } -------------------------------------------------------------------------------- /cloudfunctions/getTodayBgList/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getTodayBgList", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/imageCompose/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/imageCompose/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | const http = require('http') 4 | const https = require('https') 5 | const dayjs = require('dayjs') 6 | const images = require('images') 7 | 8 | cloud.init() 9 | 10 | const db = cloud.database() 11 | 12 | // 云函数入口函数 13 | exports.main = async (event, context) => { 14 | // 需要返回的图片格式 15 | const imgType = event.imgType || 'png' || 'jpg' || 'jpeg' || 'gif' || 'bmp' || 'raw' || 'webp' 16 | // 需要返回的文件类型 17 | const dataType = event.dataType || 'fileID' || 'base64' || 'url' 18 | // 海报层叠数组 19 | const VImage = event.data || [] 20 | // 合成一张图片 21 | const img = await renderImg(VImage) 22 | // 根据入参所需返回图片类型 23 | const resContent = await getContentByDataType(img, dataType, imgType) 24 | 25 | return { 26 | imgType, 27 | dataType, 28 | value: resContent 29 | } 30 | 31 | } 32 | 33 | // 渲染图片,将多个层合并成一张图片 34 | function renderImg (VImage) { 35 | return VImage.reduce(async (preRomise, curData) => { 36 | 37 | const preResult = await preRomise 38 | let { width, height, x = 0, y = 0 } = curData 39 | width = +((+width).toFixed()) 40 | height = +((+height).toFixed()) 41 | x = +((+x).toFixed()) 42 | y = +((+y).toFixed()) 43 | 44 | let image = await createImage(curData) 45 | image = imageResize(image, width, height) 46 | 47 | if (preResult) return drawImg(preResult, image, x, y, width, height) 48 | else return image 49 | 50 | }, Promise.resolve()) 51 | } 52 | 53 | // 获取文件内容 base64 或 文件id 或 url 54 | async function getContentByDataType (img, dataType, imgType) { 55 | const imgName = `${Date.now()}-${Math.random()}.${imgType}` 56 | const imgBuffer = img.toBuffer(imgType) 57 | if (dataType === 'base64') return imgBuffer.toString('base64') 58 | const fileID = await cloudUploadFile(`tmp/${dayjs().format('YYYY-MM-DD')}/${imgName}`, imgBuffer) 59 | db.collection('tmp-file').add({ data: { time: Date.now(), fileID: fileID } }) 60 | if (dataType === 'fileID') return fileID 61 | if (dataType === 'url') return await getFileUrlByFileID(fileID) 62 | } 63 | 64 | // 获取文件的临时访问url 65 | async function getFileUrlByFileID (fileID) { 66 | return (await cloud.getTempFileURL({ 67 | fileList: [fileID] 68 | })).fileList[0].tempFileURL 69 | } 70 | 71 | // 上传图片到云存储,返回图片id 72 | async function cloudUploadFile (cloudPath, fileContent) { 73 | return (await cloud.uploadFile({ cloudPath, fileContent })).fileID 74 | } 75 | 76 | // 根据云存储id 获取图片buffer 77 | async function getCloudBuffer (fileID) { 78 | return (await cloud.downloadFile({ fileID: fileID })).fileContent 79 | } 80 | 81 | // 根据http地址获取图片 buffer 82 | function getHttpBuffer (src) { 83 | const protocol = src.split('://')[0] 84 | return new Promise((resolve, reject) => { 85 | ;({ http, https }[protocol]).get(src, res => { 86 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 87 | const rawData = [] 88 | res.setEncoding('binary') 89 | res.on('data', chunk => rawData.push(chunk)) 90 | res.on('end', () => resolve(Buffer.from(rawData.join(''), 'binary'))) 91 | }) 92 | }) 93 | } 94 | 95 | // 创建图片 96 | async function createImage (data) { 97 | if (data.bgc && data.bgc.length) return images(+data.width, +data.height).fill(...data.bgc) 98 | else if (data.imgId) return images(await getCloudBuffer(data.imgId)) 99 | else if (data.src) return images(await getHttpBuffer(data.src)) 100 | else return images(+data.width, +data.height) 101 | } 102 | 103 | // 图片设置宽高 104 | function imageResize (image, width, height) { 105 | if (!width && !height) return image 106 | const size = image.size() 107 | if (width === size.width && height === size.height) return image 108 | if (width && height) return image.resize(width, height) 109 | if (width) return image.width(width) 110 | if (height) return image.height(height) 111 | } 112 | 113 | // 将图片画在 一张图上 114 | function drawImg (baseImg, img, x, y, width, height) { 115 | if (x >= 0 && y >= 0) return baseImg.draw(img, x, y) 116 | img = images(img, x < 0 ? Math.abs(x) : 0, y < 0 ? Math.abs(y) : 0, width, height) 117 | return baseImg.draw(img, x > 0 ? x : 0, y > 0 ? y : 0) 118 | } 119 | 120 | -------------------------------------------------------------------------------- /cloudfunctions/imageCompose/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imageCompose", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "latest", 13 | "images": "^3.2.3", 14 | "dayjs": "^1.10.3" 15 | } 16 | } -------------------------------------------------------------------------------- /cloudfunctions/imagemin/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/imagemin/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | const http = require('http') 4 | const https = require('https') 5 | const images = require('images') 6 | const dayjs = require('dayjs') 7 | 8 | cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境 9 | 10 | const db = cloud.database() 11 | 12 | // 云函数入口函数 13 | exports.main = async (event, context) => { 14 | console.log(event) 15 | 16 | const checkResult = (await cloud.callFunction({ 17 | name: 'imgSecCheck', 18 | data: event 19 | })).result 20 | 21 | if (!checkResult.originImgPath) { 22 | return checkResult 23 | } 24 | 25 | const { filePath, type = 'jpg' } = event 26 | const width = Number(event.width) 27 | const height = Number(event.height) 28 | const quality = Number(event.quality) || 100 29 | if (!filePath) return { errMsg: '图片地址不能为空' } 30 | if (!width && !height) return { errMsg: '图片尺寸不能为空' } 31 | const imgType = 'jpg' // 只考虑jpg格式 32 | const img = images(await getHttpBuffer(filePath)) 33 | if (width) img.width(width) 34 | if (height) img.height(height) 35 | const imgBuffer = img.encode('jpg', { quality: quality }) 36 | const imgName = `imagemin-${Date.now()}-${Math.random()}.${imgType}` 37 | const fileID = await cloudUploadFile(`tmp/${dayjs().format('YYYY-MM-DD')}/${imgName}`, imgBuffer) 38 | await db.collection('tmp-file').add({ data: { time: Date.now(), fileID: fileID } }) 39 | return await getFileUrlByFileID(fileID) 40 | } 41 | 42 | // 根据http地址获取图片 buffer 43 | function getHttpBuffer (src) { 44 | const protocol = src.split('://')[0] 45 | return new Promise((resolve, reject) => { 46 | ;({ http, https }[protocol]).get(src, res => { 47 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 48 | const rawData = [] 49 | res.setEncoding('binary') 50 | res.on('data', chunk => rawData.push(chunk)) 51 | res.on('end', () => resolve(Buffer.from(rawData.join(''), 'binary'))) 52 | }) 53 | }) 54 | } 55 | 56 | // 获取文件的临时访问url 57 | async function getFileUrlByFileID (fileID) { 58 | return (await cloud.getTempFileURL({ 59 | fileList: [fileID] 60 | })).fileList[0].tempFileURL 61 | } 62 | 63 | // 上传图片到云存储,返回图片id 64 | async function cloudUploadFile (cloudPath, fileContent) { 65 | return (await cloud.uploadFile({ cloudPath, fileContent })).fileID 66 | } 67 | -------------------------------------------------------------------------------- /cloudfunctions/imagemin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imagemin", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3", 13 | "images": "3.2.4", 14 | "dayjs": "^1.10.3" 15 | } 16 | } -------------------------------------------------------------------------------- /cloudfunctions/imgSecCheck/AliCloud.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const $imageaudit20191230 = require("@alicloud/imageaudit20191230"); 3 | const imageaudit20191230 = $imageaudit20191230.default 4 | const $OpenApi = require("@alicloud/openapi-client"); 5 | const $Util = require("@alicloud/tea-util"); 6 | const Util = $Util.default 7 | const accessKeyId = process.env.accessKeyId; 8 | const accessKeySecret = process.env.accessKeySecret; 9 | 10 | module.exports = class Client { 11 | /** 12 | * 使用AK&SK初始化账号Client 13 | * @param accessKeyId 14 | * @param accessKeySecret 15 | * @return Client 16 | * @throws Exception 17 | */ 18 | static createClient() { 19 | const config = new $OpenApi.Config({ 20 | accessKeyId: accessKeyId, 21 | accessKeySecret: accessKeySecret, 22 | }); 23 | // 访问的域名 24 | config.endpoint = `imageaudit.cn-shanghai.aliyuncs.com`; 25 | return new imageaudit20191230(config); 26 | } 27 | 28 | static async main(imageURL) { 29 | const client = Client.createClient(); 30 | const task0 = new $imageaudit20191230.ScanImageRequestTask({ 31 | imageURL: imageURL, 32 | }); 33 | const scanImageRequest = new $imageaudit20191230.ScanImageRequest({ 34 | task: [task0], 35 | scene: ["porn", "terrorism", "live"], 36 | }); 37 | const runtime = new $Util.RuntimeOptions({}); 38 | try { 39 | // 复制代码运行请自行打印 API 的返回值 40 | const aliResult = await client.scanImageWithOptions(scanImageRequest, runtime); 41 | return { status: aliResult.body.data.results[0].subResults.every(item => item.suggestion === 'pass') } 42 | } catch (error) { 43 | // 如有需要,请打印 error 44 | Util.assertAsString(error.message); 45 | return { error } 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /cloudfunctions/imgSecCheck/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | "security.imgSecCheck" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /cloudfunctions/imgSecCheck/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const https = require('https') 3 | const cloud = require('wx-server-sdk') 4 | const AliCloud = require('./AliCloud') 5 | cloud.init({ 6 | env:cloud.DYNAMIC_CURRENT_ENV, 7 | }) 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | try { 11 | // 图片压缩 12 | const imgInfo = await imageMinify(event) 13 | // 图片校验 14 | return await imgSecCheck(event, imgInfo) 15 | } catch (error) { 16 | return await imgSecCheck(event, event) 17 | } 18 | } 19 | 20 | // 图片压缩 21 | async function imageMinify (imgInfo) { 22 | const { width, height, filePath, type } = imgInfo 23 | // 需要压缩,腾讯免费的图片安全接口要求的 24 | if ((width > 750 && height > 750) || width > 1334 || height > 1334) { 25 | const imgSize = getImageSize({ width, height }) 26 | const { result } = await cloud.callFunction({ 27 | name: 'imageCompose', 28 | data: { 29 | imgType: 'jpg', 30 | dataType: 'url', 31 | data: [{ ...imgSize, src: filePath }] 32 | } 33 | }) 34 | return { 35 | width: imgSize.width, 36 | height: imgSize.height, 37 | filePath: result.value, 38 | type: result.imageType 39 | } 40 | } 41 | return imgInfo 42 | } 43 | 44 | // 计算压缩后的图片宽高 45 | function getImageSize (imgInfo) { 46 | let width = 0, height = 0 47 | if (imgInfo.width > imgInfo.height) { 48 | width = 750 49 | height = 750 * imgInfo.height / imgInfo.width 50 | } else if (imgInfo.height > imgInfo.width) { 51 | height = 750 52 | width = 750 * imgInfo.width / imgInfo.height 53 | } else { 54 | width = height = 750 55 | } 56 | return { 57 | width, 58 | height 59 | } 60 | } 61 | 62 | // 获取图片buffer 63 | function getBuffer (src) { 64 | const protocol = src.split('://')[0] 65 | return new Promise((resolve, reject) => { 66 | ;({ http, https }[protocol]).get(src, res => { 67 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 68 | const rawData = [] 69 | res.setEncoding('binary') 70 | res.on('data', chunk => rawData.push(chunk)) 71 | res.on('end', () => resolve(Buffer.from(rawData.join(''), 'binary'))) 72 | }) 73 | }) 74 | } 75 | 76 | // 腾讯免费的图片内容安全检测 77 | async function imgSecCheck (event, imgInfo) { 78 | // 获取图片buffer 79 | const buffer = await getBuffer(imgInfo.filePath) 80 | try { 81 | const imgResult = await cloud.openapi.security.imgSecCheck({ 82 | media:{ 83 | header: {'content-Type': 'application/octe-stream'}, 84 | contentType: `image/${imgInfo.type}`, 85 | value: buffer 86 | } 87 | }) 88 | imgResult.filePath = imgInfo.filePath 89 | imgResult.originImgPath = event.filePath 90 | imgResult.originImgType = event.type 91 | return imgResult 92 | } catch (error) { 93 | // 校验含有敏感信息 94 | if (error.errCode === 87014) return error 95 | // 不好用,老报错,用阿里再检测一次 96 | return aliCheck(event, imgInfo) 97 | } 98 | } 99 | 100 | // 阿里云的图片内容安全接口监测 101 | async function aliCheck (event, imgInfo) { 102 | const aliRes = await AliCloud.main(event.filePath) 103 | // 阿里接口调用错误了 104 | if (aliRes.error) return { errCode: -604102, msg: aliRes.error.message } 105 | // 检测通过 106 | else if (aliRes.status) return { 107 | errCode: 0, 108 | filePath: imgInfo.filePath, 109 | originImgPath: event.filePath, 110 | originImgType: event.type 111 | } 112 | // 检测不通过 113 | else return { errCode: 87014 } 114 | } 115 | -------------------------------------------------------------------------------- /cloudfunctions/imgSecCheck/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imgSecCheck", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3", 13 | "@alicloud/imageaudit20191230": "^2.0.1" 14 | } 15 | } -------------------------------------------------------------------------------- /cloudfunctions/printStyle/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/printStyle/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | const http = require('http') 4 | const https = require('https') 5 | const images = require('images') 6 | const dayjs = require('dayjs') 7 | 8 | cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境 9 | 10 | const db = cloud.database() 11 | 12 | // 云函数入口函数 13 | exports.main = async (event, context) => { 14 | console.log(event) 15 | 16 | const checkResult = (await cloud.callFunction({ 17 | name: 'imgSecCheck', 18 | data: event 19 | })).result 20 | 21 | if (!checkResult.originImgPath) { 22 | return checkResult 23 | } 24 | 25 | const { filePath } = event 26 | const width = Number(event.width) 27 | const height = Number(event.height) 28 | const resultWidth = 1200 29 | const resultHeight = 1800 30 | 31 | if (!filePath) return { errMsg: '图片地址不能为空' } 32 | if (!width || !height) return { errMsg: '图片尺寸不能为空' } 33 | if (width > (resultWidth - 100)) return { errMsg: '图片宽度不能大于1100' } 34 | if (height > (resultHeight - 100)) return { errMsg: '图片高度不能大于1700' } 35 | 36 | const sourceImg = images(await getHttpBuffer(filePath)) 37 | 38 | const resultImg = images(resultWidth, resultHeight).fill(200, 200, 200, 0) 39 | 40 | let xCount = Math.floor(resultWidth / width) 41 | let xSpaces = (resultWidth % width) / (xCount + 1) 42 | 43 | let yCount = Math.floor(resultHeight / height) 44 | let ySpaces = (resultHeight % height) / (yCount + 1) 45 | 46 | console.log('xCount', xCount, 'yCount', yCount, 'ySpaces', ySpaces, 'xSpaces', xSpaces) 47 | 48 | for (let y = 0; y < yCount; y++) { 49 | for (let x = 0; x < xCount; x++) { 50 | const offsetX = x * width + xSpaces * (x + 1) 51 | const offsetY = y * height + ySpaces * (y + 1) 52 | console.log('offsetX', offsetX, 'offsetY', offsetY) 53 | resultImg.draw(sourceImg, offsetX, offsetY) 54 | } 55 | } 56 | 57 | const imgBuffer = resultImg.encode('jpg') 58 | const imgName = `print-${Date.now()}-${Math.random()}.${'jpg'}` 59 | const fileID = await cloudUploadFile(`tmp/${dayjs().format('YYYY-MM-DD')}/${imgName}`, imgBuffer) 60 | await db.collection('tmp-file').add({ data: { time: Date.now(), fileID: fileID } }) 61 | return await getFileUrlByFileID(fileID) 62 | 63 | } 64 | 65 | // 根据http地址获取图片 buffer 66 | function getHttpBuffer (src) { 67 | const protocol = src.split('://')[0] 68 | return new Promise((resolve, reject) => { 69 | ;({ http, https }[protocol]).get(src, res => { 70 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 71 | const rawData = [] 72 | res.setEncoding('binary') 73 | res.on('data', chunk => rawData.push(chunk)) 74 | res.on('end', () => resolve(Buffer.from(rawData.join(''), 'binary'))) 75 | }) 76 | }) 77 | } 78 | 79 | // 获取文件的临时访问url 80 | async function getFileUrlByFileID (fileID) { 81 | return (await cloud.getTempFileURL({ 82 | fileList: [fileID] 83 | })).fileList[0].tempFileURL 84 | } 85 | 86 | // 上传图片到云存储,返回图片id 87 | async function cloudUploadFile (cloudPath, fileContent) { 88 | return (await cloud.uploadFile({ cloudPath, fileContent })).fileID 89 | } -------------------------------------------------------------------------------- /cloudfunctions/printStyle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preintStyle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3", 13 | "images": "3.2.4", 14 | "dayjs": "^1.10.3" 15 | } 16 | } -------------------------------------------------------------------------------- /cloudfunctions/setUserInfo/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/setUserInfo/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | // 云函数入口函数 7 | exports.main = async (event, context) => { 8 | const wxContext = cloud.getWXContext() 9 | const data = Object.assign({}, event.data) 10 | delete data._id 11 | const db = cloud.database() 12 | const res = await db.collection('user').where({ 13 | openid: event.openid || wxContext.OPENID 14 | }).update({ data }) 15 | 16 | return { 17 | res, 18 | event, 19 | openid: wxContext.OPENID, 20 | appid: wxContext.APPID, 21 | unionid: wxContext.UNIONID, 22 | } 23 | } -------------------------------------------------------------------------------- /cloudfunctions/setUserInfo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setUserInfo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.0.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/shareSuccessCallback/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/shareSuccessCallback/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 云函数入口函数,邀请成功,来领次数了 8 | exports.main = async (event, context) => { 9 | const wxContext = cloud.getWXContext() 10 | 11 | // 更新邀请记录 12 | const shareRes = await db.collection('share').where({ 13 | _openid: wxContext.OPENID, 14 | }).update({ 15 | data: { [event.openid]: true } 16 | }) 17 | 18 | // 更新成功,加3次 19 | let useCountRes 20 | if (shareRes.stats.updated) { 21 | useCountRes = await cloud.callFunction({ 22 | name: 'useCount', 23 | data: { inc: 1, openid: wxContext.OPENID } 24 | }) 25 | } 26 | 27 | return { 28 | useCountRes, 29 | shareRes, 30 | event, 31 | openid: wxContext.OPENID, 32 | appid: wxContext.APPID, 33 | unionid: wxContext.UNIONID, 34 | } 35 | } -------------------------------------------------------------------------------- /cloudfunctions/shareSuccessCallback/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shareSuccessCallback", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~1.8.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/shareUpdate/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/shareUpdate/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | const _ = db.command 7 | 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | const wxContext = cloud.getWXContext() 11 | 12 | // 不是自己邀请自己 13 | if (event.shareOpenid !== wxContext.OPENID) { 14 | // 触发消息推送 15 | await triggerMessage(event, wxContext.OPENID) 16 | // 添加到邀请成功列表中 17 | await addToInviteesTodayList(event, wxContext) 18 | } 19 | // 给当前用户设置他的上一级,上一级就是邀请他的人 20 | setParent(event.shareOpenid, wxContext.OPENID) 21 | 22 | return { 23 | event, 24 | openid: wxContext.OPENID, 25 | appid: wxContext.APPID, 26 | unionid: wxContext.UNIONID, 27 | } 28 | } 29 | 30 | // 将用户添加到 邀请者的 列表中 31 | function addToInviteesTodayList (event, wxContext) { 32 | return db.collection('share').where({ 33 | openid: event.shareOpenid, 34 | }).update({ 35 | data: { 36 | invitedList: _.addToSet(wxContext.OPENID) 37 | } 38 | }) 39 | } 40 | 41 | // 触发订阅消息 42 | async function triggerMessage (event, currentOpenid) { 43 | const { data } = await db.collection('share').where({ 44 | _openid: event.shareOpenid, 45 | }).field({ invitedList: true }).get() 46 | if (!data.length) return 47 | if (data[0].invitedList.some(openid => openid === currentOpenid)) return 48 | cloud.callFunction({ 49 | name: 'triggerSubscrib', 50 | data: { 51 | openid: event.shareOpenid, 52 | tmplId: 'CNuffKDjmxEOU_hM44Cu0KoGqOjfdacpbk4LT1abcnE' 53 | } 54 | }) 55 | } 56 | 57 | // 给用户设置邀请人 58 | async function setParent (parentId, currentId) { 59 | const { data } = await db.collection('user').where({ openid: currentId }).get() 60 | if (data.parentOpenid) return 61 | await db.collection('user').where({ openid: currentId }).update({ 62 | data: { 63 | parentOpenid: parentId 64 | } 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /cloudfunctions/shareUpdate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shareUpdate", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~1.8.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/subscribeEvent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/subscribeEvent/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | const db = cloud.database() 7 | 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | const wxContext = cloud.getWXContext() 11 | 12 | // event 数据结构 13 | // const e = { 14 | // "CreateTime": 1663989948, 15 | // "Event": "subscribe_msg_popup_event", 16 | // "FromUserName": "obRga0ZrZkNe5ZF60smAvbscTM5Y", 17 | // "List": { 18 | // "PopupScene": "0", 19 | // "SubscribeStatusString": "accept", 20 | // "TemplateId": "CNuffKDjmxEOU_hM44Cu0KoGqOjfdacpbk4LT1abcnE" 21 | // }, 22 | // "MsgType": "event", 23 | // "ToUserName": "gh_82e5b8a1fccf", 24 | // "userInfo": { 25 | // "appId": "wxbe6c30a61a51b422", 26 | // "openId": "obRga0ZrZkNe5ZF60smAvbscTM5Y" 27 | // } 28 | // } 29 | 30 | if (event.List.SubscribeStatusString !== 'accept') return 31 | 32 | db.collection('subscrib-message').add({ 33 | data: { 34 | openid: event.userInfo.openId, 35 | tmplId: event.List.TemplateId, 36 | time: event.CreateTime 37 | }, 38 | }) 39 | 40 | return { 41 | event, 42 | openid: wxContext.OPENID, 43 | appid: wxContext.APPID, 44 | unionid: wxContext.UNIONID, 45 | } 46 | } -------------------------------------------------------------------------------- /cloudfunctions/subscribeEvent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subscribeEvent", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/timingDelete/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "triggers": [ 3 | { 4 | "name": "timingDelete", 5 | "type": "timer", 6 | "config": "0 */30 * * * * *" 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /cloudfunctions/timingDelete/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | const db = cloud.database() 7 | 8 | // 云函数入口函数, 定时删除云存储的用户生成的图片 9 | exports.main = async (event, context) => { 10 | 11 | // 一小时前的毫秒数 12 | const time = Date.now() - 1000 * 60 * 60 13 | 14 | const _ = db.command 15 | // 连接数据库 16 | const tmpFiles = db.collection('tmp-file') 17 | // 查询一小时前上传的图片 18 | const { data: list } = await tmpFiles.where({ time: _.lt(time) }).limit(50).get() 19 | 20 | // 没查到 21 | if (!list.length) return { delete: false, list } 22 | // 找出图片的 fileID 23 | const fileList = list.map(v => v.fileID) 24 | // 从云存储中删除图片 25 | const { fileList: resultList } = await cloud.deleteFile({ fileList }) 26 | // 删除成功的和文件不存在的 27 | const removedFiles = resultList.filter(file => file.status === 0 || file.status === -503003).map(file => file.fileID) 28 | // 从数据库中删除记录 29 | await tmpFiles.where({ 30 | fileID: _.in(removedFiles) 31 | }).remove() 32 | 33 | return { delete: true, list: resultList } 34 | } -------------------------------------------------------------------------------- /cloudfunctions/timingDelete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timingDelete", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.3.2" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/timingGetImage/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | }, 6 | "triggers": [ 7 | { 8 | "name": "timingGetImage", 9 | "type": "timer", 10 | "config": "0 */30 * * * * *" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /cloudfunctions/timingGetImage/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const request = require('request') 3 | const cloud = require('wx-server-sdk') 4 | const imgApi = process.env.IMAGE_API 5 | const meinvImgApi = process.env.MEINV_IMAGE_API 6 | 7 | cloud.init() 8 | 9 | const db = cloud.database() 10 | // 共享给人像抠图小程序用 11 | // 云函数入口函数 12 | exports.main = async (event, context) => { 13 | 14 | // 获取一张美女图片,不管成功不成功,先存起来 15 | getMeinvImgUrl() 16 | 17 | // 返回一张风景图 18 | const imgUrl = await getImgUrl() 19 | 20 | return { 21 | imgurl: imgUrl, 22 | } 23 | } 24 | 25 | function getImgUrl () { 26 | return new Promise((resolve, reject) => { 27 | 28 | request(imgApi, async function (error, response, body) { 29 | 30 | const data = JSON.parse(body) 31 | if (data.code != 200) return reject(data) 32 | 33 | try { 34 | await db.collection('images').doc(data.imgurl).get() 35 | } catch (error) { 36 | db.collection('images').add({ 37 | data: { 38 | _id: data.imgurl, 39 | imgurl: data.imgurl, 40 | create_time: Date.now(), 41 | category: 'fengjing', 42 | type: 'm' 43 | } 44 | }) 45 | } 46 | 47 | const { total } = await db.collection('bg-images').where({ imgurl: data.imgurl }).count() 48 | 49 | if (total > 0) return resolve(data.imgurl) 50 | 51 | db.collection('bg-images').add({ 52 | data: { imgurl: data.imgurl, create_time: Date.now() } 53 | }) 54 | 55 | resolve(data.imgurl) 56 | }) 57 | }) 58 | } 59 | 60 | // 获取一张美女图片,存在库里 61 | function getMeinvImgUrl () { 62 | request(meinvImgApi, async function (error, response, body) { 63 | const data = JSON.parse(body) 64 | if (data.code != 200) return reject(data) 65 | 66 | try { 67 | await db.collection('images').doc(data.imgurl).get() 68 | } catch (error) { 69 | db.collection('images').add({ 70 | data: { 71 | _id: data.imgurl, 72 | imgurl: data.imgurl, 73 | create_time: Date.now(), 74 | category: 'meinv', 75 | type: 'm' 76 | } 77 | }) 78 | } 79 | 80 | }) 81 | } -------------------------------------------------------------------------------- /cloudfunctions/timingGetImage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timingGetImage", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/timingTriggerSignMessage/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | }, 6 | "triggers": [ 7 | { 8 | "name": "timingGetImage", 9 | "type": "timer", 10 | "config": "0 30 9 * * ? *" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /cloudfunctions/timingTriggerSignMessage/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | const db = cloud.database() 7 | 8 | const tmplId = 'h_P9sODh-NfeXteA5t7h6sS9BAIjtEyppGwt-biQIQ0' 9 | 10 | // 云函数入口函数 11 | exports.main = async (event, context) => { 12 | 13 | const res = await db.collection('subscrib-message').where({ tmplId }).get() 14 | 15 | res.data.forEach(item => getSignStatus(item.openid)) 16 | 17 | return res 18 | } 19 | 20 | // 获取前端状态 21 | async function getSignStatus (openid) { 22 | const { data } = await db.collection('user').where({ openid }).get() 23 | if (data[0].signInDate === new Date().toDateString()) return 24 | trigger(openid) 25 | } 26 | 27 | // 触发消息 28 | function trigger (openid) { 29 | cloud.callFunction({ 30 | name: 'triggerSubscrib', 31 | data: { 32 | openid, 33 | tmplId 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /cloudfunctions/timingTriggerSignMessage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timingTriggerSignMessage", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/timingUpdateBgListIndex/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | }, 6 | "triggers": [ 7 | { 8 | "name": "timingGetImage", 9 | "type": "timer", 10 | "config": "0 0 0 * * ? *" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /cloudfunctions/timingUpdateBgListIndex/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 共享给人像抠图小程序用 8 | 9 | // 云函数入口函数 10 | exports.main = async (event, context) => { 11 | 12 | const { total } = await db.collection('bg-images').where({}).count() 13 | 14 | const { data } = await db 15 | .collection('global-config') 16 | .doc('803723f46336f9d70058cdda67e1a3d8') 17 | .get() 18 | 19 | let index = data.bgListIndex + 4 20 | if (index >= total) { 21 | index = 0 22 | } 23 | 24 | await db 25 | .collection('global-config') 26 | .doc('803723f46336f9d70058cdda67e1a3d8') 27 | .update({ 28 | data: { 29 | bgListIndex: index 30 | } 31 | }) 32 | 33 | return { 34 | event, 35 | } 36 | } -------------------------------------------------------------------------------- /cloudfunctions/timingUpdateBgListIndex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timingUpdateBgListIndex", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/triggerSubscrib/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ], 5 | "triggers": [] 6 | } 7 | } -------------------------------------------------------------------------------- /cloudfunctions/triggerSubscrib/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | 6 | const db = cloud.database() 7 | 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | const tmplId = event.tmplId 11 | try { 12 | const result = await cloud.openapi.subscribeMessage.send({ 13 | touser: event.openid, 14 | templateId: tmplId, 15 | ...getMessageData(tmplId) 16 | }) 17 | // 删除订阅记录 18 | if (result.errCode === 0) { 19 | await db.collection('subscrib-message').where({ 20 | openid: event.openid, 21 | tmplId: tmplId 22 | }).remove() 23 | } 24 | return { 25 | ...result, 26 | msgid: result.msgid.toString() 27 | } 28 | } catch (err) { 29 | return err 30 | } 31 | } 32 | 33 | 34 | function getMessageData (tmplId) { 35 | return { 36 | // 邀请成功 37 | 'CNuffKDjmxEOU_hM44Cu0KoGqOjfdacpbk4LT1abcnE': { 38 | page: '/pages/share/share', 39 | data: { 40 | number1: { value: 3 }, 41 | number2: { value: 1 } 42 | } 43 | }, 44 | // 签到提醒 45 | 'h_P9sODh-NfeXteA5t7h6sS9BAIjtEyppGwt-biQIQ0': { 46 | page: '/pages/mein/mein', 47 | data: { 48 | phrase6: {value: '今日未签到'}, 49 | thing7: { value: '1次免冠照片制作' }, 50 | thing5: { value: '点击立即签到' } 51 | } 52 | } 53 | }[tmplId] 54 | } 55 | -------------------------------------------------------------------------------- /cloudfunctions/triggerSubscrib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "triggerSubscrib", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/updateImageCount/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | }, 6 | 7 | "triggers": [ 8 | { 9 | "name": "timingUpdateImageCount", 10 | "type": "timer", 11 | "config": "0 */30 * * * * *" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /cloudfunctions/updateImageCount/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | 7 | // 数据库数据发生变化时会自动触发这个云函数 8 | 9 | // 云函数入口函数 10 | exports.main = async (event, context) => { 11 | 12 | const [m_meinv, m_fengjing, m_dongman, m_biying, pc_meinv, pc_fengjing, pc_dongman, pc_biying] = await Promise.all([ 13 | getTotal('meinv', 'm'), 14 | getTotal('fengjing', 'm'), 15 | getTotal('dongman', 'm'), 16 | getTotal('biying', 'm'), 17 | getTotal('meinv', 'pc'), 18 | getTotal('fengjing', 'pc'), 19 | getTotal('dongman', 'pc'), 20 | getTotal('biying', 'pc'), 21 | ]) 22 | 23 | await db.collection('global-config') 24 | .doc('e936e40c633acad300608f043f5b6ec8') 25 | .update({ 26 | data: { 27 | m_meinv, m_fengjing, m_dongman, m_biying, pc_meinv, pc_fengjing, pc_dongman, pc_biying 28 | } 29 | }) 30 | 31 | return { 32 | m_meinv, m_fengjing, m_dongman, m_biying, pc_meinv, pc_fengjing, pc_dongman, pc_biying 33 | } 34 | } 35 | 36 | async function getTotal (category, type) { 37 | const { total } = await db.collection('images').where({ 38 | category, 39 | type 40 | }).count() 41 | return total 42 | } 43 | -------------------------------------------------------------------------------- /cloudfunctions/updateImageCount/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "updateImageCount", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /cloudfunctions/useCount/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | const _ = db.command 7 | 8 | // 云函数入口函数,更新用户剩余次数 9 | exports.main = async (event, context) => { 10 | const wxContext = cloud.getWXContext() 11 | const data = { 12 | // 累计生成照片 只增不减 13 | accumCreatePhoto: _.inc(event.inc > 0 ? 0 : -(event.inc)), 14 | count: _.inc(event.isVip ? 0 : (1 * event.inc)) 15 | } 16 | // 签到,更新签到时间 17 | if (event.signIn) { 18 | data.signInDate = new Date(Date.now() + 1000 * 60 * 60 * 8).toDateString().trim() 19 | } 20 | // 更新当前用户次数 21 | const res = await db.collection('user').where({ 22 | openid: event.openid || wxContext.OPENID 23 | }).update({ data }) 24 | 25 | return { 26 | success: res.stats.updated > 0, 27 | openid: wxContext.OPENID, 28 | appid: wxContext.APPID, 29 | unionid: wxContext.UNIONID, 30 | } 31 | } -------------------------------------------------------------------------------- /cloudfunctions/useCount/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "savePhotoId", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "latest" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/useVipCount/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | cloud.init() 5 | const db = cloud.database() 6 | const _ = db.command 7 | 8 | // 云函数入口函数 9 | exports.main = async (event, context) => { 10 | const wxContext = cloud.getWXContext() 11 | const data = { 12 | vipCount: _.inc(event.lookVideo ? 1 : (1 * event.inc)), 13 | lookVideoCount: _.inc(event.lookVideo ? 1 : 0) 14 | } 15 | 16 | const res = await db.collection('user').where({ 17 | openid: event.openid || wxContext.OPENID 18 | }).update({ data }) 19 | 20 | return { 21 | success: res.stats.updated > 0, 22 | openid: wxContext.OPENID, 23 | appid: wxContext.APPID, 24 | unionid: wxContext.UNIONID, 25 | } 26 | } -------------------------------------------------------------------------------- /cloudfunctions/useVipCount/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "savePhotoId", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "latest" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cloudfunctions/vipKoutu/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/vipKoutu/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | const https = require('https') 4 | const http = require('http') 5 | const axios = require('axios') 6 | 7 | cloud.init() 8 | 9 | // 云函数入口函数 10 | exports.main = async (event, context) => { 11 | console.log(event) 12 | let type = event.imgType 13 | // 获取图片buffer 14 | let targetBuffer = await getHttpBuffer(event.imgSrc) 15 | // buffer转成base64 16 | let imageBase64 = encodeURI(targetBuffer.toString('base64')) 17 | try { 18 | // 抠图接口调用 19 | const result = await koutu(type, imageBase64) 20 | // 抠图成功 21 | if (result.status === 0) return result 22 | } catch (error) { 23 | 24 | // 没有压缩图 25 | if (!event.compressSrc) return result 26 | 27 | // 使用压缩图抠图 28 | targetBuffer = await getHttpBuffer(event.compressSrc) 29 | imageBase64 = encodeURI(targetBuffer.toString('base64')) 30 | return await koutu('jpg', imageBase64) 31 | } 32 | 33 | } 34 | 35 | async function koutu (type, imageBase64) { 36 | return (await axios.post('https://aliapi.aisegment.com/segment/matting', { 37 | type: type, 38 | photo: imageBase64 39 | }, { 40 | headers: { 41 | 'Content-Type': 'application/json; charset=UTF-8', 42 | 'Authorization': 'APPCODE ' + process.env.APPCODE 43 | } 44 | })).data 45 | } 46 | 47 | 48 | // 根据http地址获取图片 buffer 49 | function getHttpBuffer (src) { 50 | const protocol = src.split('://')[0] 51 | return new Promise((resolve, reject) => { 52 | ;({ http, https }[protocol]).get(src, res => { 53 | if (res.statusCode !== 200) return reject(new Error('图片加载失败')) 54 | let rawData = '' 55 | res.setEncoding('binary') 56 | res.on('data', chunk => (rawData += chunk)) 57 | res.on('end', () => resolve(Buffer.from(rawData, 'binary'))) 58 | }) 59 | }) 60 | } 61 | 62 | -------------------------------------------------------------------------------- /cloudfunctions/vipKoutu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vipKoutu", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.3.2", 13 | "axios": "^0.21.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cloudfunctions/xiangguan/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "openapi": [ 4 | ] 5 | } 6 | } -------------------------------------------------------------------------------- /cloudfunctions/xiangguan/imgList.js: -------------------------------------------------------------------------------- 1 | module.exports = ["cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2sl_IaoOO.eBjSZFLXXcxmXXa_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2FF0CagOI.eBjSszhXXbHvFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2oGXyacaK.eBjSspjXXXL.XXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2o1YGakWM.eBjSZFhXXbdWpXa_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2YGTJaoOO.eBjSZFLXXcxmXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2KgaMarVkpuFjSspcXXbSMVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2x8GLaq8lpuFjy0FpXXaGrpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB27guKarJkpuFjy1zcXXa5FFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB24ZOAaJXnpuFjSZFoXXXLcpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2dMeLaB8lpuFjy0FnXXcZyXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2JtGCaHBmpuFjSZFAXXaQ0pXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2vKuMarVkpuFjSspcXXbSMVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2oMGzaJFopuFjSZFHXXbSlXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2j4GCaNlmpuFjSZPfXXc9iXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2q6WHawJkpuFjSszcXXXfsFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB21U9CaOBnpuFjSZFzXXaSrpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2zn9zaItnpuFjSZFvXXbcTpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB23ZKMaC0jpuFjy0FlXXc0bpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2urGQawxlpuFjy0FoXXa.lXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2WNmKarJkpuFjy1zcXXa5FFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2qfmIaxXkpuFjy0FiXXbUfFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2n6WAaNhmpuFjSZFyXXcLdFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2XnaKarplpuFjSspiXXcdfFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2LMKKarJkpuFjy1zcXXa5FFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2eq4vaceJ.eBjy0FiXXXqapXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2n4nCaiGO.eBjSZFpXXb3tFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2dvnHaheK.eBjSZFlXXaywXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2pvTJaheK.eBjSZFuXXcT4FXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2brWQawxlpuFjy0FoXXa.lXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB243KJaB0kpuFjy1zdXXXuUVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2v39JaB0kpuFjy1zdXXXuUVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2JSuyaUdnpuFjSZPhXXbChpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2j8CIarXlpuFjSszfXXcSGXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB24IVwamKI.eBjy1zcXXXIOpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2KZ2FaX5N.eBjSZFKXXX_QVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2Zzq8XLTJXuFjSspeXXapipXa_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2qwLJalaM.eBjSZFMXXcypVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2alPLahaK.eBjSZFAXXczFXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2HA2Bajm2.eBjSZFtXXX56VXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2_94qab5K.eBjy0FfXXbApVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2s8Bsam1I.eBjy0FjXXabfXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2y8htab1K.eBjSszbXXcTHpXa_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2BjKHawJkpuFjSszcXXXfsFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2KzptaheJ.eBjy1zdXXXfmFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2Y7Rsam1I.eBjy0FjXXabfXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2FbpCal9J.eBjy0FoXXXyvpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2OTdsaa9I.eBjy0FeXXXqwFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2KCfHafSM.eBjSZFNXXbgYpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2.1qPaCJjpuFjy0FdXXXmoFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/O1CN01H3yiph260qaKwWZnv_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/O1CN01qW6wsv260qaGc4HXp_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/O1CN015RnPdH260qaP3D2NL_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2BugOoY0kpuFjy0FjXXcBbVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2TmaLaq8lpuFjy0FpXXaGrpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB25xeCaSFmpuFjSZFrXXayOXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2cwuLarRkpuFjSspmXXc.9XXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2fJWMarVkpuFjSspcXXbSMVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2OmqxaOlnpuFjSZFgXXbi7FXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2bMiLarRkpuFjSspmXXc.9XXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2BPSzaJFopuFjSZFHXXbSlXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/O1CN015SVYFT260qaNFelFL_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/O1CN01VVymyH260qaNNSRhR_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2edPFaXOP.eBjSZFHXXXQnpXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2IJdqab1J.eBjSszcXXbFzVXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2h8YHalyN.eBjSZFgXXXmGXXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2OHzHaduO.eBjSZFCXXXULFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB216DHamiK.eBjSZFDXXbxZVXa_!!2456947600.jpg_430x430q90.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2lKzIalyN.eBjSZFkXXb8YFXa_!!2456947600.jpg", "cloud://dev-4iov0.6465-dev-4iov0-1301148496/xiangguan/TB2Inhrag1I.eBjSszeXXc2hpXa_!!2456947600.jpg"] -------------------------------------------------------------------------------- /cloudfunctions/xiangguan/index.js: -------------------------------------------------------------------------------- 1 | // 云函数入口文件 2 | const cloud = require('wx-server-sdk') 3 | 4 | const photoList = require('./imgList') 5 | 6 | // 共享给人像抠图小程序用 7 | 8 | cloud.init() 9 | 10 | // 云函数入口函数 11 | exports.main = async (event, context) => { 12 | const wxContext = cloud.getWXContext() 13 | 14 | const photoIndex = event.index || Math.floor(Math.random() * photoList.length) 15 | 16 | const imgSrc = await getFileUrlByFileID(photoList[photoIndex]) 17 | 18 | return { 19 | event, 20 | result: { 21 | length: photoList.length, 22 | photoId: imgSrc, 23 | photoIndex, 24 | }, 25 | openid: wxContext.OPENID, 26 | appid: wxContext.APPID, 27 | unionid: wxContext.UNIONID, 28 | } 29 | } 30 | 31 | 32 | // 获取文件的临时访问url 33 | async function getFileUrlByFileID (fileID) { 34 | return (await cloud.getTempFileURL({ fileList: [fileID] })).fileList[0].tempFileURL 35 | } -------------------------------------------------------------------------------- /cloudfunctions/xiangguan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xiangguan", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "wx-server-sdk": "~2.6.3" 13 | } 14 | } -------------------------------------------------------------------------------- /miniprogram/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/.DS_Store -------------------------------------------------------------------------------- /miniprogram/app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | 5 | // 版本检查 6 | this.getNewVersion() 7 | // 初始化云开发 8 | wx.cloud.init({ resourceEnv: 'dev-4iov0', traceUser: true }) 9 | // 新用户入库 10 | this.addUser() 11 | }, 12 | 13 | // 新用户入库,并获取基本信息 14 | addUser() { 15 | wx.cloud.callFunction({ 16 | name: 'addUser', 17 | success: res => { 18 | this.globalData.openid = res.result.openid 19 | }, 20 | fail(err) { 21 | wx.showToast({ 22 | title: '请求失败,请重试', 23 | icon: 'none' 24 | }) 25 | } 26 | }) 27 | }, 28 | 29 | // 下载最新版本 30 | getNewVersion() { 31 | const updateManager = wx.getUpdateManager() 32 | 33 | updateManager.onCheckForUpdate(function (res) { 34 | // 请求完新版本信息的回调 35 | console.log('有新版本:', res.hasUpdate) 36 | }) 37 | 38 | updateManager.onUpdateReady(function () { 39 | updateManager.applyUpdate() 40 | }) 41 | }, 42 | 43 | // 初始化全局数据 44 | globalData: { 45 | openid: null, 46 | photoSizeList: [{ 47 | name: '一寸', 48 | px: '295×413 px', 49 | size: '25 × 35 mm', 50 | width: 295, 51 | height: 413, 52 | discription: '教师资格证、简历、会计职称考试、健康证等。' 53 | }, 54 | { 55 | name: '小一寸', 56 | px: '260×378 px', 57 | size: '22 × 32 mm', 58 | width: 260, 59 | height: 378, 60 | discription: '驾驶证、驾照、英语AB级等。' 61 | }, 62 | { 63 | name: '大一寸', 64 | px: '390×567 px', 65 | size: '33 × 48 mm', 66 | width: 390, 67 | height: 567, 68 | discription: '计算机等级、中国护照、英语四六级考试等。' 69 | }, 70 | { 71 | name: '二寸', 72 | px: '413×579 px', 73 | size: '35 × 49 mm', 74 | width: 413, 75 | height: 579, 76 | discription: '养老护理员、医疗、职业药师资格证等。' 77 | }, 78 | { 79 | name: '小二寸', 80 | px: '413×531 px', 81 | size: '33 × 45 mm', 82 | width: 413, 83 | height: 531, 84 | discription: '国考、国家公务员、护士资格证考试、主管护师等。' 85 | }, 86 | { 87 | name: '大二寸', 88 | px: '413×626 px', 89 | size: '35 × 53 mm', 90 | width: 413, 91 | height: 626, 92 | discription: '' 93 | }, 94 | { 95 | name: '三寸', 96 | px: '649×991 px', 97 | size: '55 × 84 mm', 98 | width: 413, 99 | height: 626, 100 | discription: '' 101 | }, 102 | { 103 | name: '四寸', 104 | px: '898×1205 px', 105 | size: '76 × 102 mm', 106 | width: 413, 107 | height: 626, 108 | discription: '' 109 | }, 110 | { 111 | name: '五寸', 112 | px: '1050×1499 px', 113 | size: '89 × 127 mm', 114 | width: 413, 115 | height: 626, 116 | discription: '' 117 | }, 118 | { 119 | name: '简历', 120 | px: '295×413 px', 121 | size: '25 × 35 mm', 122 | width: 295, 123 | height: 413, 124 | discription: '简历' 125 | }, 126 | { 127 | name: '健康证', 128 | px: '295×413 px', 129 | size: '25 × 35 mm', 130 | width: 295, 131 | height: 413, 132 | discription: '健康证' 133 | }, 134 | { 135 | name: '结婚证', 136 | px: '626x413 px', 137 | size: '53 × 35 mm', 138 | width: 626, 139 | height: 413, 140 | discription: '结婚证大二寸双人证件照。' 141 | }, 142 | { 143 | name: '一寸半身照', 144 | px: '295×413 px', 145 | size: '25 × 35 mm', 146 | width: 295, 147 | height: 413, 148 | discription: '' 149 | }, 150 | { 151 | name: '二寸半身照', 152 | px: '413×579 px', 153 | size: '35 × 49 mm', 154 | width: 413, 155 | height: 579, 156 | discription: '' 157 | }, 158 | { 159 | name: '教师资格证', 160 | px: '295×413 px', 161 | size: '25 × 35 mm', 162 | width: 295, 163 | height: 413, 164 | discription: '教师资格证' 165 | } 166 | ] 167 | } 168 | }) -------------------------------------------------------------------------------- /miniprogram/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/share/share", 5 | "pages/mein/mein", 6 | "pages/editPhoto/imageStyle/imageStyle", 7 | "pages/editPhoto/hair/hair", 8 | "pages/searchSize/searchSize", 9 | "pages/searchSize/searchView/searchView", 10 | "pages/searchSize/custom/custom", 11 | "pages/editPhoto/complete/complete", 12 | "pages/helpMake/helpMake", 13 | "pages/preEdit/preEdit", 14 | "pages/editPhoto/editPhotoPlus/editPhotoPlus", 15 | "pages/imgZip/imgZip", 16 | "pages/zipSuccess/zipSuccess", 17 | "pages/imgCompose/compose" 18 | ], 19 | "window": { 20 | "backgroundColor": "#fff", 21 | "backgroundTextStyle": "light", 22 | "navigationBarBackgroundColor": "#3E85EE", 23 | "navigationBarTitleText": "", 24 | "navigationBarTextStyle": "white" 25 | }, 26 | "sitemapLocation": "sitemap.json", 27 | "style": "v2", 28 | "tabBar": { 29 | "backgroundColor": "#fff", 30 | "color": "#333333", 31 | "selectedColor": "#3E85EE", 32 | "list": [ 33 | { 34 | "pagePath": "pages/index/index", 35 | "text": "首页", 36 | "iconPath": "./images/index.png", 37 | "selectedIconPath": "./images/index-active.png" 38 | }, 39 | { 40 | "pagePath": "pages/mein/mein", 41 | "text": "我的", 42 | "iconPath": "./images/me.png", 43 | "selectedIconPath": "./images/me-active.png" 44 | } 45 | ] 46 | }, 47 | "lazyCodeLoading": "requiredComponents" 48 | } -------------------------------------------------------------------------------- /miniprogram/app.miniapp.json: -------------------------------------------------------------------------------- 1 | { 2 | "adapteByMiniprogram": { 3 | "userName": "gh_82e5b8a1fccf" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /miniprogram/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | @import "colorui/main.wxss"; 3 | @import "colorui/icon.wxss"; 4 | .container { 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | box-sizing: border-box; 9 | } 10 | 11 | button { 12 | background: initial; 13 | } 14 | 15 | button:focus{ 16 | outline: 0; 17 | } 18 | 19 | button::after{ 20 | border: none; 21 | } 22 | 23 | 24 | page { 25 | /* background: linear-gradient(to bottom, rgba(0,191,243,.2), rgba(67,142,219,.2), rgba(255,0,0,.2)); */ 26 | background-color: #F5F5F5; 27 | display: flex; 28 | flex-direction: column; 29 | justify-content: flex-start; 30 | } 31 | -------------------------------------------------------------------------------- /miniprogram/components/color-picker/color-picker.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /miniprogram/components/color-picker/color-picker.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | R:{{pickerData.red}} G:{{pickerData.green}} B:{{pickerData.blue}} 17 | HEX:{{pickerData.hex}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /miniprogram/components/color-picker/color-picker.wxss: -------------------------------------------------------------------------------- 1 | .c { 2 | color: #5f27cd; 3 | } 4 | .bg { 5 | background: #5f27cd; 6 | } 7 | .t50 { 8 | top: 50%; 9 | left: 50%; 10 | transform: translate(-50%, -50%); 11 | } 12 | .tx50 { 13 | left: 50%; 14 | transform: translateX(-50%); 15 | } 16 | .ty50 { 17 | top: 50%; 18 | transform: translateY(-50%); 19 | } 20 | .cw { 21 | color: #fff; 22 | } 23 | .bw { 24 | background: #fff; 25 | } 26 | .fl { 27 | float: left; 28 | } 29 | .fr { 30 | float: right; 31 | } 32 | .fb { 33 | font-weight: bold; 34 | } 35 | .ib { 36 | display: inline-block !important; 37 | vertical-align: middle; 38 | } 39 | .tac { 40 | text-align: center; 41 | } 42 | .tal { 43 | text-align: left; 44 | } 45 | .tar { 46 | text-align: right; 47 | } 48 | .cp { 49 | cursor: pointer; 50 | } 51 | .none { 52 | display: none !important; 53 | } 54 | .hidden { 55 | visibility: hidden; 56 | opacity: 0; 57 | } 58 | .visible { 59 | visibility: visible; 60 | opacity: 1; 61 | } 62 | .mainSection { 63 | min-height: 100vh; 64 | } 65 | .row { 66 | display: flex; 67 | align-items: center; 68 | justify-content: center; 69 | } 70 | .col { 71 | display: flex; 72 | align-items: center; 73 | flex-direction: column; 74 | justify-content: center; 75 | } 76 | .ellipsis { 77 | overflow: hidden; 78 | text-overflow: ellipsis; 79 | white-space: nowrap; 80 | } 81 | .color-picker-container { 82 | width: 600rpx; 83 | } 84 | .color-picker-container .wrapper { 85 | margin: auto; 86 | } 87 | .color-picker-container .pick-area { 88 | display: flex; 89 | justify-content: space-between; 90 | } 91 | .color-picker-container .pick-area .color-picker-map { 92 | position: relative; 93 | width: 500rpx; 94 | height: 500rpx; 95 | } 96 | .color-picker-container .pick-area .color-picker-map .color-picker-map-item { 97 | position: absolute; 98 | top: 0rpx; 99 | left: 0rpx; 100 | width: 500rpx; 101 | height: 500rpx; 102 | border: 2rpx solid transparent; 103 | } 104 | .color-picker-container .pick-area .color-picker-map .color-picker-map-item.white { 105 | z-index: 10; 106 | } 107 | .color-picker-container .pick-area .color-picker-map .color-picker-map-item.black { 108 | z-index: 11; 109 | border: 2rpx solid #666; 110 | } 111 | .color-picker-container .pick-area .color-picker-map .picker { 112 | position: absolute; 113 | z-index: 12; 114 | width: 20rpx; 115 | height: 20rpx; 116 | border: 2rpx solid #666; 117 | background: #fff; 118 | border-radius: 50%; 119 | } 120 | .color-picker-container .pick-area .color-picker-bar { 121 | position: relative; 122 | width: 60rpx; 123 | height: 500rpx; 124 | border: 2rpx solid #666; 125 | background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); 126 | } 127 | .color-picker-container .pick-area .color-picker-bar .picker { 128 | position: absolute; 129 | z-index: 10; 130 | width: 110%; 131 | height: 10rpx; 132 | left: 50%; 133 | transform: translateX(-50%); 134 | background: #fff; 135 | border: 2rpx solid #666; 136 | } 137 | .color-picker-container .data-area { 138 | margin-top: 20rpx; 139 | font-size: 26rpx; 140 | display: flex; 141 | justify-content: space-between; 142 | } 143 | /*# sourceMappingURL=./color-picker.wxss.map */ -------------------------------------------------------------------------------- /miniprogram/components/color-picker/color-picker.wxss.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["e:/MyProject/some-components/miniprogram/style/style.less","color-picker.less"],"names":[],"mappings":"AAEA;EACI,cAAA;;AAGJ;EACI,mBAAA;;AAyCJ;EACI,QAAA;EACA,SAAA;EACA,WAAW,qBAAX;;AAGJ;EACI,SAAA;EACA,WAAW,gBAAX;;AAGJ;EACI,QAAA;EACA,WAAW,gBAAX;;AAGJ;EACI,WAAA;;AAGJ;EACI,gBAAA;;AAGJ;EACI,WAAA;;AAGJ;EACI,YAAA;;AAGJ;EACI,iBAAA;;AAGJ;EACI,qBAAA;EACA,sBAAA;;AAGJ;EACI,kBAAA;;AAGJ;EACI,gBAAA;;AAGJ;EACI,iBAAA;;AAGJ;EACI,eAAA;;AAGJ;EACI,wBAAA;;AAGJ;EACI,kBAAA;EACA,UAAA;;AAGJ;EACI,mBAAA;EACA,UAAA;;AAGJ;EACI,iBAAA;;AAGJ;EACI,aAAA;EACA,mBAAA;EACA,uBAAA;;AAGJ;EACI,aAAA;EACA,mBAAA;EACA,sBAAA;EACA,uBAAA;;AAGJ;EACI,gBAAA;EACA,uBAAA;EACA,mBAAA;;ACzIJ;EACE,aAAA;;AADF,uBAGE;EACE,YAAA;;AAJJ,uBAOE;EACE,aAAA;EACA,8BAAA;;AATJ,uBAOE,WAIE;EACE,kBAAA;EACA,aAAA;EACA,cAAA;;AAdN,uBAOE,WAIE,kBAKE;EACE,kBAAA;EACA,SAAA;EACA,UAAA;EACA,aAAA;EACA,cAAA;EACA,8BAAA;;AAEA,uBAjBN,WAIE,kBAKE,uBAQG;EACC,WAAA;;AAGF,uBArBN,WAIE,kBAKE,uBAYG;EACC,WAAA;EACA,uBAAA;;AA9BV,uBAOE,WAIE,kBAuBE;EACE,kBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;ED4BJ,gBAAA;EA9BA,kBAAA;;ACrCJ,uBAOE,WAsCE;EACE,kBAAA;EACA,YAAA;EACA,cAAA;EACA,uBAAA;EACA,YAAY,uHAAZ;;AAlDN,uBAOE,WAsCE,kBAOE;EACE,kBAAA;EACA,WAAA;EACA,WAAA;EACA,aAAA;EDHJ,SAAA;EACA,WAAW,gBAAX;EAaA,gBAAA;ECRI,uBAAA;;AA3DR,uBAgEE;EACE,iBAAA;EACA,gBAAA;EACA,aAAA;EACA,8BAAA"} -------------------------------------------------------------------------------- /miniprogram/components/pageScrollMessage/pageScrollMessage.js: -------------------------------------------------------------------------------- 1 | // components/pageScrollMessage.ts 2 | Component({ 3 | /** 4 | * 组件的属性列表 5 | */ 6 | properties: { 7 | 8 | }, 9 | 10 | /** 11 | * 组件的初始数据 12 | */ 13 | data: { 14 | 15 | }, 16 | 17 | /** 18 | * 组件的方法列表 19 | */ 20 | methods: { 21 | 22 | }, 23 | lifetimes: { 24 | attached: function () { 25 | this.animate('#text', [ 26 | { transform: 'translateX(100%)' }, 27 | { transform: 'translateX(-100%)' } 28 | ], 5000) 29 | } 30 | } 31 | }) 32 | 33 | -------------------------------------------------------------------------------- /miniprogram/components/pageScrollMessage/pageScrollMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /miniprogram/components/pageScrollMessage/pageScrollMessage.wxml: -------------------------------------------------------------------------------- 1 | 2 | 制作免冠照片/证件照所上传及生成的图片资源最多保存两小时将被删除,请及时保存结果! 3 | 4 | -------------------------------------------------------------------------------- /miniprogram/components/pageScrollMessage/pageScrollMessage.wxss: -------------------------------------------------------------------------------- 1 | /* components/pageScrollMessage.wxss */ 2 | .container { 3 | width: 100%; 4 | height: 40rpx; 5 | background-color: #DBEDFC; 6 | 7 | 8 | } 9 | .container .msg { 10 | display: block; 11 | width: max-content; 12 | line-height: 40rpx; 13 | font-size: 24rpx; 14 | color: #ee8b8d; 15 | position: relative; 16 | animation: 20s identifier linear infinite normal; 17 | -webkit-animation: 20s identifier linear infinite normal; 18 | word-break: keep-all; 19 | white-space: nowrap; 20 | } 21 | 22 | @keyframes identifier { 23 | 0% { 24 | transform: translateX(100%); 25 | } 26 | 100% { 27 | transform: translateX(-100%); 28 | } 29 | } 30 | 31 | @-webkit-keyframes identifier { 32 | 0% { 33 | -webkit-transform: translateX(100%); 34 | } 35 | 100% { 36 | -webkit-transform: translateX(-100%); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /miniprogram/images/WechatIMG199.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/WechatIMG199.jpeg -------------------------------------------------------------------------------- /miniprogram/images/index-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/index-active.png -------------------------------------------------------------------------------- /miniprogram/images/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/index.png -------------------------------------------------------------------------------- /miniprogram/images/me-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/me-active.png -------------------------------------------------------------------------------- /miniprogram/images/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/me.png -------------------------------------------------------------------------------- /miniprogram/images/shareShow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/shareShow.jpg -------------------------------------------------------------------------------- /miniprogram/images/take.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/images/take.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/complete/complete.js: -------------------------------------------------------------------------------- 1 | 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | msg: '', 9 | tempFilePath: '', 10 | url: '' 11 | }, 12 | 13 | // 继续制作,返回到选择照片页面 14 | contineu () { 15 | wx.navigateBack() 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面加载 20 | */ 21 | onLoad: function (options) { 22 | this.setData({ 23 | msg: options.msg, 24 | tempFilePath: options.tempFilePath, 25 | url: options.url 26 | }) 27 | 28 | wx.showToast({ title: '制作成功', }) 29 | }, 30 | 31 | // 预览制作出来的照片 32 | preView () { 33 | wx.previewImage({ 34 | urls: [this.data.url], 35 | current: this.data.url 36 | }) 37 | }, 38 | 39 | /** 40 | * 用户点击右上角分享 41 | */ 42 | onShareAppMessage: function (res) { 43 | return { 44 | title: '证件照、免冠照、一寸照片、二寸照片、自定义尺寸、证件照换背景,免费生成、下载。', 45 | path: '/pages/index/index', 46 | imageUrl: this.data.tempFilePath 47 | } 48 | } 49 | }) -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/complete/complete.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/complete/complete.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{msg}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 请在 手机相册 查看下载结果 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/complete/complete.wxss: -------------------------------------------------------------------------------- 1 | .msg { 2 | font-size: 40rpx; 3 | text-align: center; 4 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/editPhotoPlus.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "color-picker": "/components/color-picker/color-picker", 4 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 5 | }, 6 | "navigationBarTitleText": "生成免冠照", 7 | "disableScroll": true 8 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/editPhotoPlus.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 先选右侧色系 21 | \n\n 22 | 后选左侧色值 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 41 | 42 | 43 | 53 | 54 | 55 | 65 | 66 | 67 | 68 | 移动人像可调整位置\n点击下一步 69 | 双指可调整人像大小\n点击下一步 70 | 点击右侧换装换发型\n点击下一步 71 | 72 | 73 | 74 | 我知道了 75 | 76 | 77 | 78 | {{videoLoaded ? '看视频 + 1 次' : ''}}\n\n\n\n ← 点击使用精细抠图 79 | 80 | ← 更多次数请联系作者 81 | 82 | 83 | 84 | 85 | 86 | 87 | 换装 88 | 89 | 90 | 91 | 头发 92 | 93 | 94 | 102 | 103 | 104 | 105 | 看视频 106 | 107 | 108 | 109 | 剩{{vipCount}}次 110 | 111 | 112 | 113 | 作者 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 自定义 124 | 125 | 126 | 127 | 128 | 下载次数已用完。 129 | 点击使用后即可下载 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/editPhotoPlus.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/editPhoto/editPhoto.wxss */ 2 | page { 3 | background: linear-gradient(to bottom, rgba(0,191,243,.2), rgba(67,142,219,.2), rgba(255,0,0,.2)); 4 | } 5 | .container { 6 | width: 100%; 7 | overflow: hidden; 8 | } 9 | 10 | .tabs { 11 | width: 100%; 12 | height: 100rpx; 13 | background-color: rgba(255,255,255,.2); 14 | display: flex; 15 | flex-direction: row; 16 | } 17 | 18 | .tabs .tab { 19 | width: 50%; 20 | height: 100%; 21 | text-align: center; 22 | } 23 | .tab.tab-active { 24 | color: #3E85EE; 25 | border-bottom: 3px solid #3E85EE; 26 | } 27 | .tabs .tab .name { 28 | font-size: 32rpx; 29 | line-height: 1.5; 30 | } 31 | .tabs .tab .description { 32 | font-size: 24rpx; 33 | } 34 | 35 | 36 | /* 颜色选择 */ 37 | .container .colors { 38 | width: 100%; 39 | display: flex; 40 | flex-direction: row; 41 | justify-content: space-around ; 42 | margin-top: 20rpx; 43 | } 44 | .container .colors .color { 45 | width: 15%; 46 | height: 0; 47 | padding-top: 15%; 48 | border: 2px solid #000; 49 | border-radius: 50%; 50 | overflow: hidden; 51 | } 52 | .container .colors .color.active { 53 | border-color: #3E85EE; 54 | } 55 | .container .colors .color.transparent { 56 | background-color: transparent; 57 | position: relative; 58 | } 59 | .container .colors .color.transparent::before { 60 | content: '透明'; 61 | position: absolute; 62 | left: 0; 63 | top: 50%; 64 | width: 100%; 65 | text-align: center; 66 | color: #333; 67 | line-height: 1; 68 | height: 1em; 69 | margin-top: -0.5em; 70 | font-size: 14px; 71 | } 72 | .blue { 73 | background-color: rgb(67,142,219); 74 | } 75 | .blue2 { 76 | background-color: rgb(0,191,243); 77 | } 78 | .red { 79 | background-color: red; 80 | } 81 | .white { 82 | background-color: white; 83 | } 84 | .container .colors .color.custom { 85 | padding-top: 0; 86 | height: max-content; 87 | } 88 | .container .colors .color.custom .text { 89 | width: 100%; 90 | padding-top: 50%; 91 | height: 0; 92 | text-align: center; 93 | } 94 | .container .colors .color.custom .text text { 95 | transform: translateY(-100%); 96 | display: block; 97 | font-size: 14px; 98 | } 99 | .container .colors .color .custom-bg { 100 | width: 100%; 101 | height: 0; 102 | padding-top: 50%; 103 | } 104 | 105 | /* color picker */ 106 | .color-picker-view { 107 | position: relative; 108 | margin-top: 10px; 109 | width: 100%; 110 | height: 0; 111 | overflow: visible; 112 | z-index: 99; 113 | } 114 | .color-picker-view .color-picker-content { 115 | position: absolute; 116 | left: 0; 117 | top: 0; 118 | width: 100%; 119 | background-color: #fff; 120 | } 121 | .color-picker-view .color-picker-content.hide { 122 | display: none; 123 | } 124 | .color-picker-view .color-picker-content .tips { 125 | position: absolute; 126 | width: 4em; 127 | right: 5px; 128 | top: 30px; 129 | } 130 | .color-picker-view .color-picker-content .current-color { 131 | position: absolute; 132 | width: 4em; 133 | height: 4em; 134 | right: 5px; 135 | bottom: 80px; 136 | } 137 | .color-picker-view .color-picker-content button { 138 | position: absolute; 139 | right: 5px; 140 | bottom: 30px; 141 | } 142 | 143 | /* 尺寸 */ 144 | .container .size-picker { 145 | box-sizing: border-box; 146 | width: 100%; 147 | padding: 0 5px; 148 | } 149 | 150 | /* canvas */ 151 | .container .pthto-edit-content { 152 | padding: 5px; 153 | width: 100%; 154 | box-sizing: border-box; 155 | } 156 | .container .pthto-edit-content .canvas-view { 157 | position: relative; 158 | width: 100%; 159 | height: 300px; 160 | margin: 0 auto; 161 | /* border: 10rpx solid #fff; */ 162 | box-sizing: content-box; 163 | overflow: hidden; 164 | /* box-shadow: 0 5rpx 5rpx 5rpx rgba(255,255,255,.5) inset; */ 165 | overflow: hidden; 166 | } 167 | /* .container .pthto-edit-content .canvas-view #myCanvas { 168 | width:100%; 169 | height:100%; 170 | } */ 171 | .container .pthto-edit-content .canvas-view .people-photo { 172 | position: absolute; 173 | /* left: 0; 174 | bottom: 0; */ 175 | width: 100%; 176 | transform-origin: center center; 177 | } 178 | 179 | .container .pthto-edit-content .canvas-view .guide { 180 | position: absolute; 181 | z-index: 9; 182 | left: 0; 183 | top: 0; 184 | right: 0; 185 | bottom: 0; 186 | background-color: rgba(0,0,0,.5); 187 | /* pointer-events: none; */ 188 | } 189 | 190 | .container .pthto-edit-content .canvas-view .guide .guide-know { 191 | position: absolute; 192 | width: 6em; 193 | line-height: 2em; 194 | height: 2em; 195 | left: 0; 196 | top: 0; 197 | right: 0; 198 | bottom: 0; 199 | margin: auto; 200 | border-radius: 5px; 201 | color: #fff; 202 | border: 1px solid #fff; 203 | text-align: center; 204 | } 205 | 206 | .container .pthto-edit-content .canvas-view .guide text { 207 | position: absolute; 208 | top: 10rpx; 209 | left: 0rpx; 210 | right: 0rpx; 211 | /* bottom: 0; */ 212 | margin: auto; 213 | color: #fff; 214 | text-align: center; 215 | font-size: 40rpx; 216 | } 217 | .container .pthto-edit-content .canvas-view .guide image { 218 | position: absolute; 219 | width: 200rpx; 220 | left: 0; 221 | top: 0; 222 | right: 0; 223 | bottom: 0; 224 | margin: auto; 225 | } 226 | 227 | .gesture { 228 | width: 100%; 229 | position: relative; 230 | overflow: visible; 231 | z-index: 2; 232 | } 233 | 234 | .gesture .right { 235 | right: 10rpx; 236 | } 237 | .gesture .left { 238 | left: 10rpx; 239 | } 240 | 241 | .gesture .change-clothes { 242 | position: absolute; 243 | bottom: 20rpx; 244 | width: 100rpx; 245 | height: 100rpx; 246 | text-align: center; 247 | } 248 | .gesture .change-hair { 249 | position: absolute; 250 | bottom: 250rpx; 251 | width: 100rpx; 252 | height: 100rpx; 253 | text-align: center; 254 | } 255 | .gesture .crop-image { 256 | position: absolute; 257 | top: -250rpx; 258 | width: 100rpx; 259 | height: 100rpx; 260 | text-align: center; 261 | } 262 | .gesture .look-videw { 263 | position: absolute; 264 | top: -250rpx; 265 | width: 100rpx; 266 | height: 100rpx; 267 | text-align: center; 268 | } 269 | .gesture image { 270 | width: 100%; 271 | } 272 | .gesture text { 273 | font-size: 30rpx; 274 | /* color: #06ae56; */ 275 | } 276 | 277 | .result-canvas-view { 278 | max-height: 1px; 279 | overflow: hidden; 280 | } 281 | 282 | .download-view { 283 | width: 100%; 284 | box-sizing: border-box; 285 | padding: 0 20rpx; 286 | margin-top: 50rpx; 287 | } 288 | 289 | .download-view .no-count { 290 | padding: 0 10rpx; 291 | display: flex; 292 | width: 100%; 293 | flex-direction: row; 294 | justify-content: space-around; 295 | } 296 | 297 | .download-view .no-count > button, .download-view .no-count navigator { 298 | width: 45%; 299 | margin: 0; 300 | margin-bottom: 0; 301 | box-sizing: border-box; 302 | } 303 | 304 | .download-view .no-count navigator button { 305 | box-sizing: border-box; 306 | width: 100%; 307 | margin-bottom: 0; 308 | } 309 | 310 | .download-view button { 311 | margin-bottom: 20rpx; 312 | box-shadow: 0 0 10rpx 5rpx rgba(67,142,219,.25); 313 | background-color: #3E85EE !important; 314 | color: #fff !important; 315 | font-weight: normal; 316 | height: 90rpx; 317 | padding: 0; 318 | line-height: 90rpx; 319 | width: 100% !important; 320 | border-radius: 45rpx; 321 | } 322 | 323 | 324 | .style-text { 325 | color: #333333; 326 | font-size: 24rpx !important; 327 | } 328 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/hex-rgb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const hexCharacters = 'a-f\\d'; 4 | const match3or4Hex = `#?[${hexCharacters}]{3}[${hexCharacters}]?`; 5 | const match6or8Hex = `#?[${hexCharacters}]{6}([${hexCharacters}]{2})?`; 6 | const nonHexChars = new RegExp(`[^#${hexCharacters}]`, 'gi'); 7 | const validHexSize = new RegExp(`^${match3or4Hex}$|^${match6or8Hex}$`, 'i'); 8 | 9 | module.exports = (hex, options = {}) => { 10 | if (typeof hex !== 'string' || nonHexChars.test(hex) || !validHexSize.test(hex)) { 11 | throw new TypeError('Expected a valid hex string'); 12 | } 13 | 14 | hex = hex.replace(/^#/, ''); 15 | let alpha = 1; 16 | 17 | if (hex.length === 8) { 18 | alpha = Number.parseInt(hex.slice(6, 8), 16) / 255; 19 | hex = hex.slice(0, 6); 20 | } 21 | 22 | if (hex.length === 4) { 23 | alpha = Number.parseInt(hex.slice(3, 4).repeat(2), 16) / 255; 24 | hex = hex.slice(0, 3); 25 | } 26 | 27 | if (hex.length === 3) { 28 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 29 | } 30 | 31 | const number = Number.parseInt(hex, 16); 32 | const red = number >> 16; 33 | const green = (number >> 8) & 255; 34 | const blue = number & 255; 35 | 36 | if (options.format === 'array') { 37 | return [red, green, blue, alpha]; 38 | } 39 | 40 | if (options.format === 'css') { 41 | const alphaString = alpha === 1 ? '' : ` / ${Number((alpha * 100).toFixed(2))}%`; 42 | return `rgb(${red} ${green} ${blue}${alphaString})`; 43 | } 44 | 45 | return {red, green, blue, alpha}; 46 | }; 47 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/imgSecCheck.js: -------------------------------------------------------------------------------- 1 | module.exports = function imgSecCheck (tempFilePath) { 2 | wx.showLoading({ title: '图片安全校验', }) 3 | // 获取图片信息 4 | return wx.getImageInfo({ src: tempFilePath, }) 5 | // 图片安全校验 6 | .then(res => { 7 | return wx.cloud.callFunction({ 8 | name: 'imgSecCheck', 9 | data: { 10 | width: res.width, 11 | height: res.height, 12 | type: res.type, 13 | // 上传图片到临时CDN,返回图片地址 14 | filePath: wx.cloud.CDN({ 15 | type: 'filePath', 16 | filePath: res.path, 17 | }) 18 | }, 19 | }) 20 | }) 21 | // 图片安全检测结果处理 22 | .then((res) => { 23 | if (res.result.errCode === 0) { 24 | // Object.assign(pageData, { 25 | // originImgPath: res.result.originImgPath, 26 | // originImgType: res.result.originImgType, 27 | // }) 28 | // this.baiduKoutu({ 29 | // fileID: res.result.fileId, 30 | // filePath: res.result.filePath 31 | // }) 32 | return Promise.resolve(res.result) 33 | } else if (res.result.errCode === 87014) { 34 | wx.showToast({ title: '内容可能潜在风险,请重新选择', icon: 'none' }) 35 | } else if (res.result.errCode === -604102) { 36 | wx.showToast({ title: '超时,再试一下。或换个图试试', icon: 'none', duration: 3000 }) 37 | } else { 38 | wx.showToast({ title: '又是啥问题呀,请重试' + res.result.errCode, icon: 'none' }) 39 | } 40 | }) 41 | // 错误处理 42 | .catch(console.error) 43 | } 44 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/editPhotoPlus/touch.js: -------------------------------------------------------------------------------- 1 | let canOnePointMove = false 2 | let onePoint = { 3 | x: 0, 4 | y: 0 5 | } 6 | // 双指 7 | let twoPoint = { 8 | x1: 0, 9 | y1: 0, 10 | x2: 0, 11 | y2: 0 12 | } 13 | export default { 14 | touchstart: function (e) { 15 | if (e.touches.length < 2) { 16 | canOnePointMove = true 17 | onePoint.x = e.touches[0].pageX * 2 18 | onePoint.y = e.touches[0].pageY * 2 19 | } else { 20 | twoPoint.x1 = e.touches[0].pageX * 2 21 | twoPoint.y1 = e.touches[0].pageY * 2 22 | twoPoint.x2 = e.touches[1].pageX * 2 23 | twoPoint.y2 = e.touches[1].pageY * 2 24 | } 25 | }, 26 | touchmove: function (e, oldData) { 27 | if (e.touches.length < 2 && canOnePointMove) { 28 | const onePointDiffX = e.touches[0].pageX * 2 - onePoint.x 29 | const onePointDiffY = e.touches[0].pageY * 2 - onePoint.y 30 | const imgSetData = { 31 | msg: '单点移动', 32 | left: oldData.left + onePointDiffX / 2, 33 | top: oldData.top + onePointDiffY / 2 34 | } 35 | onePoint.x = e.touches[0].pageX * 2 36 | onePoint.y = e.touches[0].pageY * 2 37 | return imgSetData 38 | } else if (e.touches.length > 1) { 39 | const preTwoPoint = JSON.parse(JSON.stringify(twoPoint)) 40 | twoPoint.x1 = e.touches[0].pageX * 2 41 | twoPoint.y1 = e.touches[0].pageY * 2 42 | twoPoint.x2 = e.touches[1].pageX * 2 43 | twoPoint.y2 = e.touches[1].pageY * 2 44 | // 计算角度,旋转(优先) 45 | const perAngle = Math.atan((preTwoPoint.y1 - preTwoPoint.y2) / (preTwoPoint.x1 - preTwoPoint.x2)) * 180 / Math.PI 46 | const curAngle = Math.atan((twoPoint.y1 - twoPoint.y2) / (twoPoint.x1 - twoPoint.x2)) * 180 / Math.PI 47 | if (Math.abs(perAngle - curAngle) > 1) { 48 | // 旋转 49 | } else { 50 | // 计算距离,缩放 51 | var preDistance = Math.sqrt(Math.pow((preTwoPoint.x1 - preTwoPoint.x2), 2) + Math.pow((preTwoPoint.y1 - preTwoPoint.y2), 2)) 52 | var curDistance = Math.sqrt(Math.pow((twoPoint.x1 - twoPoint.x2), 2) + Math.pow((twoPoint.y1 - twoPoint.y2), 2)) 53 | const imgSetData = { 54 | msg: '缩放', 55 | scale: Math.max(oldData.scale + (curDistance - preDistance) * 0.005, 0.5) 56 | } 57 | return imgSetData 58 | } 59 | } 60 | }, 61 | touchend: function (e) { 62 | canOnePointMove = false 63 | }, 64 | } 65 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/hair/hair.js: -------------------------------------------------------------------------------- 1 | // miniprogram/pages/editPhoto/imageStyle/imageStyle.js 2 | // 在页面中定义激励视频广告 3 | let videoAd = null 4 | // 在页面中定义插屏广告 5 | let interstitialAd = null 6 | let imgUrl = '' 7 | const allHair = {} 8 | Page({ 9 | 10 | /** 11 | * 页面的初始数据 12 | */ 13 | data: { 14 | TabCur: 0, 15 | videoLoaded: false, 16 | imgList: { 17 | nan: [], 18 | nv: [] 19 | } 20 | }, 21 | 22 | // 切换男女 23 | tabSelect(e) { 24 | this.setData({ 25 | TabCur: e.currentTarget.dataset.id, 26 | scrollLeft: (e.currentTarget.dataset.id - 1) * 60 27 | }) 28 | }, 29 | 30 | // 选择发型,开始看视频 31 | selectImg (e) { 32 | imgUrl = e.currentTarget.dataset.url 33 | // 用户触发广告后,显示激励视频广告 34 | if (videoAd) { 35 | videoAd.show().catch(() => { 36 | // 失败重试 37 | videoAd.load() 38 | .then(() => videoAd.show()) 39 | .catch(err => { 40 | console.log('激励视频 广告显示失败') 41 | }) 42 | }) 43 | } else { 44 | back() 45 | } 46 | }, 47 | 48 | // 返回页面,传递图片地址 49 | back () { 50 | const eventChannel = this.getOpenerEventChannel() 51 | eventChannel.emit('selectHair', {imgUrl}); 52 | wx.navigateBack({}) 53 | }, 54 | 55 | // 获取发型列表 56 | async getData () { 57 | wx.showLoading({ title: '稍等片刻...', }) 58 | const { result } = await wx.cloud.callFunction({ 59 | name: 'getHairs', 60 | }) 61 | Object.assign(allHair, result.hairs) 62 | this.setData({ 63 | imgList: { 64 | nan: allHair.nan.slice(0, 6), 65 | nv: allHair.nv.slice(0, 6), 66 | } 67 | }) 68 | wx.hideLoading() 69 | }, 70 | 71 | /** 72 | * 生命周期函数--监听页面加载 73 | */ 74 | onLoad: function (options) { 75 | wx.setNavigationBarTitle({ title: '免冠照发型' }) 76 | this.getData() 77 | 78 | // 在页面onLoad回调事件中创建激励视频广告实例 79 | if (wx.createRewardedVideoAd) { 80 | videoAd = wx.createRewardedVideoAd({ 81 | adUnitId: 'adunit-240d1c7fb731d343' 82 | }) 83 | videoAd.onLoad(() => { 84 | this.setData({ 85 | videoLoaded: true 86 | }) 87 | }) 88 | videoAd.onError((err) => { 89 | this.setData({ 90 | videoLoaded: false 91 | }) 92 | }) 93 | videoAd.onClose((res) => { 94 | console.log(res) 95 | if (res && res.isEnded) { 96 | if(imgUrl) this.back() 97 | } else { 98 | wx.showToast({ 99 | title: '看完才可以使用哦', 100 | icon: 'none' 101 | }) 102 | } 103 | }) 104 | } 105 | 106 | // 在页面onLoad回调事件中创建插屏广告实例 107 | if (wx.createInterstitialAd) { 108 | interstitialAd = wx.createInterstitialAd({ 109 | adUnitId: 'adunit-71fe77c8c4d0e3ca' 110 | }) 111 | interstitialAd.onLoad(() => {}) 112 | interstitialAd.onError((err) => {}) 113 | interstitialAd.onClose(() => {}) 114 | } 115 | }, 116 | 117 | /** 118 | * 生命周期函数--监听页面初次渲染完成 119 | */ 120 | onReady: function () { 121 | 122 | }, 123 | 124 | /** 125 | * 生命周期函数--监听页面显示 126 | */ 127 | onShow: function () { 128 | // 在适合的场景显示插屏广告 129 | if (interstitialAd) { 130 | interstitialAd.show().catch((err) => { 131 | console.error(err) 132 | }) 133 | } 134 | }, 135 | 136 | // 触底加载 137 | onReachBottom() { 138 | const key = ['nv', 'nan'][this.data.TabCur] 139 | if (this.data.imgList[key].length === allHair[key].length) return 140 | this.setData({ 141 | imgList: { 142 | ...this.data.imgList, 143 | [key]: allHair[key].slice(0, this.data.imgList[key].length + 6) 144 | } 145 | }) 146 | } 147 | }) -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/hair/hair.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/hair/hair.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{item}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 看视频后使用 16 | 17 | 18 | 19 | 20 | 21 | 看视频后使用 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/hair/hair.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/editPhoto/imageStyle/imageStyle.wxss */ 2 | .scroll-height { 3 | padding: 10rpx; 4 | overflow: auto; 5 | -webkit-overflow-scrolling: touch; 6 | } 7 | 8 | .scroll-height .img-list { 9 | display: flex; 10 | flex-direction: row; 11 | flex-wrap: wrap; 12 | justify-content: space-between; 13 | } 14 | 15 | .scroll-height .img-view { 16 | width: 49%; 17 | padding: 24.5% 0; 18 | box-sizing: border-box; 19 | background-color: rgba(255, 255, 255, .5); 20 | margin-bottom: 2%; 21 | position: relative; 22 | /* background-size: 90%; 23 | background-repeat: no-repeat; 24 | background-position: center; */ 25 | /* background-origin: center; */ 26 | } 27 | .scroll-height .img-view image { 28 | position: absolute; 29 | width: 90%; 30 | height: 90%; 31 | left: 0; 32 | top: 0; 33 | right: 0; 34 | bottom: 0; 35 | margin: auto; 36 | } 37 | 38 | .scroll-height .img-view .msg { 39 | position: absolute; 40 | background-color: rgba(255, 255, 255, .8); 41 | line-height: 2; 42 | height: 2em; 43 | width: 100%; 44 | left: 0; 45 | right: 0; 46 | bottom: 0; 47 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/imageStyle/imageStyle.js: -------------------------------------------------------------------------------- 1 | // miniprogram/pages/editPhoto/imageStyle/imageStyle.js 2 | // 在页面中定义激励视频广告 3 | let videoAd = null 4 | // 在页面中定义插屏广告 5 | let interstitialAd = null 6 | let imgUrl = '' 7 | const allClothes = {} 8 | Page({ 9 | 10 | /** 11 | * 页面的初始数据 12 | */ 13 | data: { 14 | TabCur: 0, 15 | videoLoaded: false, 16 | imgList: { 17 | nan: [], 18 | nv: [], 19 | other: [] 20 | } 21 | }, 22 | 23 | tabSelect(e) { 24 | this.setData({ 25 | TabCur: e.currentTarget.dataset.id, 26 | scrollLeft: (e.currentTarget.dataset.id-1)*60 27 | }) 28 | }, 29 | 30 | selectImg (e) { 31 | imgUrl = e.currentTarget.dataset.url 32 | 33 | // 用户触发广告后,显示激励视频广告 34 | if (videoAd) { 35 | videoAd.show().catch(() => { 36 | // 失败重试 37 | videoAd.load() 38 | .then(() => videoAd.show()) 39 | .catch(err => { 40 | console.log('激励视频 广告显示失败') 41 | }) 42 | }) 43 | } else { 44 | back() 45 | } 46 | 47 | }, 48 | 49 | back () { 50 | const eventChannel = this.getOpenerEventChannel() 51 | eventChannel.emit('selectClothes', {imgUrl}); 52 | wx.navigateBack({}) 53 | }, 54 | 55 | async getData () { 56 | wx.showLoading({ 57 | title: '稍等片刻...', 58 | }) 59 | const { result } = await wx.cloud.callFunction({ 60 | name: 'getClothes', 61 | }) 62 | Object.assign(allClothes, result.clothes) 63 | this.setData({ 64 | imgList: { 65 | nan: allClothes.nan.slice(0, 6), 66 | nv: allClothes.nv.slice(0, 6), 67 | other: allClothes.other.slice(0, 6) 68 | } 69 | }) 70 | wx.hideLoading() 71 | }, 72 | 73 | /** 74 | * 生命周期函数--监听页面加载 75 | */ 76 | onLoad: function (options) { 77 | wx.setNavigationBarTitle({ title: '免冠照/证件照换装' }) 78 | this.getData() 79 | 80 | // 在页面onLoad回调事件中创建激励视频广告实例 81 | if (wx.createRewardedVideoAd) { 82 | videoAd = wx.createRewardedVideoAd({ 83 | adUnitId: 'adunit-240d1c7fb731d343' 84 | }) 85 | videoAd.onLoad(() => { 86 | this.setData({ 87 | videoLoaded: true 88 | }) 89 | }) 90 | videoAd.onError((err) => { 91 | this.setData({ 92 | videoLoaded: false 93 | }) 94 | }) 95 | videoAd.onClose((res) => { 96 | console.log(res) 97 | if (res && res.isEnded) { 98 | if(imgUrl) this.back() 99 | } else { 100 | wx.showToast({ 101 | title: '看完才可以使用哦', 102 | icon: 'none' 103 | }) 104 | } 105 | }) 106 | } 107 | 108 | // 在页面onLoad回调事件中创建插屏广告实例 109 | if (wx.createInterstitialAd) { 110 | interstitialAd = wx.createInterstitialAd({ 111 | adUnitId: 'adunit-ef203d1fea23c207' 112 | }) 113 | interstitialAd.onLoad(() => {}) 114 | interstitialAd.onError((err) => {}) 115 | interstitialAd.onClose(() => {}) 116 | } 117 | }, 118 | 119 | /** 120 | * 生命周期函数--监听页面初次渲染完成 121 | */ 122 | onReady: function () { 123 | 124 | }, 125 | 126 | /** 127 | * 生命周期函数--监听页面显示 128 | */ 129 | onShow: function () { 130 | 131 | // 在适合的场景显示插屏广告 132 | if (interstitialAd) { 133 | interstitialAd.show().catch((err) => { 134 | console.error(err) 135 | }) 136 | } 137 | }, 138 | 139 | // 触底加载 140 | onReachBottom() { 141 | const key = ['nv', 'nan', 'other'][this.data.TabCur] 142 | if (this.data.imgList[key].length === allClothes[key].length) return 143 | this.setData({ 144 | imgList: { 145 | ...this.data.imgList, 146 | [key]: allClothes[key].slice(0, this.data.imgList[key].length + 6) 147 | } 148 | }) 149 | } 150 | 151 | }) -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/imageStyle/imageStyle.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/imageStyle/imageStyle.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{item}} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 看视频后使用 17 | 18 | 19 | 20 | 21 | 22 | 看视频后使用 23 | 24 | 25 | 26 | 27 | 28 | 看视频后使用 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/imageStyle/imageStyle.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/editPhoto/imageStyle/imageStyle.wxss */ 2 | .scroll-height { 3 | padding: 10rpx; 4 | overflow: auto; 5 | -webkit-overflow-scrolling: touch; 6 | } 7 | 8 | .scroll-height .img-list { 9 | display: flex; 10 | flex-direction: row; 11 | flex-wrap: wrap; 12 | justify-content: space-between; 13 | } 14 | 15 | .scroll-height .img-view { 16 | width: 49%; 17 | padding: 24.5% 0; 18 | box-sizing: border-box; 19 | background-color: rgba(255, 255, 255, .5); 20 | margin-bottom: 2%; 21 | position: relative; 22 | /* background-size: 90%; 23 | background-repeat: no-repeat; 24 | background-position: center; */ 25 | /* background-origin: center; */ 26 | } 27 | .scroll-height .img-view image { 28 | position: absolute; 29 | width: 90%; 30 | height: 90%; 31 | left: 0; 32 | top: 0; 33 | right: 0; 34 | bottom: 0; 35 | margin: auto; 36 | } 37 | 38 | .scroll-height .img-view .msg { 39 | position: absolute; 40 | background-color: rgba(255, 255, 255, .8); 41 | line-height: 2; 42 | height: 2em; 43 | width: 100%; 44 | left: 0; 45 | right: 0; 46 | bottom: 0; 47 | } 48 | -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/acl8s-bu9e0-016_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/acl8s-bu9e0-016_s.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/ahvga-47qgd-003_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/ahvga-47qgd-003_s.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/author.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/click-white-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/click-white-left.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/click-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/click-white.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/crop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/crop.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/look-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/look-video.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/move-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/move-white.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/use.png -------------------------------------------------------------------------------- /miniprogram/pages/editPhoto/images/zoom-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/editPhoto/images/zoom-white.png -------------------------------------------------------------------------------- /miniprogram/pages/helpMake/helpMake.js: -------------------------------------------------------------------------------- 1 | // 在页面中定义插屏广告 2 | let interstitialAd = null 3 | const app = getApp() 4 | Page({ 5 | 6 | onLoad () { 7 | // 在页面onLoad回调事件中创建插屏广告实例 8 | if (wx.createInterstitialAd) { 9 | interstitialAd = wx.createInterstitialAd({ 10 | adUnitId: 'adunit-7bd4afc44e5cebbd' 11 | }) 12 | interstitialAd.onLoad(() => {}) 13 | interstitialAd.onError((err) => {}) 14 | interstitialAd.onClose(() => {}) 15 | } 16 | }, 17 | 18 | onShow () { 19 | // 在适合的场景显示插屏广告 20 | if (interstitialAd) { 21 | interstitialAd.show().catch((err) => { 22 | console.error(err) 23 | }) 24 | } 25 | }, 26 | 27 | // 主动下发客服消息 28 | sendGroupQRCode () { 29 | wx.cloud.callFunction({ 30 | name: 'customerService' 31 | }) 32 | }, 33 | 34 | /** 35 | * 用户点击右上角分享 36 | */ 37 | onShareAppMessage: function () { 38 | return { 39 | title: '点这个帮我做张免冠照片吧。谢谢!', 40 | path: '/pages/index/index', 41 | imageUrl: '/shareShow.jpg' 42 | } 43 | }, 44 | 45 | onShareTimeline: function () { 46 | return { 47 | title: '谁帮我制作个免冠照?点这个,谢谢。', 48 | // path: '/pages/index/index', 49 | imageUrl: '/shareShow.jpg' 50 | } 51 | }, 52 | }) -------------------------------------------------------------------------------- /miniprogram/pages/helpMake/helpMake.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "帮制作", 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /miniprogram/pages/helpMake/helpMake.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 因微信版本、手机系统等原因,部分用户无法正常操作、无法成功下载图片。 17 | 18 | 19 | 20 | 21 | 22 | 操作繁琐,不够简易,提示不明确等。 23 | 24 | 25 | 26 | 27 | 28 | 虽然作者尽可能的解决以上问题,但作者一人之力,时间精力经费有限。 29 | 30 | 31 | 32 | 33 | 34 | 为了您能成功生成想要的免冠照/证件照,您可以选择找朋友帮忙制作,或联系客服帮制作。 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /miniprogram/pages/helpMake/helpMake.wxss: -------------------------------------------------------------------------------- 1 | image { 2 | width: 80vw; 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/imgCompose/compose.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | 3 | /** 4 | * 页面的初始数据 5 | */ 6 | data: { 7 | imageSrc: '', 8 | }, 9 | 10 | /** 11 | * 生命周期函数--监听页面加载 12 | */ 13 | onLoad() { 14 | 15 | }, 16 | 17 | /** 18 | * 生命周期函数--监听页面初次渲染完成 19 | */ 20 | onReady() { 21 | 22 | }, 23 | 24 | /** 25 | * 生命周期函数--监听页面显示 26 | */ 27 | onShow() { 28 | 29 | }, 30 | 31 | /** 32 | * 生命周期函数--监听页面隐藏 33 | */ 34 | onHide() { 35 | 36 | }, 37 | 38 | /** 39 | * 生命周期函数--监听页面卸载 40 | */ 41 | onUnload() { 42 | 43 | }, 44 | 45 | /** 46 | * 页面相关事件处理函数--监听用户下拉动作 47 | */ 48 | onPullDownRefresh() { 49 | 50 | }, 51 | 52 | /** 53 | * 页面上拉触底事件的处理函数 54 | */ 55 | onReachBottom() { 56 | 57 | }, 58 | 59 | /** 60 | * 用户点击右上角分享 61 | */ 62 | onShareAppMessage() { 63 | 64 | }, 65 | preView (event) { 66 | wx.previewImage({ 67 | urls: [this.data.imageSrc], 68 | current: this.data.imageSrc 69 | }) 70 | }, 71 | // 下载 72 | downloadClick() { 73 | if (!this.data.imageSrc) { 74 | wx.showToast({ 75 | title: '请上传图片', 76 | icon: 'none', 77 | duration: 3000 78 | }) 79 | } else { 80 | console.log(this.data.imageSrc); 81 | var url = this.data.imageSrc; 82 | wx.downloadFile({ 83 | url: url, 84 | success: function (res) { 85 | console.log(res); 86 | //图片保存到本地 87 | wx.saveImageToPhotosAlbum({ 88 | filePath: res.tempFilePath, 89 | success: function (data) { 90 | wx.showToast({ 91 | title: '保存成功', 92 | icon: 'success', 93 | duration: 3000 94 | }) 95 | setTimeout(() => { 96 | wx.navigateBack(); 97 | }, 3000) 98 | }, 99 | fail: function (err) { 100 | console.log(err); 101 | if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { 102 | console.log("当初用户拒绝,再次发起授权") 103 | wx.openSetting({ 104 | success(settingdata) { 105 | console.log(settingdata) 106 | if (settingdata.authSetting['scope.writePhotosAlbum']) { 107 | console.log('获取权限成功,给出再次点击图片保存到相册的提示。') 108 | } else { 109 | console.log('获取权限失败,给出不给权限就无法正常使用的提示') 110 | } 111 | } 112 | }) 113 | } 114 | }, 115 | complete(res) { 116 | console.log(res); 117 | } 118 | }) 119 | } 120 | }) 121 | } 122 | 123 | }, 124 | //选图片 125 | choosePic() { 126 | if (this.data.imageSrc) { 127 | return this.preView() 128 | } 129 | console.log('选图片') 130 | wx.chooseMedia({ 131 | count: 1, 132 | mediaType: ['image'], 133 | sizeType: ['compressed'], 134 | success: (res) => { 135 | const tempFilePath = res.tempFiles[0].tempFilePath 136 | this.uploadImg(tempFilePath) 137 | }, 138 | fail() { 139 | wx.showToast({ 140 | title: '取消选择', 141 | icon: 'none', 142 | duration: 2000 143 | }) 144 | } 145 | }) 146 | }, 147 | uploadImg(tempFilePath) { 148 | console.log(tempFilePath) 149 | wx.showLoading({ 150 | title: '图片安全校验', 151 | }) 152 | // 获取图片信息 153 | wx.getImageInfo({ 154 | src: tempFilePath, 155 | }) 156 | // 图片安全校验 157 | .then(res => { 158 | return wx.cloud.callFunction({ 159 | name: 'printStyle', 160 | data: { 161 | width: res.width, 162 | height: res.height, 163 | type: res.type, 164 | // 上传图片到临时CDN,返回图片地址 165 | filePath: wx.cloud.CDN({ 166 | type: 'filePath', 167 | filePath: res.path, 168 | }) 169 | }, 170 | }) 171 | }).then((res) => { 172 | console.log('返回值', res) 173 | wx.hideLoading() 174 | if (!res.result.errMsg) { 175 | this.setData({ 176 | imageSrc: res.result 177 | }) 178 | } else { 179 | wx.showToast({ 180 | title: res.result.errMsg, 181 | icon: 'none', 182 | duration: 3000 183 | }) 184 | } 185 | 186 | }) // 错误处理 187 | .catch(console.error) 188 | } 189 | }) -------------------------------------------------------------------------------- /miniprogram/pages/imgCompose/compose.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "图片排版", 3 | "usingComponents": { 4 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 5 | } 6 | } -------------------------------------------------------------------------------- /miniprogram/pages/imgCompose/compose.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 上传照片 4 | 5 | 6 | 排版尺寸为常用6寸,建议原图为一寸/二寸照片 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /miniprogram/pages/imgCompose/compose.wxss: -------------------------------------------------------------------------------- 1 | /* pages/imgCompose/compose.wxss */ 2 | /* pages/imgZip/imgZip.wxss */ 3 | .container { 4 | width: 100%; 5 | height: 100vh; 6 | background-color: #fff; 7 | /* overflow: hidden; */ 8 | } 9 | .backView{ 10 | margin-top: 30rpx; 11 | width: 85%; 12 | height: 700rpx; 13 | /* background-color: #999999; */ 14 | border: 1px solid #eee; 15 | border-radius: 4px; 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | } 20 | .zipBtn{ 21 | background-color: #3E85EE; 22 | margin-top: 20px; 23 | color: white; 24 | width: 90%; 25 | } -------------------------------------------------------------------------------- /miniprogram/pages/imgZip/image/choosePic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/imgZip/image/choosePic.png -------------------------------------------------------------------------------- /miniprogram/pages/imgZip/imgZip.js: -------------------------------------------------------------------------------- 1 | // pages/imgZip/imgZip.ts 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | imgType: '', 9 | realW: 0, 10 | realH: 0, 11 | size: 0, 12 | imageSrc: '', 13 | imgW: '', 14 | imgH: '', 15 | quality: '' 16 | }, 17 | 18 | /** 19 | * 生命周期函数--监听页面加载 20 | */ 21 | onLoad() { 22 | 23 | }, 24 | 25 | /** 26 | * 生命周期函数--监听页面初次渲染完成 27 | */ 28 | onReady() { 29 | 30 | }, 31 | 32 | /** 33 | * 生命周期函数--监听页面显示 34 | */ 35 | onShow() { 36 | 37 | }, 38 | 39 | /** 40 | * 生命周期函数--监听页面隐藏 41 | */ 42 | onHide() { 43 | 44 | }, 45 | 46 | /** 47 | * 生命周期函数--监听页面卸载 48 | */ 49 | onUnload() { 50 | 51 | }, 52 | 53 | /** 54 | * 页面相关事件处理函数--监听用户下拉动作 55 | */ 56 | onPullDownRefresh() { 57 | 58 | }, 59 | 60 | /** 61 | * 页面上拉触底事件的处理函数 62 | */ 63 | onReachBottom() { 64 | 65 | }, 66 | 67 | /** 68 | * 用户点击右上角分享 69 | */ 70 | onShareAppMessage() { 71 | 72 | }, 73 | imgWidth: function (e) { 74 | const { 75 | imageSrc 76 | } = this.data 77 | let msg = '' 78 | if (imageSrc === '') { 79 | msg = '请上传图片' 80 | wx.showToast({ 81 | title: msg, 82 | icon: 'none' 83 | }) 84 | this.setData({ 85 | imgW: '', 86 | }) 87 | } else { 88 | const zsw = this.data.realW; 89 | const zsh = this.data.realH; 90 | const w = e.detail.value; 91 | const h = parseFloat((w * zsh / zsw * 100) / 100).toFixed(2); 92 | console.log(h, '--', zsw, '-', zsh); 93 | this.setData({ 94 | imgW: w, 95 | imgH: w > 0 ? h : '', 96 | }) 97 | } 98 | 99 | }, 100 | imgHeight: function (e) { 101 | const { 102 | imageSrc 103 | } = this.data 104 | let msg = '' 105 | if (imageSrc === '') { 106 | msg = '请上传图片' 107 | wx.showToast({ 108 | title: msg, 109 | icon: 'none' 110 | }) 111 | this.setData({ 112 | imgH: '', 113 | }) 114 | } else { 115 | const zsw = this.data.realW; 116 | const zsh = this.data.realH; 117 | const h = e.detail.value; 118 | const w = parseFloat((h * zsw / zsh * 100) / 100).toFixed(2); 119 | console.log(h); 120 | this.setData({ 121 | imgW: h > 0 ? w : '', 122 | imgH: h, 123 | }) 124 | } 125 | 126 | }, 127 | imgQuality: function (e) { 128 | const { 129 | imageSrc 130 | } = this.data 131 | let msg = '' 132 | if (imageSrc === '') { 133 | msg = '请上传图片' 134 | wx.showToast({ 135 | title: msg, 136 | icon: 'none' 137 | }) 138 | this.setData({ 139 | quality: '', 140 | }) 141 | } else { 142 | const q = e.detail.value; 143 | this.setData({ 144 | quality: q == 0 ? '' : q 145 | }) 146 | } 147 | 148 | }, 149 | zipClick() { 150 | const { 151 | imgW, 152 | imgH, 153 | quality, 154 | imageSrc 155 | } = this.data 156 | let msg = '' 157 | if (imageSrc === '') { 158 | msg = '请上传图片' 159 | wx.showToast({ 160 | title: msg, 161 | icon: 'none' 162 | }) 163 | return 164 | } 165 | console.log('请上传图片', imageSrc) 166 | if (this.data.imgW == 0 || this.data.imgH == 0 || !this.data.imgH || !this.data.imgW) { 167 | msg = '宽度或高度不能为0' 168 | wx.showToast({ 169 | title: msg, 170 | icon: 'none' 171 | }) 172 | return 173 | } 174 | console.log('压缩质量', quality) 175 | if (this.data.quality > 100 || this.data.quality < 50) { 176 | msg = '压缩质量范围是50-100' 177 | wx.showToast({ 178 | title: msg, 179 | icon: 'none' 180 | }) 181 | return 182 | } 183 | wx.showLoading({ 184 | title: '图片压缩中', 185 | }) 186 | wx.cloud.callFunction({ 187 | name: 'imagemin', 188 | data: { 189 | width: this.data.imgW, 190 | height: this.data.imgH, 191 | type: this.data.type, 192 | quality: this.data.quality, 193 | // 上传图片到临时CDN,返回图片地址 194 | filePath: wx.cloud.CDN({ 195 | type: 'filePath', 196 | filePath: this.data.imageSrc, 197 | }) 198 | }, 199 | }).then((res) => { 200 | console.log('返回值', res) 201 | wx.hideLoading() 202 | if (!res.result.errMsg) { 203 | wx.navigateTo({ 204 | url: '/pages/zipSuccess/zipSuccess?imgurl=' + res.result, 205 | }) 206 | } else { 207 | wx.showToast({ 208 | title: res.result.errMsg, 209 | icon: 'none', 210 | duration: 3000 211 | }) 212 | } 213 | }) // 错误处理 214 | .catch(console.error) 215 | 216 | 217 | }, 218 | choosePic() { 219 | console.log('选图片') 220 | wx.chooseMedia({ 221 | count: 1, 222 | mediaType: ['image'], 223 | sizeType: ['compressed'], 224 | success: (res) => { 225 | const tempFilePath = res.tempFiles[0].tempFilePath 226 | this.setData({ 227 | imageSrc: tempFilePath 228 | }) 229 | console.log(this.data.imageSrc) 230 | const fs = wx.getFileSystemManager() 231 | fs.getFileInfo({ 232 | filePath: tempFilePath, 233 | success: (res) => { 234 | this.setData({ 235 | size: (res.size / 1024).toFixed(2) 236 | }) 237 | } 238 | }) 239 | // 获取图片信息 240 | wx.getImageInfo({ 241 | src: tempFilePath, 242 | }) 243 | // 图片安全校验 244 | .then(res => { 245 | this.setData({ 246 | imgType: res.type, 247 | realW: res.width, 248 | realH: res.height, 249 | imgW: res.width, 250 | imgH: res.height 251 | }) 252 | console.log(res, this.data.realW, '----', this.data.realH) 253 | }) 254 | }, 255 | fail() { 256 | wx.showToast({ 257 | title: '取消选择', 258 | icon: 'none', 259 | duration: 2000 260 | }) 261 | } 262 | }) 263 | } 264 | }) -------------------------------------------------------------------------------- /miniprogram/pages/imgZip/imgZip.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "图片压缩", 3 | "usingComponents": { 4 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 5 | } 6 | } -------------------------------------------------------------------------------- /miniprogram/pages/imgZip/imgZip.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 上传图片 7 | 8 | 9 | 压缩前图片宽{{realW}}px,高{{realH}}px,占用空间{{size}}kb 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /miniprogram/pages/imgZip/imgZip.wxss: -------------------------------------------------------------------------------- 1 | /* pages/imgZip/imgZip.wxss */ 2 | .container { 3 | width: 100%; 4 | height: 100vh; 5 | background-color: #fff; 6 | /* overflow: hidden; */ 7 | } 8 | .backView{ 9 | margin-top: 30rpx; 10 | width: 85%; 11 | height: 700rpx; 12 | /* background-color: #eee; */ 13 | border: 1px solid #eee; 14 | border-radius: 4px; 15 | display: flex; 16 | justify-content: center; 17 | align-items: center; 18 | margin-bottom: 30rpx; 19 | } 20 | .backView image { 21 | width: 100%; 22 | height: 100%; 23 | max-width: 100%; 24 | max-height: 100%; 25 | } 26 | 27 | .setZipView{ 28 | width: 90%; 29 | padding: 20rpx; 30 | display:flex; 31 | align-items:center; 32 | } 33 | .setZipView label { 34 | color: #999999; 35 | } 36 | input{ 37 | display:inline-block; 38 | width: 60px; 39 | height: 24px; 40 | padding: 0 5px; 41 | line-height:30px; 42 | border: 1px solid #999999; 43 | border-radius: 5px; 44 | /* position: absolute; 45 | top: 50%; 46 | transform: translateY(-50%); */ 47 | } 48 | .zipBtn{ 49 | background-color: #3E85EE; 50 | margin-top: 20px; 51 | color: white; 52 | flex: 1; 53 | } -------------------------------------------------------------------------------- /miniprogram/pages/index/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/.DS_Store -------------------------------------------------------------------------------- /miniprogram/pages/index/images/3001677327372_.pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/3001677327372_.pic.jpg -------------------------------------------------------------------------------- /miniprogram/pages/index/images/custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/custom.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon0.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon1.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon2.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon3.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon4.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon5.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon6.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/home_icon7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/home_icon7.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/hot.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/icon-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/icon-more.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/icon-size1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/icon-size1.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/icon-size2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/icon-size2.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/search-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/search-input.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/space-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/space-img.png -------------------------------------------------------------------------------- /miniprogram/pages/index/images/微信图片_20200712103220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/index/images/微信图片_20200712103220.jpg -------------------------------------------------------------------------------- /miniprogram/pages/index/index.js: -------------------------------------------------------------------------------- 1 | const app = getApp() 2 | 3 | Page({ 4 | 5 | /** 6 | * 页面的初始数据 7 | */ 8 | data: { 9 | // 近期热门列表 10 | photoSizeList: app.globalData.photoSizeList 11 | }, 12 | 13 | /** 14 | * 生命周期函数--监听页面加载 15 | */ 16 | onLoad: function (options) { 17 | wx.setNavigationBarTitle({ title: '免冠照/证件照' }) 18 | // 来自邀请,签到时使用 19 | if (options.shareOpenid) { 20 | wx.setStorage({ 21 | key: "fromShare", 22 | data: options.shareOpenid 23 | }) 24 | } 25 | }, 26 | 27 | //跳转分类页面 28 | goClassPage(e) { 29 | wx.navigateTo({ 30 | url: '/pages/searchSize/searchSize?index=' + e.currentTarget.dataset.index, 31 | }) 32 | }, 33 | 34 | // 去选择照片页面 35 | goNextPage (e) { 36 | wx.navigateTo({ 37 | url: '/pages/preEdit/preEdit?index=' + e.currentTarget.dataset.index, 38 | }) 39 | }, 40 | 41 | // 页面跳转 42 | navigateTo(e) { 43 | wx.navigateTo({ url: e.currentTarget.dataset.url, }) 44 | }, 45 | 46 | /** 47 | * 用户点击右上角分享 48 | */ 49 | onShareAppMessage: function () { 50 | return { 51 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 52 | path: '/pages/index/index', 53 | imageUrl: '/shareShow.jpg' 54 | } 55 | }, 56 | onShareTimeline: function () { 57 | return { 58 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 59 | imageUrl: '/shareShow.jpg' 60 | } 61 | }, 62 | 63 | }) -------------------------------------------------------------------------------- /miniprogram/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "免冠照/证件照", 3 | "usingComponents": { 4 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 5 | } 6 | } -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 点击“ 3 | 4 | ”添加到我的小程序我知道了 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 一键P图 23 | 24 | 背景图片随意换 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 39 | 40 | 下载 41 | 42 | 好看的风景、美女、手机壁纸、电脑壁纸 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 搜索:如“一寸” 52 | 53 | 54 | 55 | 56 | 57 | 58 | 常用尺寸 59 | 60 | 61 | 62 | 职业资格 63 | 64 | 65 | 66 | 公务员 67 | 68 | 69 | 70 | 学历语言 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 | 图片压缩 82 | 83 | 84 | 85 | 自定尺寸 86 | 87 | 88 | 89 | 90 | 打印排版 91 | 92 | 93 | 94 | 更多 95 | 96 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 112 | 115 | 116 | 120 | {{ item.name }} 121 | {{ item.px }} | {{ item.size }} 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/index/index.wxss */ 2 | .container { 3 | background-color: #fff; 4 | position: relative; 5 | box-sizing: border-box; 6 | width: 100vw; 7 | /* height: 100vh; */ 8 | display: flex; 9 | flex-direction: column; 10 | overflow-x: hidden; 11 | } 12 | 13 | swiper { 14 | width: 100%; 15 | height: 300rpx; 16 | } 17 | swiper-item { 18 | width: 100%; 19 | height: 100%; 20 | } 21 | swiper-item .swiper-item { 22 | /* background-color: pink; */ 23 | width: 100%; 24 | height: 100%; 25 | display: block; 26 | } 27 | 28 | 29 | /* 人像抠图 */ 30 | .img-p { 31 | background-color: #fff; 32 | position: relative; 33 | width: 100%; 34 | height: 100%; 35 | } 36 | .img-p image { 37 | width: 100%; 38 | height: 100%; 39 | } 40 | .img-p .msg { 41 | position: absolute; 42 | left: 30rpx; 43 | bottom: 0rpx; 44 | /* color: #fff; */ 45 | font-size: 40rpx; 46 | line-height: 2; 47 | } 48 | .img-p .msg .sub-title { 49 | font-size: 30rpx; 50 | } 51 | 52 | .search-view { 53 | /* box-sizing: border-box; */ 54 | padding-left: 30rpx; 55 | color: #999999; 56 | margin: 30rpx; 57 | width: 90%; 58 | height: 35px; 59 | padding: 15rpx; 60 | border: 1px solid #3E85EE; 61 | border-radius: 5px 62 | 63 | } 64 | /* .search-view label { 65 | width: 100%; 66 | height: 170rpx; 67 | padding-left: 50px; 68 | } */ 69 | 70 | .tip-list { 71 | padding: 0 50rpx; 72 | width: 100%; 73 | display: flex; 74 | flex-direction: row; 75 | flex-wrap: nowrap; 76 | justify-content: space-between; 77 | } 78 | .tip-list .tip-item { 79 | width: 88rpx; 80 | height: 160rpx; 81 | font-size: 22rpx; 82 | display: flex; 83 | flex-direction: column; 84 | align-items: center; 85 | } 86 | 87 | .tip-list .tip-item image { 88 | width: 60rpx; 89 | height: 60rpx; 90 | margin-bottom: 20rpx; 91 | } 92 | 93 | .make-view { 94 | box-sizing: border-box; 95 | width: 100%; 96 | padding: 30rpx 30rpx; 97 | display: flex; 98 | justify-content: space-between; 99 | } 100 | 101 | .make-view image { 102 | width: 330rpx; 103 | height: 130rpx; 104 | } 105 | 106 | 107 | .hot-view { 108 | box-sizing: border-box; 109 | width: 100%; 110 | padding: 0 40rpx; 111 | } 112 | .hot-view image { 113 | width: 184rpx; 114 | height: 50rpx; 115 | } 116 | 117 | .custom-view { 118 | box-sizing: border-box; 119 | width: 100%; 120 | padding: 20rpx 30rpx; 121 | } 122 | .custom-view image { 123 | width: 100%; 124 | height: 130rpx; 125 | } 126 | 127 | 128 | .hot-list { 129 | box-sizing: border-box; 130 | width: 100%; 131 | padding: 0rpx 30rpx; 132 | } 133 | .hot-list .hot-list-item { 134 | box-sizing: border-box; 135 | width: 100%; 136 | height: 130rpx; 137 | padding: 25rpx 40rpx; 138 | border: 1px solid #eee; 139 | border-radius: 10rpx; 140 | position: relative; 141 | margin-bottom: 20rpx; 142 | } 143 | .hot-list ad { 144 | margin-bottom: 20rpx; 145 | } 146 | .hot-list .hot-list-item .title { 147 | font-size: 26rpx; 148 | } 149 | .hot-list .hot-list-item .description { 150 | font-size: 24rpx; 151 | color: #919191; 152 | margin-top: 20rpx; 153 | } 154 | .hot-list .hot-list-item image { 155 | position: absolute; 156 | width: 60rpx; 157 | height: 60rpx; 158 | right: 40rpx; 159 | top: 36rpx; 160 | } 161 | 162 | .ad-view { 163 | width: 100%; 164 | box-sizing: border-box; 165 | padding: 0 30rpx; 166 | } 167 | 168 | 169 | .tips { 170 | height: 60rpx; 171 | line-height: 60rpx; 172 | text-align: center; 173 | color: #fff; 174 | font-size: 22rpx; 175 | padding: 0 30rpx; 176 | background: rgba(0,0,0,.6); 177 | position: fixed; 178 | top: 22rpx; 179 | right: 30rpx; 180 | z-index: 999; 181 | border-radius: 8rpx; 182 | display: flex; 183 | align-items: center; 184 | } 185 | 186 | .tips.active { 187 | display: none; 188 | } 189 | 190 | .sml_dot { 191 | width: 10rpx; 192 | height: 10rpx; 193 | border-radius: 10rpx; 194 | background: #fff; 195 | display: inline-block; 196 | } 197 | 198 | .lag_dot { 199 | width: 16rpx; 200 | height: 16rpx; 201 | border-radius: 16rpx; 202 | background: #fff; 203 | display: inline-block; 204 | margin: 0 4rpx; 205 | } 206 | .top_arrow { 207 | position: absolute; 208 | top: -35rpx; 209 | right: 100rpx; 210 | width: 0; 211 | height: 0; 212 | border: 18rpx solid transparent; 213 | border-bottom-color: rgba(0,0,0,.5); 214 | } 215 | .ok { 216 | margin-left: 30rpx; 217 | color: #f44; 218 | } -------------------------------------------------------------------------------- /miniprogram/pages/mein/images/wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/mein/images/wave.gif -------------------------------------------------------------------------------- /miniprogram/pages/mein/mein.js: -------------------------------------------------------------------------------- 1 | // miniprogram/pages/mein/mein.js 2 | // 在页面中定义激励视频广告 3 | let videoAd = null 4 | const app = getApp() 5 | Page({ 6 | 7 | /** 8 | * 页面的初始数据 9 | */ 10 | data: { 11 | userInfo: {}, 12 | authorized: false, // 用户头像昵称授权 13 | signed: false, 14 | signInLoading: false, 15 | videoLoaded: false, 16 | canIUseGetUserProfile: true, 17 | envVersion: wx.getAccountInfoSync().miniProgram.envVersion || 'release' 18 | }, 19 | 20 | // 下发赞赏二维码 21 | sendAppreciateQRCode (e) { 22 | wx.cloud.callFunction({ 23 | name: 'sendAppreciateQRCode' 24 | }) 25 | }, 26 | 27 | // 下发群二维码 28 | sendGroupQRCode () { 29 | wx.cloud.callFunction({ 30 | name: 'customerService' 31 | }) 32 | }, 33 | 34 | // 复制GitHub地址 35 | copyGithubLink() { 36 | wx.setClipboardData({ 37 | data: 'https://github.com/liuxiaojun666/ID-Photo-miniapp-wechart', 38 | success (res) { 39 | wx.showToast({ 40 | title: '开源地址已复制', 41 | }) 42 | } 43 | }) 44 | }, 45 | 46 | // 获取用户信息回调 47 | bindGetUserInfo (e) { 48 | if (e.detail.userInfo) { 49 | this.setUserInfo(e.detail.userInfo) 50 | } 51 | }, 52 | 53 | // 新的获取用户信息事件回调 54 | getUserProfile(e) { 55 | if (this.data.isLatestInfo) return 56 | wx.getUserProfile({ 57 | desc: '用于完善资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 58 | success: (res) => { 59 | this.setUserInfo(res.userInfo) 60 | this.setData({ isLatestInfo: true }) 61 | } 62 | }) 63 | }, 64 | 65 | // 设置用户信息 66 | setUserInfo (userInfo) { 67 | this.setData({ 68 | authorized: !!userInfo.nickName, 69 | userInfo: { 70 | ...this.data.userInfo, 71 | ...userInfo 72 | } 73 | }) 74 | const openid = app.globalData.openid 75 | if (!openid) return 76 | wx.cloud.callFunction({ 77 | name: 'setUserInfo', 78 | data: {openid, data: userInfo} 79 | }).then(res => { 80 | console.log(res) 81 | }) 82 | }, 83 | 84 | // 签到 85 | signIn () { 86 | if (this.data.signInLoading) return 87 | if (!this.data.authorized) { 88 | return wx.showToast({ 89 | title: '请授权登录', 90 | icon: 'none' 91 | }) 92 | } 93 | this.subscribeSign() 94 | this.setData({ signInLoading: true }) 95 | wx.cloud.callFunction({ 96 | name: 'useCount', 97 | data: {inc: 1, signIn: true} 98 | }).then(res => { 99 | wx.showToast({ title: '签到成功', }) 100 | this.getUserInfo() 101 | this.shareSuccess() 102 | }) 103 | }, 104 | 105 | // 被邀请成功 106 | shareSuccess () { 107 | const fromShare = wx.getStorageSync('fromShare') 108 | if (!fromShare) return 109 | wx.cloud.callFunction({ 110 | name: 'shareUpdate', 111 | data: { 112 | shareOpenid: fromShare 113 | } 114 | }) 115 | }, 116 | 117 | // 订阅签到 118 | subscribeSign() { 119 | const tmplIds = ["h_P9sODh-NfeXteA5t7h6sS9BAIjtEyppGwt-biQIQ0"] 120 | wx.requestSubscribeMessage({ 121 | tmplIds: tmplIds, 122 | success: (res) => { 123 | if (res[tmplIds[0]] === 'accept') { 124 | wx.showToast({ title: '订阅成功', }) 125 | } 126 | }, 127 | fail(error) { 128 | console.log(error) 129 | } 130 | }) 131 | }, 132 | 133 | // 看视频 134 | lookVideo () { 135 | // 用户触发广告后,显示激励视频广告 136 | if (videoAd) { 137 | videoAd.show().catch(() => { 138 | // 失败重试 139 | videoAd.load() 140 | .then(() => videoAd.show()) 141 | .catch(err => { 142 | wx.showToast({ 143 | title: '视频显示失败', 144 | icon: 'loading' 145 | }) 146 | }) 147 | }) 148 | } 149 | }, 150 | 151 | // 选择视频奖励 152 | modalConfirm() { 153 | wx.showModal({ 154 | title: '请选择奖励', 155 | content: '选择普通,+3次普通下载,选择升级版 +1次升级版精细抠图', 156 | showCancel: true, 157 | cancelText: '普通版', 158 | confirmText: '升级版', 159 | success: (res) => { 160 | if (res.confirm) { 161 | this.inccVipCount() 162 | } else if (res.cancel) { 163 | this.incCount() 164 | console.log('用户点击取消') 165 | } 166 | wx.showToast({ title: '奖励已下发'}) 167 | }, 168 | fail: (res) => { 169 | // console.log(res) 170 | this.incCount() 171 | } 172 | }) 173 | }, 174 | 175 | // 增加次数 176 | incCount () { 177 | wx.showLoading() 178 | wx.cloud.callFunction({ 179 | name: 'useCount', 180 | data: {inc: 1} 181 | }).then(res => { 182 | wx.hideLoading({}) 183 | this.timerFunc() 184 | }) 185 | }, 186 | 187 | // 增加vip次数 188 | inccVipCount () { 189 | wx.showLoading() 190 | wx.cloud.callFunction({ 191 | name: 'useVipCount', 192 | data: { 193 | lookVideo: true 194 | } 195 | }).then(res => { 196 | wx.hideLoading({}) 197 | this.timerFunc() 198 | }) 199 | }, 200 | 201 | /** 202 | * 生命周期函数--监听页面加载 203 | */ 204 | onLoad: function (options) { 205 | if (wx.getUserProfile) { 206 | this.setData({ 207 | canIUseGetUserProfile: true 208 | }) 209 | } 210 | 211 | // 在页面onLoad回调事件中创建激励视频广告实例 212 | if (wx.createRewardedVideoAd) { 213 | videoAd = wx.createRewardedVideoAd({ 214 | adUnitId: 'adunit-93858df72c78d43a' 215 | }) 216 | videoAd.onLoad(() => { 217 | this.setData({ 218 | videoLoaded: true 219 | }) 220 | }) 221 | videoAd.onError((err) => { 222 | this.setData({ 223 | videoLoaded: false 224 | }) 225 | }) 226 | videoAd.onClose((res) => { 227 | if (res && res.isEnded) { 228 | // this.useCount(true, 1) 229 | // this.inccVipCount() 230 | // setTimeout(this.modalConfirm, 500) 231 | // this.modalConfirm() 232 | this.incCount() 233 | } else { 234 | wx.showToast({ 235 | title: '看完才有奖励哦!', 236 | icon: 'none' 237 | }) 238 | } 239 | }) 240 | } 241 | }, 242 | 243 | /** 244 | * 生命周期函数--监听页面初次渲染完成 245 | */ 246 | onReady: function () { 247 | 248 | }, 249 | 250 | // 从数据库获取用户信息,并更新用户信息 251 | getUserInfo () { 252 | const that = this 253 | const openid = app.globalData.openid 254 | if (!openid) return 255 | const db = wx.cloud.database() 256 | db.collection('user').where({ openid }).get().then(res => { 257 | this.setData({ 258 | userInfo: res.data[0], 259 | signed: res.data[0].signInDate.trim() === new Date().toDateString().trim() 260 | }) 261 | 262 | // 如果是新接口,就算授权也不能直接获取用户信息,结束执行并设置已有信息 263 | if (this.data.canIUseGetUserProfile) { 264 | return this.setUserInfo(res.data[0]) 265 | } 266 | 267 | wx.getSetting({ 268 | success (res){ 269 | if (res.authSetting['scope.userInfo']) { 270 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称 271 | wx.getUserInfo({ 272 | success: function(res) { 273 | that.setUserInfo(res.userInfo) 274 | } 275 | }) 276 | } else { 277 | that.setData({ 278 | authorized: false 279 | }) 280 | } 281 | } 282 | }) 283 | }) 284 | }, 285 | 286 | /** 287 | * 生命周期函数--监听页面显示 288 | */ 289 | onShow: function () { 290 | this.timerFunc() 291 | }, 292 | 293 | // 定时器,解决第一次进入页面没有openid 的问题 294 | timerFunc () { 295 | const openid = app.globalData.openid 296 | if (openid) { 297 | this.getUserInfo() 298 | } else { 299 | setTimeout(() => this.timerFunc(), 3000) 300 | } 301 | }, 302 | 303 | // 作者二维码 304 | previewQRcode() { 305 | wx.previewImage({ 306 | urls: ['/images/WechatIMG199.jpeg'], 307 | current: '/images/WechatIMG199.jpeg' 308 | }) 309 | }, 310 | 311 | 312 | onShareTimeline: function () { 313 | return { 314 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 315 | imageUrl: '/shareShow.jpg' 316 | } 317 | }, 318 | }) -------------------------------------------------------------------------------- /miniprogram/pages/mein/mein.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/mein/mein.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{userInfo.nickName}} 11 | 12 | 13 | 14 | {{userInfo.nickName}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{userInfo.count || 0}} 26 | 剩余下载次数 27 | 28 | 32 | 33 | {{userInfo.accumCreatePhoto || 0}} 34 | 累计下载 35 | 36 | 37 | 38 | 39 | 我的唯一标识:{{ userInfo.openid }} 40 | 41 | 42 | 43 | 44 | 邀请好友 + 次数 45 | 46 | 47 | 48 | 49 | 50 | 看视频 + 次数 51 | 52 | 53 | 54 | 55 | 56 | 有问题找作者 57 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 项目已开源:点击复制开源地址 69 | 70 | -------------------------------------------------------------------------------- /miniprogram/pages/mein/mein.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/mein/mein.wxss */ 2 | .scrollPage { 3 | height: 100vh; 4 | } 5 | .user-info .cu-avatar { 6 | overflow: hidden; 7 | } 8 | 9 | .my-info { 10 | width: 100%; 11 | } 12 | 13 | .user-info .nickName { 14 | margin-left: 1em; 15 | text-shadow: none; 16 | display: inline-block; 17 | vertical-align: middle; 18 | } 19 | 20 | .user-info .signIn { 21 | /* height: 100%; */ 22 | vertical-align: middle; 23 | line-height: 120rpx; 24 | float: right; 25 | text-shadow: none; 26 | } 27 | 28 | .UCenter-bg { 29 | background: #3E85EE; 30 | height: 280rpx; 31 | display: flex; 32 | justify-content: center; 33 | /* padding-top: 40rpx; */ 34 | overflow: hidden; 35 | position: relative; 36 | flex-direction: column; 37 | align-items: center; 38 | color: #fff; 39 | font-weight: 300; 40 | text-shadow: 0 0 3px rgba(0, 0, 0, 0.3); 41 | } 42 | 43 | .UCenter-bg text { 44 | opacity: 0.8; 45 | } 46 | 47 | .UCenter-bg image { 48 | width: 200rpx; 49 | height: 200rpx; 50 | } 51 | 52 | .UCenter-bg .gif-wave{ 53 | position: absolute; 54 | width: 100%; 55 | bottom: 0; 56 | left: 0; 57 | z-index: 99; 58 | mix-blend-mode: screen; 59 | height: 100rpx; 60 | } 61 | 62 | .github .text-blue{ 63 | padding: 10px 0; 64 | } -------------------------------------------------------------------------------- /miniprogram/pages/preEdit/preEdit.js: -------------------------------------------------------------------------------- 1 | const app = getApp() 2 | 3 | // 非响应式数据 4 | const pageData = { 5 | photoSizeList: app.globalData.photoSizeList, 6 | width: '', 7 | height: '', 8 | originTempFilePath: '', 9 | originImgPath: '', 10 | originImgType: '', 11 | compressImagePath: '' 12 | } 13 | 14 | Page({ 15 | 16 | /** 17 | * 页面的初始数据 18 | */ 19 | data: { 20 | px: '', 21 | size: '自定义', 22 | photoName: '自定义尺寸', 23 | discription: '', 24 | }, 25 | 26 | /** 27 | * 生命周期函数--监听页面加载 28 | */ 29 | onLoad: function (options) { 30 | const {index,width,height,data} = options 31 | if (width && height) { 32 | this.setData({px: width + ' * ' + height + '像素'}); 33 | Object.assign(pageData, { width: +width, height: +height }) 34 | }else if(data){ 35 | let newData = JSON.parse(data); 36 | this.setData({ 37 | px:newData.width_px+" * "+newData.height_px + " 像素", 38 | size:newData.width_mm+" × "+newData.height_mm + " mm", 39 | photoName: "基本信息", 40 | discription: newData.name 41 | }); 42 | Object.assign(pageData, { width:+newData.width_px, height:+newData.height_px, }) 43 | }else{ 44 | const {width, height, px, size, name, discription} = pageData.photoSizeList[index]; 45 | this.setData({ px, size, photoName: name, discription: discription }); 46 | Object.assign(pageData, { width, height }) 47 | } 48 | }, 49 | 50 | /** 51 | * 选择照片 52 | */ 53 | chooseImage (event) { 54 | // wx.showLoading({title: '选择照片'}) 55 | wx.chooseMedia({ 56 | count: 1, 57 | mediaType: ['image'], 58 | sizeType: ['compressed'], 59 | success: (res) => { 60 | const tempFilePath = res.tempFiles[0].tempFilePath 61 | Object.assign(pageData, {originTempFilePath: tempFilePath}) 62 | this.imgSecCheck(tempFilePath) 63 | }, 64 | fail () { 65 | wx.showToast({ title: '取消选择', icon: 'none', duration: 2000 }) 66 | } 67 | }) 68 | }, 69 | // 图片敏感信息检测 获取图片宽高信息 70 | imgSecCheck (tempFilePath) { 71 | wx.showLoading({ title: '图片安全校验', }) 72 | // 获取图片信息 73 | wx.getImageInfo({ src: tempFilePath, }) 74 | // 图片安全校验 75 | .then(res => { 76 | return wx.cloud.callFunction({ 77 | name: 'imgSecCheck', 78 | data: { 79 | width: res.width, 80 | height: res.height, 81 | type: res.type, 82 | // 上传图片到临时CDN,返回图片地址 83 | filePath: wx.cloud.CDN({ 84 | type: 'filePath', 85 | filePath: res.path, 86 | }) 87 | }, 88 | }) 89 | }) 90 | // 图片安全检测结果处理 91 | .then((res) => { 92 | if (res.result.errCode === 0) { 93 | Object.assign(pageData, { 94 | originImgPath: res.result.originImgPath, 95 | originImgType: res.result.originImgType, 96 | compressImagePath: res.result.filePath 97 | }) 98 | this.baiduKoutu({ 99 | fileID: res.result.fileId, 100 | filePath: res.result.filePath 101 | }) 102 | } else if (res.result.errCode === 87014) { 103 | wx.showToast({ title: '内容可能潜在风险,请重新选择', icon: 'none' }) 104 | } else if (res.result.errCode === -604102) { 105 | wx.showToast({ title: '超时,再试一下。或换个图试试', icon: 'none', duration: 3000 }) 106 | } else { 107 | wx.showToast({ title: '又是啥问题呀,请换图重试', icon: 'none', duration: 3000 }) 108 | } 109 | }) 110 | // 错误处理 111 | .catch(console.error) 112 | }, 113 | 114 | // 使用百度抠图 115 | baiduKoutu (data) { 116 | wx.showLoading({ title: '智能人像分割', }) 117 | wx.cloud.callFunction({ 118 | name: 'baiduKoutu', 119 | data: data 120 | }) 121 | .then(({ result }) => { 122 | this.goEditPage(result) 123 | }).catch((error) => { 124 | console.log(error) 125 | wx.showToast({ title: '失败,请重试' }) 126 | }) 127 | }, 128 | 129 | /** 130 | * 去编辑页面 131 | */ 132 | goEditPage (data) { 133 | wx.hideLoading() 134 | const { width, height, originImgPath, originImgType, originTempFilePath, compressImagePath } = pageData 135 | wx.navigateTo({ 136 | url: '/pages/editPhoto/editPhotoPlus/editPhotoPlus', 137 | success: function (res) { 138 | res.eventChannel.emit('acceptDataFromOpenerPage', { 139 | ...data, 140 | originTempFilePath, 141 | originImgPath, 142 | originImgType, 143 | compressImagePath, 144 | width, 145 | height 146 | }) 147 | } 148 | }) 149 | }, 150 | 151 | /** 152 | * 用户点击右上角分享 153 | */ 154 | onShareAppMessage: function () { 155 | return { 156 | title: '证件照、免冠照、一寸照片、二寸照片、自定义尺寸、证件照换背景,免费生成、下载。', 157 | imageUrl: '/shareShow.jpg' 158 | } 159 | } 160 | }) -------------------------------------------------------------------------------- /miniprogram/pages/preEdit/preEdit.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "选择照片", 3 | "disableScroll": true, 4 | "usingComponents": { 5 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 6 | } 7 | } -------------------------------------------------------------------------------- /miniprogram/pages/preEdit/preEdit.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{photoName}} 7 | 8 | 照片像素:{{px}} 9 | 打印尺寸:{{size}} 10 | 照片描述:{{discription}} 11 | 12 | 13 | 拍照秘籍 14 | 15 | - 照片使用纯色背景(推荐白墙) 16 | - 避免多人出现在照片中 17 | - 露出耳朵、眉毛和下巴 18 | 19 | 20 | 21 | 22 | 23 | 开始制作 24 | 25 | 26 | 27 | 28 | 29 | 30 | 开始制作 31 | 32 | 33 | -------------------------------------------------------------------------------- /miniprogram/pages/preEdit/preEdit.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | overflow-x: hidden; 3 | } 4 | 5 | .container { 6 | height: 100vh; 7 | padding: 10rpx 30rpx; 8 | padding-bottom: 100rpx; 9 | overflow: visible; 10 | box-sizing: border-box; 11 | } 12 | 13 | .tips { 14 | width: 100%; 15 | margin-bottom: 20rpx; 16 | background-color: rgba(255,255,255,1); 17 | padding: 30rpx; 18 | line-height: 2; 19 | color: #333; 20 | border-radius: 10rpx; 21 | font-size: 28rpx; 22 | position: relative; 23 | } 24 | 25 | .tips .cu-btn { 26 | position: absolute; 27 | right: 30rpx; 28 | top: 30rpx; 29 | } 30 | 31 | .header { 32 | width: 100%; 33 | line-height: 2em; 34 | padding-left: 30rpx; 35 | font-size: 28rpx; 36 | font-weight: bold; 37 | } 38 | 39 | .tips .li { 40 | margin-right: 20rpx; 41 | } 42 | 43 | .container > .cu-btn { 44 | margin: 30rpx auto; 45 | } 46 | 47 | .help-btn { 48 | position: fixed; 49 | z-index: 9999; 50 | right: 30rpx; 51 | bottom: 150rpx; 52 | width: 100rpx; 53 | height: 100rpx; 54 | line-height: 100rpx; 55 | border-radius: 50%; 56 | background-color: rgba(0,0,0,.8); 57 | } 58 | 59 | .btn-view { 60 | position: fixed; 61 | left: 0; 62 | /* right: 0; */ 63 | bottom: 0; 64 | width: 100%; 65 | height: 120rpx; 66 | background-color: #fff; 67 | padding-top: 10rpx; 68 | box-shadow: 0 -10rpx 10rpx rgba(0,0,0,.1); 69 | z-index: 9999; 70 | } 71 | .btn-view .cu-btn { 72 | width: 90%; 73 | } 74 | 75 | .result-canvas-view { 76 | position: absolute; 77 | left: 100vw; 78 | top: 100vh; 79 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/custom/custom.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | 3 | /** 4 | * 页面的初始数据 5 | */ 6 | data: { 7 | width: '', 8 | height: '', 9 | }, 10 | 11 | // 跳转到下一页 12 | goNextPage (e) { 13 | const {width, height} = this.data 14 | wx.navigateTo({ 15 | url: '/pages/preEdit/preEdit?width=' +width+ '&height=' + height+ '&discription=' 16 | }) 17 | }, 18 | 19 | // 宽度改变 20 | widthChange (e) { 21 | this.setData({ 22 | width: +e.detail.value 23 | }) 24 | }, 25 | 26 | // 高度改变 27 | heightChange (e) { 28 | this.setData({ 29 | height: +e.detail.value 30 | }) 31 | }, 32 | 33 | // 点击确定,校验用户输入 34 | selectPhoto () { 35 | const {width, height} = this.data 36 | let msg = '' 37 | if (width < 100 || height < 100) { 38 | msg = '最小边不能小于100像素' 39 | } 40 | if (width > 2000 || height > 2000) { 41 | msg = '最大边不能大于2000像素' 42 | } 43 | if (msg) { 44 | return wx.showToast({ 45 | title: msg, 46 | icon: 'none' 47 | }) 48 | } else { 49 | this.goNextPage() 50 | } 51 | }, 52 | 53 | onShareAppMessage () { 54 | return { 55 | title: '证件照、免冠照、一寸照片、二寸照片、自定义尺寸、证件照换背景,免费生成、下载。', 56 | path: '/pages/index/index', 57 | imageUrl: '/shareShow.jpg' 58 | } 59 | }, 60 | 61 | onShareTimeline: function () { 62 | return { 63 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 64 | imageUrl: '/shareShow.jpg' 65 | } 66 | }, 67 | 68 | }) -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/custom/custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "免冠照/证件照尺寸定制", 3 | "disableScroll": true, 4 | "usingComponents": {} 5 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/custom/custom.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 宽度(像素 px) 6 | 7 | 8 | 9 | 高度(像素 px) 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/custom/custom.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100vh; 3 | display: flex; 4 | flex-direction: column; 5 | /* justify-content: space-between; */ 6 | } 7 | 8 | form { 9 | margin-top: 50rpx; 10 | } 11 | 12 | button.btn-confirm { 13 | width: 100% !important; 14 | height: 90rpx !important; 15 | padding: 0 !important; 16 | line-height: 90rpx !important; 17 | color: #fff !important; 18 | border-radius: 45rpx !important; 19 | background-color: #0081ff !important; 20 | font-weight: normal !important; 21 | margin-top: 30rpx; 22 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/images/card-people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/searchSize/images/card-people.png -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/images/custom-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/searchSize/images/custom-img.png -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/images/search-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuxiaojun666/certificate-photo/b88f28dd1a73470612852237b159e0a7856b86e9/miniprogram/pages/searchSize/images/search-input.png -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchSize.js: -------------------------------------------------------------------------------- 1 | const hideLoadView = true 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | category:"1", 9 | page:0, 10 | currentTab:0, 11 | photoSizeList:[], 12 | allRecords:[] 13 | }, 14 | //滑动切换 15 | swiperTab: function(e) { 16 | this.setData({ currentTab: e.detail.current }); 17 | }, 18 | //点击切换 19 | clickTab: function(e) { 20 | if (this.data.currentTab === e.target.dataset.current) return false; 21 | this.setData({ 22 | category: ['1', '2', '3', '4', null][e.target.dataset.current], 23 | page:0, 24 | currentTab: e.target.dataset.current, 25 | photoSizeList:[] 26 | }) 27 | this.requestdata() 28 | }, 29 | 30 | // 跳转到下一页 31 | goNextPage (e) { 32 | wx.navigateTo({ 33 | url: '/pages/preEdit/preEdit?index=' + e.currentTarget.dataset.index + '&data='+JSON.stringify(this.data.photoSizeList[e.currentTarget.dataset.index]) 34 | }) 35 | }, 36 | /** 37 | * 生命周期函数--监听页面加载 38 | */ 39 | onLoad: function (options) { 40 | // this.requestdata(); 41 | const {index} = options; 42 | this.clickTab({ target: { dataset: { current: index || '1' } } }) 43 | wx.setNavigationBarTitle({ title: '免冠照/证件照尺寸' }) 44 | }, 45 | // 跳转到搜索页面 46 | inputPush(){ 47 | wx.navigateTo({ url: './searchView/searchView' }) 48 | }, 49 | //获取数据 50 | requestdata (){ 51 | wx.showLoading({ 52 | title: '加载中...', 53 | }) 54 | const db = wx.cloud.database() 55 | const MAX_LIMIT = 20 56 | const num = this.data.page*MAX_LIMIT 57 | db.collection('photo_size').where({category_id:this.data.category}) 58 | .skip(num) 59 | .limit(MAX_LIMIT) 60 | .get({ 61 | success: res => { 62 | console.log(res) 63 | let arrNum = res.data.length 64 | console.log(arrNum); 65 | wx.hideLoading() 66 | this.setData({ 67 | photoSizeList:this.data.photoSizeList.concat(res.data), 68 | }); 69 | hideLoadView = arrNum !== 20 70 | } 71 | }) 72 | }, 73 | //加载更多 74 | moreclick(){ 75 | // 已经全部加载出来了 76 | if (hideLoadView==true) return 77 | this.setData({ page: this.data.page+=1 }) 78 | this.requestdata(); 79 | }, 80 | 81 | // 触底加载 82 | onReachBottom:function(){ 83 | this.moreclick(); 84 | }, 85 | /** 86 | * 用户点击右上角分享 87 | */ 88 | onShareAppMessage: function () { 89 | return { 90 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 91 | path: '/pages/index/index', 92 | imageUrl: '/shareShow.jpg' 93 | } 94 | }, 95 | 96 | onShareTimeline: function () { 97 | return { 98 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 99 | // path: '/pages/index/index', 100 | imageUrl: '/shareShow.jpg' 101 | } 102 | }, 103 | 104 | }) -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchSize.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "免冠照/证件照尺寸", 3 | "enablePullDownRefresh": true , 4 | "onReachBottomDistance":50, 5 | "usingComponents": { 6 | 7 | } 8 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchSize.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | {{ item.name }} 28 | {{item.width_px}}*{{item.height_px}} px | {{item.width_mm}}*{{item.height_mm}} mm 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchSize.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/index/index.wxss */ 2 | page { 3 | background: #fff 4 | } 5 | 6 | .top-fixed { 7 | position: fixed; 8 | left: 0; 9 | top: 0; 10 | width: 100%; 11 | padding: 30rpx; 12 | z-index: 999; 13 | background-color: #fff; 14 | } 15 | 16 | .swiper-tab { 17 | width: 100%; 18 | border-bottom: 2rpx solid rgba(0, 0, 0, 0); 19 | text-align: center; 20 | height: 88rpx; 21 | line-height: 88rpx; 22 | display: flex; 23 | flex-flow: row; 24 | justify-content: space-between; 25 | } 26 | 27 | .swiper-tab-item { 28 | width: 30%; 29 | color: #434343; 30 | /* border:1px solid #ccc; */ 31 | } 32 | 33 | .active { 34 | color: #3E85EE; 35 | border-bottom: 4rpx solid #3E85EE; 36 | } 37 | 38 | swiper { 39 | text-align: center; 40 | } 41 | swiper { 42 | text-align: center; 43 | } 44 | 45 | .container { 46 | padding: 30rpx; 47 | flex-direction: row; 48 | flex-wrap:wrap; 49 | justify-content:space-around; 50 | height: 100%; 51 | display: block; 52 | background-color: white; 53 | padding-top: 200rpx; 54 | margin-top: 15px; 55 | } 56 | ad { 57 | margin-bottom: 15rpx; 58 | } 59 | .container .chooseImage { 60 | margin-top: 50vh; 61 | transform: translateY(-100%); 62 | } 63 | .container .temp-image { 64 | width: 80vw; 65 | } 66 | .container .confirm-view { 67 | margin-top: 20px; 68 | } 69 | .container .confirm-view button { 70 | margin: 0 10px; 71 | } 72 | .card-view { 73 | display: flex; 74 | flex-direction: column; 75 | flex-wrap: nowrap; 76 | align-items: center; 77 | /* justify-content: space-between */ 78 | } 79 | .card { 80 | /* border: 1px solid #06ae56; */ 81 | width: 100%; 82 | padding: 30rpx; 83 | /* height: 300rpx; */ 84 | margin-bottom: 15rpx; 85 | /* float: left; */ 86 | border-radius: 10rpx; 87 | /* text-align: center; */ 88 | font-size: 26rpx; 89 | display: flex; 90 | flex-direction:column; 91 | /* overflow: hidden; */ 92 | box-sizing: border-box; 93 | /* border: 8rpx solid #fff; */ 94 | background-color: #eeeeee; 95 | } 96 | .card.custom { 97 | padding: 0; 98 | } 99 | .card.custom image { 100 | width: 100%; 101 | height: 120rpx; 102 | } 103 | .card .name { 104 | font-size: 36rpx; 105 | color: #0081ff; 106 | line-height: 2; 107 | } 108 | .card .size { 109 | font-size: 26rpx; 110 | line-height: 1.5; 111 | color: #666; 112 | } 113 | .card .discription { 114 | line-height: 1.4; 115 | font-size: 24rpx; 116 | color: #666; 117 | text-align:justify; 118 | /* padding: 8rpx 15rpx !important; */ 119 | } 120 | 121 | 122 | .hot-list-item { 123 | box-sizing: border-box; 124 | width: 100%; 125 | height: 130rpx; 126 | padding: 25rpx 40rpx; 127 | /* background: #DBEDFC; */ 128 | border: 1px solid #DBEDFC; 129 | border-radius: 10rpx; 130 | position: relative; 131 | margin-bottom: 20rpx; 132 | } 133 | .hot-list-item .title { 134 | font-size: 26rpx; 135 | } 136 | .hot-list-item .description { 137 | font-size: 24rpx; 138 | color: #919191; 139 | margin-top: 20rpx; 140 | } 141 | .hot-list-item image { 142 | position: absolute; 143 | width: 60rpx; 144 | height: 60rpx; 145 | right: 40rpx; 146 | top: 36rpx; 147 | } 148 | 149 | .transparent { 150 | width: 100%; 151 | height: 0; 152 | overflow: hidden; 153 | opacity: 0; 154 | /* margin-top: 100rpx; */ 155 | } 156 | 157 | button { 158 | 159 | box-shadow: 0 0 10rpx 5rpx rgba(67,142,219,.15); 160 | font-weight: normal !important; 161 | } 162 | 163 | 164 | .help-btn { 165 | position: fixed; 166 | z-index: 9999; 167 | right: 30rpx; 168 | bottom: 50rpx; 169 | width: 100rpx; 170 | height: 100rpx; 171 | line-height: 100rpx; 172 | border-radius: 50%; 173 | background-color: rgba(0,0,0,.8); 174 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchView/searchView.js: -------------------------------------------------------------------------------- 1 | 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | hideLoadView:true, 9 | showMenu:false, 10 | page:0, 11 | putText:"", 12 | photoSizeList:[], 13 | allRecords:[] 14 | }, 15 | //输入框值 16 | bindModel(e) { 17 | this.setData({ putText: e.detail.value }); 18 | if (this.data.putText.length==0) { 19 | this.setData({ 20 | photoSizeList:[], 21 | putText:"", 22 | showMenu:false, 23 | }) 24 | } 25 | }, 26 | 27 | // 点击搜索按钮 28 | searchClick:function () { 29 | this.setData({ 30 | showMenu:true, 31 | photoSizeList:[], 32 | page:0 33 | }); 34 | if (this.data.putText.length>0) { 35 | this.searchData(this.data.putText) 36 | } 37 | }, 38 | // 跳转到下一个页面 39 | goNextPage (e) { 40 | wx.navigateTo({ 41 | url: '/pages/preEdit/preEdit?index=' + e.currentTarget.dataset.index + '&data='+JSON.stringify(this.data.photoSizeList[e.currentTarget.dataset.index]) 42 | }) 43 | }, 44 | /** 45 | * 生命周期函数--监听页面加载 46 | */ 47 | onLoad: function (options) { 48 | wx.setNavigationBarTitle({ title: '搜索' }) 49 | }, 50 | // 键盘搜索事件 51 | confirm() { 52 | this.setData({ 53 | photoSizeList:[], 54 | page:0 55 | }); 56 | if (this.data.putText.length>0) { 57 | this.searchData(this.data.putText) 58 | } 59 | 60 | }, 61 | //搜索数据 62 | searchData(e){ 63 | wx.showLoading({ title: '搜索中...', }) 64 | const db = wx.cloud.database() 65 | const MAX_LIMIT = 20 66 | const num = this.data.page*MAX_LIMIT 67 | db.collection('photo_size').where({ 68 | name:{ 69 | $regex:'.*'+ this.data.putText, 70 | $options: 'i' 71 | } 72 | }) 73 | .skip(num) 74 | .limit(MAX_LIMIT) 75 | .get({ 76 | success: res => { 77 | wx.hideLoading() 78 | let arrNum = res.data.length 79 | this.setData({ 80 | photoSizeList:this.data.photoSizeList.concat(res.data), 81 | hideLoadView:(arrNum==20)?false:true 82 | }); 83 | } 84 | }) 85 | }, 86 | //加载更多 87 | moreclick(){ 88 | if (this.data.hideLoadView==true) { 89 | return 90 | } 91 | this.setData({ 92 | page:this.data.page+=1 93 | }) 94 | console.log("加载更多数据"+this.data.page+'页') 95 | this.searchData(this.data.putText); 96 | 97 | }, 98 | onReachBottom:function(){ 99 | this.moreclick(); 100 | }, 101 | 102 | /** 103 | * 用户点击右上角分享 104 | */ 105 | onShareAppMessage: function () { 106 | return { 107 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 108 | path: '/pages/index/index', 109 | imageUrl: '/shareShow.jpg' 110 | } 111 | }, 112 | 113 | onShareTimeline: function () { 114 | return { 115 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 116 | // path: '/pages/index/index', 117 | imageUrl: '/shareShow.jpg' 118 | } 119 | }, 120 | 121 | }) -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchView/searchView.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "搜索", 3 | "enablePullDownRefresh": true , 4 | "usingComponents": { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchView/searchView.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {{ item.name }} 23 | {{item.width_px}}*{{item.height_px}} px | {{item.width_mm}}*{{item.height_mm}} mm 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /miniprogram/pages/searchSize/searchView/searchView.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/index/index.wxss */ 2 | 3 | page { 4 | background: #fff 5 | } 6 | .container{ 7 | padding: 30rpx; 8 | } 9 | .searchView{ 10 | position: relative; 11 | margin-bottom: 20rpx; 12 | } 13 | .searchput{ 14 | width: 100%; 15 | height: 70rpx; 16 | padding-left: 30rpx; 17 | background-color:#fff; 18 | border-radius: 35rpx; 19 | border: 1px solid #3E85EE; 20 | } 21 | .searchBtn{ 22 | width: 70rpx; 23 | height: 70rpx; 24 | position: absolute; 25 | right: 0; 26 | top: 5rpx; 27 | z-index: 9; 28 | } 29 | 30 | 31 | .hot-list-item { 32 | box-sizing: border-box; 33 | width: 100%; 34 | height: 130rpx; 35 | padding: 25rpx 40rpx; 36 | background: #DBEDFC; 37 | border-radius: 10rpx; 38 | position: relative; 39 | margin-bottom: 20rpx; 40 | } 41 | .hot-list-item .title { 42 | font-size: 26rpx; 43 | } 44 | .hot-list-item .description { 45 | font-size: 24rpx; 46 | color: #919191; 47 | margin-top: 20rpx; 48 | } 49 | .hot-list-item image { 50 | position: absolute; 51 | width: 60rpx; 52 | height: 60rpx; 53 | right: 40rpx; 54 | top: 36rpx; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /miniprogram/pages/share/share.js: -------------------------------------------------------------------------------- 1 | // miniprogram/pages/share/share.js 2 | 3 | // 在页面中定义插屏广告 4 | let interstitialAd = null 5 | let shared = false 6 | const db = wx.cloud.database() 7 | 8 | Page({ 9 | 10 | /** 11 | * 页面的初始数据 12 | */ 13 | data: { 14 | allShareData: {}, 15 | invitedList: [], 16 | updateCountLoading: false, 17 | successUserList: [], 18 | showSubscribeBtn: false, 19 | subscribed: false 20 | }, 21 | 22 | // 邀请成功,领取次数 23 | shareSuccessUpdateCount (event) { 24 | if (this.data.updateCountLoading) return 25 | const openid = event.currentTarget.dataset.openid 26 | this.setData({ updateCountLoading: true }) 27 | wx.cloud.callFunction({ 28 | name: 'shareSuccessCallback', 29 | data: {openid} 30 | }).then(res => { 31 | this.setData({ updateCountLoading: false }) 32 | this.getData() 33 | }) 34 | }, 35 | 36 | /** 37 | * 生命周期函数--监听页面加载 38 | */ 39 | onLoad: function (options) { 40 | wx.setNavigationBarTitle({ title: '分享免冠照' }) 41 | 42 | // 在页面onLoad回调事件中创建插屏广告实例 43 | if (wx.createInterstitialAd) { 44 | interstitialAd = wx.createInterstitialAd({ 45 | adUnitId: 'adunit-7bd4afc44e5cebbd' 46 | }) 47 | interstitialAd.onLoad(() => {}) 48 | interstitialAd.onError((err) => {}) 49 | interstitialAd.onClose(() => {}) 50 | } 51 | }, 52 | 53 | /** 54 | * 生命周期函数--监听页面显示 55 | */ 56 | onShow: function () { 57 | this.getData() 58 | this.getSubscribeStatus() 59 | 60 | // 在适合的场景显示插屏广告 61 | if (interstitialAd) { 62 | interstitialAd.show().catch((err) => { 63 | console.error(err) 64 | }) 65 | } 66 | }, 67 | 68 | // 订阅邀请成功通知 69 | subscribeMessage () { 70 | const tmplIds = ["CNuffKDjmxEOU_hM44Cu0KoGqOjfdacpbk4LT1abcnE"] 71 | wx.requestSubscribeMessage({ 72 | tmplIds: tmplIds, 73 | success: (res) => { 74 | if (res[tmplIds[0]] === 'accept') { 75 | wx.showToast({ title: '订阅成功', }) 76 | this.setData({ subscribed: true }) 77 | } 78 | }, 79 | fail(error) { 80 | console.log(error) 81 | } 82 | }) 83 | }, 84 | 85 | // 获取订阅状态 86 | getSubscribeStatus () { 87 | db.collection('subscrib-message').where({ 88 | _openid: getApp().globalData.openid, 89 | tmplId: "CNuffKDjmxEOU_hM44Cu0KoGqOjfdacpbk4LT1abcnE" 90 | }).count({ 91 | success: (res)=> { 92 | this.setData({ subscribed: res.total > 0 }) 93 | }, 94 | fail: console.error 95 | }) 96 | }, 97 | 98 | // 是不是已经邀请过朋友,如果就展示邀请结果 99 | getData () { 100 | wx.showLoading({ title: '加载中', }) 101 | const db = wx.cloud.database() 102 | db.collection('share').where({ 103 | _openid: getApp().globalData.openid 104 | }).get().then(res => { 105 | wx.hideLoading({ complete: (res) => {}, }) 106 | console.log(res) 107 | if (!res.data[0]) { 108 | shared = false 109 | } else { 110 | shared = true 111 | this.setData({ 112 | allShareData: res.data[0], 113 | invitedList: res.data[0].invitedList 114 | }) 115 | this.getAvatar(res.data[0].invitedList || []) 116 | } 117 | }).catch(err => { 118 | wx.showToast({ 119 | title: 'err', 120 | icon: 'none' 121 | }) 122 | }) 123 | }, 124 | 125 | // 获取被邀请者的头像 126 | getAvatar (openidList = []) { 127 | if (!openidList.length) return 128 | const db = wx.cloud.database() 129 | const _ = db.command 130 | db.collection('user').where({ 131 | openid: _.or(openidList.map(v => _.eq(v))), 132 | avatarUrl: _.exists(true) 133 | }) 134 | .field({ 135 | avatarUrl: true, 136 | nickName: true 137 | }) 138 | .get().then(res => { 139 | this.setData({ 140 | successUserList: res.data 141 | }) 142 | }) 143 | }, 144 | 145 | /** 146 | * 用户点击右上角分享 147 | */ 148 | onShareAppMessage: function (res) { 149 | if (res.from === 'button') { 150 | const openid = getApp().globalData.openid 151 | if (!shared) this.share(openid) 152 | return { 153 | title: '证件照、免冠照、一寸照片、二寸照片、证件照换背景,免费生成、下载。', 154 | path: '/pages/index/index?shareOpenid=' + openid, 155 | imageUrl: '/shareShow.jpg' 156 | } 157 | } 158 | }, 159 | 160 | // 创建邀请记录 161 | share (openid) { 162 | const db = wx.cloud.database() 163 | db.collection('share').add({ 164 | data: { 165 | invitedList: [], 166 | openid, 167 | createAt: Date.now(), 168 | }, 169 | success: () => { 170 | shared = true 171 | } 172 | }) 173 | } 174 | }) -------------------------------------------------------------------------------- /miniprogram/pages/share/share.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /miniprogram/pages/share/share.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 成功邀请一位朋友 +1 次免费下载免冠照 7 | 8 | 9 | 10 | 11 | 12 | 13 | {{successUserList[index].nickName || '匿名好友'}} 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 还没有成功邀请好友。 28 | 被邀请者签到即算邀请成功。 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /miniprogram/pages/share/share.wxss: -------------------------------------------------------------------------------- 1 | /* miniprogram/pages/share/share.wxss */ 2 | .scroll-view { 3 | width: 100%; 4 | height: 100vh; 5 | overflow: auto; 6 | position: relative; 7 | background-color: #fff; 8 | -webkit-overflow-scrolling: touch; 9 | } 10 | 11 | ad { 12 | /* position: absolute; 13 | left: 0; 14 | bottom: 0; */ 15 | margin-bottom: 50rpx; 16 | } 17 | 18 | .scroll-view .jia3ci-btn { 19 | width: 6em; 20 | } 21 | 22 | button.share-btn[type='default'] { 23 | /* margin-top: 1em; */ 24 | /* background-color: transparent; */ 25 | color: #fff; 26 | } 27 | 28 | .cu-item .content { 29 | display: flex; 30 | flex-direction: row; 31 | align-items: center; 32 | } 33 | 34 | .cu-item .content .cu-avatar { 35 | margin-right: 10rpx; 36 | } 37 | 38 | .bg-gradual-green { 39 | margin-top: 30rpx; 40 | } -------------------------------------------------------------------------------- /miniprogram/pages/zipSuccess/zipSuccess.js: -------------------------------------------------------------------------------- 1 | // pages/zipSuccess/zipSuccess.ts 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | imageSrc:'' 9 | }, 10 | 11 | /** 12 | * 生命周期函数--监听页面加载 13 | */ 14 | onLoad: async function (options) { 15 | const {imgurl} = options 16 | this.setData({ imageSrc:imgurl }) 17 | const { tempFilePath, dataLength } = await this.downloadImg(imgurl) 18 | const size = (dataLength / 1024).toFixed(2) 19 | this.setData({ size: size }) 20 | }, 21 | 22 | // 将远端图片,下载到本地 23 | downloadImg(url) { 24 | return new Promise((resolve, reject) => { 25 | wx.downloadFile({ 26 | url, 27 | success(res) { 28 | if (res.statusCode === 200) { 29 | resolve(res) 30 | } else { 31 | reject(res) 32 | } 33 | }, 34 | fail(error) { 35 | reject(error) 36 | } 37 | }) 38 | }) 39 | }, 40 | /** 41 | * 生命周期函数--监听页面初次渲染完成 42 | */ 43 | onReady() { 44 | 45 | }, 46 | 47 | /** 48 | * 生命周期函数--监听页面显示 49 | */ 50 | onShow() { 51 | 52 | }, 53 | 54 | /** 55 | * 生命周期函数--监听页面隐藏 56 | */ 57 | onHide() { 58 | 59 | }, 60 | 61 | /** 62 | * 生命周期函数--监听页面卸载 63 | */ 64 | onUnload() { 65 | 66 | }, 67 | 68 | /** 69 | * 页面相关事件处理函数--监听用户下拉动作 70 | */ 71 | onPullDownRefresh() { 72 | 73 | }, 74 | 75 | /** 76 | * 页面上拉触底事件的处理函数 77 | */ 78 | onReachBottom() { 79 | 80 | }, 81 | 82 | /** 83 | * 用户点击右上角分享 84 | */ 85 | onShareAppMessage() { 86 | 87 | }, 88 | preView (event) { 89 | wx.previewImage({ 90 | urls: [this.data.imageSrc], 91 | current: this.data.imageSrc 92 | }) 93 | }, 94 | // 下载 95 | downloadClick(){ 96 | if (!this.data.imageSrc) { 97 | wx.showToast({ 98 | title: '请上传图片', 99 | icon: 'none', 100 | duration: 3000 101 | }) 102 | }else{ 103 | console.log(this.data.imageSrc); 104 | var url = this.data.imageSrc; 105 | wx.downloadFile({ 106 | url: url, 107 | success: function (res) { 108 | console.log(res); 109 | //图片保存到本地 110 | wx.saveImageToPhotosAlbum({ 111 | filePath: res.tempFilePath, 112 | success: function (data) { 113 | wx.showToast({ 114 | title: '保存成功', 115 | icon: 'success', 116 | duration: 3000 117 | }) 118 | setTimeout(() => { 119 | wx.reLaunch({ 120 | url: '/pages/index/index' 121 | }) 122 | }, 3000) 123 | }, 124 | fail: function (err) { 125 | console.log(err); 126 | if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { 127 | console.log("当初用户拒绝,再次发起授权") 128 | wx.openSetting({ 129 | success(settingdata) { 130 | console.log(settingdata) 131 | if (settingdata.authSetting['scope.writePhotosAlbum']) { 132 | console.log('获取权限成功,给出再次点击图片保存到相册的提示。') 133 | } else { 134 | console.log('获取权限失败,给出不给权限就无法正常使用的提示') 135 | } 136 | } 137 | }) 138 | } 139 | }, 140 | complete(res){ 141 | console.log(res); 142 | } 143 | }) 144 | } 145 | }) 146 | } 147 | 148 | } 149 | }) -------------------------------------------------------------------------------- /miniprogram/pages/zipSuccess/zipSuccess.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "图片压缩", 3 | "usingComponents": { 4 | "page-scroll-message": "/components/pageScrollMessage/pageScrollMessage" 5 | } 6 | } -------------------------------------------------------------------------------- /miniprogram/pages/zipSuccess/zipSuccess.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 压缩后图片占用{{size}}kb 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /miniprogram/pages/zipSuccess/zipSuccess.wxss: -------------------------------------------------------------------------------- 1 | /* pages/zipSuccess/zipSuccess.wxss *//* pages/imgZip/imgZip.wxss */ 2 | .container { 3 | width: 100%; 4 | height: 100%; 5 | /* overflow: hidden; */ 6 | } 7 | .backView{ 8 | margin-top: 30rpx; 9 | width: 85%; 10 | height: 700rpx; 11 | /* background-color: #999999; */ 12 | border: 1px solid #ccc; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | } 17 | .backView image { 18 | width: 100%; 19 | height: 100%; 20 | max-width: 100%; 21 | max-height: 100%; 22 | } 23 | .downloadBtn{ 24 | background-color: #3E85EE; 25 | margin-top: 20px; 26 | color: white; 27 | width: 90%; 28 | } -------------------------------------------------------------------------------- /miniprogram/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [ 4 | { 5 | "action": "allow", 6 | "page": "/pages/index/index" 7 | }, 8 | { 9 | "action": "allow", 10 | "page": "/pages/searchSize/searchSize" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "miniprogram/", 3 | "cloudfunctionRoot": "cloudfunctions/", 4 | "setting": { 5 | "urlCheck": false, 6 | "es6": true, 7 | "enhance": true, 8 | "postcss": true, 9 | "preloadBackgroundData": false, 10 | "minified": true, 11 | "newFeature": true, 12 | "coverView": true, 13 | "nodeModules": true, 14 | "autoAudits": false, 15 | "showShadowRootInWxmlPanel": true, 16 | "scopeDataCheck": false, 17 | "uglifyFileName": true, 18 | "checkInvalidKey": true, 19 | "checkSiteMap": true, 20 | "uploadWithSourceMap": true, 21 | "compileHotReLoad": true, 22 | "useMultiFrameRuntime": true, 23 | "useApiHook": true, 24 | "useApiHostProcess": true, 25 | "babelSetting": { 26 | "ignore": [], 27 | "disablePlugins": [], 28 | "outputPath": "" 29 | }, 30 | "enableEngineNative": false, 31 | "useIsolateContext": false, 32 | "userConfirmedBundleSwitch": true, 33 | "packNpmManually": false, 34 | "packNpmRelationList": [], 35 | "minifyWXSS": true, 36 | "disableUseStrict": false, 37 | "minifyWXML": true, 38 | "showES6CompileOption": false, 39 | "useCompilerPlugins": [ 40 | "typescript", 41 | "less" 42 | ], 43 | "lazyloadPlaceholderEnable": false, 44 | "useStaticServer": true, 45 | "condition": true 46 | }, 47 | "appid": "wxbe6c30a61a51b422", 48 | "projectname": "证件照微信小程序", 49 | "libVersion": "2.14.1", 50 | "simulatorType": "wechat", 51 | "simulatorPluginLibVersion": { 52 | "wxext14566970e7e9f62": "2.27.3" 53 | }, 54 | "compileType": "miniprogram", 55 | "srcMiniprogramRoot": "miniprogram/", 56 | "packOptions": { 57 | "ignore": [], 58 | "include": [] 59 | }, 60 | "editorSetting": { 61 | "tabIndent": "insertSpaces", 62 | "tabSize": 2 63 | }, 64 | "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", 65 | "condition": {}, 66 | "projectArchitecture": "multiPlatform" 67 | } -------------------------------------------------------------------------------- /project.miniapp.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniVersion": "v2", 3 | "name": "免冠照片", 4 | "version": "0.0.1", 5 | "mini-android": { 6 | "sdkVersion": "1.0.21", 7 | "toolkitVersion": "0.10.0", 8 | "useExtendedSdk": { 9 | "media": false, 10 | "bluetooth": false, 11 | "network": false, 12 | "scanner": false, 13 | "xweb": false 14 | }, 15 | "icons": { 16 | "hdpi": "", 17 | "xhdpi": "", 18 | "xxhdpi": "", 19 | "xxxhdpi": "" 20 | }, 21 | "splashscreen": { 22 | "hdpi": "", 23 | "xhdpi": "", 24 | "xxhdpi": "" 25 | }, 26 | "enableVConsole": "open", 27 | "privacy": { 28 | "enable": true 29 | } 30 | }, 31 | "mini-ios": { 32 | "sdkVersion": "1.1.4", 33 | "toolkitVersion": "0.0.9", 34 | "useExtendedSdk": { 35 | "WeAppOpenFuns": true, 36 | "WeAppNetwork": false, 37 | "WeAppBluetooth": false, 38 | "WeAppMedia": false, 39 | "WeAppLBS": false, 40 | "WeAppOthers": false 41 | }, 42 | "enableVConsole": "open", 43 | "icons": { 44 | "mainIcon120": "", 45 | "mainIcon180": "", 46 | "spotlightIcon80": "", 47 | "spotlightIcon120": "", 48 | "settingsIcon58": "", 49 | "settingsIcon87": "", 50 | "notificationIcon40": "", 51 | "notificationIcon60": "", 52 | "appStore1024": "" 53 | }, 54 | "splashScreen": { 55 | "customImage": "" 56 | }, 57 | "privacy": { 58 | "enable": false 59 | }, 60 | "enableOpenUrlNavigate": true, 61 | "tpush": { 62 | "accessKey": "5am+m5q/ls" 63 | } 64 | }, 65 | "versionCode": 100 66 | } -------------------------------------------------------------------------------- /project.private.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectname": "证件照开源免费版", 3 | "setting": { 4 | "compileHotReLoad": true 5 | }, 6 | "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", 7 | "libVersion": "2.30.2" 8 | } --------------------------------------------------------------------------------