Vue 电商管理系统

  • Post author:
  • Post category:vue


1、电商管理系统

本系统前端页面基于Vue和ElementUI,后端为node.js编写,主要应用于当下市场所流行的电商模式,通过科技的手段,使传统的商户交易模式变为更为高效率的电商交易模式,面向全国各地更多的客户。




项目源码https://download.csdn.net/download/Zyw907155124/64631613



https://download.csdn.net/download/Zyw907155124/64631613


2、项目演示

(1)登录页面

(2)主页

(3)用户管理

管理员可进入:实现模糊搜索、多条件分页、添加用户、编辑用户信息、删除、分配用户角色等功能。

(4)权限管理

分为1、角色列表;2、权限列表。

角色列表:可通过树状结构,查看、添加、修改、删除不同层级的权限可访问的菜单项。

权限列表:可查询上级菜单所对应的下级菜单。

(5)商品管理

分为:1、商品分类;2、商品列表;3、分类参数

商品分类:

可实现 查看、编辑不同层级的商品分类名称以及父子级关系。

商品列表:

可实现商品的多条件搜索分页,以及对具体商品的添加、修改和删除操作

本系统对于商品的添加进行了严格的校验和细致的划分。

对于商品的每一信息都进行了严格的校验,且未完成前一个步骤的情况下,无法直接跳过去进行下一个步骤的操作。

可上传商品的图片。

分类参数:负责查看、修改、删除商品分类的动态参数和静态属性

(6)订单管理

可实现查看订单的详情、修改订单付款状态、修改订单地址等功能。

(7)数据统计

通过可视化工具实现不同地区用户的来源数量。

3、源码

(1)登录页面

<template>
    <div class="login_container">
        <img src="../assets/img/bizhi2.png" style="width: 100%;height: 850px">
        <!-- 登录盒子  -->
        <div class="login_box">
            <!-- 头像 -->
            <div class="avatar_box">
                <img src="../assets/img/沃尔玛.jpg" alt="">
            </div>
            <!-- 登录表单 -->
            <el-form :model="loginForm" ref="LoginFormRef" :rules="loginFormRules" label-width="0px" class="login_form">
                <!-- 用户名 -->
                <el-form-item prop="username">
                    <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user" ></el-input>
                </el-form-item>
                <!-- 密码 -->
                <el-form-item prop="password">
                    <el-input type="password" v-model="loginForm.password" prefix-icon="iconfont icon-3702mima"></el-input>
                </el-form-item>
                <!-- 按钮 -->
                <el-form-item class="btns">
                    <el-button type="primary" @click="login">登录</el-button>
                    <el-button type="info" @click="resetLoginForm">重置</el-button>
                </el-form-item>
            </el-form>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                //数据绑定
                loginForm: {
                    username: 'admin',
                    password: '123456'
                },
                //表单验证规则
                loginFormRules: {
                    username: [
                        { required: true, message: '请输入登录名', trigger: 'blur' },
                        {
                            min: 3,
                            max: 10,
                            message: '登录名长度在 3 到 10 个字符',
                            trigger: 'blur'
                        }
                    ],
                    password: [
                        { required: true, message: '请输入密码', trigger: 'blur' },
                        {
                            min: 6,
                            max: 15,
                            message: '密码长度在 6 到 15 个字符',
                            trigger: 'blur'
                        }
                    ]
                }
            }
        },
        //添加行为,
        methods: {
            //添加表单重置方法
            resetLoginForm() {
                //this=>当前组件对象,其中的属性$refs包含了设置的表单ref
                  console.log(this)
                this.loginForm.username = '';
                this.loginForm.password = '';
                // this.$refs.LoginFormRef.resetFields()
            },
            login() {
                //点击登录的时候先调用validate方法验证表单内容是否有误
                this.$refs.LoginFormRef.validate(async valid => {
                    console.log(this.loginFormRules)
                    //如果valid参数为true则验证通过
                    if (!valid) {
                        return
                    }

                    //发送请求进行登录
                    const { data: res } = await this.$http.post('login', this.loginForm)
                    //   console.log(res);
                    if (res.meta.status !== 200) {
                        return this.$message.error('登录失败:' + res.meta.msg) //console.log("登录失败:"+res.meta.msg)
                    }

                    this.$message.success('登录成功')
                    console.log(res)
                    //保存token
                    window.sessionStorage.setItem('token', res.data.token)
                    // 导航至/home
                    this.$router.push('/home')
                })
            }
        }
    }
</script>

<style lang="less" scoped>
    .login_container {
        background-color: #2b5b6b;
        height: 100%;
    }
    .login_box {
        width: 450px;
        height: 300px;
        background: #fff;
        border-radius: 3px;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        .avatar_box {
            height: 130px;
            width: 130px;
            border: 1px solid #eee;
            border-radius: 50%;
            padding: 10px;
            box-shadow: 0 0 10px #ddd;
            position: absolute;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #fff;
            img {
                width: 100%;
                height: 100%;
                border-radius: 50%;
                background-color: #eee;
            }
        }
    }
    .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
    }
    .btns {
        display: flex;
        justify-content: flex-end;
    }
</style>

(2)主页

<template>
    <el-container class="home-container" >
        <!-- 头部区域 -->
        <el-header >
            <div>
                <!-- logo -->
<!--                <a href="Home.vue"><img src="../assets/logo.png" alt=""></a>-->
                <a href="/welcome"><img src="../assets/img/jdd.jpg" alt="" style="width: 200px;height: 20%;padding-left: 0" ></a>
                <!-- 顶部标题 -->
<!--                <span>电商后台管理系统</span>-->
                <img src="../assets/img/apple.jpg" style="width: 1350px">
            </div>
            <el-button type="info" @click="logout"> 退出 </el-button>
        </el-header>
        <!-- 页面主体区域 -->
        <el-container>
            <!-- 侧边栏 -->
            <el-aside width="200px">
                <!-- 侧边栏菜单 -->
                <el-menu
                        background-color="#333744"
                        text-color="#fff"
                        active-text-color="#ffd04b" router
                        unique-opened
                        :default-active="activePath"
                >
                    <!-- 一级菜单 -->
                    <el-submenu  v-for="item in menuList" :key="item.id" :index="item.id+''">
                        <!-- 一级菜单模板 -->
                        <template slot="title">
                            <!-- 图标 -->
                            <i :class="item.psIcon"></i>
                            <!-- 文本 -->
                            <span>{{item.authName}}</span>
                        </template>
                        <!-- 二级子菜单 -->
                        <el-menu-item :index="'/'+subItem.path" v-for="subItem in item.children" :key="subItem.id">
                            <!-- 二级菜单模板 -->
                            <template slot="title">
                                <!-- 图标 -->
                                <i class="el-icon-location"></i>
                                <!-- 文本 -->
                                <span>{{subItem.authName}}</span>
                            </template>
                        </el-menu-item>
                    </el-submenu>
                </el-menu>
            </el-aside>
            <!-- 主体结构 -->
            <el-main>
                <router-view></router-view>
            </el-main>
        </el-container>
    </el-container>
</template>

<script>
    export default {
        data() {
            return {
                // 左侧菜单数据
                menuList: null,
                // 被激活的链接地址
                activePath: ''
            }
        },
        created() {
            // 在created阶段请求左侧菜单数据
            this.getMenuList()
            this.activePath = window.sessionStorage.getItem('activePath')
        },
        methods: {
            logout() {
                window.sessionStorage.clear()
                this.$router.push('/login')
            },
            async getMenuList() {
                // 发送请求获取左侧菜单数据
                const { data: res } = await this.$http.get('menus')
                if (res.meta.status !== 200) return this.$message.error(res.meta.msg)

                this.menuList = res.data
                console.log(this.menuList)
            }
        }
    }
</script>

<style lang="less" scoped>
    .home-container {
        height: 100%;
    }
    .el-header {
        background-color: #373d41;
        display: flex;
        justify-content: space-between;
        padding-left: 0;
        align-items: center;
        color: #fff;
        font-size: 20px;
        > div {
            display: flex;
            align-items: center;
            span {
                margin-left: 15px;
            }
        }
    }

    .el-aside {
        background-color: #333744;
        .el-menu {
            border-right: none;
        }
    }

    .el-main {
        background-color: #eaedf1;
    }

    .iconfont {
        margin-right: 10px;
    }

    .toggle-button {
        background-color: #4a5064;
        font-size: 10px;
        line-height: 24px;
        color: #fff;
        text-align: center;
        letter-spacing: 0.2em;
        cursor: pointer;
    }
</style>
<template>
    <el-carousel :interval="4000" type="card" height="500px" >
        <el-carousel-item v-for="item in urls" :key="item">
            <img :src="item" >
        </el-carousel-item>
    </el-carousel>
</template>

<script>
    export default {
        data() {
            return {
                urls: [
                    'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
                    'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
                    'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
                    'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
                    'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
                    'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
                    'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',
                    'https://2oa-file.oss-cn-beijing.aliyuncs.com/t/2021-11-29/7954995fda494741af1e62d0f067ba68-car.jpg'
        ],
                currentDate: new Date()
            }
        }
    }
</script>

