+
@@ -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
diff --git a/admin-fe/src/components/pager/Pager.vue b/admin-fe/src/components/pager/Pager.vue
new file mode 100644
index 0000000..df9f908
--- /dev/null
+++ b/admin-fe/src/components/pager/Pager.vue
@@ -0,0 +1,79 @@
+
+
+ 刷新
+ 上一页
+ 1
+
+ {{
+ index
+ }}
+
+ {{
+ totalPage
+ }}
+ 下一页
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/components/pager/index.ts b/admin-fe/src/components/pager/index.ts
new file mode 100644
index 0000000..5518c80
--- /dev/null
+++ b/admin-fe/src/components/pager/index.ts
@@ -0,0 +1,4 @@
+import PagerComponent from './Pager.vue'
+
+const Pager = PagerComponent
+export default Pager
\ No newline at end of file
diff --git a/admin-fe/src/components/select/Option.vue b/admin-fe/src/components/select/Option.vue
new file mode 100644
index 0000000..df3f4f7
--- /dev/null
+++ b/admin-fe/src/components/select/Option.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/components/select/Select.vue b/admin-fe/src/components/select/Select.vue
new file mode 100644
index 0000000..b3c878f
--- /dev/null
+++ b/admin-fe/src/components/select/Select.vue
@@ -0,0 +1,159 @@
+
+
+
+
{{ selectValue }}{{ placeholder }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/components/uploader/index.ts b/admin-fe/src/components/uploader/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/admin-fe/src/components/uploader/uploader.vue b/admin-fe/src/components/uploader/uploader.vue
new file mode 100644
index 0000000..e916575
--- /dev/null
+++ b/admin-fe/src/components/uploader/uploader.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/directive/click_outside.ts b/admin-fe/src/directive/click_outside.ts
new file mode 100644
index 0000000..54f520b
--- /dev/null
+++ b/admin-fe/src/directive/click_outside.ts
@@ -0,0 +1,60 @@
+import type {
+ ObjectDirective,
+} from 'vue'
+
+const nodeList = new Map
();
+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
\ No newline at end of file
diff --git a/admin-fe/src/main.ts b/admin-fe/src/main.ts
index 0eced11..563266c 100644
--- a/admin-fe/src/main.ts
+++ b/admin-fe/src/main.ts
@@ -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')
\ No newline at end of file
diff --git a/admin-fe/src/router/routes.ts b/admin-fe/src/router/routes.ts
index dc51321..afe5a02 100644
--- a/admin-fe/src/router/routes.ts
+++ b/admin-fe/src/router/routes.ts
@@ -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',
diff --git a/admin-fe/src/service/constants.ts b/admin-fe/src/service/constants.ts
new file mode 100644
index 0000000..a07869e
--- /dev/null
+++ b/admin-fe/src/service/constants.ts
@@ -0,0 +1,3 @@
+export const UPDATE_MODEL_EVENT = 'update:modelValue'
+export const CHANGE_EVENT = 'change'
+export const INPUT_EVENT = 'input'
\ No newline at end of file
diff --git a/admin-fe/src/service/store.ts b/admin-fe/src/service/store.ts
index 5499002..c4380dc 100644
--- a/admin-fe/src/service/store.ts
+++ b/admin-fe/src/service/store.ts
@@ -52,7 +52,6 @@ export const useUserStore = defineStore('user-store', () => {
const data = await http.get('/admin/user/info')
localStorage.removeItem(TOKEN_KEY)
userinfo.value = null
-
}
return {userinfo, login, logout, updateInfo, token}
diff --git a/admin-fe/src/service/types.d.ts b/admin-fe/src/service/types.d.ts
index 7db2801..4f289db 100644
--- a/admin-fe/src/service/types.d.ts
+++ b/admin-fe/src/service/types.d.ts
@@ -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;
}
\ No newline at end of file
diff --git a/admin-fe/src/util/http.ts b/admin-fe/src/util/http.ts
index 88f6ea6..4a391e3 100644
--- a/admin-fe/src/util/http.ts
+++ b/admin-fe/src/util/http.ts
@@ -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(url, data, 'get')
}
+ put(url, data = null) {
+ return this.request(url, data, 'put')
+ }
+
+ remove(url, data = null) {
+ return this.request(url, data, 'delete')
+ }
+ delete(url, data = null) {
+ return this.request(url, data, 'delete')
+ }
+
/**
* GET /xxxx?a=1&b=2
* NAME: value
@@ -56,7 +68,9 @@ class Http {
*/
request(url: string, data: any = null, method: HttpMethod = 'post', type: 'normal' | 'form' = 'normal') {
return new Promise((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])
diff --git a/admin-fe/src/views/Login.vue b/admin-fe/src/views/Login.vue
index 86f2c32..04c98c1 100644
--- a/admin-fe/src/views/Login.vue
+++ b/admin-fe/src/views/Login.vue
@@ -63,7 +63,7 @@ p{
margin: 10px 0;
}
.login-wrapper {
- width: 500px;
+ width: 300px;
margin: 50px auto;
}
\ No newline at end of file
diff --git a/admin-fe/src/views/Test.vue b/admin-fe/src/views/Test.vue
index 1691f88..197ce61 100644
--- a/admin-fe/src/views/Test.vue
+++ b/admin-fe/src/views/Test.vue
@@ -1,43 +1,59 @@
-
-
-
-
-
- {{ JSON.stringify(data) }}
-
+
+
+
+
+
+
+ {{ JSON.stringify(data) }}
+
+
+ 商品类型
+
+ {{ op.label }}
+
+ {{ JSON.stringify(select) }}
+
\ No newline at end of file
diff --git a/admin-fe/src/views/goods/Order.vue b/admin-fe/src/views/goods/Order.vue
new file mode 100644
index 0000000..5d8e8ca
--- /dev/null
+++ b/admin-fe/src/views/goods/Order.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+ 订单编号
+ 标题
+ 价格
+ 数量
+ 状态
+ 用户
+ 创建时间
+ 操作
+
+
+ {{ it.id }}
+ {{ it.orderTitle }}
+ {{ it.price }}
+ {{ it.count }}
+ {{ StatusEnum[it.status] || '未知' }}
+ {{ it.owner?.nickname }}
+ {{ it.createTime }}
+
+ 确认
+ 取消
+ 完成
+ 删除
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/views/goods/index.vue b/admin-fe/src/views/goods/index.vue
new file mode 100644
index 0000000..e915667
--- /dev/null
+++ b/admin-fe/src/views/goods/index.vue
@@ -0,0 +1,296 @@
+
+
+
+
+
+
+ 商品编号
+ 标题
+ 图片
+ 分类
+ 类型
+ 原价
+ 售卖价
+ 库存
+ 限购数量
+ 上架时间
+ 下架时间
+ 状态
+ 操作
+
+
+ {{ it.id }}
+ {{ it.title }}
+
+ {{ CategoryEnum[it.category] || '未知' }}
+ {{ TypeEnum[it.type] || '未知' }}
+ {{ it.originPrice == 0 ? '-' : it.originPrice }}
+ {{ it.price }}
+ {{ it.stock }}
+ {{ it.limitCount }}
+ {{ it.onlineTime }}
+ {{ it.offlineTime }}
+ {{ StatusEnum[it.status] || '未知' }}
+
+ 编辑
+ 禁用
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
分类
+
+
+ {{ text }}
+
+
+
+
+
类型
+
+
+ {{ text }}
+
+
+
+
+
+
商品图片
+
+
+
+
+
+ 选择商品图
+
+
+
+
+
+
+
+
+
+
提交
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin-fe/src/views/layout/AdminLayout.vue b/admin-fe/src/views/layout/AdminLayout.vue
index 196bebc..2c1ec0e 100644
--- a/admin-fe/src/views/layout/AdminLayout.vue
+++ b/admin-fe/src/views/layout/AdminLayout.vue
@@ -6,14 +6,17 @@
{{ userStore.userinfo?.account }}
+
退出