admin base table and form

This commit is contained in:
xiaomlove
2021-04-23 01:28:41 +08:00
parent 4903fa1c90
commit d164bb15ed
14 changed files with 530 additions and 24 deletions

View File

@@ -14,14 +14,14 @@
<template #reference>
<div class="author">
<i class="icon el-icon-s-custom" />
{{ userInfo && userInfo.nickName || '' }}
{{ userInfo && userInfo.username || '' }}
<i class="el-icon-caret-bottom" />
</div>
</template>
<div class="nickname">
<p>登录名{{ userInfo && userInfo.loginUserName || '' }}</p>
<p>昵称{{ userInfo && userInfo.nickName || '' }}</p>
<el-tag size="small" effect="dark" class="logout" @click="logout">退出</el-tag>
<p>Username{{ userInfo && userInfo.username || '' }}</p>
<p>Email{{ userInfo && userInfo.email || '' }}</p>
<el-tag size="small" effect="dark" class="logout" @click="logout">Logout</el-tag>
</div>
</el-popover>
</div>
@@ -33,6 +33,8 @@ import { onMounted, reactive, toRefs } from 'vue'
import { useRouter } from 'vue-router'
import axios from '../utils/axios'
import { localRemove, pathMap } from '../utils'
import api from "../utils/api";
export default {
name: 'Header',
setup() {
@@ -44,18 +46,18 @@ export default {
})
onMounted(() => {
const pathname = window.location.hash.split('/')[1] || ''
if (!['login'].includes(pathname)) {
// getUserInfo()
}
console.log("pathname: ", pathname)
getUserInfo()
})
const getUserInfo = async () => {
const userInfo = await axios.get('/adminUser/profile')
state.userInfo = userInfo
const userInfo = await api.getUserBase()
console.log(userInfo)
state.userInfo = userInfo.data
}
const logout = () => {
axios.delete('/logout').then(() => {
api.logout().then(() => {
localRemove('token')
router.push({ path: '/login' })
router.push({ name: 'login' })
})
}
const back = () => {

View File

@@ -1,6 +1,12 @@
import axios from "./axios";
const api = {
login: (params = {}) => {
return axios.post('login', params);
},
logout: (params = {}) => {
return axios.post('logout');
},
listAllowAgent: (params = {}) => {
return axios.get('agent-allow', {params: params});
},
@@ -20,6 +26,9 @@ const api = {
listUser: (params = {}) => {
return axios.get('user', {params: params});
},
getUserBase: (params = {}) => {
return axios.get('user-base', {params: params});
},
storeUser: (params = {}) => {
return axios.post('user', params);
},

View File

@@ -1,11 +1,13 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
import {localGet} from "./index";
axios.defaults.baseURL = 'http://nexus-php8.tinyhd.net'
axios.defaults.baseURL = 'http://nexus-php8.tinyhd.net/api'
axios.defaults.withCredentials = true
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers['Content-Type'] = 'application/json'
axios.defaults.headers['Accept'] = 'application/json'
axios.defaults.headers['Authorization'] = 'Bearer ' + localGet('token')
// 请求拦截器,内部根据返回值,重新组装,统一管理。
axios.interceptors.response.use(res => {

View File

@@ -47,4 +47,6 @@ export const pathMap = {
'agent-allow-form': 'Agent allow form',
'user': 'User',
'user-form': 'User form',
'exam': 'Exam',
'exam-form': 'Exam form',
}

View File

@@ -1,3 +1,239 @@
<template>
<div>Exam Form</div>
<div class="add">
<el-card class="add-container">
<el-form :model="goodForm" :rules="rules" ref="goodRef" label-width="100px" class="goodForm">
<el-form-item required label="商品分类">
<el-cascader :placeholder="defaultCate" style="width: 300px" :props="category" @change="handleChangeCate"></el-cascader>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input style="width: 300px" v-model="goodForm.goodsName" placeholder="请输入商品名称"></el-input>
</el-form-item>
<el-form-item label="商品简介" prop="goodsIntro">
<el-input style="width: 300px" type="textarea" v-model="goodForm.goodsIntro" placeholder="请输入商品简介(100字)"></el-input>
</el-form-item>
<el-form-item label="商品价格" prop="originalPrice">
<el-input type="number" min="0" style="width: 300px" v-model="goodForm.originalPrice" placeholder="请输入商品价格"></el-input>
</el-form-item>
<el-form-item label="商品售卖价" prop="sellingPrice">
<el-input type="number" min="0" style="width: 300px" v-model="goodForm.sellingPrice" placeholder="请输入商品售价"></el-input>
</el-form-item>
<el-form-item label="商品库存" prop="stockNum">
<el-input type="number" min="0" style="width: 300px" v-model="goodForm.stockNum" placeholder="请输入商品库存"></el-input>
</el-form-item>
<el-form-item label="商品标签" prop="tag">
<el-input style="width: 300px" v-model="goodForm.tag" placeholder="请输入商品小标签"></el-input>
</el-form-item>
<el-form-item label="上架状态" prop="goodsSellStatus">
<el-radio-group v-model="goodForm.goodsSellStatus">
<el-radio label="0">上架</el-radio>
<el-radio label="1">下架</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item required label="商品主图" prop="goodsCoverImg">
<el-upload
class="avatar-uploader"
:action="uploadImgServer"
accept="jpg,jpeg,png"
:headers="{
token: token
}"
:show-file-list="false"
:before-upload="handleBeforeUpload"
:on-success="handleUrlSuccess"
>
<img style="width: 100px; height: 100px; border: 1px solid #e9e9e9;" v-if="goodForm.goodsCoverImg" :src="goodForm.goodsCoverImg" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="详情内容">
<div ref='editor'></div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAdd()">{{ id ? '立即修改' : '立即创建' }}</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import { reactive, ref, toRefs, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { localGet } from '../../utils'
export default {
name: 'AddGood',
setup() {
const { proxy } = getCurrentInstance()
console.log('proxy', proxy)
const goodRef = ref(null)
const route = useRoute()
const router = useRouter()
const { id } = route.query
const state = reactive({
token: localGet('token') || '',
id: id,
defaultCate: '',
goodForm: {
goodsName: '',
goodsIntro: '',
originalPrice: '',
sellingPrice: '',
stockNum: '',
goodsSellStatus: '0',
goodsCoverImg: '',
tag: ''
},
rules: {
goodsName: [
{ required: 'true', message: '请填写商品名称', trigger: ['change'] }
],
originalPrice: [
{ required: 'true', message: '请填写商品价格', trigger: ['change'] }
],
sellingPrice: [
{ required: 'true', message: '请填写商品售价', trigger: ['change'] }
],
stockNum: [
{ required: 'true', message: '请填写商品库存', trigger: ['change'] }
],
},
})
let instance
onMounted(() => {
instance = new WangEditor(editor.value)
instance.config.showLinkImg = false
instance.config.showLinkImgAlt = false
instance.config.showLinkImgHref = false
instance.config.uploadImgMaxSize = 2 * 1024 * 1024 // 2M
instance.config.uploadFileName = 'file'
instance.config.uploadImgHeaders = {
token: state.token
}
// 图片返回格式不同,需要自定义返回格式
instance.config.uploadImgHooks = {
// 图片上传并返回了结果,想要自己把图片插入到编辑器中
// 例如服务器端返回的不是 { errno: 0, data: [...] } 这种格式,可使用 customInsert
customInsert: function(insertImgFn, result) {
console.log('result', result)
// result 即服务端返回的接口
// insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可
if (result.data && result.data.length) {
result.data.forEach(item => insertImgFn(item))
}
}
}
instance.config.uploadImgServer = uploadImgsServer
Object.assign(instance.config, {
onchange() {
console.log('change')
},
})
instance.create()
if (id) {
axios.get(`/goods/${id}`).then(res => {
const { goods, firstCategory, secondCategory, thirdCategory } = res
state.goodForm = {
goodsName: goods.goodsName,
goodsIntro: goods.goodsIntro,
originalPrice: goods.originalPrice,
sellingPrice: goods.sellingPrice,
stockNum: goods.stockNum,
goodsSellStatus: String(goods.goodsSellStatus),
goodsCoverImg: proxy.$filters.prefix(goods.goodsCoverImg),
tag: goods.tag
}
state.categoryId = goods.goodsCategoryId
state.defaultCate = `${firstCategory.categoryName}/${secondCategory.categoryName}/${thirdCategory.categoryName}`
if (instance) {
// 初始化商品详情 html
instance.txt.html(goods.goodsDetailContent)
}
})
}
})
onBeforeUnmount(() => {
instance.destroy()
instance = null
})
const submitAdd = () => {
goodRef.value.validate((vaild) => {
if (vaild) {
// 默认新增用 post 方法
let httpOption = axios.post
let params = {
goodsCategoryId: state.categoryId,
goodsCoverImg: state.goodForm.goodsCoverImg,
goodsDetailContent: instance.txt.html(),
goodsIntro: state.goodForm.goodsIntro,
goodsName: state.goodForm.goodsName,
goodsSellStatus: state.goodForm.goodsSellStatus,
originalPrice: state.goodForm.originalPrice,
sellingPrice: state.goodForm.sellingPrice,
stockNum: state.goodForm.stockNum,
tag: state.goodForm.tag
}
if (hasEmoji(params.goodsIntro) || hasEmoji(params.goodsName) || hasEmoji(params.tag) || hasEmoji(params.goodsDetailContent)) {
ElMessage.error('不要输入表情包,再输入就打死你个龟孙儿~')
return
}
console.log('params', params)
if (id) {
params.goodsId = id
// 修改商品使用 put 方法
httpOption = axios.put
}
httpOption('/goods', params).then(() => {
ElMessage.success(id ? '修改成功' : '添加成功')
router.push({ path: '/good' })
})
}
})
}
const handleBeforeUpload = (file) => {
const sufix = file.name.split('.')[1] || ''
if (!['jpg', 'jpeg', 'png'].includes(sufix)) {
ElMessage.error('请上传 jpg、jpeg、png 格式的图片')
return false
}
}
const handleUrlSuccess = (val) => {
state.goodForm.goodsCoverImg = val.data || ''
}
const handleChangeCate = (val) => {
state.categoryId = val[2] || 0
}
return {
...toRefs(state),
goodRef,
submitAdd,
handleBeforeUpload,
handleUrlSuccess,
handleChangeCate
}
}
}
</script>
<style scoped>
.add {
display: flex;
}
.add-container {
flex: 1;
height: 100%;
}
.avatar-uploader {
width: 100px;
height: 100px;
color: #ddd;
font-size: 30px;
}
.avatar-uploader-icon {
display: block;
width: 100%;
height: 100%;
border: 1px solid #e9e9e9;
padding: 32px 17px;
}
</style>

View File

@@ -1,3 +1,162 @@
<template>
<div>Exam Index</div>
<el-card class="swiper-container">
<template #header>
<div class="header">
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">新增商品</el-button>
</div>
</template>
<el-table
v-loading="loading"
ref="multipleTable"
:data="tableData"
tooltip-effect="dark"
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="goodsId"
label="商品编号"
>
</el-table-column>
<el-table-column
prop="goodsName"
label="商品名"
>
</el-table-column>
<el-table-column
prop="goodsIntro"
label="商品简介"
>
</el-table-column>
<el-table-column
label="商品图片"
width="150px"
>
<template #default="scope">
<img style="width: 100px; height: 100px;" :key="scope.row.goodsId" :src="$filters.prefix(scope.row.goodsCoverImg)" alt="商品主图">
</template>
</el-table-column>
<el-table-column
prop="stockNum"
label="商品库存"
>
</el-table-column>
<el-table-column
prop="sellingPrice"
label="商品售价"
>
</el-table-column>
<el-table-column
label="上架状态"
>
<template #default="scope">
<span style="color: green;" v-if="scope.row.goodsSellStatus == 0">销售中</span>
<span style="color: red;" v-else>已下架</span>
</template>
</el-table-column>
<el-table-column
label="操作"
width="100"
>
<template #default="scope">
<a style="cursor: pointer; margin-right: 10px" @click="handleEdit(scope.row.goodsId)">修改</a>
<a style="cursor: pointer; margin-right: 10px" v-if="scope.row.goodsSellStatus == 0" @click="handleStatus(scope.row.goodsId, 1)">下架</a>
<a style="cursor: pointer; margin-right: 10px" v-else @click="handleStatus(scope.row.goodsId, 0)">上架</a>
</template>
</el-table-column>
</el-table>
<!--总数超过一页再展示分页器-->
<el-pagination
background
layout="prev, pager, next"
:total="total"
:page-size="pageSize"
:current-page="currentPage"
@current-change="changePage"
/>
</el-card>
</template>
<script>
import { onMounted, reactive, ref, toRefs } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
export default {
name: 'Good',
setup() {
const multipleTable = ref(null)
const router = useRouter()
const state = reactive({
loading: false,
tableData: [], // 数据列表
multipleSelection: [], // 选中项
total: 0, // 总条数
currentPage: 1, // 当前页
pageSize: 10 // 分页大小
})
onMounted(() => {
getGoodList()
})
// 获取轮播图列表
const getGoodList = () => {
state.loading = true
axios.get('/goods/list', {
params: {
pageNumber: state.currentPage,
pageSize: state.pageSize
}
}).then(res => {
state.tableData = res.list
state.total = res.totalCount
state.currentPage = res.currPage
state.loading = false
})
}
const handleAdd = () => {
router.push({ name: 'exam-form' })
}
const handleEdit = (id) => {
router.push({ path: '/add', query: { id } })
}
// 选择项
const handleSelectionChange = (val) => {
state.multipleSelection = val
}
const changePage = (val) => {
state.currentPage = val
getGoodList()
}
const handleStatus = (id, status) => {
axios.put(`/goods/status/${status}`, {
ids: id ? [id] : []
}).then(() => {
ElMessage.success('修改成功')
getGoodList()
})
}
return {
...toRefs(state),
multipleTable,
handleSelectionChange,
handleAdd,
handleEdit,
getGoodList,
changePage,
handleStatus
}
}
}
</script>
<style scoped>
.swiper-container {
min-height: 100%;
}
.el-card.is-always-shadow {
min-height: 100%!important;
}
</style>

View File

@@ -29,10 +29,14 @@
import axios from '../utils/axios'
import { reactive, ref, toRefs } from 'vue'
import { localSet } from '../utils'
import api from '../utils/api'
import { useRouter, useRoute } from 'vue-router'
export default {
name: 'Login',
setup() {
const loginForm = ref(null)
const router = useRouter()
const state = reactive({
ruleForm: {
username: '',
@@ -51,12 +55,9 @@ export default {
const submitForm = async () => {
loginForm.value.validate((valid) => {
if (valid) {
axios.post('/adminUser/login', {
userName: state.ruleForm.username || '',
passwordMd5: md5(state.ruleForm.password)
}).then(res => {
localSet('token', res)
window.location.href = '/'
api.login(state.ruleForm).then(res => {
localSet('token', res.data.token)
router.push({name: 'dashboard'})
})
} else {
console.log('error submit!!')