This commit is contained in:
LittleBoy 2023-02-07 11:33:16 +08:00
commit 1387938b75
57 changed files with 1932 additions and 243 deletions

View File

@ -20,9 +20,10 @@
"@types/node": "^18.11.10", "@types/node": "^18.11.10",
"@vitejs/plugin-vue": "^3.2.0", "@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.1", "@vitejs/plugin-vue-jsx": "^2.1.1",
"typescript": "^4.9.3",
"vite": "^3.2.4",
"less": "^4.1.3", "less": "^4.1.3",
"typescript": "^4.9.3",
"unplugin-vue-define-options": "^1.0.0",
"vite": "^3.2.4",
"vue-tsc": "^1.0.11" "vue-tsc": "^1.0.11"
} }
} }

View File

@ -38,6 +38,8 @@
--info-color: #1890ff; --info-color: #1890ff;
--info-color-deprecated-bg: #e6f7ff; --info-color-deprecated-bg: #e6f7ff;
--info-color-deprecated-border: #91d5ff; --info-color-deprecated-border: #91d5ff;
--placeholder-color: #ccc;
--border-color: #d9d9d9;
--primary-color-text: #333; --primary-color-text: #333;
--font-size: 14px; --font-size: 14px;
@ -48,7 +50,8 @@
--border-radius-middle: calc(var(--border-radius) + 2px); --border-radius-middle: calc(var(--border-radius) + 2px);
--border-radius-large: calc(var(--border-radius) + 3px); --border-radius-large: calc(var(--border-radius) + 3px);
--header-height: 80px; --header-bg: #001529;
--header-height: 50px;
--left-menu-width: 200px; --left-menu-width: 200px;
} }
@ -119,13 +122,55 @@ body {
.pointer-cursor { .pointer-cursor {
cursor: pointer; cursor: pointer;
} }
table{
.table-wrapper {
.table-toolbar{
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
table {
width: 100%; width: 100%;
border-left: solid 1px #eee; border: solid 1px #eee;
border-top: solid 1px #eee; border-collapse: collapse;
} font-size: 13px;
td,th { }
border-right: solid 1px #eee; th{
background-color: #f5f7fa;
}
td, th {
text-align: left;
border-bottom: solid 1px #eee; border-bottom: solid 1px #eee;
padding:2px 4px; padding: 6px 8px;
}
tr{
&:hover{
background-color: #f5f7fa;
}
}
}
.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;
} }

View 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>

View 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>

View 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>

View File