<style scoped>
    .el-carousel__item h3 {
        color: #475669;
        font-size: 14px;
        opacity: 0.75;
        line-height: 200px;
        margin: 0;
    }

    .el-carousel__item:nth-child(2n) {
        background-color: #99a9bf;
    }

    .el-carousel__item:nth-child(2n+1) {
        background-color: #d3dce6;
    }
    .time {
        font-size: 13px;
        color: #999;
    }

    .bottom {
        margin-top: 13px;
        line-height: 12px;
    }

    .button {
        padding: 0;
        float: right;
    }

    .image {
        width: 100%;
        display: block;
    }

    .clearfix:before,
    .clearfix:after {
        display: table;
        content: "";
    }

    .clearfix:after {
        clear: both
    }
</style>

(3)用户管理

<template>
    <div>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>用户管理</el-breadcrumb-item>
            <el-breadcrumb-item>用户列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 搜索与添加区域 -->
            <el-row :gutter="20">
                <el-col :span="7">
                    <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList">
                        <el-button slot="append" icon="el-icon-search" @click="serch"></el-button>
                    </el-input>
                </el-col>
                <el-col :span="4">
                    <el-button type="primary" @click="add()">添加用户</el-button>
                </el-col>
                <el-dialog :title=title :visible.sync="dialogFormVisible">
                    <el-form :model="users" ref="LoginFormRef" :rules="loginFormRules">
                        <el-form-item label="用户名称" prop="username">
                            <el-input v-model="users.username" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="用户密码" prop="password" v-if="users.id == null">
                            <el-input v-model="users.password" autocomplete="off" v-if="users.id == null"></el-input>
                        </el-form-item>
                        <el-form-item label="邮箱" prop="email">
                            <el-input v-model="users.email" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="手机号" prop="mobile">
                            <el-input v-model="users.mobile" autocomplete="off"></el-input>
                        </el-form-item>
                    </el-form>
                    <div slot="footer" class="dialog-footer">
                        <el-button @click="dialogFormVisible = false">取 消</el-button>
                        <el-button type="primary" @click="update(users.id)">确 定</el-button>
                    </div>
                </el-dialog>
            </el-row>
        </el-card>
        <!-- 用户列表区域 -->
        <el-table :data="userlist" border stripe>
            <el-table-column type="index"></el-table-column>
            <el-table-column label="姓名" prop="username"></el-table-column>
            <el-table-column label="邮箱" prop="email"></el-table-column>
            <el-table-column label="电话" prop="mobile"></el-table-column>
            <el-table-column label="角色" prop="role_name"></el-table-column>
            <el-table-column label="状态">
                <template slot-scope="scope">
                    <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="180px">
                <template slot-scope="scope">
                    <!-- 修改按钮 -->
                    <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button>
                    <!-- 删除按钮 -->
                    <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"></el-button>
                    <!-- 分配角色按钮 -->
                    <el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">
                        <el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button>
                    </el-tooltip>
                    <template>
                        <el-dialog :title="title" :visible.sync="duoxuan">
                        <el-select v-model="rid" autocomplete="off">
                            <el-option
                                    v-for="role in rolelist"
                                    :key="role.id"
                                    :label="role.roleName"
                                    :value="role.id">
                            </el-option>
                        </el-select>
                            <el-button type="primary" @click="setR(rid)">确 定</el-button>
                        </el-dialog>
                    </template>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页导航区域
@size-change(pagesize改变时触发)
@current-change(页码发生改变时触发)
:current-page(设置当前页码)
:page-size(设置每页的数据条数)
:total(设置总页数) -->
        <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
        </el-pagination>
    </div>
</template>

<script>
    export default {
        data () {
            return {
                // 获取查询用户信息的参数
                title:'',
                sea:false,
                duoxuan:false,
                users: {
                    id: '',
                    username: '',
                    password: '',
                    email: '',
                    mobile: ''
                },
                rid:'',
                rolelist:[],
                visible: false,
                formLabelWidth: '120px',
                dialogFormVisible: false,
                form2:{
                    email:'',mobile:''
                },
                queryInfo: {
                    query: '',
                    pagenum: 1,
                    pagesize: 2
                },
                //表单验证规则
                loginFormRules: {
                    username: [
                        {required: true, message: '请输入登录名', trigger: 'blur'},
                        {
                            min: 3,
                            max: 10,
                            message: '登录名长度在 3 到 10 个字符',
                            trigger: 'blur'
                        }
                    ],
                    password: [
                        {required: true, message: '请输入密码', trigger: 'blur'},
                        {
                            min: 6,
                            max: 15,
                            message: '密码长度在 6 到 15 个字符',
                            trigger: 'blur'
                        }
                    ],
                    email: [
                        // {required: true, message: '请输入邮箱', triggger: 'blur'},
                        {
                            pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
                            message: "正确的邮箱格式为xxx@xx.com",
                            trigger: 'blur'
                        }
                    ],
                    mobile: [
                        // {required: true, message: "请输入手机号码", trigger: "blur"},
                        {
                            pattern: /^1[356789]\d{9}$/,
                            message: "请填写11位正确的手机号码",
                            trigger: "blur"
                        }]
                },
                // 保存请求回来的用户列表数据
                userlist: [],
                total: 0,
                options: [{}],
                value: ''
            }
        },
        created () {
            this.getUserList()
        },
        methods: {
            serch(){
                if (this.queryInfo.query.trim()!=""){
                    this.sea = true;
                }
                this.queryInfo.pagenum = 1;
                this.getUserList();
            },
            async getUserList() {
                const { data: res } = await this.$http.get('users', {
                    params: this.queryInfo
                })
                if (res.meta.status !== 200) {
                    return this.$message.error('获取用户列表失败!')
                }
                this.userlist = res.data.users
                this.total = res.data.total
                console.log(res)
            },
            async setRole(row){
                this.duoxuan = true;
                this.id = row.id;
                this.rid = row.roleId;
                const { data: res } = await this.$http.get('roles/')
                if (res.meta.status !== 200) {
                    return this.$message.error('获取角色列表失败!')
                }
                this.rolelist = res.data;
            },
            setR(rid){
                console.log("rid="+rid);
                console.log("this.id="+this.id);
                console.log("users/:"+this.id+"/"+rid);
                this.$http.put("users/"+this.id+"/"+rid);
                this.getUserList();
                this.id = '';
                this.duoxuan = false;
            },
            handleSizeChange(newSize) {
                //pagesize改变时触发,当pagesize发生改变的时候,我们应该
                //以最新的pagesize来请求数据并展示数据
                //   console.log(newSize)
                // this.search?this.queryInfo.pagesize=1:this.queryInfo.pagesize = newSize;
                this.queryInfo.pagesize = newSize;
               //重新按照pagesize发送请求,请求最新的数据
                this.getUserList();
            },
            handleCurrentChange( current ) {
                //页码发生改变时触发当current发生改变的时候,我们应该
                //以最新的current页码来请求数据并展示数据
                //   console.log(current)
                if (!this.search){
                    this.queryInfo.pagenum = current;
                }
                //重新按照pagenum发送请求,请求最新的数据
                this.getUserList();
            },
            async userStateChanged(row) {
                //发送请求进行状态修改
                const { data: res } = await this.$http.put(
                    `users/${row.id}/state/${row.mg_state}`
                )
                //如果返回状态为异常状态则报错并返回
                if (res.meta.status !== 200) {
                    row.mg_state = !row.mg_state
                    return this.$message.error('修改状态失败')
                }
                this.$message.success('更新状态成功')
            },
            //添加
            add() {
                this.users = {};
                this.title = "添加";
                this.dialogFormVisible = true;
            },
            // 修改
            async showEditDialog(id) {
                //找出需要修改的数据的信息
                const {data: res} = await this.$http.get("users/" + id);
                if (res.meta.status !== 200) {
                    return this.$message.error('查询失败')
                }
                this.$message.success('查询成功');
                this.users = res.data;
                console.log(this.users);
                this.title = "修改";
                this.dialogFormVisible = true;
            },
            //添加/修改
            async update(id) {
                this.$refs.LoginFormRef.validate(async valid => {
                    console.log(this.loginFormRules);
                    //如果valid参数为true则验证通过
                    if (!valid) {
                        return
                    }
                    if (id != null) {
                        const {data: res} = await this.$http.put(`users/${id}`, this.users);
                        if (res.meta.status !== 200) {
                            return this.$message.error('修改失败')
                        }
                        this.$message.success('修改成功');
                    } else {
                        const {data: res} = await this.$http.post("users", this.users);
                        if (res.meta.status !== 201) {
                            return this.$message.error('添加失败')
                        } else {
                            this.$message.success('添加成功');
                        }
                    }
                    this.users = '';
                    this.dialogFormVisible = false;
                    this.getUserList()
                })
            },
            removeUserById(id){
                this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    this.$http.delete("users/"+id).then(() => {
                        this.queryInfo.pagenum = this.userlist.length === 1 ? this.queryInfo.pagenum - 1 : this.queryInfo.pagenum;
                        this.queryInfo.pagenum = this.queryInfo.pagenum < 1 ? 1 : this.queryInfo.pagenum;
                        this.getUserList();
                    }),
                        this.$message({
                            type: 'success',
                            message: '删除成功!'
                        });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            },
            //获取角色列表

        }
    }
</script>

<style scoped>

</style>

(4)权限管理

1、角色列表;

