移动应用开发基于uni-app写的音乐播放软件(详细讲解)

软件技术3班-卜国健 2023-05-26 17:29:22

一、首先我们进行项目的创建

1. 创建uni-app空项目并分别运行到浏览器、Android模拟器和微信开发者工具

 

2. 创建uni-app的项目并分别运行到浏览器、Android模拟器和微信开发者工具,以查看官方的组件、接口、模板等示例

3. 项目结构如下

· pages文件夹存放页面

· static内的文件不会进行编译,不要放js文件,可放到common中(注意体积限制)

· unpackage文件夹存放打包的文件

· components文件夹存放各种组件

· App.vue代表应用,包括应用层的生命周期方法,全局样式等

· pages.json整个应用的页面集合,第一项为启动页,可配置页面路由及样式和标题

· manifest.json应用配置,包括图标配置、启动界面配置、权限配置及其他开发配置

· main.js应用入口文件

 

 

 

 

 

 

 

 

 

 

 

二、主页面的UI设计

先贴一下效果图

 再把代码给大家详细贴以下

<template>
  <view :class="[displayInfo,'skeleton']" :animation="animationData" v-if="show">
	<div
	  class="skeleton-avatar"
	  v-if="avatar"
	  :style="{
		width:imgsize,
		height:imgsize,
		borderRadius:imgarc,
	}"
	/>
	<view class="skeleton-content">
	  <view v-if="title" class="skeleton-content-title" :style="titleInfo" />
	  <view v-for="(item,key) in rowDataInfo" :key="key" class="skeleton-content-row" :style="{width:rowInfo(key)}" />
	</view>
  </view>
</template>

<script>
var animation = uni.createAnimation({
  duration: 1000,
  timingFunction: "linear"
});
export default {
  data() {
	return {
	  animationData: {},
	  imgType: ["circular", "square"], //圆形----方形
	  displayType: ["vertical", "horizontal"], //垂直----水平
	  show: true //实际意义上的隐藏
	};
  },
  props: {
	// 是否显示--(明面上的意思)
	loading: {
	  type: Boolean,
	  default: true
	},
	// 是否显示标题
	title: {
	  type: Boolean,
	  default: true
	},
	// 是否显示头像
	avatar: {
	  type: Boolean,
	  default: true
	},
	// 头像大小
	avatarSize: {
	  type: Number | String,
	  default: 100
	},
	// 头像形状-圆形-方形
	isarc: {
	  type: String,
	  default: "square" //圆形
	},
	// title宽度
	titleSize: {
	  type: Number | String,
	  default: '50%'
	},
	// 标题sytle
	titleStyle: {
	  type: Object,
	  default: {}
	},
	// 几行
	row: {
	  type: Number,
	  default: 1
	},
	// 几行信息
	rowData: {
	  type: Array | String,
	  default: "80%"
	},
	// 显示类型-垂直-水平
	display: {
	  type: String,
	  default: "horizontal" //默认水平
	}
  },
  created() {
	this.animationData = animation;
  },
  watch: {
	loading(loading) {
	  if (!loading) {
		animation.opacity(0).step();
		this.animationData = animation.export();
		const time = setTimeout(() => {
		  this.show = false;
		  clearTimeout(time);
		}, 1000);
	  }
	}
  },
  computed: {
	// 循环体
	rowDataInfo() {
	  let rowArr = [];
	  for (let index = 0; index < this.row; index++) {
		rowArr.push(index);
	  }
	  return rowArr;
	},
	// tile修改字符串
	titleInfo() {
	  let titlData = "";
	  for (const key in this.titleStyle) {
		titlData += `${key}:${this.titleStyle[key]};`;
	  }
	  return titlData;
	},
	// 头像大小
	imgsize() {
	  switch (typeof this.avatarSize) {
		case "number":
		  return `${uni.upx2px(this.avatarSize)}px`;
		  break;

		default:
		  return `${uni.upx2px(parseFloat(this.avatarSize))}px`;
		  break;
	  }
	},
	// title宽度
	titlwidth() {
	  switch (typeof this.titleSize) {
		case "number":
		  return `${uni.upx2px(this.titleSize)}px`;
		  break;

		default:
		  return `${uni.upx2px(parseFloat(this.titleSize))}px`;
	  }
	},
	// 圆形
	imgarc() {
	  if (this.imgType.includes(this.isarc)) {
		if (this.isarc == "square") {
		  return "0%";
		}
		return "50%";
	  }
	  console.error(`输入错误${this.isarc}`);
	  return "0%";
	},
	// 显示类型
	displayInfo() {
	  if (this.displayType.includes(this.display)) {
		return this.display;
	  }
	  console.error(`输入错误${this.display}`);
	  return "horizontal";
	}
  },
  methods: {
	rowInfo(key) {
	  const rowTypeof = typeof this.rowData;
	  switch (rowTypeof) {
		  case 'string':
			  return this.rowData
			  break;
	  
		  case 'object':
			  // 如果没有就默认80%
			  if(!this.rowData[0]){
				return '80%';
			  } 
			  if(!this.rowData[key]){
				key = 0;
			  }
			  return (this.rowData[key].indexOf('%')>-1)?this.rowData[key]:`${uni.upx2px(parseFloat(this.rowData[key]))}px`;
			  break;
	  }
	}
  }
};
</script>

