mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-14 20:40:49 +08:00
admin base table and form
This commit is contained in:
@@ -14,14 +14,14 @@
|
|||||||
<template #reference>
|
<template #reference>
|
||||||
<div class="author">
|
<div class="author">
|
||||||
<i class="icon el-icon-s-custom" />
|
<i class="icon el-icon-s-custom" />
|
||||||
{{ userInfo && userInfo.nickName || '' }}
|
{{ userInfo && userInfo.username || '' }}
|
||||||
<i class="el-icon-caret-bottom" />
|
<i class="el-icon-caret-bottom" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="nickname">
|
<div class="nickname">
|
||||||
<p>登录名:{{ userInfo && userInfo.loginUserName || '' }}</p>
|
<p>Username:{{ userInfo && userInfo.username || '' }}</p>
|
||||||
<p>昵称:{{ userInfo && userInfo.nickName || '' }}</p>
|
<p>Email:{{ userInfo && userInfo.email || '' }}</p>
|
||||||
<el-tag size="small" effect="dark" class="logout" @click="logout">退出</el-tag>
|
<el-tag size="small" effect="dark" class="logout" @click="logout">Logout</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,6 +33,8 @@ import { onMounted, reactive, toRefs } from 'vue'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import axios from '../utils/axios'
|
import axios from '../utils/axios'
|
||||||
import { localRemove, pathMap } from '../utils'
|
import { localRemove, pathMap } from '../utils'
|
||||||
|
import api from "../utils/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Header',
|
name: 'Header',
|
||||||
setup() {
|
setup() {
|
||||||
@@ -44,18 +46,18 @@ export default {
|
|||||||
})
|
})
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const pathname = window.location.hash.split('/')[1] || ''
|
const pathname = window.location.hash.split('/')[1] || ''
|
||||||
if (!['login'].includes(pathname)) {
|
console.log("pathname: ", pathname)
|
||||||
// getUserInfo()
|
getUserInfo()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const getUserInfo = async () => {
|
const getUserInfo = async () => {
|
||||||
const userInfo = await axios.get('/adminUser/profile')
|
const userInfo = await api.getUserBase()
|
||||||
state.userInfo = userInfo
|
console.log(userInfo)
|
||||||
|
state.userInfo = userInfo.data
|
||||||
}
|
}
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
axios.delete('/logout').then(() => {
|
api.logout().then(() => {
|
||||||
localRemove('token')
|
localRemove('token')
|
||||||
router.push({ path: '/login' })
|
router.push({ name: 'login' })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const back = () => {
|
const back = () => {
|
||||||
|
|||||||
9
admin/src/utils/api.js
vendored
9
admin/src/utils/api.js
vendored
@@ -1,6 +1,12 @@
|
|||||||
import axios from "./axios";
|
import axios from "./axios";
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
|
login: (params = {}) => {
|
||||||
|
return axios.post('login', params);
|
||||||
|
},
|
||||||
|
logout: (params = {}) => {
|
||||||
|
return axios.post('logout');
|
||||||
|
},
|
||||||
listAllowAgent: (params = {}) => {
|
listAllowAgent: (params = {}) => {
|
||||||
return axios.get('agent-allow', {params: params});
|
return axios.get('agent-allow', {params: params});
|
||||||
},
|
},
|
||||||
@@ -20,6 +26,9 @@ const api = {
|
|||||||
listUser: (params = {}) => {
|
listUser: (params = {}) => {
|
||||||
return axios.get('user', {params: params});
|
return axios.get('user', {params: params});
|
||||||
},
|
},
|
||||||
|
getUserBase: (params = {}) => {
|
||||||
|
return axios.get('user-base', {params: params});
|
||||||
|
},
|
||||||
storeUser: (params = {}) => {
|
storeUser: (params = {}) => {
|
||||||
return axios.post('user', params);
|
return axios.post('user', params);
|
||||||
},
|
},
|
||||||
|
|||||||
4
admin/src/utils/axios.js
vendored
4
admin/src/utils/axios.js
vendored
@@ -1,11 +1,13 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ElMessage } from 'element-plus'
|
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.withCredentials = true
|
||||||
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
|
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
axios.defaults.headers['Content-Type'] = 'application/json'
|
axios.defaults.headers['Content-Type'] = 'application/json'
|
||||||
axios.defaults.headers['Accept'] = 'application/json'
|
axios.defaults.headers['Accept'] = 'application/json'
|
||||||
|
axios.defaults.headers['Authorization'] = 'Bearer ' + localGet('token')
|
||||||
|
|
||||||
// 请求拦截器,内部根据返回值,重新组装,统一管理。
|
// 请求拦截器,内部根据返回值,重新组装,统一管理。
|
||||||
axios.interceptors.response.use(res => {
|
axios.interceptors.response.use(res => {
|
||||||
|
|||||||
2
admin/src/utils/index.js
vendored
2
admin/src/utils/index.js
vendored
@@ -47,4 +47,6 @@ export const pathMap = {
|
|||||||
'agent-allow-form': 'Agent allow form',
|
'agent-allow-form': 'Agent allow form',
|
||||||
'user': 'User',
|
'user': 'User',
|
||||||
'user-form': 'User form',
|
'user-form': 'User form',
|
||||||
|
'exam': 'Exam',
|
||||||
|
'exam-form': 'Exam form',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,239 @@
|
|||||||
<template>
|
<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>
|
</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>
|
||||||
|
|||||||
@@ -1,3 +1,162 @@
|
|||||||
<template>
|
<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>
|
</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>
|
||||||
|
|||||||
@@ -29,10 +29,14 @@
|
|||||||
import axios from '../utils/axios'
|
import axios from '../utils/axios'
|
||||||
import { reactive, ref, toRefs } from 'vue'
|
import { reactive, ref, toRefs } from 'vue'
|
||||||
import { localSet } from '../utils'
|
import { localSet } from '../utils'
|
||||||
|
import api from '../utils/api'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
setup() {
|
setup() {
|
||||||
const loginForm = ref(null)
|
const loginForm = ref(null)
|
||||||
|
const router = useRouter()
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
ruleForm: {
|
ruleForm: {
|
||||||
username: '',
|
username: '',
|
||||||
@@ -51,12 +55,9 @@ export default {
|
|||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
loginForm.value.validate((valid) => {
|
loginForm.value.validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
axios.post('/adminUser/login', {
|
api.login(state.ruleForm).then(res => {
|
||||||
userName: state.ruleForm.username || '',
|
localSet('token', res.data.token)
|
||||||
passwordMd5: md5(state.ruleForm.password)
|
router.push({name: 'dashboard'})
|
||||||
}).then(res => {
|
|
||||||
localSet('token', res)
|
|
||||||
window.location.href = '/'
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('error submit!!')
|
console.log('error submit!!')
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
@@ -51,6 +52,10 @@ class Handler extends ExceptionHandler
|
|||||||
$msg = Arr::first(Arr::first($errors));
|
$msg = Arr::first(Arr::first($errors));
|
||||||
return response()->json(fail($msg, $errors));
|
return response()->json(fail($msg, $errors));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->renderable(function (NotFoundHttpException $e) {
|
||||||
|
return response()->json(fail('No query result.', request()->all()), 404);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,8 +67,15 @@ class Handler extends ExceptionHandler
|
|||||||
*/
|
*/
|
||||||
protected function prepareJsonResponse($request, Throwable $e)
|
protected function prepareJsonResponse($request, Throwable $e)
|
||||||
{
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
if (config('app.debug')) {
|
||||||
|
$msg = $e->getMessage() ?: get_class($e);
|
||||||
|
$data['trace'] = $e->getTraceAsString();
|
||||||
|
} else {
|
||||||
|
$msg = 'Server Error';
|
||||||
|
}
|
||||||
return new JsonResponse(
|
return new JsonResponse(
|
||||||
fail(config('app.debug') ? ($e->getMessage() ?: get_class($e)) : 'Server Error', []),
|
fail($msg, $data),
|
||||||
$this->isHttpException($e) ? $e->getStatusCode() : 500,
|
$this->isHttpException($e) ? $e->getStatusCode() : 500,
|
||||||
$this->isHttpException($e) ? $e->getHeaders() : [],
|
$this->isHttpException($e) ? $e->getHeaders() : [],
|
||||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
|
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
|
||||||
|
|||||||
38
app/Http/Controllers/AuthenticateController.php
Normal file
38
app/Http/Controllers/AuthenticateController.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Resources\ExamResource;
|
||||||
|
use App\Http\Resources\UserResource;
|
||||||
|
use App\Repositories\AuthenticateRepository;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class AuthenticateController extends Controller
|
||||||
|
{
|
||||||
|
private $repository;
|
||||||
|
|
||||||
|
public function __construct(AuthenticateRepository $repository)
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'username' => 'required',
|
||||||
|
'password' => 'required',
|
||||||
|
]);
|
||||||
|
$result = $this->repository->login($request->username, $request->password);
|
||||||
|
return $this->success($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(Request $request)
|
||||||
|
{
|
||||||
|
$result = $this->repository->logout(Auth::id());
|
||||||
|
return $this->success($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
|||||||
use App\Http\Resources\UserResource;
|
use App\Http\Resources\UserResource;
|
||||||
use App\Repositories\UserRepository;
|
use App\Repositories\UserRepository;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class UserController extends Controller
|
class UserController extends Controller
|
||||||
{
|
{
|
||||||
@@ -100,4 +101,12 @@ class UserController extends Controller
|
|||||||
$result = $this->repository->listClass();
|
$result = $this->repository->listClass();
|
||||||
return $this->success($result);
|
return $this->success($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function base()
|
||||||
|
{
|
||||||
|
$id = Auth::id();
|
||||||
|
$result = $this->repository->getBase($id);
|
||||||
|
$resource = new UserResource($result);
|
||||||
|
return $this->success($resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ class User extends Authenticatable
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'secret', 'passhash',
|
||||||
'remember_token',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace App\Repositories;
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class AuthenticateRepository extends BaseRepository
|
class AuthenticateRepository extends BaseRepository
|
||||||
{
|
{
|
||||||
|
public function login($username, $password)
|
||||||
|
{
|
||||||
|
$user = User::query()
|
||||||
|
->where('username', $username)
|
||||||
|
->firstOrFail(['id', 'secret', 'passhash']);
|
||||||
|
if (md5($user->secret . $password . $user->secret) != $user->passhash) {
|
||||||
|
throw new \InvalidArgumentException('username or password invalid');
|
||||||
|
}
|
||||||
|
$token = DB::transaction(function () use ($user) {
|
||||||
|
$user->tokens()->delete();
|
||||||
|
$tokenResult = $user->createToken(__CLASS__ . __FUNCTION__ . __LINE__);
|
||||||
|
return $tokenResult->plainTextToken;
|
||||||
|
});
|
||||||
|
$result = $user->toArray();
|
||||||
|
$result['token'] = $token;
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout($id)
|
||||||
|
{
|
||||||
|
$user = User::query()->findOrFail($id, ['id']);
|
||||||
|
$result = $user->tokens()->delete();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ class UserRepository extends BaseRepository
|
|||||||
return $query->paginate();
|
return $query->paginate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBase($id)
|
||||||
|
{
|
||||||
|
$user = User::query()->findOrFail($id, ['id', 'username', 'email', 'avatar']);
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
public function store(array $params)
|
public function store(array $params)
|
||||||
{
|
{
|
||||||
$password = $params['password'];
|
$password = $params['password'];
|
||||||
|
|||||||
@@ -14,10 +14,15 @@ use Illuminate\Support\Facades\Route;
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Route::group([], function () {
|
Route::group(['middleware' => ['auth:sanctum']], function () {
|
||||||
|
Route::post('logout', [\App\Http\Controllers\AuthenticateController::class, 'logout']);
|
||||||
Route::resource('agent-allow', \App\Http\Controllers\AgentAllowController::class);
|
Route::resource('agent-allow', \App\Http\Controllers\AgentAllowController::class);
|
||||||
Route::resource('user', \App\Http\Controllers\UserController::class);
|
Route::resource('user', \App\Http\Controllers\UserController::class);
|
||||||
|
Route::get('user-base', [\App\Http\Controllers\UserController::class, 'base']);
|
||||||
Route::resource('exam', \App\Http\Controllers\ExamController::class);
|
Route::resource('exam', \App\Http\Controllers\ExamController::class);
|
||||||
Route::get('exam-index', [\App\Http\Controllers\ExamController::class, 'indexes']);
|
Route::get('exam-index', [\App\Http\Controllers\ExamController::class, 'indexes']);
|
||||||
Route::get('class', [\App\Http\Controllers\UserController::class, 'classes']);
|
Route::get('class', [\App\Http\Controllers\UserController::class, 'classes']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::post('login', [\App\Http\Controllers\AuthenticateController::class, 'login']);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user