<template>
    <div>
        <!-- 面包屑导航区域 -->
        <el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>权限管理</el-breadcrumb-item>
            <el-breadcrumb-item>角色列表</el-breadcrumb-item>
        </el-breadcrumb>

        <!-- 卡片视图 -->
        <el-card>
            <!-- 添加角色按钮区域 -->
            <el-row>
                <el-col>
                    <el-button type="primary" @click="res()">添加角色</el-button>
                    <el-dialog :visible.sync="dialogFormVisible">
                        <el-form :model="role1" >
                            <el-form-item label="角色名称" prop="roleName">
                                <el-input v-model="role1.roleName" autocomplete="off"></el-input>
                            </el-form-item>
                            <el-form-item label="角色描述" prop="roleDesc">
                                <el-input v-model="role1.roleDesc" autocomplete="off"></el-input>
                            </el-form-item>
                        </el-form>
                        <div slot="footer" class="dialog-footer">
                            <el-button @click="dialogFormVisible = false">取 消</el-button>
                            <el-button type="primary" @click="insert()">确 定</el-button>
                        </div>
                    </el-dialog>
                </el-col>
            </el-row>

            <!-- 角色列表区域 -->
            <el-table :data="roleList" border stripe>
                <!-- 展开列 -->
                <el-table-column type="expand">
                    <template slot-scope="scope">
                        <el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id">
                            <!-- 渲染一级权限 -->
                            <el-col :span="5">
                                <el-tag closable >{{item1.authName}}</el-tag>
                                <i class="el-icon-caret-right" @close="removeRightById(scope.row, item1.id)"></i>
                            </el-col>
                            <!-- 渲染二级和三级权限 -->
                            <el-col :span="19">
                                <!-- 通过 for 循环 嵌套渲染二级权限 -->
                                <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id">
                                    <el-col :span="6">
                                        <el-tag type="success" closable>{{item2.authName}}</el-tag>
                                        <i class="el-icon-caret-right" @close="removeRightById(scope.row, item2.id)"></i>
                                    </el-col>
                                    <el-col :span="18">
                                        <el-tag closable type="warning" v-for="item3 in item2.children" :key="item3.id">{{item3.authName}}</el-tag>
                                        <i class="el-icon-caret-right" @close="removeRightById(scope.row, item3.id)"></i>
                                    </el-col>
                                </el-row>
                            </el-col>
                        </el-row>
                    </template>
                </el-table-column>
                <el-table-column label="角色名称" prop="roleName"></el-table-column>
                <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
                <el-table-column label="操作" width="300px">
                    <template slot-scope="scope">
                        <el-button size="mini" type="primary" icon="el-icon-edit" @click="selectById(scope.row.id)" >编辑</el-button>
                        <el-dialog :visible.sync="dialogFormVisible2">
                            <el-form :model="role" ref="LoginFormRef">
                                <el-form-item label="角色名称" prop="roleName">
                                    <el-input v-model="role.roleName" autocomplete="off"></el-input>
                                </el-form-item>
                                <el-form-item label="角色描述" prop="roleDesc">
                                    <el-input v-model="role.roleDesc" autocomplete="off"></el-input>
                                </el-form-item>
                            </el-form>
                            <div slot="footer" class="dialog-footer">
                                <el-button @click="dialogFormVisible2 = false">取 消</el-button>
                                <el-button type="primary" @click="upda(scope.row.id)">确 定</el-button>
                            </div>
                        </el-dialog>
                        <el-button size="mini" type="danger" icon="el-icon-delete" @click="delRole(scope.row.id)">删除</el-button>
                        <el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限</el-button>
                        <!-- 分配权限对话框 -->
                        <el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClose">
                            <!-- 树形组件
                            show-checkbox:显示复选框
                            node-key:设置选中节点对应的值
                            default-expand-all:是否默认展开所有节点
                            :default-checked-keys 设置默认选中项的数组
                            ref:设置引用 -->
                            <el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree>
                            <span slot="footer" class="dialog-footer">
        <el-button @click="setRightDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="allotRights">确 定</el-button>
    </span>
                        </el-dialog>
                    </template>
                </el-table-column>
            </el-table>
        </el-card>
    </div>
</template>


<script>
    export default {
        data() {
            return {
                // 角色列表数据
                roleList: [],
                // 控制分配权限对话框的显示
                setRightDialogVisible: false,
                // 权限树数据
                rightsList: [],
                // 树形控件的属性绑定对象
                treeProps: {
                    // 通过label设置树形节点文本展示authName
                    label: 'authName',
                    // 设置通过children属性展示子节点信息
                    children: 'children'
                },
                // 设置树形控件中默认选中的内容
                defKeys: [],
                // 保存正在操作的角色id
                roleId: '',
                role1:{
                    roleName:'',roleDesc:''
                },
                role:{
                    id:'',roleName:'',roleDesc:''
                },
                dialogFormVisible:false,
                dialogFormVisible2:false
            }
        },
        created() {
            this.getRoleList()
        },
        methods: {
            async getRoleList() {
                const { data: res } = await this.$http.get('roles')
                // 如果返回状态为异常状态则报错并返回
                if (res.meta.status !== 200) { return this.$message.error('获取角色列表失败') }
                // 如果返回状态正常,将请求的数据保存在data中
                // this.roleList = res.data
                console.log(res.data)
                this.roleList = res.data
            },
            async removeRightById(role, rightId) {
                // 弹窗提示用户是否要删除
                const confirmResult = await this.$messagebox.confirm(
                    '请问是否要删除该权限',
                    '删除提示',
                    {
                        confirmButtonText: '确认删除',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }
                ).catch(err => err)
                // 如果用户点击确认,则confirmResult 为'confirm'
                // 如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'
                if (confirmResult !== 'confirm') {
                    return this.$message.info('已经取消删除')
                }

                // 用户点击了确定表示真的要删除
                // 当发送delete请求之后,返回的数据就是最新的角色权限信息
                const { data: res } = await this.$http.delete(
                    `roles/${role.id}/rights/${rightId}`
                )
                if (res.meta.status !== 200) { return this.$message.error('删除角色权限失败') }

                // 无需再重新加载所有权限
                // 只需要对现有的角色权限进行更新即可
                role.children = res.data
                // this.getRoleList();
            },
            async showSetRightDialog(role) {
                // 将role.id保存起来以供保存权限时使用
                this.roleId = role.id
                // 获取所有权限的数据
                const { data: res } = await this.$http.get('rights/tree')
                // 如果返回状态为异常状态则报错并返回
                if (res.meta.status !== 200) return this.$message.error('获取权限树失败')
                // 如果返回状态正常,将请求的数据保存在data中
                this.rightsList = res.data

                // 调用getLeafKeys进行递归,将三级权限添加到数组中
                this.getLeafKeys(role, this.defKeys)
                // 当点击分配权限按钮时,展示对应的对话框
                this.setRightDialogVisible = true
                console.log(this.defKeys)
            },
            getLeafKeys(node, arr) {
                // 该函数会获取到当前角色的所有三级权限id并添加到defKeys中
                // 如果当前节点不包含children属性,则表示node为三级权限
                if (!node.children) {
                    return arr.push(node.id)
                }
                // 递归调用
                node.children.forEach(item => this.getLeafKeys(item, arr))
            },
            setRightDialogClose() {
                // 当用户关闭树形权限对话框的时候,清除掉所有选中状态
                this.defKeys = []
            },
            async allotRights() {
                // 当用户在树形权限对话框中点击确定,将用户选择的
                // 权限发送请求进行更新

                // 获取所有选中及半选的内容
                const keys = [
                    ...this.$refs.treeRef.getCheckedKeys(),
                    ...this.$refs.treeRef.getHalfCheckedKeys()
                ]
                // 将数组转换为 , 拼接的字符串
                const idStr = keys.join(',')

                // 发送请求完成更新
                const { data: res } = await this.$http.post(
                    `roles/${this.roleId}/rights`,
                    { rids: idStr }
                )
                if (res.meta.status !== 200) { return this.$message.error('分配权限失败') }

                this.$message.success('分配权限成功')
                this.getRoleList()
                // 关闭对话框
                this.setRightDialogVisible = false
            },
            res(){
                this.role1 = {};
                this.dialogFormVisible = true;
            },
            //新增
            async insert(){
                    //新增
                    console.log("新增");
                    const {data: res} = await this.$http.post(`roles`, this.role1);
                    if (res.meta.status !== 201) {
                        return this.$message.error('添加失败')
                    } else {
                        this.$message.success('添加成功');
                    }
                this.role1 = '';
                this.dialogFormVisible = false;
                this.getRoleList();
            },

            async selectById(id){
                const {data: res} = await this.$http.get(`roles/`+id, this.role);
                if (res.meta.status !== 200) {
                    return this.$message.error('查询失败')
                }
                this.$message.success('查询成功');
                this.role.id = res.data.id;
                this.role.roleName = res.data.roleName;
                this.role.roleDesc = res.data.roleDesc;
                console.log(this.role);
                this.dialogFormVisible2 = true;
            },
            //修改
            async upda(id){
                console.log("id="+id);
                const {data: res} = await this.$http.put(`roles/${id}`,this.role);
                if (res.meta.status !== 200) {
                    return this.$message.error('修改失败')
                } else {
                    this.$message.success('修改成功');
                }
                this.role = {};
                this.dialogFormVisible2 = false;
                this.getRoleList();
            },
            delRole(id){
                this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    console.log("id="+id);
                    this.$http.delete(`roles/${id}`).then(() => {
                        this.getRoleList();
                    })
                }),this.$message({
                    type: 'success',
                    message: '删除成功!'
                }).catch(() => {
        this.$message({
            type: 'info',
            message: '已取消删除'
        });
    });
            }
        }
    }