三、pages页设计及其代码

在pages页下创建以下四个子页:

index首页

List列表页

Player播放页

search搜索页

 

 

 

 

 

 

首先是index首页的代码

<template>
	<view class="content">
		<uamhead :title="title"></uamhead>
<!-- 		<image class="logo" src="/static/tt.png"></image>
		<view class="text-area">
			<text class="title">{{title}}</text>
		</view> -->
		<scroll-view scroll-y="true" >
			<view>
				<m-for-skeleton
				:avatarSize="200"
				:row="3"
				:title="false"
				:loading="loading"
				isarc="square"
				:titleStyle="{}"
				v-for="(item,key) in 4"
				:key="key">
				</m-for-skeleton>
			</view>
				<view class="index-list" v-for="(item,index) in playlist" :key="index" @click="handleToList(item.id)">
					<view class="index-list-item">
						<view class="index-list-img">
							<image :src="item.coverImgUrl" mode=""></image>
							<text>{{item.updateFrequency}}</text>
						</view>
						<view class="index-list-text">
							<view  v-for="(musicItem,index) in item.tracks" :key="index">
								{{index+1}}.{{musicItem.first}}-{{musicItem.second}}
							</view>
						</view>
					</view>
				</view>
		</scroll-view>
	</view>
</template>

<script>
	import { topList } from '../../common/api.js'
	// const toplistdata=require('@/static/toplist.json')
	import mForSkeleton from "@/components/m-for-skeleton/m-for-skeleton";
	import uamhead from "../../components/uamhead/uamhead.vue"
	export default {
		components: {
		    mForSkeleton
		},
		data() {
			return {
				playlist:[],
				title: 'uni-app-aa',
				loading: true
			}
		},
		onLoad() {
			// this.playlist= toplistdata;
			topList().then((res)=>{
							if(res.length){
								setTimeout(()=>{
									this.playlist = res;
									this.loading=false
								},2000);
								console.log(res)
							}
						});
		},
		methods: {
			handleToList(id){
				uni.navigateTo({
					url:'/pages/list/list?listid='+id
				})
			},
		navPlayer(id) {
		    uni.navigateTo({
				url: '/pages/search/search?listid=' + id
			})
			// hide(){
			// 	this.loading=false;
			// }
		}
		}
	}
</script>

首页的css样式

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.logo {
		height: 200rpx;
		width: 200rpx;
		margin-top: 200rpx;
		margin-left: auto;
		margin-right: auto;
		margin-bottom: 50rpx;
	}

	.text-area {
		display: flex;
		justify-content: center;
	}

	.title {
		font-size: 36rpx;
		color: #8f8f94;
	}
	
	
	.index-list{ 
		margin:0 30rpx;
		}
	.index-list-item{
		display: flex;
		margin-bottom: 35rpx;
		 }
	.index-list-img{
		width:212rpx; 
		height:212rpx; 
		margin-right:20rpx;
		border-radius: 15rpx;
		overflow: hidden;
		position: relative;
		   }
	.index-list-img image{
		width:100%;
		height:100%;
		 }
	.index-list-img text{ 
		position: absolute;
		font-size:22rpx; 
		color:white; 
		bottom: 15rpx; 
		left:15rpx;
		}
	.index-list-text{ 
		flex:1; 
		font-size:24rpx; 
		line-height: 68rpx;
		}
</style>

