uniapp访问接口先后顺序_uniapp前后端小项目上手完整教程(详细)

本教程通过实际案例介绍uni-app的开发流程,涵盖项目搭建、用户登录、API接口签名安全策略等内容,适合初学者快速上手。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本次学习为hcoder.net提供的原始教程,本文为学习笔记(对部分做了完善和扩展),仅供各位参考!

如若学习,请参见原版教程,支持正版,谢谢!

本项目为初学者项目,功能简单,就不做项目演示站了,贴上项目图片:

e1053d40f659db5c439587d323e6f72d.pngde201c35da20415a6a3593b8ad8af2c5.png7fbbfa7980fec5432b0ba323f1ef74b0.png9e17a9b25ec65367c963f8aaf10636cd.png

项目建立,本次为学习uniapp官方教程的学习笔记:

一、建立项目、配置公共登陆函数新建--项目--uniapp项目--默认模版,进入main.js,编写公共登陆函数:Vue.config.productionTip = false

...

Vue.prototype.checkLogin = function(backpage,backtype){ //定义一个登陆检查函数

var SUID = uni.getStorageSync("SUID");      //从缓存里取值

var SRAND = uni.getStorageSync("SRAND");

var SNAME = uni.getStorageSync("SNAME");

var SFACE = uni.getStorageSync("SFACE");

if(SUID == '' || SNAME == '' || SRAND == ''){       //如果没有值,跳转登陆页

uni.redirectTo({url:'../login/login?backpage='+backpage+'&backtype='+backtype});

console.log("qqw");

return false;

}

return [SUID, SRAND, SNAME, SFACE];     //有值的话,返回值

}

...

App.mpType = 'app'新建页面,调用公共登陆函数,检查登陆:

内容页面

var loginres;

export default {

data() {

return {

}

},

onLoad() {//加载页面先判断是否登陆

//console.log(options)

var Loginres = this.checkLogin('../index/index', 2) //调用公共的登陆检查函数

console.log(Loginres);

if(!Loginres){return false;}        //如果返回值不存在,返回false

},

methods: {

}

}

二、服务端代码及配置服务端源码

你可以在文末或页面右侧的资源包里下载本次项目的服务端源码,上传至服务端,并在index.php配置数据库信息

b66a7e98eba472acf56c9be338044cfd.png数据库