</script>
<style lang="less" scoped>
    .el-tag {
        margin: 7px;
    }
    .bdtop {
        border-top: 1px solid #eee;
    }
    .bdbottom {
        border-bottom: 1px solid #eee;
    }
    .vcenter {
        display: flex;
        align-items: center;
    }
</style>

2、权限列表。

<template>
    <div>
        <!-- 面包屑导航区域 -->
        <el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>权限管理</el-breadcrumb-item>
            <el-breadcrumb-item>权限列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图 -->
        <el-card>
<!--            <tree-table :data="quanxianList" :columns="columns" :selection-type="false"-->
<!--                                              :expand-type="false" show-index index-text="#" :show-row-hover="false">-->
<!--                <template slot="level" scope="scope">-->
<!--                    <el-tag type="primary" effect="plain" size="mini" v-if="scope.row.children!=null && scope.row.pid==0">一级</el-tag>-->
<!--                    <el-tag type="success" effect="plain" size="mini" v-else-if="scope.row.children!=null && scope.row.pid!=0">二级</el-tag>-->
<!--                    <el-tag type="warning" effect="plain" size="mini" v-else>三级</el-tag>-->
<!--                </template>-->
<!--            </tree-table>-->
<!--             角色列表区域-->
            <el-table :data="quanxianList" border stripe>
                <!-- 展开列 -->
                <el-table-column type="expand">
<!--                    <el-table-column label="" prop="authName"></el-table-column>-->

                    <template slot-scope="scope">
                        <el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id">
                        <!-- 渲染一级权限 -->
                            <el-col :span="5">
                                <el-tag >{{item1.authName}}</el-tag>
                                <i class="el-icon-caret-right"></i>
                            </el-col>
                            <!-- 渲染二级和三级权限 -->
                            <el-col :span="19">
                                <!-- 通过 for 循环 嵌套渲染二级权限 -->
                                <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id">
                                    <el-col :span="6">
                                        <el-tag type="success" >{{item2.authName}}</el-tag>
                                        <i class="el-icon-caret-right"></i>
                                    </el-col>
                                    <el-col :span="18">
                                        <el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item3, i2) in item1.children" :key="item3.id">
                                            <el-tag  type="warning">
                                                {{item3.authName}}
                                            </el-tag>
                                            <i class="el-icon-caret-right"></i>
                                        </el-row>
                                    </el-col>
                                </el-row>
                            </el-col>
                        </el-row>
                    </template>
                </el-table-column>
                <!-- 角色列表区域 -->
                    <!-- 添加展开列 -->
<!--                    <el-table-column label="权限ID" prop="id"></el-table-column>-->
                    <el-table-column label="权限说明" prop="authName"></el-table-column>
                    <el-table-column label="权限层级" prop="level"></el-table-column>
                    <el-table-column label="权限父ID" prop="pid"></el-table-column>
                    <el-table-column label="对应访问路径" prop="path"></el-table-column>
            </el-table>


        </el-card>
    </div>
</template>


<script>
    export default {
        data () {
            return {
                // quanxianList: [],
                quanxianList: {
                    // 通过label设置树形节点文本展示authName
                    label: 'authName',
                    // 设置通过children属性展示子节点信息
                    children: 'children'
                },
                menus:{
                    id:'',authName:'',path:'',children:''
                }
            }
        },
        created () {
            this.getRoleList()
        },
        methods: {
            async getRoleList () {
                const { data: res } = await this.$http.get('rights/tree')
                // 如果返回状态为异常状态则报错并返回
                if (res.meta.status !== 200) {
                    return this.$message.error('获取角色列表失败')
                }
                console.log(res.data)
                // 如果返回状态正常,将请求的数据保存在data中
                this.quanxianList = res.data
            },
            async getLeftList () {
                const { data: res } = await this.$http.get('menus')
                // 如果返回状态为异常状态则报错并返回
                if (res.meta.status !== 200) {
                    return this.$message.error('获取菜单列表失败')
                }
                console.log(res.data)
                // 如果返回状态正常,将请求的数据保存在data中
                this.menus = res.data
            }
        }
    }
</script>

(5)商品管理

1、商品分类;

<template>
    <div>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>商品分类</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 添加分类按钮区域 -->
            <el-row>
                <el-col>
                    <el-button type="primary" @click="showAddCateDialog">添加分类</el-button>
                </el-col>
            </el-row>
            <!-- 添加分类对话框 -->
            <el-dialog title="添加分类" :visible.sync="addCateDialogVisible" width="50%"  @close="addCateDialogClosed">
                <!-- 添加分类表单 -->
                <el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRef" label-width="100px">
                    <el-form-item label="分类名称" prop="cat_name">
                        <el-input v-model="addCateForm.cat_name"></el-input>
                    </el-form-item>
                                        <el-form-item label="父级分类" prop="cat_pid" v-if="this.pid==''">
                                            <!-- expandTrigger='hover'(鼠标悬停触发级联) v-model(设置级联菜单绑定数据) :options(指定级联菜单数据源)  :props(用来配置数据显示的规则)
                                            clearable(提供“X”号完成删除文本功能) change-on-select(是否可以选中任意一级的菜单) -->
                                            <el-cascader expandTrigger='hover' v-model="selectedKeys" :options="parentCateList" :props="cascaderProps" @change="parentCateChange" clearable change-on-select></el-cascader>
                                        </el-form-item>
                </el-form>
                <span slot="footer" class="dialog-footer">
    <el-button @click="res">取 消</el-button>
    <el-button type="primary" @click="addCate">确 定</el-button>
  </span>
            </el-dialog>
            <!-- 分类表格  -->
            <!-- 分类表格
            :data(设置数据源) :columns(设置表格中列配置信息) :selection-type(是否有复选框)
            :expand-type(是否展开数据) show-index(是否设置索引列) index-text(设置索引列头)
            border(是否添加纵向边框) :show-row-hover(是否鼠标悬停高亮) -->
            <tree-table :data="cateList" :columns="columns" :selection-type="false"
                        :expand-type="false" show-index index-text="#" border :show-row-hover="false">
                <!-- 排序 -->
                <template slot="order" slot-scope="scope">
                    <el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag>
                    <el-tag size="mini" type="success" v-else-if="scope.row.cat_level===1">二级</el-tag>
                    <el-tag size="mini" type="warning" v-else>三级</el-tag>
                </template>
                <!-- 操作 -->
                <template slot="opt" slot-scope="scope">
                    <el-button size="mini" type="primary" icon="el-icon-edit" @click="bj(scope.row.cat_id)">编辑</el-button>
                    <el-button size="mini" type="danger" icon="el-icon-delete" @click="del(scope.row.cat_id)">删除</el-button>
                </template>
            </tree-table>
            <!-- 分页 -->
            <!-- 分页 -->
            <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
            </el-pagination>
        </el-card>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                // 商品分类数据列表
                cateList: [],
                columns: [
                    {label:'分类名称',prop:'cat_name'},
                    {label:'排序',prop:'',type:'template',template:'order'},
                    {label:'操作',prop:'',type:'template',template:'opt'}
                ],
                // 查询分类数据的条件
                queryInfo: {
                    type: 3,
                    pagenum: 1,
                    pagesize: 5
                },
                pid:'',
                // 保存总数据条数
                total: 0,
                //用来显示或隐藏添加分类对话框
                addCateDialogVisible: false,
                dialogFormVisible2: false,
//添加分类的表单数据对象
                addCateForm:{
                    //分类名称
                    cat_name:'',
                    //添加分类的父级id,0则表示父级为0.添加一级分类
                    cat_pid:0,
                    //添加分类的等级,0则表示添加一级分类
                    cat_level:0
                },
//保存1,2级父级分类的列表
                parentCateList:[],
//添加分类校验规则
                addCateFormRules:{
                    //验证规则
                    cat_name:[ {required:true , message:'请输入分类名称',trigger:'blur'} ]
                },
                //配置级联菜单中数据如何展示
                cascaderProps:{
                    value:'cat_id',
                    label:'cat_name',
                    children:'children',
                    expandTrigger:'hover'
                },
