diff --git a/admin-fe/src/assets/app.css b/admin-fe/src/assets/app.less similarity index 78% rename from admin-fe/src/assets/app.css rename to admin-fe/src/assets/app.less index 7e78338..17da478 100644 --- a/admin-fe/src/assets/app.css +++ b/admin-fe/src/assets/app.less @@ -119,14 +119,46 @@ body { .pointer-cursor { cursor: pointer; } -table{ - width: 100%; - border-left: solid 1px #eee; - border-top: solid 1px #eee; - border-collapse: collapse; + +.table-wrapper { + .table-toolbar{ + display: flex; + justify-content: space-between; + margin-bottom: 20px; + } + table { + width: 100%; + border-left: solid 1px #eee; + border-top: solid 1px #eee; + border-collapse: collapse; + } + + td, th { + border-right: solid 1px #eee; + border-bottom: solid 1px #eee; + padding: 6px; + } } -td,th { - border-right: solid 1px #eee; - border-bottom: solid 1px #eee; - padding:6px; + +.page-wrapper { + margin: 20px 0; + + .page-item { + cursor: pointer; + border: solid 1px var(--primary-2); + border-radius: var(--border-radius-middle); + display: inline-block; + padding: 4px 10px; + margin: 0 5px; + min-width: 40px; + text-align: center; + + &:hover, &.current-page { + border-color: var(--primary-color); + color: var(--primary-color) + } + } +} +.display-flex{ + display: flex; } \ No newline at end of file diff --git a/admin-fe/src/components/input/input.vue b/admin-fe/src/components/input/input.vue index 1c60bda..a21b59a 100644 --- a/admin-fe/src/components/input/input.vue +++ b/admin-fe/src/components/input/input.vue @@ -24,12 +24,12 @@ import Eye from "../icon/Eye.vue"; import EyeClose from "../icon/EyeClose.vue"; interface PInputEvents { - 'update:modelValue': (value: string) => void + 'update:modelValue': (value: any) => void } type PInputProps = { placeholder?: string - modelValue?: string + modelValue?: any type?: 'text' | 'password' | 'textarea' } diff --git a/admin-fe/src/components/pager/Pager.vue b/admin-fe/src/components/pager/Pager.vue index a69b55a..0eef26b 100644 --- a/admin-fe/src/components/pager/Pager.vue +++ b/admin-fe/src/components/pager/Pager.vue @@ -76,26 +76,4 @@ export default defineComponent({ } } }) - - - \ No newline at end of file + \ 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..fdb2560 --- /dev/null +++ b/admin-fe/src/components/uploader/uploader.vue @@ -0,0 +1,62 @@ + + + + + + \ No newline at end of file diff --git a/admin-fe/src/main.ts b/admin-fe/src/main.ts index 0eced11..e88e7c5 100644 --- a/admin-fe/src/main.ts +++ b/admin-fe/src/main.ts @@ -4,7 +4,7 @@ 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"; httpConfig.baseURL = "http://localhost:8080" diff --git a/admin-fe/src/util/http.ts b/admin-fe/src/util/http.ts index eab5be4..4a391e3 100644 --- a/admin-fe/src/util/http.ts +++ b/admin-fe/src/util/http.ts @@ -7,7 +7,7 @@ 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 = { @@ -68,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(); @@ -76,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 = []; @@ -92,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, { diff --git a/admin-fe/src/views/goods/index.vue b/admin-fe/src/views/goods/index.vue index a385e9c..d9b880c 100644 --- a/admin-fe/src/views/goods/index.vue +++ b/admin-fe/src/views/goods/index.vue @@ -1,16 +1,21 @@ @@ -81,6 +146,7 @@ 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"; // //商品类别(1:普通 2:精选 3:秒杀 4:抽奖) const CategoryEnum = { @@ -138,18 +204,44 @@ function loadDataList() { }) } +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 updateData() { +async function saveGoodsData() { try { - await http.put(`/admin/goods/${editData.id}`, editData) + console.log(editData) + if (editData.id <= 0) { + await http.post('/admin/goods', editData) + } else { + await http.put(`/admin/goods/${editData.id}`, editData) + } modalVisible.value = false; - message.toast('更新成功'); + message.toast('保存成功'); loadDataList(); } catch (e) { - message.toast(e.message || '更新失败') + message.toast(e.message || '保存失败') } } diff --git a/admin-fe/vite.config.js b/admin-fe/vite.config.js index 4a6a8d8..f3710f9 100644 --- a/admin-fe/vite.config.js +++ b/admin-fe/vite.config.js @@ -2,9 +2,10 @@ import vue from '@vitejs/plugin-vue' import vueJsxPlugin from "@vitejs/plugin-vue-jsx"; export default { + hmr: true, // 开发服务信息 server: { - host:'::' + host: '::' }, // 必须配置vue插件 plugins: [ diff --git a/api/src/main/java/me/xiaoyan/point/api/config/ApplicationConfig.java b/api/src/main/java/me/xiaoyan/point/api/config/ApplicationConfig.java index a9fff36..645d4dd 100644 --- a/api/src/main/java/me/xiaoyan/point/api/config/ApplicationConfig.java +++ b/api/src/main/java/me/xiaoyan/point/api/config/ApplicationConfig.java @@ -11,7 +11,9 @@ public class ApplicationConfig implements WebMvcConfigurer { // 跨域 目前小程序不需要,暂时不用考虑 @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**").allowedOrigins("*"); + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("GET", "POST", "PUT", "DELETE"); } diff --git a/api/src/main/java/me/xiaoyan/point/api/controller/FileController.java b/api/src/main/java/me/xiaoyan/point/api/controller/FileController.java new file mode 100644 index 0000000..a710816 --- /dev/null +++ b/api/src/main/java/me/xiaoyan/point/api/controller/FileController.java @@ -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, ""); + } +} diff --git a/api/src/main/java/me/xiaoyan/point/api/pojo/dto/FileUploadResult.java b/api/src/main/java/me/xiaoyan/point/api/pojo/dto/FileUploadResult.java new file mode 100644 index 0000000..964cd35 --- /dev/null +++ b/api/src/main/java/me/xiaoyan/point/api/pojo/dto/FileUploadResult.java @@ -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; +}