@ -24,12 +24,12 @@ import Eye from "../icon/Eye.vue";
import EyeClose from "../icon/EyeClose.vue"; import EyeClose from "../icon/EyeClose.vue";
interface PInputEvents { interface PInputEvents {
'update:modelValue': (value: string) => void 'update:modelValue': (value: any) => void
} }
type PInputProps = { type PInputProps = {
placeholder?: string placeholder?: string
modelValue?: string modelValue?: any
type?: 'text' | 'password' | 'textarea' type?: 'text' | 'password' | 'textarea'
} }
@ -57,6 +57,8 @@ const value = computed({
</script> </script>
<style lang="less"> <style lang="less">
@height: 30px;
.p-input-box-focus { .p-input-box-focus {
border-color: #2a7dc9; border-color: #2a7dc9;
@ -72,7 +74,7 @@ const value = computed({
display: inline-block; display: inline-block;
width: 100%; width: 100%;
min-width: 0; min-width: 0;
padding: 4px 11px; padding: 0px 11px;
color: #000000d9; color: #000000d9;
font-size: 14px; font-size: 14px;
line-height: 1.5715; line-height: 1.5715;
@ -81,6 +83,7 @@ const value = computed({
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
border-radius: 2px; border-radius: 2px;
transition: all .3s; transition: all .3s;
height: @height;
&::-moz-placeholder { &::-moz-placeholder {
opacity: 1 opacity: 1
@ -125,6 +128,7 @@ const value = computed({
.p-input-wrapper { .p-input-wrapper {
display: inline-block; display: inline-block;
width: 100%;
} }
.p-input-wrapper-group { .p-input-wrapper-group {
@ -132,7 +136,7 @@ const value = computed({
position: relative; position: relative;
max-width: 100%; max-width: 100%;
min-width: 0; min-width: 0;
padding: 4px 6px; padding: 0px 6px;
color: #000000d9; color: #000000d9;
font-size: 14px; font-size: 14px;
line-height: 1.5715; line-height: 1.5715;
@ -141,6 +145,7 @@ const value = computed({
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
border-radius: var(--border-radius); border-radius: var(--border-radius);
transition: all .3s; transition: all .3s;
height: @height;
&:hover { &:hover {
border-color: #2a7dc9; border-color: #2a7dc9;
@ -152,6 +157,8 @@ const value = computed({
border: none; border: none;
outline: none; outline: none;
margin: 0 6px; margin: 0 6px;
height: 28px;
&:focus { &:focus {
box-shadow: none; box-shadow: none;

View File

@ -1,7 +1,9 @@
<template> <template>
<div class="modal" v-if="modalVisible"> <div class="modal" v-if="modalVisible">
<div class="modal-mask"></div> <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> <div class="modal-close" @click="modalVisible = false">关闭</div>
<slot/> <slot/>
</div> </div>
@ -12,8 +14,12 @@
import {computed} from "vue"; import {computed} from "vue";
export default { export default {
name: "modal", name: "Modal",
props: { props: {
width: {
type: String,
default: '500px'
},
modelValue: { modelValue: {
type: Boolean, type: Boolean,
require: true require: true

View 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>

View File

@ -0,0 +1,4 @@
import PagerComponent from './Pager.vue'
const Pager = PagerComponent
export default Pager

View 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>

View 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>

View 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>

View 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

View File

@ -4,8 +4,9 @@ import {createPinia} from 'pinia'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import {httpConfig} from "./util/http"; import {httpConfig} from "./util/http";
import './assets/app.css' import './assets/app.less'
import PInput from "./components/input"; import PInput from "./components/input";
import clickOutside from "./directive/click_outside";
httpConfig.baseURL = "http://localhost:8080" httpConfig.baseURL = "http://localhost:8080"
@ -14,5 +15,6 @@ const app = createApp(App)
app.use(router) app.use(router)
app.use(createPinia()); app.use(createPinia());
app.component('PInput',PInput) app.component('PInput',PInput)
app.directive('click-outside',clickOutside)
// 将应用实例挂载到 模板中 // 将应用实例挂载到 模板中
app.mount('#vue-root-app') app.mount('#vue-root-app')

View File

@ -2,24 +2,61 @@ import {RouteRecordRaw} from "vue-router";
import Home from '../views/admin/Home.vue' import Home from '../views/admin/Home.vue'
import Login from '../views/Login.vue' import Login from '../views/Login.vue'
import NotFound from '../views/NotFound.vue' import NotFound from '../views/NotFound.vue'
import Test from '../views/Test.vue'
import AdminLayout from '../views/layout/AdminLayout.vue' import AdminLayout from '../views/layout/AdminLayout.vue'
import UserIndex from '../views/user/index.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[] = [ const routes: RouteRecordRaw[] = [
{ {
path: '/', path: '/',
component: AdminLayout, component: AdminLayout,
redirect: '/home', redirect: '/home',
children: [ children: AdminRoutes
{
path: 'home',
component: Home
},
{
path: 'user',
component: UserIndex
}
]
}, },
{ {
path: '/login', path: '/login',

View File

@ -0,0 +1,3 @@
export const UPDATE_MODEL_EVENT = 'update:modelValue'
export const CHANGE_EVENT = 'change'
export const INPUT_EVENT = 'input'

View File

@ -52,7 +52,6 @@ export const useUserStore = defineStore('user-store', () => {
const data = await http.get<AdminLoginModel>('/admin/user/info') const data = await http.get<AdminLoginModel>('/admin/user/info')
localStorage.removeItem(TOKEN_KEY) localStorage.removeItem(TOKEN_KEY)
userinfo.value = null userinfo.value = null
} }
return {userinfo, login, logout, updateInfo, token} return {userinfo, login, logout, updateInfo, token}

View File

@ -27,3 +27,34 @@ type UserInfoModel = {
pointInfo: PointInfoModel; pointInfo: PointInfoModel;
parent?: UserInfoModel | null; 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;
}

View File

@ -1,12 +1,13 @@
import {toast} from "../components/message"; import {toast} from "../components/message";
import {useUserStore} from "../service/store"; import {useUserStore} from "../service/store";
import router from '../router'
export type HttpMethod = 'get' | 'post' | 'delete' | 'put' export type HttpMethod = 'get' | 'post' | 'delete' | 'put'
export enum RequestDataContentType { export enum RequestDataContentType {
JSON = 'application/json;charset=UTF-8', JSON = 'application/json;charset=UTF-8',
FORM = 'application/x-www-form-urlencoded;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 = { export const httpConfig = {
@ -41,6 +42,17 @@ class Http {
return this.request<T>(url, data, 'get') 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 * GET /xxxx?a=1&b=2
* NAME: value * NAME: value
@ -56,7 +68,9 @@ class Http {
*/ */
request<T>(url: string, data: any = null, method: HttpMethod = 'post', type: 'normal' | 'form' = 'normal') { request<T>(url: string, data: any = null, method: HttpMethod = 'post', type: 'normal' | 'form' = 'normal') {
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
let contentType = RequestDataContentType.FORM; const headers: any = {
'Content-Type': RequestDataContentType.FORM
}
if (data) { if (data) {
if (type === 'form') { if (type === 'form') {
const form = new FormData(); const form = new FormData();
@ -64,12 +78,13 @@ class Http {
form.append(key, data[key]); form.append(key, data[key]);
} }
data = form; // 将data有{} => formData data = form; // 将data有{} => formData
contentType = RequestDataContentType.FORM_DATA; // 由于使用formData // contentType = RequestDataContentType.FORM_DATA; // 由于使用formData
delete headers['Content-Type'];
method = 'post'; method = 'post';
} else if (method == 'post') { } else if (method == 'post' || method == 'put') {
// 将数据对象 转成json // 将数据对象 转成json
data = JSON.stringify(data); data = JSON.stringify(data);
contentType = RequestDataContentType.JSON; headers['Content-Type'] = RequestDataContentType.JSON;
} else { } else {
// 装对象转成 key=value&key=value // 装对象转成 key=value&key=value
const params = []; const params = [];
@ -80,10 +95,6 @@ class Http {
url += '?' + params.join('&') url += '?' + params.join('&')
} }
} }
const headers: any = {
'Content-Type': contentType
}
const token = useUserStore().token(); const token = useUserStore().token();
if (token) headers.token = token if (token) headers.token = token
fetch(httpConfig.baseURL + url, { fetch(httpConfig.baseURL + url, {
@ -99,9 +110,11 @@ class Http {
// 需要统一处理数据 // 需要统一处理数据
if (code === 403) { if (code === 403) {
toast("登录凭证无效或者已过期") toast("登录凭证无效或者已过期")
// if (r.fullPath != '/login') { // 通过router获取当前路由
// router.replace('/login'); const route = router.currentRoute;
// } if (route.fullPath != '/login') {
router.replace('/login');
}
return; return;
} else if (err) { } else if (err) {
toast(httpConfig.globalErrorHandler[code]) toast(httpConfig.globalErrorHandler[code])

View File

@ -63,7 +63,7 @@ p{
margin: 10px 0; margin: 10px 0;
} }
.login-wrapper { .login-wrapper {
width: 500px; width: 300px;
margin: 50px auto; margin: 50px auto;
} }
</style> </style>

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="demo-box"> <div class="demo-box">
<div>
<div class="form-item"> <div class="form-item">
<PInput placeholder="测试21" v-model="data.a"> <PInput placeholder="测试21" v-model="data.a">
<template #suffix> <template #suffix>
@ -17,16 +18,31 @@
{{ JSON.stringify(data) }} {{ JSON.stringify(data) }}
</div> </div>
</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> </template>
<script lang="ts" setup> <script lang="ts" setup>
import PInput from "../components/input"; 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({ const data = reactive({
a: '1', a: '1',
b: '2' b: '2'
}) })
const options = [
{value: 1, label: '普通商品'},
{value: 2, label: '精选商品'},
{value: 3, label: '秒杀商品'},
]
const select = ref()
</script> </script>
<style scoped> <style scoped>

View 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>

View 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>

View File

@ -6,14 +6,17 @@
<span> <span>
{{ userStore.userinfo?.account }} {{ userStore.userinfo?.account }}
</span> </span>
<span @click="logout">退出</span>
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div class="left-menu"> <div class="left-menu">
<div class="title">积分管理系统</div> <div class="title">积分管理系统</div>
<div class="menu-list"> <div class="menu-list">
<router-link active-class="active-menu" to="/home">HOME</router-link> <router-link v-for="r in AdminRoutes" active-class="active-menu" :to="'/' + r.path">{{
<router-link active-class="active-menu" to="/user">用户管理</router-link> r.meta.title
}}
</router-link>
</div> </div>
</div> </div>
<div class="content-wrapper"> <div class="content-wrapper">
@ -27,18 +30,26 @@
</template> </template>
<script setup> <script setup>
import {useUserStore} from "../../service/store"; import {useUserStore} from "../../service/store";
import {useRouter} from "vue-router";
import {AdminRoutes} from './../../router/routes'
const userStore = useUserStore(); const userStore = useUserStore();
const router = useRouter()
function logout() {
userStore.logout()
router.replace('/login')
}
</script> </script>
<style lang="less"> <style lang="less">
.app-admin-layout { .app-admin-layout {
min-height: 100vh; min-height: 100vh;
background-color: #eee; background-color: #eee;
} }
.header { .header {
background-color: var(--primary-color); background-color: var(--header-bg);
color: white; color: white;
line-height: var(--header-height); line-height: var(--header-height);
padding: 0 20px; padding: 0 20px;
@ -48,6 +59,7 @@ const userStore = useUserStore();
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
z-index: 9;
} }
.main { .main {
@ -56,34 +68,42 @@ const userStore = useUserStore();
.left-menu { .left-menu {
width: 200px; width: 200px;
background-color: black; background-color: #fff;
color: white; color:#333;
box-shadow: 0 2px 3px rgba(0,0,0,0.4);
position: fixed; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
z-index: 10;
font-size: 14px;
} }
.title{
.title {
height: var(--header-height); height: var(--header-height);
background-color: #333; background-color: var(--header-bg);
line-height: var(--header-height); line-height: var(--header-height);
text-align: center; text-align: center;
color:white;
font-size: var(--font-size-large); font-size: var(--font-size-large);
} }
.menu-list{
.menu-list {
margin-top: 20px; margin-top: 20px;
a{
a {
display: block; display: block;
text-decoration: none; text-decoration: none;
color:white; padding: 10px 20px;
padding:10px 20px; color:#333;
&.active-menu{
background-color: #666666; &.active-menu {
color: white; background-color: var(--primary-1);
color: var(--primary-color);
} }
} }
} }
.content-wrapper { .content-wrapper {
margin: 20px; margin: 20px;
background-color: #fff; background-color: #fff;

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="table-wrapper">
<table> <table>
<tr> <tr>
<th>UID</th> <th>UID</th>
@ -30,6 +31,8 @@
</td> </td>
</tr> </tr>
</table> </table>
<Pager :total="userTotalCount" show-refresh @refresh="loadUserList" @page-change="onPageChange"/>
</div>
<Modal v-model="modalVisible"> <Modal v-model="modalVisible">
<div> <div>
<div> <div>
@ -60,6 +63,7 @@ import {onMounted, reactive, ref} from "vue";
import http, {DataListModel} from "../../util/http"; import http, {DataListModel} from "../../util/http";
import message from "../../components/message"; import message from "../../components/message";
import Modal from "../../components/modal/modal.vue"; import Modal from "../../components/modal/modal.vue";
import Pager from "../../components/pager/Pager.vue";
// //
const GenderEnum = { const GenderEnum = {
1: '男', 2: '女' 1: '男', 2: '女'
@ -88,6 +92,11 @@ const editUserData = reactive({
}); });
const userTotalCount = ref(0) const userTotalCount = ref(0)
function onPageChange(currentPage: number) {
param.page = currentPage;
loadUserList();
}
function loadUserList() { function loadUserList() {
http.post<DataListModel<UserInfoModel>>('/admin/user/list', param).then(res => { http.post<DataListModel<UserInfoModel>>('/admin/user/list', param).then(res => {
userList.value = res.items userList.value = res.items

View File

@ -12,6 +12,15 @@
"**/*.js" "**/*.js"
], ],
"compilerOptions": { "compilerOptions": {
"jsx": "preserve" "jsx": "preserve",
"types": [
"unplugin-vue-define-options/macros-global"
],
"lib": [
"dom",
"es5",
"scripthost",
"es2015.collection"
]
} }
} }

View File

@ -1,14 +1,17 @@
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsxPlugin from "@vitejs/plugin-vue-jsx"; import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
import DefineOptions from 'unplugin-vue-define-options/vite'
export default { export default {
hmr: true,
// 开发服务信息 // 开发服务信息
server: { server: {
host:'::' host: '::'
}, },
// 必须配置vue插件 // 必须配置vue插件
plugins: [ plugins: [
vue(), vue(),
DefineOptions(),
vueJsxPlugin(), // 如果需要使用jsx或者tsx vueJsxPlugin(), // 如果需要使用jsx或者tsx
] ]
} }

View File

@ -200,7 +200,7 @@
chalk "^2.0.0" chalk "^2.0.0"
js-tokens "^4.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" version "7.20.5"
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
@ -253,7 +253,7 @@
debug "^4.1.0" debug "^4.1.0"
globals "^11.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" version "7.20.5"
resolved "https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" resolved "https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==
@ -312,6 +312,14 @@
"@jridgewell/resolve-uri" "3.1.0" "@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14" "@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": "@types/node@^18.11.10":
version "18.11.10" version "18.11.10"
resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34" resolved "https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34"
@ -376,6 +384,15 @@
"@volar/typescript" "1.0.11" "@volar/typescript" "1.0.11"
"@volar/vue-language-core" "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": "@vue/babel-helper-vue-transform-on@^1.0.2":
version "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" 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" resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2"
integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== 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: ansi-styles@^3.2.1:
version "3.2.1" version "3.2.1"
resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@ -498,11 +520,32 @@ ansi-styles@^3.2.1:
dependencies: dependencies:
color-convert "^1.9.0" 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: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 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: brace-expansion@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
@ -510,6 +553,13 @@ brace-expansion@^2.0.1:
dependencies: dependencies:
balanced-match "^1.0.0" 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: browserslist@^4.21.3:
version "4.21.4" version "4.21.4"
resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" 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" escape-string-regexp "^1.0.5"
supports-color "^5.3.0" 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: color-convert@^1.9.0:
version "1.9.3" version "1.9.3"
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 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" resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
estree-walker@^2.0.2: estree-walker@^2.0.1, estree-walker@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 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: fsevents@~2.3.2:
version "2.3.2" version "2.3.2"
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 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" resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== 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: globals@^11.1.0:
version "11.12.0" version "11.12.0"
resolved "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 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" resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== 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: is-core-module@^2.9.0:
version "2.11.0" version "2.11.0"
resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 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: dependencies:
has "^1.0.3" 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: is-what@^3.14.1:
version "3.14.1" version "3.14.1"
resolved "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" resolved "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
@ -857,6 +960,13 @@ magic-string@^0.25.7:
dependencies: dependencies:
sourcemap-codec "^1.4.8" 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: make-dir@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" 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" resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== 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: parse-node-version@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" 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" resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 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: pify@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" 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" resolved "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== 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: resolve@^1.22.1:
version "1.22.1" version "1.22.1"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 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" resolved "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== 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: tslib@^2.3.0:
version "2.4.1" version "2.4.1"
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" 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" resolved "https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== 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: update-browserslist-db@^1.0.9:
version "1.0.10" version "1.0.10"
resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" 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/runtime-dom" "3.2.45"
"@vue/server-renderer" "3.2.45" "@vue/server-renderer" "3.2.45"
"@vue/shared" "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==

View File

@ -11,7 +11,9 @@ public class ApplicationConfig implements WebMvcConfigurer {
// 跨域 目前小程序不需要暂时不用考虑 // 跨域 目前小程序不需要暂时不用考虑
@Override @Override
public void addCorsMappings(CorsRegistry registry) { public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*"); registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE");
} }

View File

@ -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, "");
}
}

View File

@ -1,13 +1,21 @@
package me.xiaoyan.point.api.controller; package me.xiaoyan.point.api.controller;
import cn.dev33.satoken.stp.StpUtil; 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 lombok.extern.slf4j.Slf4j;
import me.xiaoyan.point.api.error.BizException; import me.xiaoyan.point.api.error.BizException;
import me.xiaoyan.point.api.pojo.OrderInfo; 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.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.GoodsService;
import me.xiaoyan.point.api.service.OrderInfoService; 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.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -47,9 +55,9 @@ public class ShopOrderInfoController {
//TODO 应该定时更新缓存数据 //TODO 应该定时更新缓存数据
goodsService.queryAllGoodsIdAndStock().forEach(g -> { goodsService.queryAllGoodsIdAndStock().forEach(g -> {
log.info("缓存 id:{} stock:{} ", g.getId(), g.getStock()); 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()); stringRedisTemplate.opsForValue().set(cacheKey(g.getId()), g.getStock().toString());
@ -64,7 +72,7 @@ public class ShopOrderInfoController {
int uid = StpUtil.getLoginIdAsInt(); int uid = StpUtil.getLoginIdAsInt();
// 如果限制用户只能购买的数量可以添加一个map记录用户的请求数 // 如果限制用户只能购买的数量可以添加一个map记录用户的请求数
int size = stockOutMap.size(); int size = stockOutMap.size();
Long buyId = (long)data.getGoodsId(); Long buyId = (long) data.getGoodsId();
//1.内存判断 //1.内存判断
if (size > 0 && stockOutMap.containsKey(buyId) && stockOutMap.get(buyId)) { if (size > 0 && stockOutMap.containsKey(buyId) && stockOutMap.get(buyId)) {
throw BizException.create("库存不足"); throw BizException.create("库存不足");
@ -74,22 +82,59 @@ public class ShopOrderInfoController {
if (count < 0) { if (count < 0) {
log.info("stock count ===>" + count); log.info("stock count ===>" + count);
// 对缓存进行库存 还原 // 对缓存进行库存 还原
stringRedisTemplate.opsForValue().increment(cacheKey(buyId),data.getBuyCount()); // stringRedisTemplate.opsForValue().increment(cacheKey(buyId), data.getBuyCount()); //
throw BizException.create("库存不足"); throw BizException.create("库存不足");
} }
if(count == 0){ if (count == 0) {
// 此时库存没有了 , 保存到已买完的对象 // 此时库存没有了 , 保存到已买完的对象
stockOutMap.put(buyId,true); stockOutMap.put(buyId, true);
} }
//3.数据库 //3.数据库
try{ try {
return orderInfoService.create(uid, data); return orderInfoService.create(uid, data);
}catch (BizException e){ } catch (BizException e) {
//下单失败 对缓存进行库存 还原 //下单失败 对缓存进行库存 还原
stringRedisTemplate.opsForValue().increment(cacheKey(data.getGoodsId()),data.getBuyCount()); // stringRedisTemplate.opsForValue().increment(cacheKey(data.getGoodsId()), data.getBuyCount()); //
stockOutMap.put((long)data.getGoodsId(),false); stockOutMap.put((long) data.getGoodsId(), false);
throw e; 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);
}
} }

View File

@ -1,14 +1,15 @@
package me.xiaoyan.point.api.controller.admin; package me.xiaoyan.point.api.controller.admin;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 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.GoodsQueryParam;
import me.xiaoyan.point.api.pojo.vo.PageDataResult; import me.xiaoyan.point.api.pojo.vo.PageDataResult;
import me.xiaoyan.point.api.pojo.vo.PageParam; import me.xiaoyan.point.api.pojo.vo.PageParam;
import me.xiaoyan.point.api.service.GoodsService; import me.xiaoyan.point.api.service.GoodsService;
import org.springframework.web.bind.annotation.PostMapping; import me.xiaoyan.point.api.util.DataStatus;
import org.springframework.web.bind.annotation.RequestBody; import org.apache.ibatis.annotations.Delete;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -22,4 +23,43 @@ public class GoodsAdminController {
public PageDataResult list(@RequestBody GoodsQueryParam param) { public PageDataResult list(@RequestBody GoodsQueryParam param) {
return PageDataResult.convert(goodsService.queryByPage(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()
);
}
} }

View File

@ -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()
);
}
}

View File

@ -40,6 +40,12 @@ public class UserAdminController {
userStoreMap.put("test", UserAdminInfo.create(2, "test", "123123")); 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 * @status released
@ -50,7 +56,7 @@ public class UserAdminController {
@SneakyThrows @SneakyThrows
@PostMapping("login") @PostMapping("login")
public UserAdminInfo login(@Validated @RequestBody UserAdminInfo user) { public UserAdminInfo login(@Validated @RequestBody UserAdminInfo user) {
Thread.sleep(1); Thread.sleep(2000);
// 判断是否存在账号 // 判断是否存在账号
if (!userStoreMap.containsKey(user.getAccount())) { if (!userStoreMap.containsKey(user.getAccount())) {
throw BizException.create("账号不存在"); throw BizException.create("账号不存在");

View File

@ -17,7 +17,20 @@ public class BizException extends RuntimeException {
public static BizException create(String message) { public static BizException create(String message) {
return new BizException(-1, 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); 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("请求参数不正确");
}
} }

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -83,21 +84,25 @@ public class Goods implements Serializable {
/** /**
* 上架时间 * 上架时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date onlineTime; private Date onlineTime;
/** /**
* 下架时间 * 下架时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date offlineTime; private Date offlineTime;
/** /**
* *
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; private Date createTime;
/** /**
* *
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime; private Date updateTime;
/** /**

View File

@ -2,9 +2,11 @@ package me.xiaoyan.point.api.pojo;
import java.util.Date; import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model; import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -24,13 +26,14 @@ import java.io.Serializable;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder @Builder
@TableName(value ="order_info") @TableName(value = "order_info")
public class OrderInfo { public class OrderInfo {
@TableId @TableId
//订单编号 //订单编号
private String id; private String id;
//商品编号 //商品编号
private Long gid; private Long gid;
private String orderTitle;
//价格 //价格
private Integer price; private Integer price;
//购买数量 //购买数量
@ -40,11 +43,16 @@ public class OrderInfo {
//订单数据 //订单数据
private String data; private String data;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime; private Date updateTime;
//订单状态(0:已删除 1:已取消 2:待确认 3:已完成) //订单状态(0:已删除 1:已取消 2:待确认 3:已完成)
private Integer status; private Integer status;
@TableField(exist = false)
private UserInfo owner;
@TableField(exist = false)
private Goods goods;
} }

View File

@ -2,6 +2,7 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -22,6 +23,7 @@ public class Point implements Serializable {
private Integer totalPoint; private Integer totalPoint;
private Integer validPoint; private Integer validPoint;
private Integer expirePoint; private Integer expirePoint;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireTime; private Date expireTime;
private Date updateTime; private Date updateTime;
} }

View File

@ -3,6 +3,7 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -25,6 +26,8 @@ public class PointRecord implements Serializable {
private Integer point; private Integer point;
private Integer currentTotalPoint; private Integer currentTotalPoint;
private String reason; private String reason;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date validTime; private Date validTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date expireTime; private Date expireTime;
} }

View File

@ -2,6 +2,7 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -22,5 +23,7 @@ public class SignRecord implements Serializable {
private Integer uid; private Integer uid;
private Integer point; private Integer point;
private String ip; private String ip;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; private Date createTime;
} }

View File

@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -24,6 +26,7 @@ public class UserInfo implements Serializable {
* 用户id * 用户id
*/ */
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@NotNull
private Integer id; private Integer id;
/** /**
* 微信openid * 微信openid
@ -56,6 +59,7 @@ public class UserInfo implements Serializable {
/** /**
* 第一次登录时间 * 第一次登录时间
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date firstLoginTime; private Date firstLoginTime;
private Date updateTime; private Date updateTime;
/** /**

View File

@ -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;
}

View File

@ -2,7 +2,8 @@ package me.xiaoyan.point.api.pojo.dto;
public class OrderStatus { public class OrderStatus {
public static final int DELETE = 0; 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 CONFIRM = 2;
public static final int DONE = 3; public static final int CANCEL = 3;
public static final int FINISH = 4;
} }

View File

@ -5,5 +5,5 @@ import lombok.Data;
@Data @Data
public class GoodsQueryParam extends PageParam { public class GoodsQueryParam extends PageParam {
private String title; private String title;
private int category; private Integer category;
} }

View File

@ -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;
}

View File

@ -13,10 +13,10 @@ import lombok.experimental.Accessors;
@Accessors(chain = true) @Accessors(chain = true)
@Builder @Builder
public class PageParam { public class PageParam {
private Integer page; private Integer page = 1;
private Integer pageSize = 20; private Integer pageSize = 20;
public Page getPage(){ public Page getPage() {
return new Page().setCurrent(page).setSize(pageSize); return new Page().setCurrent(page).setSize(pageSize);
} }
} }

View File

@ -25,4 +25,5 @@ public interface GoodsService extends IService<Goods> {
boolean deductStock(int id,int count); boolean deductStock(int id,int count);
List<Goods> queryAllGoodsIdAndStock(); List<Goods> queryAllGoodsIdAndStock();
Goods queryByTitle(String title);
} }

View File

@ -1,8 +1,10 @@
package me.xiaoyan.point.api.service; package me.xiaoyan.point.api.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import me.xiaoyan.point.api.pojo.OrderInfo; import me.xiaoyan.point.api.pojo.OrderInfo;
import me.xiaoyan.point.api.pojo.vo.CreateOrderData; import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
import me.xiaoyan.point.api.pojo.vo.OrderQueryParam;
/** /**
* 订单表(OrderInfo)表服务接口 * 订单表(OrderInfo)表服务接口
@ -13,5 +15,9 @@ import me.xiaoyan.point.api.pojo.vo.CreateOrderData;
public interface OrderInfoService extends IService<OrderInfo> { public interface OrderInfoService extends IService<OrderInfo> {
OrderInfo create(int uid, CreateOrderData data); OrderInfo create(int uid, CreateOrderData data);
Page queryByPage(OrderQueryParam param,boolean queryGoods,boolean queryUser);
OrderInfo getOneByIdAndUid(String id, int uid);
} }

View File

@ -7,6 +7,7 @@ import me.xiaoyan.point.api.pojo.Goods;
import me.xiaoyan.point.api.pojo.vo.GoodsQueryParam; import me.xiaoyan.point.api.pojo.vo.GoodsQueryParam;
import me.xiaoyan.point.api.service.GoodsService; import me.xiaoyan.point.api.service.GoodsService;
import me.xiaoyan.point.api.mapper.GoodsMapper; import me.xiaoyan.point.api.mapper.GoodsMapper;
import me.xiaoyan.point.api.util.DataStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -31,9 +32,15 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods>
if (StringUtils.hasText(param.getTitle())) { if (StringUtils.hasText(param.getTitle())) {
q.eq("title", param.getTitle().trim()); 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); return getBaseMapper().selectPage(param.getPage(), q);
} }
@Override @Override
public boolean deductStock(int id, int count) { public boolean deductStock(int id, int count) {
return getBaseMapper().deductCount(id, count) == 1; return getBaseMapper().deductCount(id, count) == 1;
@ -43,6 +50,16 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods>
public List<Goods> queryAllGoodsIdAndStock() { public List<Goods> queryAllGoodsIdAndStock() {
return getBaseMapper().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);
}
} }

View File

@ -1,6 +1,7 @@
package me.xiaoyan.point.api.service.impl; package me.xiaoyan.point.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 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 com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.xiaoyan.point.api.error.BizException; import me.xiaoyan.point.api.error.BizException;
import me.xiaoyan.point.api.mapper.OrderInfoMapper; 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.UserInfo;
import me.xiaoyan.point.api.pojo.dto.OrderStatus; import me.xiaoyan.point.api.pojo.dto.OrderStatus;
import me.xiaoyan.point.api.pojo.vo.CreateOrderData; 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.service.*;
import me.xiaoyan.point.api.util.DataStatus;
import me.xiaoyan.point.api.util.OrderIdGenerator; import me.xiaoyan.point.api.util.OrderIdGenerator;
import me.xiaoyan.point.api.util.QueryWrapperUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -68,9 +72,11 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
.id(OrderIdGenerator.next()) .id(OrderIdGenerator.next())
.gid((long) data.getGoodsId()) .gid((long) data.getGoodsId())
.uid(uid) .uid(uid)
.orderTitle(goods.getTitle())
.count(data.getBuyCount()) .count(data.getBuyCount())
.price(goods.getPrice()) .price(goods.getPrice())
.status(OrderStatus.CONFIRM) // 默认为待确认
.status(OrderStatus.NOT_CONFIRM)
.build(); .build();
if (save(orderInfo)) { if (save(orderInfo)) {
return orderInfo; return orderInfo;
@ -78,6 +84,47 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
throw BizException.create("创建订单失败"); 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) { public long buyHistoryCount(int uid, int gid) {
QueryWrapper q = new QueryWrapper(); QueryWrapper q = new QueryWrapper();
q.eq("uid", uid); q.eq("uid", uid);

View File

@ -2,6 +2,7 @@ package me.xiaoyan.point.api.util;
public class DataStatus { public class DataStatus {
public static final int NORMAL = 1; public static final int NORMAL = 1;
public static final int DISABLED = 2;
public static final int DELETE = 0; public static final int DELETE = 0;
} }

View File

@ -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;
}

View File

@ -24,12 +24,30 @@ public class QueryWrapperUtil {
} }
return this; return this;
} }
public QueryWrapperUtil eq(String column, int value) {
q.eq(column, value);
return this;
}
public QueryWrapperUtil ne(String column, Object value) { public QueryWrapperUtil ne(String column, Object value) {
q.ne(column, value); q.ne(column, value);
return this; 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() { public QueryWrapper build() {
return this.q; return this.q;
} }

View File

@ -75,6 +75,7 @@ create table order_info
( (
id varchar(50) not null comment '订单编号', id varchar(50) not null comment '订单编号',
gid bigint(15) not null comment '商品编号', gid bigint(15) not null comment '商品编号',
order_title varchar(50) null comment '订单名称',
price int(10) not null comment '价格', price int(10) not null comment '价格',
uid int(10) not null comment '用户编号', uid int(10) not null comment '用户编号',
data json null comment '订单数据', data json null comment '订单数据',

View File

@ -45,6 +45,7 @@
and stock > 0 and stock > 0
and category = #{category} and category = #{category}
and status != 0 and status != 0
order by online_time desc,id desc
</select> </select>
<select id="queryAllGoodsIdAndStock" resultType="me.xiaoyan.point.api.pojo.Goods"> <select id="queryAllGoodsIdAndStock" resultType="me.xiaoyan.point.api.pojo.Goods">
select id,stock select id,stock

View File

@ -1,4 +1,8 @@
<?xml version="1.0" encoding="UTF-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" > <!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"> <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> </mapper>