//绑定用户选择的分类值
                selectedKeys:[]
            }
        },
        created() {
            this.getCateList()
        },
        methods: {
            async getCateList() {
                // 获取商品分类数据
                const { data: res } = await this.$http.get('categories', {
                    params: this.queryInfo
                })

                if (res.meta.status !== 200) {
                    return this.$message.error('获取商品列表数据失败')
                }
                // 将数据列表赋值给cateList
                this.cateList = res.data.result
                // 保存总数据条数
                this.total = res.data.total
                //   console.log(res.data);
            },
                handleSizeChange(newSize){
                    //当pagesize发生改变时触发
                    this.queryInfo.pagesize = newSize;
                    this.getCateList();
                },
                handleCurrentChange(newPage){
                    //当pagenum发生改变时触发
                    this.queryInfo.pagenum = newPage;
                    this.getCateList();
                },
            showAddCateDialog() {
                //调用getParentCateList获取分类列表
                this.getParentCateList()
                //显示添加分类对话框
                this.addCateDialogVisible = true;
            },
            res(){
                this.addCateForm.cat_name = '';
                this.addCateDialogVisible = false;
                this.pid = '';
            },
            async getParentCateList(){
                //获取父级分类数据列表
                const { data: res } = await this.$http.get('categories', {
                    params: {type:2}
                })
                if (res.meta.status !== 200) {
                    return this.$message.error('获取商品分类列表数据失败')
                }
                this.parentCateList = res.data
            },
            parentCateChange(){
                //级联菜单中选择项发生变化时触发
                console.log(this.selectedKeys)
                //如果用户选择了父级分类
                if(this.selectedKeys.length > 0){
                    //则将数组中的最后一项设置为父级分类
                    this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]
                    //level也要跟着发生变化
                    this.addCateForm.cat_level = this.selectedKeys.length
                    return
                }else{
                    this.addCateForm.cat_pid = 0
                    this.addCateForm.cat_level = 0
                    return
                }
            },
            addCateDialogClosed(){
                //当关闭添加分类对话框时,重置表单
                //this.$refs.addCateFormRef.resetFields()
                this.selectedKeys = [];
                this.addCateForm.cat_pid = 0
                this.addCateForm.cat_level = 0
            },
            async addCate() {
                console.log('pid='+this.pid);
                if (this.pid==''){
                    console.log('添加');
                    //点击确定,完成添加分类
                    console.log('cat_name='+this.addCateForm.cat_name)
                    console.log('cat_pid='+this.addCateForm.cat_pid)
                    this.$refs.addCateFormRef.validate(async valid => {
                        if (!valid) return
                        //发送请求完成添加分类
                        const {data: res} = await this.$http.post(
                            'categories',
                            this.addCateForm
                        )

                        if (res.meta.status !== 201) {
                            return this.$message.error('添加分类失败')
                        }
                        this.$message.success('添加分类成功')
                        this.getCateList()
                        this.addCateDialogVisible = false;
                        this.addCateForm = {};
                    });
                }else {
                    //编辑
                    console.log('编辑');

                    const {data: res} = await this.$http.put('categories/'+this.pid, this.addCateForm);
                    if (res.meta.status !== 200) {
                        return this.$message.error('编辑分类失败')
                    }else {
                        this.$message.success('编辑分类成功')
                    }
                    this.getCateList();
                    this.addCateDialogVisible = false;
                    this.addCateForm = {};
                    this.pid = '';
                }

            },
            async bj(id){
              this.pid = id;
              console.log("id="+id);
                this.addCateDialogVisible= true;
                const {data: res} = await this.$http.get('categories/'+this.pid);
                this.addCateForm = res.data;
            },
            del(id){
                this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    console.log("id="+id);
                    this.$http.delete("categories/"+id).then(() => {
                        this.queryInfo.pagenum = this.cateList.length === 1 ? this.queryInfo.pagenum - 1 : this.queryInfo.pagenum;
                        this.queryInfo.pagenum = this.queryInfo.pagenum < 1 ? 1 : this.queryInfo.pagenum;
                        this.getCateList();
                    }),
                        this.$message({
                            type: 'success',
                            message: '删除成功!'
                        });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            },
            }
    }
</script>

<style scoped>

</style>

2、商品列表;

<template>
    <div>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>商品列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 搜索栏 -->
            <el-row :gutter="20">
                <el-col :span="8">
                    <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable>
                        <el-button slot="append" icon="el-icon-search" @click="sea()"/>
                    </el-input>
                </el-col>
                <el-col :span="4">
                    <el-button type="primary" @click="goAddPage">添加商品</el-button>
                </el-col>
            </el-row>

            <!-- 表格区域 -->
            <el-table :data="goodsList" border stripe>
                <el-table-column type="index"/>
                <el-table-column label="商品名称" prop="goods_name"/>
                <el-table-column label="商品价格(元)" prop="goods_price" width="95px"/>
                <el-table-column label="商品重量" prop="goods_weight" width="95px"/>
                <el-table-column label="创建时间" prop="add_time" width="140px">
                    <template slot-scope="scope">
                        {{scope.row.add_time | dateFormat}}
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="125px">
                    <template slot-scope="scope">
                        <el-button size="mini" type="primary" icon="el-icon-edit" @click="update(scope.row.goods_id)"/>
                        <el-button size="mini" type="danger" icon="el-icon-delete" @click="del(scope.row.goods_id)"/>
                    </template>
                </el-table-column>
            </el-table>

            <!-- 分页 -->
            <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                           :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]"
                           :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper"
                           :total="total">
            </el-pagination>
            <el-dialog title="修改商品" :visible.sync="dialogFormVisible" width="60%">
                <el-scrollbar style="height: 400px">
                    <el-form :model="good" ref="updateGoodsForm">
                        <el-form-item label="商品名称" :label-width="formLabelWidth" prop="goods_name">
                            <el-input v-model="good.goods_name" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="商品价格" :label-width="formLabelWidth" prop="goods_price">
                            <el-input v-model="good.goods_price" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="商品重量" :label-width="formLabelWidth" prop="goods_weight">
                            <el-input v-model="good.goods_weight" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="商品数量" :label-width="formLabelWidth" prop="goods_number">
                            <el-input v-model="good.goods_number" autocomplete="off"></el-input>
                        </el-form-item>
                        <el-form-item label="商品介绍" :label-width="formLabelWidth" prop="goods_introduce">
                            <quill-editor v-model="good.goods_introduce" autocomplete="off"></quill-editor>
                        </el-form-item>
                    </el-form>
                </el-scrollbar>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="dialogFormVisible = false">取 消</el-button>
                    <el-button type="primary" @click="updateGood()">确 定</el-button>
                </div>
            </el-dialog>
        </el-card>
    </div>
</template>

<script>
    export default {
        name: "goods",
        data() {
            return {
                dialogFormVisible: false,
                formLabelWidth: '120px',
                sear: false,
                good: {

                },
                //查询参数
                queryInfo: {
                    query: '',
                    pagenum: 1,
                    pagesize: 10
                },
                //保存商品列表信息
                goodsList: [],
                //总数据条数
                total: 0,
            }
        },
        created() {
            this.getGoodsList()
        },
        methods: {
            sea() {
                if (this.queryInfo.query.trim() !== '') {
                    this.sear = true;
                }
                this.queryInfo.pagenum = 1;
                this.getGoodsList();
            },
            async getGoodsList() {
                //   根据分页获取对应的商品列表
                const {data: res} = await this.$http.get('goods', {
                    params: this.queryInfo
                });

                if (res.meta.status !== 200) {
                    return this.$message.error('获取商品列表失败')
                }
                console.log(res.data);
                this.$message.success('获取商品列表成功');
                this.goodsList = res.data.goods;
                this.total = res.data.total
            },
            handleSizeChange(newSize) {
                //当页号发生改变时,更改pagesize,重新请求
                this.queryInfo.pagesize = newSize;
                this.getGoodsList();
            },
            handleCurrentChange(newPage) {
                //当页码发生改变时,更改pagesize,重新请求
                if (!this.sear) {
                    this.queryInfo.query = '';
                }
                this.queryInfo.pagenum = newPage;
                this.getGoodsList();
            },
            goAddPage() {
                this.$router.push('/goods/add')
            },
            async update(id) {
                const {data: res} = await this.$http.get("goods/" + id);
                if (res.meta.status !== 200) {
                    return this.$message.error('查询商品失败')
                }
                this.$message.success('查询商品成功');
                this.good = res.data;
                console.log(this.good);
                this.dialogFormVisible = true;
            },
            async updateGood() {
                const {data: res} = await this.$http.put(`goods/${this.good.goods_id}`, this.good);
                console.log(res);
                if (res.meta.status !== 200) {
                    return this.$message.error('修改商品失败')
                }
                this.$message.success('修改商品成功');
                // 关闭对话框
                this.dialogFormVisible = false;
                this.getGoodsList()
            },
            async del(id) {
                const {data: res} = await this.$http.delete(`goods/${id}`);
                if (res.meta.status !== 200) {
                    return this.$message.error('删除商品失败')
                }
                this.$message.success('删除商品成功');
                this.getGoodsList();
            }

        }

    }
</script>

<style scoped>

</style>

添加商品