你可以使用Navicat等数据库工具,建立新的数据表,并执行以下语句,创建表字段:CREATE TABLE `yuedu_members` (

`u_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',

`u_openid` varchar(100) NOT NULL COMMENT 'openid',

`u_name` varchar(50) NOT NULL COMMENT '用户昵称',

`u_face` varchar(200) NOT NULL COMMENT '用户头像',

`u_random` varchar(30) NOT NULL COMMENT '用户随机码',

`u_integral` int(10) DEFAULT '0' COMMENT '积分',

`u_remainder` int(10) DEFAULT '0' COMMENT '余额',

`u_regtime` int(11) NOT NULL COMMENT '用户注册时间',

PRIMARY KEY (`u_id`),

UNIQUE KEY `u_openid` (`u_openid`),

UNIQUE KEY `u_id` (`u_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;或者可以手敲php端

php原理:

这里说一下php的运行原理 ,看图

12d0c477a15ed38aeb1c0ffa384a518e.pngphp的设计模式

MVC(models、controllers、views)和index.php

C控制器层:对不同的业务

M模型:分为数据模型和业务逻辑模型

V视图:本项目因为是前后端分离,不包含V视图层

用户首先进入index.php,进入控制器,由控制器进入视图层和模型层

0ab287cbcaf60506e4d66d3e92ba8724.png7d9d74716888695c5a15a9bfe2dad61c.png

控制器调用其他静态类:

4647347139a0dbd6ebcab13bdbf046ad.png6594d03ad800c6796f6078089f83d29a.png以下为后端php方面代码的手记

在index.php中新建访问规则:index.php入口文件的编写(详细查看注释)<?php

/*

*oldlee

*/

//设置编码

header('content-type:text/html; charset=utf-8');

// 接口认证,每次的接口访问都带上token,token可以自定义

if(empty($_GET['token'])){exit(jsonCode('error', 'token error'));}  //如果没有token,结束并token error

if($_GET['token'] != 'api2020'){exit(jsonCode('error', 'token error'));}//如果token不对,结束并token error

//定义常量

// 文件夹的定义

define("HS_DS"            , DIRECTORY_SEPARATOR);

define("HS_ROOT"          , dirname(__FILE__).HS_DS);

define("HS_CONTROLLERS"   , HS_ROOT.'controllers'.HS_DS);

define("HS_MODELS"        , HS_ROOT.'models'.HS_DS);

define("HS_TOOLS"         , HS_ROOT.'tools'.HS_DS);

/* 过滤及定义 POST    减少跨站攻击的可能性*/

if(!empty($_POST)){

define("IS_POST", false);

}else{

define("IS_POST", true);

$_POST = str_replace(array('', '"', "'"),array('<','>', '"', ''), $_POST);      //过滤尖括号,引号等

}

// 数据库配置

define('HS_DB_HOST'    , '127.0.0.1'); // mysql 服务器地址

define('HS_DB_NAME'    , '***');     // 数据库名称

define('HS_DB_USER'    , 'root');      // 数据库账号

define('HS_DB_PWD'     , '***');      // 数据库密码

define('HS_DB_PRE'     , '***');    // 数据表统一前缀

define('HS_DB_CHARSET' , 'utf8');   // mysql 字符集类型

// 微信小程序相关设置

define('HS_APPID'  , '*****');

define('HS_SECRET' , '*****');

// 自动加载各种控制器、方法和类

function hsAutoLoad($className){

$className = explode('\', $className);

if(empty($className[0])){array_shift($className);}

if(count($className) != 2){return false;}

switch($className[0]){

case 'hsModel':

$classFileName = HS_MODELS.$className[1].'.php';

break;

case 'hsTool':

$classFileName = HS_TOOLS.$className[1].'.php';

break;

}

if(empty($classFileName)){return false;}

if(is_file($classFileName)){require $classFileName;}

}

spl_autoload_register("hsAutoLoad");

// 路由解析

$_GET['c'] = empty($_GET['c']) ? 'index' : $_GET['c'];

$_GET['m'] = empty($_GET['m']) ? 'index' : $_GET['m'];

$pattern = '/^[a-zA-Z]+[0-9]*[a-zA-Z]*$/';

if(!preg_match($pattern, $_GET['c'])){$_GET['c'] = 'index';}

if(!preg_match($pattern, $_GET['m'])){$_GET['m'] = 'index';}

$controllerFileName = HS_CONTROLLERS.$_GET['c'].'.php';

if(is_file($controllerFileName)){

require $controllerFileName;

$className = '\\hsC\'.$_GET['c'];

$controller = new $className;

if(method_exists($controller, $_GET['m'])){

call_user_func(array($controller, $_GET['m']));

}

}

// json 输出

function jsonCode($status, $data){

return json_encode(array('status' => $status, 'data' => $data));

}

// 签名验证

function checkSign(){

if(empty($_POST['sign'])){exit(jsonCode('error', 'sign error'));}

$sign = explode('-', $_POST['sign']);

if(count($sign) != 2){exit(jsonCode('error', 'sign error'));}

$db = \hsTool\db::getInstance('access_tokens');

$token = $db->where('token = ?', array($sign[1]))->fetch();

if(empty($token)){exit(jsonCode('error', 'sign error'));}

$signMd5 = md5($token['token'].$token['time']);

if($signMd5 != $sign[0]){exit(jsonCode('error', 'sign error'));}

// 验证成功则删除

$db->where('token = ?', array($sign[1]))->delete();

}

// 验证用户合法性

function checkUser(){

if(empty($_POST['uid'])){exit(jsonCode('error', 'uid error'));}

if(empty($_POST['random'])){exit(jsonCode('error', 'random error'));}

$db   = \hsTool\db::getInstance('members');

$user = $db->where('u_id = ?', array($_POST['uid']))->fetch();

if(empty($user)){exit(jsonCode('error', 'user error'));}

if($user['u_random'] != $_POST['random']){exit(jsonCode('error', 'user error'));}

return $user;

}

在控制器或者模型方法里,即可返回对应的内容至前端,如echo和return等:前端接收和提交数据(定义在main.js中)://定义公共的api接口

var apitoken = 'api2020';

Vue.prototype.apiService = 'http://yuedu.oldlee.cn/index.php?token='+apitoken+'&c=';

三、用户登录

这里使用条件编译对各端的登陆做控制和区分,先使用uni.login获取用户基础信息,再调用uni.getUserInfo获取用户详细信息:

APP端登陆用户详细信息的获取(包含了unionid)// #ifdef APP-PLUS     //条件编译

uni.login({   //获取基础信息

success: (res) => {

console.log(res);

uni.getUserInfo({    //获取详细信息

success: (info) => {

console.log(info);

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

// #endif

打印:

bc8027385fdcb878ba3e2563fedfdf90.png c0bd2ad90479428312054f7d0f938a01.pngAPP端用户登陆,并提交用户信息到服务端:

APP端用户登录的后端代码:

97cf80696c2e09a8d83a13adc5ac1e28.png01f9032de3c50f0c8e15e07ad3ef6b1e.pngAPP端用户登录的前端代码:// #ifdef APP-PLUS

uni.login({

success: (res) => {

console.log(res);

//-------------------------

uni.getUserInfo({

success: (info) => {

//***********************

uni.request({

url: _self.apiService+'member&m=login',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

openid : info.userInfo.openId,

name   : info.userInfo.nickName,

face   : info.userInfo.avatarUrl,

},

success: res => {

console.log(res);

},

fail: () => {},

complete: () => {}

});

//***********************

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

//-------------------------

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

// #endif

服务端返回结果打印:

47e307683cfa5412af04253f5dc22252.pngAPP端用户登陆信息返回成功后,对用户信息缓存并跳转:

在获取服务端返回值后,哪找返回值中data中的status的值来判断是否登陆成功,为“ok”,即登陆成功:

然后,将服务端返回的用户信息进行缓存,并根据全局变量的跳转地址和方式进行跳转:success: res => {

//console.log(res); 登陆成功

//{"data":{"status":"ok","data":{"u_openid":"...","u_name":"..","u_face":"..."}

if(res.data.status == 'ok'){    //如果登陆成功

uni.showToast({title:"登录成功"});

//进行缓存

//加个空格,使其转变为字符串

uni.setStorageSync('SUID' , res.data.data.u_id + '');

uni.setStorageSync('SRAND', res.data.data.u_random + '');

uni.setStorageSync('SNAME', res.data.data.u_name + '');

uni.setStorageSync('SFACE', res.data.data.u_face + '');

//根据访问登陆页面之前的页面带过来的页面地址和跳转方式,进行跳转

if(options.backtype == 1){

uni.redirectTo({url:options.backpage});

}else{

uni.switchTab({url:options.backpage});

}

}else{      //否则直接弹出错误信息

uni.showToast({title:res.data.data.});

}

}

APP端登陆完成;微信小程序端用户登陆(后端代码同上方APP登陆的后端代码)

通过uni.login获取用户基本信息:

打开manifest.json,找到微信小程序配置,填写appid重启应用;// #ifdef MP-WEIXIN

uni.login({

success: (res) => {

console.log(res)

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

// #endif打印:

8432c23bcf05e4275993e2d7d8b095ef.pngdbf387d84e17adb40e73887185061d2e.png

小程序端登陆用户详细信息的获取

小程序端需要通过用户点击按钮获取详细信息,在uni.login完成用户基础信息获取后,配置uni.getUserInfo至view页面按钮,用户点击后获取详细信息:

视图层的按钮:

使用微信登录

js层给按钮配置getUserInfo:methods: {

getUserInfo(){

uni.getUserInfo({   //获取用户信息

success(res) {

console.log(res)

}

})

}

}

前端获取的用户信息的打印:

3970d6e2241504669ba97e675e6814b7.png

这时发现,获取的登陆成功的信息是没有openid的,我们需要使用uni.login获得的code值在服务端向腾讯服务器换取用户openid:通过uni.login获取的用户code值获取用户的openid:

//定一个全局变量,接受uni.login换取的openid

var _self , wx_res , g_options;

...

// #ifdef MP-WEIXIN

uni.login({

success: (res) => {

//console.log(res)

//直接把code通过get的方式提交 的服务器,在服务端操作换取openid

uni.request({

//这里提交到member控制器的codeToSession方法,并带上code

url: _self.apiServer+'member&m=codeToSession&code='+res.code,

method: 'GET',

success: resi => {

wx_res=resi;

}

});

},

fail: () => {

uni.showToast({title:"微信登录授权失败"});

}

})

// #endif

...

服务端通过前端获取的用户code值获取用户的openid:public function codeToSession(){    //定义类

//如果没通过get传过来code,直接报错并结束

if(empty($_GET['code'])){exit(jsonCode('error', 'code error'));}

//拼接腾讯换取openid的接口地址,要加上当前小程序的AppID、Secret(已在入口文件定义:常量)和传过来的code

$url =  "https://api.weixin.qq.com/sns/jscode2session?appid=".HS_APPID.

"&secret=".HS_SECRET."&js_code=".$_GET['code']."&grant_type=authorization_code";

//调用工具类curl,这里需要开启php的扩展工具类php_curl

$curl = new \hsTool\curl();

//调用工具的get方法

$res = $curl->get($url);

echo $res;//输出结果

}

这里需要用到一个工具类:点击查看curl工具类

namespace hsTool;

class curl {

public $httpStatus;

public $curlHndle;

public $speed;

public $timeOut = 60;

public function __construct(){

$this->curlHandle = curl_init();

curl_setopt($this->curlHandle, CURLOPT_TIMEOUT, $this->timeOut);

}

public function setopt($key, $val){

curl_setopt($this->curlHandle, $key , $val);

}

public function get($url){

curl_setopt($this->curlHandle, CURLOPT_URL            , $url);

curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER , true);

curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER , false);

curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYHOST , false);

curl_setopt($this->curlHandle, CURLOPT_ENCODING       , 'gzip,deflate');

curl_setopt($this->curlHandle, CURLOPT_TIMEOUT        , $this->timeOut);

$result =  curl_exec($this->curlHandle);

$this->http_status = curl_getinfo($this->curlHandle);

$this->speed       = round($this->httpStatus['pretransfer_time']*1000, 2);

return $result;

}

public function post($url, $data){

curl_setopt($this->curlHandle, CURLOPT_POST, 1);

curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $data);

return $this->get($url);

}

}

返回两个值:openid和session_key,再加上前面uni.getUserInfo获得的昵称和头像等信息,就可以存储用户信息了;

0776c427f92642915a91d48f037696c8.png

微信小程序端用户登陆,提交用户信息到服务端(后端代码和返回值同APP端):

后端代码:

97cf80696c2e09a8d83a13adc5ac1e28.png01f9032de3c50f0c8e15e07ad3ef6b1e.png前端代码:getUserInfo(){

uni.getUserInfo({   //获取用户信息

success(info) {

//console.log(res)

//***********************

uni.request({

url: _self.apiService+'member&m=login',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

openid : wx_res.data.openId,//wx_res是保存了openid的全局变量

name   : info.userInfo.nickName,

face   : info.userInfo.avatarUrl,

},

success: res => {

//console.log(res); 登陆成功

//{"data":{"status":"ok","data":{"u_openid":"...","u_name":"..","u_face":"..."}

if(res.data.status == 'ok'){    //如果登陆成功

uni.showToast({title:"登录成功"});

//进行缓存

//加个空格,使其转变为字符串

uni.setStorageSync('SUID' , res.data.data.u_id + '');

uni.setStorageSync('SRAND', res.data.data.u_random + '');

uni.setStorageSync('SNAME', res.data.data.u_name + '');

uni.setStorageSync('SFACE', res.data.data.u_face + '');

//根据访问登陆页面之前的页面带过来的页面地址和跳转方式,进行跳转

if(g_options.backtype == 1){

uni.redirectTo({url:g_options.backpage});

}else{

uni.switchTab({url:g_options.backpage});

}

}else{      //否则直接弹出错误信息

uni.showToast({title:res.data.data});

}

},

fail: (res) => {

console.log(res);},

complete: () => {}

});

//***********************

}

})

}

返回结果打印:

a87f3338fa7b80f6cef02846465c99a2.png

至此,登陆完成!

四、登陆扩展---unionid

打开 manifest.json ,找到APP的sdk配置,在第三方登陆里填写微信相关 appid 重启应用;

然后,根据前面的APP登陆逻辑就可以成功获取用户的unionid,但是要想实现多平台统一用户,在小程序端也还需要获取用户的unionid:

小程序获取用户的unionid需要到微信开放平台,注册账号,并完成认证(300元/年,坑~~),并将微信小程序的AppID填写到已认证的开发者账号下;

获取步骤 :1、配置小程序appid(此appid在微信开放平台已经绑定);

2、使用uni.login登录时会获取code,用code换取seesion_key;

3、在获取用户信息函数中获取到加密信息;

4、利用seesion_key及加密信息在服务端解密获取unionID

在前面微信小程序登陆的时候,调用uni.getUserInfo获取到的结果除了userinfo外,还有其他三个数据:

3970d6e2241504669ba97e675e6814b7.png

将iv、encryptedData取出,把这三个数据连同uni.login获取到的session_key传到服务端的member的wxaes的方法里:// #ifdef MP-WEIXIN

getUserInfo : (info) => {

//获取加密数据

var encryptedData = info.mp.detail.encryptedData; //按照实际情况取值

var iv            = info.mp.detail.iv;

info              = info.mp.detail.userInfo;

//info

//userInfo {"nickName":"...","gender":1,...avatarUrl":"..."}

//与服务器交互进行解密

uni.request({

url: _self.apiServer+'member&m=wxaes',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

session_key   : session_key,

encryptedData : encryptedData,

iv            : iv

},

success: res => {

console.log(res);

//此处可以成功获取 unionId 利用 unionId 完成登录即可

},

fail: () => {},

complete: () => {}

});

}

php端服务器代码:错误码的任意门class member{

//......

public function wxaes(){

//如果缺少值,报错

if(empty($_POST['session_key']) || empty($_POST['encryptedData']) || empty($_POST['iv'])){exit(jsonCode('error', 'data error'));}

//引入微信官方加解密文件

include HS_TOOLS.'WXBizDataCrypt.php';

//使用session_kei和小程序AppID实例化解密函数

$pc = new \WXBizDataCrypt(HS_APPID, $_POST['session_key']);

//定义空值,保存解密结果

$data = '';

//执行解密方法,保存错误至errCode,保存解密结果至data

$errCode = $pc->decryptData($_POST['encryptedData'], $_POST['iv'], $data);

//错误码为0,表示请求成功

if ($errCode == 0) {

exit($data);

} else {

exit(jsonCode('error', $errCode));

}

}

}

返回结果打印:

cb7295ec69628bfca82a95ea20ce1c94.png

最后,把上面的想服务端提交登陆信息的过程中,openid替换为unionid,即可,登陆功能完成;

五、登陆扩展---获取手机号码APP端获取用户手机号码(手册任意门):安卓端:

待完善

IOS端:

待完善

微信小程序获取用户手机号码(手册任意门):

待完善

六、API接口签名安全策略

原理(类似json web token):1、从服务器端获取一个唯一性的token,我们称之为accessToken;

2、前端对accessToken进行随机性拆分及md5加密,产生签名(保存在本地存储中);

3、前端在与后端进行交互时传递签名;

4、后端接收数据是验证签名;

准备两个js文件,存与commons中

md5.js(内容见资源包)

sign.js:var md5 = require('./md5.js');

module.exports = {

sign : function(apiServer){

// 环境判断非uni环境不支持

if(!uni){return '...';}

// 连接服务器获取一个临时的accessToken

uni.request({

url: apiServer+'getAccessToken',

method: 'GET',

success: res => {

if(res.data.status != 'ok'){return ;}

var data = res.data.data;

// 对 accessToken 进行md5加密

var accessToken = md5.hex_md5(data.token + data.time);

// 签名 = md5(accessToekn + time) + '-' + 'accessToekn';

var sign = accessToken + '-' + data.token;

//console.log(sign);

// 记录在本地

uni.setStorage({

key:"sign",

data:sign

});

}

});

}

}

创建数据表,存放token:DROP TABLE IF EXISTS `yuedu_access_tokens`;

CREATE TABLE `yuedu_access_tokens` (

`token` varchar(30) NOT NULL,

`time` int(11) DEFAULT NULL,

PRIMARY KEY (`token`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

服务端php代码:<?php

namespace hsc;

class getAccessToken{

public function index(){

//链接数据库的access_tokens表

$db = \hsTool\db::getInstance('access_tokens');

//利用php的uniqid函数时间函数自动生成字符串和时间戳

$token = array(

'token' => uniqid(),

'time'  => time()

);

//将token添加到数据表,并返回给前端

$db->add($token);

exit(jsonCode('ok', $token));

}

}

完成以上工作后,我们就可以在前端页面中使用该签名方式:首先,在页面引入sign.js

然后在onLoad方法中,加载sign.js文件的sign方法,并传入当前接口请求地址,实现token的生成,并在前端进行缓存

前端每次在对服务端进行request的时候,都要在缓存中取出sign,并带给服务端

服务端在登陆函数中,在调用用户模型之前,进行签名验证

//引入签名文件

var sign = require('../../commons/sign.js');

var _self,wx_res,g_option;

export default {

...

}

var loginres;

export default {

data() {

return {

}

},

onLoad() {

//预先签名

sign.sign(this.apiService);

},

methods: {

}

}

uni.login({

//***********************

// 与服务器交互时将缓存中的签名带过去

var sign = uni.getStorageSync('sign');

uni.request({

url: _self.apiService+'member&m=login',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

...

sign   : sign,//将签名带过去

},

success: res => {

...

}

});

//***********************

php服务端处理验证签名:// 签名验证

function checkSign(){

//如果签名为空,退出

if(empty($_POST['sign'])){exit(jsonCode('error', 'sign error'));}

//对签名拆分

$sign = explode('-', $_POST['sign']);

//判断签名是否是以-隔开的两段,否则退出

if(count($sign) != 2){exit(jsonCode('error', 'sign error'));}

//链接token数据表

$db = \hsTool\db::getInstance('access_tokens');

//根据前端提供的token查看,数据库是否存在

$token = $db->where('token = ?', array($sign[1]))->fetch();

//如果不存在,退出程序

if(empty($token)){exit(jsonCode('error', 'sign error'));}

//如果有的话,就对这个token和时间戳,进行md5

$signMd5 = md5($token['token'].$token['time']);

//对加密后的判断,如果签名不一致,退出程序

if($signMd5 != $sign[0]){exit(jsonCode('error', 'sign error'));}

// 验证成功则删除

$db->where('token = ?', array($sign[1]))->delete();

}

然后在php服务端的member控制器下的login方法中,登陆之前,调用此签名验证方法即可:public function login(){

checkSign();

$memberModel = new \hsModel\member();

$memberModel->login();

}

完成!

七、写作页面视图层代码(没啥可说的,都是css),关于vue知识点,详细看注释:点击这里查看视图代码

删除

+图片

添加

文章分类

{{caties[currentCateIndex]}}

发布文章

js代码,注释很清晰点击这里查看JS代码:

var sign = require('../../commons/sign.js');

var _self, loginres;

export default {

data() {

return {

title : '',//用户输入的标题

artList : [],//文章内容:图片和文本

inputContent : "",//用户输入的文本的双向绑定

needUploadImg : [],//需要上传的图片

uploadIndex : 0,//记录当前上传的图片数量

//分类

caties : ['点击选择'],//分类

currentCateIndex : 0,//分类索引

catiesFromApi : [],

// 记录真实选择的api接口数据的分类id

sedCateIndex  : 0

}

},

onLoad() {//加载页面先判断是否登陆

//console.log(options)

_self = this;

//预先签名

sign.sign(this.apiService);

loginres = this.checkLogin('../write/write', 2) //调用公共的登陆检查函数

if(!loginres){return false;}        //如果返回值不存在,返回false

// 加载文章分类

uni.request({

url: this.apiService+'category&m=index',

method: 'GET',

success: res => {

//console.log(res);

//1: "游戏"  2: "运动"  3: "聚会"

if(res.data.status == 'ok'){//如果返回值状态为:ok

// 把数据格式整理为数组格式: picker 支持的格式 ['分类名', ...]

var categories = res.data.data;

for(var k in categories){

//遍历值,并压入caties分类数组

_self.caties.push(categories[k]);

}

// 记录分类信息

_self.catiesFromApi = categories;

}

}

});

},

methods: {

//选择分类

cateChange : function(e){

//获取到用户在页面上点击的分类列表的索引,比如2

var sedIndex = e.detail.value;

//将它赋值给变量,便于视图层的展示:caties[currentCateIndex]

this.currentCateIndex = sedIndex;

// 判断用户所点击的分类的索引,如果小于1,就是没选,直接返回

if(sedIndex < 1){return ;}

//取到所选值的名称,比如2对应的是:运动

var cateName = this.caties[sedIndex];

// 根据所选的名称,在api接口传过来的分类数据里筛选

for(var k in this.catiesFromApi){

//如果相同,把api接口里对应值的索引,进行赋值

if(cateName == this.catiesFromApi[k]){

//找到他,并记录真实的api里该分类对应的索引

this.sedCateIndex = k;

break;

}

}

this.currentCateIndex = sedIndex;

},

//添加图片

addImg : function(){

uni.chooseImage({   //调用图片选择的函数

//一次选择一张

count: 1,

//图片形式:压缩

sizeType: ['compressed'],

success: function(res) {

// 成功后把类型和图片内容(临时文件地址)压入文章内容的数组中

_self.artList.push({"type":"image", "content" : res.tempFilePaths[0]})

}

})

},

//移除图片

removeImg : function(e){

//点击那张图删除那一张,先获取被点击元素 的索引

var index = e.currentTarget.dataset.index;

//console.log(e);

//弹出确认框

uni.showModal({

content:"确定要删除此图片吗",

title:'提示',

success(e) {

//如果用户点击确认

//e.confirm为true的话,代表用户点击了确认,cancel: true代表用户点击了取消

if (e.confirm) {

// 然后根据索引,在内容数组中,移除一个元素

_self.artList.splice(index, 1);

}

}

});

},

//添加文本

submit : function(res){

//console.log(res);

//获取到用户刚输入的值,这里的artText是在定义input框时候的name

var content = res.detail.value.artText;

//如果没有内容,提示用户并返回

if(content.length < 1){uni.showToast({title:"请输入内容",icon:'none'}); return ;}

//否则把用户提交的内容压入内容数组,并指定格式text

this.artList.push({"type":"text", "content" : content});

// 同时把输入框清空

this.inputContent = '';

},

//删除 文本

deleteText : function(e){

//获取当前所点击的文本的索引

var index = e.currentTarget.dataset.index;

//弹出提醒框

uni.showModal({

content:"确定要删除吗",

title:'提示',

success(e) {

//如果用户点击确认

//e.confirm为true的话,代表用户点击了确认,cancel: true代表用户点击了取消

if (e.confirm) {

_self.artList.splice(index, 1);//确认后从数组中移除

}

}

});

},

//上传数据

submitNow : function(){

// 数据验证,如果缺少就返回,不给提交

if(this.title.length < 2){uni.showToast({title:'请输入标题', icon:"none"}); return ;}

if(this.artList.length < 1){uni.showToast({title:'请填写文章内容', icon:"none"}); return ;}

if(this.sedCateIndex < 1){uni.showToast({title:'请选择分类', icon:"none"}); return ;}

// 上传图片 一次一个多次上传 [ 递归函数 ]

// 上传完成后整体提交数据

// 首先整理一下需要上传的图片

// this.needUploadImg = [{tmpurl : 临时地址, index : 数据索引}]

//把需要上传图片的数组 置空

this.needUploadImg = [];

//遍历文章内容数组,取出类型为图片的 内容

for(var i = 0; i < this.artList.length; i++){

if(this.artList[i].type == 'image'){

//保留图片在内容数组中的索引值

this.needUploadImg.push({"tmpurl" : this.artList[i].content , "indexID" : i});

}

}

//然后指定图片上传的方法

this.uploadImg();

},

//文件和 文本的上传

uploadImg : function(){

// 如果没有图片 或者已经上传完成 则开始执行文章提交,不然就先上传图片

if(this.needUploadImg.length < 1 || this.uploadIndex >=  this.needUploadImg.length){

//文章提交

uni.showLoading({title:"正在提交"});

// 先获取签名

var sign = uni.getStorageSync('sign');

// 将数据内容以POST方法提交到服务端art控制器的add方法

uni.request({

url: _self.apiService + 'art&m=add',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

title   : _self.title,//标题

content : JSON.stringify(_self.artList),//字符串格式的内容

uid     : loginres[0],//用户信息uid

random  : loginres[1],//用户信息random

cate    : _self.sedCateIndex,//文章分类

sign    : sign          //签名

},

success: res => {

console.log(res);

if(res.data.status == 'ok'){

uni.showToast({title:"提交成功", icon:"none"});

//提交成功后,清空内容数组和标题

_self.artList = [];

_self.title = '';

// 清空签名数据

sign.sign(_self.apiServer);

// 防止数据缓存

_self.currentCateIndex = 0;

_self.sedCateIndex     = 0;

_self.needUploadImg    = [];

_self.title            = '';

setTimeout(function(){

uni.switchTab({

url:'../my/my'

})

}, 1000);

}else{

uni.showToast({title:res.data.data, icon:"none"});

}

},

fail: (res) => {

console.log(res);

},

complete: () => {

}

});

return ;

}else{

//图片上传

uni.showLoading({title:"上传图片"});//先来个提示

//调用uniapp的文件上传接口,传至服务器uploadImg控制器

var uploader = uni.uploadFile({

url      : _self.apiService+'uploadImg&m=index',

//上传文件一个一个上传

filePath : _self.needUploadImg[_self.uploadIndex].tmpurl,

name     : 'file',

success: (uploadFileRes) => {

//上传成功返回的数据进行json格式转换

uploadFileRes = JSON.parse(uploadFileRes.data);

//如果状态不是:OK

if(uploadFileRes.status != 'ok'){

//打印返回值,并提示失败,退出

console.log(uploadFileRes);

uni.showToast({title:"上传图片失败,请重试!", icon:"none"});

return false;

}

// 将已经上传的文件地址赋值给文章数据

//将上传的图片的索引值取出来

var index = _self.needUploadImg[_self.uploadIndex].indexID;

// 将返回的已上传图片的地址,按照从内容数组中取出的图片的索引重新替换回去

// 这里返回的服务器图片地址为imgs/xx.png,所以要在前面加上域名地址_self.staticServer

_self.artList[index].content = _self.staticServer + uploadFileRes.data;

console.log('请问');

console.log(_self.artList);

//然后让图片的数量++,一直到大于等于需要上传图片的数量,停止

_self.uploadIndex ++;

// 递归上传,此方法1秒执行一次

setTimeout(function(){_self.uploadImg();}, 1000);

},

fail: () => {

uni.showToast({title:"上传图片失败,请重试!", icon:"none"});

}

})

}

}

}

}

由于上面js在加载页面时就获取了文章分类,我们需要在服务端数据库建立文章分类表:DROP TABLE IF EXISTS `yuedu_categories`;

CREATE TABLE `yuedu_categories` (

`cate_id` int(10) NOT NULL AUTO_INCREMENT,

`cate_pid` int(10) DEFAULT '0',

`cate_name` varchar(50) DEFAULT NULL,

`cate_order` int(10) DEFAULT NULL,

PRIMARY KEY (`cate_id`),

KEY `cate_pid` (`cate_pid`)

) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

-- ----------------------------

-- Records of yuedu_categories

-- ----------------------------

INSERT INTO `yuedu_categories` VALUES ('1', '0', '游戏', '1');

INSERT INTO `yuedu_categories` VALUES ('2', '0', '运动', '2');

INSERT INTO `yuedu_categories` VALUES ('3', '0', '聚会', '3');编写服务端php处理分类的代码:<?php

namespace hsC;

class category{

public function index(){

//查询是否有pid传过来,如果没有,就是0

$_GET['pid'] = empty($_GET['pid']) ? 0 : intval($_GET['pid']);

//链接数据表

$db = \hsTool\db::getInstance('categories');

//如果没有pid,就认为查询所有分类

if(empty($_GET['pid'])){

$categories = $db->order('cate_order asc')->fetchAll();

}else{

//否则查询指定分类

$categories = $db->order('cate_order asc')->where('cate_pid = ?', array($_GET['pid']))->fetchAll();

}

//如果查询结果为空,就返回空

if(empty($categories)){exit(jsonCode('empty', ''));}

$caties = array();

//否则遍历查询结果为数组

foreach($categories as $cate){

$caties[$cate['cate_id']] = $cate['cate_name'];

}

//并以json的形式返回

exit(jsonCode('ok', $caties));

}

}编写服务端php处理图片上传的代码:<?php

namespace hsC;

class uploadImg{

public function index(){

//判断前端提交的文件域是否存在

if(!empty($_FILES['file'])){

//获取扩展名,即后缀

$exename  = $this->getExeName($_FILES['file']['name']);

//如果后缀不在以下范围内,退出程序

if(!in_array($exename, array('png', 'gif', 'jpeg', 'jpg'))){exit(jsonCode('error', 'exename error'));}

//否则,利用存储路径、扩展名和随机生成的图片名称生成存储信息

$imageSavePath = 'imgs/'.uniqid().'.'.$exename;

//利用php函数,保存图片

if(move_uploaded_file($_FILES['file']['tmp_name'], $imageSavePath)){

exit(jsonCode('ok', $imageSavePath));

}else{

exit(jsonCode('error', 'upload error'));

}

}else{

exit(jsonCode('error', 'upload error'));

}

}

public function getExeName($fileName){

$pathinfo      = pathinfo($fileName);

return strtolower($pathinfo['extension']);

}

}编写服务端php文章处理及存储的代码:先创建文章数据表:CREATE TABLE `yuedu_articles` (

`art_id` int(11) NOT NULL AUTO_INCREMENT,

`art_cate` int(10) DEFAULT NULL,

`art_title` varchar(200) DEFAULT NULL,

`art_uid` int(11) DEFAULT NULL,

`art_content` text,

`art_createtime` int(11) DEFAULT NULL,

PRIMARY KEY (`art_id`),

KEY `art_uid` (`art_uid`),

KEY `art_cate` (`art_cate`)

) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;然后处理前端传递的数据:<?php

namespace hsC;

class art{

public function add(){

/* $_POST 格式

(

[title] => title

[content] => [

*      {"type":"image","content":"http://192.168.31.188/imgs/5c174b0fb3825.png"},

*      {"type":"text","content":"hi123"},

*      {"type":"image","content":"http://192.168.31.188/imgs/5c174b0fc3297.png"},

*      {"type":"text","content":"hi222"}]

[uid] => 8

[random] => ****

[cate] => 1

[sign] => sign

)

*/

// 先验证签名

checkSign();

// 再验证用户合法性

$user = checkUser();

// 提交主要信息,连接数据库

$dbArticles = \hsTool\db::getInstance('articles');

$addData = array(

'art_title'      => $_POST['title'],

'art_uid'        => $user['u_id'],

'art_cate'       => intval($_POST['cate']),

'art_content'    => $_POST['content'],

'art_createtime' => time()

);

//执行存储

$articleId = $dbArticles->add($addData);

if(!$articleId){exit(jsonCode('error', '服务器忙请重试'));}

// 更新会员积分(这是后面功能逻辑)

$memberDb = \hsTool\db::getInstance('members');

$memberDb->where('u_id = ?', array($user['u_id']))->filed('u_integral', 10);

exit(jsonCode('ok', 'ok'));

}

}这里用到了一个验证用户合法性的函数

// 验证用户合法性

function checkUser(){

//如果前端用户uid为空,退出

if(empty($_POST['uid'])){exit(jsonCode('error', 'uid error'));}

//如果random为空,退出

if(empty($_POST['random'])){exit(jsonCode('error', 'random error'));}

//链接数据库

$db   = \hsTool\db::getInstance('members');

//按照uid取出用户

$user = $db->where('u_id = ?', array($_POST['uid']))->fetch();

//如果为空,退出程序

if(empty($user)){exit(jsonCode('error', 'user error'));}

//如果用户的random和提交过来不一致,退出程序

if($user['u_random'] != $_POST['random']){exit(jsonCode('error', 'user error'));}

//通过后,返回用户

return $user;

}

八、会员中心页面

包含文章的删除、编辑,加载更多和会员信息等视图层代码点击查看视图层代码

{{user.artCount}}

文章

{{user.u_integral}}

积分

{{user.u_remainder}}

余额

0

消息

我的文章

{{item.art_title}}

编辑

删除

{{loadMore}}

JS代码点击查看JS代码

var _self, loginRes, page = 1;

export default {

data() {

return {

myFace : '',//头像

arts : [],//该用户的文章

loadMore : "点击加载更多",

user : []  //用户信息

};

},

onLoad:function(){

_self = this;

loginRes = this.checkLogin('../my/my', '2');

if(!loginRes){return false;}

this.myFace = loginRes[3];

},

onShow:function(){

//页面每次展示的时候,加载数据

this.arts = [];//置空用户文章

page = 1;//第一页

//获取文章列表

this.getArtList();

// 加载会员信息

uni.request({

//访问服务端的my控制器的info方法,获取用户信息和文章数

url: this.apiService + 'my&m=info',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

uid : loginRes[0],

random : loginRes[1]

},

success: res => {

console.log(res);

if(res.data.status == 'ok'){

// 成功后,赋值给全局变量user

this.user = res.data.data;

}

}

});

},

methods:{

editArt : function(e){

var artId = e.currentTarget.dataset.artid;

uni.navigateTo({

url:"../editArt/editArt?artId="+artId

});

},

removeArt : function(e){

//获取用户点击的文章id和索引(在前面::data中都有指定)

var artId = e.currentTarget.dataset.artid;

var index = e.currentTarget.dataset.index;

//展示提示框

uni.showModal({

title:"提示",

content:"确定要删除吗?",

success:function(e){

if(e.confirm == true){

//用户确定后,向服务器的my控制器的removeArt方法请求

uni.request({

url: _self.apiService + 'my&m=removeArt',

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

//带过去用户信息和文章id

data: {

uid : loginRes[0],

random : loginRes[1],

artId : artId

},

success: res => {

// console.log(res);

if(res.data.status == 'ok'){

//返回成功后,如果状态是OK,代表已删除

uni.showToast({title: "已删除", icon:"none"});

//本地的文章数据表也删除一个,(实现无需刷新页面删除)

_self.arts.splice(index, 1);

}else{

uni.showToast({title: "删除失败", icon:"none"});

}

}

});

}

}

});

},

getArtList : function(){

//如果不是'点击加载更多',说明是其他状态(加载中...),就直接返回

if(this.loadMore != '点击加载更多'){return ;}

//先改变内容为加载中...

this.loadMore = '加载中...';

//访问服务端的my控制器的arts方法发起请求,并带上当前页面,第一次页面==1

uni.request({

url: this.apiService + 'my&m=arts&page='+page,

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

uid : loginRes[0],

random : loginRes[1]

},

success: res => {

console.log(res);

if(res.data.status == 'ok'){

//返回成功后,把取到的文章值赋值给arts

this.arts = this.arts.concat(res.data.data);

//页面数量加一

page++;

//恢复底部加载显示内容

this.loadMore = '点击加载更多';

}else if(res.data.status == 'empty'){

// 如果返回空,说明没取到值,或已经全部取完

this.loadMore = '已经加载全部';

}else{

this.loadMore = '点击加载更多';

}

}

});

}

}

}

.myface{width:88px; height:88px; border:5px solid #F1F2F3; border-radius:100%; margin:15px auto;}

.myface image{width:100%; border-radius:100%;}

.myart-list{width:100%; margin:8px 0; overflow:hidden; border-bottom:1px dashed #D7D8D9;}

.myart-list .title{line-height:2em; width:100%;}

.myart-list .btns{line-height:2em; width:100%;}

.myart-list .btns view{float:right; padding:0 6px; margin:0 5px; color:#00B26A;}

.myart-list .btns view:last-child{color:#F76260;}

.loadMore{width:100%; padding:8px 0; text-align:center; color:#CCCCCC;}

php后端代码点击查看php后端代码<?php

namespace hsC;

class my{

//获取用户信息

public function info(){

//用户合法性验证

$user = checkUser();

// 链接文章数据表

$dbArt = \hsTool\db::getInstance('articles');

// 根据会员uid,查询会员文章总数

$artCountNumber = $dbArt->where('art_uid = ?', array($user['u_id']))->count();

//赋值,返回

$user['artCount'] = $artCountNumber;

exit(jsonCode('ok', $user));

}

//获取用户的文章

public function arts(){

//用户合法性验证

$user = checkUser();

// 链接文章数据表

$db = \hsTool\db::getInstance('articles');

//如果前端请求中页面的值为空,就默认为一,否则取传过来的值

$page = empty($_GET['page']) ? 1 : intval($_GET['page']);

//按照用户、文章id排序取值,然后按照(page-1)*10开始取值,每次10条数据取文章内容

$arts = $db->where('art_uid = ?', array($user['u_id']))->order('art_id desc')->limit(($page - 1) * 10, 10)->fetchAll();

//如果取出的内容为空,就返回空,否则返回取出的值

if(empty($arts)){exit(jsonCode('empty', ''));}

exit(jsonCode('ok', $arts));

}

//用户删除文章

public function removeArt(){

//用户合法性验证

$user = checkUser();

//如果用户没指定文章id,退出

if(empty($_POST['artId'])){exit(jsonCode('error', 'art id error ...'));}

// 链接文章数据表

$db = \hsTool\db::getInstance('articles');

//找到用户指定文章id的文章

$art = $db->where('art_id = ?', array($_POST['artId']))->fetch();

//如果不存在,退出

if(empty($art)){exit(jsonCode('error', 'art id error ...'));}

// 如果文章的用户id不是该用户,退出

if($art['art_uid'] != $user['u_id']){exit(jsonCode('error', 'user error'));}

// 最后,开始删除

$db->where('art_id = ?', array($_POST['artId']))->delete();

// 扣除积分

$memberDb = \hsTool\db::getInstance('members');

$memberDb->where('u_id = ?', array($user['u_id']))->filed('u_integral', -10);

exit(jsonCode('ok', 'ok'));

}

}

八、文章编辑页面视图层代码代码点击查看视图代码

删除

+图片

添加

文章分类

{{caties[currentCateIndex]}}

编辑文章

JS代码点击查看JS代码

var artId, loginRes, _self;

var signModel = require('../../commons/sign.js');

export default {

data() {

return {

title : '',

artList : [],

inputContent : "",

needUploadImg : [],

uploadIndex : 0,

//分类

caties : ['点击选择'],

currentCateIndex : 0,

catiesFromApi : [],

// 记录真实选择的api接口数据的分类id

sedCateIndex  : 0

};

},

onLoad :function(options){

artId = options.artId; //现获取到需要编辑的文章的id

_self = this;      //获得本页面的this

signModel.sign(this.apiService);   //对本页面进行签名

loginRes = this.checkLogin('../my/my', '2');   //验证用户登陆

if(!loginRes){return false;}

// 加载文章内容

uni.request({

//向服务器的art控制器的info方法请求,带过去文章id

url: this.apiService+'art&m=info&artid='+artId,

method: 'GET',

data: {},

success: res => {

//将返回的文章内容赋值给art

var art = res.data.data;

// 文章内容转换并展示

//将内容转换为json格式,然后赋值

var artContent = art.art_content;

artContent = JSON.parse(artContent);

_self.artList = artContent;

// 赋值文章标题

this.title = art.art_title;

// 加载文章分类并设置默认值

uni.request({

//向服务端的api请求,没有带任何参数,表示查询所有分类

url: _self.apiService+'category&m=index',

method: 'GET',

success: res => {

if(res.data.status == 'ok'){

// 把数据格式整理为 picker 支持的格式 ['分类名', ...]

var categories = res.data.data;

//查询结果整理为数组

for(var k in categories){

_self.caties.push(categories[k]);

}

// 记录分类信息

_self.catiesFromApi = categories;

// 获取当前分类的默认值

_self.sedCateIndex = art.art_cate;

// 对应的查找picker的默认值

var cateName = categories[art.art_cate];

for(var i = 0; i < _self.caties.length; i++){

if(cateName == _self.caties[i]){

_self.currentCateIndex = i;

break;

}

}

console.log(_self.currentCateIndex);

}

}

});

}

});

},

methods:{

submitNow : function(){

// 数据验证

if(this.title.length < 2){uni.showToast({title:'请输入标题', icon:"none"}); return ;}

if(this.artList.length < 1){uni.showToast({title:'请填写文章内容', icon:"none"}); return ;}

if(this.sedCateIndex < 1){uni.showToast({title:'请选择分类', icon:"none"}); return ;}

// 上传图片 一次一个多次上传 [ 递归函数 ]

// 上传完成后整体提交数据

// 首先整理一下需要上传的图片

// this.needUploadImg = [{tmpurl : 临时地址, index : 数据索引}]

this.needUploadImg = [];

for(var i = 0; i < this.artList.length; i++){

if(this.artList[i].type == 'image'){

if(this.artList[i].content.indexOf('192.168.31.') == -1){

this.needUploadImg.push({"tmpurl" : this.artList[i].content , "indexID" : i});

}

}

}

this.uploadImg();

},

uploadImg : function(){

// 如果没有图片 或者已经上传完成 则执行提交

if(this.needUploadImg.length < 1 || this.uploadIndex >=  this.needUploadImg.length){

uni.showLoading({title:"正在提交"});

// 将信息整合后提交到服务器

var sign = uni.getStorageSync('sign');

uni.request({

url: this.apiService + 'art&m=edit&artid='+artId,

method: 'POST',

header: {'content-type' : "application/x-www-form-urlencoded"},

data: {

title   : _self.title,

content : JSON.stringify(_self.artList),

uid     : loginRes[0],

random  : loginRes[1],

cate    : _self.sedCateIndex,

sign    : sign

},

success: res => {

if(res.data.status == 'ok'){

uni.showToast({title:"提交成功", icon:"none"});

setTimeout(function(){

uni.switchTab({

url:'../my/my'

})

}, 1000);

}else{

uni.showToast({title:res.data.data, icon:"none"});

}

}

});

return ;

}

// 上传图片

uni.showLoading({title:"上传图片"});

var uploader = uni.uploadFile({

url      : _self.apiService+'uploadImg&m=index',

filePath : _self.needUploadImg[_self.uploadIndex].tmpurl,

name     : 'file',

success: (uploadFileRes) => {

uploadFileRes = JSON.parse(uploadFileRes.data);

if(uploadFileRes.status != 'ok'){

console.log(uploadFileRes);

uni.showToast({title:"上传图片失败,请重试!", icon:"none"});

return false;

}

// 将已经上传的文件地址赋值给文章数据

var index = _self.needUploadImg[_self.uploadIndex].indexID;

_self.artList[index].content = _self.staticServer + uploadFileRes.data;

console.log(_self.artList);

_self.uploadIndex ++;

// 递归上传

setTimeout(function(){_self.uploadImg();}, 1000);

},

fail: () => {

uni.showToast({title:"上传图片失败,请重试!", icon:"none"});

}

})

},

cateChange : function(e){

var sedIndex          = e.detail.value;

this.currentCateIndex = sedIndex;

// 获取选择的分类名称

if(sedIndex < 1){return ;}

var cateName = this.caties[sedIndex];

for(var i = 0; i < this.catiesFromApi.length; i++){

if(cateName == this.catiesFromApi[i].cate_name){

this.sedCateIndex = this.catiesFromApi[i].cate_id;

break;

}

}

this.currentCateIndex = sedIndex;

console.log(this.sedCateIndex);

},

removeImg : function(e){

console.log(e);

var index = e.currentTarget.dataset.index;

uni.showModal({

content:"确定要删除此图片吗",

title:'提示',

success(e) {

if (e.confirm) {

_self.artList.splice(index, 1);

}

}

});

},

deleteText : function(e){

var index = e.currentTarget.dataset.index;

uni.showModal({

content:"确定要删除吗",

title:'提示',

success(e) {

if (e.confirm) {

_self.artList.splice(index, 1);

}

}

});

},

submit : function(res){

var content = res.detail.value.artText;

if(content.length < 1){uni.showToast({title:"请输入内容",icon:'none'}); return ;}

this.artList.push({"type":"text", "content" : content});

this.inputContent = '';

},

addImg : function(){

uni.chooseImage({

count: 1,

sizeType: ['compressed'],

success: function(res) {

_self.artList.push({"type":"image", "content" : res.tempFilePaths[0]})

}

});

}

}

}

php后端代码点击查看php后端代码<?php

namespace hsC;

class art{

public function info(){

//如果前端的文章ID为空,退出

if(empty($_GET['artid'])){exit(jsonCode('error', 'art data error'));}

//将前端传过来的值转为int属性

$_GET['artid'] = intval($_GET['artid']);

//连接文章数据表,

$dbArticles = \hsTool\db::getInstance('articles');

//按照条件查询,如果为空就返回空

$art = $dbArticles->where('art_id = ?', $_GET['artid'])->fetch();

if(empty($art)){exit(jsonCode('empty', ''));}

exit(jsonCode('ok', $art));

}

public function edit(){

// 验证签名

checkSign();

// 验证用户合法性

$user = checkUser();

// 检查文章

if(empty($_GET['artid'])){exit(jsonCode('error', 'art data error'));}

$dbArticles = \hsTool\db::getInstance('articles');

$art = $dbArticles->where('art_id = ?', $_GET['artid'])->fetch();

if(empty($art)){exit(jsonCode('error', 'art data error'));}

if($art['art_uid'] != $user['u_id']){exit(jsonCode('error', 'art data error'));}

$data = array(

'art_title'      => $_POST['title'],

'art_uid'        => $user['u_id'],

'art_cate'       => intval($_POST['cate']),

'art_content'    => $_POST['content']

);

$dbArticles->where('art_id = ?', $_GET['artid'])->update($data);

exit(jsonCode('ok', 'ok'));

}

}

九、首页布局、下拉刷新、上拉加载、分类切换视图层代码点击查看视图层代码

{{cate.name}}

{{item.art_title}}

{{item.art_title}}

JS代码点击查看JS代码

var _self, cate = 0, page = 1;

export default {

data() {

return {

categories:[{cateId : 0, name : "全部"}],

cateCurrentIndex : 0,

artList:[]

}

},

onLoad() {

_self = this;

// 页面一开始,加载文章分类

uni.request({

//请求接口,不指定pid,就是请求全部分类

url: this.apiService+'category&m=index',

method: 'GET',

success: res => {

if(res.data.status == 'ok'){

// 把请求到的分类数据保存仅数组json对象中

var categories = res.data.data;

for(var k in categories){

_self.categories.push({cateId : k, name : categories[k]});

}

}

}

});

// 页面一开始,加载全部文章

this.getNewsList();

},

//下拉刷新

onPullDownRefresh: function(){

//把页面初始化为1,文章清空,重新请求数据

page = 1;

this.artList = [];

this.getNewsList();

},

// 加载更多

//直接请求数据,这时候,page的值还是保持上一次请求的状态的

onReachBottom:function(){

this.getNewsList();

},

methods: {

tabChange : function(e){

//console.log(e);

//获得用户点击了哪一个分类

var cateid = e.currentTarget.dataset.cateid;

var index = e.currentTarget.dataset.index;

//初始化page的值,从第一页开始加载

page = 1;

//把当前点击的分类的索引赋值,通过view标签的判断,给当前分类加上样式

this.cateCurrentIndex = index;

//获取

cate = cateid;

this.artList = [];

this.getNewsList();

},

getNewsList : function(){

uni.showLoading({'title':"加载中..."});

uni.request({

url: this.apiService + 'art&m=getList&cate='+cate+'&page='+page,

method: 'GET',

success: res => {

if(res.data.status == 'empty'){

uni.showToast({

title:"已经加载全部新闻",

icon: "none"

});

}else if(res.data.status == 'ok'){

//整理新闻信息

var newsList = res.data.data;

for(var i = 0; i < newsList.length; i++){

// 把图片分离出来

var imgs = [];

var content = newsList[i].art_content;

content = JSON.parse(content);

for(var ii = 0; ii < content.length; ii++){

if(content[ii].type == 'image'){

imgs.push(content[ii].content);

}

}

newsList[i].art_content = imgs;

}

//填充数据

_self.artList = _self.artList.concat(newsList);

uni.hideLoading();

page++;

}

},

//页面完成时停止下拉刷新

complete:function(){

uni.stopPullDownRefresh();

}

});

}

}

}

php后端代码点击查看php后端代码public function getList(){

$_GET['cate'] = empty($_GET['cate']) ? 0 : intval($_GET['cate']);

$_GET['page'] = empty($_GET['page']) ? 1 : intval($_GET['page']);

$dbArticles = \hsTool\db::getInstance('articles');

if(empty($_GET['cate'])){

$arts = $dbArticles->order('art_id desc')->limit(($_GET['page'] - 1) * 10, 10)->fetchAll();

}else{

$arts = $dbArticles->where('art_cate = ?', array($_GET['cate']))->order('art_id desc')->limit(($_GET['page'] - 1) * 10, 10)->fetchAll();

}

if(empty($arts)){exit(jsonCode('empty', ''));}

exit(jsonCode('ok', $arts));

}

十、内容详情页功能、骨架屏的应用

这里的骨架屏其实就是样式,判断诗句是否加出来,而决定该标签是否存在某样式视图层代码点击查看视图层代码

{{article.art_title}}

{{article.u_name}}

{{article.art_createtime}}

{{item.content}}

JS代码点击查看JS代码

var _self ;

export default {

data() {

return {

article : [], //文章基础信息

artContents : [], // 文章项目

graceSkeleton : 'ing'  //定义骨架屏的默认值就是ing

};

},

onLoad:function(options){

_self = this;

//拿到用户点击的文章的id

var artid = options.artid;

// 加载文章详情

uni.showLoading({title:""});

//请求接口获取文章

uni.request({

url: this.apiService + 'art&m=infoWithUser&artid='+artid,

method: 'GET',

data: {},

success: res => {

console.log(res);

var art = res.data.data;

// 将文章内容转换成数组

var artContentItems = JSON.parse(art.art_content);

console.log(artContentItems);

// 首先规划骨架,先拿到返回数据的类型,利用类型先把页面撑起来,骨架画出来,这时候artContents里只有类型,没有数据

this.artContents = [];

for(var i = 0; i < artContentItems.length; i++){

this.artContents.push({'type': artContentItems[i].type});

}

// 延迟添加数据,然后把文章各类数据在赋值,同时撤销骨架屏

setTimeout(function(){

_self.article       = art;

_self.artContents   = artContentItems;

_self.graceSkeleton = 'end';

uni.hideLoading();

}, 500);

}

});

}

}

php后端代码点击查看php后端代码public function infoWithUser(){

if(empty($_GET['artid'])){exit(jsonCode('error', 'art data error'));}

$_GET['artid'] = intval($_GET['artid']);

$dbArticles = \hsTool\db::getInstance('articles');

$art = $dbArticles

->join('as a left join yuedu_members as b on a.art_uid = b.u_id')

->where('a.art_id = ?', $_GET['artid'])

->fetch('a.*, b.u_id, b.u_name, b.u_face');

if(empty($art)){exit(jsonCode('empty', ''));}

$art['art_createtime'] = date('Y-m-d H:i', $art['art_createtime']);

exit(jsonCode('ok', $art));

}

十一、内容页图片预览功能

这里的骨架屏其实就是样式,判断诗句是否加出来,而决定该标签是否存在某样式!视图层代码JS代码methods:{

showImgs : function(e){

var currentUrl = e.currentTarget.dataset.url;

// 找出图片

var imgsNeedShow = [];

for(var i = 0; i < this.artContents.length; i++){

if(this.artContents[i].type == 'image'){

imgsNeedShow.push(this.artContents[i].content);

}

}

uni.previewImage({

urls    :imgsNeedShow,

current :currentUrl

});

}

}用户注销视图代码:

{{user.u_name}}注销js代码:useroff:function(){

//用户退出登陆,删除缓存

uni.removeStorageSync("SUID");

uni.removeStorageSync("SRAND");

//提示用户已经退出登陆

uni.showToast({

title:"您已经退出登陆",

icon:"none"

});

//一秒后跳转首页

setTimeout(function(){

uni.switchTab({

url:"../index/index"

})

},1000)

}

完成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值