用uniapp跨平台开发一款商用的ai绘图ai作画软件的全过程

随着科技的发展,人工智能将会是以后的主流方向,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'">
					<text
						v-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'">
					<text
						v-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'">
					<text
						v-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: '', // 唯一ID
				taskNum: 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();
				else
					this.$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',
				}
				
				// 判断是否有id
				if(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>

三、成品展示图

ai绘图主页
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
下载链接: 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-PLUS
	const univerifyManager = uni.getUniverifyManager()
	// #endif
	
	import 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-WEIXIN
			const $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-PLUS
			if(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();
				});
			},
			// 小程序专用 2
			getuserinfo(){
				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 H5
				uni.chooseImage({
					count:1,
					crop: {width: 1000,height:1000,quality:100},
					success: (res) => {
						this.canvasBG = res.tempFilePaths[0];
						this.drawCanvas();
					}
				})
				// #endif
				
				// #ifdef H5
				uni.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 MP
						ctx.drawImage(item.url_mp, item.x, item.y, item.width, item.height);
						//#endif
						
						//#ifndef MP
						ctx.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>

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我国GIS经过三十多年的发展,理论和技术日趋成熟,在传统二维GIS已不能满足应用需求的情况下,三维GIS应运而生,并成为GIS的重要发展方向之一。上世纪八十年代末以来,空间信息三维可视化技术成为业界研究的热点并以惊人的速度迅速发展起来,首先是美国推出Google Earth、Skyline、World Wind、 Virtual Earth、ArcGIS Explorer等,我国也紧随推出了EV-Globe 、GeoGlobe、VRMap、IMAGIS等软件与国外软件竞争本土市场。三维GIS得到了各行业用户的认同,在城市规划、综合应急、军事仿真、虚拟旅游、智能交通、海洋资源管理、石油设施管理、无线通信基站选址、环保监测、地下管线等领域备受青睐。目前,我国国产三维GIS软件已占据了国内市场的半壁江山。   本文唱谈了十九个国内外主流的三维GIS软件,并对其基本特点、发展历程、应用等方面做了总结概述。由于作者水平有限,不足之处恳请读者批评指正。   国外三维GIS软件:   一重唱·美国谷歌公司:Google Earth--用户最多的三维地球软件   介绍:Google Earth以三维地球的形式把大量卫星图片、航拍照片和模拟三维图像组织在一起,使用户从不同角度浏览地球。Google Earth的数据来源于商业遥感卫星影像和航片,包括DigitalGlobe公司的QuickBird,IKOONOS及法国SPOTS。   特点:Google Earth凭借其强大的技术实力和经验,以其操作简单、用户体验超群的优势吸引了全球近十分之一的人口使用。   发展历程:Google于2004年10月收购了Keyhole公司,随之次年6月推出Google Earth系列软件。   产品形式:Google Earth客户端软件提供三个版本:个人免费版、Plus版、Pro版以及企业级解决方案,用于在企业内部部署Google Earth应用。   二重唱·美国国家航空和航天管理局(NASA):World Wind--最强大的开源地理科普软件   介绍:World Wind是NASA发布的一个开放源代码的地理科普软件,由NASA Research开发,NASA Learning Technologies来发展,它是一个可视化地球仪,将NASA、USGS以及其它WMS服务商提供的图像通过一个三维的地球模型展现,还包含了火星和月球的展现。软件用C#编写,调用微软SQL Server影像库Terrain Server来进行全球地形三维显示。它通过将遥感影像与SRTM高程(航天飞机雷达拓扑测绘)叠加生成三维地形。   特点:World Wind最大的特性是卫星数据的自动更新能力。这种能力使得World Wind具有在世界范围内跟踪近期事件、天气变化、火灾等情况的能力。   拥有NASA血统的World Wind可以利用Landsat 7、SRTM、MODIS、GLOBE , Landmark Set等多颗卫星的数据,将Landsat卫星的图像和航天飞机雷达遥感数据结合在一起,让用户体验三维地球遨游的感觉。采用了先进的流传输技术。   World Wind是个完全免费的软件,在使用上没有任何限制,主要面向科学家、研究工作者和学生群体。另外World Wind是完全开放的,用户可以修改World Wind软件本身。目前,包括国内部分三维GIS软件在内的全球许多主流三维软件都是以World Wind为技术内核发展而来。   三重唱·美国Skyline公司:Skyline Globe--个性化的三维地理信息系统   介绍:SkylineGlobe产品能够基于地表的卫星影像、航空影像创建高分辨率的三维虚拟地球场景。Skyline具有强大空间信息展示功能,支持交互式绘图工具,提供三维测量及地形分析工具,提供数据库接口支持如Oracle,ArcSDE,拥有强大数据处理能力。   特点:Skyline Globe Enterprise Solution是美国Skyline公司为网络运营三维地理信息提供的企业级解决方案。包括了Skyline整套软件工具,给客户提供一站式服务,并开放了所有的API,不论是在网络环境中还是单机应用,让用户能够根据自己的需求定制功能,建立个性化的三维地理信息系统。   产品形式:TerraExplorer、TerraExplorer Pro、TerraBuilder、TerraGate。   应用:中国数字海洋系统、公安部警卫基础工作信息系统、数字深圳三维平台、黄河可视化防汛预案管理系统、数字烟台三维城市规划信息系统等。   四重唱·美国微软公司:Virtual Earth--可以在浏览器中直接运行的三维地球软件   介绍:Virtual Earth 3D可以呈现完整交互式的三维图片,是基于地图的搜索工具,集航拍照片、地图、黄页数据于一体。在Virtual Earth 3D中,就象在大型3D游戏的虚拟现实环境中一样,用户可以在城市之间、建筑物之间“飞来飞去”。除了真实地“再现”城市的地形外,Virtual Earth 3D中也包含一些现实世界中不存在的东西。   特点:Virtual Earth 3D不要求用户在硬盘上下载应用软件,而是直接在浏览器中运行。   发展历程:在Google宣布推出Google Earth后,微软也紧跟其后启动了相关计划。2005年12月23日,微软公司收购一家从事三维地球研究的华人公司GeoTange。2006年5月3日,又收购一家专门从事遥感领域研究的公司Vexcel。随后,在2006年11月初微软发布了Virtual Earth 3D。今年6月,微软推出Bing搜索后,意味着原来的“Virtual Earth”变成了“Bing Maps and Bing Maps for Enterprise”。   五重唱·美国环境系统研究所公司(ESRI): ArcGIS Explorer--ArcGIS家族的3D后代   介绍:ArcGIS Explorer是一个免费的虚拟地球浏览器,提供自由、快速的2D和3D地理信息浏览,充满趣味性且简捷易用。ArcGIS Explorer通过继承ArcGIS Server完整的GIS性能(包括空间处理和3D服务),达到整合丰富的GIS数据集和服务器空间处理应用的目的。   特点:AreG1S Explore具有和Google Earth相似的功能,支持来自ArcGIS Server、GML、WMS、Google Earth(KML)的数据。   发展历程:ArcGIS Explorer是2006年8月推出。在明年即将发布的ArcGIS9.4中也将加强三维GIS功能。 间奏曲   国内三维GIS产品:   六重唱·北京国遥新天地信息技术有限公司:EV-Globe--国内三维海量空间信息平台佼佼者   介绍:EV-Globe具有大范围的、海量的、多源的数据一体化管理和快速三维实时漫游功能,支持三维空间查询、分析和运算,可与常规GIS软件集成,可方便快速构建三维空间信息服务系统,亦可快速在二维GIS系统完成向三维的扩展。EV-Globe提供距离测量、线段剖面、折线剖面、区域淹没、通视分析等三维GIS特色的空间分析功能。可以在EV-Globe中看到烟雾、尘暴、火焰以及下雨、下雪等特殊效果。   特点:EV-Globe基于组件式开发,所有功能以控件或类的方式封装在dll中,用户可以很方便进行各种功能定制,甚至将EV-Globe嵌入各类信息系统中。EV-Globe具备在普通PC机上就能实现的海量三维模型和影像流畅地进行各项漫游操作的功能。此外在EV-Globe服务器端,用户可根据需要绑定常规GIS平台如SuperMap,ArcGIS等。   发展历程:EV-Globe于2008年12月、2009年5月、7月分别发布了EV-Globe SDK、EV-Globe Sea和EV-Globe Web版,并将于今年12月3日正式发布EV-Globe 2.0。   产品形式:EV-Globe SDK(开发包)、EV-Globe Pro(数据浏览工具)、EV-Globe Creater(数据制作工具)、EV-Globe Datasets(影像数据集)。   应用:全国海岛海岸带三维可视化信息系统、中石油海外应急系统、中国石油中长期油气管网建设预测分析、宁波镇海环保三维影像浏览系统、遨游天府--四川省地理空间三维管理系统。   七重唱·武大吉奥信息技术有限公司:GeoGlobe--加入实时三维量测功能   介绍:GeoGlobe是武汉大学李德仁和龚建雅等教授花了近10年时间打造,由武汉大学测绘遥感信息工程国家重点实验室研发的网络环境下全球海量无缝空间数据组织、管理与可视化软件。GeoGlobe提供了一系列三维可视化及应用的功能:可视化导航与操作、可视化查询与三维分析、兴趣点标注及定位等。还提供了二次开发功能,用户可以根据应用的需要自行设计界面,调用所提供的动态库进行二次开发。   特点:GeoGlobe具有和World Wind相似的功能,加入了实时三维量测等功能。能同时处理多种来源的数据,包括三维地形图、航拍影像图、三维模型,矢量数据,是Google Earth所没有的。GeoGlobe2.0提供了海量4D数据(DEM、DOM、DLG、DRG)、地名数据、三维模型数据的完整解决方案。   发展历程:GeoGlobe于2006年4月推出,现已推出至GeoGlobe2.0。   产品形式: GeoGlobe Server、GeoGlobe Builder、GeoGlobe Viewer。   八重唱·适普软件有限公司:IMAGIS--管理意义上的“所见即所得”   介绍:IMAGIS三维可视地理信息系统是一套以数字正射影像(DOM)、数字地面模型(DEM)、数字线划图 (DLG)和数字栅格图 (DRG)作为处理对象的 GIS 系统。结合了三维可视化技术与虚拟现实技术,完全再现管理环境下的真实情况,把所有管理对象都置于一个真实的三维世界中,真正做到了管理意义上的“所见即所得”。   特点:IMAGIS在数据管理上采用了矢量数据和栅格数据混合管理的数据结构,二者可以相互独立存在,同时,栅格数据也可以作为矢量数据的属性,以适应不同情况下的要求。   发展历程:2003年3月推出IMAGIS V2.3,2004年6月推出增强版本IMAGIS V2.3.6,并在该版本中正式推出IMAGIS Web3D V1.0 中英文版本。   产品形式:   IMAGIS Education:三维可视地理信息系统教育版;   IMAGIS Classic:三维可视地理信息系统;   IMAGIS Magixity:城市建模与可视化地理信息系统;   IMAGIS 3DBrowser:影像快速漫游系统;   IMAGIS Web3D:三维场景数据网络发布系统;   IMAGIS Sup3DBrowser:3DBrowser 通用控件。   九重唱·伟景行数字城市科技有限公司:CityMaker--数字城市的三维应用   介绍:CityMaker 是数字城市三维可视化平台,主要针对城市规划领域,提供覆盖规划设计、展示、评估、管理的全方位服务。提供从三维地理信息系统建设到应用的全面解决方案。通过CityMaker三维地理信息平台,可以叠加显示城市面貌、规划图则、户籍信息、监控视频等各种二三维数据,还可快速集成已有专业系统,开展基于网络的三维专业应用。   特点:是面向规划设计师和建筑师的三维辅助设计软件,它将虚拟可视化技术融入设计过程,让设计师在三维环境下进行城市的设计、评估、分析和交流。它可以与3ds MAX等建模软件配合使用,支持材质编辑和物体运动编辑,支持火焰、喷泉、爆炸和雨雪等虚拟现实效果的制作等。   产品形式:   CityMaker Network:专业的城市级三维地理空间信息网络应用平台;   CityMaker Professional:专业的城市规划三维分析软件;   CityMaker Builder:城市级三维地理空间创建软件平台;   CityMaker Designer:面向规划设计师和建筑师的三维辅助设计软件;   CityMaker Simulation System:专业的多通道三维模拟仿真软件。   应用:数字北京、数字斯图加特、虚拟圆明园、上海世博会虚拟现实系统等。   十重唱·杭州阿拉丁信息科技股份有限公司:AlaGIS--网络仿真城市E都市的同门   介绍:AlaGIS与全球首个大规模网络仿真城市E都市同属于杭州阿拉丁公司,采用面向网络的分布式空间信息应用服务支撑平台,集二维、三维、遥感影像于一体,全面整合了GIS与数据库、软件工程、人工智能、网络技术及其他多种计算机主流技术。   特点:二三维叠加是AlaGIS的主要特点,AlaGIS平台采用的合理的二三维映射使二维图形和三维图形的数据一一对应,从而实现了二维图形和三维图形的有效结合,通过二三维的切换或者透明度变化来达到所期望的图形效果。   应用:三维地名管理系统、三维警务地理信息系统、三维数字房产管理系统、三维税源网络管理系统、三维旅游展示管理平台等。   十一重唱·北京灵图软件技术有限公司:VRMap--首次在微机上再现真三维景观   介绍:三维地理信息系统软件VRMap实现了VR和GIS技术的完美结合,可以根据卫星影像、航空影像、电子地图、高程数据、城市模型数据、虚拟效果数据生成虚拟地理场景;通过VRMap提供的二次开发包,可实现规划、国土、电信、交通、水利等各行业的专业分析。   特点:VRMap采用J2EE体系架构,快速、灵活构建基于Web的三维业务应用系统;同时VRMap提供城市级别的基于网络的海量精细场景,可快速建立三维应用。   发展历程:从2000年诞生的VRMap1.0至今,VRMap产品已升级到4.0。但是受2007年底灵图公司裁员事件影响,原VRMap团队成员流失较为严重,产品后续发展堪忧。   产品形式:VRMap标准版、VRMap专业版、VRMap企业版。   十二重唱·北京海澄华图科技有限公司:NEOMAP VPlatform--灵图VRMap的变身   介绍:NEOMAP VPlatform的简称是NVP,它可以在网络发布全球高精度DEM/DOM/DLG数据和特大城市级三维精细模型。NVP提供服务接口,支持灵活的二次开发和二三维一体化应用。NVP包含三维数据处理、三维场景整合、三维网络服务平台、三维数据浏览、运维支撑、二次开发SDK共六个子系统。   特点:NVP的多项核心技术,包括高效的海量空间数据管理技术、海量三维数据网络发布技术、地形、影像数据存储压缩技术、多精度地形、影像数据融合技术,处于国内外领先水平,在对于三维GIS系统最重要的海量数据支持、稳定性、二次开发支持、三维效果方面有显著优势。   发展历程:2008年8月成立公司,随即推出NEOMAP VPlatform。   产品形式:三维数据处理、三维场景整合、三维网络服务平台、三维数据浏览、运维支撑、二次开发SDK。   应用:数字延吉城市地理信息共享平台、苏州市基础地理信息共享平台、青岛市南区空间信息服务平台及应用、秦皇岛城市管理局。   十三重唱·中国资源卫星应用中心、北京视宝卫星图像公司、北京星天地信息科技公司:数据地球(中国)--卫星、航空、地面三种采集方式的集成   介绍:数据地球(中国)(Data Earth China)是我国第一个集数据与软件一体化的三维地理空间信息系统,它在国家863计划地球观测与导航技术领域项目支持下,由中国资源卫星应用中心、视宝公司和北京星天地公司三家联合研发的新一代自主产权的三维地理空间信息服务平台,标志着我国已拥有基于卫星、航空、地面三种方式采集到的地理信息综合开发而成的三维立体地理空间信息系统。   特点:该平台集成了国内领先的Uniscope三维GIS引擎技术,覆盖全域的高分辨率卫星影像(CBERS-02B、SPOT5)、较高精度的地形高程数据、导航用道路和POI等矢量信息,符合保密规定的政府用户还可以享受航空影像数据服务,是数据和平台,航天和航空、宏观和微观、矢量和栅格相结合的新一代三维地理信息产品。   发展历程: 2009年9月发布。   应用:城市应急指挥、国防信息化建设、国土资源管理、城市规划、环境保护、灾害防治等。   十四重唱·武汉地大信息科技发展有限公司:InfoEarth TelluroMap--三维应用系统集成   介绍:InfoEarth TelluroMap采用面向Internet的分布式计算技术和三维可视化技术,支持跨区域、跨网络的复杂大型网络三维应用系统集成。为海量三维空间数据的发布提供了可扩展的开发平台,开发者可以方便、灵活地实现网络空间数据的共享和三维可视化。   特点:InfoEarth TelluroMap基于主流技术平台。NET开发,产品开放性好、架构灵活、三维功能和GIS功能强大、支持TB级海量空间和三维模型数据发布和应用。   产品形式:   InfoEarth TelluroMap Server:服务器端应用程序和组件库;   InfoEarth TelluroMap GlobeEngine:基于组件技术的三维可视化组件;   InfoEarth TelluroMap Map:基于Ajax的WebGIS客户端组件;   InfoEarth TelluroMap Fusion:空间数据、三维模型数据入库、预处理模块。   应用:数字汉江、数字地大、移动基站三维地理信息系统设计方案、山洪(灾害)预警系统工程解决方案等。   十五重唱·北京朝夕科技有限责任公司:Drawsee Earth--在线开发的三维地理信息系统   介绍:Drawsee Earth是结合三维和网络技术的互联网三维GIS开发平台,构建企业级B/S结构三维行业应用的工具。它基于Microsoft .NET与ActiveX软件平台,通过海量数据管理、网络数据流传输、三维模型高速显示等技术,把卫星影像、数字高程、普通矢量地图、精细建筑模型等数据融合到一起。   特点:Drawsee Earth不仅可以提供三维场景可视化、海量数据管理,而是结合行业,提供三维场景动态模拟分析。将三维场景各类实体的可预见态势、不可预见态势,通过动态分析真实展现出来。   产品形式:   Drawsee EarthDesk:数据融合工具;   Drawsee EarthServer:数据服务器;   Drawsee EarthViewer:客户端插件。   应用:三维森林防火指挥系统、三维油罐监控系统、互联网3DGPS车辆监控系统等。   十六重唱·北京超维创想信息技术有限公司:Creatar --真三维地学信息系统   介绍:Creatar 1.0三维地学信息系统是超维创想公司基于北京大学科研实力进行技术创新,自主研发的新一代真三维地学信息系统系列软件。该软件是我国第一个参加科技部软件测评的真三维地学信息系统软件。   特点:完善的三维空间信息基础服务、开放的系统平台、多应用模式支持。   应用:城市地质、岩土工程、环境地质、矿产资源勘查等众多地学相关领域。   十七重唱·北京超图软件股份有限公司:SuperMap iSpace--二三维一体化的三维 GIS模块   介绍:SuperMap iSpace是SuperMap UGC新增三维GIS模块的产品研发代号。采用了SuperMap SDX+空间数据库技术来高效地、一体化地存储和管理二维三维空间数据,升级了二维显示的功能,不仅能够支持将二维的GIS数据和地图直接加载到真三维场景中进行显示,而且可以在二维窗口中显示三维数据,在二维地图中使用三维符号,真正实现了二维三维数据一体化。   特点:二维三维数据一体化、多元数据无缝集成、多元数据无缝集成、三维web浏览等;提供基本的三维空间分析能力包括:量算分析、查询统计分析、通视性分析。   发展历程:2009年10月在超图用户大会上宣布,但目前尚未看到成熟的产品。   十八重唱·中地数码集团:MapGIS-TDE--地上、地表、地下的三维空间数据模型   介绍:MAPGIS-TDE 三维处理平台是中地公司在 MAPGIS7.0 中推出的一套支持真三维数据处理及3DGIS 应用项目二次开发平台。采用三维空间数据模型、构模算法、三维可视化技术及框架加插件的软件体系结构,具备集成管理地上、地表、地下的三维空间模型的能力,可以管理从2.5维到3维、从矢量到栅格等多种三维空间数据模型,并提供多种模型建立、管理及显示的工具及接口。   特点:MAPGIS-TDE在提供一般三维空间数据模型及其管理功能的基础上,平台允许针对特定应用领域动态扩展建模及其分析功能插件,以适应特定的三维应用。   应用:MAPGIS三维数码景观系统、MAPGIS 工程勘察信息系统、MAPGIS 城市地质信息系统、MAPGIS 综合管网信息系统等。   十九重唱·广州市红鹏直升机应用服务有限公司:真三维地理信息系统--航空摄影测量的延伸   介绍:红鹏真三维地理信息系统是以普通数字地图数据为基础,利用虚拟现实技术,将高程数据用形象的方式表现出来;同时运用多媒体和三维可视化技术将图形、图像、文字和数据纳入统一的窗口系统下管理,使其具有虚拟、动态、交互等特征。   特点:红鹏真实三维数字地图不同于其它城市虚拟仿真系统,而是利用其自身优势,从低空(300米)获取高分辨率的航空影像。同时,高分辨率的航空影像也有助于量测出精准的城市建筑的空间尺度。三维数字地图的平均误差不超过1.5 米。利用航空摄影测量的方式,可以快捷、准确、低成本地构建大范围的城市三维地图。 尾声   技术的进步和用户需求的拉动在GIS从二维向三维的发展中起到了决定性的作用。GIS的三维时代,已经悄然来临并广泛应用发展。随着计算机与空间技术的进步与发展, GIS 将由各自分开独立的系统走向兼容与集成;由二维走向三维和四维, 由单机走向网络, 并最终走向社会和家庭。
新基建:AI人工智能行业重磅报告合集,共81份。 AI人工智能影响力微报告 IT宿命系列之:云计算专题-云定义一切 IDC蓬勃发展,云计算大势所趋 AI芯片行业迎来黄金发展期 AI教育:人机交互与个性化学习引领产业变革 AI会议精要 AI国芯破局,行业切入成长期 AI2.0时代来临,智能音箱巩固语音入口地位 准备好了吗,人工智能已经到来(英文版) 人工智能的未来之路 智能语音专题-谈入口太早,但不可或缺 智能语音行业专题-全新的信息流入口 智能硬件创新专题-快充、音频与视频的革命 智能仪表行业专题研究-迎接智慧城市 智能控制器-AI物联网时代的需求起爆点 智能计算芯片:人工智能产业硬件基础与第一桶金-计算机行业AI 系列报告之1 智能化驱动发展,安防开启新篇章 智能大时代,数据驱动未来 易观-智能硬件创新产业发展分析V4—完整版 亿欧-2017安防行业研究分析报告-鸟瞰人工智能应用市场 亿1欧-2017人工智能赋能医疗产业研究报告 信通院-人工智能时代的机器人3.0新生态 无人零售接棒“AI 消费” 乌镇指数-2017全球人工智能发展报告(细分领域篇) 腾讯研究院-中&美人工智能产业发展全面解读 腾讯-2017全球人工智能人才白皮书:解读世界顶级AI牛人的秘密! 刷脸时代来临-人脸识别专题 数据中心和云计算行业专题研究:IDC市场高速增长 视频人工智能大时代 生物特征识别白皮书(2017) 人脸识别专题-应用全面落地,爆发临界点已至 人机交互系列报告-全景拍摄开启视界新革命 人工智能与2030年的生活(英文版) 人工智能引领全球数字化转型 人工智能行业专题-各领域应用加速落地 人工智能行业研究报告 人工智能行业深度报告:产业爆发的拐点 人工智能芯片研发攻略-芯际争霸 人工智能芯片的竞争-GPU正红,ASIC拥抱未来 人工智能系列报告之网络安全 人工智能时代,AI赋能,世界重塑-计算机行业深度研究-国金证券-73页 人工智能深度报告 人工智能的引擎-AI芯片 人工智能创业报告——人工智能领域创业的现实与策略 人工智能产业综述报告 人工智能AI潜入数,润物细无声 人工智能:未来制胜之道 人工智能:未来决策制定的机遇与影响(英文版) 人工智能:经济发展新动力(英文版) 人工智能,这么近,那么远? 人工智能-行业应用落地是硬道理 人工智能 安防-技术为重渠道为王 计算瓶颈突破和商用价值提升引爆AI市场 计算机深度研究-后浪未起,GPU制霸A.I数据中心市场 计算机深度学习-人工智能的“神奇魔杖” 计算机人工智能行业报告 机器视觉-工业4.0添上一双慧眼 机器的崛起:高管眼中的人工智能 风口之巅,向“云”而生 从私有云和云产业链出发投资云计算 从海外科技巨头2016年财报看下一轮IT产业创新 半导体人工智能芯片-新架构改变世界 百度人工智能-2017.9- 安防智能化,从芯片开始 2017MIT人工智能5大趋势预测 2017人工智能行业系列分析——智能语音应用专题分析 2017浙商人工智能发展报告 2017设计人工智能报告——人工智能设计的未来 2017全球AI企业100强(英文版) 2017年人工智能产业专题研究报告 2016人工智能行业系列研究——计算机视觉应用专题研究报告 2016人工智能产业发展报告 2016人工智能生态报告(英文版) 2016全球人工智能发展报告·学术与研究篇 2016全球人工智能发展报告·投资与融资篇 2016全球人工智能发展报告·框架篇 2016全球人工智能发展报告·精华篇 2016全球人工智能发展报告·产业与应用篇 2015年人工智能应用市场研究报告 70页人工智能芯片行业深度研究:人工智能立夏已至,AI芯片迎接蓝海;首推,英伟达GPU 36kr-人工智能行业研究报告(2017年)全版-48页

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值