<template>
    <div>
        <h3>添加商品</h3>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>添加商品</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 消息提示 -->
            <el-alert title="添加商品信息" type="info" center show-icon :closable="false">
            </el-alert>

            <!-- 步骤条组件 -->
            <!-- align-center(居中效果) -->
            <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
                <el-step title="基本信息"></el-step>
                <el-step title="商品参数"></el-step>
                <el-step title="商品属性"></el-step>
                <el-step title="商品图片"></el-step>
                <el-step title="商品内容"></el-step>
                <el-step title="完成"></el-step>
            </el-steps>

            <!-- tab栏区域:el-tab-pane必须是el-tabs的子节点
            :tab-position="'left'"(设置tab栏为左右结构tab栏) -->
            <!-- 表单:label-position="top"(设置label在文本框上方) -->
            <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top">
                <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked">
                    <el-tab-pane label="基本信息" name="0">
                        <el-form-item label="商品名称" prop="goods_name">
                            <el-input v-model="addForm.goods_name"></el-input>
                        </el-form-item>
                        <el-form-item label="商品价格" prop="goods_price">
                            <el-input v-model="addForm.goods_price" type="number"></el-input>
                        </el-form-item>
                        <el-form-item label="商品重量" prop="goods_weight">
                            <el-input v-model="addForm.goods_weight" type="number"></el-input>
                        </el-form-item>
                        <el-form-item label="商品数量" prop="goods_number">
                            <el-input v-model="addForm.goods_number" type="number"></el-input>
                        </el-form-item>
                        <el-form-item label="商品分类" prop="goods_cat">
                            <!-- 选择商品分类的级联选择框 -->
                            <el-cascader expandTrigger='hover' v-model="addForm.goods_cat" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品参数" name="1">
                        <!-- 渲染表单item项 -->
                        <el-form-item :label="item.attr_name" :key="item.attr_id" v-for="item in manyTableData">
                            <!-- 使用数组渲染复选框组 -->
                            <el-checkbox-group v-model="item.attr_vals">
                                <el-checkbox border :label="val" v-for="(val,i) in item.attr_vals" :key="i"></el-checkbox>
                            </el-checkbox-group>
                        </el-form-item>
                    </el-tab-pane>
                    <el-tab-pane label="商品属性" name="2">
                        <!-- 循环生成静态属性 -->
                        <el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id">
                            <el-input v-model="item.attr_vals"></el-input>
                        </el-form-item>
                    </el-tab-pane>
<!--                    //在页面中添加upload组件,并设置对应的事件和属性-->
                    <el-tab-pane label="商品图片" name="3">
                        <!-- 商品图片上传
                        action:指定图片上传api接口
                        :on-preview : 当点击图片时会触发该事件进行预览操作,处理图片预览
                        :on-remove : 当用户点击图片右上角的X号时触发执行
                        :on-success:当用户点击上传图片并成功上传时触发
                        list-type :设置预览图片的方式
                        :headers :设置上传图片的请求头 -->
                        <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" list-type="picture" :headers="headerObj">
                            <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-tab-pane>
                    <!-- 富文本编辑器组件 -->
                    <el-tab-pane label="商品内容" name="4">
                        <!-- 富文本编辑器组件 -->
                        <quill-editor v-model="addForm.goods_introduce"></quill-editor>
                        <!-- 添加商品按钮 -->
                        <el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
                    </el-tab-pane>
                </el-tabs>
            </el-form>
        </el-card>
<!--        //在el-card卡片视图下面添加对话框用来预览图片-->
        <!-- 预览图片对话框 -->
        <el-dialog title="图片预览" :visible.sync="previewVisible" width="50%">
            <img :src="previewPath" class="previewImg" />
        </el-dialog>
    </div>
</template>
<script>
    var _ = require('lodash');
    export default {
        data() {
            return {
                //保存步骤条激活项索引
                activeIndex: '0',
                //添加商品的表单数据对象
                addForm: {
                    goods_name: '',
                    goods_price: 0,
                    goods_weight: 0,
                    goods_number: 0,
                    goods_cat: [],
                    //上传图片数组
                    pics: [],
                    //商品的详情介绍
                    goods_introduce:'',
                    attrs: []
                },
                //上传图片的url地址
                uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
                //图片上传组件的headers请求头对象
                headerObj: { Authorization: window.sessionStorage.getItem('token') },
                //保存预览图片的url地址
                previewPath: '',
                //控制预览图片对话框的显示和隐藏
                previewVisible:false,
                //动态参数列表
                manyTableData: [],
                //静态属性列表
                onlyTableData:[],
                //验证规则
                addFormRules: {
                    goods_name: [
                        { required: true, message: '请输入商品名称', trigger: 'blur' }
                    ],
                    goods_price: [
                        { required: true, message: '请输入商品价格', trigger: 'blur' }
                    ],
                    goods_weight: [
                        { required: true, message: '请输入商品重量', trigger: 'blur' }
                    ],
                    goods_number: [
                        { required: true, message: '请输入商品数量', trigger: 'blur' }
                    ],
                    goods_cat: [
                        { required: true, message: '请选择商品分类', trigger: 'blur' }
                    ]
                },
                //用来保存分类数据
                cateList: [],
                //配置级联菜单中数据如何展示
                cateProps: {
                    value: 'cat_id',
                    label: 'cat_name',
                    children: 'children'
                }
            }
        },
        created() {
            this.getCateList()
        },
        methods: {
            async getCateList() {
                const { data: res } = await this.$http.get('categories')

                if (res.meta.status !== 200) {
                    return this.$message.error('获取商品分类数据失败')
                }
                this.cateList = res.data
            },
            handleChange(){
                //如果用户选择的不是三级分类,该次选择无效,因为必须选择三级分类
                if(this.addForm.goods_cat.length !== 3){
                    this.addForm.goods_cat = []
                    return
                }
            },
            beforeTabLeave (activeName, oldActiveName) {
                // 在tab栏切换之前触发,两个形参为切换前,后的tab栏name
                if (oldActiveName === '0') {
                    // 在第一个标签页的时候
                    if (this.addForm.goods_cat.length !== 3) {
                        this.$message.error('请选择商品的分类')
                        return false
                    } else if (this.addForm.goods_name.trim() === '') {
                        this.$message.error('请输入商品名称')
                        return false
                    } else if (this.addForm.goods_price === '0') {
                        this.$message.error('请输入商品价格')
                        return false
                    } else if (this.addForm.goods_weight === '0') {
                        this.$message.error('请输入商品重量')
                        return false
                    } else if (this.addForm.goods_number === '0') {
                        this.$message.error('请输入商品数量')
                        return false
                    }
                }
            },
            //  获取商品参数和属性
            async tabClicked () {
                if (this.addForm.goods_cat.length === 3) {
                    // 当用户点击切换tab栏时触发
                    if (this.activeIndex === '1') {
                        // 发送请求获取动态参数
                        const { data: res } = await this.$http.get('categories/' + this.addForm.goods_cat[2] + '/attributes',
                            { params: { sel: 'many' } }
                        )
                        if (res.meta.status !== 200) {
                            return this.$message.error('获取动态参数列表失败')
                        }
                        // 将attr_vals字符串转换为数组
                        res.data.forEach(item => {
                            item.attr_vals =
                                item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')
                        })
                        this.manyTableData = res.data
                    } else if (this.activeIndex === '2') {
                        // 发送请求获取静态属性
                        const { data: res } = await this.$http.get('categories/' + this.addForm.goods_cat[2] + '/attributes',
                            { params: { sel: 'only' } }
                        )

                        if (res.meta.status !== 200) {
                            return this.$message.error('获取静态属性列表失败')
                        }
                        this.onlyTableData = res.data
                    }
                }
            },
            handlePreview(file) {
                //当用户点击图片进行预览时执行,处理图片预览
                //形参file就是用户预览的那个文件
                this.previewPath = file.response.data.url
                //显示预览图片对话框
                this.previewVisible = true
            },
            handleRemove(file) {
                //当用户点击X号删除时执行
                //形参file就是用户点击删除的文件
                //获取用户点击删除的那个图片的临时路径
                const filePath = file.response.data.tmp_path
                //使用findIndex来查找符合条件的索引
                const index = this.addForm.pics.findIndex(item => item.pic === filePath)
                //移除索引对应的图片
                this.addForm.pics.splice(index, 1)
            },
            handleSuccess(response) {
                //当上传成功时触发执行
                //形参response就是上传成功之后服务器返回的结果
                //将服务器返回的临时路径保存到addForm表单的pics数组中
                this.addForm.pics.push({ pic: response.data.tmp_path })
            },
            //编写点击事件完成商品添加
            add(){
                this.$refs.addFormRef.validate(async valid=>{
                    if(!valid) return this.$message.error("请填写必要的表单项!")

                    //将addForm进行深拷贝,避免goods_cat数组转换字符串之后导致级联选择器报错
                    const form = _.cloneDeep(this.addForm)
                    //将goods_cat从数组转换为"1,2,3"字符串形式
                    form.goods_cat = form.goods_cat.join(",")
                    //处理attrs数组,数组中需要包含商品的动态参数和静态属性
                    //将manyTableData(动态参数)处理添加到attrs
                    this.manyTableData.forEach(item=>{
                        form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals.join(" ") })
                    })
                    //将onlyTableData(静态属性)处理添加到attrs
                    this.onlyTableData.forEach(item=>{
                        form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals })
                    })

                    //发送请求完成商品的添加,商品名称必须是唯一的
                    const {data:res} = await this.$http.post('goods',form)
                    if(res.meta.status !== 201){
                        return this.$message.error('添加商品失败')
                    }
                    this.$message.success('添加商品成功')
                    //编程式导航跳转到商品列表
                    this.$router.push('/goods')
                })
            },
        }
    }
</script>

<style scoped>
    .el-checkbox {
        margin: 0 10px 0 0 !important;
    }
    .previewImg {
        width: 100%;
    }
    .btnAdd {
        margin-top: 15px;
    }
    .el-checkbox {
        margin: 0 10px 0 0 !important;
    }
    .previewImg {
        width: 100%;
    }
    .btnAdd {
        margin-top: 15px;
    }
</style>

3、分类参数

