Vue3 + Vant + Thinkphp 编辑功能

前端

<template>
    <van-nav-bar
        title="基本资料"
        left-text="返回"
        left-arrow
        @click-left="onBack"
        fixed
    />

    <div class="h50"></div>
    <div class="user">
        <div class="user-image">
            <img :src="business.avatar_cdn" alt="" />
        </div>
    </div>
    <div class="form">
        <van-form @submit="onProfile" label-align="right" colon>
            <van-field
                v-model="business.mobile"
                name="mobile"
                label="手机号"
                placeholder="手机号"
                readonly
            />

            <van-field
                v-model="business.nickname"
                name="nickname"
                label="昵称"
                placeholder="昵称"
                :rules="[{ required: true, message: '昵称不能为空' }]"
            />

            <van-field
                v-model="business.email"
                name="email"
                label="邮箱"
                placeholder="邮箱"
                :rules="[{ required: true, message: '邮箱不能为空' }]"
            />

            <van-field
                v-model="business.password"
                type="password"
                name="password"
                label="密码"
                placeholder="密码为空不修改"
            />

            <van-field name="gender" label="性别">
                <template #input>
                    <van-radio-group
                        v-model="business.gender"
                        direction="horizontal"
                    >
                        <van-radio name="0">保密</van-radio>
                        <van-radio name="1"></van-radio>
                        <van-radio name="2"></van-radio>
                    </van-radio-group>
                </template>
            </van-field>

            <van-field
                v-model="business.region_text"
                is-link
                readonly
                name="area"
                label="地区选择"
                placeholder="选择省市区"
                @click="showRegion = true"
            />

            <!-- 弹出层 + 地区选择组件 -->
            <van-popup v-model:show="showRegion" round position="bottom">
                <van-area
                    title="地区选择"
                    v-model="code"
                    :area-list="areaList"
                    @confirm="onRegionConfirm"
                    @cancel="showRegion = false"
                />
            </van-popup>

            <van-field name="avatar" label="头像">
                <template #input>
                    <van-uploader v-model="avatar" :max-count="1" />
                </template>
            </van-field>

            <div style="margin: 16px">
                <van-button round block type="primary" native-type="submit">
                    提交
                </van-button>
            </div>
        </van-form>
    </div>
</template>

<script setup>
import { ref, getCurrentInstance } from 'vue';
import { areaList } from '@vant/area-data';

const { proxy } = getCurrentInstance();

const business = ref(proxy.$cookies.get('business'));
// 省市区弹出框 false 为关闭状态
const showRegion = ref(false);
// 图片回显数据,可上传多个图片,具体参考 vant 官网
const avatar = ref([
    {
        url: business.value.avatar_cdn,
    },
]);

// 获取 cookie 里的 省市区 code 
const code = ref(business.value.district ? business.value.district : business.value.city ? business.value.city : business.value.province);

// 提交表单
const onProfile = async (values) => {
    // 封装数据
    let data = {
        busid:business.value.id,
        nickname:values.nickname.trim(),
        email:values.email.trim(),
        gender:values.gender
    }
    
    // 判断一下有没有选择地区
    if(code.value)
    {
        data.code = code.value;
    }

    // 密码
    if(values.password)
    {
        data.password = values.password;
    }

	// 判断图片是否有,有则将图片数据放入 data 里
    if(values.avatar[0].file)
    {
        data.avatar = values.avatar[0].file;
    }

	// 发送请求
    let result = await proxy.$Api.profile(data);

    if(result && result.code === 1)
    {
        proxy.$showNotify({
            type:'success',
            message:result.msg,
            duration:2000,
            onClose:() => {
                proxy.$router.push('/business/base/index');
            }
        });

        return;
    }else{
        proxy.$showNotify({
            type:'warning',
            message:result.msg,
            duration:2000,
        });

        return;
    }
}

