diff --git a/java-mall-app-shop-admin/App.vue b/java-mall-app-shop-admin/App.vue index 426fbf3..d5ba577 100644 --- a/java-mall-app-shop-admin/App.vue +++ b/java-mall-app-shop-admin/App.vue @@ -3,6 +3,7 @@ import APPUpdate, { getCurrentNo } from "@/config/appUpdate"; // #endif import { mapState, mapActions } from "vuex"; +import { webSocketManager } from "@/utils/socket.js"; export default { data() { return { @@ -27,7 +28,7 @@ export default { // #endif }, computed: { - ...mapState("user", ["uid", "userInfo", "socket"]), + ...mapState("user", ["uid", "userInfo"]), }, onShow: function () { this.globalData.isAppActive = true; @@ -40,6 +41,7 @@ export default { }, onHide: function () { this.globalData.isAppActive = false; + webSocketManager.closeAllGlobalConnections(); }, globalData: { isAppActive: false, diff --git a/java-mall-app-shop-admin/api/warehouse/commodity.js b/java-mall-app-shop-admin/api/warehouse/commodity.js new file mode 100644 index 0000000..7616cf0 --- /dev/null +++ b/java-mall-app-shop-admin/api/warehouse/commodity.js @@ -0,0 +1,174 @@ +import http from "../../utils/http"; +import config from "../../config/config"; + +/** 添加商品和编辑商品 + * + * @author Seven + * @data 2025-7-22 + * @params { + * productObj{}, + * productItems:{} + * } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-base/saveProduct + */ + +export function UpdateCommodityInfo(params) { + return http({ + url: "/shop/shop-product-base/saveProduct", + method: "post", + baseURL: config.adminApi, + headers: { + "content-type": "application/x-www-form-urlencoded;charset=UTF-8", + }, + params, + }); +} + +/** 添加商品和编辑商品 + * + * @author Seven + * @data 2025-7-22 + * @params { + * product_id : 33002 + item_id : 66419 + item_unit_price :10 + item_quantity :20 + * } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-item/editQuantity + */ + +export function UpdateCommodityPriceAndQuantity(params) { + return http({ + url: "/shop/shop-product-item/editQuantity", + method: "post", + baseURL: config.adminApi, + headers: { + "content-type": "application/x-www-form-urlencoded;charset=UTF-8", + }, + params, + }); +} + +/** 获取分类详情 + * + * @author Seven + * @data 2025-7-22 + * @params { category_id } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-base-product-category/get + */ + +export function GetClassifyInfo(params) { + return http({ + url: "/shop/shop-base-product-category/get", + method: "get", + baseURL: config.adminApi, + params, + }); +} + +/** 获取商品base详情 + * + * @author Seven + * @data 2025-7-22 + * @params { product_id : number } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-info/productInfoByPid + */ + +export function GetCommodityBaseInfo(params) { + return http({ + url: "/shop/shop-product-info/productInfoByPid", + method: "get", + baseURL: config.adminApi, + params, + }); +} + +/** 获取商品规格 库存 和 价格 + * + * @author Seven + * @data 2025-7-6 + * @param { product_id } + * @returns { } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-item/items?product_id=23591&source_lang=zh_CN + */ + +export function GetCommoditSpecification(params) { + return http({ + url: "/shop/shop-product-item/items", + method: "get", + params, + baseURL: config.adminApi, + }); +} + +/** 获取商品规格 品牌 信息 + * + * @author Seven + * @data 2025-7-22 + * @param { spec_id } + * @returns { } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-base-product-type/get + */ + +export function GetCommoditSpecificationBaseInfo(params) { + return http({ + url: "/shop/shop-base-product-type/get", + method: "get", + params, + baseURL: config.adminApi, + }); +} + +/** 添加商品规格 + * + * @author Seven + * @data 2025-7-22 + * @param { spec_id : number , spec_item_name : 10 } + * @returns { } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-spec-item/edit + */ + +export function UpdateCommoditSpecification(params) { + return http({ + url: "/shop/shop-product-spec-item/edit", + method: "post", + params, + baseURL: config.adminApi, + }); +} + +/** 删除商品规格 + * + * @author Seven + * @data 2025-7-22 + * @param { spec_item_id : number } + * @returns { } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-spec-item/delete + */ + +export function DelectCommoditSpecification(params) { + return http({ + url: "/shop/shop-product-spec-item/delete", + method: "post", + params, + baseURL: config.adminApi, + }); +} + +/** 获取当前分类规格列表 + * + * @author Seven + * @data 2025-7-22 + * @param { spec_item_id : number } + * @returns { } + * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-spec-item/specItems + */ + +export function GetClassifySpecificationList(params) { + return http({ + url: "/shop/shop-product-spec-item/specItems", + method: "get", + params, + baseURL: config.adminApi, + }); +} diff --git a/java-mall-app-shop-admin/api/warehouse/productList.js b/java-mall-app-shop-admin/api/warehouse/productList.js index 0f90f8d..6aafee6 100644 --- a/java-mall-app-shop-admin/api/warehouse/productList.js +++ b/java-mall-app-shop-admin/api/warehouse/productList.js @@ -70,21 +70,3 @@ export function DelectCommodity(params) { baseURL: config.adminApi, }); } - -/** 获取商品规格 库存 和 价格 - * - * @author Seven - * @data 2025-7-6 - * @param { product_id } - * @returns { } - * @see https://mall.gpxscs.cn/api/admin/shop/shop-product-item/items?product_id=23591&source_lang=zh_CN - */ - -export function GetCommoditSpecification(params) { - return http({ - url: "/shop/shop-product-item/items", - method: "get", - params, - baseURL: config.adminApi, - }); -} diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/README.md b/java-mall-app-shop-admin/components/poiuy-uImgUpload/README.md new file mode 100644 index 0000000..46241f3 --- /dev/null +++ b/java-mall-app-shop-admin/components/poiuy-uImgUpload/README.md @@ -0,0 +1,165 @@ + + +插件安装 + +图片上传组件 将组件放在自己的组件库里导入即可 + +### 注意 + +1.最好在回调结果后使用debugger看一下结果! + +2.请先看一下示范代码。 + +3.如果有问题和建议可以联系我,也可以自己修改成符合自己项目的。 + +4.注意,一定要去看看接口的返回值和插件写的返回值是否一样,如果不一样,可以在下面写注释的地方修改。 + +#### 导入: +> import imgUpload from '@/components/my-components/uImgUpload.vue'; + +------------------------------------- + +#### 属性 attribute + +| 属性名 | 类型 | 介绍 | 默认值 | +|----------------|--------|-------------|------------------| +| imgArr | Array | 图片展示列表 | \[\] | +| uploadImgCount | Number | 一次可选多少张图片 | 3 | +| imgCount | Number | 一共可以上传多少张图片 | 3 | +| imgSize | Number | 上传图片的大小 | 2 \(M\) | +| imgType | Array | 能上传图片的类型(注:小程序下不兼容) | \["jepg","png"\] | +| closeTip | Boolean | 是否关闭提示 | false | +| formData | Object | 提交时携带数据 | {} | +| loading | Boolean | 是否存在加载动画 | true | +| url | String | 上传图片的url (1.1.0版本新增) | "" | +| async | Boolean | 是否选择图片后直接上传 (1.1.0版本新增) | false | +| header | Array | 请求头添加新的属性值 (1.1.1版本新增) | [] | +| previewMany | Boolean | 是否开启多图预览 (1.1.2版本新增) | false | +| config | Object | 配置对象 **已经设置默认值** (1.1.2版本新增) | {} | +| pressImg | Number | 是否开启图片压缩,0-100为压缩值 (1.1.3版本新增) | -1(不开启) | +| single | Boolean | 单图模式,上传一张后不会再出现上传按钮 (1.1.7版本新增) | false | +| disabled | Boolean | 禁用,用于回显展示 (1.1.7版本新增) | false | +------------------------------------- + +>>config对象参数 + +| 属性名 | 类型 | 介绍 | 默认值 | +|----------------|--------|-------------|------------------| +| delIcon | String | 删除图片的icon[使用图片链接] | "" | +| resultTip | Boolean | 是否显示结果提示 | true | +| resultType | String | 结果展示类型 [正在开发2风格] | 1 | +| loadIcon | String | 加载时的图标[使用图片链接] | "" | +| loadText | String | 加载时的文字 | "" | +| borderStyle | String | 上传框的样式[完全自定义] | 'border:2px dotted #dadada' | +| addStyle | String | 上传图片的中间icon[1(图片标识)/2(加号标识)] | "1" | + +------------------------------------- + +#### 方法 method + +>当**async**为**true**的时候 +>存在两个方法 + +| 方法名 | 介绍 | +|--------|------------------------| +| result | 上传后的返回结果\(成功对象\),存在all和new两种类型(详细解释见下) | +| delImg | 删除图片返回下标\(传入的图片数组的下标\)*与async值无关* | +------------------------------------- +**all: 将会把之前传的和新传入的图片数组一起返回出来。** + +**new: 将新传输的图片以数组的方式直接返回,格式为["图片1","图片2"],可以直接用数组合并在提交数据上。** + +------------------------------------- + +>当**async**为**false**的时候 +>开始上传图片 在父页面中调用该方法(当async为false,也就是默认值的时候使用) +> *注意* 一定要给组件 **ref="imgUpload"** + +------------------------------------- + +####示范代码 + +``` + + + + + +``` + + +------------------------------------- + +###[彩蛋提示] +可以在组件里修改组件小提示的颜色哦!只需要修改成功失败警告的颜色即可。 + +``` +tipObj: { + prompt: '', + typeColor: '#009100', + success: '#009100', //成功-#009100; 可自定义修改 + warning: '#d3d300', // 警告 -#d3d300; 可自定义修改 + error: '#FF0000' // 失败--#FF0000; 可自定义修改 + }, + +``` + + + + + + \ No newline at end of file diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.eot b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.eot new file mode 100644 index 0000000..d134a2f Binary files /dev/null and b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.eot differ diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.svg b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.svg new file mode 100644 index 0000000..e1eef05 --- /dev/null +++ b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.svg @@ -0,0 +1,56 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.ttf b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.ttf new file mode 100644 index 0000000..a3f9005 Binary files /dev/null and b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.ttf differ diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff new file mode 100644 index 0000000..9e7fb70 Binary files /dev/null and b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff differ diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff2 b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff2 new file mode 100644 index 0000000..88b0d01 Binary files /dev/null and b/java-mall-app-shop-admin/components/poiuy-uImgUpload/font/font_2170343_nij8dm8i18.woff2 differ diff --git a/java-mall-app-shop-admin/components/poiuy-uImgUpload/imgUpload.vue b/java-mall-app-shop-admin/components/poiuy-uImgUpload/imgUpload.vue new file mode 100644 index 0000000..2dd9cbb --- /dev/null +++ b/java-mall-app-shop-admin/components/poiuy-uImgUpload/imgUpload.vue @@ -0,0 +1,950 @@ + + + + + + + diff --git a/java-mall-app-shop-admin/components/uni-nav-bar/uni-nav-bar.vue b/java-mall-app-shop-admin/components/uni-nav-bar/uni-nav-bar.vue index 6f2e56a..778e8c7 100644 --- a/java-mall-app-shop-admin/components/uni-nav-bar/uni-nav-bar.vue +++ b/java-mall-app-shop-admin/components/uni-nav-bar/uni-nav-bar.vue @@ -32,9 +32,9 @@ class="uni-navbar-btn-text" v-if="leftText.length" > - {{ - leftText - }} + + {{ leftText }} + @@ -47,8 +47,9 @@ {{ title }} + {{ title }} + @@ -68,8 +69,9 @@ {{ rightText }} + {{ rightText }} + @@ -247,6 +249,7 @@ $nav-height: 44px; /* #ifndef APP-PLUS */ font-size: 14px; /* #endif */ + font-weight: bold; } .uni-nav-bar-right-text { diff --git a/java-mall-app-shop-admin/config/componentConfig.js b/java-mall-app-shop-admin/config/componentConfig.js index 4380af0..c6fe2b3 100644 --- a/java-mall-app-shop-admin/config/componentConfig.js +++ b/java-mall-app-shop-admin/config/componentConfig.js @@ -70,8 +70,6 @@ export default { break; } - console.log("marketId", marketId); - console.log("phoneInfo.brand", phoneInfo.brand); let params = { marketId: 100, diff --git a/java-mall-app-shop-admin/main.js b/java-mall-app-shop-admin/main.js index aa2c81c..11c0451 100644 --- a/java-mall-app-shop-admin/main.js +++ b/java-mall-app-shop-admin/main.js @@ -4,14 +4,11 @@ import store from "./store"; import uView from "@/uni_modules/uview-ui"; import "./uni.promisify.adaptor"; import tui from "./utils/httpRequest"; -import Socket from "./utils/socket.js"; Vue.use(uView); Vue.config.productionTip = false; -// Vue.prototype.$utils = Utils Vue.prototype.tui = tui; -Vue.prototype.$Socket = Socket; App.mpType = "app"; const app = new Vue({ diff --git a/java-mall-app-shop-admin/manifest.json b/java-mall-app-shop-admin/manifest.json index 74e0f54..04364a9 100644 --- a/java-mall-app-shop-admin/manifest.json +++ b/java-mall-app-shop-admin/manifest.json @@ -4,7 +4,7 @@ "package" : "com.xiaofa.shopAdmin", "description" : "", "versionName" : "1.0.0", - "versionCode" : 102, + "versionCode" : 100, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { @@ -17,19 +17,13 @@ "autoclose" : true, "delay" : 0 }, - "ios" : { - "capabilities" : { - "com.apple.SafariKeychain" : true - } - }, /* 模块配置 */ "modules" : { "Maps" : {}, "Camera" : {}, "Record" : {}, "VideoPlayer" : {}, - "UIWebview" : {}, - "Push" : {} + "UIWebview" : {} }, /* 应用发布信息 */ "distribute" : { @@ -68,25 +62,7 @@ }, /* SDK配置 */ "sdkConfigs" : { - "maps" : {}, - "push" : { - "igexin" : { - "appid" : "rQxaGAKl7t83KlTubAaKC3", - "appkey" : "QSElTn6ttq5nmrIZtFhCT", - "appsecret" : "zbC1MMuzaf7kW91fdhm9v8" - }, - "unipush" : { - "version" : "2", - "offline" : false, - "hms" : {}, - "oppo" : {}, - "vivo" : {}, - "mi" : {}, - "meizu" : {}, - "honor" : {}, - "fcm" : {} - } - } + "maps" : {} }, "icons" : { "android" : { @@ -121,7 +97,7 @@ } }, "splashscreen" : { - "androidStyle" : "common" + "androidStyle" : "default" } } }, diff --git a/java-mall-app-shop-admin/pages/IM/IM.vue b/java-mall-app-shop-admin/pages/IM/IM.vue index 85a8673..259913f 100644 --- a/java-mall-app-shop-admin/pages/IM/IM.vue +++ b/java-mall-app-shop-admin/pages/IM/IM.vue @@ -6,7 +6,8 @@ {{ msgInfo.mine.user_nickname }} @@ -149,7 +150,7 @@ export default { }, onLoad() {}, computed: { - ...mapState("user", ["uid", "userInfo", "socket", "imWeidu", "getMsg"]), + ...mapState("user", ["uid", "userInfo", "imWeidu", "getMsg"]), }, watch: { imWeidu: { @@ -198,9 +199,9 @@ export default { this.loading = true; let res = await GetImConfig({ type: "json", uid: this.uid }); + console.log(res); if (res && res.status == 200) { this.msgInfo = res.data; - if (res.data.friend.length > 0) { let _imWeiDu = uni.getStorageSync("imWeiDu"); if (_imWeiDu) { @@ -217,6 +218,7 @@ export default { this.msgList = res.data.friend[0].list; } } + // this.connectSocket(this.userInfo); } this.loading = false; }, @@ -279,11 +281,12 @@ export default { .IM-status { display: flex; align-items: center; - gap: 10rpx; + gap: 20rpx; .img { width: 88rpx; height: 88rpx; - border-radius: 10rpx; + border-radius: 100%; + margin-right: 20rpx; } } @@ -384,7 +387,7 @@ export default { } .icon-kefu { - // margin-right: 40rpx; + margin-right: 40rpx; } .IM-loading { diff --git a/java-mall-app-shop-admin/pages/IM/IMmsgContent.vue b/java-mall-app-shop-admin/pages/IM/IMmsgContent.vue index 6f64fc8..dad1a7e 100644 --- a/java-mall-app-shop-admin/pages/IM/IMmsgContent.vue +++ b/java-mall-app-shop-admin/pages/IM/IMmsgContent.vue @@ -144,6 +144,7 @@ import tChatBar from "@/components/t-chat-bar/t-chat-bar"; import tuiBadge from "@/components/tui-badge/tui-badge.vue"; import emoji from "../../static/im/emojiData.js"; import { mapState } from "vuex"; +import { webSocketManager } from "@/utils/socket.js"; export default { components: { tChatBar, @@ -178,7 +179,6 @@ export default { RECORDER: uni.getRecorderManager(), playMsgid: "", isPlayVoice: false, - // socket: {}, chattype: "user", isHideKeyBoard: false, emojiPath: "https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/", @@ -326,7 +326,7 @@ export default { }, }, computed: { - ...mapState("user", ["uid", "userInfo", "socket", "getMsg"]), + ...mapState("user", ["uid", "userInfo", "getMsg"]), }, methods: { async setMsgRead(lastid) { @@ -515,26 +515,7 @@ export default { // } } }, - // socketServer() { - // console.log(this.userInfo.im.node_site_url); - // this.socket = new this.$Socket({ - // url: this.userInfo.im.node_site_url, - // maxInterValCount: 5, - // interValTime: 2000, - // onClose: (res) => { - // console.log("socket关闭"); - // }, - // onOpen: (res) => { - // console.log("socket连接成功"); - // }, - // onMsg: (res) => { - // console.log("onMsg", res); - // }, - // }); - - // this.socket.connectserver(this.userInfo.im); - // }, processTimeString(timeStr) { const parts = timeStr.split(":"); @@ -800,7 +781,9 @@ export default { if (res && res.status == 200) { chat_data.mine.message_id = res.data.message_other_id; - this.socket.nsend(chat_data); + let connection = webSocketManager.getConnection("ws1"); + + connection.send(chat_data); let lastid = res.data.message_other_id; let tempmy = this.userInfo.im; ///TOID现在是模拟的 diff --git a/java-mall-app-shop-admin/pages/audit/checkAudit.vue b/java-mall-app-shop-admin/pages/audit/checkAudit.vue index c8839dc..35397b9 100644 --- a/java-mall-app-shop-admin/pages/audit/checkAudit.vue +++ b/java-mall-app-shop-admin/pages/audit/checkAudit.vue @@ -276,6 +276,7 @@ :placeholder="item.placeholder" :readonly="item.isReadonly" :maxlength="item.maxLength" + clearable /> @@ -117,5 +1053,255 @@ export default { @import "@/styles/variables.scss"; .addAndEditProduct-container { + width: 100vw; + height: calc(100vh - 120rpx); + overflow: auto; + + .nav-bar { + ::v-deep.uni-navbar__content { + z-index: 1100; + } + } + + .addAndEditProduct-content { + .u-form { + background: #f5f6fa; + + .form-item { + background: #fff; + padding: 0 40rpx; + } + + .form-item-margin-top-20 { + margin-top: 40rpx; + } + + .form-item-margin-20 { + margin: 40rpx 0; + } + + .form-sale-time-item { + ::v-deep.u-form-item__body__right__content__slot { + display: flex; + flex-flow: column; + align-items: normal; + } + + .sale-time { + margin: 40rpx 0; + background: #eeeeee; + padding: 20rpx; + color: #6666; + } + } + + .form-item-specification { + ::v-deep.u-form-item__body__right__content__slot { + display: flex; + flex-flow: column; + align-items: normal; + } + + .total-content { + display: flex; + justify-content: space-between; + margin: 8px 0 8px 8px; + font-weight: bold; + } + + .specification-content { + background: #f5f6fa; + border-radius: 16rpx; + + .specification-item { + display: flex; + flex-flow: column; + justify-content: space-between; + padding: 8px; + + .specification-name { + margin: 3px 0; + font-size: 16px; + font-weight: bold; + } + + .specification-info { + display: flex; + justify-content: space-between; + height: 96rpx; + font-size: 24rpx; + color: #626262; + } + + .info-item { + display: flex; + flex-flow: column; + justify-content: space-around; + } + } + } + } + } + + .tips-content { + padding: 40rpx 20rpx; + background: #f5f6fa; + + .tips { + font-size: 28rpx; + color: #666666; + } + + .btn-unfold { + margin: 52rpx auto 20rpx; + width: 200rpx; + font-size: 24rpx; + height: 60rpx; + + &::after { + border: none; + } + } + } + } + + .affirm-popup { + ::v-deep.u-popup__content { + border-radius: 16rpx; + } + + ::v-deep.u-fade-enter-to { + z-index: 10076 !important; + } + + .affirm-popup-content { + width: 600rpx; + + .affirm-popup-title { + padding: 40rpx; + text-align: center; + font-weight: bold; + } + + .affirm-popup-tips { + padding: 0 60rpx; + font-size: 28rpx; + text-align: center; + } + + .popup-btn-list { + display: flex; + margin: 50rpx; + + .btn-item { + width: 46%; + height: 80rpx; + border-color: #909193; + + &::after { + border: none; + } + } + + .btn-item-2 { + background: $base-color; + color: #fff; + border: none; + } + } + } + } + + .add-popup { + ::v-deep.u-popup__content { + border-radius: 16rpx; + } + + ::v-deep.u-fade-enter-to { + z-index: 10076 !important; + } + + .add-popup-content { + width: 700rpx; + + .add-popup-title { + padding: 40rpx; + text-align: center; + font-weight: bold; + font-size: 36rpx; + } + + .add-tips { + padding: 0 24rpx; + text-align: center; + color: red; + } + + .u-form { + margin: 0 40rpx; + + .form-item { + margin: 28rpx 0; + } + } + + .popup-btn-list { + display: flex; + margin: 50rpx; + + .btn-item { + width: 46%; + height: 80rpx; + border-color: #909193; + + &::after { + border: none; + } + } + + .btn-item-2 { + background: $base-color; + color: #fff; + border: none; + } + } + } + } + + .bottom-btn { + position: fixed; + bottom: 0; + left: 0; + right: 0; + padding: 20rpx; + background: #fff; + border-top: 2rpx solid $uni-border-color; + z-index: 2; + + .bottom-list { + display: flex; + align-items: center; + justify-content: flex-end; + + .bottom-btn-item { + font-size: 28rpx; + border-color: $base-color; + color: #fff; + background: $base-color; + + .bottom-icon { + margin-right: 8rpx; + } + + &::after { + border: none; + } + } + } + } + + .loading-page { + z-index: 1099; + } } diff --git a/java-mall-app-shop-admin/pages/warehouse/manage/batchSearch.vue b/java-mall-app-shop-admin/pages/warehouse/manage/batchSearch.vue index fdcd0c7..aadc1a8 100644 --- a/java-mall-app-shop-admin/pages/warehouse/manage/batchSearch.vue +++ b/java-mall-app-shop-admin/pages/warehouse/manage/batchSearch.vue @@ -179,7 +179,7 @@ shape="circle" @click="handerPageBack" > - 确认 + 退出 @@ -759,7 +759,7 @@ export default { .affirm-popup-title { padding: 40rpx; text-align: center; - font-weight: bold; + font-weight: bold; } .affirm-popup-tips { diff --git a/java-mall-app-shop-admin/pages/warehouse/manage/classifyList.vue b/java-mall-app-shop-admin/pages/warehouse/manage/classifyList.vue index 1168658..cd5597b 100644 --- a/java-mall-app-shop-admin/pages/warehouse/manage/classifyList.vue +++ b/java-mall-app-shop-admin/pages/warehouse/manage/classifyList.vue @@ -381,6 +381,7 @@ type="number" placeholder="请输入商品排序" border="none" + clearable > @@ -229,6 +232,7 @@ diff --git a/java-mall-app-shop-admin/pages/warehouse/manage/productList.vue b/java-mall-app-shop-admin/pages/warehouse/manage/productList.vue index 0556fe9..5bcdbce 100644 --- a/java-mall-app-shop-admin/pages/warehouse/manage/productList.vue +++ b/java-mall-app-shop-admin/pages/warehouse/manage/productList.vue @@ -6,9 +6,9 @@ :border="false" :fixed="true" :height="'44px'" - rightWidth="0" + :rightWidth="0" :leftWidth="30" - backgroundColor="#f5f6fa" + backgroundColor="#f5f6fa" > 下架 - - - 编辑 + + + 编辑 + @@ -189,32 +194,25 @@ + <<<<<<< Updated upstream ======= + 分类管理 + >>>>>>> Stashed changes - - 分类管理 + + 分类管理 - + 批量操作 - - 商品新建 + + + 商品新建 + @@ -268,8 +266,8 @@ :placeholder="String(item.item_quantity)" border="surround" type="number" + clearable v-model="item.item_quantity_2" - @change="haderPopupRepertoryInputChange($event, index)" > @@ -282,13 +280,27 @@ :hairline="true" :plain="true" shape="circle" - @click="handerSave" + @click="updateCommodityPriceAndQuantity" > 保存 + + + + @@ -297,18 +309,22 @@ import { GetProductCategoryTree, GetProductList, UpdateProductPutaway, - GetCommoditSpecification, } from "@/api/warehouse/productList"; +import { + GetCommoditSpecification, + UpdateCommodityPriceAndQuantity, +} from "@/api/warehouse/commodity"; import navBar from "@/components/uni-nav-bar/uni-nav-bar"; import tuiCollapse from "../manage/components/tui-collapse/tui-collapse.vue"; import favoriteLoading from "@/components/favorite-loading/favorite-loading.vue"; - +import addAndEditProduct from "./addAndEditProduct.vue"; export default { name: "productList", components: { navBar, tuiCollapse, favoriteLoading, + addAndEditProduct, }, data() { return { @@ -372,6 +388,10 @@ export default { time2: null, showBottomPopup: false, currSpecificationList: [], + showRightPopup: false, + productItem: {}, + isAdd: true, + selectCommodisItems: [], }; }, computed: {}, @@ -431,9 +451,61 @@ export default { }; let res = await GetCommoditSpecification(params); - if (res && res.status == 200) { - this.currSpecificationList = res.data; + this.selectCommodisItems = res.data; + + // 1. 解析产品规格模板 + let productSpec = JSON.parse(JSON.stringify(item.product_spec)); + + productSpec = JSON.parse(productSpec); + if (!Array.isArray(productSpec)) { + productSpec = [productSpec]; // 确保是数组格式 + } + + const specItemMap = []; + + productSpec.forEach((specGroup) => { + if (specGroup.item && Array.isArray(specGroup.item)) { + specGroup.item.forEach((item) => { + specItemMap.push(item.id); + }); + } + }); + + this.currSpecificationList = this.selectCommodisItems + .map((item) => { + try { + // 解析规格数据(兼容字符串和对象) + const itemSpec = + typeof item.item_spec === "string" + ? JSON.parse(item.item_spec) + : item.item_spec; + + if (Array.isArray(itemSpec) && itemSpec.length > 0) { + const firstSpec = itemSpec[0]; + + // 匹配规格逻辑 + if (firstSpec.item && firstSpec.item.id) { + const matchedSpec = specItemMap.find( + (id) => id == firstSpec.item.id + ); + + if (matchedSpec) { + return { + ...item, + spec_item_id: matchedSpec, + item_is_default: Boolean(item.item_is_default), + // 保留原始数据(调试用) + }; + } + } + } + } catch (e) { + console.error(`处理商品 ${item.item_id} 时出错:`, e); + } + }) + .filter((item) => item != undefined); + console.log(this.currSpecificationList); this.showBottomPopup = true; } }, @@ -642,18 +714,49 @@ export default { url: "../../news/search/search", }); }, - haderPopupPriceInputChange(e, index) { - this.currSpecificationList[index].item_unit_price_2 = e; + async updateCommodityPriceAndQuantity(item) { + let params = {}; + + let res = await UpdateCommodityPriceAndQuantity(params); + if (res && res.status) { + this.$refs.uToast.show({ + message: "修改成功", + type: "succeed", + duration: 1000, + }); + + // 静默更新数组 + let listParams = { + kind_id: "1201,1202,1203", + pageNum: 9999, + pageSize: this.pageSize, + product_state_id: this.currProductStateId, + category_id: this.currCategoryId, + openCount: true, + }; + + let result = await GetProductList(listParams); + if (result && result.status == 200) { + this.commodityList = res.data.items; + } + } + this.showBottomPopup = false; }, - haderPopupRepertoryInputChange(e, index) { - this.currSpecificationList[index].item_quantity_2 = e; - }, - handerSave() {}, skipuBatch() { uni.navigateTo({ url: "/pages/warehouse/manage/batch", }); }, + skipuClassify() { + uni.navigateTo({ + url: "/pages/warehouse/manage/classifyList", + }); + }, + handerProductEdit(item) { + this.productItem = item; + this.showRightPopup = true; + this.isAdd = false; + }, }, }; @@ -661,14 +764,14 @@ export default { diff --git a/java-mall-app-shop-admin/pages/warehouse/manage/specificationOfGood.vue b/java-mall-app-shop-admin/pages/warehouse/manage/specificationOfGood.vue new file mode 100644 index 0000000..da1e9d9 --- /dev/null +++ b/java-mall-app-shop-admin/pages/warehouse/manage/specificationOfGood.vue @@ -0,0 +1,1165 @@ + + + + + diff --git a/java-mall-app-shop-admin/pages/warehouse/manage/typeManagement.vue b/java-mall-app-shop-admin/pages/warehouse/manage/typeManagement.vue index a014b9c..62aa5ce 100644 --- a/java-mall-app-shop-admin/pages/warehouse/manage/typeManagement.vue +++ b/java-mall-app-shop-admin/pages/warehouse/manage/typeManagement.vue @@ -15,44 +15,46 @@ :refresher-triggered="isRefreshing" @refresherrefresh="handleRefresh" > - - - - {{ item.type_name }} - - {{ item.type_category_name }} + + + + {{ item.type_name }} + + {{ item.type_category_name }} + - - - - 删除 + + + + 删除 + + + + + 编辑 + - - - 编辑 - - - - + - + 新建商品类型 @@ -209,7 +216,10 @@ v-for="(item, index) in allSpecificationList" :key="index" > - 分类名称:{{ item.name }} + + + 分类名称:{{ item.name }} + diff --git a/java-mall-app-shop-admin/store/modules/user.js b/java-mall-app-shop-admin/store/modules/user.js index ed04423..649f3c9 100644 --- a/java-mall-app-shop-admin/store/modules/user.js +++ b/java-mall-app-shop-admin/store/modules/user.js @@ -1,7 +1,7 @@ import { GetAuditInfo, GetAuditStatus } from "../../api/audit"; import { GetAccountLogin, GetLogin } from "../../api/login"; import { OutLogin } from "../../api/user"; -import $Socket from "../../utils/socket"; +import { webSocketManager } from "@/utils/socket.js"; import Vue from "vue"; // import $cookies from '../../utils/vue-cookies' @@ -72,10 +72,12 @@ const mutations = { state.imWeidu = {}; state.dashboardInfo = {}; }, - CONNECT_SOCKET(state, { socket }) { - state.socket = socket; + CONNECT_SOCKET(state, { webSocketManager }) { + state.socket = webSocketManager; }, GET_IM_MSG(state, { msg }) { + console.log("GET_IM_MSG", msg); + if (!msg.id) return; state.getMsg = msg; var im = {}; let _imWeiDu = uni.getStorageSync("imWeiDu"); @@ -121,8 +123,7 @@ const actions = { // #endif - params.cid = "ae9c4517a4ae17ea33570839a0f5fdba"; - params.osType = 1; + console.log("push_clientid"); const res = await GetAccountLogin(params); @@ -195,6 +196,8 @@ const actions = { } }, async LoginOut({ commit }, isTokenExpires) { + webSocketManager.closeAllGlobalConnections(); + if (isTokenExpires) { await OutLogin(); @@ -379,27 +382,22 @@ const actions = { }, connectSocket({ commit }, userInfo) { if (!userInfo) return; - var socket = new $Socket({ - url: userInfo.im.node_site_url, - maxInterValCount: 5, - interValTime: 20000, - onClose: (res) => { - console.log("sockrt关闭"); - }, - onOpen: (res) => { - console.log("sockrt连接成功"); - }, - onMsg: (res) => { - console.log("onMsg", res); - if (res) { - let msg = JSON.parse(res.data); - commit("GET_IM_MSG", { msg }); - } - }, - }); - socket.connectserver(userInfo.im); - commit("CONNECT_SOCKET", { socket }); + webSocketManager.createGlobalConnection( + "ws1", + userInfo.im.node_site_url, + 2000 + ); + + webSocketManager.getConnection("ws1").onMessage((res) => { + // 处理消息逻辑 + if (res) { + let msg = res; + + console.log("ws1收到的消息:", msg); + commit("GET_IM_MSG", { msg }); + } + }); }, }; diff --git a/java-mall-app-shop-admin/styles/variables.scss b/java-mall-app-shop-admin/styles/variables.scss index e582cee..e32cfd0 100644 --- a/java-mall-app-shop-admin/styles/variables.scss +++ b/java-mall-app-shop-admin/styles/variables.scss @@ -1,4 +1,3 @@ /*主颜色*/ -$base-color: #FE411B; // 蓝色 #4b71ff - -$base-btn-bg-color: #FE411B; +$base-color: #fe411b; // 蓝色 #4b71ff +$base-btn-bg-color: #fe411b; diff --git a/java-mall-app-shop-admin/uni_modules/uview-ui/components/u-toast/u-toast.vue b/java-mall-app-shop-admin/uni_modules/uview-ui/components/u-toast/u-toast.vue index f194830..f2a7fce 100644 --- a/java-mall-app-shop-admin/uni_modules/uview-ui/components/u-toast/u-toast.vue +++ b/java-mall-app-shop-admin/uni_modules/uview-ui/components/u-toast/u-toast.vue @@ -1,291 +1,306 @@ diff --git a/java-mall-app-shop-admin/utils/socket.js b/java-mall-app-shop-admin/utils/socket.js index 5c931ff..0ab2c3b 100644 --- a/java-mall-app-shop-admin/utils/socket.js +++ b/java-mall-app-shop-admin/utils/socket.js @@ -1,261 +1,189 @@ -const noop = function() {}; -class Socket { - static stopTime = 0; - static concatCount = 0; - constructor({ - url = '', - onOpen = noop, - onMsg = noop, - onClose = noop, - onError = noop, - onReload = noop, - onRdFinsh = noop, - maxInterValCount = 10, - interValTime = 2000, - SocketState = {}, - ...args - } = {}) { - this.isRconnectIng = false; //是否处于重连状态 - this.waiting = Promise.resolve(false); //心跳检查必须等待重连完成后 - this.waitDep = []; //等待时收集依赖的容器 - this.SocketTask = { - nsend: noop, - nclose: noop, - nrconnect: noop, - isconnect: false, - uniColse: false, - maxInterValCount, - interValTime, - InterValCount: 0, - eventPatch: null, - url, - onOpen, - onMsg, - onClose, - onError, - onReload, - onRdFinsh, - extra: args - }; - this._EventDispath(this.SocketTask); - //this.initChat(this.SocketTask, this.SocketTask.extra); - return this.SocketTask; +class WebsocketUtil { + constructor(url, time) { + this.is_open_socket = false; // 避免重复连接 + this.url = url; // 地址 + this.data = null; + // 心跳检测 + this.timeout = time; // 多少秒执行检测 + this.heartbeatInterval = null; // 检测服务器端是否还活着 + this.reconnectInterval = null; // 重连之后多久再次重连 + this.messageCallbacks = []; // 存储消息回调函数 + this.store = null; // 存储Vuex store引用 - } - set CONCATCOUNT(value) { - Socket.concatCount = value; - if (value > 0) this._notify(); - } - get CONCATCOUNT() { - return Socket.concatCount - } - /** - * 仅供内部使用,通知所有收集到的依赖 - */ - _notify() { - for (let i = 0; i < this.waitDep.length; i++) { - this.waitDep[i].call(this.SocketTask); - } - this.waitDep = []; - } - /** - * 仅供内部使用,确认当前是否连接成功,收集依赖 - */ - _chunkConnect(fn) { - if (Socket.concatCount > 0) { - fn(); - } else { - this.waitDep.push(fn); - } - } - /** - * 仅供内部使用,事件注册 - */ - _EventDispath({ - onReload - } = {}) { - let SocketTask = this.SocketTask; - let events = { - onOpen: [], - onMsg: [], - onClose: [], - onError: [], - onReload: [], - onRdFinsh: [], - } - SocketTask.connectserver = config => { - //重连,判断是否 - if (!SocketTask.isconnect && !this.isRconnectIng) - { - this.SocketTask.url = config.node_site_url - this.initChat(this.SocketTask, this.SocketTask.extra); - } - } - SocketTask.nsend = msg => { - let text = JSON.stringify(msg) - this._chunkConnect(() => { - uni.sendSocketMessage({ - data: text - }) - }) - } - SocketTask.nclose = t => { - this._chunkConnect(() => { - SocketTask.uniColse = true; - uni.closeSocket(); - }) - } - SocketTask.nrconnect = t => { - this._chunkConnect(() => { - this.waiting = new Promise(async (resolve) => { - uni.closeSocket(); - let reloadStatus = false; - try { - const res = await this.initChat(SocketTask, SocketTask.extra); - reloadStatus = res; - } catch (e) {} - onReload.call(SocketTask, reloadStatus, SocketTask); - SocketTask.eventPatch.dispatchEvent('onReload', reloadStatus); - resolve(reloadStatus); - }) - }) - } + try { + return this.connectSocketInit(); + } catch (e) { + this.is_open_socket = false; + } + } - function EventDispatcher() { - this.events = events; - } - for (let key in events) { - EventDispatcher.prototype[key] = function(handler) { - if (typeof handler != 'function') return; - this.events[key].push(handler) - } - } - EventDispatcher.prototype.dispatchEvent = function(type, msg) { - let evenArr = this.events[type]; - if (evenArr.length > 0) { - for (let i = 0; i < evenArr.length; i++) { - evenArr[i].call(SocketTask, msg, SocketTask); - } - } - } - SocketTask.eventPatch = new EventDispatcher(); - } - /** - * 心跳检测 - */ - async hbDetection() { - const SocketTask = this.SocketTask; - if (SocketTask.uniColse) { - return false; - } - clearTimeout(Socket.stopTime); - if (!SocketTask.isconnect) { //未连接则启动连接 - if (SocketTask.maxInterValCount > SocketTask.InterValCount) { - Socket.stopTime = setTimeout(async () => { - try { - const R_result = await this.waiting; - if (R_result) return; - this.isRconnectIng = true; - const openResult = await this.initChat(SocketTask, SocketTask.extra); - if (openResult) return; - SocketTask.InterValCount++; - return this.hbDetection(); - } catch (e) { - return this.hbDetection(); - } - }, SocketTask.interValTime) - } else { - SocketTask.onRdFinsh.call(SocketTask, SocketTask.maxInterValCount, SocketTask); - SocketTask.eventPatch.dispatchEvent('onRdFinsh', SocketTask.maxInterValCount); - } - } - } - /** - * websocket监听事件 - */ - SocketEvents({ - onOpen, - onMsg, - onClose, - onError, - onReload, - } = {}) { - return new Promise((resolve, reject) => { - const SocketTask = this.SocketTask; - uni.onSocketOpen(res => { - this.CONCATCOUNT += 1; - this.isRconnectIng = false; - SocketTask.isconnect = true; - SocketTask.InterValCount = 0; - SocketTask.uniColse = false; - resolve(true); - onOpen.call(SocketTask, res, SocketTask); - SocketTask.eventPatch.dispatchEvent('onOpen', res) - }) - uni.onSocketMessage(msg => { - onMsg.call(SocketTask, msg, SocketTask); - SocketTask.eventPatch.dispatchEvent('onMsg', msg) - }) - uni.onSocketClose(async err => { - SocketTask.isconnect = false; - resolve(false); - if (!this.isRconnectIng) { - this.hbDetection(); - onClose.call(SocketTask, err, SocketTask); - SocketTask.eventPatch.dispatchEvent('onClose', err); - } - }) - uni.onSocketError(err => { - uni.closeSocket(); - onError.call(SocketTask, err, SocketTask); - SocketTask.eventPatch.dispatchEvent('onError', err) - }) - }) - } - /** - * 开始初始化chat - */ - initChat({ - url, - onOpen, - onMsg, - onClose, - onError, - onReload - } = {}, args) { - return new Promise(async (resolve, reject) => { - try { - await this.connectSocket(url, args); - let res = await this.SocketEvents({ - onOpen, - onMsg, - onClose, - onError, - onReload, - }) - resolve(res); - } catch (e) { - console.log(e) - reject(); - } - }) - } - /** - * 连接webSocket - */ - connectSocket(url, args) { - return new Promise((resolve, reject) => { - uni.connectSocket({ - url, - success: () => { - resolve(); - }, - fail: err => { - reject(); - }, - ...args - }) - }) - } + // 设置store + setStore(store) { + this.store = store; + } + + // 创建WebSocket连接 + connectSocketInit() { + if (this.is_open_socket) return; + + this.socketTask = uni.connectSocket({ + url: this.url, + success: () => { + this.is_open_socket = true; + return this.socketTask; + }, + }); + + this.socketTask.onOpen((res) => { + clearInterval(this.reconnectInterval); + clearInterval(this.heartbeatInterval); + this.is_open_socket = true; + this.start(); + + this.socketTask.onMessage((res) => { + try { + const data = JSON.parse(res.data); + // 如果有store,将数据存入store + if (this.store) { + this.store.commit("setWebSocketData", data); + } + // 执行所有注册的回调函数 + this.messageCallbacks.forEach((callback) => callback(data)); + } catch (e) { + console.error("WebSocket消息解析错误:", e); + } + }); + }); + + this.socketTask.onError((res) => { + this.is_open_socket = false; + if (this.socketTask) { + this.socketTask.close(); + } + }); + + this.socketTask.onClose(() => { + this.is_open_socket = false; + }); + } + + // 发送消息 + send(value) { + if (!this.is_open_socket) return; + + this.socketTask.send({ + data: JSON.stringify(value), + success() { + console.log("消息发送成功", value); + }, + fail(err) { + console.error("消息发送失败", err); + }, + }); + } + + // 关闭连接 + close() { + clearInterval(this.heartbeatInterval); + clearInterval(this.reconnectInterval); + this.is_open_socket = false; + if (this.socketTask) { + this.socketTask.close(); + } + } + + // 开启心跳检测 + start() { + // this.heartbeatInterval = setInterval(() => { + // const heartbeatMsg = { + // type: 'heartbeat', + // userId: uni.getStorageSync('userinfo')?.user_id + // } + // this.send(heartbeatMsg) + // }, this.timeout) + } + + // 重新连接 + reconnect() { + clearInterval(this.heartbeatInterval); + if (!this.is_open_socket) { + this.reconnectInterval = setInterval(() => { + this.connectSocketInit(); + }, 3000); + } + } + + // 注册消息回调 + onMessage(callback) { + this.messageCallbacks.push(callback); + } + + // 移除消息回调 + offMessage(callback) { + this.messageCallbacks = this.messageCallbacks.filter( + (cb) => cb !== callback + ); + } } -export default Socket + +class WebSocketManager { + constructor() { + this.connections = {}; // 存储所有WebSocket连接 + this.globalConnections = []; // 存储需要全局管理的连接名称 + } + + // 创建全局连接 + createGlobalConnection(name, url, time, store) { + if (!this.connections[name]) { + this.connections[name] = new WebsocketUtil(url, time); + if (store) this.connections[name].setStore(store); + this.globalConnections.push(name); + } + return this.connections[name]; + } + + // 创建页面级连接 + createPageConnection(name, url, time) { + if (!this.connections[name]) { + this.connections[name] = new WebsocketUtil(url, time); + } + return this.connections[name]; + } + + // 获取连接 + getConnection(name) { + return this.connections[name]; + } + + // 关闭指定连接 + closeConnection(name) { + console.log("关闭指定连接", name); + if (this.connections[name]) { + this.connections[name].close(); + delete this.connections[name]; + + // 从全局连接列表中移除 + this.globalConnections = this.globalConnections.filter((n) => n !== name); + } + } + + // 关闭所有全局连接 + closeAllGlobalConnections() { + this.globalConnections.forEach((name) => { + this.closeConnection(name); + }); + } + + // 关闭所有连接 + closeAllConnections() { + Object.keys(this.connections).forEach((name) => { + this.connections[name].close(); + }); + this.connections = {}; + this.globalConnections = []; + } +} + +// 导出单例实例 +export const webSocketManager = new WebSocketManager(); +export default WebsocketUtil;