<template>
    <div>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>商品管理</el-breadcrumb-item>
            <el-breadcrumb-item>分类参数</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 警告区域 :closable="false"(是否展示“X”号) show-icon(显示图标) -->
            <el-alert title="注意:只允许为第三级分类设置相关参数" type="warning" :closable="false" show-icon>
            </el-alert>

            <!-- 选择商品分类区域 -->
            <el-row class="cat_opt">
                <el-col>
                    <span>选择商品分类:</span>
                    <!-- 选择商品分类的级联选择框 -->
                    <el-cascader expandTrigger='hover' v-model="selectedCateKeys" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader>
                </el-col>
                <el-col>
                    <!-- tab页签区域 -->
                    <el-tabs v-model="activeName" @tab-click="handleTabClick">
                        <!-- 添加动态参数的面板 将标签页改为many -->
                        <el-tab-pane label="动态参数" name="many">
                            <el-button size="mini" type="primary" :disabled="isButtonDisabled" @click="add()">添加参数</el-button>
                            <!-- 添加分类对话框 -->
                            <el-dialog title="添加动态参数" :visible.sync="addCateDialogVisible" width="50%"  @close="addCateDialogClosed">
                                <!-- 添加分类表单 -->
                                <el-form :model="param" label-width="100px">
                                    <el-form-item label="参数名称" prop="attr_name">
                                        <el-input v-model="param.attr_name"></el-input>
                                    </el-form-item>
                                    <el-form-item label="[only,many]" prop="attr_sel" v-show="false">
                                        <el-input v-model="param.attr_sel">many</el-input>
                                    </el-form-item>
                                    <el-form-item label="参数详情" prop="attr_vals">
                                        <el-input v-model="param.attr_vals"></el-input>
                                    </el-form-item>

                                </el-form>
                                <span slot="footer" class="dialog-footer">
    <el-button @click="res">取 消</el-button>
    <el-button type="primary" @click="addMany">确 定</el-button>
  </span>
                            </el-dialog>
                            <!-- 动态参数表格 -->
                            <el-table :data="manyTableData" border stripe>
                                <!-- 展开行 -->
                                <el-table-column type="expand">
                                    <template slot-scope="scope">
                                        <!-- 循环渲染Tag标签 -->
                                        <el-tag v-for="(item,i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i,scope.row)">{{item}}</el-tag>
                                        <!-- 输入的文本框 -->
                                        <el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"></el-input>
                                        <!-- 添加按钮 -->
                                        <el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button>
                                    </template>
                                </el-table-column>
                                <!-- 索引列 -->
                                <el-table-column type="index"></el-table-column>
                                <el-table-column label="参数名称" prop="attr_name"></el-table-column>
                                <el-table-column label="操作">
                                    <template slot-scope="scope">
                                        <el-button size="mini" type="primary" icon="el-icon-edit" @click="bj(scope.row.attr_id)">编辑</el-button>
                                        <el-button size="mini" type="danger" icon="el-icon-delete" @click="del(scope.row.attr_id)">删除</el-button>
                                    </template>
                                </el-table-column>
                            </el-table>

                        </el-tab-pane>
                        <!-- 添加静态属性的面板 将标签页改为only -->
                        <el-tab-pane label="静态属性" name="only">
                            <el-button size="mini" type="primary" :disabled="isButtonDisabled" @click="oponly">添加属性</el-button>
                            <el-dialog title="添加静态属性" :visible.sync="addCateDialogVisible2" width="50%"  @close="addCateDialogClosed">
                                <!-- 添加分类表单 -->
                                <el-form :model="param" label-width="100px">
                                    <el-form-item label="参数名称" prop="attr_name">
                                        <el-input v-model="param.attr_name"></el-input>
                                    </el-form-item>
                                    <el-form-item label="[only,many]" prop="attr_sel" v-show="false">
                                        <el-input v-model="param.attr_sel" >only</el-input>
                                    </el-form-item>
                                    <el-form-item label="参数详情" prop="attr_vals">
                                        <el-input v-model="param.attr_vals"></el-input>
                                    </el-form-item>

                                </el-form>
                                <span slot="footer" class="dialog-footer">
    <el-button @click="res">取 消</el-button>
    <el-button type="primary" @click="addonly">确 定</el-button>
  </span>
                            </el-dialog>
                            <!-- 静态属性表格 -->
                            <el-table :data="onlyTableData" border stripe>
                                <!-- 索引列 -->
                                <el-table-column type="index"></el-table-column>
                                <el-table-column label="属性名称" prop="attr_name"></el-table-column>
                                <el-table-column label="操作">
                                    <template slot-scope="scope">
                                        <el-button size="mini" type="primary" icon="el-icon-edit" @click="bj2(scope.row.attr_id)">编辑</el-button>
                                        <el-button size="mini" type="danger" icon="el-icon-delete" @click="del(scope.row.attr_id)">删除</el-button>
                                    </template>
                                </el-table-column>
                            </el-table>
                        </el-tab-pane>
                    </el-tabs>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                addCateDialogVisible:false,
                addCateDialogVisible2:false,
                //分类列表
                cateList: [],
                //用户在级联下拉菜单中选中的分类id
                selectedCateKeys: [],
                //配置级联菜单中数据如何展示
                cateProps: {
                    value: 'cat_id',
                    label: 'cat_name',
                    children: 'children'
                },
                pid:'',
                param:{
                    attr_name:'',attr_sel:'',attr_vals:''
                },
                //tab页签激活显示的页签项
                activeName: 'many',
                //用来保存动态参数数据
                manyTableData: [],
                //用来保存静态属性数据
                onlyTableData: []
            }
        },
        created() {
            this.getCateList()
        },
        methods: {
            add(){
                this.pid = '';
                this.addCateDialogVisible = true;
            },
            oponly(){
                this.pid = '';
                this.addCateDialogVisible2 = true;
            },
            res(){
                this.param = {};
                this.addCateDialogVisible = false;
                this.addCateDialogVisible2 = false;
                this.pid = '';
            },
            async addMany(){
                console.log("cateId="+this.cateId);
                console.log("this.pid"+this.pid);
                if (this.pid==''){
                    console.log("新增动态");
                    this.param.attr_sel = "many";
                    const { data: res } = await this.$http.post(`categories/${this.cateId}/attributes`,this.param);
                    if (res.meta.status !== 201) {
                        return this.$message.error('创建动态参数失败')
                    }
                    this.$message.success('创建动态参数成功');
                }else {
                    console.log("编辑动态");
                    const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${this.pid}`,this.param);
                    if (res.meta.status !== 200) {
                        return this.$message.error('编辑动态参数失败')
                    }
                    this.$message.success('编辑动态参数成功');
                }
                this.handleChange();
                this.param = {};
                this.addCateDialogVisible = false;
            },
            async addonly(){
                console.log("cateId="+this.cateId);
                console.log("this.pid"+this.pid);
                if (this.pid==''){
                    console.log("新增静态");
                    this.param.attr_sel = "only";
                    const { data: res } = await this.$http.post(`categories/${this.cateId}/attributes`,this.param);
                    if (res.meta.status !== 201) {
                        return this.$message.error('新增静态失败')
                    }
                    this.$message.success('新增静态成功');
                }else {
                    console.log("编辑静态");
                    const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${this.pid}`,this.param);
                    if (res.meta.status !== 200) {
                        return this.$message.error('编辑静态参数失败')
                    }
                    this.$message.success('编辑静态参数成功');
                }

                this.handleChange();
                this.param = {};
                this.addCateDialogVisible2 = false;
            },
            async bj(id){
                console.log("id="+id);
                this.pid = id;
                this.addCateDialogVisible = true;
                const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes/${id}`);
                this.param = res.data;
                if (res.meta.status !== 200) {
                    return this.$message.error('查询分类失败')
                }else {
                    this.$message.success('查询分类成功')
                }
            },
            async bj2(id){
                console.log("id="+id);
                this.pid = id;
                this.addCateDialogVisible2 = true;
                const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes/${id}`);
                this.param = res.data;
                if (res.meta.status !== 200) {
                    return this.$message.error('查询分类失败')
                }else {
                    this.$message.success('查询分类成功')
                }
            },
            del(id){
                this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    console.log("id="+id);
                    console.log(`categories/${this.cateId}/attributes/${id}`)
                    this.$http.delete(`categories/${this.cateId}/attributes/${id}`).then(() => {
                        this.handleChange();
                    }),
                        this.$message({
                            type: 'success',
                            message: '删除成功!'
                        });
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            },
            async getCateList(){
                //获取所有的商品分类列表
                const { data: res } = await this.$http.get('categories')

                if (res.meta.status !== 200) {
                    return this.$message.error('获取分类数据失败')
                }
                //将数据列表赋值给cateList
                this.cateList = res.data
                // //保存总数据条数
                // this.total = res.data.total
                //   console.log(res.data);
            },
            // 修改原handleChange方法
            async handleChange() {
                // 证明选中的不是三级分类
                if (this.selectedCateKeys.length !== 3) {
                    this.selectedCateKeys = []
                    this.manyTableData = []
                    this.onlyTableData = []
                    return
                }
                // 证明选中的是三级分类
                console.log(this.selectedCateKeys)
                // 根据所选分类的Id,和当前所处的面板,获取对应的参数
                const { data: res } = await this.$http.get(
                    `categories/${this.cateId}/attributes`,
                    {
                        params: { sel: this.activeName }
                    }
                )
                if (res.meta.status !== 200) {
                    return this.$message.error('获取参数列表失败!')
                }
                // 处理文本框中的值
                res.data.forEach(item => {
                    item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
                    // 控制文本框的显示与隐藏
                    item.inputVisible = false
                    // 文本框中输入的值
                    item.inputValue = ''
                })
                console.log(res.data)
                if (this.activeName === 'many') {
                    this.manyTableData = res.data
                } else {
                    this.onlyTableData = res.data
                }
            },
            handleTabClick() {
                console.log(this.activeName)
                this.handleChange()
            },
            //  展示输入框
            showInput(row){
                //用户点击添加按钮时触发
                row.inputVisible = true
                //$nextTick:在页面上元素被重新渲染之后,调用回调函数的代码
                this.$nextTick.then(()=>{
                    //让文本框自动获得焦点
                    this.$refs.saveTagInput.$refs.input.focus()
                })
            },
            handleInputConfirm(row){
                //当用户在文本框中按下enter键或者焦点离开时都会触发执行
                //判断用户在文本框中输入的内容是否合法
                if(row.inputValue.trim().length===0){
                    row.inputValue = ''
                    row.inputVisible = false
                    return
                }

                // row.inputVisible = false
                //如果用户输入了真实合法的数据,需要保存起来
                row.attr_vals.push(row.inputValue.trim())
                row.inputValue = ''
                row.inputVisible = false

                this.saveAttrVals(row)
            },
            handleClose(index,row){
                //删除对应索引的参数可选项
                row.attr_vals.splice(index,1)
                //调用函数,完成保存可选项的操作
                this.saveAttrVals(row)
            },
            async saveAttrVals(row){
                //封装函数,完成保存可选项的操作
                //发起请求,保存参数细项
                const {data:res} = await this.$http.put(`categories/${this.cateId}/attributes/${row.attr_id}`,
                    {attr_name:row.attr_name,attr_sel:row.attr_sel,attr_vals:row.attr_vals.join(' ')})

                if (res.meta.status !== 200) {
                    return this.$message.error('修改参数项失败')
                }
                this.$message.success('修改参数项成功')
            }
        },
        computed: {
            //添加计算属性用来获取按钮禁用与否
            isButtonDisabled() {
                return this.selectedCateKeys.length !== 3
            },
            //获取选中的三级分类id
            cateId() {
                if (this.selectedCateKeys.length === 3) {
                    return this.selectedCateKeys[this.selectedCateKeys.length - 1]
                }
                return null
            },

        }
    }