效果图如下

 List列表页代码

<template>
	
	<view>
		<uamhead :title="title"></uamhead>
		<view class="list-head">
			<view class="list-head-img">
				<image :src="playlist.coverImgUrl" mode=""></image>
				<text class="iconfont iconyousanjiao">{{ playlist.playCount  }}</text>
			</view>
			<view class="list-head-text">
				<view>{{ playlist.name }}</view>
				<view>{{ playlist.description }}</view>
			</view>
		</view>
		<view>
			<view v-show="isShow" class="list-music-title">
				<text class="iconfont iconbofang1"></text>
				<text>播放全部</text>
				<text>(共{{ playlist.trackCount }}首)</text>
			</view>
			<view class="list-music">
				<view class="list-music-item" v-for="(item,index) in playlist.tracks" :key="item.id"  @tap="navPlayer(item.id)">
					<view class="list-music-top">{{ index + 1 }}</view>
					<view class="list-music-song">
						<view>{{ item.name }}</view>
						<view>
							<image v-if=" privileges[index].flag > 60 && privileges[index].flag < 70" src="../../static/dujia.png" mode=""></image>
							<image v-if="privileges[index].maxbr == 999000" src="../../static/sq.png" mode=""></image>
							{{ item.ar[0].name }} - {{ item.name }}
						</view>
					</view>
					<text class="iconfont iconbofang"></text>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import {list} from '../../common/api.js'
	export default {
		data() {
			return {
				title:"榜单列表",
				playlist : {
					coverImgUrl : '',
					trackCount : '',
					creator : ''
				},
				privileges : [],
				isShow : false
			}
		},
		onLoad(options){
			let listid=options.listid;
			list(listid).then((res)=>{
				if(res.data.code=='200'){
					this.title=res.data.playlist.name
					this.playlist=res.data.playlist
					this.privileges = res.data.privileges
					this.isShow=true
					}
				}
			)},
		methods: {
			navPlayer(id){
				console.log(id)
				uni.navigateTo({
					url:'/pages/player/player?playerid='+id
				})
			},
		}
	}
</script>

 List列表页css样式

<style>
	.list-head{
		display: flex;
		 margin:30rpx;
	}
	.list-head-img{
		width:260rpx;
		height:260rpx;
		border-radius: 15rpx; 
		margin-right:40rpx; 
		overflow: hidden; 
		position: relative;
	}
	.list-head-img image{ 
		width:100%; 
		height:100%;
	}
	.list-head-img text{ 
		position: absolute; 
		font-size:26rpx; 
		color:white; 
		right:8rpx; 
		top:8rpx;
	}
	.list-head-text{ 
		flex:1; 
		font-size:20rpx; 
		color:#c3d1e3;
	}
	.list-head-text image{ 
		width:52rpx; 
		height:52rpx; 
		border-radius: 50%;
	}
	
	.list-music{ 
		background:white; 
		border-radius: 50rpx; 
		overflow: hidden; 
		margin-top:45rpx;
	}
	.list-music-item{ 
		display: flex; 
		margin:0 30rpx 70rpx 44rpx; 
		align-items: center;
	}
	.list-music-top{ 
		width:56rpx; 
		font-size:24rpx; 
		color:#979797;
	}
	.list-music-song{ 
		flex:1; 
		line-height: 40rpx;
	}
	
		
	.list-music-title{ 
		height:58rpx; 
		line-height: 58rpx; 
		margin:30rpx 30rpx 70rpx 30rpx;
	}
	
	.list-music-song image{ 
		width:34rpx; 
		height:22rpx; 
		margin-right:10rpx;
	}
</style>

 List列表页效果图

 

Player播放页代码

<template>
	<view class="player-head">
		<uamhead :title="title"></uamhead>
		<view class="song">{{song.artist}}:{{song.name}}</view>
		<view  class="player">
			<image :src="song.picUrl" :class="{ 'run' : isplayrotate }" mode=""></image>
				<text class="iconfont iconpause" @tap="noPlaying" v-if="isplayrotate"></text>
				<text class="iconfont iconbofang" @tap="playing" v-else></text>
			<view></view>
		</view>
		<scroll-view class="lyric" scroll-y="true">
			<view class="wrap" :style="{ transform : 'translateY(' + - (lyricIndex - 1) * 84 +'rpx)'}">
				<view class="index-item" :class="{active: lyricIndex == index}" v-for="(item,index) in song.lyric"
					:key="index">
					{{item.lyric}}
				</view>
			</view>
		</scroll-view>
	</view>

