Directorio de artículos
estructura del proyecto
página de inicio de sesión
código principal
<template>
<div class="home-container">
<div class="top"></div>
<div class="login-form">
<div class="decoration">
<div class="tou"></div>
<div class="hand-left"></div>
<div class="hand-right"></div>
</div>
<div class="title">欢迎登录</div>
<el-form
label-width="80px"
:model="loginForm"
ref="loginFormRef"
:rules="loginFormRules"
>
<el-form-item label="用户名">
<el-input
v-model="loginForm.username"
placeholder="请输入帐号/手机号/邮箱"
></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input
v-model="loginForm.password"
:type="passwordType == true ? 'password' : 'text'"
>
<i
slot="suffix"
class="iconfont"
:class="
passwordType == true
? 'icon-yanjing_yincang_o'
: 'icon-yanjing_xianshi_o'
"
@click="onSee"
></i>
</el-input>
</el-form-item>
<el-button
type="primary"
class="btn-blue"
@click="submitForm('loginFormRef')"
>立即登录</el-button
>
</el-form>
</div>
</div>
</template>
<script>
export default {
data () {
return {
loginForm: {
username: '',
password: ''
},
loginFormRules: {
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur'
},
{
min: 3,
max: 10,
message: '密码的长度应为3-10个字符'
}
],
username: [
{
required: true,
message: '请输入用户名',
trigger: 'blur'
}
]
},
passwordType: true
}
},
created () {
},
computed: {
},
methods: {
async submitForm (formName) {
this.$refs[formName].validate(async (valid) => {
if (valid) {
// 解构赋值 await async化简promise
const {
data: res } = await this.$http.post(
'/user/login',
this.loginForm
)
if (res.code !== 200) return this.$message.error(res.msg)
this.$message.success('登录成功!')
localStorage.setItem('Authorization', res.token)
this.$router.push('/home')
}
})
},
// 修改密码是否
onSee () {
console.log(123)
this.passwordType = !this.passwordType
}
}
}
</script>
página delantera
código principal
<template>
<el-container class="home-container">
<el-header class="header">
<span class="title">后台管理系统</span>
<div class="header-right">
<div class="logo">
<img :src="`http://127.0.0.1:3007/${userInfo.avator}`" alt="" />
</div>
<span class="userInfo">{
{
userInfo.username}}</span>
<i class="iconfont icon-tuichu" @click="logout"></i>
</div>
</el-header>
<el-container class="bottom-container">
<el-aside :width="isCollapse ? '64px' : '260px'" class="aside">
<el-menu
class="el-menu-vertical-demo"
background-color="#001529"
text-color="#fff"
active-text-color="#FFFFFF"
unique-opened
:default-active="this.$router.path"
:collapse="isCollapse"
:collapse-transition="false"
router
>
<el-menu-item :index="item.path" v-for="(item,index) in MenuItem" :key="index" v-show="item.children.length === 0" >
<i class="iconfont" :class="item.icon"></i>
<span slot="title">{
{
item.rightName}}</span>
</el-menu-item>
<el-submenu :index="item.path" v-for="(item,index) in MenuItem" :key="index" v-show="item.children.length !== 0">
<template slot="title">
<i class="iconfont " :class="item.icon"></i>
<span>{
{
item.rightName}}</span>
</template>
<el-menu-item :index="i2.path" v-for="(i2,idx2) in item.children" :key="idx2">
<i class="iconfont" :class="i2.icon"></i>
<span slot="title">{
{
i2.rightName}}</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main class="main">
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
data () {
return {
isCollapse: false,
userInfo: '',
MenuItem: []
}
},
created () {
},
mounted () {
this.getUserInfo()
},
computed: {
},
methods: {
async getUserInfo () {
const {
data: res } = await this.$http.get('/user/getUserInfo')
if (res.code !== 200) return this.$message.error('获取用户信息失败!')
this.userInfo = res.data
this.getRightMenu()
},
logout () {
localStorage.removeItem('Authorization')
this.$router.push('/login')
},
async getRightMenu () {
const {
data: res } = await this.$http.post('/right/getRightMenu', {
id: this.userInfo.id })
if (res.code !== 200) return this.$message.error(res.msg)
this.MenuItem = res.data
}
}
}
</script>
Estadísticas de usuario
estadísticas de productos básicos
Estadísticas de pedidos
código principal
<template>
<div class="home-container">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">首页</a></el-breadcrumb-item>
</el-breadcrumb>
<div class="mainbody">
<div class="top-container">
<div class="box-card" @click="getUserChart">
<div class="title">用户统计</div>
<div class="number">{
{
typeNum.users}}</div>
<div class="type">当前用户总数量</div>
</div>
<div class="box-card" @click="getGoods">
<div class="title">商品统计</div>
<div class="number">{
{
typeNum.goods}}</div>
<div class="type">当前总商品数量</div>
</div>
<div class="box-card" @click="getOrder">
<div class="title">订单统计</div>
<div class="number">{
{
typeNum.orders}}</div>
<div class="type">当前总订单数量</div>
</div>
<div class="box-card">
<div class="title">销售统计</div>
<div class="number">{
{
parseFloat(typeNum.sum).toFixed(2)}}</div>
<div class="type">当前总销售额(元)</div>
</div>
</div>
<div class="bottom-container">
<div class="bottom-item">
<div class="chart-box" v-if="!isOrder">
<div
id="myCharts"
ref="myCharts"
class="chart"
style="width: 100%; height: 100%"
></div>
</div>
<div class="chart-box" v-if="!isOrder">
<div
id="myCharts2"
ref="myCharts2"
class="chart"
style="width: 100%; height: 100%"
></div>
</div>
<div class="chart-box" v-if="isOrder">
<div
id="myCharts3"
ref="myCharts3"
class="chart"
style="width: 100%; height: 100%"
></div>
</div>
<div class="chart-box" v-if="isOrder">
<div class="time-box-container">
<div class="time-box">
<el-timeline>
<el-timeline-item v-for="(item,index) in orderTimeLine" :key="index">
<!-- 时间线左侧 -->
<div class="companydel">{
{
item.createTime}}</div>
<!-- 时间线右侧 -->
<div class="contentdel">
<span>用户</span>
<span class="content-emphasis">{
{
item.userName}}</span>
<span>在店铺</span>
<span class="content-emphasis">{
{
item.shopName}}</span>
<span>购买了</span>
<span class="content-emphasis">{
{
item.goodName}}</span>
</div>
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
chart1: null,
chart2: null,
chart3: null,
isOrder: false,
orderTimeLine: [],
typeNum: {
},
time: null
}
},
created () {
this.getTypeNum()
},
mounted () {
this.chart1 = this.$echarts.init(this.$refs.myCharts)
this.chart2 = this.$echarts.init(this.$refs.myCharts2)
this.getUserPie()
this.getUserCreateChart()
this.timer = setInterval(() => {
this.getTypeNum()
}, 300000)
},
computed: {
},
methods: {
// 获取用户组成分析
async getUserPie () {
const {
data: res } = await this.$http.get('/analysis/getUserPie')
if (res.code !== 200) return res.cc(res.msg)
const option = {
title: {
text: '用户组成分析',
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}人 ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: res.data.catalogue
},
series: [
{
name: '用户角色',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: res.data.data,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
this.chart1.setOption(option)
},
// 用户增长趋势 -Todo
async getUserCreateChart () {
const {
data: res } = await this.$http.get('/analysis/getUserLine')
if (res.code !== 200) return this.$message.error(res.msg)
const option = {
title: {
text: '用户增长情况分析'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#ddd'
}
},
backgroundColor: 'rgba(255,255,255,1)',
padding: [5, 10],
textStyle: {
color: '#7588E4'
},
extraCssText: 'box-shadow: 0 0 5px rgba(0,0,0,0.3)'
},
legend: {
right: 20,
orient: 'vertical',
data: ['今日', '昨日']
},
xAxis: {
type: 'category',
data: res.data.category,
boundaryGap: false,
splitLine: {
show: false,
interval: 'auto',
lineStyle: {
color: ['#D4DFF5']
}
},
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#609ee9'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
}
},
yAxis: {
type: 'value',
name: '单位:人',
minInterval: 1,
splitLine: {
show: false,
lineStyle: {
color: ['#D4DFF5']
}
},
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#609ee9'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
}
},
series: [
{
name: '今日',
type: 'line',
smooth: true,
showSymbol: false,
symbol: 'circle',
symbolSize: 6,
data: res.data.today,
itemStyle: {
normal: {
color: '#f7b851'
}
},
lineStyle: {
normal: {
width: 3
}
}
},
{
name: '昨日',
type: 'line',
smooth: true,
showSymbol: false,
symbol: 'circle',
symbolSize: 6,
data: res.data.yesterday,
itemStyle: {
normal: {
color: '#58c8da'
}
},
lineStyle: {
normal: {
width: 3
}
}
}
]
}
this.chart2.setOption(option)
},
// 用户统计
getUserChart () {
this.isOrder = false
this.chart1.dispose()
this.chart2.dispose()
if (this.chart3 !== null) {
this.chart3.dispose()
}
this.$nextTick(() => {
this.chart1 = this.$echarts.init(this.$refs.myCharts)
this.chart2 = this.$echarts.init(this.$refs.myCharts2)
this.getUserPie()
this.getUserCreateChart()
})
},
// 商品销量前五
async getGoodsTop5 () {
const {
data: res } = await this.$http.get('/analysis/getGoodsTop5')
if (res.code !== 200) return this.$message.error(res.msg)
const option = {
title: {
text: '商品销售量前五'
},
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
legend: {
data: ['销售量', '销售金额'],
align: 'right',
right: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: res.data.category,
axisLabel: {
interval: 0,
formatter: function (params) {
var val = ''
// 判断长度
if (params.length > 4) {
// 替换原字符
val = params.substr(0, 4) + '...'
// 返回
return val
} else {
// 否则返回原string
return params
}
}
}
}
],
yAxis: [
{
type: 'value',
axisLabel: {
formatter: '{value}'
}
}
],
series: [
{
name: '销售量',
type: 'bar',
data: res.data.saleCount
},
{
name: '销售金额',
type: 'bar',
data: res.data.sum
}
]
}
this.chart1.setOption(option)
},
// 获取销售额前五的店铺
async getShopTop5 () {
const {
data: res } = await this.$http.get('/analysis/getGoodsTop5')
if (res.code !== 200) return res.cc(res.msg)
const option = {
title: {
text: '店铺销售金额前5'
},
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
legend: {
data: ['销售金额'],
align: 'right',
right: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: res.data.category,
axisLabel: {
interval: 0,
formatter: function (params) {
var val = ''
// 判断长度
if (params.length > 4) {
// 替换原字符
val = params.substr(0, 4) + '...'
// 返回
return val
} else {
// 否则返回原string
return params
}
}
}
}
],
yAxis: [
{
type: 'value',
name: 'aa',
axisLabel: {
formatter: '{value}'
}
}
],
series: [
{
name: '销售金额',
type: 'bar',
data: res.data.sum
}
]
}
this.chart2.setOption(option)
},
// 商品统计
getGoods () {
this.isOrder = false
this.chart1.dispose()
this.chart2.dispose()
if (this.chart3 !== null) {
this.chart3.dispose()
}
this.$nextTick(() => {
this.chart1 = this.$echarts.init(this.$refs.myCharts)
this.chart2 = this.$echarts.init(this.$refs.myCharts2)
this.getGoodsTop5()
this.getShopTop5()
})
},
getOrder () {
this.chart1.dispose()
this.chart2.dispose()
this.isOrder = true
this.$nextTick(() => {
this.getOrderLine()
this.getOrderTimeline()
})
},
async getOrderLine () {
const {
data: res } = await this.$http.get('/analysis/getOrderLine')
if (res.code !== 200) return res.cc(res.msg)
this.chart3 = this.$echarts.init(this.$refs.myCharts3)
const option = {
title: {
text: '订单增长趋势'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#ddd'
}
},
backgroundColor: 'rgba(255,255,255,1)',
padding: [5, 10],
textStyle: {
color: '#7588E4'
},
extraCssText: 'box-shadow: 0 0 5px rgba(0,0,0,0.3)'
},
legend: {
right: 20,
orient: 'vertical',
data: ['今日', '昨日']
},
xAxis: {
type: 'category',
data: res.data.category,
boundaryGap: false,
splitLine: {
show: false,
interval: 'auto',
lineStyle: {
color: ['#D4DFF5']
}
},
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#609ee9'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
}
},
yAxis: {
type: 'value',
minInterval: 1,
splitLine: {
show: false,
lineStyle: {
color: ['#D4DFF5']
}
},
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#609ee9'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
}
},
series: [
{
name: '今日',
type: 'line',
smooth: true,
showSymbol: false,
symbol: 'circle',
symbolSize: 6,
data: res.data.today,
itemStyle: {
normal: {
color: '#f7b851'
}
},
lineStyle: {
normal: {
width: 3
}
}
},
{
name: '昨日',
type: 'line',
smooth: true,
showSymbol: false,
symbol: 'circle',
symbolSize: 6,
data: res.data.yesterday,
itemStyle: {
normal: {
color: '#58c8da'
}
},
lineStyle: {
normal: {
width: 3
}
}
}
]
}
this.chart3.setOption(option)
},
async getTypeNum () {
const {
data: res } = await this.$http.get('/analysis/getAllTypeNum')
if (res.code !== 200) return this.$message.error(res.msg)
this.typeNum = res.data
// setInterval(() => {
// }, 300000)
},
async getOrderTimeline () {
const {
data: res } = await this.$http.get('/analysis/getOrderTimeLine')
if (res.code !== 200) return this.$message.error(res.msg)
this.orderTimeLine = res.data
}
},
beforeDestroy () {
clearInterval(this.timer)
}
}
</script>
Gestión de usuarios
Lista de usuarios
lista de roles
Ver
los permisos de asignación de usuario correspondientes al rol
gestión de mercancías
lista de productos
Editar/Agregar artículo
gestión de pedidos
código principal
<template>
<div class="home-container">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">订单管理</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="/">订单列表</a></el-breadcrumb-item>
</el-breadcrumb>
<el-card>
<el-form
ref="orderFormref"
:model="queryInfo"
label-width="90px"
class="form"
>
<el-row type="flex" justify="space-between">
<el-col :span="6" class="form-top" :style="{'max-height':(isShow?42+'px':300+'px')}" >
<el-form-item label="订单编号" prop="orderNum">
<el-input v-model="queryInfo.orderNum"></el-input>
</el-form-item>
<el-form-item label="支付状态" prop="is_pay">
<el-select v-model="queryInfo.is_pay" placeholder="请选择">
<el-option key="0" label="待支付" value="0"> </el-option>
<el-option key="1" label="已支付" value="0"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="发货情况" prop="is_deliver">
<el-select v-model="queryInfo.is_deliver" placeholder="请选择">
<el-option key="0" label="未发货" value="0"> </el-option>
<el-option key="1" label="已发货" value="0"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="订单状态" prop="order_status">
<el-select v-model="queryInfo.order_status" placeholder="请选择">
<el-option key="0" label="进行中" value="0"> </el-option>
<el-option key="1" label="已完成" value="0"> </el-option>
<el-option key="2" label="已关闭" value="0"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="收货时间" prop="finishTime">
<el-date-picker
type="date"
placeholder="选择日期"
v-model="queryInfo.finishTime"
style="width: 100%"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6" class="btn-box">
<el-button type="text" class="btn-stretch" @click="hideAndShow()">{
{
isShow?'展开':'收起'}}</el-button>
<el-button @click="resetAddForm('orderFormref')">重置</el-button>
<!-- <el-button @click="addGood()">新增</el-button> -->
<el-button type="primary" class="btn-blue" @click="getGoodsList()"
>查询</el-button
>
</el-col>
</el-row>
</el-form>
</el-card>
<el-card>
<el-table
:data="orderList"
style="width: 100%; margin-top: 20px"
border
stripe
>
<el-table-column
type="index"
label="序号"
:index="indexMethod"
width="80"
>
</el-table-column>
<el-table-column prop="orderNum" label="订单编号" width="200"> </el-table-column>
<el-table-column prop="username" label="用户名" width="120"> </el-table-column>
<el-table-column prop="is_pay" label="是否支付">
<template slot-scope="scope">
<el-tag
type="success"
v-if="scope.row.order_status != 2 && scope.row.is_pay == 1"
>已支付</el-tag
>
<el-tag
type="warning"
v-if="scope.row.order_status != 2 && scope.row.is_pay == 0"
>待支付</el-tag
>
<el-tag type="info" v-if="scope.row.order_status == 2"
>已关闭</el-tag
>
</template>
</el-table-column>
<el-table-column prop="is_deliver" label="是否发货">
<template slot-scope="scope">
<el-tag
type="success"
v-if="scope.row.order_status != 2 && scope.row.is_deliver == 1"
>已发货</el-tag
>
<el-tag
type="warning"
v-if="scope.row.order_status != 2 && scope.row.is_deliver == 0"
>待发货</el-tag
>
<el-tag type="info" v-if="scope.row.order_status == 2"
>已关闭</el-tag
>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180"> </el-table-column>
<el-table-column prop="finishTime" label="收货时间" width="180"> </el-table-column>
<el-table-column prop="order_status" label="订单状态">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.order_status == 1"
>已完成</el-tag
>
<el-tag type="warning" v-if="scope.row.order_status == 0"
>进行中</el-tag
>
<el-tag type="info" v-if="scope.row.order_status == 2"
>已关闭</el-tag
>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="340">
<template slot-scope="scope">
<!-- 修改按钮 -->
<el-button
type="primary"
class="btn-blue"
v-if="scope.row.order_status == 0 && scope.row.is_deliver == 0 "
@click="changeDeliver(scope.row)"
>发货</el-button
>
<el-button
type="primary"
class="btn-blue"
v-if="scope.row.order_status != 2 && scope.row.is_deliver == 1"
>查看物流
</el-button>
<el-button
type="warning"
v-if="scope.row.order_status != 2 && scope.row.is_deliver == 1"
@click="
btnOptCommon(scope.row.id, 'delayTime', '是否确认延长收货?')
"
>延长收货</el-button
>
<el-button
type="danger"
@click="
btnOptCommon(scope.row.id, 'delOrder', '是否确认删除订单?')
"
>删除订单</el-button
>
<el-button
type="info"
v-if="scope.row.order_status != 2 && scope.row.is_pay == 0"
@click="
btnOptCommon(scope.row.id, 'cancelOrder', '是否确认取消订单?')
"
>取消订单</el-button
>
</template>
</el-table-column>
</el-table>
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page.sync="queryInfo.page" :page-size="queryInfo.limit" layout="total, prev, pager, next" :total="total">
</el-pagination>
</el-card>
</div>
</template>
<script>
export default {
data () {
return {
queryInfo: {
limit: 10,
page: 1,
query: '',
orderNum: '',
is_pay: '',
finishTime: '',
order_status: '',
is_deliver: ''
},
isShow: true,
total: 0,
orderList: []
}
},
created () {
this.getOrderList()
},
computed: {
},
methods: {
open_editForm (id) {
},
indexMethod (index) {
return (this.queryInfo.page - 1) * this.queryInfo.limit + index + 1
},
async getOrderList () {
const {
data: res } = await this.$http.post(
'/order/getAllOrder',
this.queryInfo
)
if (res.code !== 200) return this.$message.error(res.msg)
this.orderList = res.data.data
this.total = res.data.total
},
btnOptCommon (id, url, text) {
this.$confirm(text, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
const {
data: res } = await this.$http.post(`/order/${
url}`, {
id: id
})
if (res.code !== 200) return this.$message.error(res.msg)
this.getOrderList()
})
},
handleSizeChange (val) {
this.queryInfo.limit = val
this.getOrderList()
},
handleCurrentChange (val) {
this.queryInfo.page = val
this.getOrderList()
},
changeDeliver (item) {
if (item.is_pay === 0) {
return this.$message.error('买家尚未完成付款!')
}
const id = item.id
this.$prompt('请输入快递单号', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern:
/[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
inputErrorMessage: '快递单号格式不正确'
}).then(async () => {
const {
data: res } = await this.$http.post('/order/changeIsDeliver', {
id: id
})
if (res.code !== 200) return this.$message.error(res.msg)
this.getOrderList()
})
},
hideAndShow () {
this.isShow = !this.isShow
}
}
}
</script>
dirección del código fuente
https://gitee.com/lyy_97591/node_vue.git