</script>

(6)订单管理

<template>
    <div>
        <h3>订单列表</h3>
        <!-- 面包屑导航 -->
        <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>订单管理</el-breadcrumb-item>
            <el-breadcrumb-item>订单列表</el-breadcrumb-item>
        </el-breadcrumb>
        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 搜索栏 -->
            <el-row :gutter="20">
                <el-col :span="8">
                    <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable>
                        <el-button slot="append" icon="el-icon-search" @click="getOrderList"></el-button>
                    </el-input>
                </el-col>
            </el-row>

            <!-- 订单表格 -->
            <el-table :data="orderList" border stripe>
                <el-table-column type="index"></el-table-column>
                <el-table-column label="订单编号" prop="order_number"></el-table-column>
                <el-table-column label="订单价格" prop="order_price"></el-table-column>
                <el-table-column label="是否付款" prop="pay_status">
                    <template slot-scope="scope">
                        <el-tag type="success" v-if="scope.row.pay_status === '1'">已付款</el-tag>
                        <el-tag type="danger" v-else>未付款</el-tag>
                    </template>
                </el-table-column>
                <el-table-column label="是否发货" prop="is_send"></el-table-column>
                <el-table-column label="下单时间" prop="create_time">
                    <template slot-scope="scope">
                        {{scope.row.create_time | dateFormat}}
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="125px">
                    <template slot-scope="scope">
                        <!-- 给修改地址按钮添加点击事件 -->
                        <el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditAddress(scope.row.order_id)" ></el-button>
                        <!-- 给查看物流添加点击事件 -->
                        <el-button size="mini" type="success" icon="el-icon-location" @click="showProgress(scope.row.order_id)"></el-button>
                    </template>
                </el-table-column>
            </el-table>

            <!-- 分页 -->
            <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
            </el-pagination>
        </el-card>
        <!-- 修改地址对话框 -->
        <el-dialog title="修改收货地址" :visible.sync="addressVisible" width="50%" @close="addressDialogClosed">
            <!-- 添加表单 -->
            <el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="100px">
                <el-form-item label="省市区县" prop="address1">
                    <el-cascader :options="cityData" v-model="addressForm.address1"></el-cascader>
                </el-form-item>
                <el-form-item label="详细地址" prop="address2">
                    <el-input v-model="addressForm.address2"></el-input>
                </el-form-item>
            </el-form>
            <span slot="footer" class="dialog-footer">
        <el-button @click="addressVisible = false">取 消</el-button>
        <el-button type="primary" @click="addressVisible = false">确 定</el-button>
    </span>
        </el-dialog>
        <!-- 物流信息进度对话框 -->
        <el-dialog title="物流进度" :visible.sync="progressVisible" width="50%">
            <!-- 时间线组件  -->
            <el-timeline>
                <el-timeline-item v-for="(activity, index) in progressInfo"
                                  :key="index" :timestamp="activity.time">
                    {{activity.context}}
                </el-timeline-item>
            </el-timeline>
        </el-dialog>
    </div>
</template>
<script>
    import cityData from './citydata.js'
    export default {
        data() {
            return {
                //查询条件
                queryInfo: {
                    query: '',
                    pagenum: 1,
                    pagesize: 10
                },
                //订单列表数据
                orderList: [],
                //数据总条数
                total: 0,
                //控制修改地址对话框的显示和隐藏
                addressVisible:false,
                //修改收货地址的表单
                addressForm: {
                    address1: [],
                    address2: ''
                },
                //控制物流进度对话框的显示和隐藏
                progressVisible: false,
                //保存物流信息
                progressInfo: [],
                addressFormRules:{
                    address1:[{ required: true, message: '请选择省市区县', trigger: 'blur' }],
                    address2:[{ required: true, message: '请输入详细地址', trigger: 'blur' }],
                },
                //将导入的cityData数据保存起来
                cityData:cityData
            }
        },
        created() {
            this.getOrderList()
        },
        methods: {
            async getOrderList() {
                console.log("query="+this.queryInfo.query)
                const { data: res } = await this.$http.get('orders', {
                    params: this.queryInfo
                })

                if (res.meta.status !== 200) {
                    return this.$message.error('获取订单列表数据失败!')
                }

                this.total = res.data.total
                this.orderList = res.data.goods
            },
            handleSizeChange(newSize){
                this.queryInfo.pagesize = newSize
                this.getOrderList()
            },
            handleCurrentChange(newPage){
                this.queryInfo.pagenum = newPage
                this.getOrderList()
            },
            async showEditAddress(id) {
                //当用户点击修改收货地址按钮时触发
                this.addressVisible = true;
                console.log("id="+id);
                const { data: res } = await this.$http.get('orders/'+id);
                this.addressForm = res.data;

            },
            addressDialogClosed(){
                this.$refs.addressFormRef.resetFields()
            },
            async showProgress(id) {
                //发送请求获取物流数据
                console.log(id)
                const { data: res } = await this.$http.get('/kuaidi/'+id);

                if (res.meta.status !== 200) {
                    return this.$message.error('获取物流进度失败!')
                }
                this.progressInfo = res.data
                //显示对话框
                this.progressVisible = true
            },

        }
    }
</script>

<style lang="less" scoped>
    .el-cascader{
        width: 100%;
    }
</style>

(7)数据统计

<template>
    <div>
        <!-- 面包屑导航区域 -->
        <el-breadcrumb separator-class="el-icon-arrow-right">
            <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>数据统计</el-breadcrumb-item>
            <el-breadcrumb-item>数据报表</el-breadcrumb-item>
        </el-breadcrumb>

        <!-- 卡片视图区域 -->
        <el-card>
            <!-- 2. 为ECharts准备一个具备大小(宽高)的Dom -->
            <div id="main" style="width: 750px;height:400px;"></div>
        </el-card>
    </div>
</template>

<script>
    // 1. 导入 echarts
    import echarts from 'echarts'
    import _ from 'lodash'

    export default {
        data() {
            return {
                // 需要合并的数据
                options: {
                    title: {
                        text: '用户来源'
                    },
                    tooltip: {
                        trigger: 'axis',
                        axisPointer: {
                            type: 'cross',
                            label: {
                                backgroundColor: '#E9EEF3'
                            }
                        }
                    },
                    grid: {
                        left: '3%',
                        right: '4%',
                        bottom: '3%',
                        containLabel: true
                    },
                    xAxis: [
                        {
                            boundaryGap: false
                        }
                    ],
                    yAxis: [
                        {
                            type: 'value'
                        }
                    ]
                }
            }
        },
        created() {},
        // 此时,页面上的元素,已经被渲染完毕了!
        async mounted() {
            // 3. 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById('main'))

            const { data: res } = await this.$http.get('reports/type/1')
            if (res.meta.status !== 200) {
                return this.$message.error('获取折线图数据失败!')
            }

            // 4. 准备数据和配置项
            const result = _.merge(res.data, this.options)
            console.log("result="+result);
            // 5. 展示数据
            myChart.setOption(result)
        },
        methods: {}
    }
</script>

<style lang="less" scoped>
</style>



版权声明:本文为Zyw907155124原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。