Browse Source

fix:添加产品功能

storeBelieveInLight 1 year ago
parent
commit
83690e51e8

+ 2 - 1
App.vue

@@ -75,7 +75,8 @@
75 75
 			// 	// 	url: "/pages/my/index"
76 76
 			// 	// });
77 77
 			// });
78
-			// uni.redirectTo({
78
+			// uni.redirectTo({
79
+				
79 80
 			// 	url: "/pages/my/index"
80 81
 			// });
81 82
 

+ 79 - 1
commit/product.js

@@ -227,4 +227,82 @@ export function Addseckill(data) {
227 227
          data,
228 228
          apiType
229 229
      })
230
-}
230
+}
231
+
232
+
233
+
234
+
235
+/** 
236
+ * 文件上传
237
+*/
238
+
239
+export function uploadFile(data) {
240
+    const params = new FormData()
241
+    params.append('file', data.file)
242
+     return myRequestPhp({
243
+         url: `${pre}mer/upload/image/0/file`,
244
+         method: 'POST',
245
+         contentType: "application/x-www-form-urlencoded",
246
+         data: params,
247
+         apiType
248
+     })
249
+}
250
+
251
+/**
252
+ * 获取商品分类
253
+ */
254
+export function getGoodsClass(data) {
255
+    return myRequestPhp({
256
+        url: `${pre}mer/store/category/select`,
257
+        method: 'GET',
258
+        data,
259
+        apiType
260
+    })
261
+}
262
+/**
263
+ * 品牌选择
264
+ */
265
+export function getBrandSelect(data) {
266
+    return myRequestPhp({
267
+        url: `${pre}mer/store/category/brandlist`,
268
+        method: 'GET',
269
+        data,
270
+        apiType
271
+    })
272
+}
273
+
274
+
275
+/**
276
+ * @description 商品列表 -- 运费模板筛选
277
+ */
278
+export function shippingListApi(data) {
279
+    return myRequestPhp({
280
+        url: `${pre}mer/store/shipping/list`,
281
+        method: 'GET',
282
+        data,
283
+        apiType
284
+    })
285
+}
286
+
287
+/**
288
+ * 添加商品
289
+ */
290
+export function addProduct(data) {
291
+    return myRequestPhp({
292
+        url: `${pre}mer/store/product/create`,
293
+        method: 'POST',
294
+        data,
295
+        apiType
296
+    })
297
+}
298
+
299
+// mer/store/shipping/list
300
+
301
+
302
+// /**
303
+//  * @description 配置状态
304
+//  */
305
+// export function productConfigApi() {
306
+//     return request.get(`store/product/config`)
307
+// }
308
+

+ 8 - 6
commit/requestPhp.js

@@ -1,20 +1,22 @@
1 1
 
2 2
 
3 3
 import {getPhpToken} from "../utils/readAppInfo"
4
-let baseURL = 'http://testshop.hxxfb.com/'
4
+export let baseURL = 'http://testshop.hxxfb.com/'
5 5
 // 请求超出时间
6 6
 const timeout = 50000
7
-
7
+// const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuaHh4ZmIuY29tIiwiYXVkIjoiYXBpLmh4eGZiLmNvbSIsImlhdCI6MTc0MDM3MTM1NSwibmJmIjoxNzQwMzcxMzU1LCJleHAiOjE3NTExNzEzNTUsImp0aSI6WzI5OCwibWVyIl19.j2f8VwdwWY4zBpcijHntzUZ9YUms2SxChaVsYf7KnPs"
8
+// const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuaHh4ZmIuY29tIiwiYXVkIjoiYXBpLmh4eGZiLmNvbSIsImlhdCI6MTc0MDM3MTM1NSwibmJmIjoxNzQwMzcxMzU1LCJleHAiOjE3NTExNzEzNTUsImp0aSI6WzI5OCwibWVyIl19.j2f8VwdwWY4zBpcijHntzUZ9YUms2SxChaVsYf7KnPs"
9
+// const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuaHh4ZmIuY29tIiwiYXVkIjoiYXBpLmh4eGZiLmNvbSIsImlhdCI6MTc0MDU3MTQ0OCwibmJmIjoxNzQwNTcxNDQ4LCJleHAiOjE3NTEzNzE0NDgsImp0aSI6WzM1NCwibWVyIl19.PVwYv71Gq9RxTxQclUoFMgc8cZt0ju3zQkxk6SGCQJw"
10
+export const phpToken = uni.getStorageSync('phpToken') || ""
8 11
 // 需要修改token,和根据实际修改请求头