// selectedOptions 拿到选择的 省市区编码值
const onRegionConfirm = ({ selectedOptions }) => {
    let [province, city, district] = selectedOptions;

    let regionText = '';
    let regionCode = null;

	// 省
    if (province.value) {
        regionText = province.text + '-';
        regionCode = province.value;
    }

	// 市
    if(city.value)
    {
        regionText += city.text + '-';
        regionCode = city.value;
    }
	
	// 区
    if(district.value)
    {
        regionText += district.text;
        regionCode = district.value;
    }

    code.value = regionCode;
    business.value.region_text = regionText;
    showRegion.value = false;
};

// 返回上一页
const onBack = () => {
    proxy.$router.back();
};
</script>

<style>
@import url('/assets/css/profile.css');
</style>

后端

<?php

namespace app\shop\controller\business;

use think\Controller;

class Base extends Controller
{
   protected $BusinessModel = null;

   public function __construct()
   {
        parent::__construct();

        $this->BusinessModel = model('business.Business');
   }

   public function profile()
   {
        // 接收全部参数,不包括头像
        $params = $this->request->param();

        $business = $this->BusinessModel->find($params['busid']);

        if(!$business)
        {
            $this->error('用户不存在');
        }

        // 封装数据
        $data = [
            'id' => $params['busid'],
            'nickname' => $params['nickname'],
            'email' => $params['email'],
            'gender' => $params['gender']
        ];

		// 邮箱认证功能
        if($data['email'] != $business['email'])
        {
            $data['auth'] = 0;
        }

        if(!empty($params['password']))
        {
            $repass = md5($params['password'] . $business['salt']);

            if($repass == $business['password'])
            {
                $this->error('新密码与原密码一致');
            }

            $salt = build_randstr();
            $password = md5($params['password'] . $salt);

            $data['salt'] = $salt;
            $data['password'] = $password;
        }

        if(!empty($params['code']))
        {
            $path = model('Region')->where(['code' => $params['code']])->value('parentpath');

            if(!$path)
            {
                $this->error('所选地区不存在');
            }

            $pathArr = explode(',',$path);

            $data['province'] = $pathArr[0] ?? null;
            $data['city'] = $pathArr[1] ?? null;
            $data['district'] = $pathArr[2] ?? null;
        }

		// 判断是否有文件变量声明并且文件大小大于0
        if(isset($_FILES['avatar']) && $_FILES['avatar']['size'] > 0)
        {
        	// 上传文件
            $res = build_upload('avatar');

            if($res['code'] === 0)
            {
                $this->error($res['msg']);
            }

            $data['avatar'] = $res['data'];
        }

        $result = $this->BusinessModel->validate('common/business/Business.profile')->isUpdate(true)->save($data);

		// 判断用户是否更新成功
        if($result === false)
        {
        	// 将上传的文件删除
            if(isset($data['avatar']) && $_FILES['avatar']['size'] > 0)
            {
                @is_file('.' . $data['avatar']) && @unlink('.' . $data['avatar']);
            }

            $this->error($this->BusinessModel->getError());
        }else{
			// 将原来的图片删除
            if(isset($data['avatar']) && $_FILES['avatar']['size'] > 0)
            {
                @is_file('.' . $business['avatar']) && @unlink('.' . $business['avatar']);
            }

            // 如果没有路由守卫的,需要重新查询一次新的数据返回存cookie
            
            $this->success('更新成功');
        }


   }
}

调用接口

import { POST, UPLOAD } from "../../services/request";

export default {
    profile(data = {})
    {
        return UPLOAD({
            url:'/business/base/profile',
            params:data
        });
    }
}

Api 接口封装方法

request.js

//引入axios异步请求插件
import axios from 'axios';

let cancel,
    promiseArr = {};

const CancelToken = axios.CancelToken;

//请求拦截器
axios.interceptors.request.use(
    (config) => {
        //发起请求时,取消掉当前正在进行的相同请求
        if (promiseArr[config.url]) {
            promiseArr[config.url]('操作取消');
            promiseArr[config.url] = cancel;
        } else {
            promiseArr[config.url] = cancel;
        }
        return config;
    },
    (error) => {
        return Promise.reject(error);
    }
);