</template>

<script>
	import {
		songDetail,
		songLyric,
		songUrl,
	} from '../../common/api.js'
	//创建innerAudioContext实例
	const innerAudioContext = uni.createInnerAudioContext();
	export default {
		data() {
			return {
				title: '黑胶唱片',
				song: {
					id: '',
					name: '',
					artist: '',
					picUrl: '',
					songUrl: '',
					lyric: '',
				},
				isplayrotate: false,
				lyricIndex: 0,
			}
		},
		onLoad(options) {
			let playerid = options.playerid
			//歌曲id
			songDetail(playerid).then(res => {
				let s = res.data.songs[0]
				this.song.name = s.name
				this.song.id = s.id
				this.song.artist = s.ar[0].name
				this.song.picUrl = s.al.picUrl
				//播放音乐url
				songUrl(this.song.id).then(res => {
					this.song.songUrl = res.data.data[0].url
					console.log(this.song.songUrl)
				})
				// 歌词
					songLyric(playerid).then(res => {
									// this.song.lyric=res.data.lrc.lyric;
									let lyric = res.data.lrc.lyric;
									let result = [];
									let re = /\[([^\]]+)\]([^[]+)/g;
									lyric.replace(re, ($0, $1, $2) => {
										result.push({
											time: this.formatTimeToSec($1),lyric: $2});
									});
									this.song.lyric = result;
								})
				console.log(res)
			})

		},
		methods: {
			playing() {
				innerAudioContext.autoplay = true
				innerAudioContext.src = this.song.songUrl;
				innerAudioContext.onPlay(() => {
					// innerAudioContext.play()
					this.listenLyricIndex()
					this.isplayrotate = true,
						console.log("开始播放")
				})
				this.innerAudioContext = innerAudioContext
			},
			noPlaying() {
				innerAudioContext.pause()
				this.isplayrotate = false;
				console.log("停止播放")
				innerAudioContext.onPause(() => { //暂停时调用的方法  
					innerAudioContext.startTime = innerAudioContext.currentTime
					//startTime  设置开始时间  currentTime 暂停时的秒数   官方文档都有写
				})
			},
			formatTimeToSec(time) {
				var arr = time.split(':');
				return (parseFloat(arr[0]) * 60 + parseFloat(arr[1])).toFixed(2);
			},
			listenLyricIndex() {
				clearInterval(this.timer);
				this.timer = setInterval(() => {
					for (var i = 0; i < this.song.lyric.length; i++) {
						if (this.song.lyric[this.song.lyric.length - 1].time < this.innerAudioContext
							.currentTime) {
							this.lyricIndex = this.song.lyric.length - 1;
							break;
						}
						if (this.song.lyric[i].time < this.innerAudioContext.currentTime && this.song.lyric[i + 1]
							.time > this.innerAudioContext.currentTime) {
							this.lyricIndex = i;
						}
					}
				})
			}

		}
	}
</script>

Player播放页css样式

<style>
	.player-head{
		margin: 0 auto;
		text-align: center;
	}
	.song{
		margin: 20px;
	}
	.player{ 
		width:580rpx; 
		height:580rpx;  
		background:url(~@/static/disc.png);
		background-size:cover;
		margin:210rpx auto 44rpx auto; 
		position: relative;
		z-index: 2;
	}
	.player image{ 
		width:380rpx; 
		height:380rpx; 
		border-radius: 50%; 
		position: absolute; 
		left:0; 
		top:0; 
		right:0; 
		bottom:0; 
		margin:auto; 
		z-index: 3;
	}
	
	.player text{ 
		width:100rpx; 
		height:100rpx; 
		font-size:100rpx; 
		position: absolute; 
		left:0; 
		top:0; 
		right:0; 
		bottom:0; 
		margin:auto; 
		color:white;
		z-index: 4;
	}
	.player view{ 
		position: absolute; 
		width:170rpx; 
		height:266rpx; 
		position: absolute; 
		left:60rpx; 
		right:0;  
		margin:auto; 
		top:-170rpx; 
		background:url(~@/static/needle.png); 
		background-size:cover;
	}
	
	.player image{ 
		width:380rpx; 
		height:380rpx; 
		border-radius: 50%; 
		position: absolute; 
		left:0; 
		top:0; 
		right:0; 
		bottom:0; 
		margin:auto; 
		z-index: 3;
		animation:10s linear infinite move; 
		animation-play-state: paused;
	}
	@keyframes move{
		from{ 
			transform : rotate(0deg);
		}
		to{ 
			transform : rotate(360deg);
		}
	}
	.player .run{ 
		animation-play-state: running;
	}
	.lyric{
		height: 250px;
		line-height: 90px;
		font-size: 32rpx;
		color: #949495;
		text-align: center;
		overflow: hidden;
	}
	.active{
		color: #ffaa7f;
	}
	.lyric .wrap{
		transition: .5s;
	}
	.lyric .item{
		height: 82rpx;
	}