9 12
 export function myRequestPhp(options) {
10 13
 	let url = options.url;
11 14
 	let method = options.method || "get";
12 15
 	let data = options.data || {};
16
+	let contentType = options.contentType || "application/json;charset=UTF-8";
13 17
 	let header = {
14
-		'Content-Type': 'application/json;charset=UTF-8',
15
-		// 'X-Token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuaHh4ZmIuY29tIiwiYXVkIjoiYXBpLmh4eGZiLmNvbSIsImlhdCI6MTc0MDM3MTM1NSwibmJmIjoxNzQwMzcxMzU1LCJleHAiOjE3NTExNzEzNTUsImp0aSI6WzI5OCwibWVyIl19.j2f8VwdwWY4zBpcijHntzUZ9YUms2SxChaVsYf7KnPs',
16
-		"X-Token": uni.getStorageSync('phpToken'), //token
17
-		// "X-Token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuaHh4ZmIuY29tIiwiYXVkIjoiYXBpLmh4eGZiLmNvbSIsImlhdCI6MTc0MDU3MTQ0OCwibmJmIjoxNzQwNTcxNDQ4LCJleHAiOjE3NTEzNzE0NDgsImp0aSI6WzM1NCwibWVyIl19.PVwYv71Gq9RxTxQclUoFMgc8cZt0ju3zQkxk6SGCQJw",
18
+		'Content-Type': contentType,
19
+		"X-Token": phpToken,
18 20
 		...options.header
19 21
 	}
20 22
 	return new Promise((resolve, reject) => {

File diff suppressed because it is too large
+ 239 - 0
components/editor-icon.css


+ 385 - 0
components/editor.vue

@@ -0,0 +1,385 @@
1
+<template>
2
+  <view class="container">
3
+    <view class="page-body">
4
+      <view class="wrapper">
5
+        <view
6
+          class="toolbar"
7
+          @tap="format"
8
+          style="height: 120px; overflow-y: auto"
9
+        >
10
+          <view
11
+            :class="formats.bold ? 'ql-active' : ''"
12
+            class="iconfont icon-zitijiacu"
13
+            data-name="bold"
14
+          >
15
+          </view>
16
+          <view
17
+            :class="formats.italic ? 'ql-active' : ''"
18
+            class="iconfont icon-zitixieti"
19
+            data-name="italic"
20
+          >
21
+          </view>
22
+          <view
23
+            :class="formats.underline ? 'ql-active' : ''"
24
+            class="iconfont icon-zitixiahuaxian"
25
+            data-name="underline"
26
+          ></view>
27
+          <view
28
+            :class="formats.strike ? 'ql-active' : ''"
29
+            class="iconfont icon-zitishanchuxian"
30
+            data-name="strike"
31
+          ></view>
32
+          <view
33
+            :class="formats.align === 'left' ? 'ql-active' : ''"
34
+            class="iconfont icon-zuoduiqi"
35
+            data-name="align"
36
+            data-value="left"
37
+          ></view>
38
+          <view
39
+            :class="formats.align === 'center' ? 'ql-active' : ''"
40
+            class="iconfont icon-juzhongduiqi"
41
+            data-name="align"
42
+            data-value="center"
43
+          ></view>
44
+          <view
45
+            :class="formats.align === 'right' ? 'ql-active' : ''"
46
+            class="iconfont icon-youduiqi"
47
+            data-name="align"
48
+            data-value="right"
49
+          ></view>
50
+          <view
51
+            :class="formats.align === 'justify' ? 'ql-active' : ''"
52
+            class="iconfont icon-zuoyouduiqi"
53
+            data-name="align"
54
+            data-value="justify"
55
+          ></view>
56
+          <view
57
+            :class="formats.lineHeight ? 'ql-active' : ''"
58
+            class="iconfont icon-line-height"
59
+            data-name="lineHeight"
60
+            data-value="2"
61
+          ></view>
62
+          <view
63
+            :class="formats.letterSpacing ? 'ql-active' : ''"
64
+            class="iconfont icon-Character-Spacing"
65
+            data-name="letterSpacing"
66
+            data-value="2em"
67
+          ></view>
68
+          <view
69
+            :class="formats.marginTop ? 'ql-active' : ''"
70
+            class="iconfont icon-722bianjiqi_duanqianju"
71
+            data-name="marginTop"
72
+            data-value="20px"
73
+          ></view>
74
+          <view
75
+            :class="formats.previewarginBottom ? 'ql-active' : ''"
76
+            class="iconfont icon-723bianjiqi_duanhouju"
77
+            data-name="marginBottom"
78
+            data-value="20px"
79
+          ></view>
80
+          <view class="iconfont icon-clearedformat" @tap="removeFormat"></view>
81
+          <view
82
+            :class="formats.fontFamily ? 'ql-active' : ''"
83
+            class="iconfont icon-font"
84
+            data-name="fontFamily"
85
+            data-value="Pacifico"
86
+          ></view>
87
+          <view
88
+            :class="formats.fontSize === '24px' ? 'ql-active' : ''"
89
+            class="iconfont icon-fontsize"
90
+            data-name="fontSize"
91
+            data-value="24px"
92
+          ></view>
93
+
94
+          <view
95
+            :class="formats.color === '#0000ff' ? 'ql-active' : ''"
96
+            class="iconfont icon-text_color"
97
+            data-name="color"
98
+            data-value="#0000ff"
99
+          ></view>
100
+          <view
101
+            :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
102
+            class="iconfont icon-fontbgcolor"
103
+            data-name="backgroundColor"
104
+            data-value="#00ff00"
105
+          ></view>
106
+          <view class="iconfont icon-date" @tap="insertDate"></view>
107
+          <view
108
+            class="iconfont icon--checklist"
109
+            data-name="list"
110
+            data-value="check"
111
+          ></view>
112
+          <view
113
+            :class="formats.list === 'ordered' ? 'ql-active' : ''"
114
+            class="iconfont icon-youxupailie"
115
+            data-name="list"
116
+            data-value="ordered"
117
+          ></view>
118
+          <view
119
+            :class="formats.list === 'bullet' ? 'ql-active' : ''"
120
+            class="iconfont icon-wuxupailie"
121
+            data-name="list"
122
+            data-value="bullet"
123
+          ></view>
124
+          <view class="iconfont icon-undo" @tap="undo"></view>
125
+          <view class="iconfont icon-redo" @tap="redo"></view>
126
+          <view
127
+            class="iconfont icon-outdent"
128
+            data-name="indent"
129
+            data-value="-1"
130
+          ></view>
131
+          <view
132
+            class="iconfont icon-indent"
133
+            data-name="indent"
134
+            data-value="+1"
135
+          ></view>
136
+          <view class="iconfont icon-fengexian" @tap="insertDivider"></view>
137
+          <view class="iconfont icon-charutupian" @tap="insertImage"></view>
138
+          <view
139
+            :class="formats.header === 1 ? 'ql-active' : ''"
140
+            class="iconfont icon-format-header-1"
141
+            data-name="header"
142
+            :data-value="1"
143
+          ></view>
144
+          <view
145
+            :class="formats.script === 'sub' ? 'ql-active' : ''"
146
+            class="iconfont icon-zitixiabiao"
147
+            data-name="script"
148
+            data-value="sub"
149
+          ></view>
150
+          <view
151
+            :class="formats.script === 'super' ? 'ql-active' : ''"
152
+            class="iconfont icon-zitishangbiao"
153
+            data-name="script"
154
+            data-value="super"
155
+          ></view>
156
+          <view class="iconfont icon-shanchu" @tap="clear"></view>
157
+          <view
158
+            :class="formats.direction === 'rtl' ? 'ql-active' : ''"
159
+            class="iconfont icon-direction-rtl"
160
+            data-name="direction"
161
+            data-value="rtl"
162
+          ></view>
163
+        </view>
164
+        <view class="editor-wrapper">
165
+          <editor
166
+            id="editor"
167
+            class="ql-container"
168
+            :placeholder="placeholder"
169
+            @statuschange="onStatusChange"
170
+            :show-img-resize="true"
171
+            @ready="onEditorReady"
172
+            @input="getCtx"
173
+          ></editor>
174
+        </view>
175
+      </view>
176
+    </view>
177
+  </view>
178
+</template>
179
+
180
+<script>
181
+import { baseURL, phpToken } from "@/commit/requestPhp.js";
182
+export default {
183
+  props: {
184
+    value: {
185
+      type: String,
186
+      default: "",
187
+    },
188
+	placeholder:{
189
+		type:String,
190
+		default:"开始输入..."
191
+	},
192
+	readOnly:{
193
+		type:Boolean,
194
+		default:false
195
+	
196
+	}
197
+  },
198
+  data() {
199
+    return {
200
+      richText: "",
201
+      formats: {},
202
+    };
203
+  },
204
+
205
+  methods: {
206
+    readOnlyChange() {
207
+      this.readOnly = !this.readOnly;
208
+    },
209
+    onEditorReady() {
210
+      // 富文本节点渲染完成
211
+      const query = uni.createSelectorQuery().in(this);
212
+      query
213
+        .select("#editor")
214
+        .context((res) => {
215
+          this.editorCtx = res.context;
216
+          if (this.value) {
217
+            this.editorCtx.setContents({
218
+              html: this.value,
219
+            });
220
+          }
221
+        })
222
+        .exec(this);
223
+    },
224
+    // 失去焦点时,获取富文本的内容
225
+    getCtx(e) {
226
+      this.richText = e.detail.html;
227
+      this.$emit("input", e.detail.html);
228
+    },
229
+    undo() {
230
+      this.editorCtx.undo();
231
+    },
232
+    redo() {
233
+      this.editorCtx.redo();
234
+    },
235
+    format(e) {
236
+      let { name, value } = e.target.dataset;
237
+      if (!name) return;
238
+      // console.log('format', name, value)
239
+      this.editorCtx.format(name, value);
240
+    },
241
+    onStatusChange(e) {
242
+      const formats = e.detail;
243
+      this.formats = formats;
244
+    },
245
+    insertDivider() {
246
+      this.editorCtx.insertDivider({
247
+        success: function () {
248
+          console.log("insert divider success");
249
+        },
250
+      });
251
+    },
252
+    clear() {
253
+      this.editorCtx.clear({
254
+        success: function (res) {
255
+          console.log("clear success");
256
+        },
257
+      });
258
+    },
259
+    removeFormat() {
260
+      this.editorCtx.removeFormat();
261
+    },
262
+    insertDate() {
263
+      const date = new Date();
264
+      const formatDate = `${date.getFullYear()}/${
265
+        date.getMonth() + 1
266
+      }/${date.getDate()}`;
267
+      this.editorCtx.insertText({
268
+        text: formatDate,
269
+      });
270
+    },
271
+    insertImage() {
272
+      var that = this;
273
+      uni.chooseImage({
274
+        async success(res) {
275
+          console.log("选择图片成功");
276
+          console.log(res);
277
+          const tempFiles = res.tempFiles;
278
+          const data = await that.uploadFileData(tempFiles[0]);
279
+          that.editorCtx.insertImage({
280
+            width: "50%",
281
+            height: "20%",
282
+            src: data,
283
+          });
284
+        },
285
+      });
286
+    },
287
+    async uploadFileData(files) {
288
+      uni.showLoading({
289
+        title: "上传中",
290
+        mask: true,
291
+      });
292
+      const uploadUrl = `${baseURL}/mer/upload/image/0/file`;
293
+      return new Promise((resolve, reject) => {
294
+        uni.uploadFile({
295
+          url: uploadUrl,
296
+          file: files,
297
+          name: "file",
298
+          header: {
299
+            "X-Token": phpToken,
300
+            // 'Content-Type': "application/json;charset=UTF-8",
301
+          },
302
+          success: (res) => {
303
+            const data = JSON.parse(res.data);
304
+            if (data.status === 200) {
305
+              uni.showToast({
306
+                title: "上传成功",
307
+                icon: "success",
308
+              });
309
+              this.paths = data.data.src || "";
310
+            } else {
311
+              uni.showToast({
312
+                title: "上传失败",
313
+                icon: "error",
314
+              });
315
+              this.paths = "";
316
+            }
317
+            resolve(this.paths);
318
+            console.log("上传成功", JSON.parse(res.data));
319
+            uni.hideLoading();
320
+            uni.hideToast();
321
+          },
322
+          fail: (err) => {
323
+            console.error("上传失败", err);
324
+            this.paths = "";
325
+            resolve(this.paths);
326
+            uni.hideLoading();
327
+            uni.hideToast();
328
+          },
329
+          complete: () => {
330
+            uni.hideLoading();
331
+            uni.hideToast();
332
+          },
333
+        });
334
+      });
335
+    },
336
+  },
337
+};
338
+</script>
339
+
340
+<style>
341
+@import "./editor-icon.css";
342
+
343
+.page-body {
344
+  height: calc(100vh - var(--window-top) - var(--status-bar-height));
345
+}
346
+
347
+.wrapper {
348
+  height: 100%;
349
+}
350
+
351
+.editor-wrapper {
352
+  height: calc(100vh - var(--window-top) - var(--status-bar-height) - 140px);
353
+  background: #eee;
354
+}
355
+
356
+.iconfont {
357
+  display: inline-block;
358
+  padding: 8px 8px;
359
+  width: 24px;
360
+  height: 24px;
361
+  cursor: pointer;
362
+  font-size: 20px;
363
+}
364
+
365
+.toolbar {
366
+  box-sizing: border-box;
367
+  border-bottom: 0;
368
+  font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
369
+}
370
+
371
+.ql-container {
372
+  box-sizing: border-box;
373
+  padding: 12px 15px;
374
+  width: 100%;
375
+  min-height: 30vh;
376
+  height: 100%;
377
+  margin-top: 20px;
378
+  font-size: 16px;
379
+  line-height: 1.5;
380
+}
381
+
382
+.ql-active {
383
+  color: #06c;
384
+}
385
+</style>

+ 1 - 0
package.json

@@ -1,6 +1,7 @@
1 1
 {
2 2
   "dependencies": {
3 3
     "@dcloudio/uni-ui": "^1.5.7",
4
+    "@wangeditor/editor-for-vue": "^1.0.2",
4 5
     "base-64": "^1.0.0",
5 6
     "crypto-js": "^4.1.1",
6 7
     "easemob-websdk": "^4.6.0",

+ 44 - 0
pages/onlineproductselection/components/listCard/ActionButtons.vue

@@ -0,0 +1,44 @@
1
+<template>
2
+  <view class="action-buttons">
3
+    <view class="checkbox-item">
4
+      <checkbox v-model="isAllChecked" />全选
5
+    </view>
6
+    <button type="primary" size="mini" @click="cancelAllChecked">取消全选</button>
7
+    <button type="default" size="mini" @click="batchAddToManagement">批量加入商品管理</button>
8
+  </view>
9
+</template>
10
+
11
+<script>
12
+export default {
13
+  data() {
14
+    return {
15
+      isAllChecked: false
16
+    };
17
+  },
18
+  methods: {
19
+    cancelAllChecked() {
20
+      this.isAllChecked = false;
21
+      // 这里实现取消全选的逻辑,例如取消所有商品的选中状态
22
+      console.log('取消全选');
23
+    },
24
+    batchAddToManagement() {
25
+      // 这里实现批量加入商品管理的逻辑,例如提交选中的商品数据
26
+      console.log('执行批量加入商品管理');
27
+    }
28
+  }
29
+};
30
+</script>
31
+
32
+<style lang="scss">
33
+.action-buttons {
34
+  display: flex;
35
+  align-items: center;
36
+  margin-bottom: 20rpx;
37
+}
38
+.checkbox-item {
39
+  display: flex;
40
+  align-items: center;
41
+  margin-right: 20rpx;
42
+  font-size: 28rpx;
43
+}
44
+</style>

File diff suppressed because it is too large
+ 269 - 0
pages/onlineproductselection/components/listCard/CategoryNav.vue


+ 128 - 0
pages/onlineproductselection/components/listCard/ProductCardOnline.vue

@@ -0,0 +1,128 @@
1
+<template>
2
+  <view class="product-card">
3
+    <view class="product-cover">
4
+      <image
5
+        :src="product.image"
6
+        @error="handleError"
7
+        mode="scaleToFill"
8
+        class="product-img"
9
+        v-if="!isError && product.image"
10
+      />
11
+      <image
12
+        src="/static/images/new_ui/kongtu.png"
13
+        mode="scaleToFill"
14
+        class="product-img"
15
+        v-else
16
+      />
17
+
18
+      <!-- <checkbox v-model="product.isChecked" class="product-checkbox" /> -->
19
+    </view>
20
+    <view class="product-info">
21
+      <view class="product-title">{{ product.title }}</view>
22
+      <view class="product-price">
23
+        销售价:{{ product.salePrice }} 
24
+      </view>
25
+       <view class="product-price">
26
+        养老金:{{ product.pension }}
27
+      </view>
28
+      <view class="product-profit">创客利润:{{ product.creatorProfit }}</view>
29
+      <view class="product-status">状态:{{ product.status }}</view>
30
+      <view class="product-desc">{{ product.description }}</view>
31
+      <view class="product-actions">
32
+        <button type="primary"  @click="viewDetails">商品详情</button>
33
+        <button type="primary" class="isjiaru"  @click="addToInventory " >加入商品库</button>
34
+      </view>
35
+    </view>
36
+  </view>
37
+</template>
38
+
39
+<script>
40
+export default {
41
+  props: {
42
+    product: {
43
+      type: Object,
44
+      required: true,
45
+    },
46
+  },
47
+  data() {
48
+    return {
49
+      isError: false,
50
+    };
51
+  },
52
+  methods: {
53
+    handleError() {
54
+      this.isError = true;
55
+    },
56
+    viewDetails() {
57
+      // 这里实现查看商品详情的逻辑,例如跳转页面
58
+      console.log("查看商品详情", this.product);
59
+    },
60
+    addToInventory() {
61
+      // 这里实现加入商品库的逻辑,例如提交数据
62
+      console.log("加入商品库", this.product);
63
+    },
64
+  },
65
+};
66
+</script>
67
+
68
+<style lang="scss">
69
+.product-card {
70
+  width: calc(100% - 52rpx);
71
+  background-color: #fff;
72
+  border-radius: 10rpx;
73
+  box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
74
+  margin-bottom: 20rpx;
75
+  overflow: hidden;
76
+  border: 2rpx solid red;
77
+}
78
+.product-cover {
79
+  position: relative;
80
+  height: 210rpx;
81
+  image {
82
+    width: 100%;
83
+    height: 100%;
84
+  }
85
+}
86
+
87
+.product-checkbox {
88
+  position: absolute;
89
+  top: 10rpx;
90
+  right: 10rpx;
91
+}
92
+.product-info {
93
+  padding: 15rpx;
94
+}
95
+.product-title {
96
+  font-size: 28rpx;
97
+  margin-bottom: 10rpx;
98
+}
99
+.product-price,
100
+.product-profit,
101
+.product-status {
102
+  font-size: 24rpx;
103
+  color: #666;
104
+  margin-bottom: 5rpx;
105
+}
106
+.product-desc {
107
+  font-size: 22rpx;
108
+  color: #999;
109
+  margin-bottom: 10rpx;
110
+}
111
+.product-actions  {
112
+  margin-right: 5rpx;
113
+  width: 100%;
114
+  display: flex;
115
+  gap: 26rpx;
116
+  button{
117
+//    width: 45%;
118
+   width: 100%;
119
+   background: #007aff;
120
+  color: white;
121
+
122
+  }
123
+  .isjiaru{
124
+    background: transparent;
125
+    color: #ff3c00;
126
+  }
127
+}
128
+</style>

+ 231 - 0
pages/onlineproductselection/components/listCard/Statistics.vue

@@ -0,0 +1,231 @@
1
+<template>
2
+  <view class="content" @touchmove.prevent>
3
+    <view class="nav">
4
+      <!-- 选项卡水平方向滑动,scroll-with-animation是滑动到下一个选项时,有一个延时效果 -->
5
+      <scroll-view
6
+        @touchmove.stop
7
+        class="tab-scroll"
8
+        scroll-x="true"
9
+        scroll-with-animation
10
+        :scroll-left="scrollLeft"
11
+      >
12
+        <view class="tab-scroll_box">
13
+          <!-- 选项卡类别列表 -->
14
+          <view
15
+            class="tab-scroll_item"
16
+            v-for="(item, index) in category"
17
+            :key="index"
18
+            :class="{ active: isActive == index }"
19
+            @click="chenked(index, item)"
20
+          >
21
+            <text>{{ item.title }}</text
22
+            ><text v-if="item.count > 0">({{ item.count || 0 }})</text>
23
+          </view>
24
+        </view>
25
+      </scroll-view>
26
+    </view>
27
+  </view>
28
+</template>
29
+<script>
30
+export default {
31
+  props: {
32
+    category: {
33
+      type: Array,
34
+      default: () => [
35
+        {
36
+          type: 0,
37
+          title: "全部",
38
+          count: 0,
39
+        },
40
+        {
41
+          type: 1,
42
+          title: "已加入商品管理",
43
+          count: 0,
44
+        },
45
+        {
46
+          type: 2,
47
+          title: "未加入商品管理",
48
+          count: 0,
49
+        },
50
+      ],
51
+    },
52
+    isShowNumber: {
53
+      type: Boolean,
54
+      default: true,
55
+    },
56
+    curruntActive: {
57
+      type: [Number, String],
58
+      default: 0,
59
+    },
60
+  },
61
+  watch: {
62
+    // swiper与上面选项卡联动
63
+    currentindex(newval) {
64
+      this.isActive = newval;
65
+      this.scrollLeft = 0;
66
+      // 滑动swiper后,每个选项距离其父元素最左侧的距离
67
+      for (let i = 0; i < newval - 1; i++) {
68
+        this.scrollLeft += this.category[i].width;
69
+      }
70
+    },
71
+  },
72
+  data() {
73
+    return {
74
+      isActive: 0,
75
+      index: 0,
76
+      currentindex: 0,
77
+      // category:[
78
+      // 	{
79
+      // 		id:1,
80
+      // 		name:'选项',
81
+      // 	},
82
+      // 	{
83
+      // 		id:2,
84
+      // 		name:'选项卡二',
85
+      // 	},
86
+      // 	{
87
+      // 		id:3,
88
+      // 		name:'选项卡三',
89
+      // 	},
90
+      // 	{
91
+      // 		id:4,
92
+      // 		name:'选项卡四',
93
+      // 	},
94
+      // 	{
95
+      // 		id:5,
96
+      // 		name:'选项卡五',
97
+      // 	},
98
+      // 	{
99
+      // 		id:6,
100
+      // 		name:'选项卡六',
101
+      // 	},
102
+      // ],
103
+      contentScrollW: 0, // 导航区宽度
104
+      scrollLeft: 0, // 横向滚动条位置
105
+      fullHeight: "",
106
+    };
107
+  },
108
+  mounted() {
109
+    var that = this;
110
+    this.isActive = this.curruntActive;
111
+    //获取手机屏幕的高度,让其等于swiper的高度,从而使屏幕充满
112
+    uni.getSystemInfo({
113
+      success: function (res) {
114
+        that.fullHeight = "height:" + res.windowHeight + "px";
115
+      },
116
+    });
117
+    // 获取标题区域宽度,和每个子元素节点的宽度
118
+    this.getScrollW();
119
+  },
120
+  methods: {
121
+    // 获取标题区域宽度,和每个子元素节点的宽度以及元素距离左边栏的距离
122
+    getScrollW() {
123
+      const query = uni.createSelectorQuery().in(this);
124
+      query
125
+        .select(".tab-scroll")
126
+        .boundingClientRect((data) => {
127
+          this.contentScrollW = data.width;
128
+        })
129
+        .exec();
130
+      query
131
+        .selectAll(".tab-scroll_item")
132
+        .boundingClientRect((data) => {
133
+          let dataLen = data.length;
134
+          for (let i = 0; i < dataLen; i++) {
135
+            this.category[i].left = data[i].left;
136
+            this.category[i].width = data[i].width;
137
+          }
138
+        })
139
+        .exec();
140
+    },
141
+    // 当前点击子元素靠左留一个选项展示,子元素宽度不相同也可实现
142
+    chenked(index, item) {
143
+      this.isActive = index;
144
+      this.scrollLeft = 0;
145
+      for (let i = 0; i < index - 1; i++) {
146
+        this.scrollLeft += this.category[i].width;
147
+      }
148
+      this.$emit("tabs", { index, item });
149
+    },
150
+    // swiper滑动时,获取其索引,也就是第几个
151
+    change(e) {
152
+      const { current } = e.detail;
153
+      this.currentindex = current;
154
+    },
155
+  },
156
+};
157
+</script>
158
+<style lang="scss">
159
+.content {
160
+  display: flex;
161
+  flex-direction: column;
162
+  width: 100%;
163
+  flex: 1;
164
+  .nav {
165
+    background-color: #fff;
166
+    // position: fixed;
167
+    z-index: 99;
168
+    width: 100%;
169
+    align-items: center;
170
+    // min-height: 40rpx;
171
+    .tab-scroll {
172
+      flex: 1;
173
+      overflow: hidden;
174
+      box-sizing: border-box;
175
+      padding-left: 30rpx;
176
+      padding-right: 30rpx;
177
+      .tab-scroll_box {
178
+        display: flex;
179
+        align-items: center;
180
+        flex-wrap: nowrap;
181
+        box-sizing: border-box;
182
+        // height: 100rpx;
183
+        .tab-scroll_item {
184
+          line-height: 80rpx;
185
+          margin-right: 35rpx;
186
+          flex-shrink: 0;
187
+          display: flex;
188
+          justify-content: center;
189
+          font-size: 30rpx;
190
+          padding-top: 10rpx;
191
+          color: #333;
192
+        }
193
+      }
194
+    }
195
+  }
196
+  .swiper-content {
197
+    padding-top: 120rpx;
198
+    // flex: 1;
199
+    .swiperitem-content {
200
+      background-color: #ffffff;
201
+      .nav_item {
202
+        background-color: #ffffff;
203
+        padding: 20rpx 40rpx 0rpx 40rpx;
204
+      }
205
+    }
206
+  }
207
+}
208
+.active {
209
+  position: relative;
210
+  color: #1f7cff !important;
211
+  font-weight: 600;
212
+}
213
+.active::after {
214
+  content: "";
215
+  position: absolute;
216
+  width: 50rpx;
217
+  height: 8rpx;
218
+  background-color: #1f7cff;
219
+  left: 0px;
220
+  right: 0px;
221
+  bottom: 0px;
222
+  margin: auto;
223
+  border-radius: 8rpx;
224
+  margin-top: 10rpx;
225
+  transition: all 0.3s;
226
+}
227
+/* 隐藏滚动条,但依旧具备可以滚动的功能 */
228
+/deep/.uni-scroll-view::-webkit-scrollbar {
229
+  display: none;
230
+}
231
+</style>

+ 141 - 44
pages/onlineproductselection/onlineproductselection.vue

@@ -5,54 +5,124 @@
5 5
         headerTitle="在线选品列表"
6 6
         @headerJump="headerJump"
7 7
       ></headersVue>
8
-	      <view class="headers-box">
9
-      <searchVue
10
-        placeholder="请输入商品名称"
11
-        @search="onHandleSearch"
12
-      ></searchVue>
13
-      <!-- <tablist
14
-        class="mt"
15
-        :category="category"
16
-        :curruntActive="curruntActive"
17
-        @tabs="tabsHandleClick"
18
-      ></tablist> -->
8
+      <view class="headers-box">
9
+        <searchVue
10
+          placeholder="请输入商品名称"
11
+          @search="onHandleSearch"
12
+        ></searchVue>
13
+        <Statistics />
14
+        <CategoryNav />
15
+        <!-- <ActionButtons class="mt"/> -->
16
+        <!-- <view class="sort-buttons">
17
+      <button type="default" size="mini" @click="sortByPrice">{{ sortBy === 'price'? '销售价 ▲' : '销售价 ▼' }}</button>
18
+      <button type="default" size="mini" @click="sortByProfit">{{ sortBy === 'profit'? '创客利润 ▲' : '创客利润 ▼' }}</button>
19
+    </view> -->
20
+      </view>
19 21
     </view>
22
+    <view class="online-content">
23
+      <view class="product-list">
24
+        <ProductCard
25
+          :product="product"
26
+          v-for="(product, index) in filteredProducts"
27
+          :key="index"
28
+        />
29
+      </view>
30
+      <view
31
+        class=""
32
+        style="margin: 60rpx 0 30rpx 0; color: #999999; font-size: 26rpx"
33
+      >
34
+        —— 已经到最底部了哦 ——
35
+      </view>
20 36
     </view>
21
-	<view class="online-content">
22
-		<button @click="goJump">详情</button>
23
-	</view>
24 37
   </view>
25 38
 </template>
26 39
 
27 40
 <script>
28 41
 import headersVue from "../../shop/components/headers/headers.vue";
29 42
 import searchVue from "../../shop/commodity/components/search/search.vue";
43
+import CategoryNav from "./components/listCard/CategoryNav.vue";
44
+import Statistics from "./components/listCard/Statistics.vue";
45
+import ActionButtons from "./components/listCard/ActionButtons.vue";
46
+import ProductCard from "./components/listCard/ProductCardOnline.vue";
30 47
 export default {
31 48
   components: {
32 49
     headersVue,
33
-	searchVue
50
+    searchVue,
51
+    CategoryNav,
52
+    Statistics,
53
+    ActionButtons,
54
+    ProductCard,
34 55
   },
35 56
   data() {
36 57
     return {
37
-		   curruntActive: 0,
58
+      curruntActive: 0,
38 59
       category: [],
39
-	};
60
+      list: [],
61
+      page: 1,
62
+      limit: 10,
63
+      tihsi: "",
64
+      is_panudn: true,
65
+      products: [
66
+        {
67
+          imageUrl: "商品图片地址1",
68
+          title: "DT15100心相印茶语丝享100抽*20包三层塑装纸面巾",
69
+          salePrice: 43.06,
70
+          pension: 0.0,
71
+          creatorProfit: 7.17,
72
+          status: "正常",
73
+          description: "心相印经典纸巾产品",
74
+          isChecked: false,
75
+        },
76
+        {
77
+          imageUrl: "商品图片地址2",
78
+          title: "测试商品 请勿下单",
79
+          salePrice: 72.88,
80
+          pension: 0.0,
81
+          creatorProfit: 12.14,
82
+          status: "正常",
83
+          description: "伊利测试商品 不会发货",
84
+          isChecked: false,
85
+        },
86
+      ],
87
+      totalCount: 2,
88
+      joinedCount: 0,
89
+      notJoinedCount: 2,
90
+      sortBy: "",
91
+      searchValue: "",
92
+    };
93
+  },
94
+  methods: {
95
+    headerJump() {
96
+      uni.switchTab({ url: "../../shop/productManagementModule/index" });
97
+    },
98
+    goJump() {
99
+      console.log("fff");
100
+      uni.redirectTo({ url: "./onlineDetail" });
101
+    },
102
+    onHandleSearch(val) {
103
+      console.log(val);
104
+    },
105
+    tabsHandleClick() {},
106
+    sortByPrice() {
107
+      this.sortBy = this.sortBy === "price" ? "" : "price";
108
+    },
109
+    sortByProfit() {
110
+      this.sortBy = this.sortBy === "profit" ? "" : "profit";
111
+    },
112
+  },
113
+  computed: {
114
+    filteredProducts() {
115
+      let filtered = this.products.filter((product) => {
116
+        return product.title.includes(this.searchValue);
117
+      });
118
+      if (this.sortBy === "price") {
119
+        return filtered.sort((a, b) => a.salePrice - b.salePrice);
120
+      } else if (this.sortBy === "profit") {
121
+        return filtered.sort((a, b) => a.creatorProfit - b.creatorProfit);
122
+      }
123
+      return filtered;
124
+    },
40 125
   },
41
-  methods:{
42
-	headerJump(){
43
-		uni.switchTab({ url: '../../shop/productManagementModule/index' })
44
-	},
45
-	goJump(){
46
-		console.log("fff")
47
-		uni.redirectTo({ url: './onlineDetail' })
48
-	},
49
-	onHandleSearch(val){
50
-
51
-	},
52
-	tabsHandleClick(){
53
-
54
-	}
55
-  }
56 126
 };
57 127
 </script>
58 128
 <style>
@@ -62,17 +132,44 @@ page {
62 132
 }
63 133
 </style>
64 134
 <style lang="scss" scoped>
65
-.online-class-wrapper{
66
-	height: 100%;
67
-	.headers{
68
-		border: 2rpx solid red;
69
-		width: 100%;
70
-		height: 100rpx;
71
-		box-sizing: border-box;
72
-
73
-	}
74
-	.online-content{
75
-
76
-	}
135
+.online-class-wrapper {
136
+  height: 100%;
137
+  .headers {
138
+    width: 100%;
139
+    box-sizing: border-box;
140
+    .headers-box {
141
+      margin-top: 95rpx;
142
+      position: fixed;
143
+      top: 0rpx;
144
+      z-index: 666;
145
+    }
146
+  }
147
+  .online-content {
148
+	display: flex;
149
+	flex-direction: column;
150
+	align-items: center;
151
+    margin-top: 450rpx;
152
+    box-sizing: border-box;
153
+    padding-bottom: 60rpx;
154
+	width: 100%;
155
+    .product-list {
156
+	width: 100%;
157
+	display: flex;
158
+	flex-direction: column;
159
+	align-items: center;
160
+    }
161
+  }
162
+}
163
+.mt {
164
+  margin-top: 30rpx;
165
+  padding: 0 15rpx;
166
+}
167
+.sort-buttons {
168
+  display: flex;
169
+  //   align-items: center;
170
+  margin-bottom: 20rpx;
171
+}
172
+.sort-buttons button {
173
+  margin-right: 10rpx;
77 174
 }
78 175
 </style>

+ 7 - 1
shop/commodity/components/search/search.vue

@@ -6,7 +6,7 @@
6 6
 		          referrerpolicy="no-referrer"
7 7
 		          src="/static/images/new_ui/search_icon_blod.png"
8 8
 		        />
9
-				<input class="search__input" v-model="searchValue" @input="haandleSearchInput" type="text" placeholder="请输入订单号/收货人/联系方式/提货码" />
9
+				<input class="search__input" v-model="searchValue" @input="haandleSearchInput" type="text" :placeholder="placeholder" />
10 10
 		        <button class="search__btn" @click="haandleSearch">
11 11
 		          搜索
12 12
 		        </button>
@@ -16,6 +16,12 @@
16 16
 
17 17
 <script>
18 18
 	export default {
19
+		props: {
20
+			placeholder: {
21
+				type: String,
22
+				default: "请输入订单号/收货人/联系方式/提货码",
23
+			},
24
+		},
19 25
 		data() {
20 26
 			return {
21 27
 				searchValue:""

+ 455 - 51
shop/productManagementModule/addProduct.vue

@@ -7,23 +7,19 @@
7 7
       </view>
8 8
     </view>
9 9
     <view class="content-wrrapper">
10
-      <uploadVue formItemTitle="商品封面" :maxLimit="1"></uploadVue>
11
-      <uploadVue formItemTitle="商品轮播图"></uploadVue>
12
-      <view class="business-box">
13
-        <view class="business-box-txt">商品详情 </view>
14
-        <view class="business-box--arae">
15
-          <textarea
16
-            @blur="bindTextAreaBlur"
17
-            :maxlength="-1"
18
-            auto-height
19
-            class="araebox"
20
-          />
21
-        </view>
22
-        <uploadVue :maxLimit="200" class="upload-boxx"></uploadVue>
23
-      </view>
10
+      <uploadVue
11
+        formItemTitle="商品封面"
12
+        :maxLimit="1"
13
+        @changeFiles="changeFilesfengmian"
14
+      ></uploadVue>
15
+      <uploadVue
16
+        formItemTitle="商品轮播图"
17
+        @changeFiles="changeFileslunbotu"
18
+      ></uploadVue>
19
+
24 20
       <view class="form-box">
25 21
         <view class="form-item-box">
26
-          <view class="form-box-txt mustBox">商户商品分类</view>
22
+          <view class="form-box-txt">商户商品分类</view>
27 23
           <view
28 24
             class="form-box--arae"
29 25
             @click="handleClick"
@@ -33,7 +29,23 @@
33 29
           </view>
34 30
           <uni-icons
35 31
             type="right"
36
-            size="30"
32
+            size="26"
33
+            class="icon-boxx"
34
+            color="#333"
35
+          ></uni-icons>
36
+        </view>
37
+        <view class="form-item-box">
38
+          <view class="form-box-txt">平台商品分类</view>
39
+          <view
40
+            class="form-box--arae"
41
+            @click="handleClickpingtai"
42
+            :class="[pingtaifeilei.name == '' ? 'classification' : '']"
43
+          >
44
+            {{ pingtaifeilei.name || "平台商品分类" }}
45
+          </view>
46
+          <uni-icons
47
+            type="right"
48
+            size="26"
37 49
             class="icon-boxx"
38 50
             color="#333"
39 51
           ></uni-icons>
@@ -43,52 +55,99 @@
43 55
           <view class="form-box--arae">
44 56
             <input
45 57
               type="text"
46
-              v-model="formData.businessName"
58
+              v-model="formData.store_name"
47 59
               placeholder="请输入商品名称"
60
+              :adjust-position="false"
48 61
               class="placeholder-class"
49 62
             />
50 63
           </view>
51 64
         </view>
52
-      </view>
53
-      <view class="form-box mr-t">
54 65
         <view class="form-item-box">
55
-          <view class="form-box-txt mustBox">商品售价</view>
66
+          <view class="form-box-txt mustBox">商品单位</view>
56 67
           <view class="form-box--arae">
57 68
             <input
58 69
               type="text"
59
-              v-model="formData.businessPrice"
60
-              placeholder="请输入商品售价"
70
+              v-model="formData.unit_name"
71
+              placeholder="请输入单位"
72
+              :adjust-position="false"
61 73
               class="placeholder-class"
62 74
             />
63 75
           </view>
64 76
         </view>
65 77
         <view class="form-item-box">
66
-          <view class="form-box-txt mustBox">商品原价</view>
78
+          <view class="form-box-txt">品牌选择</view>
67 79
           <view class="form-box--arae">
68
-            <input
69
-              type="text"
70
-              v-model="formData.businessOriginalPrice"
71
-              placeholder="请输入商品原价"
72
-              class="placeholder-class"
73
-            />
80
+            <uni-data-select
81
+              v-model="formData.brand_id"
82
+              placeholder="请选择品牌"
83
+              :localdata="brandData"
84
+              :clear="true"
85
+            ></uni-data-select>
74 86
           </view>
75 87
         </view>
76 88
         <view class="form-item-box">
77
-          <view class="form-box-txt mustBox">商品库存</view>
89
+          <view class="form-box-txt mustBox">运费模板</view>
78 90
           <view class="form-box--arae">
79
-            <input
80
-              type="text"
81
-              v-model="formData.businessStock"
82
-              placeholder="请输入商品库存"
83
-              class="placeholder-class"
84
-            />
91
+            <uni-data-select
92
+              v-model="formData.temp_id"
93
+              placeholder="请选择运费模板"
94
+              :localdata="shippingList"
95
+              :clear="true"
96
+            ></uni-data-select>
85 97
           </view>
86 98
         </view>
99
+        <view v-if="shippingList.length == 0" class="tips-messge">运费模板为空,请在后台添加运费模板</view>
100
+      </view>
101
+      <view
102
+        class="form-box mr-t"
103
+        v-for="(item, index) in attrValue"
104
+        :key="index"
105
+      >
106
+        <view
107
+          class="form-item-box"
108
+          v-for="(its, idx) in item.modules"
109
+          :key="idx"
110
+          v-if="its.isshow"
111
+        >
112
+          <template v-if="its.isshow">
113
+            <view class="form-box-txt" :class="[its.ismust ? 'mustBox' : '']">{{
114
+              its.title
115
+            }}</view>
116
+            <view class="form-box--arae">
117
+              <input
118
+                v-if="its.form_type == 'input'"
119
+                :type="its.type"
120
+                v-model="item.modules[idx].value"
121
+                :adjust-position="false"
122
+                :placeholder="`请输入${its.title}`"
123
+                class="placeholder-class"
124
+              />
125
+            </view>
126
+          </template>
127
+        </view>
128
+      </view>
129
+      <view class="business-box pb15">
130
+        <view class="business-box-txt">商品简介 </view>
131
+        <view class="business-box--arae">
132
+          <textarea
133
+            @blur="bindTextAreaBlur"
134
+            v-model="formData.store_info"
135
+            placeholder="输入商品简介"
136
+            :maxlength="-1"
137
+            :adjust-position="false"
138
+            auto-height
139
+            class="araebox"
140
+          />
141
+        </view>
142
+      </view>
143
+      <view class="business-box pb15">
144
+        <view class="business-box-txt">商品详情 </view>
145
+        <customEditor :value="formData.content" @input="changeTextHandle"/>
87 146
       </view>
88 147
     </view>
89 148
     <view class="btn-list">
90
-      <view class="btn quxiao"> 保存草稿 </view>
91
-      <view class="btn primary"> 发布商品 </view>
149
+      <!-- <view class="btn quxiao"> 保存草稿 </view> -->
150
+      <view class="btn primary" @click="handleSubmit"> 发布商品 </view>
92 151
     </view>
93 152
     <cascadePicker
94 153
       ref="picker"
@@ -97,6 +156,13 @@
97 156
       :data="cascadePicker.data"
98 157
       @callback="pickerCallback"
99 158
     />
159
+    <cascadePicker
160
+      ref="pickerpingtai"
161
+      :title="cascadepintaiPicker.title"
162
+      :layer="cascadepintaiPicker.layer"
163
+      :data="cascadepintaiPicker.data"
164
+      @callback="pickerCallbackpintai"
165
+    />
100 166
   </view>
101 167
 </template>
102 168
 
@@ -106,39 +172,225 @@ import headersVue from "../components/headers/headers.vue";
106 172
 import uploadVue from "./components/upload.vue";
107 173
 import cascadePicker from "../components/cascade-picker.vue";
108 174
 import { reNameKeys } from "@/utils/index";
109
-import { handleData } from "./mockDataPro";
175
+import customEditor from "@/components/editor.vue";
176
+import {
177
+  getGoodsClass,
178
+  getBrandSelect,
179
+  shippingListApi,
180
+  getGoodsClassCategory,
181
+  uploadFile,
182
+  addProduct,
183
+} from "@/commit/product.js";
110 184
 export default {
111 185
   components: {
112 186
     headersVue,
113 187
     uploadVue,
114 188
     cascadePicker,
189
+    customEditor,
115 190
   },
116 191
   data() {
117 192
     return {
118
-      cascadeData: [],
193
+      cascadeData: [], //商户商品分类
194
+      pingtaifeiData: [], //平台商品分类:
119 195
       cascadePicker: {
120
-        title: "选择分类",
196
+        title: "选择商户商品分类",
197
+        layer: null,
198
+        data: [],
199
+      },
200
+      cascadepintaiPicker: {
201
+        title: "选择平台商品分类",
121 202
         layer: null,
122 203
         data: [],
123 204
       },
124 205
       classification: {
206
+        //商户商品分类
207
+        name: "",
208
+        id: "",
209
+      },
210
+      pingtaifeilei: {
211
+        //平台商品分类:
125 212
         name: "",
126 213
         id: "",
127 214
       },
128 215
       formData: {
129
-        businessName: "", //商品名称
130
-        businessPrice: "", //商品售价
131
-        businessOriginalPrice: "", //商品原价
132
-        businessStock: "", //商品库存
216
+        store_name: "", //商品名称
217
+        mer_cate_id: [], //商户商品分类:
218
+        cate_id: "", //平台商品分类:
219
+        brand_id: "", //品牌选择:
220
+        keyword: "", //商品关键字:
221
+        unit_name: "", //单位:
222
+        store_info: "", //商品简介:
223
+        image: "", //商品封面图:
224
+        temp_id: "", //运费模板:
225
+        slider_image: [], //商品轮播图:
226
+        couponData: [], //优惠券:
227
+        content: "", //商品详情:
228
+        sort: 0, //排序:
229
+        extension_type: "0", //是否单独设置:
230
+        is_good: 0, //商品推荐:0 1
231
+        is_gift_bag: 0, //是否包邮:0 1
232
+        give_coupon_ids: [], //优惠券:
233
+        // checkboxGroup: [], //商品推荐:0单规格 1 多规格
234
+        stock_num: "", //商品库存
235
+        unit_name: "", //单位
236
+        share_num: "",
237
+        shares_num: "",
238
+        share_is: "0",
239
+        spec_type: "0",
240
+        type: 1,
241
+        unit_name: "",
242
+        attr: [],
243
+        commission: "",
244
+        pension: "",
245
+        pension_is: "0",
246
+        pension_num: "",
133 247
       },
134
-	  selectData:[]
248
+      selectData: [],
249
+      brandData: [],
250
+      shippingList: [], //运费模板
251
+      attrValue: [
252
+        {
253
+          modules: [
254
+            {
255
+              title: "商品售价",
256
+              props: "price",
257
+              value: "",
258
+              type: "digit",
259
+              ismust: true,
260
+              form_type: "input",
261
+              isshow: true,
262
+            },
263
+            {
264
+              title: "商品成本价",
265
+              value: "",
266
+              props: "cost",
267
+              type: "digit",
268
+              ismust: true,
269
+              form_type: "input",
270
+              isshow: true,
271
+            },
272
+            {
273
+              title: "商品原价",
274
+              value: "",
275
+              props: "ot_price",
276
+              type: "digit",
277
+              ismust: true,
278
+              form_type: "input",
279
+              isshow: true,
280
+            },
281
+            {
282
+              title: "商品库存",
283
+              value: "",
284
+              props: "stock",
285
+              type: "number",
286
+              ismust: true,
287
+              form_type: "input",
288
+              isshow: true,
289
+            },
290
+            {
291
+              title: "商品编号",
292
+              value: "",
293
+              props: "bar_code",
294
+              type: "text",
295
+              ismust: false,
296
+              form_type: "input",
297
+              isshow: true,
298
+            },
299
+            {
300
+              title: "重量(KG)",
301
+              value: "",
302
+              props: "weight",
303
+              type: "digit",
304
+              ismust: false,
305
+              form_type: "input",
306
+              isshow: true,
307
+            },
308
+            {
309
+              title: "商品体积(m³)",
310
+              value: "",
311
+              props: "volume",
312
+              type: "digit",
313
+              ismust: false,
314
+              form_type: "input",
315
+              isshow: true,
316
+            },
317
+            {
318
+              title: "商品图片",
319
+              value: "",
320
+              props: "image",
321
+              type: "",
322
+              ismust: false,
323
+              form_type: "file",
324
+              isshow: false,
325
+            },
326
+          ],
327
+        },
328
+      ],
135 329
     };
136 330
   },
137
-  onLoad() {},
138
-  onShow() {
139
-    this.cascadeData = reNameKeys(handleData);
331
+  async onLoad() {
332
+    await this.initData();
140 333
   },
334
+  onShow() {},
141 335
   methods: {
336
+    async initData() {
337
+      await this.getBrandSelectData();
338
+      await this.getShippingListData();
339
+      await this.getGoodsClassData();
340
+      await this.getGoodsClassCategoryData();
341
+    },
342
+    async getGoodsClassData() {
343
+      try {
344
+        const res = await getGoodsClass();
345
+        if (res.data.status == 200) {
346
+          const data = res.data.data || [];
347
+          this.cascadeData = reNameKeys(data);
348
+        }
349
+      } catch (error) {}
350
+    },
351
+    async getBrandSelectData() {
352
+      //  { value: 0, text: "篮球" },
353
+      try {
354
+        const res = await getBrandSelect();
355
+        if (res.data.status == 200) {
356
+          const data = res.data.data || [];
357
+          console.log(data, "品牌数据");
358
+          this.brandData = data.map((item) => {
359
+            return {
360
+              value: item.brand_id || "",
361
+              text: item.brand_name || "",
362
+              ...item,
363
+            };
364
+          });
365
+        }
366
+      } catch (error) {}
367
+    },
368
+    async getShippingListData() {
369
+      try {
370
+        const res = await shippingListApi();
371
+        if (res.data.status == 200) {
372
+          const data = res.data.data || [];
373
+          console.log(data, "运费模板");
374
+          this.shippingList = data.map((item) => {
375
+            return {
376
+              value: item.shipping_template_id || "",
377
+              text: item.name || "",
378
+              // ...item,
379
+            };
380
+          });
381
+        }
382
+      } catch (error) {}
383
+    },
384
+    async getGoodsClassCategoryData() {
385
+      try {
386
+        const res = await getGoodsClassCategory();
387
+        if (res.data.status == 200) {
388
+          const data = res.data.data || [];
389
+          this.pingtaiData = reNameKeys(data);
390
+        }
391
+      } catch (error) {}
392
+    },
393
+
142 394
     headerJump() {
143 395
       uni.redirectTo({
144 396
         url: "/shop/productManagementModule/productManagement",
@@ -148,8 +400,9 @@ export default {
148 400
     pickerCallback(e) {
149 401
       const { data } = e;
150 402
       const lastItem = data[data.length - 1];
151
-	  this.selectData = data || [];
403
+      this.selectData = data || [];
152 404
       console.log(lastItem, "pickerCallback回調");
405
+      this.formData.mer_cate_id.push(lastItem.id);
153 406
       this.classification = lastItem || {};
154 407
     },
155 408
     handleClick() {
@@ -159,9 +412,150 @@ export default {
159 412
         console.log("打开成功");
160 413
       });
161 414
     },
415
+    handleClickpingtai() {
416
+      //pingtai
417
+      const _this = this;
418
+      _this.cascadepintaiPicker.data = _this.pingtaiData;
419
+      _this.$refs.pickerpingtai.open().then(function () {
420
+        console.log("打开成功");
421
+      });
422
+    },
423
+    pickerCallbackpintai(e) {
424
+      const { data } = e;
425
+      const lastItem = data[data.length - 1];
426
+      this.selectData = data || [];
427
+      console.log(lastItem, "pickerCallbackpintai");
428
+      this.pingtaifeilei = lastItem || {};
429
+    },
162 430
     bindTextAreaBlur(e) {
163 431
       console.log(e.detail.value);
164 432
     },
433
+ async   handleSubmit() {
434
+      if (this.formData.image == "") {
435
+        uni.showToast({
436
+          title: "请上传商品封面图",
437
+          icon: "none",
438
+        });
439
+        return;
440
+      }
441
+      if (this.formData.slider_image.length == 0) {
442
+        uni.showToast({
443
+          title: "请上传商品轮播图",
444
+          icon: "none",
445
+        });
446
+        return;
447
+      }
448
+      if (this.formData.store_name == "") {
449
+        uni.showToast({
450
+          title: "请填写商品名称",
451
+          icon: "none",
452
+        });
453
+        return;
454
+      }
455
+      if (this.formData.unit_name == "") {
456
+        uni.showToast({
457
+          title: "请填写商品单位",
458
+          icon: "none",
459
+        });
460
+        return;
461
+      }
462
+      if(!this.formData.temp_id || this.formData.temp_id == ""){
463
+        uni.showToast({
464
+          title: "请选择运费模板",
465
+          icon: "none",
466
+        });
467
+        return
468
+
469
+      }
470
+      const dataList = (this.attrValue[0] && this.attrValue[0].modules) || [];
471
+      const dataNew = dataList.map((item, index) => {
472
+        return {
473
+          tips: dataList[index].title,
474
+          props: dataList[index].props,
475
+          isEmpty:
476
+            dataList[index].ismust &&
477
+            (dataList[index].value == "" ||
478
+              dataList[index].value == undefined ||
479
+              dataList[index].value == null),
480
+          value: dataList[index].value || "",
481
+        };
482
+      });
483
+      const data = dataNew.some((item) => item.isEmpty);
484
+      if (data) {
485
+        uni.showToast({
486
+          title: "请填写完所有必填项",
487
+          icon: "none",
488
+        });
489
+        return;
490
+      }
491
+      if (this.formData.store_info == "") {
492
+        uni.showToast({
493
+          title: "请填写商品简介",
494
+          icon: "none",
495
+        });
496
+        return;
497
+      }
498
+      let attrValueData = dataNew.map((item, index) => {
499
+        let parmas = {};
500
+        if ([item.props] == "image") {
501
+          Object.assign(parmas, { image: this.formData.image });
502
+        } else {
503
+          Object.assign(parmas, { [item.props]: item.value });
504
+        }
505
+        return parmas;
506
+      });
507
+      // 使用 reduce 方法合并数组中的对象
508
+      const mergedObject = attrValueData.reduce((acc, current) => {
509
+        return { ...acc, ...current };
510
+      }, {});
511
+
512
+      // 转换为包含合并后对象的数组
513
+      const result = [mergedObject];
514
+      const newParams = { ...this.formData, attrValue: result };
515
+      uni.showLoading({
516
+        title: "数据提交中...",
517
+      })
518
+      try {
519
+         const res = await addProduct(newParams);
520
+         if(res.data.status == 200){
521
+            uni.showToast({
522
+              title:  "数据提交成功",
523
+              icon: "none",
524
+            });
525
+            uni.redirectTo({
526
+              url:'./productManagement'
527
+            })
528
+         }else{
529
+          uni.showToast({
530
+              title:  res.data.message,
531
+              icon: "none",
532
+            });
533
+
534
+         }
535
+         uni.hideLoading();
536
+         uni.hideToast()
537
+      } catch (error) {
538
+        
539
+      }finally{
540
+        uni.hideLoading();
541
+      }
542
+    },
543
+    changeFilesfengmian(data) {
544
+      const pathsArr = data.fileList || [];
545
+      const paths = pathsArr.map((item) => item.path).join("") || "";
546
+      this.formData.image = paths;
547
+    },
548
+    changeFileslunbotu(data) {
549
+      const pathsArr = data.fileList || [];
550
+      this.formData.slider_image = [];
551
+      pathsArr.forEach((item) => {
552
+        this.formData.slider_image.push(item.path);
553
+      });
554
+    },
555
+    changeTextHandle(data){
556
+      // console.log(data)
557
+      this.formData.content = data;
558
+    }
165 559
   },
166 560
 };
167 561
 </script>
@@ -207,7 +601,7 @@ page {
207 601
     padding: 32rpx 32rpx 0;
208 602
     box-sizing: border-box;
209 603
     background-color: #ffffff;
210
-    min-height: 500rpx;
604
+    min-height: 250rpx;
211 605
     .business-box-txt {
212 606
       width: 100%;
213 607
       font-weight: 600;
@@ -324,6 +718,9 @@ page {
324 718
   color: #333333;
325 719
   text-align: right;
326 720
   padding: 0rpx 0rpx 0rpx 15rpx;
721
+  outline: none;
722
+  border: none;
723
+  background: none;
327 724
   /deep/.uni-input-placeholder {
328 725
     color: #999;
329 726
   }
@@ -331,4 +728,11 @@ page {
331 728
 .mr-t {
332 729
   margin-top: 26rpx;
333 730
 }
731
+.pb15 {
732
+  padding-bottom: 25rpx !important;
733
+}
734
+.tips-messge{
735
+  padding-bottom: 15rpx;
736
+  color: #ff6010;
737
+}
334 738
 </style>

+ 110 - 43
shop/productManagementModule/components/upload.vue

@@ -1,13 +1,15 @@
1 1
 <template>
2 2
   <view class="upload-wrapper">
3
-    <view class="upload-wrapper-text ismust" v-if="formItemTitle"> {{ formItemTitle }} </view>
3
+    <view class="upload-wrapper-text ismust" v-if="formItemTitle">
4
+      {{ formItemTitle }}
5
+    </view>
4 6
     <view class="upload-wrapper-upload">
5 7
       <view class="upload-files-content">
6 8
         <template v-if="fileListData.length > 0">
7 9
           <view
8 10
             class="upload-files"
9 11
             v-for="(item, index) in fileListData"
10
-            :key="index+item.path"
12
+            :key="index + item.path"
11 13
           >
12 14
             <view class="image-box">
13 15
               <image :src="item.path" class="show-image-box"></image>
@@ -20,7 +22,12 @@
20 22
             </view>
21 23
           </view>
22 24
         </template>
23
-        <view class="upload-no-files" :style="getUploadClass" v-if="isHidden"  @click="uploadDataFiles">
25
+        <view
26
+          class="upload-no-files"
27
+          :style="getUploadClass"
28
+          v-if="isHidden"
29
+          @click="uploadDataFiles"
30
+        >
24 31
           <image src="/static/images/new_ui/xiangji.png"></image>
25 32
           <view class="upload-no-files__text"> {{ setImageTitle }} </view>
26 33
         </view>
@@ -30,6 +37,8 @@
30 37
 </template>
31 38
 
32 39
 <script>
40
+import { uploadFile } from "@/commit/product.js";
41
+import { baseURL, phpToken } from "@/commit/requestPhp.js";
33 42
 export default {
34 43
   props: {
35 44
     maxLimit: {
@@ -52,59 +61,67 @@ export default {
52 61
       type: Boolean,
53 62
       default: false,
54 63
     },
55
-	imageTitle:{
56
-		type: String,
57
-		default: "",
58
-	},
59
-	index:{
60
-		type: Number,
61
-		default: 0,
62
-	}
64
+    imageTitle: {
65
+      type: String,
66
+      default: "",
67
+    },
68
+    index: {
69
+      type: Number,
70
+      default: 0,
71
+    },
63 72
   },
64 73
   data() {
65 74
     return {
66 75
       fileListData: [],
76
+      paths: "",
67 77
     };
68 78
   },
69 79
   methods: {
70
-	emitChangeFiles(data){
71
-		this.$emit('changeFiles',{
72
-			fileList:data || [],
73
-			index:this.index
74
-		})
75
-		// console.log(data,"emitChangeFiles")
76
-	},
80
+    emitChangeFiles(data) {
81
+      this.$emit("changeFiles", {
82
+        fileList: data || [],
83
+        index: this.index,
84
+      });
85
+      // console.log(data,"emitChangeFiles")
86
+    },
77 87
     uploadDataFiles() {
78
-		if(this.disabled || this.maxLimit == 0){
79
-			return ;
80
-		}
88
+      if (this.disabled || this.maxLimit == 0) {
89
+        return;
90
+      }
81 91
       let that = this;
82 92
       uni.chooseImage({
83
-        count:1, // 最多可以选择的图片张数,默认9
93
+        count: 1, // 最多可以选择的图片张数,默认9
84 94
         // sizeType: ["compressed"], // original 原图,compressed 压缩图,默认二者都有
85 95
         sourceType: ["album", "camera"], // album 从相册选图,camera 使用相机,默认二者都有
86
-        success: function (res) {
87
-          console.log(res, "456456465");
88
-          if (that.maxLimit.length === 0) {
89
-          	that.fileListData.push({
90
-				path:res.tempFilePaths[0],
91
-				Files:res.tempFiles[0]
92
-			})
96
+        success: async function (res) {
97
+            const tempFiles = res.tempFiles;
98
+            const tempFilePaths = res.tempFilePaths;
99
+          if (that.fileListData.length === 0) {
100
+          
101
+            const data = await that.uploadFileData(tempFiles[0]);
102
+            if (!data) {
103
+              that.fileListData = [];
104
+            } else {
105
+              that.fileListData.push({
106
+                path: data,
107
+                Files: tempFiles,
108
+              });
109
+            }
93 110
           } else if (that.fileListData.length < that.maxLimit) {
94
-          	that.fileListData = that.fileListData.concat({
95
-				path:res.tempFilePaths[0],
96
-				Files:res.tempFiles[0]
97
-			}); //concat追加到数组
111
+            const dataPath = await that.uploadFileData(tempFiles[0]);
112
+            that.fileListData = that.fileListData.concat({
113
+              path: dataPath,
114
+              Files: tempFiles,
115
+            }); //concat追加到数组
98 116
           }
99
-		   that.emitChangeFiles(that.fileListData)
100
-		//   console.log(that.fileListData,"that.fileListData")
117
+          that.emitChangeFiles(that.fileListData);
118
+          //   console.log(that.fileListData,"that.fileListData")
119
+        },
120
+        fail() {
121
+          that.emitChangeFiles([]);
101 122
         },
102
-		fail() {
103
-			  that.emitChangeFiles([])
104
-		}
105
-		
106 123
       });
107
-	 
124
+
108 125
       // let that = this;
109 126
       // that.$time.uploadPictures((res) => {
110 127
       //   console.log(res);
@@ -114,10 +131,59 @@ export default {
114 131
     onHandleDelete(item, index) {
115 132
       if (!this.disabledDetele) {
116 133
         this.fileListData.splice(index, 1);
117
-		console.log(this.fileListData,"this.fileListData")
118
-		 this.emitChangeFiles(this.fileListData)
134
+        console.log(this.fileListData, "this.fileListData");
135
+        this.emitChangeFiles(this.fileListData);
119 136
       }
120 137
     },
138
+    async uploadFileData(files) {
139
+      uni.showLoading({
140
+        title: "上传中",
141
+        mask: true,
142
+      });
143
+      const uploadUrl = `${baseURL}/mer/upload/image/0/file`;
144
+      return new Promise((resolve, reject) => {
145
+        uni.uploadFile({
146
+          url: uploadUrl,
147
+          file: files,
148
+          name: "file",
149
+          header: {
150
+            "X-Token": phpToken,
151
+            // 'Content-Type': "application/json;charset=UTF-8",
152
+          },
153
+          success: (res) => {
154
+            const data = JSON.parse(res.data);
155
+            if (data.status === 200) {
156
+              uni.showToast({
157
+                title: "上传成功",
158
+                icon: "success",
159
+              });
160
+              this.paths = data.data.src || "";
161
+            } else {
162
+              uni.showToast({
163
+                title: "上传失败",
164
+                icon: "error",
165
+              });
166
+              this.paths = "";
167
+            }
168
+            resolve(this.paths);
169
+            console.log("上传成功", JSON.parse(res.data));
170
+             uni.hideLoading();
171
+            uni.hideToast();
172
+          },
173
+          fail: (err) => {
174
+            console.error("上传失败", err);
175
+            this.paths = "";
176
+            resolve(this.paths);
177
+             uni.hideLoading();
178
+            uni.hideToast();
179
+          },
180
+          complete: () => {
181
+            uni.hideLoading();
182
+            uni.hideToast();
183
+          },
184
+        });
185
+      });
186
+    },
121 187
   },
122 188
   computed: {
123 189
     isHidden() {
@@ -128,7 +194,8 @@ export default {
128 194
         return "禁止上传图片";
129 195
       } else if (
130 196
         this.fileListData.length < this.maxLimit &&
131
-        this.fileListData.length > 0 && this.imageTitle
197
+        this.fileListData.length > 0 &&
198
+        this.imageTitle
132 199
       ) {
133 200
         return `${this.fileListData.length} / ${this.maxLimit}`;
134 201
       } else {