//响应拦截器即异常处理
axios.interceptors.response.use(
    (response) => {
        // return response
        return response.data;
    },
    (err) => {
        if (err && err.response) {
            switch (err.response.status) {
                case 400:
                    err.message = '错误请求';
                    break;
                case 401:
                    err.message = '未授权,请重新登录';
                    break;
                case 403:
                    err.message = '拒绝访问';
                    break;
                case 404:
                    err.message = '请求错误,未找到该资源';
                    break;
                case 405:
                    err.message = '请求方法未允许';
                    break;
                case 408:
                    err.message = '请求超时';
                    break;
                case 500:
                    err.message = '服务器端出错';
                    break;
                case 501:
                    err.message = '网络未实现';
                    break;
                case 502:
                    err.message = '网络错误';
                    break;
                case 503:
                    err.message = '服务不可用';
                    break;
                case 504:
                    err.message = '网络超时';
                    break;
                case 505:
                    err.message = 'http版本不支持该请求';
                    break;
                default:
                    err.message = `连接错误${err.response.status}`;
            }
        } else {
            err.message = '连接到服务器失败';
        }
        console.error(err.message);
        return Promise.resolve(err.response);
    }
);

//请求的默认前缀 只要是发出去请求就会 默认带上这个前缀
axios.defaults.baseURL = '/shop';

//设置默认请求头 异步的
axios.defaults.headers = {
    'X-Requested-With': 'XMLHttpRequest',
};

//设置超时请求时间
axios.defaults.timeout = 10000;

//get请求
let GET = (data = {}) => {
    return new Promise((resolve, reject) => {
        axios({
            method: 'get',
            url: data.url,
            params: data.params,
            cancelToken: new CancelToken((c) => {
                cancel = c;
            }),
        }).then((res) => {
            resolve(res);
        });
    });
};

//post请求
let POST = (data = {}) => {
    return new Promise((resolve, reject) => {
        axios({
            method: 'post',
            url: data.url,
            data: data.params,
            cancelToken: new CancelToken((c) => {
                cancel = c;
            }),
        }).then((res) => {
            resolve(res);
        });
    });
};

// 文件上传请求
let UPLOAD = (data = {}) => {
    //封装表单数据对象
    var RequestData = new FormData();

    if (JSON.stringify(data.params) != '{}') {
        for (var key in data.params) {
            RequestData.append(key, data.params[key]);
        }
    }

    return new Promise((resolve, reject) => {
        axios({
            method: 'post',
            url: data.url,
            data: RequestData,
            headers: { 'Content-Type': 'multipart/form-data' },
            cancelToken: new CancelToken((c) => {
                cancel = c;
            }),
        }).then((res) => {
            resolve(res);
        });
    });
};

export { GET, POST, UPLOAD };

图片上传方法

if(!function_exists('build_upload'))
{
    /**
     * 单图上传
     * @param string $name 字段名
     * @return array 返回结果集
    */
    function build_upload($name)
    {
        $file = request()->file($name);

        $result = [
            'code' => 0,
            'msg' => '请上传文件',
            'data' => null
        ];

        if($file)
        {
            $info = $file->move(ROOT_PATH . 'public/uploads');

            if($info)
            {
                $result['data'] = '/uploads/' . $info->getSaveName();
                $result['code'] = 1;
                $result['msg'] = '上传成功';
            }else{
                $result['msg'] = $file->getError();
            }
        }

        return $result;
    }
}
【项目资源】:包含前端、后端、移动开发、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源,毕业设计等各种技术项目的源码。包括C++、Java、python、web、C#、EDA等项目的源码。 【适用人群】:适用于希望学习不同技术领域的初学者或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。 【项目资源】:包含前端、后端、移动开发、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源,毕业设计等各种技术项目的源码。包括C++、Java、python、web、C#、EDA等项目的源码。 【适用人群】:适用于希望学习不同技术领域的初学者或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值