</style>

Player播放页效果图

 

search搜索页代码

<template>
	<view class="search">
		<uamhead :title="title"></uamhead>
		<view class="container">
			<scroll-view scroll-y="true">
				<view class="search-search">
					<text class="iconfont iconsearch"></text>
					<input type="text" placeholder="搜索歌曲" v-model="searchWord" @confirm="handleToSearch" @input="handleToSuggest" />
					<text v-show="searchType == 2" @tap="handleToClose" class="iconfont iconguanbi"></text>
				</view>
				<block v-if="searchType == 1">
					<view class="search-history">
						<view class="search-history-head">
							<text>历史记录</text>
							<text class="iconfont iconlajitong" @tap="handleToClear"></text>
						</view>
						<view class="search-history-list">
							<view v-for="(item,index) in historyList" :key="index" @tap="handleToWord(item)">{{ item }}</view>
						</view>
					</view>
					<view class="search-hot">
						<view class="search-hot-title">热搜榜</view>
						<view class="search-hot-item" v-for="(item,index) in searchHot" :key="index"
							@tap="handleToWord(item.searchWord)">
							<view class="search-hot-top">{{ index + 1 }}</view>
							<view class="search-hot-word">
								<view>
									{{ item.searchWord }}
									<image :src="item.iconType ? item.iconUrl : ''" mode="aspectFit"></image>
								</view>
								<view>{{ item.content }}</view>
							</view>
						</view>
					</view>
				</block>
				<block v-else-if="searchType == 2">
					<view class="search-result">
						<view class="search-result-item" v-for="(item,index) in searchList" :key="index"
							@tap="handleToDetail(item.id)">
							<view class="search-result-word">
								<view>{{ item.name }}</view>
								<view>{{ item.artists.name }} - {{ item.album.name }}</view>
							</view>
							<text class="iconfont iconbofang"></text>
						</view>
					</view>
				</block>
				<block v-else-if="searchType == 3">
					<view class="search-suggest">
						<view class="search-suggest-title">搜索"{{ this.searchWord }}"</view>
						<view class="search-suggest-item" v-for="(item,index) in suggestList" :key="index"
							@tap="handleToWord(item.keyword)">
							<text class="iconfont iconsearch"></text>
							{{ item.keyword }}
						</view>
				 </view>
				</block>
			</scroll-view>
		</view>
	</view>
</template>

<script>
	// 导入组件
	import mForSkeleton from "@/components/m-for-skeleton/m-for-skeleton";
	import {
		searchHot,
		searchWord,
		searchSuggest
	} from '../../common/api.js'
	import '../../common/iconfont.css'
	export default {
		data() {
			return {
				title:'搜索页',
				searchHot: [],
				searchWord: '',
				historyList: [],
				searchType: 1,
				searchList: [],
				suggestList: []
			}
		},
		onLoad(options) {
			let seId = options.songId
			searchHot(seId).then((res) => {
				if (res.data.code == '200') {
					this.searchHot = res.data.data;
				}
			});
			uni.getStorage({
				key: 'searchHistory',
				success: (res) => {
					this.historyList = res.data;
				}
			});
		},
		methods: {
			handleToSearch() {
				this.historyList.unshift(this.searchWord);
				this.historyList = [...new Set(this.historyList)];
				if (this.historyList.length > 10) {
					this.historyList.length = 10;
				}
				uni.setStorage({
					key: 'searchHistory',
					data: this.historyList
				});
				this.getSearchList(this.searchWord);
			},
			handleToClear() {
				uni.removeStorage({
					key: 'searchHistory',
					success: () => {
						this.historyList = [];
					}
				});
			},
			getSearchList(word) {
				searchWord(word).then((res) => {
					if (res.data.code == '200') {
						this.searchList = res.data.result.songs;
						this.searchType = 2;
					}
				});
			},
			handleToClose() {
				this.searchWord = '';
				this.searchType = 1;
			},
			handleToSuggest(ev) {
				let value = ev.detail.value;
				if (!value) {
					this.searchType = 1;
					return;
				}
				searchSuggest(value).then((res) => {
					if (res.data.code == '200') {
						this.suggestList = res.data.result.allMatch;
						this.searchType = 3;
					}
				});
			},
			handleToWord(word) {
				this.searchWord = word;
				this.handleToSearch();
			},
			handleToDetail(songId) {
				uni.navigateTo({
					url: '/pages/player/player?songId=' + songId
				});
			}
		}
	}
