merge
This commit is contained in:
commit
1387938b75
@ -20,9 +20,10 @@
|
||||
"@types/node": "^18.11.10",
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"@vitejs/plugin-vue-jsx": "^2.1.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.2.4",
|
||||
"less": "^4.1.3",
|
||||
"typescript": "^4.9.3",
|
||||
"unplugin-vue-define-options": "^1.0.0",
|
||||
"vite": "^3.2.4",
|
||||
"vue-tsc": "^1.0.11"
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,8 @@
|
||||
--info-color: #1890ff;
|
||||
--info-color-deprecated-bg: #e6f7ff;
|
||||
--info-color-deprecated-border: #91d5ff;
|
||||
--placeholder-color: #ccc;
|
||||
--border-color: #d9d9d9;
|
||||
|
||||
--primary-color-text: #333;
|
||||
--font-size: 14px;
|
||||
@ -48,7 +50,8 @@
|
||||
--border-radius-middle: calc(var(--border-radius) + 2px);
|
||||
--border-radius-large: calc(var(--border-radius) + 3px);
|
||||
|
||||
--header-height: 80px;
|
||||
--header-bg: #001529;
|
||||
--header-height: 50px;
|
||||
--left-menu-width: 200px;
|
||||
}
|
||||
|
||||
@ -119,13 +122,55 @@ body {
|
||||
.pointer-cursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
table{
|
||||
width: 100%;
|
||||
border-left: solid 1px #eee;
|
||||
border-top: solid 1px #eee;
|
||||
|
||||
.table-wrapper {
|
||||
.table-toolbar{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border: solid 1px #eee;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
th{
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
td, th {
|
||||
text-align: left;
|
||||
border-bottom: solid 1px #eee;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
tr{
|
||||
&:hover{
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
td,th {
|
||||
border-right: solid 1px #eee;
|
||||
border-bottom: solid 1px #eee;
|
||||
padding:2px 4px;
|
||||
|
||||
.page-wrapper {
|
||||
margin: 20px 0;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
|
||||
.page-item {
|
||||
cursor: pointer;
|
||||
border: solid 1px var(--primary-2);
|
||||
border-radius: var(--border-radius-middle);
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
margin: 0 5px;
|
||||
min-width: 26px;
|
||||
text-align: center;
|
||||
|
||||
&:hover, &.current-page {
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color)
|
||||
}
|
||||
}
|
||||
}
|
||||
.display-flex{
|
||||
display: flex;
|
||||
}
|
94
admin-fe/src/components/button/Index.vue
Normal file
94
admin-fe/src/components/button/Index.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<button :disabled="disabled || loading" class="btn" :class="{
|
||||
'btn-primary':btnType == 'primary',
|
||||
'btn-default':btnType == 'default',
|
||||
'btn-text':btnType == 'text',
|
||||
'btn-link':btnType == 'link',
|
||||
}">
|
||||
<span v-if="loading" class="btn-icon">
|
||||
<Loading v-if="loading" class="btn-icon-loading"/>
|
||||
</span>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineOptions} from "unplugin-vue-define-options/macros";
|
||||
import {computed} from "vue";
|
||||
import Loading from "../icon/Loading.vue";
|
||||
|
||||
defineOptions({
|
||||
name: 'PButton'
|
||||
})
|
||||
type ButtonProps = {
|
||||
type?: 'primary' | 'default' | 'text' | 'link'
|
||||
/**
|
||||
* 是否正在加载中
|
||||
*/
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
const props = defineProps<ButtonProps>()
|
||||
const btnType = computed(() => props.type || 'primary')
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@keyframes anim-loading {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
padding: 5px 15px;
|
||||
line-height: 20px;
|
||||
border-radius: var(--border-radius-middle);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
&:hover{
|
||||
background-color: var(--primary-color-hover);
|
||||
}
|
||||
&:active{
|
||||
background-color: var(--primary-color-active);
|
||||
}
|
||||
&[disabled]{
|
||||
background-color: var(--primary-3);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-default {
|
||||
}
|
||||
|
||||
&.btn-text {
|
||||
}
|
||||
|
||||
&.btn-link {
|
||||
padding:2px;
|
||||
background: none;
|
||||
color: var(--primary-color)
|
||||
}
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
margin-right: 5px;
|
||||
|
||||
.btn-icon-loading {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
vertical-align: middle;
|
||||
animation: anim-loading 1s infinite linear;
|
||||
//transition: transform ;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
17
admin-fe/src/components/icon/ArrowDown.vue
Normal file
17
admin-fe/src/components/icon/ArrowDown.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<svg :style="sizeStyle" class="icon-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path fill="currentColor"
|
||||
d="M500.8 604.779L267.307 371.392l-45.227 45.27 278.741 278.613L779.307 416.66l-45.248-45.248z"></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from "vue";
|
||||
import iconComponent from "./iconComponent";
|
||||
|
||||
export default defineComponent({
|
||||
name: "ArrowDown",
|
||||
props: iconComponent.props,
|
||||
setup: iconComponent.setup
|
||||
})
|
||||
</script>
|
18
admin-fe/src/components/icon/Loading.vue
Normal file
18
admin-fe/src/components/icon/Loading.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg :style="sizeStyle" class="icon-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path fill="currentColor"
|
||||
d="M270.4 214.4C336 160 420 128 512 128c212 0 384 172 384 384h64c0-247.2-200.8-448-448-448-107.2 0-205.6 37.6-282.4 100l40.8 50.4z"
|
||||
p-id="2703"></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from "vue";
|
||||
import iconComponent from "./iconComponent";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Loading",
|
||||
props: iconComponent.props,
|
||||
setup: iconComponent.setup
|
||||
})
|
||||
</script>
|
@ -24,144 +24,151 @@ import Eye from "../icon/Eye.vue";
|
||||
import EyeClose from "../icon/EyeClose.vue";
|
||||
|
||||
interface PInputEvents {
|
||||
'update:modelValue': (value: string) => void
|
||||
'update:modelValue': (value: any) => void
|
||||
}
|
||||
|
||||
type PInputProps = {
|
||||
placeholder?: string
|
||||
modelValue?: string
|
||||
type?: 'text' | 'password' | 'textarea'
|
||||
placeholder?: string
|
||||
modelValue?: any
|
||||
type?: 'text' | 'password' | 'textarea'
|
||||
}
|
||||
|
||||
const props = defineProps<PInputProps>()
|
||||
const emit = defineEmits<PInputEvents>()
|
||||
const slots = useSlots()
|
||||
const state = reactive({
|
||||
focus: false,
|
||||
hover: false,
|
||||
passwordVisible: false
|
||||
focus: false,
|
||||
hover: false,
|
||||
passwordVisible: false
|
||||
})
|
||||
const inputType = computed(() => {
|
||||
const type = props.type || 'text';
|
||||
if (type === 'password' && state.passwordVisible) return 'text';
|
||||
return type;
|
||||
const type = props.type || 'text';
|
||||
if (type === 'password' && state.passwordVisible) return 'text';
|
||||
return type;
|
||||
})
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@height: 30px;
|
||||
|
||||
.p-input-box-focus {
|
||||
|
||||
border-color: #2a7dc9;
|
||||
box-shadow: 0 0 0 2px #0960bd33;
|
||||
outline: 0;
|
||||
border-color: #2a7dc9;
|
||||
box-shadow: 0 0 0 2px #0960bd33;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.p-input {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 4px 11px;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
transition: all .3s;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 0px 11px;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
transition: all .3s;
|
||||
height: @height;
|
||||
|
||||
&::-moz-placeholder {
|
||||
opacity: 1
|
||||
}
|
||||
&::-moz-placeholder {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #bfbfbf;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none
|
||||
}
|
||||
&::placeholder {
|
||||
color: #bfbfbf;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none
|
||||
}
|
||||
|
||||
|
||||
&:placeholder-shown {
|
||||
text-overflow: ellipsis
|
||||
}
|
||||
&:placeholder-shown {
|
||||
text-overflow: ellipsis
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #2a7dc9;
|
||||
border-right-width: 1px !important
|
||||
}
|
||||
&:hover {
|
||||
border-color: #2a7dc9;
|
||||
border-right-width: 1px !important
|
||||
}
|
||||
|
||||
.p-input-rtl .p-input:hover {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px !important
|
||||
}
|
||||
.p-input-rtl .p-input:hover {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px !important
|
||||
}
|
||||
|
||||
&:focus, .p-input-focused {
|
||||
border-right-width: 1px !important;
|
||||
}
|
||||
&:focus, .p-input-focused {
|
||||
border-right-width: 1px !important;
|
||||
}
|
||||
|
||||
.p-input-disabled {
|
||||
color: #00000040;
|
||||
background-color: #f5f5f5;
|
||||
border-color: #d9d9d9;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
opacity: 1
|
||||
}
|
||||
.p-input-disabled {
|
||||
color: #00000040;
|
||||
background-color: #f5f5f5;
|
||||
border-color: #d9d9d9;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.p-input-wrapper {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-input-wrapper-group {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
padding: 4px 6px;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: var(--border-radius);
|
||||
transition: all .3s;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
padding: 0px 6px;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: var(--border-radius);
|
||||
transition: all .3s;
|
||||
height: @height;
|
||||
|
||||
&:hover {
|
||||
border-color: #2a7dc9;
|
||||
border-right-width: 1px !important
|
||||
}
|
||||
|
||||
.p-input {
|
||||
padding: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin: 0 6px;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
&:hover {
|
||||
border-color: #2a7dc9;
|
||||
border-right-width: 1px !important
|
||||
}
|
||||
|
||||
.p-input {
|
||||
padding: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin: 0 6px;
|
||||
height: 28px;
|
||||
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-input-prefix, .p-input-suffix {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="modal" v-if="modalVisible">
|
||||
<div class="modal-mask"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-content" :style="{
|
||||
width: width+(/(px|%)/.test(width)?'':'px')
|
||||
}">
|
||||
<div class="modal-close" @click="modalVisible = false">关闭</div>
|
||||
<slot/>
|
||||
</div>
|
||||
@ -12,8 +14,12 @@
|
||||
import {computed} from "vue";
|
||||
|
||||
export default {
|
||||
name: "modal",
|
||||
name: "Modal",
|
||||
props: {
|
||||
width: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
},
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
require: true
|
||||
|
79
admin-fe/src/components/pager/Pager.vue
Normal file
79
admin-fe/src/components/pager/Pager.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="page-wrapper">
|
||||
<span v-if="showRefresh" class="page-item refresh" @click="$emit('refresh')">刷新</span>
|
||||
<span v-if="page > 1" class="prev-page page-item" @click="$emit('page-change',--page)">上一页</span>
|
||||
<span class="page-item page-first" :class="{'current-page':page == 1}"
|
||||
@click="$emit('page-change',(page=1))">1</span>
|
||||
<template v-for="index in pageData">
|
||||
<span class="page-item" :class="{'current-page':page == index}" @click="$emit('page-change',(page=index))">{{
|
||||
index
|
||||
}}</span>
|
||||
</template>
|
||||
<span class="page-item page-end" :class="{'current-page':page == totalPage}"
|
||||
@click="$emit('page-change',(page=totalPage))">{{
|
||||
totalPage
|
||||
}}</span>
|
||||
<span v-if="page < totalPage" class="page-item next-page" @click="$emit('page-change',++ page)">下一页</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {computed, defineComponent, ref} from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: "Pager",
|
||||
props: {
|
||||
/**
|
||||
* 总条数
|
||||
*/
|
||||
total: {
|
||||
type: Number,
|
||||
require: true
|
||||
},
|
||||
showRefresh: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 每页的条数
|
||||
*/
|
||||
pageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
emits: ['refresh', 'page-change'],
|
||||
setup(props) {
|
||||
const totalPage = computed(() => Math.ceil((props.total || 0) / props.pageSize))
|
||||
const page = ref(props.currentPage)
|
||||
const pageData = computed(() => {
|
||||
// 循环使用的开始和结束页码
|
||||
let start = page.value - 2, end = page.value + 2
|
||||
if (start <= 1) {
|
||||
// 补足少的页码
|
||||
end += 1 - start;
|
||||
start = 2;
|
||||
}
|
||||
if (end >= totalPage.value) {
|
||||
end = totalPage.value - 1;
|
||||
start = end - 4;
|
||||
if (start <= 1) {
|
||||
start = 2;
|
||||
}
|
||||
}
|
||||
const pages = []
|
||||
for (; start <= end; start++) {
|
||||
pages.push(start)
|
||||
}
|
||||
return pages
|
||||
})
|
||||
return {
|
||||
totalPage, page, pageData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
4
admin-fe/src/components/pager/index.ts
Normal file
4
admin-fe/src/components/pager/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import PagerComponent from './Pager.vue'
|
||||
|
||||
const Pager = PagerComponent
|
||||
export default Pager
|
33
admin-fe/src/components/select/Option.vue
Normal file
33
admin-fe/src/components/select/Option.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<li :ref="element" @click="onClick($event)">
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, onMounted, onUpdated, ref} from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "POption",
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number, Object],
|
||||
}
|
||||
},
|
||||
emits: ['select'],
|
||||
setup(props,context) {
|
||||
function onClick(e) {
|
||||
e.target.data = props.value
|
||||
}
|
||||
const element = ref<HTMLElement>()
|
||||
|
||||
return {
|
||||
element,onClick
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
159
admin-fe/src/components/select/Select.vue
Normal file
159
admin-fe/src/components/select/Select.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="select-wrapper" :class="{active:isActive}" v-click-outside="onClose">
|
||||
<div class="select-box-wrapper" @click="isActive = !isActive">
|
||||
<div class="select-value">{{ selectValue }}<span v-if="!selectValue" class="placeholder">{{ placeholder }}</span>
|
||||
</div>
|
||||
<div class="icon-arrow">
|
||||
<ArrowDown class="icon-select-arrow"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-options-wrapper">
|
||||
<ul @click="onSelect">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import ArrowDown from "../icon/ArrowDown.vue";
|
||||
import {defineComponent, nextTick, ref, SetupContext} from "vue";
|
||||
import {CHANGE_EVENT, UPDATE_MODEL_EVENT} from "../../service/constants";
|
||||
|
||||
export interface OptionItemType {
|
||||
label: string;
|
||||
value?: any;
|
||||
}
|
||||
|
||||
// declare var OptionItemArray: OptionItemType[];
|
||||
|
||||
export default defineComponent({
|
||||
name: "PSelect",
|
||||
components: {ArrowDown},
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number, Object]
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
UPDATE_MODEL_EVENT,
|
||||
CHANGE_EVENT,
|
||||
'focus',
|
||||
'blur',
|
||||
],
|
||||
setup(props, ctx: SetupContext) {
|
||||
const selectValue = ref(props.modelValue)
|
||||
const isActive = ref(false)
|
||||
|
||||
function onSelect(e: MouseEvent) {
|
||||
// 原生dom操作
|
||||
const target = e.target as HTMLElement;
|
||||
if (target.tagName.toLowerCase() != 'li') return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
//
|
||||
const text = target.textContent
|
||||
selectValue.value = text
|
||||
const arr = [].slice.call (target.parentElement.children)
|
||||
arr.forEach(ele => (ele as HTMLElement).classList.remove("selected"))
|
||||
target.classList.add("selected")
|
||||
console.log(target.data)
|
||||
ctx.emit(UPDATE_MODEL_EVENT,target.data)
|
||||
onClose()
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
isActive.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
selectValue, isActive, onClose, onSelect
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&.active {
|
||||
.select-options-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.select-box-wrapper {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 3px var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-box-wrapper {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 3px 6px;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-middle);
|
||||
transition: all .3s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: var(--placeholder-color)
|
||||
}
|
||||
|
||||
.select-value {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
// 右侧图标
|
||||
.icon-arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.select-options-wrapper {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 32px;
|
||||
box-shadow: 0 0 7px rgba(0, 0, 0, 0.1);
|
||||
border-radius: var(--border-radius-middle);
|
||||
|
||||
ul, li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
0
admin-fe/src/components/uploader/index.ts
Normal file
0
admin-fe/src/components/uploader/index.ts
Normal file
71
admin-fe/src/components/uploader/uploader.vue
Normal file
71
admin-fe/src/components/uploader/uploader.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="upload-wrapper">
|
||||
<input type="file" :id="fileUploaderId" :accept="accept" @change="onFileSelectChange" class="file-handler"/>
|
||||
<label :for="fileUploaderId">
|
||||
<!-- 上传触发元素的插槽 -->
|
||||
<slot/>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Uploader"
|
||||
})
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import http from "../../util/http";
|
||||
|
||||
type PropsType = {
|
||||
/**
|
||||
* 上传路径
|
||||
*/
|
||||
action: string
|
||||
/**
|
||||
* 文件上传默认的参数名称,默认为file
|
||||
*/
|
||||
fieldName?: string
|
||||
/**
|
||||
* 允许上传的文件类型,默认为*表示所有文件
|
||||
*/
|
||||
accept?: string
|
||||
|
||||
modelValue?: string
|
||||
}
|
||||
type EmitsType = {
|
||||
onSuccess: () => void
|
||||
onError: () => void
|
||||
'update:modelValue': (value: string) => void
|
||||
}
|
||||
type FileUploadResultModel = {
|
||||
url: string
|
||||
name?: string
|
||||
}
|
||||
const props = defineProps<PropsType>()
|
||||
const emits = defineEmits<EmitsType>()
|
||||
const fileUploaderId = Math.random().toString(16).substring(3)
|
||||
|
||||
async function onFileSelectChange(e: InputEvent) {
|
||||
//@ts-ignore
|
||||
const files = e.target.files as FileList
|
||||
if (!files || files.length == 0) return;
|
||||
const data = {},fieldName = props.fieldName || 'file'
|
||||
data[fieldName] = files[0];
|
||||
const ret = await http.request<FileUploadResultModel>(props.action, data, 'post', 'form')
|
||||
emits('update:modelValue', ret.url)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-wrapper{
|
||||
position: relative;
|
||||
}
|
||||
.file-handler{
|
||||
opacity: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
60
admin-fe/src/directive/click_outside.ts
Normal file
60
admin-fe/src/directive/click_outside.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import type {
|
||||
ObjectDirective,
|
||||
} from 'vue'
|
||||
|
||||
const nodeList = new Map<Element, Function>();
|
||||
document.addEventListener('mousedown', (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
// @ts-ignore
|
||||
const paths = e.path as Element[];
|
||||
// console.log(paths)
|
||||
// 已经绑定过指令的元素
|
||||
nodeList.forEach((fn, ele) => {
|
||||
// 当前点击元素的数组
|
||||
for (const c of paths) {
|
||||
// 找到对应的元素 说明是点击了该元素或者他的子元素
|
||||
if (c == ele) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 没有找到 说明在外面 可以出发事件
|
||||
fn.call(null)
|
||||
})
|
||||
})
|
||||
|
||||
// 定义指令
|
||||
const clickOutside: ObjectDirective = {
|
||||
// 在绑定元素的 attribute 前
|
||||
// 或事件监听器应用前调用
|
||||
created(el, binding, vnode, prevVnode) {
|
||||
// 下面会介绍各个参数的细节
|
||||
nodeList.set(el,binding.value)
|
||||
// console.log(nodeList)
|
||||
},
|
||||
// 在元素被插入到 DOM 前调用
|
||||
beforeMount(el, binding, vnode, prevVnode) {
|
||||
},
|
||||
// 在绑定元素的父组件
|
||||
// 及他自己的所有子节点都挂载完成后调用
|
||||
mounted(el, binding, vnode, prevVnode) {
|
||||
},
|
||||
// 绑定元素的父组件更新前调用
|
||||
beforeUpdate(el, binding, vnode, prevVnode) {
|
||||
},
|
||||
// 在绑定元素的父组件
|
||||
// 及他自己的所有子节点都更新后调用
|
||||
updated(el, binding, vnode, prevVnode) {
|
||||
},
|
||||
// 绑定元素的父组件卸载前调用
|
||||
beforeUnmount(el, binding, vnode, prevVnode) {
|
||||
nodeList.delete(el)
|
||||
// console.log('remove ', index, el)
|
||||
// nodeList.splice(index, 1)// 移除要销毁的元素
|
||||
// console.log(nodeList)
|
||||
},
|
||||
// 绑定元素的父组件卸载后调用
|
||||
unmounted(el, binding, vnode, prevVnode) {
|
||||
}
|
||||
}
|
||||
|
||||
export default clickOutside
|
@ -4,8 +4,9 @@ import {createPinia} from 'pinia'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import {httpConfig} from "./util/http";
|
||||
import './assets/app.css'
|
||||
import './assets/app.less'
|
||||
import PInput from "./components/input";
|
||||
import clickOutside from "./directive/click_outside";
|
||||
|
||||
httpConfig.baseURL = "http://localhost:8080"
|
||||
|
||||
@ -14,5 +15,6 @@ const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(createPinia());
|
||||
app.component('PInput',PInput)
|
||||
app.directive('click-outside',clickOutside)
|
||||
// 将应用实例挂载到 模板中
|
||||
app.mount('#vue-root-app')
|
@ -2,24 +2,61 @@ import {RouteRecordRaw} from "vue-router";
|
||||
import Home from '../views/admin/Home.vue'
|
||||
import Login from '../views/Login.vue'
|
||||
import NotFound from '../views/NotFound.vue'
|
||||
import Test from '../views/Test.vue'
|
||||
import AdminLayout from '../views/layout/AdminLayout.vue'
|
||||
import UserIndex from '../views/user/index.vue'
|
||||
import GoodsIndex from '../views/goods/index.vue'
|
||||
import OrderIndex from '../views/goods/Order.vue'
|
||||
|
||||
export const AdminRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
meta: {
|
||||
title: 'Home'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
name: 'UserIndex',
|
||||
component: UserIndex,
|
||||
meta: {
|
||||
title: '用户管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'goods',
|
||||
name: 'GoodsIndex',
|
||||
component: GoodsIndex,
|
||||
meta: {
|
||||
title: '商品管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'order',
|
||||
name: 'OrderIndex',
|
||||
component: OrderIndex,
|
||||
meta: {
|
||||
title: '订单管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'test',
|
||||
name: 'TestIndex',
|
||||
component: Test,
|
||||
meta: {
|
||||
title: '测试页'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
component: AdminLayout,
|
||||
redirect: '/home',
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
component: UserIndex
|
||||
}
|
||||
]
|
||||
children: AdminRoutes
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
|
3
admin-fe/src/service/constants.ts
Normal file
3
admin-fe/src/service/constants.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const UPDATE_MODEL_EVENT = 'update:modelValue'
|
||||
export const CHANGE_EVENT = 'change'
|
||||
export const INPUT_EVENT = 'input'
|
@ -52,7 +52,6 @@ export const useUserStore = defineStore('user-store', () => {
|
||||
const data = await http.get<AdminLoginModel>('/admin/user/info')
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
userinfo.value = null
|
||||
|
||||
}
|
||||
|
||||
return {userinfo, login, logout, updateInfo, token}
|
||||
|
31
admin-fe/src/service/types.d.ts
vendored
31
admin-fe/src/service/types.d.ts
vendored
@ -26,4 +26,35 @@ type UserInfoModel = {
|
||||
status: number;
|
||||
pointInfo: PointInfoModel;
|
||||
parent?: UserInfoModel | null;
|
||||
}
|
||||
type GoodsModel = {
|
||||
id?: number;
|
||||
category?: number;
|
||||
type?: number;
|
||||
title?: string;
|
||||
originPrice?: number;
|
||||
price?: number;
|
||||
stock?: number;
|
||||
limitCount?: number;
|
||||
cover?: string;
|
||||
description?: string;
|
||||
notice?: string;
|
||||
onlineTime?: string;
|
||||
offlineTime?: string;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
status?: number;
|
||||
}
|
||||
type OrderInfoModel = {
|
||||
id?: string;
|
||||
gid?: number;
|
||||
orderTitle?: string;
|
||||
price?: number;
|
||||
count?: number;
|
||||
uid?: number;
|
||||
data?: any;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
status?: number;
|
||||
owner?: UserInfoModel;
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import {toast} from "../components/message";
|
||||
import {useUserStore} from "../service/store";
|
||||
import router from '../router'
|
||||
|
||||
export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
|
||||
|
||||
export enum RequestDataContentType {
|
||||
JSON = 'application/json;charset=UTF-8',
|
||||
FORM = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||
FORM_DATA = 'multipart/form-data',
|
||||
}
|
||||
|
||||
export const httpConfig = {
|
||||
@ -41,6 +42,17 @@ class Http {
|
||||
return this.request<T>(url, data, 'get')
|
||||
}
|
||||
|
||||
put<T>(url, data = null) {
|
||||
return this.request<T>(url, data, 'put')
|
||||
}
|
||||
|
||||
remove<T>(url, data = null) {
|
||||
return this.request<T>(url, data, 'delete')
|
||||
}
|
||||
delete<T>(url, data = null) {
|
||||
return this.request<T>(url, data, 'delete')
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /xxxx?a=1&b=2
|
||||
* NAME: value
|
||||
@ -56,7 +68,9 @@ class Http {
|
||||
*/
|
||||
request<T>(url: string, data: any = null, method: HttpMethod = 'post', type: 'normal' | 'form' = 'normal') {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
let contentType = RequestDataContentType.FORM;
|
||||
const headers: any = {
|
||||
'Content-Type': RequestDataContentType.FORM
|
||||
}
|
||||
if (data) {
|
||||
if (type === 'form') {
|
||||
const form = new FormData();
|
||||
@ -64,12 +78,13 @@ class Http {
|
||||
form.append(key, data[key]);
|
||||
}
|
||||
data = form; // 将data有{} => formData
|
||||
contentType = RequestDataContentType.FORM_DATA; // 由于使用formData
|
||||
// contentType = RequestDataContentType.FORM_DATA; // 由于使用formData
|
||||
delete headers['Content-Type'];
|
||||
method = 'post';
|
||||
} else if (method == 'post') {
|
||||
} else if (method == 'post' || method == 'put') {
|
||||
// 将数据对象 转成json
|
||||
data = JSON.stringify(data);
|
||||
contentType = RequestDataContentType.JSON;
|
||||
headers['Content-Type'] = RequestDataContentType.JSON;
|
||||
} else {
|
||||
// 装对象转成 key=value&key=value
|
||||
const params = [];
|
||||
@ -80,10 +95,6 @@ class Http {
|
||||
url += '?' + params.join('&')
|
||||
}
|
||||
}
|
||||
|
||||
const headers: any = {
|
||||
'Content-Type': contentType
|
||||
}
|
||||
const token = useUserStore().token();
|
||||
if (token) headers.token = token
|
||||
fetch(httpConfig.baseURL + url, {
|
||||
@ -99,9 +110,11 @@ class Http {
|
||||
// 需要统一处理数据
|
||||
if (code === 403) {
|
||||
toast("登录凭证无效或者已过期")
|
||||
// if (r.fullPath != '/login') {
|
||||
// router.replace('/login');
|
||||
// }
|
||||
// 通过router获取当前路由
|
||||
const route = router.currentRoute;
|
||||
if (route.fullPath != '/login') {
|
||||
router.replace('/login');
|
||||
}
|
||||
return;
|
||||
} else if (err) {
|
||||
toast(httpConfig.globalErrorHandler[code])
|
||||
|
@ -63,7 +63,7 @@ p{
|
||||
margin: 10px 0;
|
||||
}
|
||||
.login-wrapper {
|
||||
width: 500px;
|
||||
width: 300px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
</style>
|
@ -1,43 +1,59 @@
|
||||
<template>
|
||||
<div class="demo-box">
|
||||
<div class="form-item">
|
||||
<PInput placeholder="测试21" v-model="data.a">
|
||||
<template #suffix>
|
||||
<span>密码</span>
|
||||
</template>
|
||||
</PInput>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<PInput type="password" placeholder="测试12" v-model="data.b"/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<PInput type="password" placeholder="测试12" v-model="data.b"/>
|
||||
</div>
|
||||
<div>
|
||||
{{ JSON.stringify(data) }}
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<div>
|
||||
<div class="form-item">
|
||||
<PInput placeholder="测试21" v-model="data.a">
|
||||
<template #suffix>
|
||||
<span>密码</span>
|
||||
</template>
|
||||
</PInput>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<PInput type="password" placeholder="测试12" v-model="data.b"/>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<PInput type="password" placeholder="测试12" v-model="data.b"/>
|
||||
</div>
|
||||
<div>
|
||||
{{ JSON.stringify(data) }}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
商品类型
|
||||
<p-select v-model="select">
|
||||
<p-option v-for="op in options" :key="op.value" :value="op">{{ op.label }}</p-option>
|
||||
</p-select>
|
||||
{{ JSON.stringify(select) }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import PInput from "../components/input";
|
||||
import {reactive} from "vue";
|
||||
import {reactive, ref} from "vue";
|
||||
import PSelect from "../components/select/Select.vue";
|
||||
import POption from "../components/select/Option.vue";
|
||||
|
||||
const data = reactive({
|
||||
a: '1',
|
||||
b: '2'
|
||||
a: '1',
|
||||
b: '2'
|
||||
})
|
||||
const options = [
|
||||
{value: 1, label: '普通商品'},
|
||||
{value: 2, label: '精选商品'},
|
||||
{value: 3, label: '秒杀商品'},
|
||||
]
|
||||
const select = ref()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demo-box {
|
||||
width: 500px;
|
||||
border: solid 1px #f00;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
width: 500px;
|
||||
border: solid 1px #f00;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin: 10px 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
140
admin-fe/src/views/goods/Order.vue
Normal file
140
admin-fe/src/views/goods/Order.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="table-toolbar">
|
||||
<div class="search-form">
|
||||
<span>订单编号:<PInput style="width:200px" v-model="param.id" placeholder="要查询的订单编号"/></span>
|
||||
<span>标题:<PInput style="width:200px" v-model="param.title" placeholder="要查询的订单标题"/></span>
|
||||
<span>订单开始:<PInput style="width:200px" v-model="param.createTimeStart" placeholder="要查询的订单开始时间"/></span>
|
||||
<span>订单结束:<PInput style="width:200px" v-model="param.createTimeEnd" placeholder="要查询的订单结束时间"/></span>
|
||||
|
||||
<span>
|
||||
<PButton type="default" @click="onReset">重置</PButton>
|
||||
</span>
|
||||
<span><PButton :loading="searching" @click="onSearch">搜索</PButton></span>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>订单编号</th>
|
||||
<th>标题</th>
|
||||
<th>价格</th>
|
||||
<th>数量</th>
|
||||
<th>状态</th>
|
||||
<th>用户</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="it in goodsList" :key="it.id">
|
||||
<td>{{ it.id }}</td>
|
||||
<td>{{ it.orderTitle }}</td>
|
||||
<td>{{ it.price }}</td>
|
||||
<td>{{ it.count }}</td>
|
||||
<td>{{ StatusEnum[it.status] || '未知' }}</td>
|
||||
<td>{{ it.owner?.nickname }}</td>
|
||||
<td>{{ it.createTime }}</td>
|
||||
<td width="160">
|
||||
<PButton v-if="it.status == 1" type="link" @click="updateStatus(it.id,2)">确认</PButton>
|
||||
<PButton v-if="it.status < 3" type="link" @click="updateStatus(it.id,3)">取消</PButton>
|
||||
<PButton v-if="it.status < 3" type="link" @click="updateStatus(it.id,4)">完成</PButton>
|
||||
<PButton type="link" @click="removeData(it.id)">删除</PButton>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<Pager :total="totalCount" show-refresh @refresh="loadDataList" @page-change="onPageChange"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import http, {DataListModel} from "../../util/http";
|
||||
import message from "../../components/message";
|
||||
import Pager from "../../components/pager/Pager.vue";
|
||||
import PButton from "../../components/button/Index.vue";
|
||||
|
||||
const StatusEnum = {
|
||||
1: '待确认',
|
||||
2: '已确认',
|
||||
3: '已取消',
|
||||
4: '已完成',
|
||||
0: '已删除'
|
||||
}
|
||||
const param = reactive({
|
||||
id: null,
|
||||
title: null,
|
||||
createTimeStart: null,
|
||||
createTimeEnd: null,
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const goodsList = ref<OrderInfoModel[]>([]);
|
||||
const totalCount = ref(0)
|
||||
const searching = ref(false)
|
||||
|
||||
function onPageChange(currentPage: number) {
|
||||
param.page = currentPage;
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
param.page = 1;
|
||||
param.title = null;
|
||||
param.id = null
|
||||
param.createTimeStart = null
|
||||
param.createTimeEnd = null
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function onSearch() {
|
||||
param.page = 1;
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function loadDataList() {
|
||||
searching.value = true
|
||||
http.post<DataListModel<OrderInfoModel>>('/admin/order/list', param).then(res => {
|
||||
goodsList.value = res.items
|
||||
totalCount.value = res.total
|
||||
}).finally(() => searching.value = false)
|
||||
}
|
||||
|
||||
async function updateStatus(id: string, status: number) {
|
||||
if (!confirm('是否继续操作?')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await http.put(`/admin/order/${id}/status?status=${status}`);
|
||||
message.toast('操作成功');
|
||||
loadDataList();
|
||||
} catch (e) {
|
||||
message.toast(e.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param id 要删除的用户id
|
||||
*/
|
||||
async function removeData(id: string) {
|
||||
if (!confirm('是否删除?')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await http.delete(`/admin/order/${id}`);
|
||||
message.toast('删除成功');
|
||||
loadDataList();
|
||||
} catch (e) {
|
||||
message.toast(e.message || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadDataList)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-form span {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
296
admin-fe/src/views/goods/index.vue
Normal file
296
admin-fe/src/views/goods/index.vue
Normal file
@ -0,0 +1,296 @@
|
||||
<template>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="table-toolbar">
|
||||
<div class="search-form">
|
||||
<span>标题:<PInput style="width:200px" v-model="param.title" placeholder="要查询的商品标题"/></span>
|
||||
<span>
|
||||
分类:
|
||||
<PSelect v-model="param.category">
|
||||
<POption v-for="(text,value) in CategoryEnum" :key="value" :value="value">{{text}}</POption>
|
||||
</PSelect>
|
||||
</span>
|
||||
<span>
|
||||
<PButton type="default" @click="onReset">重置</PButton>
|
||||
</span>
|
||||
<span><PButton :loading="searching" @click="onSearch">搜索</PButton></span>
|
||||
</div>
|
||||
<div class="toolbar-extra">
|
||||
<PButton @click="onEditData({id:0})">新增</PButton>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>商品编号</th>
|
||||
<th>标题</th>
|
||||
<th>图片</th>
|
||||
<th>分类</th>
|
||||
<th>类型</th>
|
||||
<th>原价</th>
|
||||
<th>售卖价</th>
|
||||
<th>库存</th>
|
||||
<th>限购数量</th>
|
||||
<th>上架时间</th>
|
||||
<th>下架时间</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="it in goodsList" :key="it.id">
|
||||
<td>{{ it.id }}</td>
|
||||
<td>{{ it.title }}</td>
|
||||
<td><img :src="it.cover" style="width: 32px;height: 32px;"/></td>
|
||||
<td>{{ CategoryEnum[it.category] || '未知' }}</td>
|
||||
<td>{{ TypeEnum[it.type] || '未知' }}</td>
|
||||
<td>{{ it.originPrice == 0 ? '-' : it.originPrice }}</td>
|
||||
<td>{{ it.price }}</td>
|
||||
<td>{{ it.stock }}</td>
|
||||
<td>{{ it.limitCount }}</td>
|
||||
<td>{{ it.onlineTime }}</td>
|
||||
<td>{{ it.offlineTime }}</td>
|
||||
<td>{{ StatusEnum[it.status] || '未知' }}</td>
|
||||
<td>
|
||||
<PButton type="link" @click="onEditData(it)">编辑</PButton>
|
||||
<PButton type="link">禁用</PButton>
|
||||
<PButton type="link" @click="removeData(it.id)">删除</PButton>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<Pager :total="totalCount" show-refresh @refresh="loadDataList" @page-change="onPageChange"/>
|
||||
</div>
|
||||
<Modal v-model="modalVisible">
|
||||
<div>
|
||||
<div>
|
||||
<span>标题</span>
|
||||
<p>
|
||||
<PInput placeholder="请输入标题" v-model="editData.title"/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="display-flex">
|
||||
<div>
|
||||
|
||||
<span>分类</span>
|
||||
<p>
|
||||
<select v-model="editData.category">
|
||||
<option v-for="(text,value) in CategoryEnum" :key="value" :value="value">{{ text }}</option>
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>类型</span>
|
||||
<p>
|
||||
<select v-model="editData.type">
|
||||
<option v-for="(text,value) in TypeEnum" :key="value" :value="value">{{ text }}</option>
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品图片</span>
|
||||
<!-- 上传组件 -->
|
||||
<Uploader action="/file/upload" v-model="editData.cover">
|
||||
<div v-if="editData.cover">
|
||||
<img :src="editData.cover" alt="" style="width: 120px;height: 120px;">
|
||||
</div>
|
||||
<div v-else>选择商品图</div>
|
||||
</Uploader>
|
||||
</div>
|
||||
<div>
|
||||
<span>描述</span>
|
||||
<p>
|
||||
<PInput placeholder="请输入描述" v-model="editData.description"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>原价</span>
|
||||
<p>
|
||||
<PInput type="number" placeholder="请输入原价" v-model="editData.originPrice"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>销售价格</span>
|
||||
<p>
|
||||
<PInput type="number" placeholder="请输入销售价格" v-model="editData.price"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>库存</span>
|
||||
<p>
|
||||
<PInput type="number" placeholder="请输入库存" v-model="editData.stock"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>限购数量</span>
|
||||
<p>
|
||||
<PInput type="number" placeholder="请输入限购数量" v-model="editData.limitCount"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>上架时间</span>
|
||||
<p>
|
||||
<PInput placeholder="请输入上架时间" v-model="editData.onlineTime"/>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>下架时间</span>
|
||||
<p>
|
||||
<PInput placeholder="请输入下架时间" v-model="editData.offlineTime"/>
|
||||
</p>
|
||||
</div>
|
||||
<PButton @click="saveGoodsData">提交</PButton>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import http, {DataListModel} from "../../util/http";
|
||||
import message from "../../components/message";
|
||||
import Modal from "../../components/modal/modal.vue";
|
||||
import Pager from "../../components/pager/Pager.vue";
|
||||
import Uploader from "../../components/uploader/uploader.vue";
|
||||
import PButton from "../../components/button/Index.vue";
|
||||
import PSelect, {OptionItemType} from "../../components/select/Select.vue";
|
||||
import POption from "../../components/select/Option.vue";
|
||||
|
||||
//
|
||||
//商品类别(1:普通 2:精选 3:秒杀 4:抽奖)
|
||||
const CategoryEnum = {
|
||||
1: '普通',
|
||||
2: '精选',
|
||||
3: '秒杀',
|
||||
4: '抽奖',
|
||||
}
|
||||
//商品类型(1:实物 2:虚拟)
|
||||
const TypeEnum = {
|
||||
1: '实物',
|
||||
2: '虚拟'
|
||||
}
|
||||
|
||||
const options: OptionItemType[] = [
|
||||
{
|
||||
label: '普通', value: 1
|
||||
},
|
||||
{
|
||||
label: '精选', value: 2
|
||||
},
|
||||
{
|
||||
label: '秒杀', value: 3
|
||||
}
|
||||
]
|
||||
|
||||
const StatusEnum = {
|
||||
1: '正常',
|
||||
2: '禁用',
|
||||
0: '已删除'
|
||||
}
|
||||
const param = reactive({
|
||||
title: null,
|
||||
category: null,
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const modalVisible = ref(false)
|
||||
|
||||
const goodsList = ref<GoodsModel[]>([]);
|
||||
const editData = reactive<GoodsModel>({
|
||||
id: 0
|
||||
});
|
||||
const totalCount = ref(0)
|
||||
const searching = ref(false)
|
||||
const saving = ref(false)
|
||||
|
||||
function onPageChange(currentPage: number) {
|
||||
param.page = currentPage;
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
param.page = 1;
|
||||
param.title = null;
|
||||
param.category = null
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function onSearch() {
|
||||
param.page = 1;
|
||||
loadDataList();
|
||||
}
|
||||
|
||||
function loadDataList() {
|
||||
searching.value = true
|
||||
http.post<DataListModel<GoodsModel>>('/admin/goods/list', param).then(res => {
|
||||
goodsList.value = res.items
|
||||
totalCount.value = res.total
|
||||
}).finally(() => searching.value = false)
|
||||
}
|
||||
|
||||
function fillDataToObject(obj: any, data: any, keys: string[]) {
|
||||
keys.forEach(key => {
|
||||
if (typeof (data[key]) === 'undefined') {
|
||||
if (typeof (obj[key]) != 'undefined') {
|
||||
// 如果数据的字段不存在则将在字段的值设置为null
|
||||
obj[key] = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
obj[key] = data[key]
|
||||
})
|
||||
}
|
||||
|
||||
const fieldKey = [
|
||||
'id', 'cover', 'title',
|
||||
'type', 'category', 'description',
|
||||
'offlineTime', 'onlineTime', 'price',
|
||||
'originPrice', 'stock', 'limitCount',
|
||||
]
|
||||
|
||||
function onEditData(data: GoodsModel) {
|
||||
fillDataToObject(editData, data, fieldKey)
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
async function saveGoodsData() {
|
||||
try {
|
||||
saving.value = true
|
||||
if (editData.id <= 0) {
|
||||
await http.post('/admin/goods', editData)
|
||||
} else {
|
||||
await http.put(`/admin/goods/${editData.id}`, editData)
|
||||
}
|
||||
modalVisible.value = false;
|
||||
message.toast('保存成功');
|
||||
loadDataList();
|
||||
} catch (e) {
|
||||
message.toast(e.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
* @param id 要删除的用户id
|
||||
*/
|
||||
async function removeData(id: number) {
|
||||
if (!confirm('是否删除?')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await http.delete(`/admin/goods/${id}`);
|
||||
message.toast('删除成功');
|
||||
loadDataList();
|
||||
} catch (e) {
|
||||
message.toast(e.message || '删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadDataList)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-form span {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
@ -6,14 +6,17 @@
|
||||
<span>
|
||||
{{ userStore.userinfo?.account }}
|
||||
</span>
|
||||
<span @click="logout">退出</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="left-menu">
|
||||
<div class="title">积分管理系统</div>
|
||||
<div class="menu-list">
|
||||
<router-link active-class="active-menu" to="/home">HOME</router-link>
|
||||
<router-link active-class="active-menu" to="/user">用户管理</router-link>
|
||||
<router-link v-for="r in AdminRoutes" active-class="active-menu" :to="'/' + r.path">{{
|
||||
r.meta.title
|
||||
}}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
@ -27,18 +30,26 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {useUserStore} from "../../service/store";
|
||||
import {useRouter} from "vue-router";
|
||||
import {AdminRoutes} from './../../router/routes'
|
||||
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter()
|
||||
|
||||
function logout() {
|
||||
userStore.logout()
|
||||
router.replace('/login')
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
||||
.app-admin-layout {
|
||||
min-height: 100vh;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: var(--primary-color);
|
||||
background-color: var(--header-bg);
|
||||
color: white;
|
||||
line-height: var(--header-height);
|
||||
padding: 0 20px;
|
||||
@ -48,6 +59,7 @@ const userStore = useUserStore();
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.main {
|
||||
@ -56,34 +68,42 @@ const userStore = useUserStore();
|
||||
|
||||
.left-menu {
|
||||
width: 200px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
background-color: #fff;
|
||||
color:#333;
|
||||
box-shadow: 0 2px 3px rgba(0,0,0,0.4);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
font-size: 14px;
|
||||
}
|
||||
.title{
|
||||
|
||||
.title {
|
||||
height: var(--header-height);
|
||||
background-color: #333;
|
||||
background-color: var(--header-bg);
|
||||
line-height: var(--header-height);
|
||||
text-align: center;
|
||||
color:white;
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
.menu-list{
|
||||
|
||||
.menu-list {
|
||||
margin-top: 20px;
|
||||
a{
|
||||
|
||||
a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color:white;
|
||||
padding:10px 20px;
|
||||
&.active-menu{
|
||||
background-color: #666666;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
color:#333;
|
||||
|
||||
&.active-menu {
|
||||
background-color: var(--primary-1);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
margin: 20px;
|
||||
background-color: #fff;
|
||||
|
@ -1,35 +1,38 @@
|
||||
<template>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>UID</th>
|
||||
<th>昵称</th>
|
||||
<th>OPEN_ID</th>
|
||||
<th>性别</th>
|
||||
<th>地区</th>
|
||||
<th>推荐者</th>
|
||||
<th>状态</th>
|
||||
<th>积分</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="u in userList" :key="u.id">
|
||||
<td>{{ u.id }}</td>
|
||||
<td>{{ u.nickname }}</td>
|
||||
<td>{{ u.openId }}</td>
|
||||
<td>{{ GenderEnum[u.gender] || '未知' }}</td>
|
||||
<td>{{ u.province + '-' + u.city }}</td>
|
||||
<td>{{ u.parent?.nickname || '-' }}</td>
|
||||
<td>{{ StatusEnum[u.status] || '未知' }}</td>
|
||||
<td>{{ u.pointInfo?.totalPoint }}</td>
|
||||
<td>{{ u.firstLoginTime }}</td>
|
||||
<td>
|
||||
<span @click="editUser(u)">编辑</span>
|
||||
<span>禁用</span>
|
||||
<span @click="removeUser(u.id)">删除</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<tr>
|
||||
<th>UID</th>
|
||||
<th>昵称</th>
|
||||
<th>OPEN_ID</th>
|
||||
<th>性别</th>
|
||||
<th>地区</th>
|
||||
<th>推荐者</th>
|
||||
<th>状态</th>
|
||||
<th>积分</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="u in userList" :key="u.id">
|
||||
<td>{{ u.id }}</td>
|
||||
<td>{{ u.nickname }}</td>
|
||||
<td>{{ u.openId }}</td>
|
||||
<td>{{ GenderEnum[u.gender] || '未知' }}</td>
|
||||
<td>{{ u.province + '-' + u.city }}</td>
|
||||
<td>{{ u.parent?.nickname || '-' }}</td>
|
||||
<td>{{ StatusEnum[u.status] || '未知' }}</td>
|
||||
<td>{{ u.pointInfo?.totalPoint }}</td>
|
||||
<td>{{ u.firstLoginTime }}</td>
|
||||
<td>
|
||||
<span @click="editUser(u)">编辑</span>
|
||||
<span>禁用</span>
|
||||
<span @click="removeUser(u.id)">删除</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<Pager :total="userTotalCount" show-refresh @refresh="loadUserList" @page-change="onPageChange"/>
|
||||
</div>
|
||||
<Modal v-model="modalVisible">
|
||||
<div>
|
||||
<div>
|
||||
@ -60,6 +63,7 @@ import {onMounted, reactive, ref} from "vue";
|
||||
import http, {DataListModel} from "../../util/http";
|
||||
import message from "../../components/message";
|
||||
import Modal from "../../components/modal/modal.vue";
|
||||
import Pager from "../../components/pager/Pager.vue";
|
||||
//
|
||||
const GenderEnum = {
|
||||
1: '男', 2: '女'
|
||||
@ -88,6 +92,11 @@ const editUserData = reactive({
|
||||
});
|
||||
const userTotalCount = ref(0)
|
||||
|
||||
function onPageChange(currentPage: number) {
|
||||
param.page = currentPage;
|
||||
loadUserList();
|
||||
}
|
||||
|
||||
function loadUserList() {
|
||||
http.post<DataListModel<UserInfoModel>>('/admin/user/list', param).then(res => {
|
||||
userList.value = res.items
|
||||
|
@ -12,6 +12,15 @@
|
||||
"**/*.js"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve"
|
||||
"jsx": "preserve",
|
||||
"types": [
|
||||
"unplugin-vue-define-options/macros-global"
|
||||
],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es5",
|
||||
"scripthost",
|
||||
"es2015.collection"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,14 +1,17 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
|
||||
import DefineOptions from 'unplugin-vue-define-options/vite'
|
||||
|
||||
export default {
|
||||
hmr: true,
|
||||
// 开发服务信息
|
||||
server: {
|
||||
host:'::'
|
||||
host: '::'
|
||||
},
|
||||
// 必须配置vue插件
|
||||
plugins: [
|
||||
vue(),
|
||||
DefineOptions(),
|
||||
vueJsxPlugin(), // 如果需要使用jsx或者tsx
|
||||
]
|
||||
}
|
@ -200,7 +200,7 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.5":
|
||||
"@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.20.0", "@babel/parser@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
|
||||
integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
|
||||
@ -253,7 +253,7 @@
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5":
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
|
||||
integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==
|
||||
@ -312,6 +312,14 @@
|
||||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@rollup/pluginutils@^4.2.1":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
|
||||
dependencies:
|
||||
estree-walker "^2.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@types/node@^18.11.10":
|
||||
version "18.11.10"
|
||||
resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34"
|
||||
@ -376,6 +384,15 @@
|
||||
"@volar/typescript" "1.0.11"
|
||||
"@volar/vue-language-core" "1.0.11"
|
||||
|
||||
"@vue-macros/common@~0.13.4":
|
||||
version "0.13.4"
|
||||
resolved "https://registry.npmmirror.com/@vue-macros/common/-/common-0.13.4.tgz#f1a12c63aad18ad0020101bf386f5e9c95d444ba"
|
||||
integrity sha512-mQooO33XcY4kQyKBrbGfdIPPsYhpcfmH75SQnXx2vNsNLSNvhLuDaIIV0fhMJ0HV5Z02V9Ka1gx7v1g5bk9Q0A==
|
||||
dependencies:
|
||||
"@babel/types" "^7.20.2"
|
||||
"@vue/compiler-sfc" "^3.2.45"
|
||||
magic-string "^0.26.7"
|
||||
|
||||
"@vue/babel-helper-vue-transform-on@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz#9b9c691cd06fc855221a2475c3cc831d774bc7dc"
|
||||
@ -491,6 +508,11 @@
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2"
|
||||
integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==
|
||||
|
||||
acorn@^8.8.1:
|
||||
version "8.8.1"
|
||||
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
|
||||
integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
@ -498,11 +520,32 @@ ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
ast-walker-scope@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmmirror.com/ast-walker-scope/-/ast-walker-scope-0.3.0.tgz#955b00af19946e76d39ba86d3046b9bc2b7312d9"
|
||||
integrity sha512-bsOBv3jB+1kGaxwPHhkLiagS+75KfzEqtkNWvATgMGtXM6kJZG3PlG4fYQFMiHeLpoAkwc6G61w07+hEXx39aA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.20.0"
|
||||
"@babel/types" "^7.20.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||
@ -510,6 +553,13 @@ brace-expansion@^2.0.1:
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
browserslist@^4.21.3:
|
||||
version "4.21.4"
|
||||
resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
|
||||
@ -539,6 +589,21 @@ chalk@^2.0.0:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
@ -742,11 +807,18 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
estree-walker@^2.0.2:
|
||||
estree-walker@^2.0.1, estree-walker@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
@ -762,6 +834,13 @@ gensync@^1.0.0-beta.2:
|
||||
resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
@ -806,6 +885,13 @@ image-size@~0.5.0:
|
||||
resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
|
||||
integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.9.0:
|
||||
version "2.11.0"
|
||||
resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
|
||||
@ -813,6 +899,23 @@ is-core-module@^2.9.0:
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-what@^3.14.1:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
|
||||
@ -857,6 +960,13 @@ magic-string@^0.25.7:
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
magic-string@^0.26.7:
|
||||
version "0.26.7"
|
||||
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f"
|
||||
integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
make-dir@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
@ -911,6 +1021,11 @@ node-releases@^2.0.6:
|
||||
resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
|
||||
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
parse-node-version@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
|
||||
@ -926,6 +1041,11 @@ picocolors@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
pify@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
||||
@ -953,6 +1073,13 @@ prr@~1.0.1:
|
||||
resolved "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
|
||||
integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
resolve@^1.22.1:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
|
||||
@ -1026,6 +1153,13 @@ to-fast-properties@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tslib@^2.3.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
|
||||
@ -1036,6 +1170,26 @@ typescript@^4.9.3:
|
||||
resolved "https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
|
||||
integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==
|
||||
|
||||
unplugin-vue-define-options@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/unplugin-vue-define-options/-/unplugin-vue-define-options-1.0.0.tgz#0aabe28748d5b445e68b6d44bc1170e3d01622a9"
|
||||
integrity sha512-j90zM7NhZXBL5uMlHKzSOjvU98lFcIErdgAhj7bEEdvZarkwOkEUgMFsZDwStN9FEcMAiS/BTvcyGfItu3ry/g==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^4.2.1"
|
||||
"@vue-macros/common" "~0.13.4"
|
||||
ast-walker-scope "^0.3.0"
|
||||
unplugin "^1.0.0"
|
||||
|
||||
unplugin@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/unplugin/-/unplugin-1.0.0.tgz#8d12e0d116bf56313d42755094fc370e9c18da86"
|
||||
integrity sha512-H5UnBUxfhTXBXGo2AwKsl0UaLSHzSNDZNehPQSgdhVfO/t+XAS1Yoj3vmLrrlBrS9ZwtH5tejbX/TCp5DcyCKg==
|
||||
dependencies:
|
||||
acorn "^8.8.1"
|
||||
chokidar "^3.5.3"
|
||||
webpack-sources "^3.2.3"
|
||||
webpack-virtual-modules "^0.4.6"
|
||||
|
||||
update-browserslist-db@^1.0.9:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
|
||||
@ -1094,3 +1248,13 @@ vue@^3.2.45:
|
||||
"@vue/runtime-dom" "3.2.45"
|
||||
"@vue/server-renderer" "3.2.45"
|
||||
"@vue/shared" "3.2.45"
|
||||
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack-virtual-modules@^0.4.6:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz#3e4008230731f1db078d9cb6f68baf8571182b45"
|
||||
integrity sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==
|
||||
|
@ -11,7 +11,9 @@ public class ApplicationConfig implements WebMvcConfigurer {
|
||||
// 跨域 目前小程序不需要,暂时不用考虑
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedOrigins("*");
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE");
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
package me.xiaoyan.point.api.controller;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import me.xiaoyan.point.api.pojo.dto.FileUploadResult;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("file")
|
||||
public class FileController {
|
||||
|
||||
@SneakyThrows
|
||||
@PostMapping("upload")
|
||||
public FileUploadResult upload(MultipartFile multipartFile) {
|
||||
Thread.sleep(1);
|
||||
String url = "https://m.360buyimg.com/mobilecms/s750x750_jfs/t1/211852/28/15135/312413/6235a37fEc6496443/de07faa29ece45ee.jpg";
|
||||
return new FileUploadResult(url, "");
|
||||
}
|
||||
}
|
@ -1,13 +1,21 @@
|
||||
package me.xiaoyan.point.api.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.xiaoyan.point.api.error.BizException;
|
||||
import me.xiaoyan.point.api.pojo.OrderInfo;
|
||||
import me.xiaoyan.point.api.pojo.UserInfo;
|
||||
import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
|
||||
import me.xiaoyan.point.api.pojo.vo.OrderQueryParam;
|
||||
import me.xiaoyan.point.api.pojo.vo.PageDataResult;
|
||||
import me.xiaoyan.point.api.service.GoodsService;
|
||||
import me.xiaoyan.point.api.service.OrderInfoService;
|
||||
import me.xiaoyan.point.api.util.OrderStatus;
|
||||
import me.xiaoyan.point.api.util.QueryWrapperUtil;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -47,9 +55,9 @@ public class ShopOrderInfoController {
|
||||
//TODO 应该定时更新缓存数据
|
||||
goodsService.queryAllGoodsIdAndStock().forEach(g -> {
|
||||
log.info("缓存 id:{} stock:{} ", g.getId(), g.getStock());
|
||||
if(g.getStock() == 0){
|
||||
if (g.getStock() == 0) {
|
||||
// 原本就没有库存
|
||||
stockOutMap.put(g.getId(),true);
|
||||
stockOutMap.put(g.getId(), true);
|
||||
}
|
||||
// 缓存库存
|
||||
stringRedisTemplate.opsForValue().set(cacheKey(g.getId()), g.getStock().toString());
|
||||
@ -64,7 +72,7 @@ public class ShopOrderInfoController {
|
||||
int uid = StpUtil.getLoginIdAsInt();
|
||||
// 如果限制用户只能购买的数量,可以添加一个map记录用户的请求数
|
||||
int size = stockOutMap.size();
|
||||
Long buyId = (long)data.getGoodsId();
|
||||
Long buyId = (long) data.getGoodsId();
|
||||
//1.内存判断
|
||||
if (size > 0 && stockOutMap.containsKey(buyId) && stockOutMap.get(buyId)) {
|
||||
throw BizException.create("库存不足");
|
||||
@ -74,22 +82,59 @@ public class ShopOrderInfoController {
|
||||
if (count < 0) {
|
||||
log.info("stock count ===>" + count);
|
||||
// 对缓存进行库存 还原
|
||||
stringRedisTemplate.opsForValue().increment(cacheKey(buyId),data.getBuyCount()); //
|
||||
stringRedisTemplate.opsForValue().increment(cacheKey(buyId), data.getBuyCount()); //
|
||||
throw BizException.create("库存不足");
|
||||
}
|
||||
if(count == 0){
|
||||
if (count == 0) {
|
||||
// 此时库存没有了 , 保存到已买完的对象
|
||||
stockOutMap.put(buyId,true);
|
||||
stockOutMap.put(buyId, true);
|
||||
}
|
||||
//3.数据库
|
||||
try{
|
||||
try {
|
||||
return orderInfoService.create(uid, data);
|
||||
}catch (BizException e){
|
||||
} catch (BizException e) {
|
||||
//下单失败 对缓存进行库存 还原
|
||||
stringRedisTemplate.opsForValue().increment(cacheKey(data.getGoodsId()),data.getBuyCount()); //
|
||||
stockOutMap.put((long)data.getGoodsId(),false);
|
||||
stringRedisTemplate.opsForValue().increment(cacheKey(data.getGoodsId()), data.getBuyCount()); //
|
||||
stockOutMap.put((long) data.getGoodsId(), false);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 只能查询自己关联的订单信息
|
||||
@PostMapping("/query")
|
||||
public Page<OrderInfo> query(@RequestBody OrderQueryParam param) {
|
||||
param.setUid(StpUtil.getLoginIdAsInt());
|
||||
return orderInfoService.queryByPage(param, true, false);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public OrderInfo get(@PathVariable("id") String id) {
|
||||
return orderInfoService.getOneByIdAndUid(id, StpUtil.getLoginIdAsInt());
|
||||
}
|
||||
|
||||
private boolean updateStatus(String id, int status) {
|
||||
if (!StringUtils.hasText(id)) throw BizException.paramError();
|
||||
QueryWrapper<OrderInfo> q = QueryWrapperUtil.builder()
|
||||
.eq("id", id)
|
||||
.eq("uid", StpUtil.getLoginIdAsInt())
|
||||
.build();
|
||||
return orderInfoService.update(
|
||||
OrderInfo.builder()
|
||||
.status(status)
|
||||
.build(),
|
||||
q
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/cancel")
|
||||
public boolean cancelOrder(@PathVariable("id") String id) {
|
||||
return updateStatus(id, OrderStatus.CANCELED);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/delete")
|
||||
public boolean deleteOrder(@PathVariable("id") String id) {
|
||||
return updateStatus(id, OrderStatus.DELETE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
package me.xiaoyan.point.api.controller.admin;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import me.xiaoyan.point.api.error.BizException;
|
||||
import me.xiaoyan.point.api.pojo.Goods;
|
||||
import me.xiaoyan.point.api.pojo.vo.GoodsQueryParam;
|
||||
import me.xiaoyan.point.api.pojo.vo.PageDataResult;
|
||||
import me.xiaoyan.point.api.pojo.vo.PageParam;
|
||||
import me.xiaoyan.point.api.service.GoodsService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import me.xiaoyan.point.api.util.DataStatus;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@ -22,4 +23,43 @@ public class GoodsAdminController {
|
||||
public PageDataResult list(@RequestBody GoodsQueryParam param) {
|
||||
return PageDataResult.convert(goodsService.queryByPage(param));
|
||||
}
|
||||
|
||||
// 新增
|
||||
@PostMapping
|
||||
public Goods create(@RequestBody Goods goods) {
|
||||
if (!goodsService.save(goods)) {
|
||||
throw BizException.saveFail();
|
||||
}
|
||||
return goods;
|
||||
}
|
||||
|
||||
// 修改
|
||||
@PutMapping("/{id}")
|
||||
public Goods update(@RequestBody Goods goods, @PathVariable("id") long id) {
|
||||
goods.setId(id);
|
||||
if (!goodsService.updateById(goods)) {
|
||||
throw BizException.saveFail();
|
||||
}
|
||||
return goods;
|
||||
}
|
||||
|
||||
// 查询
|
||||
@GetMapping("/{id}")
|
||||
public Goods get(@PathVariable("id") long id) {
|
||||
return goodsService.getById(id);
|
||||
}
|
||||
|
||||
// 删除
|
||||
@DeleteMapping("/{id}")
|
||||
public boolean remove(@PathVariable("id") long id) {
|
||||
if (id < 1) throw BizException.paramError();
|
||||
// 只是逻辑删除 -> 将状态更新未删除即可
|
||||
|
||||
return goodsService.updateById(
|
||||
Goods.builder()
|
||||
.id(id)
|
||||
.status(DataStatus.DELETE) // 状态设置为删除
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
package me.xiaoyan.point.api.controller.admin;
|
||||
|
||||
import me.xiaoyan.point.api.error.BizException;
|
||||
import me.xiaoyan.point.api.pojo.OrderInfo;
|
||||
import me.xiaoyan.point.api.pojo.vo.OrderQueryParam;
|
||||
import me.xiaoyan.point.api.pojo.vo.PageDataResult;
|
||||
import me.xiaoyan.point.api.service.OrderInfoService;
|
||||
import me.xiaoyan.point.api.util.DataStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/order")
|
||||
public class OrderAdminController {
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
|
||||
@PostMapping("/list")
|
||||
public PageDataResult list(@RequestBody OrderQueryParam param) {
|
||||
return PageDataResult.convert(orderInfoService.queryByPage(param,false,true));
|
||||
}
|
||||
|
||||
// 修改
|
||||
@PutMapping("/{id}")
|
||||
public OrderInfo update(@RequestBody OrderInfo info, @PathVariable("id") String id) {
|
||||
info.setId(id);
|
||||
if (!orderInfoService.updateById(info)) {
|
||||
throw BizException.saveFail();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// 查询
|
||||
@GetMapping("/{id}")
|
||||
public OrderInfo get(@PathVariable("id") String id) {
|
||||
return orderInfoService.getById(id);
|
||||
}
|
||||
|
||||
// 删除
|
||||
@DeleteMapping("/{id}")
|
||||
public boolean remove(@PathVariable("id") String id) {
|
||||
if (!StringUtils.hasText(id)) throw BizException.paramError();
|
||||
// 只是逻辑删除 -> 将状态更新未删除即可
|
||||
|
||||
return orderInfoService.updateById(
|
||||
OrderInfo.builder()
|
||||
.id(id)
|
||||
.status(DataStatus.DELETE) // 状态设置为删除
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
public boolean setStatus(@PathVariable("id") String id, int status) {
|
||||
if (!StringUtils.hasText(id)) throw BizException.paramError();
|
||||
return orderInfoService.updateById(
|
||||
OrderInfo.builder()
|
||||
.id(id)
|
||||
.status(status)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
@ -40,6 +40,12 @@ public class UserAdminController {
|
||||
userStoreMap.put("test", UserAdminInfo.create(2, "test", "123123"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public UserInfo update(@RequestBody UserInfo userInfo) {
|
||||
if (!userInfoService.updateById(userInfo)) throw BizException.saveFail();
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @status released
|
||||
@ -50,7 +56,7 @@ public class UserAdminController {
|
||||
@SneakyThrows
|
||||
@PostMapping("login")
|
||||
public UserAdminInfo login(@Validated @RequestBody UserAdminInfo user) {
|
||||
Thread.sleep(1);
|
||||
Thread.sleep(2000);
|
||||
// 判断是否存在账号
|
||||
if (!userStoreMap.containsKey(user.getAccount())) {
|
||||
throw BizException.create("账号不存在");
|
||||
|
@ -17,7 +17,20 @@ public class BizException extends RuntimeException {
|
||||
public static BizException create(String message) {
|
||||
return new BizException(-1, message);
|
||||
}
|
||||
public static BizException create(int code,String message) {
|
||||
|
||||
public static BizException create(int code, String message) {
|
||||
return new BizException(code, message);
|
||||
}
|
||||
|
||||
public static BizException saveFail() {
|
||||
return create(1022, "保存失败");
|
||||
}
|
||||
|
||||
public static BizException paramError(String message) {
|
||||
return create(1201, message);
|
||||
}
|
||||
|
||||
public static BizException paramError() {
|
||||
return paramError("请求参数不正确");
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -83,21 +84,25 @@ public class Goods implements Serializable {
|
||||
/**
|
||||
* 上架时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date onlineTime;
|
||||
|
||||
/**
|
||||
* 下架时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date offlineTime;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
|
@ -2,9 +2,11 @@ package me.xiaoyan.point.api.pojo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -24,13 +26,14 @@ import java.io.Serializable;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@TableName(value ="order_info")
|
||||
@TableName(value = "order_info")
|
||||
public class OrderInfo {
|
||||
@TableId
|
||||
//订单编号
|
||||
private String id;
|
||||
//商品编号
|
||||
private Long gid;
|
||||
private String orderTitle;
|
||||
//价格
|
||||
private Integer price;
|
||||
//购买数量
|
||||
@ -39,12 +42,17 @@ public class OrderInfo {
|
||||
private Integer uid;
|
||||
//订单数据
|
||||
private String data;
|
||||
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
//订单状态(0:已删除 1:已取消 2:待确认 3:已完成)
|
||||
private Integer status;
|
||||
|
||||
@TableField(exist = false)
|
||||
private UserInfo owner;
|
||||
@TableField(exist = false)
|
||||
private Goods goods;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package me.xiaoyan.point.api.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -22,6 +23,7 @@ public class Point implements Serializable {
|
||||
private Integer totalPoint;
|
||||
private Integer validPoint;
|
||||
private Integer expirePoint;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
private Date updateTime;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package me.xiaoyan.point.api.pojo;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -25,6 +26,8 @@ public class PointRecord implements Serializable {
|
||||
private Integer point;
|
||||
private Integer currentTotalPoint;
|
||||
private String reason;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date validTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package me.xiaoyan.point.api.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@ -22,5 +23,7 @@ public class SignRecord implements Serializable {
|
||||
private Integer uid;
|
||||
private Integer point;
|
||||
private String ip;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
|
@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@ -24,6 +26,7 @@ public class UserInfo implements Serializable {
|
||||
* 用户id
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
@NotNull
|
||||
private Integer id;
|
||||
/**
|
||||
* 微信openid
|
||||
@ -56,6 +59,7 @@ public class UserInfo implements Serializable {
|
||||
/**
|
||||
* 第一次登录时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date firstLoginTime;
|
||||
private Date updateTime;
|
||||
/**
|
||||
|
@ -0,0 +1,16 @@
|
||||
package me.xiaoyan.point.api.pojo.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FileUploadResult implements Serializable {
|
||||
private String url;
|
||||
private String name;
|
||||
}
|
@ -2,7 +2,8 @@ package me.xiaoyan.point.api.pojo.dto;
|
||||
|
||||
public class OrderStatus {
|
||||
public static final int DELETE = 0;
|
||||
public static final int CANCEL = 1;
|
||||
public static final int NOT_CONFIRM = 1;
|
||||
public static final int CONFIRM = 2;
|
||||
public static final int DONE = 3;
|
||||
public static final int CANCEL = 3;
|
||||
public static final int FINISH = 4;
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ import lombok.Data;
|
||||
@Data
|
||||
public class GoodsQueryParam extends PageParam {
|
||||
private String title;
|
||||
private int category;
|
||||
private Integer category;
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package me.xiaoyan.point.api.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderQueryParam extends PageParam {
|
||||
private String id;
|
||||
private String title;
|
||||
private String createTimeStart;
|
||||
private String createTimeEnd;
|
||||
private int uid;
|
||||
private Integer status;
|
||||
}
|
@ -13,10 +13,10 @@ import lombok.experimental.Accessors;
|
||||
@Accessors(chain = true)
|
||||
@Builder
|
||||
public class PageParam {
|
||||
private Integer page;
|
||||
private Integer page = 1;
|
||||
private Integer pageSize = 20;
|
||||
|
||||
public Page getPage(){
|
||||
public Page getPage() {
|
||||
return new Page().setCurrent(page).setSize(pageSize);
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,5 @@ public interface GoodsService extends IService<Goods> {
|
||||
boolean deductStock(int id,int count);
|
||||
|
||||
List<Goods> queryAllGoodsIdAndStock();
|
||||
Goods queryByTitle(String title);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package me.xiaoyan.point.api.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import me.xiaoyan.point.api.pojo.OrderInfo;
|
||||
import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
|
||||
import me.xiaoyan.point.api.pojo.vo.OrderQueryParam;
|
||||
|
||||
/**
|
||||
* 订单表(OrderInfo)表服务接口
|
||||
@ -13,5 +15,9 @@ import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
|
||||
public interface OrderInfoService extends IService<OrderInfo> {
|
||||
|
||||
OrderInfo create(int uid, CreateOrderData data);
|
||||
|
||||
Page queryByPage(OrderQueryParam param,boolean queryGoods,boolean queryUser);
|
||||
|
||||
OrderInfo getOneByIdAndUid(String id, int uid);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import me.xiaoyan.point.api.pojo.Goods;
|
||||
import me.xiaoyan.point.api.pojo.vo.GoodsQueryParam;
|
||||
import me.xiaoyan.point.api.service.GoodsService;
|
||||
import me.xiaoyan.point.api.mapper.GoodsMapper;
|
||||
import me.xiaoyan.point.api.util.DataStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -31,9 +32,15 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods>
|
||||
if (StringUtils.hasText(param.getTitle())) {
|
||||
q.eq("title", param.getTitle().trim());
|
||||
}
|
||||
if (param.getCategory() != null) {
|
||||
q.eq("category", param.getCategory());
|
||||
}
|
||||
// 不查询删除状态数据
|
||||
q.ne("status", DataStatus.DELETE);
|
||||
return getBaseMapper().selectPage(param.getPage(), q);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean deductStock(int id, int count) {
|
||||
return getBaseMapper().deductCount(id, count) == 1;
|
||||
@ -43,6 +50,16 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods>
|
||||
public List<Goods> queryAllGoodsIdAndStock() {
|
||||
return getBaseMapper().queryAllGoodsIdAndStock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Goods queryByTitle(String title) {
|
||||
if (!StringUtils.hasText(title)) {
|
||||
return null;
|
||||
}
|
||||
QueryWrapper q = new QueryWrapper();
|
||||
q.eq("title", title);
|
||||
return getOne(q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.xiaoyan.point.api.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import me.xiaoyan.point.api.error.BizException;
|
||||
import me.xiaoyan.point.api.mapper.OrderInfoMapper;
|
||||
@ -9,8 +10,11 @@ import me.xiaoyan.point.api.pojo.OrderInfo;
|
||||
import me.xiaoyan.point.api.pojo.UserInfo;
|
||||
import me.xiaoyan.point.api.pojo.dto.OrderStatus;
|
||||
import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
|
||||
import me.xiaoyan.point.api.pojo.vo.OrderQueryParam;
|
||||
import me.xiaoyan.point.api.service.*;
|
||||
import me.xiaoyan.point.api.util.DataStatus;
|
||||
import me.xiaoyan.point.api.util.OrderIdGenerator;
|
||||
import me.xiaoyan.point.api.util.QueryWrapperUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -68,9 +72,11 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
|
||||
.id(OrderIdGenerator.next())
|
||||
.gid((long) data.getGoodsId())
|
||||
.uid(uid)
|
||||
.orderTitle(goods.getTitle())
|
||||
.count(data.getBuyCount())
|
||||
.price(goods.getPrice())
|
||||
.status(OrderStatus.CONFIRM)
|
||||
// 默认为待确认
|
||||
.status(OrderStatus.NOT_CONFIRM)
|
||||
.build();
|
||||
if (save(orderInfo)) {
|
||||
return orderInfo;
|
||||
@ -78,6 +84,47 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
|
||||
throw BizException.create("创建订单失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page queryByPage(OrderQueryParam param, boolean queryGoods, boolean queryUser) {
|
||||
|
||||
final QueryWrapper q = QueryWrapperUtil.builder()
|
||||
.eq("id", param.getId())
|
||||
.eq("order_title", param.getTitle())
|
||||
.ge("create_time", param.getCreateTimeStart())
|
||||
.le("create_time", param.getCreateTimeEnd())
|
||||
.ne("status", DataStatus.DELETE)
|
||||
.build();
|
||||
// 根据状态筛选
|
||||
if (param.getStatus() != null && param.getStatus() > 0) {
|
||||
q.eq("status", param.getStatus());
|
||||
}
|
||||
q.orderByDesc("create_time");
|
||||
final Page<OrderInfo> page = getBaseMapper().selectPage(param.getPage(), q);
|
||||
page.getRecords().forEach(o -> {
|
||||
if (queryUser) {
|
||||
// 查询订单的归属用户
|
||||
o.setOwner(userInfoService.getById(o.getUid()));
|
||||
}
|
||||
if (queryGoods) {
|
||||
// 查询订单商品信息
|
||||
o.setGoods(goodsService.getById(o.getGid()));
|
||||
}
|
||||
});
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderInfo getOneByIdAndUid(String id, int uid) {
|
||||
QueryWrapper q = new QueryWrapper();
|
||||
q.eq("uid", uid);
|
||||
q.eq("id", id);
|
||||
final OrderInfo o = getOne(q);
|
||||
if(null != o){
|
||||
o.setGoods(goodsService.getById(o.getGid()));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
public long buyHistoryCount(int uid, int gid) {
|
||||
QueryWrapper q = new QueryWrapper();
|
||||
q.eq("uid", uid);
|
||||
|
@ -2,6 +2,7 @@ package me.xiaoyan.point.api.util;
|
||||
|
||||
public class DataStatus {
|
||||
public static final int NORMAL = 1;
|
||||
public static final int DISABLED = 2;
|
||||
public static final int DELETE = 0;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package me.xiaoyan.point.api.util;
|
||||
|
||||
public class OrderStatus {
|
||||
public static final int NOT_CONFIRM = 1;
|
||||
public static final int CONFIRMED = 2;
|
||||
public static final int CANCELED = 3;
|
||||
public static final int FINISH = 4;
|
||||
public static final int DELETE = 0;
|
||||
}
|
@ -24,12 +24,30 @@ public class QueryWrapperUtil {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public QueryWrapperUtil eq(String column, int value) {
|
||||
q.eq(column, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperUtil ne(String column, Object value) {
|
||||
q.ne(column, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperUtil le(String column, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
q.le(column, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperUtil ge(String column, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
q.ge(column, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper build() {
|
||||
return this.q;
|
||||
}
|
||||
|
@ -48,25 +48,25 @@ create table sign_record
|
||||
create_time datetime default current_timestamp
|
||||
) engine = innodb
|
||||
collate = 'utf8mb4_general_ci' comment '打卡记录表';
|
||||
drop table if exists goods;
|
||||
drop table if exists goods;
|
||||
create table goods
|
||||
(
|
||||
id bigint(15) primary key auto_increment,
|
||||
category tinyint(2) null default 1 comment '商品类别(1:普通 2:精选 3:秒杀 4:抽奖)',
|
||||
type tinyint(2) null default 1 comment '商品类型(1:实物 2:虚拟)',
|
||||
category tinyint(2) null default 1 comment '商品类别(1:普通 2:精选 3:秒杀 4:抽奖)',
|
||||
type tinyint(2) null default 1 comment '商品类型(1:实物 2:虚拟)',
|
||||
title varchar(50) not null,
|
||||
origin_price int(10) unsigned comment '原价' default 0,
|
||||
price int(10) unsigned not null comment '价格',
|
||||
stock int(10) unsigned not null comment '库存数量',
|
||||
limit_count int(10) unsigned null default 1 comment '购买最大数量(0表示不限制)',
|
||||
limit_count int(10) unsigned null default 1 comment '购买最大数量(0表示不限制)',
|
||||
cover varchar(200) not null comment '商品图',
|
||||
description text not null comment '描述',
|
||||
notice varchar(500) null comment '提示',
|
||||
online_time datetime not null comment '上架时间',
|
||||
offline_time datetime not null comment '下架时间',
|
||||
create_time datetime default current_timestamp,
|
||||
create_time datetime default current_timestamp,
|
||||
update_time datetime null on update current_timestamp,
|
||||
status tinyint(2) default 1,
|
||||
status tinyint(2) default 1,
|
||||
index ix_title (title)
|
||||
) engine = innodb
|
||||
collate = 'utf8mb4_general_ci' comment '商品表';
|
||||
@ -75,6 +75,7 @@ create table order_info
|
||||
(
|
||||
id varchar(50) not null comment '订单编号',
|
||||
gid bigint(15) not null comment '商品编号',
|
||||
order_title varchar(50) null comment '订单名称',
|
||||
price int(10) not null comment '价格',
|
||||
uid int(10) not null comment '用户编号',
|
||||
data json null comment '订单数据',
|
||||
|
@ -45,6 +45,7 @@
|
||||
and stock > 0
|
||||
and category = #{category}
|
||||
and status != 0
|
||||
order by online_time desc,id desc
|
||||
</select>
|
||||
<select id="queryAllGoodsIdAndStock" resultType="me.xiaoyan.point.api.pojo.Goods">
|
||||
select id,stock
|
||||
|
@ -1,4 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="me.xiaoyan.point.api.mapper.OrderInfoMapper">
|
||||
<resultMap id="orderInfoMap" type="me.xiaoyan.point.api.pojo.OrderInfo">
|
||||
<association property="owner"/>
|
||||
</resultMap>
|
||||
<select id="selectByPage" resultMap="orderInfoMap"></select>
|
||||
</mapper>
|
Loading…
x
Reference in New Issue
Block a user