vue3-admin-gpt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.env.development +14 -0
  2. package/.env.production +14 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +106 -0
  5. package/README.md +104 -0
  6. package/build-zip.cjs +53 -0
  7. package/cli.js +110 -0
  8. package/jsconfig.json +9 -0
  9. package/package.json +92 -0
  10. package/public/index.html +20 -0
  11. package/public/robots.txt +2 -0
  12. package/rspack.config.js +282 -0
  13. package/rspack.js +162 -0
  14. package/src/App.vue +9 -0
  15. package/src/api/icon.js +9 -0
  16. package/src/api/router.js +9 -0
  17. package/src/api/table.js +25 -0
  18. package/src/api/tree.js +9 -0
  19. package/src/api/user.js +34 -0
  20. package/src/assets/error_images/401.png +0 -0
  21. package/src/assets/error_images/404.png +0 -0
  22. package/src/assets/error_images/cloud.png +0 -0
  23. package/src/assets/login_images/background.jpg +0 -0
  24. package/src/assets/logo.png +0 -0
  25. package/src/assets/qr_logo/lqr_logo.png +0 -0
  26. package/src/assets/vuejs-fill.svg +4 -0
  27. package/src/components/VabPageHeader/index.vue +133 -0
  28. package/src/config/index.js +7 -0
  29. package/src/config/net.config.js +20 -0
  30. package/src/config/permission.js +136 -0
  31. package/src/config/setting.config.js +62 -0
  32. package/src/config/settings.js +6 -0
  33. package/src/config/theme.config.js +14 -0
  34. package/src/layouts/EmptyLayout.vue +3 -0
  35. package/src/layouts/components/VabAppMain/index.vue +109 -0
  36. package/src/layouts/components/VabAvatar/index.vue +255 -0
  37. package/src/layouts/components/VabBreadcrumb/index.vue +61 -0
  38. package/src/layouts/components/VabFullScreen/index.vue +61 -0
  39. package/src/layouts/components/VabLogo/index.vue +94 -0
  40. package/src/layouts/components/VabNav/index.vue +176 -0
  41. package/src/layouts/components/VabSide/components/VabMenuItem.vue +80 -0
  42. package/src/layouts/components/VabSide/components/VabSideItem.vue +100 -0
  43. package/src/layouts/components/VabSide/components/VabSubmenu.vue +56 -0
  44. package/src/layouts/components/VabSide/index.vue +123 -0
  45. package/src/layouts/components/VabTabs/index.vue +500 -0
  46. package/src/layouts/components/VabTheme/index.vue +603 -0
  47. package/src/layouts/components/VabTop/index.vue +286 -0
  48. package/src/layouts/export.js +29 -0
  49. package/src/layouts/index.vue +339 -0
  50. package/src/main.js +40 -0
  51. package/src/plugins/echarts.js +4 -0
  52. package/src/plugins/index.js +44 -0
  53. package/src/plugins/support.js +16 -0
  54. package/src/router/index.js +400 -0
  55. package/src/store/index.js +26 -0
  56. package/src/store/modules/errorLog.js +27 -0
  57. package/src/store/modules/routes.js +60 -0
  58. package/src/store/modules/settings.js +73 -0
  59. package/src/store/modules/table.js +22 -0
  60. package/src/store/modules/tabsBar.js +109 -0
  61. package/src/store/modules/user.js +131 -0
  62. package/src/styles/element-variables.scss +13 -0
  63. package/src/styles/loading.scss +345 -0
  64. package/src/styles/nav-icons.scss +52 -0
  65. package/src/styles/normalize.scss +353 -0
  66. package/src/styles/spinner/dots.css +68 -0
  67. package/src/styles/spinner/gauge.css +104 -0
  68. package/src/styles/spinner/inner-circles.css +51 -0
  69. package/src/styles/spinner/plus.css +341 -0
  70. package/src/styles/themes/default.scss +1 -0
  71. package/src/styles/transition.scss +18 -0
  72. package/src/styles/vab.scss +476 -0
  73. package/src/styles/variables.scss +69 -0
  74. package/src/utils/accessToken.js +56 -0
  75. package/src/utils/eventBus.js +8 -0
  76. package/src/utils/handleRoutes.js +100 -0
  77. package/src/utils/index.js +231 -0
  78. package/src/utils/message.js +67 -0
  79. package/src/utils/pageTitle.js +11 -0
  80. package/src/utils/password.js +43 -0
  81. package/src/utils/permission.js +19 -0
  82. package/src/utils/request.js +187 -0
  83. package/src/utils/static.js +81 -0
  84. package/src/utils/vab.js +218 -0
  85. package/src/utils/validate.js +48 -0
  86. package/src/views/401.vue +302 -0
  87. package/src/views/404.vue +302 -0
  88. package/src/views/demo/index.vue +591 -0
  89. package/src/views/index/index.vue +1489 -0
  90. package/src/views/login/index.vue +456 -0
  91. package/src/views/register/index.vue +524 -0
  92. package/src/views/vab/calendar.vue +488 -0
  93. package/src/views/vab/campaign.vue +1006 -0
  94. package/src/views/vab/chart.vue +189 -0
  95. package/src/views/vab/customer.vue +666 -0
  96. package/src/views/vab/editor.vue +84 -0
  97. package/src/views/vab/form.vue +151 -0
  98. package/src/views/vab/help.vue +390 -0
  99. package/src/views/vab/icon.vue +113 -0
  100. package/src/views/vab/knowledge.vue +820 -0
  101. package/src/views/vab/nested/menu1/menu2/menu3.vue +29 -0
  102. package/src/views/vab/nested/menu1/menu2.vue +33 -0
  103. package/src/views/vab/nested/menu1.vue +33 -0
  104. package/src/views/vab/nested.vue +97 -0
  105. package/src/views/vab/notification.vue +416 -0
  106. package/src/views/vab/order.vue +507 -0
  107. package/src/views/vab/permissions.vue +214 -0
  108. package/src/views/vab/product.vue +724 -0
  109. package/src/views/vab/project.vue +559 -0
  110. package/src/views/vab/settings.vue +319 -0
  111. package/src/views/vab/statistics.vue +431 -0
  112. package/src/views/vab/table.vue +110 -0
  113. package/src/views/vab/task.vue +613 -0
  114. package/src/views/vab/team.vue +662 -0
  115. package/src/views/vab/tree.vue +44 -0
  116. package/src/views/vab/upload.vue +180 -0
  117. package/src/views/vab/vue3Demo/index.vue +103 -0
  118. package/src/views/vab/workflow.vue +863 -0