</script>

search搜索页css样式

<style scoped>
	.search-search {
		display: flex;
		background: #f7f7f7;
		height: 73rpx;
		margin: 28rpx 30rpx 30rpx 30rpx;
		border-radius: 50rpx;
		align-items: center;
	}

	.search-search text {
		margin: 0 27rpx;
	}

	.search-search input {
		font-size: 26rpx;
		flex: 1;
	}

	.search-history {
		margin: 0 30rpx;
		font-size: 26rpx;
	}

	.search-history-head {
		display: flex;
		justify-content: space-between;
	}

	.search-history-list {
		display: flex;
		margin-top: 36rpx;
		flex-wrap: wrap;
	}

	.search-history-list view {
		padding: 20rpx 40rpx;
		background: #f7f7f7;
		border-radius: 50rpx;
		margin-right: 30rpx;
		margin-bottom: 20rpx;
	}

	.search-hot {
		margin: 30rpx 30rpx;
		font-size: 26rpx;
		color: #bebebe;
	}
	.search-hot-item {
		display: flex;
		align-items: center;
		margin-top: 40rpx;
	}
	.search-hot-top {
		width: 60rpx;
		color: #fb222;
		font-size: 34rpx;
	}

	.search-hot-word {
		flex: 1;
	}

	.search-hot-word view:nth-child(1) {
		font-size: rpx;
		color: black;
	}

	.search-hot-word image {
		width: 48rpx;
		height: 22rpx;
	}

	.search-result {
		border-top: 2rpx #e5e5e5 solid;
		padding: 30rpx;
	}

	.search-result-item {
		display: flex;
		align-items: center;
		border-bottom: 2rpx #e5e5e5 solid;
		padding-bottom: 30rpx;
		margin-bottom: 30rpx;
	}

	.search-result-item text {
		font-size: 50rpx;
	}

	.search-result-word {
		flex: 1;
	}

	.search-result-word view:nth-child(1) {
		font-size: 28rpx;
		color: #3e6694;
	}

	.search-result-word view:nth-child(2) {
		font-size: 26rpx;
	}

	.search-suggest {
		border-top: 2rpx #e5e5e5 solid;
		padding: 30rpx;
		font-size: 26rpx;
	}

	.search-suggest-title {
		color: #537caa;
		margin-bottom: 40rpx;
	}

	.search-suggest-item {
		color: #666666;
		margin-bottom: 70rpx;
	}

	.search-suggest-item text {
		color: #c2c2c2;
		font-size: 26rpx;
		margin-right: 26rpx;
	}
</style>

search搜索页效果图

 我们还可以在首页面添加搜索组件代码如下

<view class="index-search" @tap="navPlayer">
				<text class="iconfont iconsearch"></text>
				<input type="text" placeholder="搜索歌曲">
			</view>

 这样我们基于uni-app开发的音乐播放软件就制作完成了

大家有问题的可以提出来,欢迎讨论。

 

...全文
375 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

1,366

社区成员

发帖
与我相关
我的任务
社区描述
柳职院电子信息工程学院同学们的学习园地
社区管理员
  • c_university_1974
  • qq_39231145
  • zhuisir
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

各位加入社区的同学,请完善社区信息,把社区昵称改为【班级-姓名】,社区签名改为【班级-学号-姓名】的格式

如【社区昵称】20计应1班  张某某(班级用简称)

     【社区签名】2020级计算机应用技术1班 20201234567 张某某 (班级用全称)

试试用AI创作助手写篇文章吧