随着科技的发展,人工智能将会是以后的主流方向,ai绘图、chatgpt高科技先后出台,都获得了火爆的发展,因此想借着势头开发一款主打ai绘图的app+小程序
一、前期准备
1、选定技术框架
因为后面需要多平台发布,而尽可能减少开发成本,因此选定了uniapp框架作为开发框架,前期目标是 android、ios以及 微信小程序三个平台,android平台名称:易绘,IOS端名称:易绘ai作画,微信小程序端:栾青易绘ai绘画。
2、ai绘图的api选择
有两个方向,一个是百度云,一个是腾讯云,各有优劣,价格方面腾讯云更优惠,但百度推出的时间更早。因为前期接入的是百度云的sdk,所以还是选择了百度云sdk。
3、功能整合
主打ai绘图(用文字生成图片),次功能头像制作、老照片修复处理,真人动漫化头像,二次元图片,壁纸美图。开放了一部分功能免费使用,以留住用户。
二、ai绘图
<template><view><uni-nav-bar :statusBar="true" :border="true" :fixed="true" :leftWidth="500"><template v-slot:left class="uni_flex_row_align_left"><view class="uni_flex_row_align_left uni_14px uni_font_medium ml14" v-if="isLogined" @click="gotoTaskList"><image src="@/static/icon/icon_task.png" style="width: 40rpx;height: 40rpx;margin-left: 10rpx;"></image><text class="uni_15px ml6 uni_color_666">任务列表</text><text class="ml4 mr2">(</text><text class="uni_15px" style="color: #f43533;">{{taskNum}}</text><text class="ml2">)</text></view><view v-else class="uni_14px uni_font_regular ml14 uni_color_666">用文字绘出梦想,用梦想成就未来</view></template></uni-nav-bar><!-- 主绘制面板视图 --><view class="page_root"><view style="width: 690rpx;height: 15rpx;"></view><!-- 输入框内容 START --><view class="input_box"><!-- 文本输入框 --><textarea class="textarea_style uni_font_light" :maxlength="maxNum" placeholder="输入你的奇思妙想,AI智能为你作画" v-model="inputContent"></textarea><!-- 信息操作栏 START --><view class="input_action_bar"><view class="uni_flex_row_align_left" @click="startVoice"><uni-icons type="mic-filled" color="#9686FB" size="19"></uni-icons><text class="uni_14px ml4 uni_font_regular input_func_text" style="color: #9686FB;">语音输入</text></view><view class="uni_flex_row_align_right"><view class="uni_flex_row_align_left" v-if="inputContent"><text class="num g1">{{inputContent.length || 0}}</text><text class="num g2 ml4 mr4">/</text><text class="num g2">{{maxNum}}</text></view><view class="uni_flex_row_align_right" v-else @click="gotoSchool"><uni-icons type="help" size="21" color="#9686FB"></uni-icons><text class="uni_14px ml4 uni_font_regular input_func_text" style="color: #9686FB;">教程帮助</text></view><view class="sp_line" v-if="inputContent"></view><!-- 清除 --><uni-icons type="clear" v-if="inputContent" color="#9686FB" size="19" @click="onClear" click="ml10"></uni-icons><!-- 复制 --><uni-icons type="paperclip" v-if="inputContent" color="#9686FB" size="19" class="ml20" @click="onCopy"></uni-icons></view></view><!-- 信息操作栏 START --></view><!-- 输入框内容 END --><!-- 示例 START --><view class="uni_flex_row_between mt6" v-if="exampleObj.id"><!-- 示例内容 --><text class="tip_text" @click="onClickExample">示例:{{exampleObj.content}}</text><!-- 刷新示例数据 --><uni-icons :animation="animationData" type="loop" color="#9686FB" class="refresh_icon" size="18" @click="queryExample"></uni-icons></view><!-- 示例 END --><!-- 关键字模块 START --><view class="mt18 uni_flex_row_between_center"><view class="uni_flex_row_align_left"><image src="../../static/icon/icon_keyword.png" style="width: 36rpx; height: 36rpx;"></image><text class="ml4 uni_16px uni_color_666">可以试试</text></view><view class="uni_flex_row_align_right"><uni-icons type="loop" color="#9686FB" size="17" @click="queryStyles"></uni-icons><text class="ml4 uni_13px change_next" @click="queryStyles">换一批</text></view></view><view class="uni_flex_warp uni_flex_row_between mt8"><view v-for="(keyword,id) in keywords" v-bind:key="id" class="tag_item" @click="onClickKeyword(keyword)">{{keyword.name}}</view></view><view class="uni_flex_row_align_left mt20"><text class="uni_16px uni_color_666">高级设置</text><switch style="transform: scale(0.6);" color="#9686FB" :checked="isShowHeightSetting" @change="setSettingOfShowHeight"></switch></view><!-- 画作风格 START --><view class="uni_flex_row_between mt10" v-if="isShowHeightSetting"><view class="uni_flex_row_align_left"><image src="../../static/icon/icon_style.png" style="width: 34rpx;height: 34rpx;"></image><text class="ml4 uni_15px uni_color_666">绘图风格</text><text class="ml4 uni_11px uni_color_bbb">(可选,默认不限风格)</text></view><view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'style')"><text class="uni_14px uni_font_regular uni_color_app1">{{imageStyle.tag || '请选择风格'}}</text><uni-icons type="right" color="#9686FB" class="ml6"></uni-icons></view></view><!-- 画作风格 END --><!-- 生成张数 START --><view class="uni_flex_row_between mt12" v-if="isShowHeightSetting"><view class="uni_flex_row_align_left"><image src="../../static/icon/icon_number.png" style="width: 34rpx;height: 34rpx;"></image><text class="ml4 uni_15px uni_color_666">图片生成张数</text><text class="ml4 uni_11px uni_color_bbb">(可选,默认1张)</text></view><view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'number')"><text class="uni_14px uni_font_regular uni_color_app1">{{createAiNumber + ' 张' || '请选择张数'}}</text><uni-icons type="right" color="#9686FB" class="ml6"></uni-icons></view></view><!-- 生成张数 END --><!-- 画作分辨率 START --><view class="uni_flex_row_between mt12" v-if="isShowHeightSetting"><view class="uni_flex_row_align_left"><image src="../../static/icon/icon_ratio.png" style="width: 34rpx;height: 34rpx;"></image><text class="ml4 uni_15px uni_color_666">分辨率</text><text class="ml4 uni_11px uni_color_bbb">(可选,默认1024*1536)</text></view><view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'ratio')"><text class="uni_14px uni_font_regular uni_color_app1">{{imageRatio.tag || '请选择分辨率'}}</text><uni-icons type="right" color="#9686FB" class="ml6"></uni-icons></view></view><!-- 画作分辨率 END --><!-- 开始绘画按钮 --><view class="uni_flex_row_align_center mt30"><view class="start_button able" @click="onCheckStatusAndShowDialog"><text class="uni_font_regular" style="letter-spacing: 2rpx;">{{isLogined ? 'Ai 绘画创作':'登录后使用Ai绘画功能'}}</text></view><uni-badge :text="freeNum" absolute="rightTop" size="normal" :offset="[5,5]"><view v-if="freeNum>0" class="start_test_btn" @click="showTestDialog(true)">免费体验</view><view v-else class="start_test_btn_unable">暂不可用</view></uni-badge></view><!-- 客服信息 --><view class="customer uni_12px mt10 uni_color_999"><text @click="onCopy('729913920')">QQ交流学习群:729913920 (点击复制)</text></view></view><!-- 数据选择弹框 --><uni-popup type="bottom" ref="pickerDataObj"><view class="popup_dialog_style"><view class="popup_dialog_title"><text class="uni_color_999 uni_font_regular" @click="showPickerDataObj(false)">取消</text><text class="uni_color_000 uni_font_medium" v-if="dialogType === 'number'">请选择画作生成张数</text><text class="uni_color_000 uni_font_medium" v-else-if="dialogType === 'style'">请选择画作风格</text><text class="uni_color_000 uni_font_medium" v-else-if="dialogType === 'ratio'">请选择画作分辨率</text><text class="uni_color_222 uni_font_regular" @click="onPickerData('',true)">确定</text></view><view class="uni_flex_warp uni_flex_row_between dialog_data_view" v-if="dialogType === 'number'"><textv-if="dialogType === 'number'"v-for="(dateItem, index) in dialogData" v-bind:key="index" class="style_item_big" :class="tempImageNumber === dateItem ? 'style_checked':''"@click="onPickerData(dateItem)">{{dateItem}}张</text></view><view class="uni_flex_warp uni_flex_row_align_left" v-if="dialogType === 'style'"><textv-for="(ratio,index) in styles" v-bind:key="index" class="style_item_big" :class="tempImageStyle.id === ratio.id ? 'style_checked':''"@click="onPickerData(ratio)">{{ratio.tag}}</text></view><view class="uni_flex_warp uni_flex_row_between" v-if="dialogType === 'ratio'"><textv-for="(ratio,index) in ratios" v-bind:key="index" class="style_item_big" :class="tempImageRatio.id === ratio.id ? 'style_checked':''"@click="onPickerData(ratio)">{{ratio.tag}}</text></view></view></uni-popup> <!-- 开始AI绘图任务后的状态展示弹窗 --><uni-popup ref="taskStatusDialog" type="center"><view class="task_status_dialog"><image src="../../static/img_ai_status_dialog.gif" style="width: 400rpx;height: 160rpx;"></image><view class="uni_14px uni_font_regular">{{aiRequestStatusText}}</view><view class="uni_flex_row_align_center uni_flex_warp mt10 ml10 mr10"><view v-for="(wait,wid) in waitTime" v-bind:key="wid" class="white_point"></view></view><view class="mt4 uni_16px uni_font_medium">{{waitTime.length}}s</view></view></uni-popup><uni-popup ref="dialogTest" type="bottom"><view class="test_dialog uni_flex_col_align_center"><view class="uni_font_medium uni_17px uni_color_333 mt10 mb15">免费体验说明</view><text class="uni_15px uni_color_999 ml5 mr5" style="letter-spacing: 2rpx;">全系统今日总共还<text class="uni_font_regular uni_color_666">剩余{{freeNum}}次免费体验机会,</text>系统每天晚上20点下放随机数量的免费体验次数,当天用完即止,每个账号可先到先得免费使用一次ai绘图功能(注:为避免占用,每个账号每天有且只有一次免费体验机会)</text><text class="dialog_btn bg1 mt30" @click="onConfirmTest">立即免费创作</text><text class="dialog_btn bg3 mt20 mb15" @click="showTestDialog(false)">取消</text></view></uni-popup> <!-- 提示信息弹窗 --><uni-popup ref="message" type="message"><uni-popup-message type="error" :message="messageText" :duration="3200"></uni-popup-message></uni-popup><uni-popup ref="confirmDialog" type="center"><view class="uni_flex_col_align_center"><view class="confirm_dialog_bg mb10"><view class="uni_18px uni_color_000 uni_font_medium mt10 mb15">温馨提示</view><view class="uni_16px uni_color_666 ml10 mr10" style="letter-spacing: 2rpx;"><text>您正在使用易绘的Ai绘画功能,此功能按次收费,本次操作将从您的易绘账号上</text><text class="uni_16px uni_font_regular ml2 uni_color_red">扣除 {{price * createAiNumber}} 钻石</text><text class="ml2">({{createAiNumber}}张图) 是否已知悉并确认?</text></view><!-- <view class="uni_16px uni_color_666" v-if="createAiNumber <= 1">您正在使用Ai绘画功能,AI绘图功能单次需扣除 {{price}} 钻石,是否确定?</view> --><!-- <view class="uni_16px uni_color_666" v-else>您正在使用Ai绘画功能,当前您设置了同时生成{{createAiNumber}}张图片,本次需扣除 {{price * createAiNumber}} 钻石,是否确定并开始绘画?</view> --><text class="dialog_btn bg1 mt30 mb20" @click="onClickStartButton('pay')">确认并开始绘画</text><text v-if="couponNum>0" class="dialog_btn bg2 mb25" @click="onClickStartButton('coupon')">使用免费券抵扣<text class="uni_12px uni_color_fff uni_font_light">({{couponNum}}张)</text></text><!-- <text class="dialog_btn bg3 mt20 mb15" @click="showTestDialog(false)">取消绘画</text> --></view><uni-icons type="close" size="45" color="#ffffff" @click="showAiConfirmDialog(false)"></uni-icons></view></uni-popup></view>
</template><script>import api from '../../common/network/api/api.js';import dataBase from '../../common/database.js';import UI from '../../common/UI.js';import handle from '../../common/handle.js';export default {data() {return {maxNum: 100,inputContent: '', // 输入的描述内容errorTip: '', // 错误提示exampleObj: {}, // 示例tempImageRatio: {},tempImageStyle: {},tempImageNumber: 1,imageStyle: {},imageRatio: {},keywords: [], // 关键字列表// simpleKeyWords:[], // 简单列表的关键字styles: [], // 风格列表ratios: [] ,// 分辨率列表countAiTask: undefined, // ai绘图状态弹窗的计时器waitTime: [], // ai绘图状态弹窗的 等待时间计时waiting: '', // 预估等待时间ableReload: true, // 是否可以重新加载aiRequestStatusText: '', // AI请求messageTimeoutTask: undefined, // 临时消息的计时器对象aiStatus: 0, // AI绘图状态 0|队列中 1|已完成aiId: '', // 唯一IDtaskNum: 0,createAiNumber: 1, // 申请AI绘画的图片数量isLogined: false,price: 0,dialogType: '',dialogData: [],animationData:{},freeNum: 0, // 系统免费ai体验次数isShowHeightSetting: false,}},onShow() {this.init();this.getSettingOfShowHeight();console.error("环境:",process.env.NODE_ENV)},onLoad() {uni.$on('networkResume',()=>{this.init();});},onUnload() {uni.$off('networkResume');this.showTaskStatusDialog(false);},methods: {getSettingOfShowHeight(){let temp = dataBase.queryStorage('heightSetting') || false;this.isShowHeightSetting = temp;return temp;},setSettingOfShowHeight(e){this.isShowHeightSetting = e.detail.value;dataBase.insertStorage('heightSetting',e.detail.value);},showAiConfirmDialog(show){if(show)this.$refs.confirmDialog.open();elsethis.$refs.confirmDialog.close();},// 确定体验onConfirmTest(){this.showTestDialog(false);},showTestDialog(show){if(show){this.onCheckStatusAndShowDialog('free');// this.$refs.dialogTest.open();}else{this.$refs.dialogTest.close();}},init(){// 允许重新加载this.ableReload = true;this.isLogined = dataBase.queryLoginStatus();this.queryStyles(); // 查询风格/分辨率等this.queryExample(); // 查询示例if(this.isLogined){this.queryTaskNum(); // 查询任务数据api.doPost({action:'queryUnreadMessageNumber'}).then(res=>{let unread = res.data.unread;unread > 0 ? uni.showTabBarRedDot({index: 4}) : uni.hideTabBarRedDot({index: 4});});} },gotoSchool(){console.error("前往学院");uni.navigateTo({url:'/pages_sub/center/pages/school/school'});},// 前往任务列表页面gotoTaskList(){uni.navigateTo({url:'/pages_sub/ai/pages/ai-task-list/ai-task-list'});},// 查询任务数量queryTaskNum(){api.doPost({action:'queryAITaskNumOrList'}).then(res=>{this.taskNum = res.data.num;this.couponNum = res.data.freeConpon;});},// 点击主类关键字onClickKeyword(item){if(this.inputContent && this.inputContent.length > 0){let lastWord = this.inputContent.substring(this.inputContent.length - 1, this.inputContent.length);if(lastWord === ','){this.inputContent = this.inputContent.substring(0, this.inputContent.length - 1);}}this.inputContent += (this.inputContent ? ',':'') + item.name;// 限制字数if(this.inputContent.length > this.maxNum){this.inputContent = this.inputContent.substring(0, this.maxNum);}},// 选择风格onPickerData(style, confirm){if(confirm){this.showPickerDataObj(false);switch(this.dialogType){case 'number': this.createAiNumber = this.tempImageNumber; break;case 'style': this.imageStyle = this.tempImageStyle; break;case 'ratio': this.imageRatio = this.tempImageRatio; break;}}else{switch(this.dialogType){case 'number': this.tempImageNumber = style; break;case 'style': this.tempImageStyle = style; break;case 'ratio': this.tempImageRatio = style; break;}}},// 显示和隐藏任务进度弹窗showTaskStatusDialog(show){const $that = this;if(this.countAiTask){clearInterval(this.countAiTask);}if(show){this.waitTime = [];$that.countAiTask = setInterval(()=>{$that.waitTime.push("");// 是否可以重新加载if($that.ableReload){$that.queryAiResult();}}, 2000);$that.$refs.taskStatusDialog.open();}else{$that.inputContent = '';$that.$refs.taskStatusDialog.close();}},// 检查条件以及展示价格确认onCheckStatusAndShowDialog(mode){const $that = this;// 未登录if(!this.isLogined){uni.showModal({title: '提示',content: '需要登录才能使用此功能,是否前往登录?',confirmText: '前往登录',cancelText: '暂不登录',complete(res) {if(res.confirm){uni.navigateTo({url:'/pages_sub/login/pages/login/login'});return "NO";}}});return "NO";}if(!this.inputContent){this.messageText = '请在下方输入画作的文字描述'this.$refs.message.open();return "NO";}if(this.price > 0 && mode !== 'free'){// 开始ai的确认弹窗this.showAiConfirmDialog(true);return "NO";}$that.onClickStartButton(mode);return "OK"},// 开始AI绘画onClickStartButton(mode){// 隐藏确认对话框this.showAiConfirmDialog(false);uni.showLoading({title:'正在提交云端',});let data = {action:'queryAiTask',text: this.inputContent, ratio: this.imageRatio.tag, style: this.imageStyle.tag, num:this.createAiNumber,mode: mode // 免费 付费};api.doPost(data).then(res=>{uni.hideLoading();if(res.code === 208){handle.gotoRecharge(res.message);return;}this.showTaskStatusDialog(true);this.aiStatus = res.data.status;this.aiId = res.data.aid;this.aiRequestStatusText = '已成功连接云端';// 队列中if(this.aiStatus === 0){this.waiting = res.data.waiting;this.aiRequestStatusText = "正在刻画绘图...,预估时长【"+res.data.waiting+"】";this.queryTaskNum();}// 已完成else{this.aiRequestStatusText = '已完成AI绘图作画任务';this.showTaskStatusDialog(false);console.error("AI绘图完成",res.data);uni.navigateTo({url:'/pages_sub/ai/pages/preview-ai-img/preview-ai-img?data='+JSON.stringify(res.data)});}},err=>{uni.hideLoading();});},// 单纯查询ai绘图状态queryAiResult(){this.ableReload = false;console.error("AI任务查询开始");api.doPost({action:'queryAiResult',aid:this.aiId}).then(res=>{console.error("AI任务查询结果:",res);let code = res.code;if(code === 200){this.showTaskStatusDialog(false);uni.navigateTo({url:'/pages_sub/ai/pages/preview-ai-img/preview-ai-img?data='+JSON.stringify(res.data)});}else{this.ableReload = true;}}, err=>{console.error("AI任务查询失败:",err);this.ableReload = false;this.showTaskStatusDialog(false);}).catch(e=>{console.error("AI任务发生异常:",e);});},// 清除内容onClear(){this.inputContent = '';},// 复制内容onCopy(text){uni.setClipboardData({data: this.inputContent,success() {uni.showToast({title:'复制成功~',icon:'success'});}});},// 显示/关闭数据选择弹窗showPickerDataObj(show, type){if(show){this.dialogType = type;switch(this.dialogType){case 'number': this.dialogData = [1,2,3,4,5,6]; break;case 'style': this.dialogData = this.styles; break;case 'ratio': this.dialogData = this.ratios; break;}this.$refs.pickerDataObj.open();}else{this.$refs.pickerDataObj.close();}},// 点击示例文字时,会把示例内容带入到输入框里onClickExample(){const $that = this;// 判断示例是否存在内容if(this.exampleObj.content){// 如果输入框内已有内容,就弹出对话框提示用户是否确定要用示例替换if(this.inputContent){uni.showModal({title:'温馨提示',content:"是否确定要用示例的内容「替换」输入框描述内容?",confirmText: '确认替换',cancelText: '取消',complete(res) {if(res.confirm){$that.inputContent = $that.exampleObj.content;}}})}else{this.inputContent = this.exampleObj.content;}}},// 加载/刷新示例queryExample(){let data = {action:'queryExample',}// 判断是否有idif(this.exampleObj && this.exampleObj.id){data.id = this.exampleObj.id;}api.doPost(data).then(res=>{this.exampleObj = res.data;});},// 查询风格列表queryStyles(){api.doPost({action:'queryStyles'}).then(res=>{this.styles = res.data.styles || [];this.ratios = res.data.ratios || [];this.keywords = res.data.keywords || [];this.price = res.data.price || 0;this.freeNum = res.data.freeNum;if(this.styles && this.styles.length > 0){this.imageStyle = this.styles[0];this.tempImageStyle = this.styles[0];}if(this.ratios && this.ratios.length > 0){this.imageRatio = this.ratios[0];this.tempImageRatio = this.ratios[0];}});},startVoice() {if(!this.isLogined){UI.gotoLoginWithComfirmDialog();return;}// #ifdef APP-PLUS// 开始语音识别const $that = this;plus.speech.startRecognize({engine: 'baidu'}, (e => {if (e) {$that.inputContent += e;// 限制字数if($that.inputContent.length > $that.maxNum){$that.inputContent = $that.inputContent.substring(0, $that.maxNum);}}}), (e => {if (e.code === 1310722) {uni.showToast({title: '抱歉,没有听清,请您提高音量再试一遍吧',icon: 'none'})}}))// #endif}}}
</script><style>
@import url('index.css');
</style>
三、成品展示图
下载链接: Android apk
苹果链接: AppStore链接
三、登录页
<template><view class="page pageStyle"><view class="uni_flex_col_align_center flexX margin-top-100"><view class="uni_flex_col_align_center margin-bottom-50"><image src="/static/app_logo200.png" class="iconimg"></image><view class="app_name uni_font_medium uni_25px">{{appName}}</view><view class="app_version uni-font-regular">{{version}}</view></view><!-- #ifdef MP-WEIXIN --><button @click="getuserinfo" withCredentials="true" class="btn_func uni_flex_row_align_center uni_font_medium">微信授权登陆</button><view class="btn_func_un uni_flex_row_align_center uni-font-regular" @click="finishPage">暂不登录</view><!-- #endif --><!-- #ifdef APP-PLUS --><view v-if="isShowWX" class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('app-wx')"><image class="icon_wechat" src="/pages_sub/login/static/icon_share_wx.png"></image><text style="letter-spacing: 4rpx;">微信授权登录</text></view><view v-if="isCanPhoneNumberLogin" class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('mobile')"><uni-icons type="phone-filled" color="#E00300" size="20"></uni-icons><view style="margin-left: 15rpx;">手机号一键登录</view></view><view class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('apple')" v-if="isApple"><image class="icon_apple" src="/pages_sub/login/static/icon_apple.png"></image><view>通过 Apple 登录</view></view><view class="btn_func_un uni_flex_row_align_center uni-font-regular" @click="finishPage">暂不登录</view><!-- #endif --><!-- #ifdef H5 --><!-- 下载按钮 --><view><view class="uni_flex_row_align_center download_btn bbg1" @click="download(1)"><image style="width: 45rpx;height: 45rpx;" class="mr4" src="https://mp-4600ec1f-16af-433c-a045-130f8b08315f.cdn.bspapp.com/cloudstorage/3122fd08-63b0-42cd-91f1-f11421e72389.png"></image><text class="uni_15px uni_font_regular" style="letter-spacing: 2rpx;">下载App (安卓)</text></view><view class="uni_flex_row_align_center download_btn bbg2" @click="download(2)"><image style="width: 40rpx;height: 40rpx;" class="mr4" src="https://mp-4600ec1f-16af-433c-a045-130f8b08315f.cdn.bspapp.com/cloudstorage/a8074492-ae50-42ad-ba0e-5399354ad9c6.png"></image><text class="uni_15px uni_font_regular" style="letter-spacing: 2rpx;">下载App (苹果)</text></view><view class="uni_flex_col_align_center mt15"><image style="width: 288rpx;height: 288rpx;" class="mr4" src="https://luanqing.oss-cn-shanghai.aliyuncs.com/icon/yihui/min_code_yihui.jpeg"></image><text class="uni_15px uni_font_regular mt6" style="letter-spacing: 2rpx;">微信小程序码</text></view></view><!-- #endif --></view><!-- #ifdef APP-PLUS --><view class="right uni_flex_col_align_center flexS"><view class="uni_flex_row_align_center uni_14px uni-font-regular"><checkbox style="transform: scale(0.65);" color="#8256f6" :checked="agreeState" @click="doAgree"></checkbox><view class="uni-font-light">已阅读并同意</view><view class="privacy uni_font_medium" @click="gotoUserAgreen">《用户服务协议》</view><view class="privacy uni_font_medium" @click="gotoPrivacy">《隐私政策》</view></view><view class="uni_13px mt6">上海栾青网络科技有限公司出品</view><view class="uni_13px mb15">Copyright © 2023</view></view><!-- #endif --></view></template><script>// #ifdef APP-PLUSconst univerifyManager = uni.getUniverifyManager()// #endifimport api from '../../../../common/network/api/api.js';import dataBase from '../../../../common/database.js';export default{data(){return{agreeState: false,version:"v1.0",appName: '易绘',fromSource: 'index', // 来源,如果是index,则登录后返回主页,否则返回上一级code:undefined,isShowWX:true,isApple:false,isCanPhoneNumberLogin: false,}},onLoad(opt) {// #ifdef MP-WEIXINconst $that = this;this.fromSource = opt.fromSource || 'index';uni.login({success(res) {$that.code = res.code;},fail(res) {},}) // #endif},onShow() {const $that = this;$that.version = 'v'+ uni.getSystemInfoSync().appVersion;// #ifdef APP-PLUSif(plus.runtime.isApplicationExist({pname:'com.tencent.mm',action:'weixin://'})){console.log("微信应用已安装");$that.isShowWX = true;}else{console.log("微信应用未安装");$that.isShowWX = false;}uni.preLogin({provider:'univerify',success: (suc) => {this.isCanPhoneNumberLogin = true;if(univerifyManager){univerifyManager.preLogin();}},});this.isApple = uni.getSystemInfoSync().platform == 'ios';// #endif},methods:{download(type){if(type === 1){window.location.href = dataBase.appDownloadUrl;}else if(type === 2){window.location.href = 'https://apps.apple.com/cn/app/%E6%98%93%E7%BB%98ai%E4%BD%9C%E7%94%BB/id1670258950';}},finishPage(){uni.navigateBack({delta:1});},// 登录动作中枢doLoginAction(type){if(!this.agreeState){const $that = this;uni.showModal({title:'温馨提示',content:'您是否已阅读并同意 《用户服务协议》、 《隐私政策》?',confirmText:'同意并登录',cancelText:'拒绝',complete: (res) => {if(res.confirm){$that.agreeState = true;$that.doLoginAction(type);}}});return;}switch(type){case 'apple': this.login4ApplePhone(); break;case 'app-wx': this.login4AppWx(); break;case 'mobile': this.login4Mobild(); break;}},gotoUserAgreen(){uni.navigateTo({url:'/pages_sub/center/pages/webview/webview?url='+dataBase.appAgreement+'&title=用户协议'});},gotoPrivacy(){uni.navigateTo({url:'/pages_sub/center/pages/webview/webview?url='+dataBase.appPrivacy+'&title=隐私协议'});},doAgree(){this.agreeState = !this.agreeState;},// 手机号一键登录login4Mobild(){const $that = this;// 一键登录必须是手机使用流量的前提下才能获取到手机号码,用Wi-Fi联网时无法获取到手机号码,同时如果是双卡手机,获取到的手机号码是默认移动数据的那个手机卡的号码。// #ifdef APP-PLUS// 预登录 START// 1.提高一键登录的加载速度 // 2.判断一键登录环境是否可用univerifyManager.preLogin();// 调用一键登录弹框univerifyManager.login({univerifyStyle: {fullScreen:false,icon: {path:'/static/app_logo200.png'},privacyTerms:{defaultCheckBoxState: false,checkBoxSize: 14,privacyItems:[{ "url": dataBase.appAgreement, "title": "用户服务协议" },{ "url": dataBase.appPrivacy, "title": "隐私协议" } ]},authButton:{normalColor:"#9686FB",disabledColor:"#AAAAAA"},otherLoginButton: { visible: true, // 是否显示其他登录按钮,默认值:true normalColor: "", // 其他登录按钮正常状态背景颜色 默认值:透明 highlightColor: "", // 其他登录按钮按下状态背景颜色 默认值:透明 textColor: "#656565", // 其他登录按钮文字颜色 默认值:#656565 title: "其他登录方式", // 其他登录方式按钮文字 默认值:“其他登录方式” borderColor: "", //边框颜色 默认值:透明(仅iOS支持) borderRadius: "0px" // 其他登录按钮圆角 默认值:"24px" (按钮高度的一半)}, },success (auth) {uniCloud.callFunction({name:'getPhoneNumber',data:{ openid: auth.authResult.openid, access_token: auth.authResult.access_token},success(cloudRes) {let {code, phoneNumber} = cloudRes.result;console.error("手机号:",phoneNumber);if(code === 200){const data = {name: '手机用户',unionId: phoneNumber};$that.submitLoginData(data);univerifyManager.close();}else{univerifyManager.close();}},fail: (err) => {console.error("云函数失败:",err);}})},fail(res) {// 点击其他登录方式if(res.code === 30002){univerifyManager.close();}}})// #endif},// 苹果登录login4ApplePhone(){const $that = this;uni.login({ provider: 'apple', success(loginRes){ // 登录成功 uni.getUserInfo({ provider: 'apple', success(res) { // 苹果登录成功:{"errMsg":"getUserInfo:ok","userInfo":{"openId":"001465.6449e1ad2e46401488d67bae89d79c8a.1706","fullName":{"familyName":"许","giveName":"仕永","givenName":"仕永"},"email":"835588741@qq.com","authorizationCode":"c9acf4dd2a2a9491a93d158a7669b4165.0.mruwv.b0hncISXQbHMGinz5_f3vg","identityToken":"eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmx1YW5xaW5nLmJhYnkiLCJleHAiOjE2MjA5MjU2MTIsImlhdCI6MTYyMDgzOTIxMiwic3ViIjoiMDAxNDY1LjY0NDllMWFkMmU0NjQwMTQ4OGQ2N2JhZTg5ZDc5YzhhLjE3MDYiLCJjX2hhc2giOiJqVG9MWHFKd3BBemxxSlNmV2xjWFBBIiwiZW1haWwiOiI4MzU1ODg3NDFAcXEuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwiYXV0aF90aW1lIjoxNjIwODM5MjEyLCJub25jZV9zdXBwb3J0ZWQiOnRydWUsInJlYWxfdXNlcl9zdGF0dXMiOjJ9.nAHGpGDpjALPmz-vReHjIAV3IyrYJTrLxHmLDqTd2AShYH6XZW3Qh-27aAgbWjg8_3w4Ex22Tpv8_NhKNTAIrHjCNMN5jnw6WkXL4-utKTZi4mlX4p_WiPpkZSa7e0cJZYKrN5b3UEiI1Vf6yu8b5TeKz4E7cg1Iq6RfFNmXLYJmt92WRtwSPexinTySjVvZmxGZ-_7nnh-TowHXjedLBsUsdB7oRlp1xSRpRQm78YKnibwmF2__iejPTmKL_WwOTXXBsg4NrF5h7rFS0Z7jGvA8WPziJxoaeHDP_j1Iw2pcmfzMLA7FhSXlkcYd38To2Wv01kOma0fLlfIm8JovIg","realUserStatus":2}} at pages/user/login/login.vue:93 __ERROR console.error("苹果登录成功:",res?.userInfo?.fullName?.giveName || "苹果用户",res?.userInfo?.openId);// 获取用户信息成功 const data = {name: res?.userInfo?.fullName?.giveName || "苹果用户",unionId: res?.userInfo?.openId};$that.submitLoginData(data);} }) }, }); },// App端微信login4AppWx(){const $that = this;uni.login({provider: 'weixin',success: function (loginRes) {uni.getUserInfo({success(res) {const data = {avatar:res.userInfo.avatarUrl,name:res.userInfo.nickName,gender:res.userInfo.gender,unionId: res.userInfo.unionId,};$that.submitLoginData(data);},})},});},// 提交登录信息给后端submitLoginData(data){data.action = "submitLoginData";api.doPost(data).then(res=>{let temp = res.data;dataBase.insertUUIDAtStorage(temp.uuid);dataBase.insertTokenAtStorage(temp.token);this.finishPage();});},// 小程序专用 2getuserinfo(){let $that = this;let code = $that.code; uni.showToast({title:'正在请求中',icon:'none'});uni.getUserProfile({desc:'用于登录获取昵称头像',success(res) {console.error("获取的用户资料:",JSON.stringify(res));const data = {action:'submitLoginDataMP',code:code,avatar:res.userInfo.avatarUrl,name:res.userInfo.nickName,gender:res.userInfo.gender,};api.doPost(data).then(response=>{dataBase.insertTokenAtStorage(response.data.token);dataBase.insertUUIDAtStorage(response.data.uuid);$that.finishPage();});}})},}}
</script><style>@import url("login.css");
</style>
四、头像制作
<template><view><uni-nav-bar :statusBar="true" :border="false" :fixed="true" :leftWidth="400" :rightWidth="200"><template v-slot:left class="uni_flex_row_align_left uni_14px uni_font_medium"><text class="button_import" @click="importImageBg">导入图片</text><text class="button_save" @click="onSave2PhoneStorage">保存本地<!-- <text class="uni_12px" style="color: #E00300;">免费</text> --></text></template><!-- #ifndef MP --><template v-slot:right><view class="uni_flex_row_align_center" @click="onClickHelp(true)"><uni-icons type="help-filled" size="22" color="#A2A3A4"></uni-icons><view class="uni_12px uni_color_999">帮助</view></view></template><!-- #endif --></uni-nav-bar><view class="uni_flex_col_align_center" v-if="isShowCanvas" style="background-color: #ffffff;position: sticky;top: 140rpx;z-index: 999;"><canvas id="canvasId" canvas-id="canvasId" class="avatar_panel" @touchstart="onTouchStart" @touchmove="onTouchMove"></canvas></view><!-- 缩放、旋转操作栏 START --><view v-if="moveElement.url_mp || moveElement.url" class="uni_flex_row_align_center mt10"><view class="tag_fun_text2" @click="onImgTagRotate(-1)"><image class="tag_fun_img" src="../../static/icon/icon_left.png"></image></view><view class="tag_fun_text ml12 mr8" @click="onImgTagScan(-1)">缩小</view><!-- #ifdef MP --><!-- <image class="selected_tag" :src="moveElement.url_mp" mode="widthFix"></image> --><!-- #endif --><view class="tag_fun_text ml10 mr10" @click="onImgTagRemove">移除</view><!-- #ifndef MP --><!-- <image class="selected_tag" :src="moveElement.url" mode="widthFix"></image> --><!-- #endif --><view class="tag_fun_text ml8 mr12" @click="onImgTagScan(1)">放大</view><view class="tag_fun_text2" @click="onImgTagRotate(1)"><image class="tag_fun_img" src="../../static/icon/icon_right.png"></image></view></view><!-- 缩放、旋转操作栏 END --><view class="uni_flex_row_align_left ml15 mt25"><image src="/static/icon/icon_color_piacker.png" style="width: 30rpx;height: 30rpx;"></image><text class="uni_14px uni_color_222 ml8 uni_font_medium">背景颜色</text></view><view class="uni_flex_warp uni_flex_row_between ml15 mr15 mt12"><view v-for="(color,id) in colorList" class="bg" v-bind:key="id" :style="'background-color:#'+color" @click="onClickColorItem(color)"></view></view><view class="input_view ml15 mr15" v-if="false"><view class="uni_flex_row_align_left uni_font_regular uni_17px"><text class="mr2">#</text><input class="input_style" maxlength="6" v-model="customColor" placeholder="请输入6位十六进制码" /></view><view class="uni_flex_row_align_right" @click="onClickColorItem()"><text class="uni_green_209B5C uni_14px uni_font_medium">使用自定义</text><view class="color_tag ml10" :style="'background-color:#'+customColor"></view></view></view><view class="uni_flex_row_align_left ml15 mt25"><image src="/static/icon/icon_img_tag.png" style="width: 30rpx;height: 30rpx;"></image><text class="uni_14px uni_color_222 ml8 uni_font_medium">装饰点缀图案</text></view><!-- 点缀的图案 --><swiper class="uni_flex_col_align_center ml14 mr14 mt12" style="height: 365rpx;" :indicator-dots="true" indicator-active-color="#8256f6"><swiper-item v-for="(page,pid) in imgTag" v-bind:key="pid" style="width: 690rpx;"><view class="uni_flex_warp"><view class="tag_item" v-for="(tag, tid) in page" v-bind:key="tid" @click="onPickerImgTag(tag,tid)"><image class="tag_size" :src="tag.url"></image></view></view></swiper-item></swiper><!-- <view class="page_tip_view mt20" v-if="false"><text class="uni_13px">三步自定义头像制作</text><text class="mt4 uni_12px">1.选择背景,添加头像的背景主图</text><text class="mt2 uni_12px">2.点击添加装饰点缀的图案</text><text class="mt2 uni_12px">3.点击保存,即可保存到手机(相册查看)</text></view> --><uni-popup ref="showHelpDialog" type="top" :isMaskClick="false"><view class="popup_help_view"><view class="page_tip_view mt40"><text class="uni_13px">三步自定义头像制作</text><text class="mt4 uni_12px">1.选择背景,添加头像的背景主图</text><text class="mt2 uni_12px">2.点击添加装饰点缀的图案</text><text class="mt2 uni_12px">3.点击保存,即可保存到手机(相册查看)</text></view><view style="height: 40rpx;"></view><view class="mt18 uni_15px button_round_style" @click="onClickHelp(false)">好的</view></view></uni-popup></view>
</template><script>import api from '../../common/network/api/api';import dataBase from '../../common/database.js';export default {data() {return {isShowCanvas: true,// tagImages:[],drawTag:[], // 绘制在画布上的点缀图案列表imgTag:[], // 可选的点缀图案canvasBG:'', // 画布的背景主图moveElement:{}, // 当前选中的元素canvasBgColor:'#ffffff', // 画布的背景颜色上色customColor:'FFFFFF', // 自定义颜色吗canvasSize: 231,colorList:["9686FB","6BADFF","77C38F","FFE16B","FF9F6B","FF6B6B"],}},onShow() {this.queryTags();this.isLogin = dataBase.queryLoginStatus();if(this.isLogin){api.doPost({action:'queryUnreadMessageNumber'}).then(res=>{let unread = res.data.unread;unread > 0 ? uni.showTabBarRedDot({index: 4}) : uni.hideTabBarRedDot({index: 4});});}},methods: {onImgTagRemove(){let id = -1;this.drawTag.forEach((item,itemid)=>{if(item.id === this.moveElement.id){id = itemid;}});if(id != -1){this.drawTag.splice(id,1);}this.moveElement = {};this.drawCanvas();},// 保存到本地手机存储onSave2PhoneStorage(item){uni.canvasToTempFilePath({canvasId:'canvasId',success:(res)=>{// 保存到手机本地存储uni.saveImageToPhotosAlbum({filePath: res.tempFilePath,success:(res)=>{uni.showToast({title:'已下载到手机(可在相册查看)',icon:'none'});}});}});},// 导入头像主图importImageBg(){// #ifndef H5uni.chooseImage({count:1,crop: {width: 1000,height:1000,quality:100},success: (res) => {this.canvasBG = res.tempFilePaths[0];this.drawCanvas();}})// #endif// #ifdef H5uni.chooseFile({count:1,extension:['.png','.jpg'],complete: (res) => {console.error("选择成功:",res);}});// #endif},// 显示帮助弹窗onClickHelp(show){this.isShowCanvas = !show;if(show){this.$refs.showHelpDialog.open();}else{this.$refs.showHelpDialog.close();}},// 查询图案标签列表queryTags(){let tagData = dataBase.queryStorage("app_tag_lsit");if(tagData && tagData.date > (new Date().getTime())){this.imgTag = tagData.list;}else{api.doPost({action:'queryImageTag'}).then(res=>{let temp = res.data || [];let pageSize = 18;let pageNum = temp.length / 18 + (temp.length % 18 > 0 ? 1:0);pageNum = Math.floor(pageNum);this.imgTag = [];for(let i = 0 ; i < pageNum; i++){let sub; if(i < pageNum - 1){sub = temp.slice(i * pageSize, pageSize);}else{sub = temp.slice(i * pageSize, i * pageSize + temp.length % 18);}this.imgTag.push(sub);}let insertData = { list: this.imgTag, date: (new Date().getTime() + 18000000) };dataBase.insertStorage('app_tag_lsit', insertData);})}},// 缩放onImgTagScan(mode){if(this.moveElement && this.moveElement.url){this.moveElement.width += (5 * mode); this.moveElement.height += (5 * mode); } this.drawCanvas();},// 旋转onImgTagRotate(mode){if(this.moveElement && this.moveElement.url){if(this.moveElement.degree >= 360){this.moveElement.degree = mode === -1 ? 350:10;}else if(this.moveElement.degree <= 0){this.moveElement.degree = mode === -1 ? 360:10;}else{this.moveElement.degree += (10 * mode); }}this.drawCanvas();},// 选择饰品图标onPickerImgTag(item,id){item.id = id;item.x = 0;item.y = 0;item.width = 50;item.height = 50;item.degree = 360;this.drawTag.push(item);this.drawCanvas();},// 点击颜色卡onClickColorItem(color){if(color){this.customColor = color;this.canvasBgColor = "#"+color;this.drawCanvas();}else{if(!this.customColor || this.customColor.length < 6){this.customColor = "FFFFFF";uni.showToast({title:"自定义颜色码不合法,请输入6位的十六进制颜色码",icon:'none'});return;}else{this.canvasBgColor = "#"+this.customColor;this.drawCanvas();}}},// 绘制的中枢核心方法drawCanvas(){const ctx = uni.createCanvasContext('canvasId');// 填充背景色ctx.setFillStyle(this.canvasBgColor);ctx.fillRect(0, 0, this.canvasSize, this.canvasSize);// 绘制背景图if(this.canvasBG){ctx.drawImage(this.canvasBG, this.bgLeft, this.bgTop, this.canvasSize, this.canvasSize);}// 饰品点缀图以及旋转逻辑处理this.drawTag.forEach(item=>{if(item.url){// 旋转的处理代码 if(item.degree > 0){ctx.translate(item.x + item.width / 2, item.y + item.height / 2);ctx.rotate(item.degree * Math.PI / 180);ctx.translate((item.x + item.width / 2) * -1, (item.y + item.height / 2) * -1);}if(this.moveElement && item.id === this.moveElement.id){ctx.setStrokeStyle('red');ctx.strokeRect(item.x - 2 , item.y - 2 , item.width + 2, item.height + 2)}// 绘制饰品点缀图//#ifdef MPctx.drawImage(item.url_mp, item.x, item.y, item.width, item.height);//#endif//#ifndef MPctx.drawImage(item.url, item.x, item.y, item.width, item.height);//#endif}});ctx.draw();},// 触摸开始,用于定位点击的是哪个tag装饰onTouchStart(e){let x = e.touches[0].x;let y = e.touches[0].y;x = x < 0 ? 0 : x;y = y < 0 ? 0 : y;x = x > this.canvasSize ? this.canvasSize : x;y = y > this.canvasSize ? this.canvasSize : y;let cur = this.drawTag.find(item=>{return ((x >= item.x && y >= item.y) && (x <= (item.x + item.width) && y <= (item.y + item.height)));});if(cur){this.moveElement = cur;}this.drawCanvas();},// 移动进行中,拖动生效onTouchMove(e){let x = e.touches[0].x;let y = e.touches[0].y;x = x < 0 ? 0 : x;y = y < 0 ? 0 : y;x = x - this.moveElement.width;y = y - this.moveElement.height;// let offset = 20;// x = x > this.canvasSize - this.moveElement.width - offset ? this.canvasSize - this.moveElement.width - offset : x;// y = y > this.canvasSize - this.moveElement.height - offset ? this.canvasSize - this.moveElement.height - offset: y;this.moveElement.x = x;this.moveElement.y = y;this.drawCanvas();},}}
</script><style>
@import url('index.css');
</style>