@@ -0,0 +1,724 @@
1
+ <template>
2
+ <div class="product-container">
3
+ <el-card shadow="never">
4
+ <template #header>
5
+ <div class="card-header">
6
+ <span>产品管理</span>
7
+ <div class="header-actions">
8
+ <el-input
9
+ v-model="searchText"
10
+ placeholder="搜索产品..."
11
+ clearable
12
+ style="width: 200px; margin-right: 10px"
13
+ >
14
+ <template #prefix>
15
+ <el-icon><Search /></el-icon>
16
+ </template>
17
+ </el-input>
18
+ <el-button type="primary" @click="showAddProductDialog">添加产品</el-button>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange">
24
+ <el-tab-pane label="所有产品" name="all">
25
+ <el-table
26
+ :data="filteredProducts"
27
+ style="width: 100%"
28
+ row-key="id"
29
+ v-loading="loading"
30
+ >
31
+ <el-table-column prop="name" label="产品名称" min-width="200">
32
+ <template #default="{ row }">
33
+ <div class="product-name">
34
+ <el-image
35
+ :src="row.image"
36
+ fit="cover"
37
+ style="width: 40px; height: 40px; border-radius: 4px"
38
+ />
39
+ <span style="margin-left: 10px">{{ row.name }}</span>
40
+ </div>
41
+ </template>
42
+ </el-table-column>
43
+ <el-table-column prop="category" label="产品分类" width="120" />
44
+ <el-table-column prop="brand" label="品牌" width="120" />
45
+ <el-table-column prop="price" label="价格" width="100">
46
+ <template #default="{ row }">
47
+ ¥{{ row.price }}
48
+ </template>
49
+ </el-table-column>
50
+ <el-table-column prop="stock" label="库存" width="100">
51
+ <template #default="{ row }">
52
+ <el-tag :type="getStockType(row.stock)">
53
+ {{ row.stock }}
54
+ </el-tag>
55
+ </template>
56
+ </el-table-column>
57
+ <el-table-column label="状态" width="100">
58
+ <template #default="{ row }">
59
+ <el-tag :type="getStatusType(row.status)">
60
+ {{ getStatusText(row.status) }}
61
+ </el-tag>
62
+ </template>
63
+ </el-table-column>
64
+ <el-table-column prop="createTime" label="创建时间" width="180" />
65
+ <el-table-column label="操作" width="200">
66
+ <template #default="{ row }">
67
+ <el-button type="text" @click="viewProduct(row)">查看</el-button>
68
+ <el-button type="text" @click="editProduct(row)">编辑</el-button>
69
+ <el-button type="text" @click="deleteProduct(row)">删除</el-button>
70
+ </template>
71
+ </el-table-column>
72
+ </el-table>
73
+ </el-tab-pane>
74
+
75
+ <el-tab-pane label="在售产品" name="onsale">
76
+ <el-table
77
+ :data="onsaleProducts"
78
+ style="width: 100%"
79
+ row-key="id"
80
+ >
81
+ <el-table-column prop="name" label="产品名称" min-width="200">
82
+ <template #default="{ row }">
83
+ <div class="product-name">
84
+ <el-image
85
+ :src="row.image"
86
+ fit="cover"
87
+ style="width: 40px; height: 40px; border-radius: 4px"
88
+ />
89
+ <span style="margin-left: 10px">{{ row.name }}</span>
90
+ </div>
91
+ </template>
92
+ </el-table-column>
93
+ <el-table-column prop="category" label="产品分类" width="120" />
94
+ <el-table-column prop="brand" label="品牌" width="120" />
95
+ <el-table-column prop="price" label="价格" width="100">
96
+ <template #default="{ row }">
97
+ ¥{{ row.price }}
98
+ </template>
99
+ </el-table-column>
100
+ <el-table-column prop="stock" label="库存" width="100">
101
+ <template #default="{ row }">
102
+ <el-tag :type="getStockType(row.stock)">
103
+ {{ row.stock }}
104
+ </el-tag>
105
+ </template>
106
+ </el-table-column>
107
+ <el-table-column label="操作" width="150">
108
+ <template #default="{ row }">
109
+ <el-button type="text" @click="viewProduct(row)">查看</el-button>
110
+ <el-button type="text" @click="editProduct(row)">编辑</el-button>
111
+ </template>
112
+ </el-table-column>
113
+ </el-table>
114
+ </el-tab-pane>
115
+
116
+ <el-tab-pane label="缺货产品" name="outofstock">
117
+ <el-table
118
+ :data="outOfStockProducts"
119
+ style="width: 100%"
120
+ row-key="id"
121
+ >
122
+ <el-table-column prop="name" label="产品名称" min-width="200">
123
+ <template #default="{ row }">
124
+ <div class="product-name">
125
+ <el-image
126
+ :src="row.image"
127
+ fit="cover"
128
+ style="width: 40px; height: 40px; border-radius: 4px"
129
+ />
130
+ <span style="margin-left: 10px">{{ row.name }}</span>
131
+ </div>
132
+ </template>
133
+ </el-table-column>
134
+ <el-table-column prop="category" label="产品分类" width="120" />
135
+ <el-table-column prop="brand" label="品牌" width="120" />
136
+ <el-table-column prop="price" label="价格" width="100">
137
+ <template #default="{ row }">
138
+ ¥{{ row.price }}
139
+ </template>
140
+ </el-table-column>
141
+ <el-table-column prop="stock" label="库存" width="100">
142
+ <template #default="{ row }">
143
+ <el-tag :type="getStockType(row.stock)">
144
+ {{ row.stock }}
145
+ </el-tag>
146
+ </template>
147
+ </el-table-column>
148
+ <el-table-column label="操作" width="150">
149
+ <template #default="{ row }">
150
+ <el-button type="text" @click="editProduct(row)">补货</el-button>
151
+ </template>
152
+ </el-table-column>
153
+ </el-table>
154
+ </el-tab-pane>
155
+ </el-tabs>
156
+
157
+ <div class="pagination-container">
158
+ <el-pagination
159
+ v-model:current-page="currentPage"
160
+ v-model:page-size="pageSize"
161
+ :page-sizes="[10, 20, 50, 100]"
162
+ :total="totalProducts"
163
+ layout="total, sizes, prev, pager, next, jumper"
164
+ @size-change="handleSizeChange"
165
+ @current-change="handleCurrentChange"
166
+ />
167
+ </div>
168
+ </el-card>
169
+
170
+ <!-- 添加/编辑产品对话框 -->
171
+ <el-dialog
172
+ v-model="productDialogVisible"
173
+ :title="editingProduct ? '编辑产品' : '添加产品'"
174
+ width="800px"
175
+ >
176
+ <el-form
177
+ ref="productFormRef"
178
+ :model="productForm"
179
+ :rules="productRules"
180
+ label-width="100px"
181
+ >
182
+ <el-row :gutter="20">
183
+ <el-col :span="12">
184
+ <el-form-item label="产品名称" prop="name">
185
+ <el-input v-model="productForm.name" />
186
+ </el-form-item>
187
+
188
+ <el-form-item label="产品分类" prop="category">
189
+ <el-select v-model="productForm.category" placeholder="请选择产品分类" style="width: 100%">
190
+ <el-option
191
+ v-for="category in categories"
192
+ :key="category.id"
193
+ :label="category.name"
194
+ :value="category.name"
195
+ />
196
+ </el-select>
197
+ </el-form-item>
198
+
199
+ <el-form-item label="品牌" prop="brand">
200
+ <el-input v-model="productForm.brand" />
201
+ </el-form-item>
202
+
203
+ <el-form-item label="价格" prop="price">
204
+ <el-input-number
205
+ v-model="productForm.price"
206
+ :min="0"
207
+ :step="0.01"
208
+ controls-position="right"
209
+ style="width: 100%"
210
+ />
211
+ </el-form-item>
212
+
213
+ <el-form-item label="库存" prop="stock">
214
+ <el-input-number
215
+ v-model="productForm.stock"
216
+ :min="0"
217
+ controls-position="right"
218
+ style="width: 100%"
219
+ />
220
+ </el-form-item>
221
+
222
+ <el-form-item label="状态" prop="status">
223
+ <el-select v-model="productForm.status" placeholder="请选择状态" style="width: 100%">
224
+ <el-option label="在售" value="onsale"></el-option>
225
+ <el-option label="下架" value="offsale"></el-option>
226
+ <el-option label="缺货" value="outofstock"></el-option>
227
+ </el-select>
228
+ </el-form-item>
229
+ </el-col>
230
+
231
+ <el-col :span="12">
232
+ <el-form-item label="产品图片">
233
+ <el-upload
234
+ class="avatar-uploader"
235
+ action="https://jsonplaceholder.typicode.com/posts/"
236
+ :show-file-list="false"
237
+ :on-success="handleImageSuccess"
238
+ :before-upload="beforeImageUpload"
239
+ >
240
+ <img
241
+ v-if="productForm.image"
242
+ :src="productForm.image"
243
+ class="avatar"
244
+ alt="Product Image"
245
+ />
246
+ <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
247
+ </el-upload>
248
+ </el-form-item>
249
+
250
+ <el-form-item label="产品描述" prop="description">
251
+ <el-input
252
+ v-model="productForm.description"
253
+ type="textarea"
254
+ :rows="5"
255
+ placeholder="请输入产品描述"
256
+ />
257
+ </el-form-item>
258
+ </el-col>
259
+ </el-row>
260
+ </el-form>
261
+
262
+ <template #footer>
263
+ <span class="dialog-footer">
264
+ <el-button @click="productDialogVisible = false">取消</el-button>
265
+ <el-button
266
+ type="primary"
267
+ @click="saveProduct"
268
+ >
269
+ 保存
270
+ </el-button>
271
+ </span>
272
+ </template>
273
+ </el-dialog>
274
+
275
+ <!-- 产品详情对话框 -->
276
+ <el-dialog
277
+ v-model="detailDialogVisible"
278
+ title="产品详情"
279
+ width="800px"
280
+ >
281
+ <el-row :gutter="20">
282
+ <el-col :span="8">
283
+ <div class="product-image-detail">
284
+ <el-image
285
+ :src="detailProduct.image"
286
+ fit="cover"
287
+ style="width: 100%; height: 300px; border-radius: 8px"
288
+ />
289
+ </div>
290
+ </el-col>
291
+ <el-col :span="16">
292
+ <el-descriptions :column="1" border>
293
+ <el-descriptions-item label="产品名称">{{ detailProduct.name }}</el-descriptions-item>
294
+ <el-descriptions-item label="产品分类">{{ detailProduct.category }}</el-descriptions-item>
295
+ <el-descriptions-item label="品牌">{{ detailProduct.brand }}</el-descriptions-item>
296
+ <el-descriptions-item label="价格">
297
+ <span class="product-price">¥{{ detailProduct.price }}</span>
298
+ </el-descriptions-item>
299
+ <el-descriptions-item label="库存">
300
+ <el-tag :type="getStockType(detailProduct.stock)">
301
+ {{ detailProduct.stock }}
302
+ </el-tag>
303
+ </el-descriptions-item>
304
+ <el-descriptions-item label="状态">
305
+ <el-tag :type="getStatusType(detailProduct.status)">
306
+ {{ getStatusText(detailProduct.status) }}
307
+ </el-tag>
308
+ </el-descriptions-item>
309
+ <el-descriptions-item label="创建时间">{{ detailProduct.createTime }}</el-descriptions-item>
310
+ </el-descriptions>
311
+ </el-col>
312
+ </el-row>
313
+
314
+ <el-divider>产品描述</el-divider>
315
+
316
+ <div class="product-description">
317
+ {{ detailProduct.description }}
318
+ </div>
319
+
320
+ <el-tabs v-model="productActiveTab" style="margin-top: 20px">
321
+ <el-tab-pane label="销售记录" name="sales">
322
+ <el-table :data="productSales" style="width: 100%">
323
+ <el-table-column prop="orderId" label="订单号" width="150" />
324
+ <el-table-column prop="customer" label="客户" width="150" />
325
+ <el-table-column prop="quantity" label="数量" width="100" />
326
+ <el-table-column prop="amount" label="金额" width="120">
327
+ <template #default="{ row }">
328
+ ¥{{ row.amount.toLocaleString() }}
329
+ </template>
330
+ </el-table-column>
331
+ <el-table-column prop="date" label="销售时间" width="180" />
332
+ </el-table>
333
+ </el-tab-pane>
334
+ <el-tab-pane label="评价" name="reviews">
335
+ <el-table :data="productReviews" style="width: 100%">
336
+ <el-table-column prop="customer" label="客户" width="150" />
337
+ <el-table-column label="评分" width="120">
338
+ <template #default="{ row }">
339
+ <el-rate
340
+ v-model="row.rating"
341
+ disabled
342
+ show-score
343
+ text-color="#ff9900"
344
+ score-template="{value}分"
345
+ />
346
+ </template>
347
+ </el-table-column>
348
+ <el-table-column prop="content" label="评价内容" />
349
+ <el-table-column prop="date" label="评价时间" width="180" />
350
+ </el-table>
351
+ </el-tab-pane>
352
+ </el-tabs>
353
+
354
+ <template #footer>
355
+ <span class="dialog-footer">
356
+ <el-button @click="detailDialogVisible = false">关闭</el-button>
357
+ <el-button type="primary" @click="editProduct(detailProduct)">编辑</el-button>
358
+ </span>
359
+ </template>
360
+ </el-dialog>
361
+ </div>
362
+ </template>
363
+
364
+ <script>
365
+ import { Search, Plus } from "@element-plus/icons-vue";
366
+
367
+ export default {
368
+ name: "Product",
369
+ components: {
370
+ Search,
371
+ Plus
372
+ },
373
+ data() {
374
+ return {
375
+ activeTab: "all",
376
+ productActiveTab: "sales",
377
+ searchText: "",
378
+ currentPage: 1,
379
+ pageSize: 10,
380
+ totalProducts: 0,
381
+ loading: false,
382
+ productDialogVisible: false,
383
+ detailDialogVisible: false,
384
+ editingProduct: null,
385
+ products: [
386
+ {
387
+ id: 1,
388
+ name: "iPhone 14 Pro",
389
+ category: "手机",
390
+ brand: "Apple",
391
+ price: 7999.00,
392
+ stock: 50,
393
+ status: "onsale",
394
+ image: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
395
+ description: "全新iPhone 14 Pro,配备A16仿生芯片,支持5G网络,拥有超瓷晶面板和全天候显示功能。",
396
+ createTime: "2023-01-15 10:30:00"
397
+ },
398
+ {
399
+ id: 2,
400
+ name: "MacBook Pro 14英寸",
401
+ category: "笔记本",
402
+ brand: "Apple",
403
+ price: 15999.00,
404
+ stock: 20,
405
+ status: "onsale",
406
+ image: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
407
+ description: "搭载M2 Pro芯片的MacBook Pro,性能强劲,续航持久,适合专业用户使用。",
408
+ createTime: "2023-02-20 14:45:00"
409
+ },
410
+ {
411
+ id: 3,
412
+ name: "AirPods Pro",
413
+ category: "耳机",
414
+ brand: "Apple",
415
+ price: 1899.00,
416
+ stock: 0,
417
+ status: "outofstock",
418
+ image: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
419
+ description: "主动降噪无线耳机,支持空间音频,提供卓越的音质体验。",
420
+ createTime: "2023-03-10 09:15:00"
421
+ },
422
+ {
423
+ id: 4,
424
+ name: "iPad Air",
425
+ category: "平板",
426
+ brand: "Apple",
427
+ price: 4399.00,
428
+ stock: 30,
429
+ status: "onsale",
430
+ image: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
431
+ description: "轻薄便携的iPad Air,配备绚丽的Liquid Retina显示屏和A14仿生芯片。",
432
+ createTime: "2023-04-05 16:20:00"
433
+ },
434
+ {
435
+ id: 5,
436
+ name: "Apple Watch Series 8",
437
+ category: "智能手表",
438
+ brand: "Apple",
439
+ price: 2999.00,
440
+ stock: 15,
441
+ status: "offsale",
442
+ image: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
443
+ description: "先进的健康和健身功能,支持心电图和血氧检测,全天候视网膜显示屏。",
444
+ createTime: "2023-05-12 11:30:00"
445
+ }
446
+ ],
447
+ categories: [
448
+ { id: 1, name: "手机" },
449
+ { id: 2, name: "笔记本" },
450
+ { id: 3, name: "平板" },
451
+ { id: 4, name: "耳机" },
452
+ { id: 5, name: "智能手表" }
453
+ ],
454
+ productForm: {
455
+ name: "",
456
+ category: "",
457
+ brand: "",
458
+ price: 0,
459
+ stock: 0,
460
+ status: "onsale",
461
+ image: "",
462
+ description: ""
463
+ },
464
+ detailProduct: {},
465
+ productRules: {
466
+ name: [
467
+ { required: true, message: "请输入产品名称", trigger: "blur" }
468
+ ],
469
+ category: [
470
+ { required: true, message: "请选择产品分类", trigger: "change" }
471
+ ],
472
+ brand: [
473
+ { required: true, message: "请输入品牌", trigger: "blur" }
474
+ ],
475
+ price: [
476
+ { required: true, message: "请输入价格", trigger: "blur" }
477
+ ],
478
+ stock: [
479
+ { required: true, message: "请输入库存", trigger: "blur" }
480
+ ],
481
+ status: [
482
+ { required: true, message: "请选择状态", trigger: "change" }
483
+ ]
484
+ },
485
+ productSales: [
486
+ {
487
+ orderId: "SO202305001",
488
+ customer: "张三",
489
+ quantity: 2,
490
+ amount: 15998,
491
+ date: "2023-05-01 14:30:00"
492
+ },
493
+ {
494
+ orderId: "SO202304015",
495
+ customer: "李四",
496
+ quantity: 1,
497
+ amount: 7999,
498
+ date: "2023-04-15 10:15:00"
499
+ }
500
+ ],
501
+ productReviews: [
502
+ {
503
+ customer: "王五",
504
+ rating: 5,
505
+ content: "产品质量非常好,使用体验很棒!",
506
+ date: "2023-05-10 16:00:00"
507
+ },
508
+ {
509
+ customer: "赵六",
510
+ rating: 4,
511
+ content: "产品不错,性价比高,推荐购买。",
512
+ date: "2023-05-08 09:30:00"
513
+ }
514
+ ]
515
+ };
516
+ },
517
+ computed: {
518
+ filteredProducts() {
519
+ let result = this.products;
520
+
521
+ // 搜索过滤
522
+ if (this.searchText) {
523
+ result = result.filter(product =>
524
+ product.name.toLowerCase().includes(this.searchText.toLowerCase()) ||
525
+ product.category.toLowerCase().includes(this.searchText.toLowerCase()) ||
526
+ product.brand.toLowerCase().includes(this.searchText.toLowerCase())
527
+ );
528
+ }
529
+
530
+ // 分页处理
531
+ const start = (this.currentPage - 1) * this.pageSize;
532
+ const end = start + this.pageSize;
533
+ return result.slice(start, end);
534
+ },
535
+ onsaleProducts() {
536
+ return this.products.filter(product => product.status === "onsale");
537
+ },
538
+ outOfStockProducts() {
539
+ return this.products.filter(product => product.stock === 0);
540
+ }
541
+ },
542
+ methods: {
543
+ handleTabChange(tab) {
544
+ this.activeTab = tab;
545
+ this.currentPage = 1;
546
+ },
547
+ handleSizeChange(val) {
548
+ this.pageSize = val;
549
+ this.currentPage = 1;
550
+ },
551
+ handleCurrentChange(val) {
552
+ this.currentPage = val;
553
+ },
554
+ getStatusText(status) {
555
+ const statusMap = {
556
+ "onsale": "在售",
557
+ "offsale": "下架",
558
+ "outofstock": "缺货"
559
+ };
560
+ return statusMap[status] || status;
561
+ },
562
+ getStatusType(status) {
563
+ const typeMap = {
564
+ "onsale": "success",
565
+ "offsale": "info",
566
+ "outofstock": "danger"
567
+ };
568
+ return typeMap[status] || "info";
569
+ },
570
+ getStockType(stock) {
571
+ if (stock === 0) {
572
+ return "danger";
573
+ } else if (stock < 10) {
574
+ return "warning";
575
+ }
576
+ return "success";
577
+ },
578
+ showAddProductDialog() {
579
+ this.editingProduct = null;
580
+ this.productForm = {
581
+ name: "",
582
+ category: "",
583
+ brand: "",
584
+ price: 0,
585
+ stock: 0,
586
+ status: "onsale",
587
+ image: "",
588
+ description: ""
589
+ };
590
+ this.productDialogVisible = true;
591
+ this.$nextTick(() => {
592
+ this.$refs.productFormRef.resetFields();
593
+ });
594
+ },
595
+ editProduct(product) {
596
+ this.editingProduct = product;
597
+ this.productForm = { ...product };
598
+ this.productDialogVisible = true;
599
+ this.detailDialogVisible = false;
600
+ },
601
+ viewProduct(product) {
602
+ this.detailProduct = { ...product };
603
+ this.detailDialogVisible = true;
604
+ },
605
+ deleteProduct(product) {
606
+ this.$confirm(`确定要删除产品"${product.name}"吗?`, "提示", {
607
+ confirmButtonText: "确定",
608
+ cancelButtonText: "取消",
609
+ type: "warning"
610
+ }).then(() => {
611
+ const index = this.products.findIndex(p => p.id === product.id);
612
+ if (index !== -1) {
613
+ this.products.splice(index, 1);
614
+ this.totalProducts = this.products.length;
615
+ this.$message.success("产品删除成功");
616
+ }
617
+ }).catch(() => {
618
+ this.$message.info("已取消删除");
619
+ });
620
+ },
621
+ saveProduct() {
622
+ this.$refs.productFormRef.validate((valid) => {
623
+ if (valid) {
624
+ if (this.editingProduct) {
625
+ // 编辑产品
626
+ const index = this.products.findIndex(p => p.id === this.editingProduct.id);
627
+ if (index !== -1) {
628
+ this.products[index] = { ...this.editingProduct, ...this.productForm };
629
+ this.$message.success("产品信息更新成功");
630
+ }
631
+ } else {
632
+ // 添加产品
633
+ const newProduct = {
634
+ id: Date.now(),
635
+ createTime: new Date().toLocaleString(),
636
+ ...this.productForm
637
+ };
638
+ this.products.push(newProduct);
639
+ this.totalProducts = this.products.length;
640
+ this.$message.success("产品添加成功");
641
+ }
642
+ this.productDialogVisible = false;
643
+ }
644
+ });
645
+ },
646
+ handleImageSuccess(response, file) {
647
+ this.productForm.image = URL.createObjectURL(file.raw);
648
+ },
649
+ beforeImageUpload(file) {
650
+ const isJPG = file.type === "image/jpeg" || file.type === "image/png";
651
+ const isLt2M = file.size / 1024 / 1024 < 2;
652
+
653
+ if (!isJPG) {
654
+ this.$message.error("产品图片只能是 JPG 或 PNG 格式!");
655
+ }
656
+ if (!isLt2M) {
657
+ this.$message.error("产品图片大小不能超过 2MB!");
658
+ }
659
+ return isJPG && isLt2M;
660
+ }
661
+ }
662
+ };
663
+ </script>
664
+
665
+ <style lang="scss" scoped>
666
+ .product-container {
667
+ padding: 20px;
668
+
669
+ .card-header {
670
+ display: flex;
671
+ justify-content: space-between;
672
+ align-items: center;
673
+ font-weight: bold;
674
+ }
675
+
676
+ .product-name {
677
+ display: flex;
678
+ align-items: center;
679
+ }
680
+
681
+ .product-price {
682
+ font-size: 18px;
683
+ font-weight: bold;
684
+ color: #fa541c;
685
+ }
686
+
687
+ .product-description {
688
+ line-height: 1.8;
689
+ color: #666;
690
+ }
691
+
692
+ .pagination-container {
693
+ margin-top: 20px;
694
+ text-align: right;
695
+ }
696
+
697
+ .avatar-uploader .avatar {
698
+ width: 178px;
699
+ height: 178px;
700
+ display: block;
701
+ }
702
+ }
703
+
704
+ .avatar-uploader .el-upload {
705
+ border: 1px dashed #d9d9d9;
706
+ border-radius: 6px;
707
+ cursor: pointer;
708
+ position: relative;
709
+ overflow: hidden;
710
+ transition: var(--el-transition-duration-fast);
711
+ }
712
+
713
+ .avatar-uploader .el-upload:hover {
714
+ border-color: var(--el-color-primary);
715
+ }
716
+
717
+ .el-icon.avatar-uploader-icon {
718
+ font-size: 28px;
719
+ color: #8c939d;
720
+ width: 178px;
721
+ height: 178px;
722
+ text-align: center;
723
+ }
724
+ </style>